Java tutorial
package org.apache.rya.indexing.mongodb.geo; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import org.apache.rya.api.domain.RyaStatement; import org.apache.rya.api.resolver.RyaToRdfConversions; import org.apache.rya.indexing.accumulo.geo.GeoParseUtils; import org.apache.rya.indexing.mongodb.IndexingMongoDBStorageStrategy; import org.bson.Document; import org.openrdf.model.Statement; import org.openrdf.query.MalformedQueryException; import com.mongodb.BasicDBObject; import com.mongodb.BasicDBObjectBuilder; import com.mongodb.DBCollection; import com.mongodb.DBObject; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKTReader; public class GeoMongoDBStorageStrategy extends IndexingMongoDBStorageStrategy { private static final Logger LOG = Logger.getLogger(GeoMongoDBStorageStrategy.class); private static final String GEO = "location"; public enum GeoQueryType { INTERSECTS { @Override public String getKeyword() { return "$geoIntersects"; } }, WITHIN { @Override public String getKeyword() { return "$geoWithin"; } }, EQUALS { @Override public String getKeyword() { return "$near"; } }, NEAR { @Override public String getKeyword() { return "$near"; } }; public abstract String getKeyword(); } public static class GeoQuery { private final GeoQueryType queryType; private final Geometry geo; private final Double maxDistance; private final Double minDistance; public GeoQuery(final GeoQueryType queryType, final Geometry geo) { this(queryType, geo, 0, 0); } public GeoQuery(final GeoQueryType queryType, final Geometry geo, final double maxDistance, final double minDistance) { this.queryType = queryType; this.geo = geo; this.maxDistance = maxDistance; this.minDistance = minDistance; } public GeoQueryType getQueryType() { return queryType; } public Geometry getGeo() { return geo; } public Double getMaxDistance() { return maxDistance; } public Double getMinDistance() { return minDistance; } } private final Double maxDistance; public GeoMongoDBStorageStrategy(final Double maxDistance) { this.maxDistance = maxDistance; } @Override public void createIndices(final DBCollection coll) { coll.createIndex(new BasicDBObject(GEO, "2dsphere")); } public DBObject getQuery(final GeoQuery queryObj) throws MalformedQueryException { final Geometry geo = queryObj.getGeo(); final GeoQueryType queryType = queryObj.getQueryType(); if (queryType == GeoQueryType.WITHIN && !(geo instanceof Polygon)) { //They can also be applied to MultiPolygons, but those are not supported either. throw new MalformedQueryException("Mongo Within operations can only be performed on Polygons."); } else if (queryType == GeoQueryType.NEAR && !(geo instanceof Point)) { //They can also be applied to Point, but those are not supported either. throw new MalformedQueryException("Mongo near operations can only be performed on Points."); } BasicDBObject query; if (queryType.equals(GeoQueryType.EQUALS)) { if (geo.getNumPoints() == 1) { final List circle = new ArrayList(); circle.add(getPoint(geo)); circle.add(maxDistance); final BasicDBObject polygon = new BasicDBObject("$centerSphere", circle); query = new BasicDBObject(GEO, new BasicDBObject(GeoQueryType.WITHIN.getKeyword(), polygon)); } else { query = new BasicDBObject(GEO, getCorrespondingPoints(geo)); } } else if (queryType.equals(GeoQueryType.NEAR)) { final BasicDBObject geoDoc = new BasicDBObject("$geometry", getDBPoint(geo)); if (queryObj.getMaxDistance() != 0) { geoDoc.append("$maxDistance", queryObj.getMaxDistance()); } if (queryObj.getMinDistance() != 0) { geoDoc.append("$minDistance", queryObj.getMinDistance()); } query = new BasicDBObject(GEO, new BasicDBObject(queryType.getKeyword(), geoDoc)); } else { final BasicDBObject geoDoc = new BasicDBObject("$geometry", getCorrespondingPoints(geo)); query = new BasicDBObject(GEO, new BasicDBObject(queryType.getKeyword(), geoDoc)); } return query; } @Override public DBObject serialize(final RyaStatement ryaStatement) { // if the object is wkt, then try to index it // write the statement data to the fields try { final Statement statement = RyaToRdfConversions.convertStatement(ryaStatement); final Geometry geo = (new WKTReader()).read(GeoParseUtils.getWellKnownText(statement)); if (geo == null) { LOG.error("Failed to parse geo statement: " + statement.toString()); return null; } final BasicDBObject base = (BasicDBObject) super.serialize(ryaStatement); if (geo.getNumPoints() > 1) { base.append(GEO, getCorrespondingPoints(geo)); } else { base.append(GEO, getDBPoint(geo)); } return base; } catch (final ParseException e) { LOG.error("Could not create geometry for statement " + ryaStatement, e); return null; } } public Document getCorrespondingPoints(final Geometry geo) { //Polygons must be a 3 dimensional array. //polygons must be a closed loop final Document geoDoc = new Document(); if (geo instanceof Polygon) { final Polygon poly = (Polygon) geo; final List<List<List<Double>>> DBpoints = new ArrayList<>(); // outer shell of the polygon final List<List<Double>> ring = new ArrayList<>(); for (final Coordinate coord : poly.getExteriorRing().getCoordinates()) { ring.add(getPoint(coord)); } DBpoints.add(ring); // each hold in the polygon for (int ii = 0; ii < poly.getNumInteriorRing(); ii++) { final List<List<Double>> holeCoords = new ArrayList<>(); for (final Coordinate coord : poly.getInteriorRingN(ii).getCoordinates()) { holeCoords.add(getPoint(coord)); } DBpoints.add(holeCoords); } geoDoc.append("coordinates", DBpoints).append("type", "Polygon"); } else { final List<List<Double>> points = getPoints(geo); geoDoc.append("coordinates", points).append("type", "LineString"); } return geoDoc; } private List<List<Double>> getPoints(final Geometry geo) { final List<List<Double>> points = new ArrayList<>(); for (final Coordinate coord : geo.getCoordinates()) { points.add(getPoint(coord)); } return points; } public Document getDBPoint(final Geometry geo) { return new Document().append("coordinates", getPoint(geo)).append("type", "Point"); } private List<Double> getPoint(final Coordinate coord) { final List<Double> point = new ArrayList<>(); point.add(coord.x); point.add(coord.y); return point; } private List<Double> getPoint(final Geometry geo) { final List<Double> point = new ArrayList<>(); point.add(geo.getCoordinate().x); point.add(geo.getCoordinate().y); return point; } }