From 7a725db2355e0c379db0f614eccc24976746bdcc Mon Sep 17 00:00:00 2001 From: Warwick Dufour Date: Mon, 23 Apr 2018 10:42:53 +0100 Subject: [PATCH] Handle Elasticgeo Circles by approximating the circle boundary using interpolation with a great circle line radius. --- .../data/elasticsearch/ElasticParserUtil.java | 185 ++++++++++++++--- .../elasticsearch/FilterToElasticHelper.java | 19 +- .../elasticsearch/ElasticParserUtilTest.java | 193 +++++++++++------- 3 files changed, 287 insertions(+), 110 deletions(-) diff --git a/modules/unsupported/elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticParserUtil.java b/modules/unsupported/elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticParserUtil.java index 3255059271e..d16eb1e0ca0 100644 --- a/modules/unsupported/elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticParserUtil.java +++ b/modules/unsupported/elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticParserUtil.java @@ -27,9 +27,12 @@ import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.LinearRing; import com.vividsolutions.jts.geom.Polygon; +import java.awt.geom.Point2D; +import org.geotools.referencing.GeodeticCalculator; +import org.geotools.referencing.datum.DefaultEllipsoid; /** - * Utilities for parsing Elasticsearch document source and field content to + * Utilities for parsing Elasticsearch document source and field content to * extract values and create geometries. * */ @@ -38,15 +41,30 @@ public class ElasticParserUtil { private final static Logger LOGGER = Logging.getLogger(ElasticParserUtil.class); private static final Pattern GEO_POINT_PATTERN; + static { GEO_POINT_PATTERN = Pattern.compile("\\s*([-+]?\\d*\\.?\\d*)[^-+\\d\\.]+([-+]?\\d*\\.?\\d*)\\s*"); } private static final Pattern GEO_HASH_PATTERN; + static { GEO_HASH_PATTERN = Pattern.compile("[0123456789bcdefghjkmnpqrstuvwxyz]+"); } + private static final Pattern ELASTIC_DISTANCE_PATTERN; + + static { + ELASTIC_DISTANCE_PATTERN = Pattern.compile("([0-9]+(\\.[0-9]+)?)([a-zA-Z]*)"); + } + + private static final double CIRCLE_INTERPOLATION_INTERVAL = 500.0; + private static final int MAX_CIRCLE_POINTS = 500; + private static final int MIN_CIRCLE_POINTS = 40; + private static final double MIN_CIRCLE_RADIUS_M = 0.001; + + private final GeodeticCalculator geodeticCalculator; + private final GeometryFactory geometryFactory; private boolean unsupportedEncodingMessage; @@ -54,12 +72,14 @@ public class ElasticParserUtil { public ElasticParserUtil() { this.geometryFactory = new GeometryFactory(); this.unsupportedEncodingMessage = false; + this.geodeticCalculator = new GeodeticCalculator(DefaultEllipsoid.WGS84); } /** * Create point geometry given geo_point or geo_shape definition. GeoPoint * can be defined by string, geohash, coordinate array or properties map. * GeoShape is defined by properties map. + * * @param obj GeoPoint or GeoShape definition * @return Geometry */ @@ -73,28 +93,28 @@ public Geometry createGeometry(Object obj) { // coordinate final double y = Double.valueOf(listMatcher.group(1)); final double x = Double.valueOf(listMatcher.group(2)); - geometry = geometryFactory.createPoint(new Coordinate(x,y)); + geometry = geometryFactory.createPoint(new Coordinate(x, y)); } else if (GEO_HASH_PATTERN.matcher((String) obj).matches()) { // geohash final LatLong latLon = GeoHash.decodeHash((String) obj); final Coordinate geoPoint = new Coordinate(latLon.getLon(), latLon.getLat()); final double lat = geoPoint.y; final double lon = geoPoint.x; - geometry = geometryFactory.createPoint(new Coordinate(lon,lat)); + geometry = geometryFactory.createPoint(new Coordinate(lon, lat)); } else { geometry = null; } - } else if (obj instanceof List && ((List) obj).size()==2) { + } else if (obj instanceof List && ((List) obj).size() == 2) { // geo_point by coordinate array final List values = (List) obj; if (Number.class.isAssignableFrom(values.get(0).getClass())) { final double x = ((Number) values.get(0)).doubleValue(); final double y = ((Number) values.get(1)).doubleValue(); - geometry = geometryFactory.createPoint(new Coordinate(x,y)); + geometry = geometryFactory.createPoint(new Coordinate(x, y)); } else if (values.get(0) instanceof String) { final double x = Double.valueOf((String) values.get(0)); final double y = Double.valueOf((String) values.get(1)); - geometry = geometryFactory.createPoint(new Coordinate(x,y)); + geometry = geometryFactory.createPoint(new Coordinate(x, y)); } else { geometry = null; } @@ -108,12 +128,13 @@ public Geometry createGeometry(Object obj) { } /** - * Create geometry given property map defining geo_shape type and coordinates - * or geo_point lat and lon. + * Create geometry given property map defining geo_shape type and + * coordinates or geo_point lat and lon. + * * @param properties Properties * @return Geometry */ - @SuppressWarnings({ "rawtypes", "unchecked" }) + @SuppressWarnings({"rawtypes", "unchecked"}) public Geometry createGeometry(final Map properties) { final Geometry geometry; switch (String.valueOf(properties.get("type")).toUpperCase()) { @@ -123,83 +144,99 @@ public Geometry createGeometry(final Map properties) { final Coordinate coordinate = createCoordinate(posList); geometry = geometryFactory.createPoint(coordinate); break; - } case "LINESTRING": { + } + case "LINESTRING": { final List> posList; posList = (List) properties.get("coordinates"); final Coordinate[] coordinates = createCoordinates(posList); geometry = geometryFactory.createLineString(coordinates); break; - } case "POLYGON": { + } + case "POLYGON": { final List>> posList; posList = (List) properties.get("coordinates"); geometry = createPolygon(posList); break; - } case "MULTIPOINT": { + } + case "MULTIPOINT": { final List> posList; posList = (List) properties.get("coordinates"); final Coordinate[] coordinates = createCoordinates(posList); geometry = geometryFactory.createMultiPoint(coordinates); break; - } case "MULTILINESTRING": { + } + case "MULTILINESTRING": { final List>> posList; posList = (List) properties.get("coordinates"); final LineString[] lineStrings = new LineString[posList.size()]; - for (int i=0; i>>> posList; posList = (List) properties.get("coordinates"); final Polygon[] polygons = new Polygon[posList.size()]; - for (int i=0; i> list; + } + case "GEOMETRYCOLLECTION": { + final List> list; list = (List) properties.get("geometries"); final Geometry[] geometries = new Geometry[list.size()]; - for (int i=0; i> posList; posList = (List) properties.get("coordinates"); final Coordinate[] coords = createCoordinates(posList); final Envelope envelope = new Envelope(coords[0], coords[1]); geometry = geometryFactory.toGeometry(envelope); break; - } default: + } + case "CIRCLE": { + final List posList; + posList = (List) properties.get("coordinates"); + final String radius = (String) properties.get("radius"); + final Coordinate coordinate = createCoordinate(posList); + geometry = createCircle(coordinate, radius); + break; + } + default: // check if this is a geo_point final Object latObj = properties.get("lat"); final Object lonObj = properties.get("lon"); if (latObj != null && lonObj != null) { final Double lat; if (latObj instanceof Number) { - lat = ((Number)latObj).doubleValue(); + lat = ((Number) latObj).doubleValue(); } else if (latObj instanceof String) { - lat = new Double((String)latObj); + lat = new Double((String) latObj); } else { lat = null; } final Double lon; if (lonObj instanceof Number) { - lon = ((Number)lonObj).doubleValue(); + lon = ((Number) lonObj).doubleValue(); } else if (lonObj instanceof String) { - lon = new Double((String)lonObj); + lon = new Double((String) lonObj); } else { lon = null; } if (lat != null && lon != null) { - geometry = geometryFactory.createPoint(new Coordinate(lon,lat)); + geometry = geometryFactory.createPoint(new Coordinate(lon, lat)); } else { geometry = null; } @@ -214,17 +251,17 @@ public Geometry createGeometry(final Map properties) { private Polygon createPolygon(final List>> posList) { final Coordinate[] shellCoordinates = createCoordinates(posList.get(0)); final LinearRing shell = geometryFactory.createLinearRing(shellCoordinates); - final LinearRing[] holes = new LinearRing[posList.size()-1]; - for (int i=1; i> posList) { final Coordinate[] coordinates = new Coordinate[posList.size()]; - for (int i=0; i posList) { x = Double.valueOf(posList.get(0).toString()); y = Double.valueOf(posList.get(1).toString()); } - return new Coordinate(x,y); + return new Coordinate(x, y); } /** * Read field from document source. + * * @param source Source * @param name Field to extract. * @return List of values or empty list if not found @@ -271,7 +309,7 @@ private void readField(Object entry, List keys, List values) { readField(object, keys, values); } } else if (!keys.isEmpty() && Map.class.isAssignableFrom(entry.getClass())) { - final Object nextEntry = ((Map) entry).get(keys.get(0)); + final Object nextEntry = ((Map) entry).get(keys.get(0)); final List newKeys = keys.subList(1, keys.size()); readField(nextEntry, newKeys, values); } else if (entry != null) { @@ -285,7 +323,8 @@ public static boolean isGeoPointFeature(Map map) { if (map.size() == 2 && map.containsKey("coordinates")) { try { result = "geo_point".equals(((Map) map.get("coordinates")).get("type")); - } catch (Exception e) { } + } catch (Exception e) { + } } return result; } @@ -311,4 +350,84 @@ public static String urlDecode(String value) { return value; } + /** + * Interpolates a JTS polygon from a circle definition. Assumes WGS84 CRS. + * + * @param centreCoord The centre of the circle + * @param radius Consists of a numeric value with a units string appended to + * it. + * @return A polygon that is an interpolated form of a circle + */ + private Geometry createCircle(Coordinate centreCoord, String radius) { + + if (centreCoord == null) { + return null; + } + + final double radM; + try { + radM = convertToMeters(radius); + } + catch(Exception e) { + return null; + } + + // Reject circles with radii below an arbitrary minimum. + if (radM < MIN_CIRCLE_RADIUS_M) { + return null; + } + + // Interpolate a circle on the surface of the ellipsoid at an arbitrary + // interval and then ensure that the number of interpolated points are + // within a specified range + final double circumferance = radM * 2.0 * Math.PI; + int numPoints = (int) (circumferance / CIRCLE_INTERPOLATION_INTERVAL); + numPoints = Math.max(MIN_CIRCLE_POINTS, numPoints); + numPoints = Math.min(MAX_CIRCLE_POINTS, numPoints); + final double angularIncrement = 360.0 / numPoints; + geodeticCalculator.setStartingGeographicPoint(centreCoord.x, centreCoord.y); + final Coordinate[] linearRingCoords = new Coordinate[numPoints + 1]; + double angle = 0.0; + for (int i = 0; i < numPoints; i++) { + geodeticCalculator.setDirection(angle, radM); + Point2D point2D = geodeticCalculator.getDestinationGeographicPoint(); + linearRingCoords[i] = new Coordinate(point2D.getX(), point2D.getY()); + angle += angularIncrement; + } + linearRingCoords[numPoints] = linearRingCoords[0]; + final LinearRing linearRing = geometryFactory.createLinearRing(linearRingCoords); + return geometryFactory.createPolygon(linearRing); + } + + /** + * Converts an Elasticsearch distance string consisting of value and unit + * into metres. + * @param distanceWithUnit String of the form of a decimal number + * concatenated with a unit string as defined in + * {@link FilterToElasticHelper#UNITS_MAP}. If the unit string is missing + * then the number is assumed to be metres. + * @return distance in metres. + * @throws IllegalArgumentException + */ + static final double convertToMeters(String distanceWithUnit) throws IllegalArgumentException { + if (distanceWithUnit == null || distanceWithUnit.isEmpty()) { + throw new IllegalArgumentException("Null of zero length distance string argument"); + } + final Matcher matcher = ELASTIC_DISTANCE_PATTERN.matcher(distanceWithUnit); + if (matcher.matches()) { + final double distance = Double.valueOf(matcher.group(1)); + final String unit = matcher.group(3); + Double conversion = FilterToElasticHelper.UNITS_MAP.get(unit); + if (conversion == null) { + if (unit != null && ! unit.isEmpty()) { + throw new IllegalArgumentException("Illegal unit: " + unit); + } else { + conversion = new Double(1.0); + } + } + return distance * conversion; + } else { + throw new IllegalArgumentException("Distance string argument has incorrect format"); + } + } } diff --git a/modules/unsupported/elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/FilterToElasticHelper.java b/modules/unsupported/elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/FilterToElasticHelper.java index 2972a8f1123..f12c76f971e 100644 --- a/modules/unsupported/elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/FilterToElasticHelper.java +++ b/modules/unsupported/elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/FilterToElasticHelper.java @@ -69,21 +69,28 @@ class FilterToElasticHelper { /** * Conversion factor from common units to meter */ - protected static final Map UNITS_MAP = new HashMap() { + static final Map UNITS_MAP = new HashMap() { private static final long serialVersionUID = 1L; { + // Metric + put("millimeter", 0.001); + put("mm", 0.001); + put("cm", 0.01); + put("m", 1.0); put("kilometers", 1000.0); put("kilometer", 1000.0); - put("mm", 0.001); - put("millimeter", 0.001); + put("km", 1000.0); + // Other + put("in", 0.0254); + put("ft", 0.3048); + put("feet", 0.3048); + put("yd", 0.9144); put("mi", 1609.344); put("miles", 1609.344); put("NM", 1852d); - put("feet", 0.3048); - put("ft", 0.3048); - put("in", 0.0254); + put("nmi", 1852d); } }; diff --git a/modules/unsupported/elasticsearch/src/test/java/mil/nga/giat/data/elasticsearch/ElasticParserUtilTest.java b/modules/unsupported/elasticsearch/src/test/java/mil/nga/giat/data/elasticsearch/ElasticParserUtilTest.java index 9b20e0f1333..d202411f9f4 100644 --- a/modules/unsupported/elasticsearch/src/test/java/mil/nga/giat/data/elasticsearch/ElasticParserUtilTest.java +++ b/modules/unsupported/elasticsearch/src/test/java/mil/nga/giat/data/elasticsearch/ElasticParserUtilTest.java @@ -15,8 +15,6 @@ import java.util.Map; import java.util.Random; -import mil.nga.giat.data.elasticsearch.ElasticParserUtil; - import org.junit.Before; import org.junit.Test; @@ -41,7 +39,7 @@ public class ElasticParserUtilTest { private GeometryFactory geometryFactory; - private Map properties; + private Map properties; private Random rand; @@ -58,62 +56,62 @@ public void setUp() throws ReflectiveOperationException { @Test public void testParseGeoPointPatternForNegatives() { - final double lat = rand.nextDouble()*90-90; - final double lon = rand.nextDouble()*180-180; + final double lat = rand.nextDouble() * 90 - 90; + final double lon = rand.nextDouble() * 180 - 180; final String value = lat + "," + lon; final Geometry geom = parserUtil.createGeometry(value); - assertTrue(geom.equals(geometryFactory.createPoint(new Coordinate(lon,lat)))); + assertTrue(geom.equals(geometryFactory.createPoint(new Coordinate(lon, lat)))); } @Test public void testGeoPointPatternForFractions() { - final double lat = rand.nextDouble()*2-1; - final double lon = rand.nextDouble()*2-1; + final double lat = rand.nextDouble() * 2 - 1; + final double lon = rand.nextDouble() * 2 - 1; final String value = (lat + "," + lon).replace("0.", "."); final Geometry geom = parserUtil.createGeometry(value); - assertTrue(geom.equals(geometryFactory.createPoint(new Coordinate(lon,lat)))); + assertTrue(geom.equals(geometryFactory.createPoint(new Coordinate(lon, lat)))); } @Test public void testGeoPointPatternForWholeValues() { final Geometry geom = parserUtil.createGeometry("45,90"); - assertTrue(geom.equals(geometryFactory.createPoint(new Coordinate(90,45)))); + assertTrue(geom.equals(geometryFactory.createPoint(new Coordinate(90, 45)))); } @Test public void testGeoPointPatternWithSpace() { final Geometry geom = parserUtil.createGeometry("45, 90"); - assertTrue(geom.equals(geometryFactory.createPoint(new Coordinate(90,45)))); + assertTrue(geom.equals(geometryFactory.createPoint(new Coordinate(90, 45)))); } @Test public void testGeoPointAsDoubleProperties() { - final double lat = rand.nextDouble()*90-90; - final double lon = rand.nextDouble()*180-180; + final double lat = rand.nextDouble() * 90 - 90; + final double lon = rand.nextDouble() * 180 - 180; properties.put("lat", lat); properties.put("lon", lon); final Geometry geometry = parserUtil.createGeometry(properties); - assertTrue(geometry.equals(geometryFactory.createPoint(new Coordinate(lon,lat)))); + assertTrue(geometry.equals(geometryFactory.createPoint(new Coordinate(lon, lat)))); } @Test public void testGeoPointAsIntegerProperties() { - final int lat = rand.nextInt(180)-90; - final int lon = rand.nextInt(360)-180; + final int lat = rand.nextInt(180) - 90; + final int lon = rand.nextInt(360) - 180; properties.put("lat", lat); properties.put("lon", lon); final Geometry geometry = parserUtil.createGeometry(properties); - assertTrue(geometry.equals(geometryFactory.createPoint(new Coordinate(lon,lat)))); + assertTrue(geometry.equals(geometryFactory.createPoint(new Coordinate(lon, lat)))); } @Test public void testGeoPointAsStringProperties() { - final double lat = rand.nextDouble()*90-90; - final double lon = rand.nextDouble()*180-180; + final double lat = rand.nextDouble() * 90 - 90; + final double lon = rand.nextDouble() * 180 - 180; properties.put("lat", String.valueOf(lat)); properties.put("lon", String.valueOf(lon)); final Geometry geometry = parserUtil.createGeometry(properties); - assertTrue(geometry.equals(geometryFactory.createPoint(new Coordinate(lon,lat)))); + assertTrue(geometry.equals(geometryFactory.createPoint(new Coordinate(lon, lat)))); } @Test @@ -121,59 +119,59 @@ public void testGeoPointAsInvalidProperties() { properties.put("lat", true); properties.put("lon", true); final Geometry geometry = parserUtil.createGeometry(properties); - assertTrue(geometry==null); + assertTrue(geometry == null); } @Test public void testGeoPointAsUnrecognizedProperties() { - final double lat = rand.nextDouble()*90-90; - final double lon = rand.nextDouble()*180-180; + final double lat = rand.nextDouble() * 90 - 90; + final double lon = rand.nextDouble() * 180 - 180; properties.put("latD", lat); properties.put("lonD", lon); final Geometry geometry = parserUtil.createGeometry(properties); - assertTrue(geometry==null); + assertTrue(geometry == null); } @Test public void testGeoPointAsStringArray() { - final double lat = rand.nextDouble()*90-90; - final double lon = rand.nextDouble()*180-180; - final Geometry geometry = parserUtil.createGeometry(Arrays.asList(new String[] { - String.valueOf(lon),String.valueOf(lat)})); - assertTrue(geometry.equals(geometryFactory.createPoint(new Coordinate(lon,lat)))); + final double lat = rand.nextDouble() * 90 - 90; + final double lon = rand.nextDouble() * 180 - 180; + final Geometry geometry = parserUtil.createGeometry(Arrays.asList(new String[]{ + String.valueOf(lon), String.valueOf(lat)})); + assertTrue(geometry.equals(geometryFactory.createPoint(new Coordinate(lon, lat)))); } @Test public void testGeoPointAsInvalidArray() { - final Geometry geometry = parserUtil.createGeometry(Arrays.asList(new Boolean[] {true,true})); - assertTrue(geometry==null); + final Geometry geometry = parserUtil.createGeometry(Arrays.asList(new Boolean[]{true, true})); + assertTrue(geometry == null); } @Test public void testGeoPointAsDoubleArray() { - final double lat = rand.nextDouble()*90-90; - final double lon = rand.nextDouble()*180-180; - final Geometry geometry = parserUtil.createGeometry(Arrays.asList(new Double[] {lon,lat})); - assertTrue(geometry.equals(geometryFactory.createPoint(new Coordinate(lon,lat)))); + final double lat = rand.nextDouble() * 90 - 90; + final double lon = rand.nextDouble() * 180 - 180; + final Geometry geometry = parserUtil.createGeometry(Arrays.asList(new Double[]{lon, lat})); + assertTrue(geometry.equals(geometryFactory.createPoint(new Coordinate(lon, lat)))); } @Test public void testGeoHash() { - final double lat = rand.nextDouble()*90-90; - final double lon = rand.nextDouble()*180-180; + final double lat = rand.nextDouble() * 90 - 90; + final double lon = rand.nextDouble() * 180 - 180; String geohash = GeoHash.encodeHash(lat, lon, 11); - final Geometry expected = geometryFactory.createPoint(new Coordinate(lon,lat)); + final Geometry expected = geometryFactory.createPoint(new Coordinate(lon, lat)); final Geometry actual = parserUtil.createGeometry(geohash); assertEquals(0, expected.distance(actual), 1e-5); } @Test public void testInvalidStringGeometry() { - final double lat = rand.nextDouble()*90-90; - final double lon = rand.nextDouble()*180-180; - assertTrue(parserUtil.createGeometry(String.valueOf(lat))==null); - assertTrue(parserUtil.createGeometry(lat + "," + lon + "," + 0)==null); - assertTrue(parserUtil.createGeometry("x:" + lat + "," + lon)==null); + final double lat = rand.nextDouble() * 90 - 90; + final double lon = rand.nextDouble() * 180 - 180; + assertTrue(parserUtil.createGeometry(String.valueOf(lat)) == null); + assertTrue(parserUtil.createGeometry(lat + "," + lon + "," + 0) == null); + assertTrue(parserUtil.createGeometry("x:" + lat + "," + lon) == null); } @Test @@ -185,7 +183,7 @@ public void testGeoShapePoint() throws JsonParseException, JsonMappingException, @Test public void testGeoShapePointString() throws JsonParseException, JsonMappingException, IOException { Point geom = rgb.createRandomPoint(); - final Map map = new HashMap<>(); + final Map map = new HashMap<>(); final List coords = new ArrayList<>(); coords.add(String.valueOf(geom.getX())); coords.add(String.valueOf(geom.getY())); @@ -206,6 +204,20 @@ public void testGeoShapePolygon() throws JsonParseException, JsonMappingExceptio assertTrue(parserUtil.createGeometry(rgb.toMap(geom)).equalsExact(geom, 1e-9)); } + @Test + public void testGeoShapeCircle() throws JsonParseException, JsonMappingException, IOException { + Map inputMap = new HashMap<>(); + inputMap.put("type", "circle"); + inputMap.put("radius", "5nmi"); + List posList = new ArrayList<>(); + posList.add("8.0"); + posList.add("35.0"); + inputMap.put("coordinates", posList); + Geometry geometry = parserUtil.createGeometry(inputMap); + + assertNotNull(geometry); + } + @Test public void testGeoShapeMultiPoint() throws JsonParseException, JsonMappingException, IOException { MultiPoint geom = rgb.createRandomMultiPoint(); @@ -241,14 +253,14 @@ public void testGeoShapeEnvelope() throws JsonParseException, JsonMappingExcepti @Test public void testUnrecognizedGeometry() { final Geometry geom = parserUtil.createGeometry(3.0); - assertTrue(geom==null); + assertTrue(geom == null); } @Test public void testReadStringField() { properties.put("attr", "value"); List values = parserUtil.readField(properties, "attr"); - assertTrue(values.size()==1); + assertTrue(values.size() == 1); assertTrue(values.get(0).equals("value")); } @@ -256,78 +268,117 @@ public void testReadStringField() { public void testReadNumericField() { properties.put("attr", 2.3); List values = parserUtil.readField(properties, "attr"); - assertTrue(values.size()==1); + assertTrue(values.size() == 1); assertTrue(values.get(0).equals(2.3)); } @Test public void testReadStringFieldWithConfuser() { - properties.put("parent1", new LinkedHashMap()); + properties.put("parent1", new LinkedHashMap()); ((Map) properties.get("parent1")).put("attr", "value2"); properties.put("attr", "value"); - properties.put("parent2", new LinkedHashMap()); + properties.put("parent2", new LinkedHashMap()); ((Map) properties.get("parent2")).put("attr", "value3"); List values = parserUtil.readField(properties, "attr"); - assertTrue(values.size()==1); + assertTrue(values.size() == 1); assertTrue(values.get(0).equals("value")); } @Test public void testReadInnerString() { - properties.put("parent", new LinkedHashMap()); + properties.put("parent", new LinkedHashMap()); ((Map) properties.get("parent")).put("attr", "value"); List values = parserUtil.readField(properties, "parent.attr"); - assertTrue(values.size()==1); + assertTrue(values.size() == 1); assertTrue(values.get(0).equals("value")); } @Test public void testReadInnerStringArray() { - properties.put("parent", new LinkedHashMap()); - ((Map) properties.get("parent")).put("attr", Arrays.asList(new String[] {"value1", "value2"})); + properties.put("parent", new LinkedHashMap()); + ((Map) properties.get("parent")).put("attr", Arrays.asList(new String[]{"value1", "value2"})); List values = parserUtil.readField(properties, "parent.attr"); - assertTrue(values.size()==2); + assertTrue(values.size() == 2); assertTrue(values.get(0).equals("value1")); assertTrue(values.get(1).equals("value2")); } @Test public void testReadStringFromObjectArray() { - properties.put("parent", new ArrayList>()); - ((List)properties.get("parent")).add(new LinkedHashMap()); - ((Map) ((List)properties.get("parent")).get(0)).put("attr", "value1"); - ((List)properties.get("parent")).add(new LinkedHashMap()); - ((Map) ((List)properties.get("parent")).get(1)).put("attr", "value2"); + properties.put("parent", new ArrayList>()); + ((List) properties.get("parent")).add(new LinkedHashMap()); + ((Map) ((List) properties.get("parent")).get(0)).put("attr", "value1"); + ((List) properties.get("parent")).add(new LinkedHashMap()); + ((Map) ((List) properties.get("parent")).get(1)).put("attr", "value2"); List values = parserUtil.readField(properties, "parent.attr"); - assertTrue(values.size()==2); + assertTrue(values.size() == 2); assertTrue(values.get(0).equals("value1")); assertTrue(values.get(1).equals("value2")); } @Test public void testReadStringFromObjectArrayOnceRemoved() { - properties.put("parent", new ArrayList>()); - ((List)properties.get("parent")).add(new LinkedHashMap()); - ((Map) ((List)properties.get("parent")).get(0)).put("child", new LinkedHashMap()); - ((Map)((Map) ((List)properties.get("parent")).get(0)).get("child")).put("attr", "value1"); - ((List)properties.get("parent")).add(new LinkedHashMap()); - ((Map) ((List)properties.get("parent")).get(1)).put("child", new LinkedHashMap()); - ((Map)((Map) ((List)properties.get("parent")).get(1)).get("child")).put("attr", "value2"); + properties.put("parent", new ArrayList>()); + ((List) properties.get("parent")).add(new LinkedHashMap()); + ((Map) ((List) properties.get("parent")).get(0)).put("child", new LinkedHashMap()); + ((Map) ((Map) ((List) properties.get("parent")).get(0)).get("child")).put("attr", "value1"); + ((List) properties.get("parent")).add(new LinkedHashMap()); + ((Map) ((List) properties.get("parent")).get(1)).put("child", new LinkedHashMap()); + ((Map) ((Map) ((List) properties.get("parent")).get(1)).get("child")).put("attr", "value2"); List values = parserUtil.readField(properties, "parent.child.attr"); - assertTrue(values.size()==2); + assertTrue(values.size() == 2); assertTrue(values.get(0).equals("value1")); assertTrue(values.get(1).equals("value2")); } @Test public void testReadMapField() { - final Map map = new LinkedHashMap(); + final Map map = new LinkedHashMap(); properties.put("attr", map); map.put("attr2", "value2"); map.put("attr3", "value3"); List values = parserUtil.readField(properties, "attr"); - assertTrue(values.size()==1); + assertTrue(values.size() == 1); assertTrue(values.get(0).equals(map)); } + @Test + public void testConvertToMeters() { + double distance = ElasticParserUtil.convertToMeters("1.2mm"); + assertEquals(0.0012, distance, 0.0000000001); + distance = ElasticParserUtil.convertToMeters("1.2"); + assertEquals(1.2, distance, 0.0000000001); + distance = ElasticParserUtil.convertToMeters("12"); + assertEquals(12.0, distance, 0.0000000001); + distance = ElasticParserUtil.convertToMeters("0.12cm"); + assertEquals(0.0012, distance, 0.0000000001); + try { + distance = ElasticParserUtil.convertToMeters("999xyz"); + fail("Shouldn't get here"); + } + catch(IllegalArgumentException iae) { + + } + try { + distance = ElasticParserUtil.convertToMeters("mm1.2"); + fail("Shouldn't get here"); + } + catch(IllegalArgumentException iae) { + + } + try { + distance = ElasticParserUtil.convertToMeters(".2"); + fail("Shouldn't get here"); + } + catch(IllegalArgumentException iae) { + + } + try { + distance = ElasticParserUtil.convertToMeters(".2m"); + fail("Shouldn't get here"); + } + catch(IllegalArgumentException iae) { + + } + } }