/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2015, Open Source Geospatial Foundation (OSGeo) * (C) 2014-2015, Boundless * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 GNU * Lesser General Public License for more details. */ package org.geotools.data.mongodb; import java.util.ArrayList; import java.util.List; import org.geotools.geometry.jts.Geometries; import com.mongodb.BasicDBList; import com.mongodb.BasicDBObjectBuilder; import com.mongodb.DBObject; import com.vividsolutions.jts.algorithm.CGAlgorithms; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.CoordinateSequence; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryCollection; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.LinearRing; import com.vividsolutions.jts.geom.MultiLineString; import com.vividsolutions.jts.geom.MultiPoint; import com.vividsolutions.jts.geom.MultiPolygon; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; public class MongoGeometryBuilder { GeometryFactory geometryFactory; // MongoDB 2.4 doesn't support the multi-geometry // GeoJSON types. A lot of multi-geometry instances encode // a single geometry. This flag will allow the conversion // of JTS multi-geometry types encoding a single geometry // to their single geometry analog. This should ease some of // the pain... boolean opportunisticMultiGeometryCoversion = true; public MongoGeometryBuilder() { this(new GeometryFactory()); } public MongoGeometryBuilder(GeometryFactory geomFactory) { this.geometryFactory = geomFactory; } public Geometry toGeometry(DBObject obj) { if (obj == null) { return null; } String type = (String)obj.get("type"); Geometries g = Geometries.getForName(type); if (g == null) { throw new IllegalArgumentException("Unable to create geometry of type: " + type); } BasicDBList list = (BasicDBList) obj.get("coordinates"); switch(g) { case POINT: return toPoint(list); case LINESTRING: return toLineString(list); case POLYGON: return toPolygon(list); case MULTIPOINT: return toMultiPoint(list); case MULTILINESTRING: return toMultiLineString(list); case MULTIPOLYGON: return toMultiPolygon(list); case GEOMETRYCOLLECTION: return toGeometryCollection((BasicDBList) obj.get("geometries")); default: throw new IllegalArgumentException("Unknown geometry type: " + type); } } public DBObject toObject(Envelope envelope) { return toObject(geometryFactory.toGeometry(envelope)); } public DBObject toObject(Geometry geom) { Geometries g = Geometries.get(geom); switch(g) { case POINT: return toObject((Point)geom); case LINESTRING: return toObject((LineString)geom); case POLYGON: return toObject((Polygon)geom); case MULTIPOINT: return toObject((MultiPoint)geom); case MULTILINESTRING: return toObject((MultiLineString)geom); case MULTIPOLYGON: return toObject((MultiPolygon)geom); case GEOMETRYCOLLECTION: return toObject((GeometryCollection) geom); default: throw new IllegalArgumentException("Unknown geometry type: " + geom); } } public GeometryCollection toGeometryCollection(BasicDBList obj) { List<Geometry> geoms = new ArrayList<Geometry>(); for (Object o : obj) { geoms.add(toGeometry((DBObject)o)); // JG: changed from toGeometry( obj ) } return geometryFactory.createGeometryCollection(geoms.toArray(new Geometry[geoms.size()])); } public DBObject toObject(GeometryCollection gc) { return null; } public MultiPolygon toMultiPolygon(List<?> list) { List<Polygon> polys = new ArrayList<Polygon>(); for (Object o : list) { polys.add(toPolygon((List<?>)o)); } return geometryFactory.createMultiPolygon(polys.toArray(new Polygon[polys.size()])); } public DBObject toObject(MultiPolygon mp) { if (opportunisticMultiGeometryCoversion && mp.getNumGeometries() == 1) { return toObject((Polygon)mp.getGeometryN(0)); } List<Object> l = new BasicDBList(); for (int i = 0; i < mp.getNumGeometries(); i++) { l.add(toList(((Polygon)mp.getGeometryN(i)))); } return BasicDBObjectBuilder.start() .add("type", "MultiPolygon") .add("coordinates", l) .get(); } public MultiLineString toMultiLineString(List<?> list) { List<LineString> lines = new ArrayList<LineString>(); for (Object o : list) { lines.add(toLineString((List<?>)o)); } return geometryFactory.createMultiLineString(lines.toArray(new LineString[lines.size()])); } public DBObject toObject(MultiLineString ml) { if (opportunisticMultiGeometryCoversion && ml.getNumGeometries() == 1) { return toObject((LineString)ml.getGeometryN(0)); } List<Object> l = new BasicDBList(); for (int i = 0; i < ml.getNumGeometries(); i++) { l.add(toList(((LineString)ml.getGeometryN(i)).getCoordinateSequence())); } return BasicDBObjectBuilder.start() .add("type", "MultiLineString") .add("coordinates", l) .get(); } public MultiPoint toMultiPoint(List<?> list) { List<Point> points = new ArrayList<Point>(); for (Object o : list) { points.add(toPoint((List<?>)o)); } return geometryFactory.createMultiPoint(points.toArray(new Point[points.size()])); } public DBObject toObject(MultiPoint mp) { if (opportunisticMultiGeometryCoversion && mp.getNumGeometries() == 1) { return toObject((Point)mp.getGeometryN(0)); } return BasicDBObjectBuilder.start() .add("type", "MultiPoint") .add("coordinates", toList(mp.getCoordinates())) .get(); } public Polygon toPolygon(List<?> list) { LinearRing outer = (LinearRing) toLineString((List<?>)list.get(0)); List<LinearRing> inner = new ArrayList<LinearRing>(); for (int i = 1; i < list.size(); i++) { inner.add((LinearRing) toLineString((List<?>)list.get(i))); } return geometryFactory.createPolygon(outer, inner.toArray(new LinearRing[inner.size()])); } public DBObject toObject(Polygon p) { return BasicDBObjectBuilder.start() .add("type", "Polygon") .add("coordinates", toList(p)) .get(); } public LineString toLineString(List<?> list) { List<Coordinate> coordList = new ArrayList<Coordinate>(list.size()); for (Object o : list) { coordList.add(toCoordinate((List<?>)o)); } Coordinate[] coords = coordList.toArray(new Coordinate[coordList.size()]); if (coords.length > 3 && coords[0].equals(coords[coords.length-1])) { return geometryFactory.createLinearRing(coords); } return geometryFactory.createLineString(coords); } public DBObject toObject(LineString l) { return BasicDBObjectBuilder.start() .add("type", "LineString") .add("coordinates", toList(l.getCoordinateSequence())) .get(); } public Point toPoint(List<?> list) { return geometryFactory.createPoint(toCoordinate(list)); } public DBObject toObject(Point p) { return BasicDBObjectBuilder.start() .add("type", "Point") .add("coordinates", toList(p.getCoordinate())) .get(); } public Coordinate toCoordinate(List<?> list) { double x = ((Number)list.get(0)).doubleValue(); double y = ((Number)list.get(1)).doubleValue(); return new Coordinate(x, y); } List<?> toList(Coordinate c) { BasicDBList l = new BasicDBList(); l.add(c.x); l.add(c.y); return l; } List<?> toList(CoordinateSequence cs) { BasicDBList l = new BasicDBList(); for (int i = 0; i < cs.size(); i++) { BasicDBList m = new BasicDBList(); m.add(cs.getX(i)); m.add(cs.getY(i)); l.add(m); } return l; } List<?> toList(Coordinate[] cs) { BasicDBList l = new BasicDBList(); for (int i = 0; i < cs.length; i++) { BasicDBList m = new BasicDBList(); m.add(cs[i].x); m.add(cs[i].y); l.add(m); } return l; } List<?> toList(Polygon p) { BasicDBList l = new BasicDBList(); if (!CGAlgorithms.isCCW(p.getExteriorRing().getCoordinates())) { l.add(toList(p.getExteriorRing().reverse().getCoordinates())); } else { l.add(toList(p.getExteriorRing().getCoordinateSequence())); } for (int i = 0; i < p.getNumInteriorRing(); i++) { l.add(toList(p.getInteriorRingN(i).getCoordinateSequence())); } return l; } }