/* * Licensed to Crate under one or more contributor license agreements. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. Crate 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. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial * agreement. */ package io.crate.geo; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.vividsolutions.jts.geom.*; import com.vividsolutions.jts.geom.impl.CoordinateArraySequenceFactory; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.locationtech.spatial4j.context.jts.JtsSpatialContext; import org.locationtech.spatial4j.io.WKTWriter; import org.locationtech.spatial4j.shape.Shape; import org.locationtech.spatial4j.shape.jts.JtsGeometry; import org.locationtech.spatial4j.shape.jts.JtsPoint; import java.util.List; import java.util.Map; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.hamcrest.core.Is.is; public class GeoJSONUtilsTest { @Rule public ExpectedException expectedException = ExpectedException.none(); static { ClassLoader.getSystemClassLoader().setDefaultAssertionStatus(true); } private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory(CoordinateArraySequenceFactory.instance()); public final static List<Shape> SHAPES = ImmutableList.<Shape>of( new JtsGeometry(new Polygon(GEOMETRY_FACTORY.createLinearRing(new Coordinate[]{ new Coordinate(0.0, 1.0), new Coordinate(100.0, 0.1), new Coordinate(20.0, 23.567), new Coordinate(0.0, 1.0) }), new LinearRing[0], GEOMETRY_FACTORY), JtsSpatialContext.GEO, true, true), new JtsGeometry(new MultiPolygon( new Polygon[]{ new Polygon(GEOMETRY_FACTORY.createLinearRing(new Coordinate[]{ new Coordinate(0.0, 1.0), new Coordinate(0.1, 1.1), new Coordinate(1.1, 60.0), new Coordinate(0.0, 1.0) }), new LinearRing[0], GEOMETRY_FACTORY), new Polygon(GEOMETRY_FACTORY.createLinearRing(new Coordinate[]{ new Coordinate(2.0, 1.0), new Coordinate(2.1, 1.1), new Coordinate(2.1, 70.0), new Coordinate(2.0, 1.0) }), new LinearRing[0], GEOMETRY_FACTORY) }, GEOMETRY_FACTORY ), JtsSpatialContext.GEO, true, true), new JtsGeometry(GEOMETRY_FACTORY.createMultiPoint(new Coordinate[]{ new Coordinate(0.0, 0.0), new Coordinate(1.0, 1.0) }), JtsSpatialContext.GEO, true, true), new JtsGeometry(GEOMETRY_FACTORY.createMultiLineString(new LineString[]{ GEOMETRY_FACTORY.createLineString(new Coordinate[]{ new Coordinate(0.0, 1.0), new Coordinate(0.1, 1.1), new Coordinate(1.1, 80.0), new Coordinate(0.0, 1.0) }), GEOMETRY_FACTORY.createLineString(new Coordinate[]{ new Coordinate(2.0, 1.0), new Coordinate(2.1, 1.1), new Coordinate(2.1, 60.0), new Coordinate(2.0, 1.0) }) }), JtsSpatialContext.GEO, true, true) ); @Test public void testShape2Map() throws Exception { for (Shape shape : SHAPES) { Map<String, Object> map = GeoJSONUtils.shape2Map(shape); assertThat(map, hasKey("type")); GeoJSONUtils.validateGeoJson(map); } } @Test public void testPoint2Map() throws Exception { Point point = GEOMETRY_FACTORY.createPoint(new Coordinate(0.0, 0.0)); Shape shape = new JtsPoint(point, JtsSpatialContext.GEO); Map<String, Object> map = GeoJSONUtils.shape2Map(shape); assertThat(map, hasEntry("type", (Object) "Point")); assertThat(map.get("coordinates").getClass().isArray(), is(true)); assertThat(((double[]) map.get("coordinates")).length, is(2)); } @Test public void testMapFromWktRoundTrip() throws Exception { String wkt = "MULTILINESTRING ((10.05 10.28, 20.95 20.89), (20.95 20.89, 31.92 21.45))"; Shape shape = GeoJSONUtils.wkt2Shape(wkt); Map<String, Object> map = GeoJSONUtils.shape2Map(shape); Map<String, Object> wktMap = GeoJSONUtils.wkt2Map(wkt); assertThat(map.get("type"), is(wktMap.get("type"))); assertThat(map.get("coordinates"), is(wktMap.get("coordinates"))); Shape mappedShape = GeoJSONUtils.map2Shape(map); String wktFromMap = new WKTWriter().toString(mappedShape); assertThat(wktFromMap, is(wkt)); } @Test public void testInvalidWKT() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage(allOf( startsWith("Cannot convert WKT \""), endsWith("\" to shape"))); GeoJSONUtils.wkt2Map("multilinestring (((10.05 10.28 3.4 8.4, 20.95 20.89 4.5 9.5),\n" + " \n" + "( 20.95 20.89 4.5 9.5, 31.92 21.45 3.6 8.6)))"); } @Test public void testMap2Shape() throws Exception { Shape shape = GeoJSONUtils.map2Shape(ImmutableMap.<String, Object>of( GeoJSONUtils.TYPE_FIELD, GeoJSONUtils.LINE_STRING, GeoJSONUtils.COORDINATES_FIELD, new Double[][]{{0.0, 0.1}, {1.0, 1.1}} )); assertThat(shape, instanceOf(JtsGeometry.class)); assertThat(((JtsGeometry) shape).getGeom(), instanceOf(LineString.class)); } @Test public void testInvalidMap() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Cannot convert Map \"{}\" to shape"); GeoJSONUtils.map2Shape(ImmutableMap.<String, Object>of()); } @Test public void testValidateMissingType() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Invalid GeoJSON: type field missing"); GeoJSONUtils.validateGeoJson(ImmutableMap.of()); } @Test public void testValidateWrongType() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Invalid GeoJSON: invalid type"); GeoJSONUtils.validateGeoJson(ImmutableMap.of(GeoJSONUtils.TYPE_FIELD, "Foo")); } @Test public void testValidateMissingCoordinates() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Invalid GeoJSON: coordinates field missing"); GeoJSONUtils.validateGeoJson(ImmutableMap.of(GeoJSONUtils.TYPE_FIELD, GeoJSONUtils.LINE_STRING)); } @Test public void testValidateGeometriesMissing() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Invalid GeoJSON: geometries field missing"); GeoJSONUtils.validateGeoJson(ImmutableMap.of(GeoJSONUtils.TYPE_FIELD, GeoJSONUtils.GEOMETRY_COLLECTION)); } @Test public void testInvalidGeometryCollection() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Invalid GeoJSON: invalid GeometryCollection"); GeoJSONUtils.validateGeoJson( ImmutableMap.of( GeoJSONUtils.TYPE_FIELD, GeoJSONUtils.GEOMETRY_COLLECTION, GeoJSONUtils.GEOMETRIES_FIELD, ImmutableList.<Object>of("ABC") ) ); } @Test public void testValidateInvalidCoordinates() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Invalid GeoJSON: invalid coordinate"); GeoJSONUtils.validateGeoJson( ImmutableMap.of( GeoJSONUtils.TYPE_FIELD, GeoJSONUtils.POINT, GeoJSONUtils.COORDINATES_FIELD, "ABC" ) ); } @Test public void testInvalidNestedCoordinates() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Invalid GeoJSON: invalid coordinate"); GeoJSONUtils.validateGeoJson( ImmutableMap.of( GeoJSONUtils.TYPE_FIELD, GeoJSONUtils.POINT, GeoJSONUtils.COORDINATES_FIELD, new double[][]{ {0.0, 1.0}, {1.0, 0.0} } ) ); } @Test public void testInvalidDepthNestedCoordinates() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Invalid GeoJSON: invalid coordinate"); GeoJSONUtils.validateGeoJson( ImmutableMap.of( GeoJSONUtils.TYPE_FIELD, GeoJSONUtils.POLYGON, GeoJSONUtils.COORDINATES_FIELD, new double[][]{ {0.0, 1.0}, {1.0, 0.0} } ) ); } }