/*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is the Kowari Metadata Store.
*
* The Initial Developer of the Original Code is Plugged In Software Pty
* Ltd (http://www.pisoftware.com, mailto:info@pisoftware.com). Portions
* created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
* Plugged In Software Pty Ltd. All Rights Reserved.
*
* Contributor(s): N/A.
*
* [NOTE: The text of this Exhibit A may differ slightly from the text
* of the notices in the Source Code files of the Original Code. You
* should use the text of this Exhibit A rather than the text found in the
* Original Code Source Code for Your Modifications.]
*
*/
package org.mulgara.resolver.gis;
// Java 2 standard packages
// Log4J
import org.apache.log4j.Logger;
//JRDF
import org.jrdf.graph.*;
import org.jrdf.vocabulary.*;
// Local packages
import org.mulgara.query.*;
import org.mulgara.query.rdf.*;
import org.mulgara.resolver.gis.tools.*;
import org.mulgara.resolver.spi.*;
import org.mulgara.store.statement.*;
import org.mulgara.store.tuples.*;
/**
* Wraps a Tuples with the Variables: [$x, $xlat, $xlong, $y, $ylat, $ylong] and
* generates the following distance statements for each row:
* $x $z $y
* $y $z $x
* $z <:type> <:distance>
* $z <:magnitude> "f($xlat,$xlong,$ylat,$ylong)"^^xsd:double
*
* @created 2004-11-17
*
* @author <a href="mailto:robert.turner@tucanatech.com">Robert Turner</a>
*
* @version $Revision: 1.6 $
*
* @modified $Date: 2005/01/05 04:58:29 $
*
* @maintenanceAuthor $Author: newmana $
*
* @company <A href="mailto:info@PIsoftware.com">Plugged In Software</A>
*
* @copyright ©2001 <a href="http://www.pisoftware.com/">Plugged In
* Software Pty Ltd</a>
*
* @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
*/
public class GISDistanceStatements implements Statements, Cloneable {
/** Logger. */
@SuppressWarnings("unused")
private final static Logger logger = Logger.getLogger(GISDistanceStatements.class.getName());
/** Number of statements generated per row of the Tuples */
private static final int STATEMENTS_PER_ROW = 4;
/** Data source for calculations */
private Tuples points = null;
/** Used to calculate GeoSpatial distances */
private DistanceCalculator calculator = null;
/** Used to allocate nodes */
private ResolverSession resolverSession = null;
/** Local Node Id's used to describe a distance object */
private long rdfType = -1;
private long distType = -1;
private long distMagnitude = -1;
/** Variables that the Tuples contain */
public static final Variable X_VAR;
public static final Variable XLAT_VAR;
public static final Variable XLONG_VAR;
public static final Variable Y_VAR;
public static final Variable YLAT_VAR;
public static final Variable YLONG_VAR;
//initialize Variables
static {
X_VAR = new Variable("x");
XLAT_VAR = new Variable("xlat");
XLONG_VAR = new Variable("xlong");
Y_VAR = new Variable("y");
YLAT_VAR = new Variable("ylat");
YLONG_VAR = new Variable("ylong");
}
/** Variable indexes */
private int xIndex = -1;
private int xlatIndex = -1;
private int xlongIndex = -1;
private int yIndex = -1;
private int ylatIndex = -1;
private int ylongIndex = -1;
/** Current Subject */
private long subject = -1;
/** Current Predicate */
private long predicate = -1;
/** Current Object */
private long object = -1;
/** Current 'x' coordinate */
private LocalGISCoordinate xCoord = null;
/** Current 'y' coordinate */
private LocalGISCoordinate yCoord = null;
/** Current Distance between 'X' and 'Y' Coordinates */
private LocalGISDistance distance = null;
/** Current Row in the statements */
private long currentRow = -1;
/** Number of rows Expected */
private long rowCount = -1;
/**
* Constructor.
*
* @param points Tuples
* @param calculator DistanceCalculator
* @param resolverSession ResolverSession
* @throws ResolverException
*/
public GISDistanceStatements(Tuples points, DistanceCalculator calculator,
ResolverSession resolverSession) throws ResolverException {
//validate
if (calculator == null) {
throw new IllegalArgumentException("DistanceCalculator is null.");
}
if (resolverSession == null) {
throw new IllegalArgumentException("ResolverSession is null.");
}
if (points == null) {
throw new IllegalArgumentException("Tuples is null.");
}
//find the Variables
try {
//check for emtpy Tuples
if (!(points.isEmpty())) {
xIndex = points.getColumnIndex(X_VAR);
xlatIndex = points.getColumnIndex(XLAT_VAR);
xlongIndex = points.getColumnIndex(XLONG_VAR);
yIndex = points.getColumnIndex(Y_VAR);
ylatIndex = points.getColumnIndex(YLAT_VAR);
ylongIndex = points.getColumnIndex(YLONG_VAR);
}
}
catch (TuplesException tuplesException) {
String tuplesVars = "Tuples.Variables: ";
Variable [] vars = points.getVariables();
if (vars != null) {
for (int i = 0; i < vars.length; i++) {
tuplesVars += vars[i] + ", ";
}
} else {
tuplesVars += "null";
}
String tuples = "Tuples: " + points + "(" + points.getClass().getName() + ")";
throw new ResolverException("Tuples must contain the Variables: " +
X_VAR + ", " + XLAT_VAR + ", " + XLONG_VAR + ", " + Y_VAR + ", " +
YLAT_VAR + ", " + YLONG_VAR + ".\n" + tuplesVars + ".\n" + tuples, tuplesException);
}
//pre-localize nodes
try {
rdfType = resolverSession.localizePersistent(new URIReferenceImpl(RDF.TYPE));
distType = resolverSession.localizePersistent(LocalGISDistance.RDF_TYPE);
distMagnitude = resolverSession.localizePersistent(LocalGISDistance.MAGNITUDE);
}
catch (LocalizeException localizeException) {
throw new ResolverException("Failed to pre-localize Nodes.",
localizeException);
}
//initialize members
this.points = points;
this.calculator = calculator;
this.resolverSession = resolverSession;
}
/**
* getSubject
*
* @return long
*/
public long getSubject() throws TuplesException {
if (subject == -1) {
throw new TuplesException("beforeFirst() not called or end already reached.");
}
return subject;
}
/**
* getPredicate
*
* @return long
*/
public long getPredicate() throws TuplesException {
if (predicate == -1) {
throw new TuplesException("beforeFirst() not called or end already reached.");
}
return predicate;
}
/**
* getObject
*
* @return long
*/
public long getObject() throws TuplesException {
if (object == -1) {
throw new TuplesException("beforeFirst() not called or end already reached.");
}
return object;
}
/**
* beforeFirst
*
* @throws TuplesException
*/
public void beforeFirst() throws TuplesException {
currentRow = -1;
subject = -1;
predicate = -1;
object = -1;
xCoord = null;
yCoord = null;
distance = null;
points.beforeFirst();
}
/**
* Statements have 3 columns: $subject, $predicate, $object
*
* @param column Variable
* @return int
* @throws TuplesException
*/
public int getColumnIndex(Variable column) throws TuplesException {
if (column.equals(StatementStore.VARIABLES[0])) {
return 0;
}
else if (column.equals(StatementStore.VARIABLES[1])) {
return 1;
}
else if (column.equals(StatementStore.VARIABLES[2])) {
return 2;
}
else {
throw new TuplesException("Statements does not contain Variable: " +
column);
}
}
/**
* Statements have 3 columns: $subject, $predicate, $object
*
* @return int
*/
public int getNumberOfVariables() {
return 3;
}
/**
* Statements have 3 columns: $subject, $predicate, $object
*
* @return Variable[]
*/
public Variable[] getVariables() {
return new Variable[] {
StatementStore.VARIABLES[0],
StatementStore.VARIABLES[1],
StatementStore.VARIABLES[2]
};
}
/**
* isUnconstrained
*
* @return boolean
*/
public boolean isUnconstrained() {
return false;
}
/**
* Returns the number of rows in this Statements.
*
* @return long
* @throws TuplesException
*/
public long getRowCount() throws TuplesException {
//lazily evaluate
if (rowCount == -1) {
rowCount = points.getRowCount() * STATEMENTS_PER_ROW;
}
return rowCount;
}
/**
* getRowUpperBound
*
* @return long
* @throws TuplesException
*/
public long getRowUpperBound() throws TuplesException {
return points.getRowUpperBound() * STATEMENTS_PER_ROW;
}
/**
* getRowExpectedCount
*
* @return The expected wize of this result
* @throws TuplesException when accessing data
*/
public long getRowExpectedCount() throws TuplesException {
return points.getRowExpectedCount() * STATEMENTS_PER_ROW;
}
/**
* getRowCardinality
*
* @return int
* @throws TuplesException
*/
public int getRowCardinality() throws TuplesException {
return points.getRowCardinality();
}
/* (non-Javadoc)
* @see org.mulgara.query.Cursor#isEmpty()
*/
public boolean isEmpty() throws TuplesException {
return points.isEmpty();
}
/**
* Determines which 'sub row' to return from the tuples.
*
* @return boolean
* @throws TuplesException
*/
public boolean next() throws TuplesException {
//are there any statements?
if (points.isEmpty()) {
return false;
}
//reset statement
subject = -1;
predicate = -1;
object = -1;
//has the end been reached already?
if (currentRow >= getRowCount()) {
return false;
}
currentRow++;
//what 'sub statement' is next?
int subRow = (int) currentRow % STATEMENTS_PER_ROW;
switch (subRow) {
case (0):
//are there any more Tuples
if (!nextInTuples()
|| (xCoord == null) || (yCoord == null) || (distance == null)) {
return false;
}
//$x $z $y
subject = xCoord.getNodeId();
predicate = distance.getNodeId();
object = yCoord.getNodeId();
break;
case (1):
//$y $z $x
subject = yCoord.getNodeId();
predicate = distance.getNodeId();
object = xCoord.getNodeId();
break;
case (2):
//$z rdf:type :distance
subject = distance.getNodeId();
predicate = rdfType;
object = distType;
break;
case (3):
//$z :magnitude "size"
subject = distance.getNodeId();
predicate = distMagnitude;
object = localizeMagnitude(distance.getMagnitude());
break;
default:
throw new TuplesException(
"Failed to determine current statement position.");
}
return true;
}
/**
* Creates a Literal node and localizes it for the given magnitude.
*
* @param mag double
* @throws TuplesException
* @return long
*/
public long localizeMagnitude(double mag) throws TuplesException {
try {
Literal literal = new LiteralImpl(mag);
return resolverSession.localizePersistent(literal);
}
catch (LocalizeException localException) {
throw new TuplesException("Failed to localize magnitude.",
localException);
}
}
/**
* calls next on the Tuples and loads the next X & Y coordinates
*
* @return boolean
* @throws TuplesException
*/
public boolean nextInTuples() throws TuplesException {
//reset 'old' coords
xCoord = null;
yCoord = null;
distance = null;
//are there any more?
boolean next = points.next();
if (next) {
//get 'X'
long x = points.getColumnValue(xIndex);
long xlat = points.getColumnValue(xlatIndex);
long xlong = points.getColumnValue(xlongIndex);
xCoord = new LocalGISCoordinate(x, xlat, xlong, resolverSession);
//get 'Y'
long y = points.getColumnValue(yIndex);
long ylat = points.getColumnValue(ylatIndex);
long ylong = points.getColumnValue(ylongIndex);
yCoord = new LocalGISCoordinate(y, ylat, ylong, resolverSession);
//determine distance
distance = new LocalGISDistance(xCoord, yCoord, resolverSession,
calculator);
}
return next;
}
/**
* close
*
* @throws TuplesException
*/
public void close() throws TuplesException {
points.close();
}
/**
* Returns a new GISDistanceStatements using a copy of this Statements Tuples.
* @return Object
*/
public Object clone() {
try {
GISDistanceStatements gds = (GISDistanceStatements)super.clone();
gds.points = (Tuples)points.clone();
return gds;
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Failed to clone GISDistanceStatements.", e);
}
}
}