/*
* Copyright (C) 2011 Andrea Schweer
*
* This file is part of the Digital Parrot.
*
* The Digital Parrot is free software; you can redistribute it and/or modify
* it under the terms of the Eclipse Public License as published by the Eclipse
* Foundation or its Agreement Steward, either version 1.0 of the License, or
* (at your option) any later version.
*
* The Digital Parrot is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the Eclipse Public License for
* more details.
*
* You should have received a copy of the Eclipse Public License along with the
* Digital Parrot. If not, see http://www.eclipse.org/legal/epl-v10.html.
*
*/
package net.schweerelos.parrot.model;
import net.schweerelos.parrot.util.QuadTree;
import net.schweerelos.parrot.util.QuadTreeImpl;
import com.hp.hpl.jena.ontology.Individual;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntResource;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.NodeIterator;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
public abstract class LocatedThingsHelper {
static private final String ABSOLUTELY_PLACED_THING = "http://parrot.resnet.scms.waikato.ac.nz/Parrot/Terms/TimeAndPlace/2008/11/TimeAndPlace.owl#AbsolutelyPlacedThing";
static private final String PLACED_THING = "http://parrot.resnet.scms.waikato.ac.nz/Parrot/Terms/TimeAndPlace/2008/11/TimeAndPlace.owl#PlacedThing";
static private final String LATITUDE = "http://parrot.resnet.scms.waikato.ac.nz/Parrot/Terms/TimeAndPlace/2008/11/TimeAndPlace.owl#lat";
static private final String LONGITUDE = "http://parrot.resnet.scms.waikato.ac.nz/Parrot/Terms/TimeAndPlace/2008/11/TimeAndPlace.owl#long";
private static final String COORD_PRECISION = "http://parrot.resnet.scms.waikato.ac.nz/Parrot/Terms/TimeAndPlace/2008/11/TimeAndPlace.owl#coordPrecision";
private static final String LOCATED_IN = "http://parrot.resnet.scms.waikato.ac.nz/Parrot/Terms/TimeAndPlace/2008/11/TimeAndPlace.owl#locatedIn";
public static net.schweerelos.parrot.util.QuadTree<CenteredThing<NodeWrapper>> extractLocatedThings(
ParrotModel pModel) {
OntModel model = pModel.getOntModel();
Resource placedThingClass = model.createClass(PLACED_THING);
QuadTree<CenteredThing<NodeWrapper>> result = new QuadTreeImpl<CenteredThing<NodeWrapper>>();
ExtendedIterator<Individual> instances = model
.listIndividuals(placedThingClass);
while (instances.hasNext()) {
Individual instance = instances.next();
NodeWrapper wrapper = pModel.getNodeWrapper(instance);
try {
CenteredThing<NodeWrapper> thing = createCenteredThingFor(
instance, model, wrapper);
result.put(thing.getLat(), thing.getLon(), thing);
} catch (NotPlacedThingException e) { /* ignore */
}
}
return result;
}
public static CenteredThing<NodeWrapper> getAsLocatedThing(
NodeWrapper currentNode) throws NotPlacedThingException {
try {
return createCenteredThingFor(currentNode.getOntResource()
.asIndividual(),
currentNode.getOntResource().getOntModel(), currentNode);
} catch (NullPointerException npe) {
throw new NotPlacedThingException(currentNode
+ " isn't a placed thing", npe);
}
}
private static CenteredThing<NodeWrapper> createCenteredThingFor(
Individual instance, OntModel model, NodeWrapper wrapper)
throws NotPlacedThingException {
float latitude = extractLatitude(instance, model);
float longitude = extractLongitude(instance, model);
CoordinatePrecision precision;
try {
precision = extractPrecision(instance, model);
} catch (NotPlacedThingException e) {
// use default precision
// TODO is this a good idea?
precision = CoordinatePrecision.RoomPrecision;
}
return new LocatedThing(longitude, latitude, precision, wrapper);
}
public static boolean isLocatedThing(NodeWrapper currentNode,
ParrotModel model) {
return isLocatedThing(currentNode, model.getOntModel());
}
private static boolean isLocatedThing(NodeWrapper currentNode,
OntModel model) {
if (!currentNode.isOntResource()) {
return false;
}
OntResource res = currentNode.getOntResource();
if (isAbsolutelyPlacedThing(res)) {
return true;
}
return isIndirectlyPlacedThing(res, model);
}
private static boolean isIndirectlyPlacedThing(OntResource node,
OntModel model) {
Property locatedInProperty = model.createProperty(LOCATED_IN);
if (!node.hasProperty(locatedInProperty)) {
return false;
}
NodeIterator values = node.listPropertyValues(locatedInProperty);
while (values.hasNext()) {
RDFNode value = values.next();
if (isAbsolutelyPlacedThing(value)) {
return true;
}
}
return false;
}
private static boolean isAbsolutelyPlacedThing(RDFNode node) {
if (!node.canAs(OntResource.class)) {
return false;
}
OntResource res = node.as(OntResource.class);
return isAbsolutelyPlacedThing(res);
}
private static boolean isAbsolutelyPlacedThing(OntResource res) {
if (!res.isIndividual()) {
return false;
}
return res.asIndividual().hasOntClass(ABSOLUTELY_PLACED_THING);
}
private static float extractLatitude(OntResource node, OntModel model)
throws NotPlacedThingException {
// TODO #42 do it this way for timed things too
Property latitudeProperty = model.createProperty(LATITUDE);
RDFNode latitudeNode;
// System.out.println("extracting latitude for " + node.getLocalName());
if (node.hasProperty(latitudeProperty)) {
// System.out.println("directly getting latitude value");
latitudeNode = node.getPropertyValue(latitudeProperty);
} else {
// System.out.println("trying to find indirect latitude value");
latitudeNode = findNearest(node, model, latitudeProperty);
// System.out.println("success finding indirect latitude value");
}
return extractFloat(latitudeNode);
}
/**
* Goes through enclosing placed things and takes property value of the one
* with the smallest precision
*
* @param node
* @param model
* @param latitudeProperty
* @return
* @throws NotPlacedThingException
*/
private static RDFNode findNearest(OntResource node, OntModel model,
Property property) throws NotPlacedThingException {
Property locatedInProperty = model.createProperty(LOCATED_IN);
Property coordPrecisionProperty = model.createProperty(COORD_PRECISION);
if (!node.hasProperty(locatedInProperty)) {
throw new NotPlacedThingException("couldn't find a value for "
+ property.getLocalName() + " from " + node.getLocalName());
}
CoordinatePrecision currentSmallestPrecision = null;
RDFNode currentBest = null;
NodeIterator locatedIns = node.listPropertyValues(locatedInProperty);
// System.out.println("things that " + node.getLocalName() +
// " is located in");
while (locatedIns.hasNext()) {
RDFNode locatedIn = locatedIns.next();
// System.out.println("looking at " + locatedIn);
if (locatedIn.canAs(OntResource.class)) {
OntResource res = locatedIn.as(OntResource.class);
if (res.hasProperty(property)
&& res.hasProperty(coordPrecisionProperty)) {
RDFNode precisionNode = res
.getPropertyValue(coordPrecisionProperty);
if (precisionNode.canAs(Individual.class)) {
Individual precisionIndiv = precisionNode
.as(Individual.class);
CoordinatePrecision thisPrecision = CoordinatePrecision
.valueOf(precisionIndiv.getLocalName());
if (currentSmallestPrecision == null
|| thisPrecision
.compareTo(currentSmallestPrecision) < 0) {
currentBest = res.getPropertyValue(property);
currentSmallestPrecision = thisPrecision;
}
}
}
}
}
if (currentBest == null) {
throw new NotPlacedThingException("couldn't find a value for "
+ property.getLocalName() + " from " + node.getLocalName());
}
return currentBest;
}
private static float extractLongitude(OntResource node, OntModel model)
throws NotPlacedThingException {
Property longitudeProperty = model.createProperty(LONGITUDE);
RDFNode longitudeValue;
if (node.hasProperty(longitudeProperty)) {
longitudeValue = node.getPropertyValue(longitudeProperty);
} else {
longitudeValue = findNearest(node, model, longitudeProperty);
}
return extractFloat(longitudeValue);
}
private static CoordinatePrecision extractPrecision(OntResource node,
OntModel model) throws NotPlacedThingException {
Property coordPrecisionProperty = model.createProperty(COORD_PRECISION);
RDFNode coordPrecisionValue;
if (node.hasProperty(coordPrecisionProperty)) {
coordPrecisionValue = node.getPropertyValue(coordPrecisionProperty);
} else {
coordPrecisionValue = findNearest(node, model,
coordPrecisionProperty);
}
Individual coordPrecision = coordPrecisionValue.as(Individual.class);
CoordinatePrecision precision = CoordinatePrecision
.valueOf(coordPrecision.getLocalName());
return precision;
}
private static float extractFloat(RDFNode node) {
if (!node.isLiteral()) {
throw new IllegalArgumentException("Node is not a literal");
}
Object value = ((Literal) node).getValue();
if (value != null && !value.toString().equals("")) {
return Float.parseFloat(value.toString());
} else {
return 0f;
}
}
private static final class LocatedThing implements
CenteredThing<NodeWrapper> {
private final float longitude;
private final float latitude;
private final CoordinatePrecision precision;
private final NodeWrapper node;
private LocatedThing(float longitude, float latitude,
CoordinatePrecision precision, NodeWrapper node) {
this.longitude = longitude;
this.latitude = latitude;
this.precision = precision;
this.node = node;
}
@Override
public String getLabel() {
return node.toString();
}
@Override
public float getLat() {
return latitude;
}
@Override
public float getLon() {
return longitude;
}
@Override
public NodeWrapper getValue() {
return node;
}
@Override
public CoordinatePrecision getPrecision() {
return precision;
}
public String toString() {
return "Located thing (" + getLabel() + ")";
}
}
}