package org.opendatasoft.elasticsearch.plugin; import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.hash.MurmurHash3; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.geom.MultiPolygon; import org.locationtech.jts.geom.CoordinateList; import org.locationtech.jts.geom.GeometryCollection; import org.locationtech.jts.geom.LinearRing; import org.locationtech.jts.io.ParseException; import org.locationtech.jts.io.WKBReader; import org.locationtech.jts.io.WKBWriter; import org.locationtech.jts.io.WKTWriter; import org.locationtech.jts.io.geojson.GeoJsonWriter; import org.opendatasoft.elasticsearch.search.aggregations.bucket.geoshape.InternalGeoShape; import java.util.Arrays; import java.util.List; public class GeoUtils { public static long getHashFromWKB(BytesRef wkb) { return MurmurHash3.hash128(wkb.bytes, wkb.offset, wkb.length, 0, new MurmurHash3.Hash128()).h1; } public static List<GeoPoint> getBboxFromCoords(Coordinate[] coords) { GeoPoint topLeft = new GeoPoint( org.elasticsearch.common.geo.GeoUtils.normalizeLat(coords[0].y), org.elasticsearch.common.geo.GeoUtils.normalizeLon(coords[0].x) ); GeoPoint bottomRight = new GeoPoint( org.elasticsearch.common.geo.GeoUtils.normalizeLat(coords[2].y), org.elasticsearch.common.geo.GeoUtils.normalizeLon(coords[2].x) ); return Arrays.asList(topLeft, bottomRight); } public static GeoPoint getCentroidFromGeom(Geometry geom) { Geometry geom_centroid = geom.getCentroid(); return new GeoPoint(geom_centroid.getCoordinate().y, geom_centroid.getCoordinate().x); } // Return true if wkb is a point // http://en.wikipedia.org/wiki/Well-known_text#Well-known_binary public static boolean wkbIsPoint(byte[] wkb) { if (wkb.length < 5) { return false; } // Big endian or little endian shape representation if (wkb[0] == 0) { return wkb[1] == 0 && wkb[2] == 0 && wkb[3] == 0 && wkb[4] == 1; } else { return wkb[1] == 1 && wkb[2] == 0 && wkb[3] == 0 && wkb[4] == 0; } } public static double getMeterByPixel(int zoom, double lat) { return (org.elasticsearch.common.geo.GeoUtils.EARTH_EQUATOR / 256) * (Math.cos(Math.toRadians(lat)) / Math.pow(2, zoom)); } public static double getDecimalDegreeFromMeter(double meter) { return meter * 360 / org.elasticsearch.common.geo.GeoUtils.EARTH_EQUATOR; } public static double getDecimalDegreeFromMeter(double meter, double latitude) { return meter * 360 / (org.elasticsearch.common.geo.GeoUtils.EARTH_EQUATOR * Math.cos(Math.toRadians(latitude))); } public static double getToleranceFromZoom(int zoom) { /* This is a simplified formula for double meterByPixel = GeoUtils.getMeterByPixel(zoom, lat); double tol = GeoUtils.getDecimalDegreeFromMeter(meterByPixel, lat); */ return 360 / (256 * Math.pow(2, zoom)); } public static String exportWkbTo(BytesRef wkb, InternalGeoShape.OutputFormat output_format, GeoJsonWriter geoJsonWriter) throws ParseException { switch (output_format) { case WKT: Geometry geom = new WKBReader().read(wkb.bytes); return new WKTWriter().write(geom); case WKB: return WKBWriter.toHex(wkb.bytes); default: Geometry geo = new WKBReader().read(wkb.bytes); return geoJsonWriter.write(geo); } } public static String exportGeoTo(Geometry geom, InternalGeoShape.OutputFormat outputFormat, GeoJsonWriter geoJsonWriter) { switch (outputFormat) { case WKT: return new WKTWriter().write(geom); case WKB: return WKBWriter.toHex(new WKBWriter().write(geom)); default: return geoJsonWriter.write(geom); } } public static Geometry removeDuplicateCoordinates(Geometry geom) { if (geom.isEmpty()) { return geom; } if (geom instanceof Polygon) { return removeDuplicateCoordinates((Polygon) geom); } if (geom instanceof MultiPolygon) { return removeDuplicateCoordinates((MultiPolygon) geom); } if (geom instanceof GeometryCollection) { return removeDuplicateCoordinates((GeometryCollection) geom); } return geom; } public static Polygon removeDuplicateCoordinates(Polygon polygon) { LinearRing polygonShell = removeDuplicateCoordinates((LinearRing) polygon.getExteriorRing()); LinearRing[] holes = new LinearRing[polygon.getNumInteriorRing()]; for (int i = 0; i < polygon.getNumInteriorRing(); i++) { holes[i] = removeDuplicateCoordinates((LinearRing) polygon.getInteriorRingN(i)); } return polygon.getFactory().createPolygon(polygonShell, holes); } public static LinearRing removeDuplicateCoordinates(LinearRing linearRing) { return linearRing.getFactory().createLinearRing( new CoordinateList(linearRing.getCoordinates(), false).toCoordinateArray() ); } public static MultiPolygon removeDuplicateCoordinates(MultiPolygon multiPolygon) { Polygon[] polygons = new Polygon[multiPolygon.getNumGeometries()]; for (int i = 0; i < multiPolygon.getNumGeometries(); i++) { polygons[i] = (Polygon) removeDuplicateCoordinates(multiPolygon.getGeometryN(i)); } return multiPolygon.getFactory().createMultiPolygon(polygons); } public static GeometryCollection removeDuplicateCoordinates(GeometryCollection geometryCollection) { Geometry[] geometries = new Geometry[geometryCollection.getNumGeometries()]; for (int i = 0; i < geometryCollection.getNumGeometries(); i++) { geometries[i] = removeDuplicateCoordinates(geometryCollection.getGeometryN(i)); } return geometryCollection.getFactory().createGeometryCollection(geometries); } }