/******************************************************************************* * Copyright (c) 2015 Voyager Search and MITRE * All rights reserved. This program and the accompanying materials * are made available under the terms of the Apache License, Version 2.0 which * accompanies this distribution and is available at * http://www.apache.org/licenses/LICENSE-2.0.txt ******************************************************************************/ package org.locationtech.spatial4j.io; import org.locationtech.spatial4j.context.jts.DatelineRule; import org.locationtech.spatial4j.context.jts.JtsSpatialContext; import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory; import org.locationtech.spatial4j.context.jts.ValidationRule; import org.locationtech.spatial4j.exception.InvalidShapeException; import org.locationtech.spatial4j.shape.Rectangle; import org.locationtech.spatial4j.shape.Shape; import org.locationtech.spatial4j.shape.ShapeFactory; import org.locationtech.spatial4j.shape.SpatialRelation; import org.locationtech.spatial4j.shape.jts.JtsGeometry; import org.junit.Test; import java.text.ParseException; import java.util.Arrays; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class JtsWktShapeParserTest extends WktShapeParserTest { //By extending WktShapeParserTest we inherit its test too final JtsSpatialContext ctx;//note: masks superclass public JtsWktShapeParserTest() { super(createSpatialContext()); this.ctx = (JtsSpatialContext) super.ctx; } static JtsSpatialContext createSpatialContext() { JtsSpatialContextFactory factory = new JtsSpatialContextFactory(); factory.useJtsMulti = false; return factory.newSpatialContext(); } @Test public void testParsePolygon() throws ParseException { Shape polygonNoHoles = ctx.getShapeFactory().polygon() .pointXY(100, 0) .pointXY(101, 0) .pointXY(101, 1) .pointXY(100, 2) .pointXY(100, 0) .build(); String polygonNoHolesSTR = "POLYGON ((100 0, 101 0, 101 1, 100 2, 100 0))"; assertParses(polygonNoHolesSTR, polygonNoHoles); assertParses("POLYGON((100 0,101 0,101 1,100 2,100 0))", polygonNoHoles); assertParses("GEOMETRYCOLLECTION ( "+polygonNoHolesSTR+")", ctx.makeCollection(Arrays.asList(polygonNoHoles))); Shape polygonWithHoles = ctx.getShapeFactory().polygon() .pointXY(100, 0) .pointXY(101, 0) .pointXY(101, 1) .pointXY(100, 1) .pointXY(100, 0) .hole() .pointXY(100.2, 0.2) .pointXY(100.8, 0.2) .pointXY(100.8, 0.8) .pointXY(100.2, 0.8) .pointXY(100.2, 0.2) .endHole() .build(); assertParses("POLYGON ((100 0, 101 0, 101 1, 100 1, 100 0), (100.2 0.2, 100.8 0.2, 100.8 0.8, 100.2 0.8, 100.2 0.2))", polygonWithHoles); assertParses("POLYGON EMPTY", ctx.getShapeFactory().polygon().build()); } @Test public void testPolyToRect() throws ParseException { //poly is a rect (no dateline issue) assertParses("POLYGON((0 5, 10 5, 10 20, 0 20, 0 5))", ctx.makeRectangle(0, 10, 5, 20)); } @Test public void polyToRect180Rule() throws ParseException { //crosses dateline Rectangle expected = ctx.makeRectangle(160, -170, 0, 10); //counter-clockwise assertParses("POLYGON((160 0, -170 0, -170 10, 160 10, 160 0))", expected); //clockwise assertParses("POLYGON((160 10, -170 10, -170 0, 160 0, 160 10))", expected); } @Test public void polyToRectCcwRule() throws ParseException { JtsSpatialContext ctx = new JtsSpatialContextFactory() { { datelineRule = DatelineRule.ccwRect;} }.newSpatialContext(); //counter-clockwise assertEquals(wkt(ctx, "POLYGON((160 0, -170 0, -170 10, 160 10, 160 0))"), ctx.makeRectangle(160, -170, 0, 10)); //clockwise assertEquals(wkt(ctx, "POLYGON((160 10, -170 10, -170 0, 160 0, 160 10))"), ctx.makeRectangle(-170, 160, 0, 10)); } @Test public void testParseMultiPolygon() throws ParseException { ShapeFactory.MultiPolygonBuilder multiPolygonBuilder = ctx.getShapeFactory().multiPolygon(); multiPolygonBuilder.add(multiPolygonBuilder.polygon() .pointXY(100, 0) .pointXY(101, 0)//101 .pointXY(101, 2)//101 .pointXY(100, 1) .pointXY(100, 0)); multiPolygonBuilder.add(multiPolygonBuilder.polygon() .pointXY( 0, 0) .pointXY( 2, 0) .pointXY( 2, 2) .pointXY( 0, 1) .pointXY( 0, 0)); Shape s = multiPolygonBuilder.build(); assertParses("MULTIPOLYGON(" + "((100 0, 101 0, 101 2, 100 1, 100 0))" + ',' + "((0 0, 2 0, 2 2, 0 1, 0 0))" + ")", s); assertParses("MULTIPOLYGON EMPTY", ctx.getShapeFactory().multiPolygon().build()); } @Test public void testLineStringDateline() throws ParseException { //works because we use JTS (JtsGeometry); BufferedLineString doesn't yet do DL wrap. Shape s = wkt("LINESTRING(160 10, -170 15)"); assertEquals(30, s.getBoundingBox().getWidth(), 0.0 ); } @Test public void testWrapTopologyException() throws Exception { //test that we can catch ParseException without having to detect TopologyException too assert ctx.getValidationRule() != ValidationRule.none; try { wkt("POLYGON((0 0, 10 0, 10 20))"); fail(); } catch (InvalidShapeException e) { //expected } try { wkt("POLYGON((0 0, 10 0, 10 20, 5 -5, 0 20, 0 0))"); fail(); } catch (InvalidShapeException e) { //expected } } @Test public void testPolygonRepair() throws ParseException { //because we're going to test validation System.setProperty(JtsGeometry.SYSPROP_ASSERT_VALIDATE, "false"); //note: doesn't repair all cases; this case isn't: //ctx.readShapeFromWkt("POLYGON((0 0, 10 0, 10 20))");//doesn't connect around String wkt = "POLYGON((0 0, 10 0, 10 20, 5 -5, 0 20, 0 0))";//Topology self-intersect JtsSpatialContextFactory factory = new JtsSpatialContextFactory(); factory.validationRule = ValidationRule.repairBuffer0; JtsSpatialContext ctx = factory.newSpatialContext(); Shape buffer0 = wkt(ctx,wkt); assertTrue(buffer0.getArea(ctx) > 0); factory = new JtsSpatialContextFactory(); factory.validationRule = ValidationRule.repairConvexHull; ctx = factory.newSpatialContext(); Shape cvxHull = wkt(ctx,wkt); assertTrue(cvxHull.getArea(ctx) > 0); assertEquals(SpatialRelation.CONTAINS, cvxHull.relate(buffer0)); factory = new JtsSpatialContextFactory(); factory.validationRule = ValidationRule.none; ctx = factory.newSpatialContext(); wkt(ctx,wkt); } }