/* * Licensed to CRATE Technology GmbH ("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.types; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.locationtech.spatial4j.shape.Shape; import io.crate.geo.GeoJSONUtils; import io.crate.geo.GeoJSONUtilsTest; import io.crate.test.integration.CrateUnitTest; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.junit.Test; import java.io.IOException; import java.util.List; import java.util.Map; import static org.hamcrest.Matchers.*; public class GeoShapeTypeTest extends CrateUnitTest { private static final List<String> WKT = ImmutableList.of( "multipolygon empty", "MULTIPOLYGON (" + " ((40 40, 20 45, 45 30, 40 40)),\n" + " ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20))" + ")", "polygon (( 10 10, 10 20, 20 20, 20 15, 10 10))", "linestring ( 10.05 10.28 , 20.95 20.89 )", "multilinestring (( 10.05 10.28 , 20.95 20.89 ),( 20.95 20.89, 31.92 21.45))", "point ( 10.05 10.28 )", "multipoint (10 10, 20 20)" ); private static Map<String, Object> parse(String json) { try { return JsonXContent.jsonXContent.createParser(json).mapOrdered(); } catch (IOException e) { throw new RuntimeException(e); } } private static final List<Map<String, Object>> GEO_JSON = ImmutableList.of( parse("{ \"type\": \"Point\", \"coordinates\": [100.0, 0.0] }"), parse("{ \"type\": \"LineString\",\n" + " \"coordinates\": [ [100.0, 0.0], [101.0, 1.0] ]\n" + " }"), parse("{ \"type\": \"Polygon\",\n" + " \"coordinates\": [\n" + " [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ]\n" + " ]\n" + " }"), parse("{ \"type\": \"Polygon\",\n" + " \"coordinates\": [\n" + " [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ],\n" + " [ [100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2] ]\n" + " ]\n" + " }"), parse("{ \"type\": \"MultiPoint\",\n" + " \"coordinates\": [ [100.0, 0.0], [101.0, 1.0] ]\n" + " }"), parse("{ \"type\": \"MultiLineString\",\n" + " \"coordinates\": [\n" + " [ [100.0, 0.0], [101.0, 1.0] ],\n" + " [ [102.0, 2.0], [103.0, 3.0] ]\n" + " ]\n" + " }"), parse("{ \"type\": \"MultiPolygon\",\n" + " \"coordinates\": [\n" + " [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]],\n" + " [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]],\n" + " [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]]\n" + " ]\n" + " }"), parse("{ \"type\": \"GeometryCollection\",\n" + " \"geometries\": [\n" + " { \"type\": \"Point\",\n" + " \"coordinates\": [100.0, 0.0]\n" + " },\n" + " { \"type\": \"LineString\",\n" + " \"coordinates\": [ [101.0, 0.0], [102.0, 1.0] ]\n" + " }\n" + " ]\n" + " }") ); private GeoShapeType type = GeoShapeType.INSTANCE; @Test public void testStreamer() throws Exception { for (Map<String, Object> geoJSON : GEO_JSON) { Map<String, Object> value = type.value(geoJSON); BytesStreamOutput out = new BytesStreamOutput(); type.streamer().writeValueTo(out, value); StreamInput in = out.bytes().streamInput(); Map<String, Object> streamedValue = type.readValueFrom(in); assertThat(streamedValue.size(), is(value.size())); assertThat(streamedValue.get("type"), is(value.get("type"))); assertThat(streamedValue.get("coordinates"), is(value.get("coordinates"))); } } @Test public void testCompareValueTo() throws Exception { Map<String, Object> val1 = type.value("POLYGON ( (0 0, 20 0, 20 20, 0 20, 0 0 ))"); Map<String, Object> val2 = type.value("POINT (10 10)"); assertThat(type.compareValueTo(val1, val2), is(1)); assertThat(type.compareValueTo(val2, val1), is(-1)); assertThat(type.compareValueTo(val2, val2), is(0)); } @Test public void testInvalidStringValueCausesIllegalArgumentException() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Cannot convert \"foobar\" to geo_shape"); type.value("foobar"); } @Test public void testInvalidTypeCausesIllegalArgumentException() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Cannot convert \"200\" to geo_shape"); type.value(200); } @Test public void testInvalidCoordinates() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage(allOf( startsWith("Cannot convert \""), endsWith("\" to geo_shape")) ); type.value(ImmutableMap.of( GeoJSONUtils.TYPE_FIELD, GeoJSONUtils.LINE_STRING, GeoJSONUtils.COORDINATES_FIELD, new double[][]{ new double[]{170.0d, 99.0d}, new double[]{180.5d, -180.5d} } )); } @Test public void testConvertFromValidWKT() throws Exception { for (String wkt : WKT) { Map<String, Object> geoShape = type.value(wkt); assertThat(geoShape, is(notNullValue())); } } @Test public void testConvertFromValidGeoJSON() throws Exception { for (Map<String, Object> geoJSON : GEO_JSON) { Map<String, Object> geoShape = type.value(geoJSON); assertThat(geoShape, is(notNullValue())); } } @Test public void testNullValue() throws Exception { assertThat(type.value(null), is(nullValue())); } @Test public void testValueShape() throws Exception { for (Shape shape : GeoJSONUtilsTest.SHAPES) { Map<String, Object> map = type.value(shape); GeoJSONUtils.validateGeoJson(map); } } }