Merge branch 'master' of github.com:thingsboard/thingsboard
This commit is contained in:
commit
116dfe8e46
2
pom.xml
2
pom.xml
@ -88,7 +88,7 @@
|
||||
<springfox-swagger.version>3.0.4</springfox-swagger.version>
|
||||
<swagger-annotations.version>1.6.3</swagger-annotations.version>
|
||||
<spatial4j.version>0.7</spatial4j.version>
|
||||
<jts.version>1.15.0</jts.version>
|
||||
<jts.version>1.18.2</jts.version>
|
||||
<bouncycastle.version>1.67</bouncycastle.version>
|
||||
<winsw.version>2.0.1</winsw.version>
|
||||
<postgresql.driver.version>42.2.20</postgresql.driver.version>
|
||||
|
||||
@ -18,20 +18,35 @@ package org.thingsboard.rule.engine.geo;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import lombok.NonNull;
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
import org.locationtech.jts.geom.CoordinateSequence;
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
import org.locationtech.jts.geom.util.GeometryFixer;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
|
||||
import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
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 java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class GeoUtil {
|
||||
|
||||
private static final SpatialContext distCtx = SpatialContext.GEO;
|
||||
private static final JtsSpatialContext jtsCtx;
|
||||
|
||||
private static final JsonParser JSON_PARSER = new JsonParser();
|
||||
|
||||
static {
|
||||
JtsSpatialContextFactory factory = new JtsSpatialContextFactory();
|
||||
factory.normWrapLongitude = true;
|
||||
@ -44,43 +59,148 @@ public class GeoUtil {
|
||||
return unit.fromKm(distCtx.getDistCalc().distance(xLL, yLL) * DistanceUtils.DEG_TO_KM);
|
||||
}
|
||||
|
||||
public static synchronized boolean contains(String polygon, Coordinates coordinates) {
|
||||
JsonArray polygonArray = new JsonParser().parse(polygon).getAsJsonArray();
|
||||
|
||||
JsonArray arrayWithCoords = polygonArray;
|
||||
JsonArray innerArray = polygonArray.get(0).getAsJsonArray();
|
||||
if (!containsPrimitives(innerArray)) {
|
||||
arrayWithCoords = innerArray;
|
||||
public static synchronized boolean contains(@NonNull String polygonInString, @NonNull Coordinates coordinates) {
|
||||
if (polygonInString.isEmpty() || polygonInString.isBlank()) {
|
||||
throw new RuntimeException("Polygon string can't be empty or null!");
|
||||
}
|
||||
|
||||
Shape shape = buildPolygonFromJsonCoords(arrayWithCoords);
|
||||
Point point = jtsCtx.getShapeFactory().pointXY(coordinates.getLongitude(), coordinates.getLatitude());
|
||||
return shape.relate(point).equals(SpatialRelation.CONTAINS);
|
||||
JsonArray polygonsJson = normalizePolygonsJson(JSON_PARSER.parse(polygonInString).getAsJsonArray());
|
||||
List<Geometry> polygons = buildPolygonsFromJson(polygonsJson);
|
||||
Set<Geometry> holes = extractHolesFrom(polygons);
|
||||
polygons.removeIf(holes::contains);
|
||||
|
||||
Geometry globalGeometry = unionToGlobalGeometry(polygons, holes);
|
||||
var point = jtsCtx.getShapeFactory().getGeometryFactory()
|
||||
.createPoint(new Coordinate(coordinates.getLatitude(), coordinates.getLongitude()));
|
||||
|
||||
return globalGeometry.contains(point);
|
||||
}
|
||||
|
||||
private static Shape buildPolygonFromJsonCoords(JsonArray coordinates) {
|
||||
ShapeFactory.PolygonBuilder polygonBuilder = jtsCtx.getShapeFactory().polygon();
|
||||
boolean isFirst = true;
|
||||
double firstLat = 0.0;
|
||||
double firstLng = 0.0;
|
||||
for (JsonElement element : coordinates) {
|
||||
double lat = element.getAsJsonArray().get(0).getAsDouble();
|
||||
double lng = element.getAsJsonArray().get(1).getAsDouble();
|
||||
if (isFirst) {
|
||||
firstLat = lat;
|
||||
firstLng = lng;
|
||||
isFirst = false;
|
||||
}
|
||||
polygonBuilder.pointXY(jtsCtx.getShapeFactory().normX(lng), jtsCtx.getShapeFactory().normX(lat));
|
||||
private static Geometry unionToGlobalGeometry(List<Geometry> polygons, Set<Geometry> holes) {
|
||||
Geometry globalPolygon = polygons.stream().reduce(Geometry::union).orElseThrow(() ->
|
||||
new RuntimeException("Error while calculating globalPolygon - the result of all polygons union is null"));
|
||||
Optional<Geometry> globalHole = holes.stream().reduce(Geometry::union);
|
||||
if (globalHole.isEmpty()) {
|
||||
return globalPolygon;
|
||||
} else {
|
||||
return globalPolygon.difference(globalHole.get());
|
||||
}
|
||||
polygonBuilder.pointXY(jtsCtx.getShapeFactory().normX(firstLng), jtsCtx.getShapeFactory().normX(firstLat));
|
||||
return polygonBuilder.buildOrRect();
|
||||
}
|
||||
|
||||
private static JsonArray normalizePolygonsJson(JsonArray polygonsJsonArray) {
|
||||
JsonArray result = new JsonArray();
|
||||
normalizePolygonsJson(polygonsJsonArray, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void normalizePolygonsJson(JsonArray polygonsJsonArray, JsonArray result) {
|
||||
if (containsArrayWithPrimitives(polygonsJsonArray)) {
|
||||
result.add(polygonsJsonArray);
|
||||
} else {
|
||||
for (JsonElement element : polygonsJsonArray) {
|
||||
if (containsArrayWithPrimitives(element.getAsJsonArray())) {
|
||||
result.add(element);
|
||||
} else {
|
||||
normalizePolygonsJson(element.getAsJsonArray(), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<Geometry> extractHolesFrom(List<Geometry> polygons) {
|
||||
Map<Geometry, List<Geometry>> polygonsHoles = new HashMap<>();
|
||||
|
||||
for (Geometry polygon : polygons) {
|
||||
List<Geometry> holes = polygons.stream()
|
||||
.filter(another -> !another.equalsExact(polygon))
|
||||
.filter(another -> {
|
||||
JtsGeometry currentGeo = jtsCtx.getShapeFactory().makeShape(polygon);
|
||||
JtsGeometry anotherGeo = jtsCtx.getShapeFactory().makeShape(another);
|
||||
|
||||
boolean currentContainsAnother = currentGeo
|
||||
.relate(anotherGeo)
|
||||
.equals(SpatialRelation.CONTAINS);
|
||||
|
||||
boolean anotherWithinCurrent = anotherGeo
|
||||
.relate(currentGeo)
|
||||
.equals(SpatialRelation.WITHIN);
|
||||
|
||||
return currentContainsAnother && anotherWithinCurrent;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!holes.isEmpty()) {
|
||||
polygonsHoles.put(polygon, holes);
|
||||
}
|
||||
}
|
||||
|
||||
return polygonsHoles.values().stream().flatMap(Collection::stream).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private static List<Geometry> buildPolygonsFromJson(JsonArray polygonsJsonArray) {
|
||||
List<Geometry> polygons = new LinkedList<>();
|
||||
|
||||
for (JsonElement polygonJsonArray : polygonsJsonArray) {
|
||||
polygons.add(
|
||||
buildPolygonFromCoordinates(parseCoordinates(polygonJsonArray.getAsJsonArray()))
|
||||
);
|
||||
}
|
||||
|
||||
return polygons;
|
||||
}
|
||||
|
||||
private static Geometry buildPolygonFromCoordinates(List<Coordinate> coordinates) {
|
||||
if (coordinates.size() == 2) {
|
||||
Coordinate a = coordinates.get(0);
|
||||
Coordinate c = coordinates.get(1);
|
||||
coordinates.clear();
|
||||
|
||||
Coordinate b = new Coordinate(a.x, c.y);
|
||||
Coordinate d = new Coordinate(c.x, a.y);
|
||||
coordinates.addAll(List.of(a, b, c, d, a));
|
||||
}
|
||||
|
||||
CoordinateSequence coordinateSequence = jtsCtx
|
||||
.getShapeFactory()
|
||||
.getGeometryFactory()
|
||||
.getCoordinateSequenceFactory()
|
||||
.create(coordinates.toArray(new Coordinate[0]));
|
||||
|
||||
return GeometryFixer.fix(jtsCtx.getShapeFactory().getGeometryFactory().createPolygon(coordinateSequence));
|
||||
}
|
||||
|
||||
private static List<Coordinate> parseCoordinates(JsonArray coordinatesJson) {
|
||||
List<Coordinate> result = new LinkedList<>();
|
||||
|
||||
for (JsonElement coords : coordinatesJson) {
|
||||
double x = coords.getAsJsonArray().get(0).getAsDouble();
|
||||
double y = coords.getAsJsonArray().get(1).getAsDouble();
|
||||
result.add(new Coordinate(x, y));
|
||||
}
|
||||
|
||||
if (result.size() >= 3) {
|
||||
result.add(result.get(0));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean containsPrimitives(JsonArray array) {
|
||||
for (JsonElement element : array) {
|
||||
return element.isJsonPrimitive();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean containsArrayWithPrimitives(JsonArray array) {
|
||||
for (JsonElement element : array) {
|
||||
if (!containsPrimitives(element.getAsJsonArray())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,163 @@
|
||||
/**
|
||||
* Copyright © 2016-2021 The Thingsboard Authors
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
package org.thingsboard.rule.engine.geo;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TbGeoUtilTest {
|
||||
|
||||
public static final String SIMPLE_RECT = "[[51.903762928405555,23.642220786948297],[44.669801219635644,41.83345155830211]]";
|
||||
public static final String SIMPLE_RECT_WITH_HOLE_IN_CENTER = "[[[44.66980121963565,23.642220786948297],[44.66980121963565,41.83345155830211],[51.903762928405555,41.83345155830211],[51.903762928405555,23.642220786948297]],[[46.10464044504632,26.234282119122227],[50.8755868028522,26.25625220459488],[51.04164771375101,38.5595000692786],[45.99790855491869,38.75723083853248]]]";
|
||||
public static final String SAND_CLOCK = "[[47.45865912532852,25.531200822337155],[49.76760268310416,29.353995694578202],[51.42691963936519,25.355440138555966],[51.413219087617655,39.41629484105169],[49.78179072754938,33.37452133607305],[47.81395478511215,38.867042704235466]]";
|
||||
public static final String SAND_CLOCK_WITH_HOLE_IN_CENTER = "[[[51.426919639365195,25.355440138555966],[49.76760268310416,29.353995694578202],[47.45865912532852,25.531200822337155],[47.81395478511215,38.867042704235466],[49.78179072754938,33.37452133607305],[51.413219087617655,39.41629484105169]],[[49.8243299406579,30.210829028011513],[50.34591034217041,31.04569227597222],[49.56853374046142,32.53965808811239],[49.02409241675058,31.57297432731579]]]";
|
||||
public static final String SELF_INTERSECTING = "[[47.42893833699058,27.178954662008522],[51.71367987390804,37.46095466320854],[51.659197648757306,27.947907653551276],[47.41407351681856,37.46095466320854]]";
|
||||
public static final String SELF_INTERSECTING_WITH_HOLES = "[[[[47.42893833699058,27.17895466200852],[47.41407351681856,37.46095466320853],[49.63741575793274,32.47858934221699]],[[47.84342032696093,29.20045703244998],[49.124803688667576,32.38611942598416],[47.858163521459254,34.714948486085035]]],[[[51.659197648757306,27.947907653551276],[49.63741575793274,32.47858934221699],[51.71367987390804,37.46095466320853]],[[51.20718653775245,30.738363015535448],[51.317169097567344,34.58312797324915],[50.1351109330126,32.51793993882006]]]]";
|
||||
|
||||
public static final Coordinates POINT_INSIDE_SIMPLE_RECT_CENTER = new Coordinates(48.37082198780869, 32.673342414527355);
|
||||
public static final Coordinates POINT_INSIDE_SIMPLE_RECT_NEAR_BORDER = new Coordinates(48.42916753187315,40.956064637716224);
|
||||
public static final Coordinates POINT_OUTSIDE_SIMPLE_RECT = new Coordinates(52.94806646045028,32.91501335472649);
|
||||
public static final Coordinates POINT_INSIDE_SAND_CLOCK_CENTER = new Coordinates(49.993588800145105,31.289062500000004);
|
||||
public static final Coordinates POINT_INSIDE_SAND_CLOCK_NEAR_BORDER = new Coordinates(47.798651123976306,26.895045405470082);
|
||||
public static final Coordinates POINT_OUTSIDE_SAND_CLOCK_1 = new Coordinates(49.553754212665936,28.03748985004787);
|
||||
public static final Coordinates POINT_OUTSIDE_SAND_CLOCK_2 = new Coordinates(46.9802466961145,32.321728498980335);
|
||||
public static final Coordinates POINT_INSIDE_SELF_INTERSECTING_UPPER_CENTER = new Coordinates(50.750366308834884,32.51952867922265);
|
||||
public static final Coordinates POINT_INSIDE_SELF_INTERSECTING_LOWER_CENTER = new Coordinates(48.1371117277312,32.40967825185941);
|
||||
public static final Coordinates POINT_INSIDE_SELF_INTERSECTING_NEAR_BORDER = new Coordinates(51.16552151942722,35.66125090181154);
|
||||
public static final Coordinates POINT_OUTSIDE_SELF_INTERSECTING_1= new Coordinates(49.66777277299077,33.26651158529272);
|
||||
public static final Coordinates POINT_OUTSIDE_SELF_INTERSECTING_2 = new Coordinates(47.10052840114779,32.16800731166027);
|
||||
public static final Coordinates POINT_OUTSIDE_SELF_INTERSECTING_3 = new Coordinates(78.76578380252519,15.646485040786361);
|
||||
|
||||
|
||||
@Test
|
||||
public void testPointsInSimplePolygons() {
|
||||
Assert.assertTrue("Polygon " + SIMPLE_RECT + " must contain the dot " + POINT_INSIDE_SIMPLE_RECT_CENTER,
|
||||
GeoUtil.contains(SIMPLE_RECT, POINT_INSIDE_SIMPLE_RECT_CENTER)
|
||||
);
|
||||
Assert.assertTrue("Polygon " + SIMPLE_RECT + " must contain the dot " + POINT_INSIDE_SIMPLE_RECT_NEAR_BORDER,
|
||||
GeoUtil.contains(SIMPLE_RECT, POINT_INSIDE_SIMPLE_RECT_NEAR_BORDER)
|
||||
);
|
||||
Assert.assertTrue("Polygon " + SIMPLE_RECT_WITH_HOLE_IN_CENTER + " must contain the dot "
|
||||
+ POINT_INSIDE_SIMPLE_RECT_NEAR_BORDER,
|
||||
GeoUtil.contains(SIMPLE_RECT_WITH_HOLE_IN_CENTER, POINT_INSIDE_SIMPLE_RECT_NEAR_BORDER)
|
||||
);
|
||||
|
||||
Assert.assertFalse("Polygon " + SIMPLE_RECT + " must not contain the dot "
|
||||
+ POINT_OUTSIDE_SIMPLE_RECT,
|
||||
GeoUtil.contains(SIMPLE_RECT, POINT_OUTSIDE_SIMPLE_RECT)
|
||||
);
|
||||
Assert.assertFalse("Polygon " + SIMPLE_RECT_WITH_HOLE_IN_CENTER + " must not contain the dot "
|
||||
+ POINT_OUTSIDE_SIMPLE_RECT,
|
||||
GeoUtil.contains(SIMPLE_RECT_WITH_HOLE_IN_CENTER, POINT_OUTSIDE_SIMPLE_RECT)
|
||||
);
|
||||
Assert.assertFalse("Polygon " + SIMPLE_RECT_WITH_HOLE_IN_CENTER + " must not contain the dot "
|
||||
+ POINT_INSIDE_SIMPLE_RECT_CENTER,
|
||||
GeoUtil.contains(SIMPLE_RECT_WITH_HOLE_IN_CENTER, POINT_INSIDE_SIMPLE_RECT_CENTER)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPointsInComplexPolygons() {
|
||||
Assert.assertTrue("Polygon " + SAND_CLOCK + " must contain the dot " + POINT_INSIDE_SAND_CLOCK_CENTER,
|
||||
GeoUtil.contains(SAND_CLOCK, POINT_INSIDE_SAND_CLOCK_CENTER)
|
||||
);
|
||||
Assert.assertTrue("Polygon " + SAND_CLOCK + " must contain the dot " + POINT_INSIDE_SAND_CLOCK_NEAR_BORDER,
|
||||
GeoUtil.contains(SAND_CLOCK, POINT_INSIDE_SAND_CLOCK_NEAR_BORDER)
|
||||
);
|
||||
Assert.assertTrue("Polygon " + SAND_CLOCK_WITH_HOLE_IN_CENTER + " must contain the dot "
|
||||
+ POINT_INSIDE_SAND_CLOCK_NEAR_BORDER,
|
||||
GeoUtil.contains(SAND_CLOCK_WITH_HOLE_IN_CENTER, POINT_INSIDE_SAND_CLOCK_NEAR_BORDER)
|
||||
);
|
||||
|
||||
Assert.assertFalse("Polygon " + SAND_CLOCK + " must not contain the dot "
|
||||
+ POINT_OUTSIDE_SAND_CLOCK_1,
|
||||
GeoUtil.contains(SAND_CLOCK, POINT_OUTSIDE_SAND_CLOCK_1)
|
||||
);
|
||||
Assert.assertFalse("Polygon " + SAND_CLOCK + " must not contain the dot "
|
||||
+ POINT_OUTSIDE_SAND_CLOCK_2,
|
||||
GeoUtil.contains(SAND_CLOCK, POINT_OUTSIDE_SAND_CLOCK_2)
|
||||
);
|
||||
Assert.assertFalse("Polygon " + SAND_CLOCK_WITH_HOLE_IN_CENTER + " must not contain the dot "
|
||||
+ POINT_INSIDE_SAND_CLOCK_CENTER,
|
||||
GeoUtil.contains(SAND_CLOCK_WITH_HOLE_IN_CENTER, POINT_INSIDE_SAND_CLOCK_CENTER)
|
||||
);
|
||||
Assert.assertFalse("Polygon " + SAND_CLOCK_WITH_HOLE_IN_CENTER + " must not contain the dot "
|
||||
+ POINT_OUTSIDE_SAND_CLOCK_1,
|
||||
GeoUtil.contains(SAND_CLOCK_WITH_HOLE_IN_CENTER, POINT_OUTSIDE_SAND_CLOCK_1)
|
||||
);
|
||||
Assert.assertFalse("Polygon " + SAND_CLOCK_WITH_HOLE_IN_CENTER + " must not contain the dot "
|
||||
+ POINT_OUTSIDE_SAND_CLOCK_2,
|
||||
GeoUtil.contains(SAND_CLOCK_WITH_HOLE_IN_CENTER, POINT_OUTSIDE_SAND_CLOCK_2)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPointsInSelfIntersectingPolygons() {
|
||||
Assert.assertTrue("Polygon " + SELF_INTERSECTING + " must contain the dot "
|
||||
+ POINT_INSIDE_SELF_INTERSECTING_UPPER_CENTER,
|
||||
GeoUtil.contains(SELF_INTERSECTING, POINT_INSIDE_SELF_INTERSECTING_UPPER_CENTER)
|
||||
);
|
||||
Assert.assertTrue("Polygon " + SELF_INTERSECTING + " must contain the dot "
|
||||
+ POINT_INSIDE_SELF_INTERSECTING_LOWER_CENTER,
|
||||
GeoUtil.contains(SELF_INTERSECTING, POINT_INSIDE_SELF_INTERSECTING_LOWER_CENTER)
|
||||
);
|
||||
Assert.assertTrue("Polygon " + SELF_INTERSECTING + " must contain the dot "
|
||||
+ POINT_INSIDE_SELF_INTERSECTING_NEAR_BORDER,
|
||||
GeoUtil.contains(SELF_INTERSECTING, POINT_INSIDE_SELF_INTERSECTING_NEAR_BORDER)
|
||||
);
|
||||
Assert.assertTrue("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must contain the dot "
|
||||
+ POINT_INSIDE_SAND_CLOCK_NEAR_BORDER,
|
||||
GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_INSIDE_SELF_INTERSECTING_NEAR_BORDER)
|
||||
);
|
||||
|
||||
Assert.assertFalse("Polygon " + SELF_INTERSECTING + " must not contain the dot "
|
||||
+ POINT_OUTSIDE_SELF_INTERSECTING_1,
|
||||
GeoUtil.contains(SELF_INTERSECTING, POINT_OUTSIDE_SELF_INTERSECTING_1)
|
||||
);
|
||||
Assert.assertFalse("Polygon " + SELF_INTERSECTING + " must not contain the dot "
|
||||
+ POINT_OUTSIDE_SELF_INTERSECTING_2,
|
||||
GeoUtil.contains(SELF_INTERSECTING, POINT_OUTSIDE_SELF_INTERSECTING_2)
|
||||
);
|
||||
Assert.assertFalse("Polygon " + SELF_INTERSECTING + " must not contain the dot "
|
||||
+ POINT_OUTSIDE_SELF_INTERSECTING_3,
|
||||
GeoUtil.contains(SELF_INTERSECTING, POINT_OUTSIDE_SELF_INTERSECTING_3)
|
||||
);
|
||||
Assert.assertFalse("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must not contain the dot "
|
||||
+ POINT_OUTSIDE_SELF_INTERSECTING_1,
|
||||
GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_OUTSIDE_SELF_INTERSECTING_1)
|
||||
);
|
||||
Assert.assertFalse("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must not contain the dot "
|
||||
+ POINT_OUTSIDE_SELF_INTERSECTING_2,
|
||||
GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_OUTSIDE_SELF_INTERSECTING_2)
|
||||
);
|
||||
Assert.assertFalse("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must not contain the dot "
|
||||
+ POINT_OUTSIDE_SELF_INTERSECTING_3,
|
||||
GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_OUTSIDE_SELF_INTERSECTING_3)
|
||||
);
|
||||
Assert.assertFalse("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must not contain the dot "
|
||||
+ POINT_INSIDE_SELF_INTERSECTING_UPPER_CENTER,
|
||||
GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_INSIDE_SELF_INTERSECTING_UPPER_CENTER)
|
||||
);
|
||||
Assert.assertFalse("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must not contain the dot "
|
||||
+ POINT_INSIDE_SELF_INTERSECTING_LOWER_CENTER,
|
||||
GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_INSIDE_SELF_INTERSECTING_LOWER_CENTER)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user