diff --git a/USING.md b/USING.md index b8d1560734..0ce1b50cef 100644 --- a/USING.md +++ b/USING.md @@ -120,6 +120,9 @@ module org.foo.baz { requires org.locationtech.jts.io.sde; // jts-io-sde } ``` +## JTS System Properties + +* `-Djts.overlay=ng` enables the use of OverlayNG in `Geometry` overlay methods. (*Note: in a future release this will become the default behaviour*) ## JTS Tools diff --git a/doc/JTSOp.md b/doc/JTSOp.md index cdea13e874..cdebead07c 100644 --- a/doc/JTSOp.md +++ b/doc/JTSOp.md @@ -4,7 +4,7 @@ * Execute JTS operations on geometry to produce the results, in various spatial formats * Chain together sequences of JTS operations to accomplish a spatial processing task -* Extract and transform geometry from data files +* Extract, subset, validate and transform geometry from data files * Convert geometry from one format into another * Generate geometry for testing or display purposes * Summarize the contents of geometry datafiles @@ -19,12 +19,16 @@ * standard input (WKT or WKB) * files in various formats (WKT, WKB, GeoJSON, GML, SHP) * a single input containing two geometries can supply both A and B (option `-ab`) +* Apply a limit and/or offset when reading from some file formats: + * `-limit` specifies a limit + * `-offseet` specified an offset + * supported for WKT, WKB, SHP file formats * Execute any spatial or scalar function available in the JTS TestBuilder * "spread" execution over each geometry component from one or both inputs * `-each [ a | b | ab | aa ]` * the `-each aa` parameter uses the A input for both arguments for binary operations * the `-index` parameter uses a spatial index for binary operations -* Run an operation multiple times using a sequence of different argument values +* Run an operation multiple times using a set of different argument values * `-args v1,v2,v3 ...` * Repeat operation execution multiple times, to provide better timing results * `-repeat n` @@ -36,8 +40,12 @@ * `-explode` * Display information about the input geometries and function timing * `-v` +* Display timing information + * `-time` * Load external spatial functions dynamically (as a Java class with static methods) * `-geomfunc classname` +* List all available functions + * `-help` * chain operations together by writing/reading input from `stdin` and using shell piping ## Examples @@ -86,6 +94,10 @@ jtsop -a "POINT (10 10)" -f geojson + * Validate geometries from a WKT file using limit and offset + + jtsop -a some-file-with-geom.wkt -limit 100 -offset 40 -f txt Geometry.isValid + * Compute an operation on a geometry and output only geometry metrics and timing jtsop -v -a some-geom.wkt Buffer.buffer 10 diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 9dd330d045..9386f66049 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -27,7 +27,13 @@ Distributions for older JTS versions can be obtained at the * Add `KMLReader` (#593) * Add `Densifier.setValidated` method to allow disabling expensive polygon validation (#595) * Add `OverlayNG` codebase (#599) +* Add system property `jts.overlay=ng` to enable use of OverlayNG in `Geometry` methods (#615) +* Add `SnapRoundingNoder` (#599) +* Add `SnappingNoder` (#599) +* Change `GeometryPrecisionReducer` to use OverlayNG with Snap-Rounding +* Change `GeometryNoder` to use `SnapRoundingNoder` * Add `KdTree` `size` and `depth` methods (#603) +* Improve `WKBWriter` to write empty Polygons using a more compact representation (#623) ### Bug Fixes @@ -35,6 +41,15 @@ Distributions for older JTS versions can be obtained at the * Fix `PackedCoordinateSequence` to always use XYZM coordinates when dimension is 4 (#591) * Fix `OrdinateFormat` to work around a JDK issue with the minus sign character in `Locale.NO` (#596) * Fix `GeoJsonReader` to throw a `ParseException` for empty arrays (#600) +* Fix `WKTFileReader` handling of files with large amount of whitespace (#616) +* Fix `WKBWriter` to output 3D empty Points with 3 ordinates (#622) +* Fix `Geometry.reverse` to handle all geometry structures (#628) + +## JTS TestBuilder + +### Functionality Improvements + +* Add Geometry Inspector sorting by Area or Length @@ -71,6 +86,11 @@ Distributions for older JTS versions can be obtained at the * Enhance `-geomfunc` to load multiple function classes * Fix function registry to replace matching loaded functions (#569) +## JtsOp + +* Added `-limit` and `-offset` options for reading from file inputs (#617) + + # Version 1.17.0 diff --git a/modules/app/src/main/java/org/locationtech/jtstest/cmd/CommandOptions.java b/modules/app/src/main/java/org/locationtech/jtstest/cmd/CommandOptions.java index 3226ed86dc..89830af7c5 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/cmd/CommandOptions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/cmd/CommandOptions.java @@ -38,5 +38,7 @@ public class CommandOptions { public static final String FORMAT_GEOJSON = "geojson"; public static final String FORMAT_SVG = "svg"; public static final String TIME = "time"; + public static final String LIMIT = "limit"; + public static final String OFFSET = "offset"; } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpCmd.java b/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpCmd.java index 43ed3e09ef..30bc865646 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpCmd.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpCmd.java @@ -17,6 +17,7 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.io.WKTConstants; +import org.locationtech.jtstest.cmd.JTSOpRunner.OpParams; import org.locationtech.jtstest.command.CommandLine; import org.locationtech.jtstest.command.Option; import org.locationtech.jtstest.command.OptionSpec; @@ -37,6 +38,9 @@ * --- Compute the area of a WKT geometry, output it * jtsop -a some-file-with-geom.wkt -f txt area * + * --- Validate geometries from a WKT file using limit and offset + * jtsop -a some-file-with-geom.wkt -limit 100 -offset 40 -f txt isValid + * * --- Compute the unary union of a WKT geometry, output as WKB * jtsop -a some-file-with-geom.wkt -f wkb Overlay.unaryUnion * @@ -75,7 +79,7 @@ public static void main(String[] args) JTSOpCmd cmd = new JTSOpCmd(); int rc = 1; try { - JTSOpRunner.OpParams cmdArgs = cmd.parseArgs(args); + OpParams cmdArgs = cmd.parseArgs(args); cmd.execute(cmdArgs); rc = 0; } @@ -104,12 +108,14 @@ private static CommandLine createCmdLine() { .addOptionSpec(new OptionSpec(CommandOptions.GEOMA, 1)) .addOptionSpec(new OptionSpec(CommandOptions.GEOMB, 1)) .addOptionSpec(new OptionSpec(CommandOptions.GEOMAB, 1)) - .addOptionSpec(new OptionSpec(CommandOptions.SRID, 1)) .addOptionSpec(new OptionSpec(CommandOptions.EACH, 1)) .addOptionSpec(new OptionSpec(CommandOptions.INDEX, 0)) .addOptionSpec(new OptionSpec(CommandOptions.EXPLODE, 0)) .addOptionSpec(new OptionSpec(CommandOptions.FORMAT, 1)) + .addOptionSpec(new OptionSpec(CommandOptions.LIMIT, 1)) + .addOptionSpec(new OptionSpec(CommandOptions.OFFSET, 1)) .addOptionSpec(new OptionSpec(CommandOptions.REPEAT, 1)) + .addOptionSpec(new OptionSpec(CommandOptions.SRID, 1)) .addOptionSpec(new OptionSpec(CommandOptions.VALIDATE, 0)) .addOptionSpec(new OptionSpec(OptionSpec.OPTION_FREE_ARGS, OptionSpec.NARGS_ONE_OR_MORE)); return commandLine; @@ -121,37 +127,45 @@ private static CommandLine createCmdLine() { " [ -a | | stdin | ]", " [ -b | | stdin | ]", " [ -ab | | stdin | ]", - " [ -srid ]", + " [ -limit ]", + " [ -offset ]", " [ -each ( a | b | ab | aa ) ]", " [ -index ]", " [ -repeat ]", " [ -validate ]", " [ -explode", + " [ -srid ]", " [ -f ( txt | wkt | wkb | geojson | gml | svg ) ]", - " [ -geomfunc ]", " [ -time ]", " [ -v, -verbose ]", " [ -help ]", + " [ -geomfunc ]", " [ -op ]", " [ op [ args... ]]", - " op name of the operation (Category.op)", + " op name of the operation (in format Category.op)", " args one or more scalar arguments to the operation", " - To run over multiple arguments use v1,v2,v3 OR val(v1,v2,v3,..)", "", + "===== Input options:", " -a Geometry A: literal, stdin (WKT or WKB), or filename (extension: WKT, WKB, GeoJSON, GML, SHP)", " -b Geometry A: literal, stdin (WKT or WKB), or filename (extension: WKT, WKB, GeoJSON, GML, SHP)", - " -srid Sets the SRID on output geometries", + " -limit Limits the number of geometries read from A, or B if specified", + " -offset Uses an offset to read geometries from A, or B if specified", + "===== Operation options:", " -each execute op on each component of A, B, both A & B, or A & A", " -index index geometry B", " -repeat repeat the operation N times", " -validate validate the result of each operation", + " -geomfunc specifies class providing geometry operations", + " -op separator to delineate operation arguments", + "===== Output options:", + " -srid Sets the SRID on output geometries", " -explode output atomic geometries", " -f output format to use. If omitted output is silent", - " -geomfunc specifies class providing geometry operations", + "===== Logging options:", " -time display execution time", " -v, -verbose display information about execution", - " -help print a list of available operations", - " -op separator for op arguments" + " -help print a list of available operations" }; private void printHelp(boolean showFunctions) { @@ -243,7 +257,7 @@ private static boolean isWKT(String arg) { if (arg.toUpperCase().endsWith(" " + WKTConstants.EMPTY)) return true; return false; } - + void execute(JTSOpRunner.OpParams cmdArgs) { if (isHelp || isHelpWithFunctions) { printHelp(isHelpWithFunctions); @@ -261,7 +275,7 @@ JTSOpRunner.OpParams parseArgs(String[] args) throws ParseException, ClassNotFou } commandLine.parse(args); - JTSOpRunner.OpParams cmdArgs = new JTSOpRunner.OpParams(); + OpParams cmdArgs = new JTSOpRunner.OpParams(); String argA = commandLine.getOptionArg(CommandOptions.GEOMA, 0); if (argA != null) { @@ -294,6 +308,14 @@ JTSOpRunner.OpParams parseArgs(String[] args) throws ParseException, ClassNotFou cmdArgs.isExplode = commandLine.hasOption(CommandOptions.EXPLODE); + int paramLimit = commandLine.hasOption(CommandOptions.LIMIT) + ? commandLine.getOptionArgAsInt(CommandOptions.LIMIT, 0) + : -1; + + int paramOffset = commandLine.hasOption(CommandOptions.OFFSET) + ? commandLine.getOptionArgAsInt(CommandOptions.OFFSET, 0) + : 0; + cmdArgs.format = commandLine.getOptionArg(CommandOptions.FORMAT, 0); cmdArgs.srid = commandLine.hasOption(CommandOptions.SRID) @@ -358,9 +380,28 @@ else if (each.equalsIgnoreCase("aa")) { cmdArgs.argList = parseOpArg(freeArgs[1]); } } + + /** + * ====== Apply extra parameter logic + */ + //--- apply limit to A if no B, or else to B + // This allows applying a binary op with a fixed LHS to a limited set of RHS geoms + if (OpParams.isGeometryInput(cmdArgs.fileB, cmdArgs.geomB)) { + cmdArgs.limitB = paramLimit; + cmdArgs.offsetB = paramOffset; + } + else { + cmdArgs.limitA = paramLimit; + cmdArgs.offsetA = paramOffset; + } + return cmdArgs; } + private void applyParameters() { + + } + private String[] parseOpArg(String arg) { if (isArgMultiValues(arg)) { return parseValues(arg); @@ -420,7 +461,4 @@ private String[] parseMacroArgs(String macroTerm) { String args = macroTerm.substring(indexLeft + 1, indexRight); return args.split(","); } - - - } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java b/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java index 41fba18a9f..df6d08dc89 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java @@ -30,8 +30,8 @@ import org.locationtech.jtstest.geomfunction.GeometryFunction; import org.locationtech.jtstest.geomfunction.GeometryFunctionRegistry; import org.locationtech.jtstest.testbuilder.ui.SwingUtil; -import org.locationtech.jtstest.util.io.IOUtil; import org.locationtech.jtstest.util.io.MultiFormatBufferedReader; +import org.locationtech.jtstest.util.io.MultiFormatFileReader; import org.locationtech.jtstest.util.io.MultiFormatReader; /** @@ -82,23 +82,43 @@ public class JTSOpRunner { private boolean isTime; static class OpParams { - String operation; + static final int OFFSET_DEFAULT = 0; + static final int LIMIT_DEFAULT = -1; + public String fileA; String geomA; + public int limitA = LIMIT_DEFAULT; + public int offsetA = OFFSET_DEFAULT; + public String fileB; public String geomB; + public int limitB = LIMIT_DEFAULT; + public int offsetB = OFFSET_DEFAULT; + public boolean isGeomAB = false; - //public String[] arg1; String format = null; public Integer repeat; public boolean eachA = false; public boolean eachB = false; public boolean eachAA = false; - public String[] argList; public boolean validate = false; public boolean isIndexed = false; public boolean isExplode = false; public int srid; + + String operation; + public String[] argList; + + /** + * Tests whether an input geometry has been supplied. + * + * @param file + * @param geom + * @return true if an input geometry is present + */ + static boolean isGeometryInput(String file, String geom) { + return file != null || geom != null; + } } public JTSOpRunner() { @@ -144,10 +164,10 @@ void execute(OpParams param) { loadGeometry(); if (geomA != null) { - printGeometrySummary("A", geomA, param.fileA); + printGeometrySummary("A", geomA, fileInfo(param.fileA, param.limitA, param.offsetA) ); } if (geomB != null) { - printGeometrySummary("B", geomB, param.fileB); + printGeometrySummary("B", geomB, fileInfo(param.fileB, param.limitB, param.offsetB) ); } //--- If -each aa specified, use A for B @@ -179,16 +199,17 @@ private GeometryFactory createGeometryFactory(int srid) { private void loadGeometry() { if (param.isGeomAB) { + //--- limiting is not used for AB reading loadGeometryAB(); } else { - geomA = readGeometry("A", param.fileA, param.geomA); - geomB = readGeometry("B", param.fileB, param.geomB); + geomA = readGeometry("A", param.fileA, param.geomA, param.limitA, param.offsetA); + geomB = readGeometry("B", param.fileB, param.geomB, param.limitB, param.offsetB); } } private void loadGeometryAB() { - Geometry geomAB = readGeometry("AB", param.fileA, param.geomA); + Geometry geomAB = readGeometry("AB", param.fileA, param.geomA, OpParams.LIMIT_DEFAULT, OpParams.OFFSET_DEFAULT); if (geomAB.getNumGeometries() < 2) { throw new CommandError(ERR_REQUIRED_B); } @@ -236,7 +257,7 @@ private void executeFunctionSpreadA(FunctionInvoker fun) { private void executeFunctionSpreadB(Geometry geomA, FunctionInvoker fun, String header) { int numGeom = 1; - if (fun.isBinaryGeom()) { + if ( fun.isBinaryGeom() && geomB != null ) { numGeom = geomB.getNumGeometries(); } boolean isSpread = geomB != null @@ -351,19 +372,19 @@ private void validate(Object result) { logError("Result is invalid"); } } - + /** * Reads a geometry from a literal or a filename. * If neither are provided this geometry is not present. * * @param geomLabel label for geometry being read - * @param filename the filename to read from, if present - * @param geom the geometry literal, if present + * @param filename the filename to read from, if present, or null + * @param geom the geometry literal, if present, or null * @param geomA2 * @return the geometry read, or null * @throws Exception */ - private Geometry readGeometry(String geomLabel, String filename, String geom) { + private Geometry readGeometry(String geomLabel, String filename, String geom, int limit, int offset) { String geomDesc = " " + geomLabel + " "; if (geom != null) { // read a literal from the argument @@ -387,7 +408,7 @@ private Geometry readGeometry(String geomLabel, String filename, String geom) { } try { - return IOUtil.readFile(filename, geomFactory ); + return MultiFormatFileReader.readFile(filename, limit, offset, geomFactory ); } catch (FileNotFoundException ex) { throw new CommandError(ERR_FILE_NOT_FOUND, filename); @@ -466,24 +487,35 @@ private void printGeometrySummary(String label, Geometry geom, String source) { printlnInfo( GeometryOutput.writeGeometrySummary(label, geom) + srcname); } + private static String fileInfo(String filename, int limit, int offset) { + if (filename == null) return null; + String info = filename; + if (limit > OpParams.LIMIT_DEFAULT) info += " LIMIT " + limit; + if (offset > OpParams.OFFSET_DEFAULT) info += " OFFSET " + offset; + return info; + } + private void checkFunctionArgs(GeometryFunction func, Geometry geomB, String[] argList) { Class[] paramTypes = func.getParameterTypes(); int nParam = paramTypes.length; + /* + // disable this check for now, since it does not handle functions where B is optional if (func.isBinary() && geomB == null) throw new CommandError(ERR_REQUIRED_B); - /** - // MD not sure whether to check this? - if (! func.isBinary() && geomB != null) - throw new CommandError(ERR_REQUIRED_B); - */ + */ /* * check count of supplied args. * Assumes B has been checked. */ int argCount = 0; - if (func.isBinary() && geomB != null) argCount++; + if (func.isBinary() + // disable B check for now + // && geomB != null + ) { + argCount++; + } if (argList != null) argCount++; if (nParam != argCount) { throw new CommandError(ERR_WRONG_ARG_COUNT, func.getName()); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/AffineTransformationFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/AffineTransformationFunctions.java index 4229037002..ca36a87997 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/AffineTransformationFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/AffineTransformationFunctions.java @@ -18,7 +18,7 @@ public class AffineTransformationFunctions { - @Metadata(description="Transforms a geometry using one to three control vectors") + @Metadata(description="Transforms a geometry using 1, 2 or 3 control vectors") public static Geometry transformByVectors(Geometry g, Geometry control) { int nControl = control.getNumGeometries(); @@ -85,7 +85,9 @@ private static AffineTransformation viewportTrans(Envelope srcEnv, Envelope view return trans; } - public static Geometry scale(Geometry g, double scale) + public static Geometry scale(Geometry g, + @Metadata(title="Scale factor") + double scale) { Coordinate centre = envelopeCentre(g); AffineTransformation trans = AffineTransformation.scaleInstance(scale, scale, centre.x, centre.y); @@ -170,4 +172,14 @@ public static Geometry translateToOrigin(Geometry g) AffineTransformation trans = AffineTransformation.translationInstance(-lowerLeft.x, -lowerLeft.y); return trans.transform(g); } + @Metadata(description="Translates a geometry by an offset (dx,dy)") + public static Geometry translate(Geometry g, + @Metadata(title="dX") + double dx, + @Metadata(title="dY") + double dy) + { + AffineTransformation trans = AffineTransformation.translationInstance(dx, dy); + return trans.transform(g); + } } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/CreateRandomShapeFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/CreateRandomShapeFunctions.java index 1c39fcdfeb..708e313afa 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/CreateRandomShapeFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/CreateRandomShapeFunctions.java @@ -216,6 +216,33 @@ public static Geometry randomSegmentsInGrid(Geometry g, int nPts) { return geomFact.buildGeometry(lines); } + public static Geometry randomSegmentsRectilinear(Geometry g, int nPts) { + Envelope env = FunctionsUtil.getEnvelopeOrDefault(g); + GeometryFactory geomFact = FunctionsUtil.getFactoryOrDefault(g); + double xLen = env.getWidth(); + double yLen = env.getHeight(); + + List lines = new ArrayList(); + + for (int i = 0; i < nPts; i++) { + double x0 = env.getMinX() + xLen * Math.random(); + double x1 = env.getMinY() + yLen * Math.random(); + double v = env.getMinX() + xLen * Math.random(); + double y0 = v; + double y1 = v; + boolean isXFixed = Math.random() < 0.5; + if (isXFixed) { + y0 = x0; + y1 = x1; + x0 = v; + x1 = v; + } + lines.add(geomFact.createLineString(new Coordinate[] { + new Coordinate(x0, y0), new Coordinate(x1, y1) })); + } + return geomFact.buildGeometry(lines); + } + public static Geometry randomLineString(Geometry g, int nPts) { Envelope env = FunctionsUtil.getEnvelopeOrDefault(g); GeometryFactory geomFact = FunctionsUtil.getFactoryOrDefault(g); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/FunctionsUtil.java b/modules/app/src/main/java/org/locationtech/jtstest/function/FunctionsUtil.java index f36d5432b3..ec135a8c01 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/FunctionsUtil.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/FunctionsUtil.java @@ -12,6 +12,7 @@ package org.locationtech.jtstest.function; +import java.awt.Color; import java.awt.Graphics2D; import java.util.List; @@ -48,14 +49,25 @@ public static GeometryFactory getFactoryOrDefault(Geometry g1, Geometry g2) return JTSTestBuilder.getGeometryFactory(); } + public static boolean isShowingIndicators() { + return JTSTestBuilderFrame.isShowingIndicators(); + } + public static void showIndicator(Geometry geom) { + showIndicator(geom, AppConstants.INDICATOR_LINE_CLR); + } + + public static void showIndicator(Geometry geom, Color lineClr) + { + if (! isShowingIndicators()) return; + GeometryEditPanel panel = JTSTestBuilderFrame .instance().getTestCasePanel() .getGeometryEditPanel(); Graphics2D gr = (Graphics2D) panel.getGraphics(); GeometryPainter.paint(geom, panel.getViewport(), gr, - AppConstants.INDICATOR_LINE_CLR, + lineClr, AppConstants.INDICATOR_FILL_CLR); } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/NodingFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/NodingFunctions.java index 6c2dc4b3f1..9df4b396f1 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/NodingFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/NodingFunctions.java @@ -148,7 +148,7 @@ private static List createSegmentStrings(Geometry geom) @Metadata(description="Nodes input using the SnappingNoder") public static Geometry snappingNoder(Geometry geom, Geometry geom2, - @Metadata(description="Snapping distance") + @Metadata(title="Snap distance") double snapDistance) { List segs = createSegmentStrings(geom); @@ -164,7 +164,7 @@ public static Geometry snappingNoder(Geometry geom, Geometry geom2, @Metadata(description="Nodes input using the SnapRoundingNoder") public static Geometry snapRoundingNoder(Geometry geom, Geometry geom2, - @Metadata(description="Scale factor") + @Metadata(title="Scale factor") double scaleFactor) { List segs = createSegmentStrings(geom); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayNGFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayNGFunctions.java index fb94adc792..d65d03d1b5 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayNGFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayNGFunctions.java @@ -41,7 +41,9 @@ public static Geometry symDifference(Geometry a, Geometry b) { return OverlayNG.overlay(a, b, SYMDIFFERENCE ); } - public static Geometry union(Geometry a, Geometry b) { + public static Geometry union(Geometry a, + @Metadata(isRequired=false) + Geometry b) { return OverlayNG.overlay(a, b, UNION ); } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayNGTestFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayNGTestFunctions.java index 89c4a56f2b..c62e993679 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayNGTestFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayNGTestFunctions.java @@ -124,6 +124,16 @@ public static Geometry edgesUnionResult(Geometry a, Geometry b, double scaleFact return ovr.getResult(); } + public static Geometry edgesUnionAll(Geometry a, Geometry b, double scaleFactor) { + PrecisionModel pm = new PrecisionModel(scaleFactor); + // force non-null inputs + a = sameOrEmpty(a, b); + b = sameOrEmpty(b, a); + OverlayNG ovr = new OverlayNG(a, b, pm, UNION); + ovr.setOutputEdges(true); + return ovr.getResult(); + } + public static Geometry intersectionSRNoOpt(Geometry a, Geometry b, double scaleFactor) { PrecisionModel pm = new PrecisionModel(scaleFactor); OverlayNG ovr = new OverlayNG(a, b, pm, INTERSECTION); @@ -224,4 +234,10 @@ private static Geometry toLines(List sections, GeometryFactory fac if (lines.length == 1) return lines[0]; return factory.createMultiLineString(lines); } + + public static Geometry edgesOverlayResult(Geometry a) { + OverlayNG ovr = new OverlayNG(a, null, UNION); + ovr.setOutputResultEdges(true); + return ovr.getResult(); + } } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/PolygonOverlayFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/PolygonOverlayFunctions.java index c9b40335cc..dd39215497 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/PolygonOverlayFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/PolygonOverlayFunctions.java @@ -16,39 +16,193 @@ import java.util.Collection; import java.util.List; +import org.locationtech.jts.algorithm.locate.SimplePointInAreaLocator; +import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Point; import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.geom.PrecisionModel; import org.locationtech.jts.geom.util.LinearComponentExtracter; -import org.locationtech.jts.noding.snapround.GeometryNoder; +import org.locationtech.jts.index.strtree.STRtree; +import org.locationtech.jts.operation.overlayng.OverlayNG; +import org.locationtech.jts.operation.overlayng.OverlayNGRobust; import org.locationtech.jts.operation.polygonize.Polygonizer; - +import org.locationtech.jtstest.geomfunction.Metadata; public class PolygonOverlayFunctions { - public static Geometry overlaySnapRounded(Geometry g1, Geometry g2, double precisionTol) + public static Geometry overlaySR(Geometry g1, Geometry g2, + @Metadata(title="Scale factor") + double scale) + { + PrecisionModel pm = new PrecisionModel(scale); + return computeOverlay(g1, g2, new Noder() { + public Geometry node(Geometry inputLines) { + return OverlayNG.overlay(inputLines, null, OverlayNG.UNION, pm); + } + }); + } + + public static Geometry overlay(Geometry g1, Geometry g2) + { + return computeOverlay(g1, g2, new Noder( ) { + public Geometry node(Geometry inputLines) { + return OverlayNGRobust.overlay(inputLines, null, OverlayNG.UNION); + } + }); + } + + interface Noder { + Geometry node(Geometry inputLines); + } + + @Metadata(description="Nodes linework using Snapping iterated until noding is valid") + public static Geometry overlayIterSnap(Geometry g1, Geometry g2, double snapTol) + { + Geometry result = computeOverlay(g1, g2, new IteratedSnappingNoder( snapTol ) ); + if (result == null) { + throw new RuntimeException("Unable to compute valid noding using iterated snapping"); + } + return result; + } + + /** + * Input geometry may be lines or polygons. + * + * @param g1 + * @param g2 a geometry to overlay (may be null) + * @param noder + * @return Noded, polygonized dataset + */ + private static Geometry computeOverlay(Geometry g1, Geometry g2, Noder noder) { - PrecisionModel pm = new PrecisionModel(precisionTol); GeometryFactory geomFact = g1.getFactory(); - + List lines = LinearComponentExtracter.getLines(g1); // add second input's linework, if any if (g2 != null) LinearComponentExtracter.getLines(g2, lines); - List nodedLinework = new GeometryNoder(pm).node(lines); - // union the noded linework to remove duplicates - Geometry nodedDedupedLinework = geomFact.buildGeometry(nodedLinework).union(); + Geometry inputLines = g1.getFactory().buildGeometry(lines); + Geometry nodedDedupedLinework = noder.node(inputLines); + // polygonize the result Polygonizer polygonizer = new Polygonizer(); polygonizer.add(nodedDedupedLinework); - Collection polys = polygonizer.getPolygons(); + List resultants = (List) polygonizer.getPolygons(); + + // use PIP to find polygons which have a parent + List polys = ParentFinder.findParents(g1, g2, resultants); // convert to collection for return Polygon[] polyArray = GeometryFactory.toPolygonArray(polys); return geomFact.createGeometryCollection(polyArray); } + + private static Geometry node(Geometry inputLines, PrecisionModel pm) { + if (pm == null) { + return OverlayNGRobust.overlay(inputLines, null, OverlayNG.UNION); + } + return OverlayNG.overlay(inputLines, null, OverlayNG.UNION, pm); + } + + static class IteratedSnappingNoder implements Noder { + + private double snapTol; + public IteratedSnappingNoder(double snapTol) { + this.snapTol = snapTol; + } + + @Override + public Geometry node(Geometry geom) { + double snapDist = snapTol; + int count = 0; + while (count < 10) { + Geometry noded = nodeSnapDedup(geom, snapDist); + if (noded != null) + return noded; + // try increasing distance + snapDist = 2 * snapDist; + count++; + } + // FAIL! + return null; + } + + private Geometry nodeSnapDedup(Geometry geom, double snapDist) { + Geometry noded = NodingFunctions.snappingNoder(geom, null, snapDist); + Geometry dedup = DissolveFunctions.dissolve(noded); + Geometry intNodes = NodingFunctions.findInteriorNodes(dedup); + + // not full noded at given snap distance + if (! intNodes.isEmpty()) + return null; + + // success! + return dedup; + } + } + + /** + * Finds parentage of a set of overlay resultants. + * Currently just finds set of resultants which have at least one parent . + * This effectively removes holes from the result set. + * + * @author mdavis + * + */ + static class ParentFinder { + + public static List findParents(Geometry source1, Geometry source2, List resultants) { + ParentFinder hd = new ParentFinder(); + hd.addSourcePolygons(source1); + hd.addSourcePolygons(source2); + return hd.findParents(resultants); + } + + /** + * Spatial index containing source polygons + */ + private STRtree sourceIndex = new STRtree(); + + public ParentFinder() { + + } + + public void addSourcePolygons(Geometry source) { + if (source == null || source.getDimension() < 2) return; + for (int i = 0; i < source.getNumGeometries(); i++) { + Geometry geom = source.getGeometryN(i); + if (geom instanceof Polygon) { + sourceIndex.insert(geom.getEnvelopeInternal(), geom); + } + } + } + + public List findParents(List resultants) { + List polys = new ArrayList(); + for (Polygon res : resultants) { + Point intPt = res.getInteriorPoint(); + Coordinate intCoord = intPt.getCoordinate(); + + List candidates = sourceIndex.query(intPt.getEnvelopeInternal()); + for (Polygon cand : candidates) { + + boolean isParent = SimplePointInAreaLocator.isContained(intCoord, cand); + if (isParent) { + /** + * For now, keep resultants which have at least one parent. + * This could be enhanced to record all parents of a resultant. + */ + polys.add(res); + break; + } + } + } + return polys; + } + } } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/PrecisionFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/PrecisionFunctions.java index 77ddaecfc5..fb699ce7c4 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/PrecisionFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/PrecisionFunctions.java @@ -14,18 +14,23 @@ import org.locationtech.jts.geom.*; import org.locationtech.jts.precision.*; +import org.locationtech.jtstest.geomfunction.Metadata; public class PrecisionFunctions { - public static Geometry reducePrecisionPointwise(Geometry geom, double scaleFactor) + public static Geometry reducePrecisionPointwise(Geometry geom, + @Metadata(title="Scale factor") + double scaleFactor) { PrecisionModel pm = new PrecisionModel(scaleFactor); Geometry reducedGeom = GeometryPrecisionReducer.reducePointwise(geom, pm); return reducedGeom; } - public static Geometry reducePrecision(Geometry geom, double scaleFactor) + public static Geometry reducePrecision(Geometry geom, + @Metadata(title="Scale factor") + double scaleFactor) { PrecisionModel pm = new PrecisionModel(scaleFactor); Geometry reducedGeom = GeometryPrecisionReducer.reduce(geom, pm); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/SortingFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/SortingFunctions.java index 35165391e8..2b4711e1f0 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/SortingFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/SortingFunctions.java @@ -48,6 +48,28 @@ public static Geometry sortByArea(Geometry g) return g.getFactory().buildGeometry(geoms); } + public static Geometry sortByMinX(Geometry g) + { + List geoms = components(g); + // annotate geometries with area + for (Geometry geom : geoms) { + geom.setUserData(geom.getEnvelopeInternal().getMinX()); + } + Collections.sort(geoms, new UserDataDoubleComparator()); + return g.getFactory().buildGeometry(geoms); + } + + public static Geometry sortByMinY(Geometry g) + { + List geoms = components(g); + // annotate geometries with area + for (Geometry geom : geoms) { + geom.setUserData(geom.getEnvelopeInternal().getMinY()); + } + Collections.sort(geoms, new UserDataDoubleComparator()); + return g.getFactory().buildGeometry(geoms); + } + private static List components(Geometry g) { List comp = new ArrayList(); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/BaseGeometryFunction.java b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/BaseGeometryFunction.java index 83ef994b82..006d8e1dc4 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/BaseGeometryFunction.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/BaseGeometryFunction.java @@ -50,6 +50,7 @@ public static int firstScalarParamIndex(GeometryFunction func) { protected String[] parameterNames; protected Class[] parameterTypes; protected Class returnType; + protected boolean isRequiredB = true; public BaseGeometryFunction( String category, @@ -121,6 +122,10 @@ public boolean isBinary() { return parameterTypes.length > 0 && parameterTypes[0] == Geometry.class; } + public boolean isRequiredB() { + return isRequiredB; + } + public String getSignature() { StringBuffer paramTypes = new StringBuffer(); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/GeometryFunction.java b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/GeometryFunction.java index 6f2ce77618..d752170c1e 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/GeometryFunction.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/GeometryFunction.java @@ -95,6 +95,8 @@ public interface GeometryFunction */ boolean equals(Object obj); - public abstract boolean isBinary(); + boolean isBinary(); + + boolean isRequiredB(); } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/GeometryFunctionRegistry.java b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/GeometryFunctionRegistry.java index 124c5826dd..e05ddb39b9 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/GeometryFunctionRegistry.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/GeometryFunctionRegistry.java @@ -13,14 +13,19 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; -import org.locationtech.jts.geom.*; +import org.locationtech.jts.geom.Geometry; import org.locationtech.jtstest.function.AffineTransformationFunctions; import org.locationtech.jtstest.function.BoundaryFunctions; import org.locationtech.jtstest.function.BufferByUnionFunctions; import org.locationtech.jtstest.function.BufferFunctions; -import org.locationtech.jtstest.function.LineSegmentFunctions; import org.locationtech.jtstest.function.ConstructionFunctions; import org.locationtech.jtstest.function.ConversionFunctions; import org.locationtech.jtstest.function.CreateFractalShapeFunctions; @@ -34,19 +39,20 @@ import org.locationtech.jtstest.function.GeometryFunctions; import org.locationtech.jtstest.function.JTSFunctions; import org.locationtech.jtstest.function.LineHandlingFunctions; +import org.locationtech.jtstest.function.LineSegmentFunctions; import org.locationtech.jtstest.function.LinearReferencingFunctions; import org.locationtech.jtstest.function.NodingFunctions; import org.locationtech.jtstest.function.OffsetCurveFunctions; import org.locationtech.jtstest.function.OrientationFunctions; import org.locationtech.jtstest.function.OverlayFunctions; import org.locationtech.jtstest.function.OverlayNGFunctions; +import org.locationtech.jtstest.function.OverlayNGOptFunctions; import org.locationtech.jtstest.function.OverlayNGRobustFunctions; -import org.locationtech.jtstest.function.OverlayNoSnapFunctions; import org.locationtech.jtstest.function.OverlayNGSRFunctions; -import org.locationtech.jtstest.function.OverlayNGOptFunctions; import org.locationtech.jtstest.function.OverlayNGSnappingFunctions; import org.locationtech.jtstest.function.OverlayNGStrictFunctions; import org.locationtech.jtstest.function.OverlayNGTestFunctions; +import org.locationtech.jtstest.function.OverlayNoSnapFunctions; import org.locationtech.jtstest.function.PointLocationFunctions; import org.locationtech.jtstest.function.PolygonOverlayFunctions; import org.locationtech.jtstest.function.PolygonizeFunctions; @@ -87,7 +93,6 @@ public static GeometryFunctionRegistry createTestBuilderRegistry() funcRegistry.add(LineHandlingFunctions.class); funcRegistry.add(NodingFunctions.class); funcRegistry.add(PolygonizeFunctions.class); - funcRegistry.add(PolygonOverlayFunctions.class); funcRegistry.add(PrecisionFunctions.class); funcRegistry.add(PreparedGeometryFunctions.class); funcRegistry.add(SelectionFunctions.class); @@ -118,10 +123,9 @@ public static GeometryFunctionRegistry createTestBuilderRegistry() funcRegistry.add(OverlayNGOptFunctions.class); - - funcRegistry.add(OverlayNoSnapFunctions.class); funcRegistry.add(PointLocationFunctions.class); + funcRegistry.add(PolygonOverlayFunctions.class); //funcRegistry.add(OverlayEnhancedPrecisionFunctions.class); //funcRegistry.add(OverlayCommonBitsRemovedFunctions.class); funcRegistry.add(SnappingFunctions.class); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/Metadata.java b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/Metadata.java index a29535ff8a..68df941c35 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/Metadata.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/Metadata.java @@ -19,4 +19,5 @@ String name() default ""; String title() default ""; String description() default ""; + boolean isRequired() default true; } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/MetadataUtil.java b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/MetadataUtil.java index 0b27c66f02..8fdf68ea78 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/MetadataUtil.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/MetadataUtil.java @@ -37,4 +37,15 @@ public static String title(Annotation[] anno) { return null; } + public static boolean isRequired(Annotation[] anno) { + for (int i = 0; i < anno.length; i++) { + if (anno[i] instanceof Metadata) { + Metadata doc = (Metadata) anno[i]; + if (doc != null) + return doc.isRequired(); + } + } + return true; + } + } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/RepeaterGeometryFunction.java b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/RepeaterGeometryFunction.java index 8b6fc15961..5bbb6297e5 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/RepeaterGeometryFunction.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/RepeaterGeometryFunction.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2020 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.geomfunction; import java.util.ArrayList; @@ -9,14 +20,26 @@ import org.locationtech.jtstest.function.FunctionsUtil; import org.locationtech.jtstest.util.ClassUtil; +/** + * Repeats a function a given number of times. + * If the function has a single numeric argument, + * the argument will be multiplied by the repeat counter for every call, + * and the function results will be accumulated + * into a collection to provide the final result. + * + * @author Martin Davis + * + */ public class RepeaterGeometryFunction implements GeometryFunction { private GeometryFunction fun; private int count; + private boolean hasRepeatableArg; public RepeaterGeometryFunction(GeometryFunction fun, int count) { this.fun = fun; this.count = count; + hasRepeatableArg = hasRepeatableArg(fun); } public String getCategory() { @@ -55,6 +78,10 @@ public boolean isBinary() { return fun.isBinary(); } + public boolean isRequiredB() { + return fun.isRequiredB(); + } + public Object invoke(Geometry geom, Object[] args) { if (! isRepeatable(fun)) { @@ -63,7 +90,9 @@ public Object invoke(Geometry geom, Object[] args) { //TODO: handle repeating methods with integer arg int repeatArgIndex = repeatableArgIndex(fun); - Double argStart = ClassUtil.toDouble(args[repeatArgIndex]); + double argStart = 0; + if (repeatArgIndex < args.length) + argStart = ClassUtil.toDouble(args[repeatArgIndex]); return invokeRepeated(geom, args, argStart); } @@ -72,23 +101,28 @@ public static boolean isRepeatable(GeometryFunction fun) { Class[] paramType = fun.getParameterTypes(); int repeatArgIndex = repeatableArgIndex(fun); - // abort if no repeatable parameter - if (paramType.length < repeatArgIndex + 1) return false; + + // allow no repeatable arg + if (paramType.length < repeatArgIndex + 1) return true; + Class type = paramType[repeatArgIndex]; if (! ClassUtil.isDouble(type)) return false; - /* - Double argBase = ClassUtil.toDouble(args[0]); - if (argBase == null) return false; - */ - return true; } + public static boolean hasRepeatableArg(GeometryFunction fun) { + Class[] paramType = fun.getParameterTypes(); + int numParam = paramType.length; + int numGeomParam = fun.isBinary() ? 1 : 0; + return numParam > numGeomParam; + } + public static int repeatableArgIndex(GeometryFunction fun) { if (fun.isBinary()) return 1; return 0; } + private Object invokeRepeated(Geometry geom, Object[] args, double argStart) { List results = new ArrayList(); int repeatArgIndex = repeatableArgIndex(fun); @@ -96,16 +130,20 @@ private Object invokeRepeated(Geometry geom, Object[] args, double argStart) { double val = argStart * i; Geometry result = (Geometry) fun.invoke(geom, copyArgs(args, repeatArgIndex, val)); if (result == null) continue; - - FunctionsUtil.showIndicator(result); - results.add(result); + //System.out.println("Repeat: " + i); + if (hasRepeatableArg || i == 1) { + FunctionsUtil.showIndicator(result); + results.add(result); + } } return geom.getFactory().buildGeometry(results); } private Object[] copyArgs(Object[] args, int replaceIndex, double val) { Object[] newArgs = args.clone(); - newArgs[replaceIndex] = val; + // only copy arg if there is a repeatable arg + if (newArgs.length > replaceIndex) + newArgs[replaceIndex] = val; return newArgs; } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/SpreaderGeometryFunction.java b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/SpreaderGeometryFunction.java index a3abbcbd01..c907800d13 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/SpreaderGeometryFunction.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/SpreaderGeometryFunction.java @@ -52,7 +52,9 @@ public String getSignature() { public boolean isBinary() { return fun.isBinary(); } - + public boolean isRequiredB() { + return fun.isRequiredB(); + } public Object invoke(Geometry geom, Object[] args) { List result = new ArrayList(); if (isEachA) { diff --git a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/StaticMethodGeometryFunction.java b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/StaticMethodGeometryFunction.java index 73fd761c5b..319bc7ea7c 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/StaticMethodGeometryFunction.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/StaticMethodGeometryFunction.java @@ -128,6 +128,18 @@ private static Class[] extractParamTypes(Method method) return types; } + + private static boolean extractRequiredB(Method method) { + Annotation[][] anno = method.getParameterAnnotations(); + if (anno.length <= 1) return false; + Class[] methodParamTypes = method.getParameterTypes(); + boolean isRequired = false; + if (methodParamTypes[1] == Geometry.class) { + isRequired = MetadataUtil.isRequired(anno[1]); + } + return isRequired; + } + private Method method; public StaticMethodGeometryFunction( @@ -141,8 +153,9 @@ public StaticMethodGeometryFunction( { super(category, name, description, parameterNames, parameterTypes, returnType); this.method = method; + isRequiredB = extractRequiredB(method); } - + public Object invoke(Geometry g, Object[] arg) { return invoke(method, null, createFullArgs(g, arg)); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryFunctionTreePanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryFunctionTreePanel.java index bad94ee848..b17e5639ec 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryFunctionTreePanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryFunctionTreePanel.java @@ -53,8 +53,11 @@ private static GeometryFunction getFunctionFromNode(Object value) { } private class GeometryFunctionRenderer extends DefaultTreeCellRenderer { - private final ImageIcon binaryIcon = new ImageIcon(this.getClass() - .getResource("BinaryGeomFunction.png")); + private final ImageIcon binaryIcon = new ImageIcon(this.getClass() + .getResource("BinaryGeomFunction.png")); + + private final ImageIcon binaryOptBIcon = new ImageIcon(this.getClass() + .getResource("BinaryGeomFunctionOptB.png")); private final ImageIcon unaryIcon = new ImageIcon(this.getClass() .getResource("UnaryGeomFunction.png")); @@ -69,7 +72,7 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, hasFocus); if (leaf) { GeometryFunction func = getFunctionFromNode(value); - setIcon(func.isBinary() ? binaryIcon : unaryIcon); + setIcon( computeIcon(func) ); //String name = StringUtil.capitalize(func.getName()); String name = func.getName(); setText(name); @@ -80,6 +83,14 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, return this; } + private ImageIcon computeIcon(GeometryFunction func) { + ImageIcon icon = unaryIcon; + if (func.isBinary()) { + icon = func.isRequiredB() ? binaryIcon : binaryOptBIcon; + } + return icon; + } + } public GeometryFunctionTreePanel() { diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryTreeModel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryTreeModel.java index 853e8b6609..42c825aaa5 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryTreeModel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryTreeModel.java @@ -15,6 +15,7 @@ import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.Vector; @@ -37,13 +38,18 @@ public class GeometryTreeModel implements TreeModel { + public static Comparator SORT_AREA_ASC = new AreaComparator(false); + public static Comparator SORT_AREA_DESC = new AreaComparator(true); + public static Comparator SORT_LEN_ASC = new LengthComparator(false); + public static Comparator SORT_LEN_DESC = new LengthComparator(true); + private Vector treeModelListeners = new Vector(); private GeometricObjectNode rootGeom; - public GeometryTreeModel(Geometry geom, int source) + public GeometryTreeModel(Geometry geom, int source, Comparator comp) { - rootGeom = GeometryNode.create(geom, new GeometryContext(source)); + rootGeom = GeometryNode.create(geom, new GeometryContext(source, comp)); } // ////////////// TreeModel interface implementation /////////////////////// @@ -117,6 +123,37 @@ public void valueForPathChanged(TreePath path, Object newValue) System.out .println("*** valueForPathChanged : " + path + " --> " + newValue); } + + public static class AreaComparator implements Comparator { + + private int dirFactor; + + public AreaComparator(boolean direction) { + this.dirFactor = direction ? 1 : -1; + } + + @Override + public int compare(Object o1, Object o2) { + double area1 = ((GeometricObjectNode) o1).getGeometry().getArea(); + double area2 = ((GeometricObjectNode) o2).getGeometry().getArea(); + return dirFactor * Double.compare(area1, area2); + } + } + public static class LengthComparator implements Comparator { + + private int dirFactor; + + public LengthComparator(boolean direction) { + this.dirFactor = direction ? 1 : -1; + } + + @Override + public int compare(Object o1, Object o2) { + double area1 = ((GeometricObjectNode) o1).getGeometry().getLength(); + double area2 = ((GeometricObjectNode) o2).getGeometry().getLength(); + return dirFactor * Double.compare(area1, area2); + } + } } abstract class GeometricObjectNode @@ -169,10 +206,24 @@ public String getText() class GeometryContext { int source = 0; + private Comparator comp; GeometryContext(int source) { this.source = source; } + + public GeometryContext(int source, Comparator comp) { + this.source = source; + this.comp = comp; + } + + public Comparator getComparator() { + return comp; + } + + public boolean isSorted() { + return comp != null; + } } abstract class GeometryNode extends GeometricObjectNode @@ -307,12 +358,15 @@ public ImageIcon getIcon() protected void fillChildren() { - children.add(new LinearRingNode((LinearRing) poly.getExteriorRing(), - "Shell", context)); for (int i = 0; i < poly.getNumInteriorRing(); i++) { children.add(new LinearRingNode((LinearRing) poly.getInteriorRingN(i), "Hole " + i, context)); } + if (context.isSorted()) { + children.sort(context.getComparator()); + } + children.add(0, new LinearRingNode((LinearRing) poly.getExteriorRing(), + "Shell", context)); } } @@ -426,6 +480,9 @@ protected void fillChildren() node.setIndex(i); children.add(node); } + if (context.isSorted()) { + children.sort(context.getComparator()); + } } public ImageIcon getIcon() diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryTreePanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryTreePanel.java index ce75f14266..f6ccc98c17 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryTreePanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryTreePanel.java @@ -143,9 +143,13 @@ private static Geometry getGeometryFromNode(Object value) { return ((GeometricObjectNode) value).getGeometry(); } - public void populate(Geometry geom, int source) { - tree.setModel(new GeometryTreeModel(geom, source)); - } + public void populate(Geometry geom, int source) { + tree.setModel(new GeometryTreeModel(geom, source, null)); + } + + public void populate(Geometry geom, int source, Comparator comp) { + tree.setModel(new GeometryTreeModel(geom, source, comp)); + } //Required by TreeWillExpandListener interface. public void treeWillExpand(TreeExpansionEvent e) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java index 96ca6e20d0..94d96b7491 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java @@ -15,7 +15,9 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; +import java.awt.FlowLayout; import java.awt.event.ActionEvent; +import java.util.Comparator; import javax.swing.Box; import javax.swing.BoxLayout; @@ -32,10 +34,10 @@ public class InspectorPanel extends TestBuilderPanel { private static final int BOX_SPACER = 5; - private final ImageIcon downIcon = IconLoader.icon("Down.png"); - private final ImageIcon upIcon = IconLoader.icon("Up.png"); - private final ImageIcon zoomIcon = IconLoader.icon("MagnifyCursor.gif"); - private final ImageIcon copyIcon = IconLoader.icon("Copy.png"); + private final ImageIcon downIcon = AppIcons.DOWN; + private final ImageIcon upIcon = AppIcons.UP; + private final ImageIcon zoomIcon = AppIcons.ZOOM; + private final ImageIcon copyIcon = AppIcons.COPY; GeometryTreePanel geomTreePanel; @@ -49,6 +51,14 @@ public class InspectorPanel extends TestBuilderPanel { private boolean showExpand = true; + private int source; + + private Geometry geometry; + + private Comparator sorterArea; + + private Comparator sorterLen; + public InspectorPanel() { this(true); } @@ -125,6 +135,9 @@ public void actionPerformed(ActionEvent e) { this.add(btnPanel, BorderLayout.WEST); if (showExpand) { + JPanel btn2Panel = new JPanel(); + btn2Panel.setLayout(new BoxLayout(btn2Panel, BoxLayout.PAGE_AXIS)); + btn2Panel.setPreferredSize(new java.awt.Dimension(30, 30)); btnExpand.setEnabled(true); btnExpand.setMaximumSize(new Dimension(30, 30)); btnExpand.setText("..."); @@ -135,12 +148,39 @@ public void actionPerformed(ActionEvent e) btnExpand_actionPerformed(); } }); - JPanel btn2Panel = new JPanel(); - btn2Panel.setLayout(new BoxLayout(btn2Panel, BoxLayout.PAGE_AXIS)); - btn2Panel.setPreferredSize(new java.awt.Dimension(30, 30)); btn2Panel.add(btnExpand); this.add(btn2Panel, BorderLayout.EAST); } + + JButton btnSortNone = SwingUtil.createButton(AppIcons.CLEAR, "Unsorted", new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + sortNone(); + } + }); + JButton btnSortByArea = SwingUtil.createButton(AppIcons.ICON_POLYGON, "Sort by Area (Asc/Desc)", new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + sortByArea(); + } + }); + JButton btnSortByLen = SwingUtil.createButton(AppIcons.ICON_LINESTRING, "Sort by Length (Asc/Desc)", new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + sortByLen(); + } + }); + + JPanel sortPanel = new JPanel(); + sortPanel.setLayout(new BoxLayout(sortPanel, BoxLayout.LINE_AXIS)); + sortPanel.add(Box.createRigidArea(new Dimension(160, 0))); + sortPanel.add(new JLabel("Sort")); + sortPanel.add(Box.createRigidArea(new Dimension(10, 0))); + sortPanel.add(btnSortNone); + //sortPanel.add(new JLabel(AppIcons.ICON_LINESTRING)); + sortPanel.add(Box.createRigidArea(new Dimension(10, 0))); + sortPanel.add(btnSortByLen); + //sortPanel.add(new JLabel(AppIcons.ICON_POLYGON)); + sortPanel.add(Box.createRigidArea(new Dimension(10, 0))); + sortPanel.add(btnSortByArea); + this.add(sortPanel, BorderLayout.NORTH); } private void btnExpand_actionPerformed() { @@ -160,10 +200,47 @@ private void btnCopy_actionPerformed(ActionEvent e) { SwingUtil.copyToClipboard(geom, isFormatted); } - public void setGeometry(String tag, Geometry a, int source) + public void setGeometry(String tag, Geometry geom, int source) { + this.source = source; + this.geometry = geom; + lblGeom.setText(tag); lblGeom.setForeground(source == 0 ? Color.BLUE : Color.RED); - geomTreePanel.populate(a, source); + + sortNone(); + } + + public void sortNone() + { + sorterLen = null; + sorterArea = null; + geomTreePanel.populate(geometry, source); } + + public void sortByArea() + { + sorterLen = null; + + if (sorterArea == GeometryTreeModel.SORT_AREA_ASC) { + sorterArea = GeometryTreeModel.SORT_AREA_DESC; + } + else { + sorterArea = GeometryTreeModel.SORT_AREA_ASC; + } + geomTreePanel.populate(geometry, source, sorterArea); + } + + public void sortByLen() + { + sorterArea = null; + if (sorterLen == GeometryTreeModel.SORT_LEN_ASC) { + sorterLen = GeometryTreeModel.SORT_LEN_DESC; + } + else { + sorterLen = GeometryTreeModel.SORT_LEN_ASC; + } + geomTreePanel.populate(geometry, source, sorterLen); + } + } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java index b04f8e8aaa..3fc5af929c 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java @@ -84,6 +84,8 @@ public class JTSTestBuilderFrame extends JFrame { private static JTSTestBuilderFrame singleton = null; + static boolean isShowingIndicators = true; + private ResultController resultController = new ResultController(this); private JTSTestBuilderMenuBar tbMenuBar = new JTSTestBuilderMenuBar(this); private JTSTestBuilderToolBar tbToolBar = new JTSTestBuilderToolBar(this); @@ -203,12 +205,26 @@ public static JTSTestBuilderFrame instance() { } return singleton; } - + /** + * Tests if the TestBuilder is running. + * Useful to allow functions to decide whether to show indicators + * (if functions are running under JtsOpCmd, they should not show indicators + * since that seriously impacts performance). + * + * @return true if there is a TestBuilder instance running + */ + public static boolean isRunning() { + return singleton != null; + } + public static boolean isShowingIndicators() { + return isRunning() && isShowingIndicators; + } + public static GeometryEditPanel getGeometryEditPanel() { return instance().getTestCasePanel().getGeometryEditPanel(); } - + public TestBuilderModel getModel() { return tbModel; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderMenuBar.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderMenuBar.java index 8f509a2ede..921d3ea8a0 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderMenuBar.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderMenuBar.java @@ -83,6 +83,13 @@ public void actionPerformed(ActionEvent e) { tbFrame.actionInspectGeometry(); } }); + JMenuItem menuShowIndicators = menuItemCheck("ShowIndicators", + JTSTestBuilderFrame.isShowingIndicators, + new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + JTSTestBuilderFrame.isShowingIndicators = ! JTSTestBuilderFrame.isShowingIndicators; + } + }); menuLoadXmlTestFile.setText("Open XML File(s)..."); menuLoadXmlTestFile.addActionListener( new java.awt.event.ActionListener() { @@ -173,10 +180,12 @@ public void actionPerformed(ActionEvent e) { jMenuView.add(menuViewText); jMenuView.add(menuViewGeometry); + jMenuEdit.addSeparator(); + jMenuView.add(menuShowIndicators); + //========================== jMenuEdit.setText("Edit"); jMenuEdit.add(deleteAllTestCasesMenuItem); - jMenuEdit.addSeparator(); jMenuEdit.add(precisionModelMenuItem); jMenuEdit.addSeparator(); jMenuEdit.add(removeDuplicatePoints); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerStylePanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerStylePanel.java index e961a48a70..27fc426c4e 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerStylePanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerStylePanel.java @@ -108,12 +108,12 @@ public void setLayer(Layer layer, boolean isModifiable) { cbEndpoint.setSelected(layer.getLayerStyle().isEndpoints()); cbDashed.setSelected(geomStyle().isDashed()); cbOffset.setSelected(layer.getLayerStyle().isOffset()); - offsetSizeModel.setValue( layer.getLayerStyle().getOffsetSize() ); + offsetSizeModel.setValue(layer.getLayerStyle().getOffsetSize() ); cbStroked.setSelected(geomStyle().isStroked()); cbFilled.setSelected(geomStyle().isFilled()); cbOrient.setSelected(layer.getLayerStyle().isOrientations()); cbStructure.setSelected(layer.getLayerStyle().isOrientations()); - lineWidthModel.setValue(geomStyle().getStrokeWidth()); + lineWidthModel.setValue((double) geomStyle().getStrokeWidth()); setPaletteType(comboPalette, layer.getLayerStyle().getFillType()); updateStyleControls(); } @@ -219,7 +219,7 @@ public void colorChanged(Color clr) { } ); - vertexSizeModel = new SpinnerNumberModel(4.0, 0, 100.0, 1); + vertexSizeModel = new SpinnerNumberModel(4, 0, 100, 1); spinnerVertexSize = new JSpinner(vertexSizeModel); spinnerVertexSize.setMaximumSize(new Dimension(40,16)); spinnerVertexSize.setAlignmentX(Component.LEFT_ALIGNMENT); @@ -279,7 +279,7 @@ public void actionPerformed(ActionEvent arg0) { } }); - lineWidthModel = new SpinnerNumberModel(1.0, 0, 100.0, 0.2); + lineWidthModel = new SpinnerNumberModel(1.0, 0, 100, 0.2); spinnerLineWidth = new JSpinner(lineWidthModel); //widthSpinner.setMinimumSize(new Dimension(50,12)); //widthSpinner.setPreferredSize(new Dimension(50,12)); @@ -306,9 +306,6 @@ public void stateChanged(ChangeEvent e) { } } }); - addRow("Line", cbStroked, btnLineColor, btnVertexSynch, sliderLineAlpha, spinnerLineWidth); - - //============================================= cbDashed = new JCheckBox(); cbDashed.setText("Dashed"); //cbDashed.setToolTipText(AppStrings.STYLE_VERTEX_ENABLE); @@ -320,6 +317,10 @@ public void actionPerformed(ActionEvent e) { JTSTestBuilder.controller().geometryViewChanged(); } }); + + addRow("Line", cbStroked, btnLineColor, btnVertexSynch, sliderLineAlpha, spinnerLineWidth, cbDashed); + + //============================================= cbEndpoint = new JCheckBox(); cbEndpoint.setText("Endpoints"); @@ -365,7 +366,7 @@ public void actionPerformed(ActionEvent e) { JTSTestBuilder.controller().geometryViewChanged(); } }); - offsetSizeModel = new SpinnerNumberModel(LayerStyle.INIT_OFFSET_SIZE, -100, 100.0, 1); + offsetSizeModel = new SpinnerNumberModel(LayerStyle.INIT_OFFSET_SIZE, -100, 100, 1); spinnerOffsetSize = new JSpinner(offsetSizeModel); spinnerOffsetSize.setMaximumSize(new Dimension(40,16)); spinnerOffsetSize.setAlignmentX(Component.LEFT_ALIGNMENT); @@ -378,7 +379,7 @@ public void stateChanged(ChangeEvent e) { }); // Leave on separate line to allow room for dash style - addRow("", cbDashed, cbEndpoint, cbOrient, cbStructure, cbOffset, spinnerOffsetSize); + addRow("", cbEndpoint, cbOrient, cbStructure, cbOffset, spinnerOffsetSize); //============================================= cbFilled = new JCheckBox(); @@ -465,7 +466,7 @@ public void colorChanged(Color clr) { } ); - labelSizeModel = new SpinnerNumberModel(4.0, 0, 100.0, 1); + labelSizeModel = new SpinnerNumberModel(4, 0, 100, 1); spinnerLabelSize = new JSpinner(labelSizeModel); spinnerLabelSize.setMaximumSize(new Dimension(40,16)); spinnerLabelSize.setAlignmentX(Component.LEFT_ALIGNMENT); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/util/io/IOUtil.java b/modules/app/src/main/java/org/locationtech/jtstest/util/io/IOUtil.java index 476a610bca..9732a60e1b 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/util/io/IOUtil.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/util/io/IOUtil.java @@ -45,7 +45,7 @@ public static Geometry readFile(String filename, GeometryFactory geomFact) return readWKBHexFile(filename, geomFact); if (ext.equalsIgnoreCase(".gml")) return readGMLFile(filename, geomFact); - if (ext.equalsIgnoreCase(".geojson")) + if (ext.equalsIgnoreCase(".geojson") || ext.equalsIgnoreCase(".json")) return readGeoJSONFile(filename, geomFact); return readWKTFile(filename, geomFact); } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/util/io/MultiFormatFileReader.java b/modules/app/src/main/java/org/locationtech/jtstest/util/io/MultiFormatFileReader.java new file mode 100644 index 0000000000..5fe8da648b --- /dev/null +++ b/modules/app/src/main/java/org/locationtech/jtstest/util/io/MultiFormatFileReader.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2020 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jtstest.util.io; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKBHexFileReader; +import org.locationtech.jts.io.WKBReader; +import org.locationtech.jts.io.WKTFileReader; +import org.locationtech.jts.io.WKTReader; +import org.locationtech.jtstest.testbuilder.io.shapefile.Shapefile; +import org.locationtech.jtstest.util.FileUtil; + + +/** + * Reads a {@link Geometry} collection from a file which is in + * WKT, WKBHex, GML, GeoJSON, or SHP format. + * + * @author Martin Davis + * @version 1.7 + */ +public class MultiFormatFileReader +{ + public static Geometry readFile(String filename, int limit, int offset, GeometryFactory geomFactory) throws Exception { + MultiFormatFileReader rdr = new MultiFormatFileReader(geomFactory); + rdr.setLimit(limit); + rdr.setOffset(offset); + return rdr.read(filename); + } + + private GeometryFactory geomFact; + private int limit = -1; + private int offset = 0; + + public MultiFormatFileReader() + { + this(new GeometryFactory()); + } + + public MultiFormatFileReader(GeometryFactory geomFactory) + { + this.geomFact = geomFactory; + } + + /** + * Sets the maximum number of geometries to read. + * + * @param limit the maximum number of geometries to read + */ + public void setLimit(int limit) + { + this.limit = limit; + } + + /** + * Sets the number of geometries to skip before storing. + * + * @param offset the number of geometries to skip + */ + public void setOffset(int offset) + { + this.offset = offset; + } + + public Geometry read(String filename) + throws Exception + { + String ext = FileUtil.extension(filename); + if (ext.equalsIgnoreCase(".wkb")) + return readWKBHexFile(filename); + if (ext.equalsIgnoreCase(".shp")) + return readShapefile(filename); + + if (ext.equalsIgnoreCase(".gml")) + return IOUtil.readFile(filename, geomFact); + if (ext.equalsIgnoreCase(".geojson")) + return IOUtil.readFile(filename, geomFact); + + return readWKTFile(filename); + } + + private Geometry readWKBHexFile(String filename) + throws ParseException, IOException + { + WKBReader reader = new WKBReader(geomFact); + WKBHexFileReader fileReader = new WKBHexFileReader(filename, reader); + if (limit >= 0) fileReader.setLimit(limit); + if (offset > 0) fileReader.setOffset(offset); + List geomList = fileReader.read(); + return toGeometry(geomList); + } + + private Geometry readWKTFile(String filename) + throws ParseException, IOException + { + WKTReader reader = new WKTReader(geomFact); + WKTFileReader fileReader = new WKTFileReader(filename, reader); + if (limit >= 0) fileReader.setLimit(limit); + if (offset > 0) fileReader.setOffset(offset); + List geomList = fileReader.read(); + return toGeometry(geomList); + } + + private Geometry readShapefile(String filename) + throws Exception + { + Shapefile shpfile = new Shapefile(new FileInputStream(filename)); + shpfile.readStream(geomFact); + int count = 0; + List geomList = new ArrayList(); + do { + Geometry geom = shpfile.next(); + if (geom == null || geomList.size() > limit) + break; + if (count >= offset) { + geomList.add(geom); + } + count++; + } while (true); + return toGeometry(geomList); + } + + private Geometry toGeometry(List geomList) { + if (geomList.size() == 1) + return (Geometry) geomList.get(0); + + return geomFact.createGeometryCollection(GeometryFactory.toGeometryArray(geomList)); + } + +} diff --git a/modules/app/src/main/resources/org/locationtech/jtstest/testbuilder/BinaryGeomFunctionOptB.png b/modules/app/src/main/resources/org/locationtech/jtstest/testbuilder/BinaryGeomFunctionOptB.png new file mode 100644 index 0000000000..3eebd5b42f Binary files /dev/null and b/modules/app/src/main/resources/org/locationtech/jtstest/testbuilder/BinaryGeomFunctionOptB.png differ diff --git a/modules/app/src/test/java/org/locationtech/jtstest/cmd/JTSOpCmdTest.java b/modules/app/src/test/java/org/locationtech/jtstest/cmd/JTSOpCmdTest.java index baeaea36bb..a8045a8535 100644 --- a/modules/app/src/test/java/org/locationtech/jtstest/cmd/JTSOpCmdTest.java +++ b/modules/app/src/test/java/org/locationtech/jtstest/cmd/JTSOpCmdTest.java @@ -61,10 +61,13 @@ public void testErrorMissingGeomABuffer() { JTSOpRunner.ERR_REQUIRED_A ); } + /* + // Missing B check is disabled for now public void testErrorMissingGeomBUnion() { runCmdError( args("-a", "POINT ( 1 1 )", "Overlay.union" ), JTSOpRunner.ERR_REQUIRED_B ); } + */ public void testErrorMissingGeomAUnion() { runCmdError( args("-b", "POINT ( 1 1 )", "Overlay.union" ), diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequences.java b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequences.java index 186c609ed5..3217a8678b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequences.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequences.java @@ -29,6 +29,8 @@ public class CoordinateSequences { */ public static void reverse(CoordinateSequence seq) { + if (seq.size() <= 1) return; + int last = seq.size() - 1; int mid = last / 2; for (int i = 0; i <= mid; i++) { diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java b/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java index 9aa1349c29..b01c0b85a6 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java @@ -590,8 +590,8 @@ public Point getInteriorPoint() *
  • a point, returns a Point. *
  • a line parallel to an axis, a two-vertex LineString *
  • otherwise, returns a - * Polygon whose vertices are (minx miny, maxx miny, - * maxx maxy, minx maxy, minx miny). + * Polygon whose vertices are (minx miny, minx maxy, + * maxx maxy, maxx miny, minx miny). * * *@return a Geometry representing the envelope of this Geometry @@ -1325,31 +1325,7 @@ public Geometry reverse() { */ public Geometry intersection(Geometry other) { - /** - * TODO: MD - add optimization for P-A case using Point-In-Polygon - */ - // special case: if one input is empty ==> empty - if (this.isEmpty() || other.isEmpty()) - return OverlayOp.createEmptyResult(OverlayOp.INTERSECTION, this, other, factory); - - // compute for GCs - // (An inefficient algorithm, but will work) - // TODO: improve efficiency of computation for GCs - if (this.isGeometryCollection()) { - final Geometry g2 = other; - return GeometryCollectionMapper.map( - (GeometryCollection) this, - new GeometryMapper.MapOp() { - public Geometry map(Geometry g) { - return g.intersection(g2); - } - }); - } - - // No longer needed since GCs are handled by previous code - //checkNotGeometryCollection(this); - //checkNotGeometryCollection(other); - return SnapIfNeededOverlayOp.overlayOp(this, other, OverlayOp.INTERSECTION); + return GeometryOverlay.intersection(this, other); } /** @@ -1388,21 +1364,7 @@ public Geometry map(Geometry g) { */ public Geometry union(Geometry other) { - // handle empty geometry cases - if (this.isEmpty() || other.isEmpty()) { - if (this.isEmpty() && other.isEmpty()) - return OverlayOp.createEmptyResult(OverlayOp.UNION, this, other, factory); - - // special case: if either input is empty ==> other input - if (this.isEmpty()) return other.copy(); - if (other.isEmpty()) return copy(); - } - - // TODO: optimize if envelopes of geometries do not intersect - - checkNotGeometryCollection(this); - checkNotGeometryCollection(other); - return SnapIfNeededOverlayOp.overlayOp(this, other, OverlayOp.UNION); + return GeometryOverlay.union(this, other); } /** @@ -1424,13 +1386,7 @@ public Geometry union(Geometry other) */ public Geometry difference(Geometry other) { - // special case: if A.isEmpty ==> empty; if B.isEmpty ==> A - if (this.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.DIFFERENCE, this, other, factory); - if (other.isEmpty()) return copy(); - - checkNotGeometryCollection(this); - checkNotGeometryCollection(other); - return SnapIfNeededOverlayOp.overlayOp(this, other, OverlayOp.DIFFERENCE); + return GeometryOverlay.difference(this, other); } /** @@ -1453,20 +1409,7 @@ public Geometry difference(Geometry other) */ public Geometry symDifference(Geometry other) { - // handle empty geometry cases - if (this.isEmpty() || other.isEmpty()) { - // both empty - check dimensions - if (this.isEmpty() && other.isEmpty()) - return OverlayOp.createEmptyResult(OverlayOp.SYMDIFFERENCE, this, other, factory); - - // special case: if either input is empty ==> result = other arg - if (this.isEmpty()) return other.copy(); - if (other.isEmpty()) return copy(); - } - - checkNotGeometryCollection(this); - checkNotGeometryCollection(other); - return SnapIfNeededOverlayOp.overlayOp(this, other, OverlayOp.SYMDIFFERENCE); + return GeometryOverlay.symDifference(this, other); } /** @@ -1491,7 +1434,7 @@ public Geometry symDifference(Geometry other) * @see UnaryUnionOp */ public Geometry union() { - return UnaryUnionOp.union(this); + return GeometryOverlay.union(this); } /** @@ -1817,7 +1760,7 @@ protected boolean isEquivalentClass(Geometry other) { *@throws IllegalArgumentException if g is a GeometryCollection * but not one of its subclasses */ - protected static void checkNotGeometryCollection(Geometry g) { + static void checkNotGeometryCollection(Geometry g) { if (g.isGeometryCollection()) { throw new IllegalArgumentException("Operation does not support GeometryCollection arguments"); } diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryCollection.java b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryCollection.java index be615b11b0..6d062f3a91 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryCollection.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryCollection.java @@ -286,12 +286,11 @@ public GeometryCollection reverse() { protected GeometryCollection reverseInternal() { - int numGeometries = geometries.length; - Collection reversed = new ArrayList<>(numGeometries); - for (int i = 0; i < numGeometries; i++) { - reversed.add(geometries[i].reverse()); + Geometry[] geometries = new Geometry[this.geometries.length]; + for (int i = 0; i < geometries.length; i++) { + geometries[i] = this.geometries[i].reverse(); } - return (GeometryCollection) getFactory().buildGeometry(reversed); + return new GeometryCollection(geometries, factory); } } diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryOverlay.java b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryOverlay.java new file mode 100644 index 0000000000..862804feac --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryOverlay.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2020 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.geom; + +import org.locationtech.jts.geom.util.GeometryCollectionMapper; +import org.locationtech.jts.geom.util.GeometryMapper; +import org.locationtech.jts.operation.overlay.OverlayOp; +import org.locationtech.jts.operation.overlay.snap.SnapIfNeededOverlayOp; +import org.locationtech.jts.operation.overlayng.OverlayNGRobust; +import org.locationtech.jts.operation.union.UnaryUnionOp; + +/** + * Internal class which encapsulates the runtime switch to use OverlayNG, + * and some additional extensions for optimization and GeometryCollection handling. + *

    + * This class allows the {@link Geometry} overlay methods to be + * switched between the original algorithm and the modern OverlayNG codebase + * via a system property jts.overlay. + *

      + *
    • jts.overlay=old - (default) use original overlay algorithm + *
    • jts.overlay=ng - use OverlayNG + *
    + * + * @author mdavis + * + */ +class GeometryOverlay +{ + public static String OVERLAY_PROPERTY_NAME = "jts.overlay"; + + public static String OVERLAY_PROPERTY_VALUE_NG = "ng"; + public static String OVERLAY_PROPERTY_VALUE_OLD = "old"; + + /** + * Currently the original JTS overlay implementation is the default + */ + public static boolean OVERLAY_NG_DEFAULT = false; + + private static boolean isOverlayNG = OVERLAY_NG_DEFAULT; + + static { + setOverlayImpl(System.getProperty(OVERLAY_PROPERTY_NAME)); + } + + /** + * This function is provided primarily for unit testing. + * It is not recommended to use it dynamically, since + * that may result in inconsistent overlay behaviour. + * + * @param overlayImplCode the code for the overlay method (may be null) + */ + static void setOverlayImpl(String overlayImplCode) { + if (overlayImplCode == null) + return; + // set flag explicitly since current value may not be default + isOverlayNG = OVERLAY_NG_DEFAULT; + + if (OVERLAY_PROPERTY_VALUE_NG.equalsIgnoreCase(overlayImplCode) ) + isOverlayNG = true; + } + + private static Geometry overlay(Geometry a, Geometry b, int opCode) { + if (isOverlayNG) { + return OverlayNGRobust.overlay(a, b, opCode); + } + else { + return SnapIfNeededOverlayOp.overlayOp(a, b, opCode); + } + } + + static Geometry difference(Geometry a, Geometry b) + { + // special case: if A.isEmpty ==> empty; if B.isEmpty ==> A + if (a.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.DIFFERENCE, a, b, a.getFactory()); + if (b.isEmpty()) return a.copy(); + + Geometry.checkNotGeometryCollection(a); + Geometry.checkNotGeometryCollection(b); + + return overlay(a, b, OverlayOp.DIFFERENCE); + } + + static Geometry intersection(Geometry a, Geometry b) + { + /** + * TODO: MD - add optimization for P-A case using Point-In-Polygon + */ + // special case: if one input is empty ==> empty + if (a.isEmpty() || b.isEmpty()) + return OverlayOp.createEmptyResult(OverlayOp.INTERSECTION, a, b, a.getFactory()); + + // compute for GCs + // (An inefficient algorithm, but will work) + // TODO: improve efficiency of computation for GCs + if (a.isGeometryCollection()) { + final Geometry g2 = b; + return GeometryCollectionMapper.map( + (GeometryCollection) a, + new GeometryMapper.MapOp() { + public Geometry map(Geometry g) { + return g.intersection(g2); + } + }); + } + + // No longer needed since GCs are handled by previous code + //checkNotGeometryCollection(this); + //checkNotGeometryCollection(other); + + return overlay(a, b, OverlayOp.INTERSECTION); + } + + static Geometry symDifference(Geometry a, Geometry b) + { + // handle empty geometry cases + if (a.isEmpty() || b.isEmpty()) { + // both empty - check dimensions + if (a.isEmpty() && b.isEmpty()) + return OverlayOp.createEmptyResult(OverlayOp.SYMDIFFERENCE, a, b, a.getFactory()); + + // special case: if either input is empty ==> result = other arg + if (a.isEmpty()) return b.copy(); + if (b.isEmpty()) return a.copy(); + } + + Geometry.checkNotGeometryCollection(a); + Geometry.checkNotGeometryCollection(b); + return overlay(a, b, OverlayOp.SYMDIFFERENCE); + } + + static Geometry union(Geometry a, Geometry b) + { + // handle empty geometry cases + if (a.isEmpty() || a.isEmpty()) { + if (b.isEmpty() && b.isEmpty()) + return OverlayOp.createEmptyResult(OverlayOp.UNION, a, b, a.getFactory()); + + // special case: if either input is empty ==> other input + if (a.isEmpty()) return b.copy(); + if (b.isEmpty()) return a.copy(); + } + + // TODO: optimize if envelopes of geometries do not intersect + + Geometry.checkNotGeometryCollection(a); + Geometry.checkNotGeometryCollection(b); + + return overlay(a, b, OverlayOp.UNION); + } + + static Geometry union(Geometry a) { + if (isOverlayNG) { + return OverlayNGRobust.union(a); + } + else { + return UnaryUnionOp.union(a); + } + } +} diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/MultiLineString.java b/modules/core/src/main/java/org/locationtech/jts/geom/MultiLineString.java index 08c684614f..d49f562234 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/MultiLineString.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/MultiLineString.java @@ -107,6 +107,14 @@ public MultiLineString reverse() { return (MultiLineString) super.reverse(); } + protected MultiLineString reverseInternal() { + LineString[] lineStrings = new LineString[this.geometries.length]; + for (int i = 0; i < lineStrings.length; i++) { + lineStrings[i] = (LineString) this.geometries[i].reverse(); + } + return new MultiLineString(lineStrings, factory); + } + protected MultiLineString copyInternal() { LineString[] lineStrings = new LineString[this.geometries.length]; for (int i = 0; i < lineStrings.length; i++) { diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/MultiPoint.java b/modules/core/src/main/java/org/locationtech/jts/geom/MultiPoint.java index 37f0c00f0a..7fb61c1e8e 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/MultiPoint.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/MultiPoint.java @@ -79,7 +79,15 @@ public Geometry getBoundary() { public MultiPoint reverse() { return (MultiPoint) super.reverse(); } - + + protected MultiPoint reverseInternal() { + Point[] points = new Point[this.geometries.length]; + for (int i = 0; i < points.length; i++) { + points[i] = (Point) this.geometries[i].copy(); + } + return new MultiPoint(points, factory); + } + public boolean isValid() { return true; } diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/MultiPolygon.java b/modules/core/src/main/java/org/locationtech/jts/geom/MultiPolygon.java index a4281512e7..9ec9f94a93 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/MultiPolygon.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/MultiPolygon.java @@ -124,6 +124,14 @@ public MultiPolygon reverse() { return (MultiPolygon) super.reverse(); } + protected MultiPolygon reverseInternal() { + Polygon[] polygons = new Polygon[this.geometries.length]; + for (int i = 0; i < polygons.length; i++) { + polygons[i] = (Polygon) this.geometries[i].reverse(); + } + return new MultiPolygon(polygons, factory); + } + protected MultiPolygon copyInternal() { Polygon[] polygons = new Polygon[this.geometries.length]; for (int i = 0; i < polygons.length; i++) { diff --git a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java index e165f9b717..e80e0224ab 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java @@ -23,8 +23,8 @@ /** - * An implementation of a 2-D KD-Tree. KD-trees provide fast range searching on - * point data. + * An implementation of a 2-D KD-Tree. KD-trees provide fast range searching + * and fast lookup for point data. *

    * This implementation supports detecting and snapping points which are closer * than a given distance tolerance. @@ -36,6 +36,17 @@ * is incremented. * If more than one node in the tree is within tolerance of an inserted point, * the closest and then lowest node is snapped to. + *

    + * Note that the structure of a KD-Tree depends on the order of insertion of the points. + * A tree may become imbalanced if the inserted points are coherent + * (e.g. monotonic in one or both dimensions). + * A perfectly balanced tree has depth of only log2(N), + * but an imbalanced tree may be much deeper. + * This has a serious impact on query efficiency. + * Even worse, since recursion is used for querying the tree + * an extremely deep tree may cause a {@link StackOverflowException}. + * One solution to this is to randomize the order of points before insertion + * (e.g. by using Fisher-Yates shuffling). * * @author David Skea * @author Martin Davis @@ -234,16 +245,13 @@ private KdNode insertExact(Coordinate p, Object data) { * then top-bottom (by Y ordinate) */ while (currentNode != null) { - // test if point is already a node (not strictly necessary) - if (currentNode != null) { - boolean isInTolerance = p.distance(currentNode.getCoordinate()) <= tolerance; - - // check if point is already in tree (up to tolerance) and if so simply - // return existing node - if (isInTolerance) { - currentNode.increment(); - return currentNode; - } + boolean isInTolerance = p.distance(currentNode.getCoordinate()) <= tolerance; + + // check if point is already in tree (up to tolerance) and if so simply + // return existing node + if (isInTolerance) { + currentNode.increment(); + return currentNode; } if (isOddLevel) { diff --git a/modules/core/src/main/java/org/locationtech/jts/io/WKBWriter.java b/modules/core/src/main/java/org/locationtech/jts/io/WKBWriter.java index 71f8ab9d31..51b7b7b3ab 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/WKBWriter.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/WKBWriter.java @@ -45,9 +45,7 @@ *

      *
    • Point: a WKBPoint with NaN ordinate values
    • *
    • LineString: a WKBLineString with zero points
    • - *
    • Polygon: currently output as a WKBPolygon with one LinearRing with zero points. - * Note: This is different to other systems. It will change to a WKBPolygon with zero LinearRings. - *
    • + *
    • Polygon: a WKBPolygon with zero rings
    • *
    • Multi geometries: a WKBMulti geometry of appropriate type with zero elements
    • *
    • GeometryCollections: a WKBGeometryCollection with zero elements
    • *
  • @@ -344,7 +342,8 @@ private void writePoint(Point pt, OutStream os) throws IOException writeByteOrder(os); writeGeometryType(WKBConstants.wkbPoint, pt, os); if (pt.getCoordinateSequence().size() == 0) { - writeNaNs(2, os); + // write empty point as NaNs (extension to OGC standard) + writeNaNs(outputDimension, os); } else { writeCoordinateSequence(pt.getCoordinateSequence(), false, os); } @@ -362,6 +361,11 @@ private void writePolygon(Polygon poly, OutStream os) throws IOException { writeByteOrder(os); writeGeometryType(WKBConstants.wkbPolygon, poly, os); + //--- write empty polygons with no rings (OCG extension) + if (poly.isEmpty()) { + writeInt(0, os); + return; + } writeInt(poly.getNumInteriorRing() + 1, os); writeCoordinateSequence(poly.getExteriorRing().getCoordinateSequence(), true, os); for (int i = 0; i < poly.getNumInteriorRing(); i++) { diff --git a/modules/core/src/main/java/org/locationtech/jts/io/WKTFileReader.java b/modules/core/src/main/java/org/locationtech/jts/io/WKTFileReader.java index 0c45852aad..b787cc6fed 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/WKTFileReader.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/WKTFileReader.java @@ -170,24 +170,22 @@ private boolean isAtLimit(List geoms) if (geoms.size() < limit) return false; return true; } - - private static final int MAX_LOOKAHEAD = 1000; - + /** - * Tests if reader is at EOF. + * Tests if reader is at EOF, and skips any leading whitespace */ - private boolean isAtEndOfFile(BufferedReader bufferedReader) - throws IOException - { - bufferedReader.mark(MAX_LOOKAHEAD); - - StreamTokenizer tokenizer = new StreamTokenizer(bufferedReader); - int type = tokenizer.nextToken(); - - if (type == StreamTokenizer.TT_EOF) { - return true; - } - bufferedReader.reset(); - return false; - } + private boolean isAtEndOfFile(BufferedReader bufferedReader) throws IOException { + // skip whitespace + int ch; + do { + bufferedReader.mark(1); + ch = bufferedReader.read(); + // EOF reached + if (ch < 0) return true; + } while (Character.isWhitespace(ch)); + bufferedReader.reset(); + + return false; + } + } diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingIntersectionAdder.java b/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingIntersectionAdder.java index 081102f70f..fb01cc4122 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingIntersectionAdder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingIntersectionAdder.java @@ -68,25 +68,25 @@ public void processIntersections( Coordinate p10 = seg1.getCoordinates()[segIndex1]; Coordinate p11 = seg1.getCoordinates()[segIndex1 + 1]; - li.computeIntersection(p00, p01, p10, p11); -//if (li.hasIntersection() && li.isProper()) Debug.println(li); - /** - * Process single point intersections only. - * Two-point (collinear) ones are handled by the near-vertex code + * Don't node intersections which are just + * due to the shared vertex of adjacent segments. */ - if (li.hasIntersection() && li.getIntersectionNum() == 1) { + if (! isAdjacent(seg0, segIndex0, seg1, segIndex1)) { + li.computeIntersection(p00, p01, p10, p11); + //if (li.hasIntersection() && li.isProper()) Debug.println(li); + /** - * Don't node intersections which are just - * due to the shared vertex of adjacent segments. + * Process single point intersections only. + * Two-point (collinear) ones are handled by the near-vertex code */ - if (! isAdjacent(seg0, segIndex0, seg1, segIndex1)) { - - Coordinate intPt = li.getIntersection(0); - Coordinate snapPt = snapPointIndex.snap(intPt); - - ((NodedSegmentString) seg0).addIntersection(snapPt, segIndex0); - ((NodedSegmentString) seg1).addIntersection(snapPt, segIndex1); + if (li.hasIntersection() && li.getIntersectionNum() == 1) { + + Coordinate intPt = li.getIntersection(0); + Coordinate snapPt = snapPointIndex.snap(intPt); + + ((NodedSegmentString) seg0).addIntersection(snapPt, segIndex0); + ((NodedSegmentString) seg1).addIntersection(snapPt, segIndex1); } } diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingNoder.java b/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingNoder.java index 4ae27c9714..8c74ba4744 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingNoder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingNoder.java @@ -53,6 +53,11 @@ public class SnappingNoder private double snapTolerance; private List nodedResult; + /** + * Creates a snapping noder using the given snap distance tolerance. + * + * @param snapTolerance points are snapped if within this distance + */ public SnappingNoder(double snapTolerance) { this.snapTolerance = snapTolerance; snapIndex = new SnappingPointIndex(snapTolerance); diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingPointIndex.java b/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingPointIndex.java index a89cccd17e..68496d4dde 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingPointIndex.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingPointIndex.java @@ -16,7 +16,7 @@ import org.locationtech.jts.index.kdtree.KdTree; /** - * An index providing fast creating and lookup of snap points. + * An index providing fast creation and lookup of snap points. * * @author mdavis * @@ -32,11 +32,24 @@ public class SnappingPointIndex { */ private KdTree snapPointIndex; - SnappingPointIndex(double snapTolerance) { + /** + * Creates a snap point index using a specified distance tolerance. + * + * @param snapTolerance points are snapped if within this distance + */ + public SnappingPointIndex(double snapTolerance) { this.snapTolerance = snapTolerance; snapPointIndex = new KdTree(snapTolerance); } + /** + * Snaps a coordinate to an existing snap point, + * if it is within the snap tolerance distance. + * Otherwise adds the coordinate to the snap point index. + * + * @param p the point to snap + * @return the point it snapped to, or the input point + */ public Coordinate snap(Coordinate p) { /** * Inserting the coordinate snaps it to any existing diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/GeometryNoder.java b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/GeometryNoder.java index 0ef1925731..819dc14e8d 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/GeometryNoder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/GeometryNoder.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2016 Vivid Solutions. + * Copyright (c) 2020 Martin Davis. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -31,10 +32,9 @@ * Nodes the linework in a list of {@link Geometry}s using Snap-Rounding * to a given {@link PrecisionModel}. *

    - * The input coordinates are expected to be rounded - * to the given precision model. - * This class does not perform that function. - * GeometryPrecisionReducer may be used to do this. + * Input coordinates do not need to be rounded to the + * precision model. + * All output coordinates are rounded to the precision model. *

    * This class does not dissolve the output linework, * so there may be duplicate linestrings in the output. @@ -42,7 +42,6 @@ * the linework to be unique. Using UnaryUnion is one way * to do this (although this is an inefficient approach). * - * */ public class GeometryNoder { @@ -83,8 +82,7 @@ public List node(Collection geoms) geomFact = geom0.getFactory(); List segStrings = toSegmentStrings(extractLines(geoms)); - //Noder sr = new SimpleSnapRounder(pm); - Noder sr = new MCIndexSnapRounder(pm); + Noder sr = new SnapRoundingNoder(pm); sr.computeNodes(segStrings); Collection nodedLines = sr.getNodedSubstrings(); diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/HotPixel.java b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/HotPixel.java index e9355fc0db..8b71e32a9c 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/HotPixel.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/HotPixel.java @@ -132,11 +132,6 @@ private double scaleRound(double val) return (double) Math.round(val * scaleFactor); } - private Coordinate scaleRound(Coordinate p) - { - return new Coordinate(scaleRound(p.x), scaleRound(p.y)); - } - /** * Scale without rounding. * This ensures intersections are checked against original @@ -254,23 +249,27 @@ private boolean intersectsScaled(double p0x, double p0y, int orientUL = CGAlgorithmsDD.orientationIndex(px, py, qx, qy, minx, maxy); if (orientUL == 0) { + // upward segment does not intersect pixel interior if (py < qy) return false; + // downward segment must intersect pixel interior return true; } int orientUR = CGAlgorithmsDD.orientationIndex(px, py, qx, qy, maxx, maxy); if (orientUR == 0) { + // downward segment does not intersect pixel interior if (py > qy) return false; + // upward segment must intersect pixel interior return true; } - //--- check crossing Top side + //--- check crossing Top side if (orientUL != orientUR) { return true; } int orientLL = CGAlgorithmsDD.orientationIndex(px, py, qx, qy, minx, miny); - if (orientUL == 0) { - // LL corner is the only one in pixel interior + if (orientLL == 0) { + // segment crossed LL corner, which is the only one in pixel interior return true; } //--- check crossing Left side @@ -280,7 +279,9 @@ private boolean intersectsScaled(double p0x, double p0y, int orientLR = CGAlgorithmsDD.orientationIndex(px, py, qx, qy, maxx, miny); if (orientLR == 0) { + // upward segment does not intersect pixel interior if (py < qy) return false; + // downward segment must intersect pixel interior return true; } diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/HotPixelIndex.java b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/HotPixelIndex.java index bdfff20e3e..aadef01d60 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/HotPixelIndex.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/HotPixelIndex.java @@ -11,7 +11,9 @@ */ package org.locationtech.jts.noding.snapround; +import java.util.Iterator; import java.util.List; +import java.util.Random; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; @@ -21,9 +23,12 @@ import org.locationtech.jts.index.kdtree.KdTree; /** - * An index which creates {@link HotPixel}s for provided points, - * and allows performing range queries on them. - * + * An index which creates unique {@link HotPixel}s for provided points, + * and performs range queries on them. + * The points passed to the index do not needed to be + * rounded to the specified scale factor; this is done internally + * when creating the HotPixels for them. + * * @author mdavis * */ @@ -33,54 +38,105 @@ class HotPixelIndex { /** * Use a kd-tree to index the pixel centers for optimum performance. - * Since HotPixels have an extent, queries to the - * index must enlarge the query range by a suitable value + * Since HotPixels have an extent, range queries to the + * index must enlarge the query range by a suitable value * (using the pixel width is safest). */ private KdTree index = new KdTree(); - + public HotPixelIndex(PrecisionModel pm) { this.precModel = pm; scaleFactor = pm.getScale(); } - + + /** + * Utility class to shuffle an array of {@link Coordinate}s using + * the Fisher-Yates shuffle algorithm + * + * @see Fihser-Yates shuffle + */ + private static final class CoordinateShuffler implements Iterator { + + private final Random rnd = new Random(13); + private final Coordinate[] coordinates; + private final int[] indices; + private int index; + + /** + * Creates an instance of this class + * @param pts An array of {@link Coordinate}s. + */ + public CoordinateShuffler(Coordinate[] pts) { + coordinates = pts; + indices = new int[pts.length]; + for (int i = 0; i < pts.length; i++) + indices[i] = i; + index = pts.length - 1; + } + + @Override + public boolean hasNext() { + return index >= 0; + } + + @Override + public Coordinate next() { + int j = rnd.nextInt(index + 1); + Coordinate res = coordinates[indices[j]]; + indices[j] = indices[index--]; + return res; + } + } + /** * Adds a list of points as non-node pixels. - * + * * @param pts the points to add */ public void add(Coordinate[] pts) { - for (Coordinate pt : pts) { - add(pt); + /** + * Shuffle the points before adding. + * This avoids having long monontic runs of points + * causing an unbalanced KD-tree, which would create + * performance and robustness issues. + */ + Iterator it = new CoordinateShuffler(pts); + while (it.hasNext()) { + add(it.next()); } } - + /** * Adds a list of points as node pixels. - * + * * @param pts the points to add */ public void addNodes(List pts) { + /** + * Node points are not shuffled, since they are + * added after the vertex points, and hence the KD-tree should + * be reasonably balanced already. + */ for (Coordinate pt : pts) { HotPixel hp = add(pt); hp.setToNode(); } } - + /** * Adds a point as a Hot Pixel. * If the point has been added already, it is marked as a node. - * + * * @param p the point to add * @return the HotPixel for the point */ public HotPixel add(Coordinate p) { // TODO: is there a faster way of doing this? Coordinate pRound = round(p); - + HotPixel hp = find(pRound); /** - * Hot Pixels which are added more than once + * Hot Pixels which are added more than once * must have more than one vertex in them * and thus must be nodes. */ @@ -88,7 +144,7 @@ public HotPixel add(Coordinate p) { hp.setToNode(); return hp; } - + /** * A pixel containing the point was not found, so create a new one. * It is initially set to NOT be a node @@ -101,22 +157,22 @@ public HotPixel add(Coordinate p) { private HotPixel find(Coordinate pixelPt) { KdNode kdNode = index.query(pixelPt); - if (kdNode == null) + if (kdNode == null) return null; return (HotPixel) kdNode.getData(); } - + private Coordinate round(Coordinate pt) { Coordinate p2 = pt.copy(); precModel.makePrecise(p2); return p2; } - + /** * Visits all the hot pixels which may intersect a segment (p0-p1). * The visitor must determine whether each hot pixel actually intersects * the segment. - * + * * @param p0 the segment start point * @param p1 the segment end point * @param visitor the visitor to apply diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/EdgeNodingBuilder.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/EdgeNodingBuilder.java index d2f9833153..73d59e264c 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/EdgeNodingBuilder.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/EdgeNodingBuilder.java @@ -226,7 +226,7 @@ private void add(Geometry g, int geomIndex) else if (g instanceof LineString) addLine((LineString) g, geomIndex); else if (g instanceof MultiLineString) addCollection((MultiLineString) g, geomIndex); else if (g instanceof MultiPolygon) addCollection((MultiPolygon) g, geomIndex); - else if (g instanceof GeometryCollection) addCollection((GeometryCollection) g, geomIndex); + else if (g instanceof GeometryCollection) addGeometryCollection((GeometryCollection) g, geomIndex, g.getDimension()); // ignore Point geometries - they are handled elsewhere } @@ -238,6 +238,18 @@ private void addCollection(GeometryCollection gc, int geomIndex) } } + private void addGeometryCollection(GeometryCollection gc, int geomIndex, int expectedDim) + { + for (int i = 0; i < gc.getNumGeometries(); i++) { + Geometry g = gc.getGeometryN(i); + // check for mixed-dimension input, which is not supported + if (g.getDimension() != expectedDim) { + throw new IllegalArgumentException("Overlay input is mixed-dimension"); + } + add(g, geomIndex); + } + } + private void addPolygon(Polygon poly, int geomIndex) { LinearRing shell = poly.getExteriorRing(); diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/MaximalEdgeRing.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/MaximalEdgeRing.java index a1671597c3..fe23121fea 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/MaximalEdgeRing.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/MaximalEdgeRing.java @@ -236,8 +236,6 @@ private Coordinate[] getCoordinates() { OverlayEdge edge = startEdge; do { coords.add(edge.orig()); - if (edge == null) - break; if (edge.nextResultMax() == null) { break; } diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayNG.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayNG.java index a65b1cbe6f..b56a2e0b29 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayNG.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayNG.java @@ -42,8 +42,7 @@ *

  • {@link SYMDIFFERENCE} - all points which lie in one geometry but not both * * Input geometries may have different dimension. - * Collections must be homogeneous - * (all elements must have the same dimension). + * Input collections must be homogeneous (all elements must have the same dimension). *

    * The precision model used for the computation can be supplied * independent of the precision model of the input geometry. @@ -74,16 +73,25 @@ * (via {@link #setStrictMode(boolean)}. * In strict mode result semantics are: *

      - *
    • Result geometries are homogeneous (all components are of same dimension), - * except for some cases of symmetricDifference. - *
    • Lines and Points resulting from topology collapses are not included in the result + *
    • Lines and Points resulting from topology collapses are not included in the result
    • + *
    • Result geometry is homogeneous + * for the {@link #INTERSECTION} and {@link #DIFFERENCE} operations. + *
    • Result geometry is homogeneous + * for the {@link #UNION} and {@link #SYMDIFFERENCE} operations + * if the inputs have the same dimension
    • *
    * Strict mode has the following benefits: *
      - *
    • Results are simpler - *
    • Overlay operations are easily chainable + *
    • Results are simpler
    • + *
    • Overlay operations are chainable + * without needing to remove lower-dimension elements
    • *
    * The original JTS overlay semantics corresponds to non-strict mode. + *

    + * If a robustness error occurs, a {@link TopologyException} is thrown. + * These are usually caused by numerical rounding causing the noding output + * to not be fully noded. + * For robust computation with full-precision {@link OverlayNGRobust} can be used. * * @author mdavis * @@ -269,8 +277,6 @@ public static Geometry overlay(Geometry geom0, Geometry geom1, int opCode) /** * Computes a union operation on * the given geometry, with the supplied precision model. - * The primary use for this is to perform precision reduction - * (round the geometry to the supplied precision). *

    * The input must be a valid geometry. * Collections must be homogeneous. @@ -284,9 +290,6 @@ public static Geometry overlay(Geometry geom0, Geometry geom1, int opCode) * @return the result of the union operation * * @see OverlayMixedPoints - * @see PrecisionReducer - * @see UnaryUnionNG - * @see CoverageUnion */ static Geometry union(Geometry geom, PrecisionModel pm) { @@ -299,6 +302,7 @@ static Geometry union(Geometry geom, PrecisionModel pm) * Computes a union of a single geometry using a custom noder. *

    * The primary use of this is to support coverage union. + * Because of this the overlay is performed using strict mode. * * @param geom the geometry to union * @param pm the precision model to use (maybe be null) @@ -311,6 +315,7 @@ static Geometry union(Geometry geom, PrecisionModel pm, Noder noder) { OverlayNG ov = new OverlayNG(geom, pm); ov.setNoder(noder); + ov.setStrictMode(true); Geometry geomOv = ov.getResult(); return geomOv; } @@ -380,8 +385,12 @@ public OverlayNG(Geometry geom0, Geometry geom1, int opCode) { * Sets whether the overlay results are computed according to strict mode * semantics. *

      - *
    • Result geometry is always homogeneous (except for some SymmetricDifference cases) *
    • Lines resulting from topology collapse are not included + *
    • Result geometry is homogeneous + * for the {@link #INTERSECTION} and {@link #DIFFERENCE} operations. + *
    • Result geometry is homogeneous + * for the {@link #UNION} and {@link #SYMDIFFERENCE} operations + * if the inputs have the same dimension *
    * * @param isStrictMode true if strict mode is to be used @@ -432,7 +441,7 @@ public void setOutputResultEdges(boolean isOutputResultEdges ) { } //--------------------------------- - private void setNoder(Noder noder) { + void setNoder(Noder noder) { this.noder = noder; } @@ -440,8 +449,12 @@ private void setNoder(Noder noder) { * Gets the result of the overlay operation. * * @return the result of the overlay operation. + * + * @throws IllegalArgumentException if the input is not supported (e.g. a mixed-dimension geometry) + * @throws TopologyException if a robustness error occurs */ public Geometry getResult() { + // handle empty inputs which determine result if (OverlayUtil.isEmptyResult(opCode, inputGeom.getGeometry(0), inputGeom.getGeometry(1), @@ -449,18 +462,18 @@ public Geometry getResult() { return createEmptyResult(); } - // special logic for Point-Point inputs + // handle Point-Point inputs if (inputGeom.isAllPoints()) { return OverlayPoints.overlay(opCode, inputGeom.getGeometry(0), inputGeom.getGeometry(1), pm); } - // special logic for Point-nonPoint inputs + // handle Point-nonPoint inputs if (! inputGeom.isSingle() && inputGeom.hasPoints()) { return OverlayMixedPoints.overlay(opCode, inputGeom.getGeometry(0), inputGeom.getGeometry(1), pm); } + // handle case where both inputs are formed of edges (Lines and Polygons) Geometry result = computeEdgeOverlay(); - return result; } diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayNGRobust.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayNGRobust.java index b81944d910..3d8805c9e5 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayNGRobust.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayNGRobust.java @@ -53,9 +53,15 @@ */ public class OverlayNGRobust { - //--- The following function is provided to allow use in the TestRunner - - public static Geometry union(Geometry a) { + /** + * Computes unary union using robust computation. + * + * @param geom the geometry to union + * @return the union result + * + * @see UnaryUnionOp + */ + public static Geometry union(Geometry geom) { UnionStrategy unionSRFun = new UnionStrategy() { public Geometry union(Geometry g0, Geometry g1) { @@ -68,7 +74,7 @@ public boolean isFloatingPrecision() { } }; - UnaryUnionOp op = new UnaryUnionOp(a); + UnaryUnionOp op = new UnaryUnionOp(geom); op.setUnionFunction(unionSRFun); return op.union(); } @@ -197,9 +203,9 @@ private static Geometry overlaySnapping(Geometry geom0, Geometry geom1, int opCo */ private static Geometry overlaySnapBoth(Geometry geom0, Geometry geom1, int opCode, double snapTol) { try { - Geometry snap0 = overlaySnapTol(geom0, null, OverlayNG.UNION, snapTol); - Geometry snap1 = overlaySnapTol(geom1, null, OverlayNG.UNION, snapTol); - //log("Snapping BOTH with " + snapTol, geom0, geom1); + Geometry snap0 = snapSelf(geom0, snapTol); + Geometry snap1 = snapSelf(geom1, snapTol); + //log("Snapping BOTH with " + snapTol, geom0, geom1); return overlaySnapTol(snap0, snap1, opCode, snapTol); } @@ -208,7 +214,31 @@ private static Geometry overlaySnapBoth(Geometry geom0, Geometry geom1, int opCo } return null; } - + + /** + * Self-snaps a geometry by running a union operation with it as the only input. + * This helps to remove narrow spike/gore artifacts to simplify the geometry, + * which improves robustness. + * Collapsed artifacts are removed from the result to allow using + * it in further overlay operations. + * + * @param geom geometry to self-snap + * @param snapTol snap tolerance + * @return the snapped geometry (homogeneous) + */ + private static Geometry snapSelf(Geometry geom, double snapTol) { + OverlayNG ov = new OverlayNG(geom, null); + SnappingNoder snapNoder = new SnappingNoder(snapTol); + ov.setNoder(snapNoder); + /** + * Ensure the result is not mixed-dimension, + * since it will be used in further overlay computation. + * It may however be lower dimension, if it collapses completely due to snapping. + */ + ov.setStrictMode(true); + return ov.getResult(); + } + private static Geometry overlaySnapTol(Geometry geom0, Geometry geom1, int opCode, double snapTol) { SnappingNoder snapNoder = new SnappingNoder(snapTol); return OverlayNG.overlay(geom0, geom1, opCode, snapNoder); diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/PrecisionReducer.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/PrecisionReducer.java index 15112073ac..d4a9a737c0 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/PrecisionReducer.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/PrecisionReducer.java @@ -14,6 +14,7 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.PrecisionModel; +import org.locationtech.jts.geom.TopologyException; /** * Functions to reduce the precision of a geometry @@ -45,6 +46,8 @@ public class PrecisionReducer { * @param geom the geometry to reduce * @param pm the precision model to use * @return the precision-reduced geometry + * + * @throws IllegalArgumentException if the reduction fails due to invalid input geometry is invalid */ public static Geometry reducePrecision(Geometry geom, PrecisionModel pm) { OverlayNG ov = new OverlayNG(geom, pm); @@ -55,8 +58,13 @@ public static Geometry reducePrecision(Geometry geom, PrecisionModel pm) { if (geom.getDimension() == 2) { ov.setAreaResultOnly(true); } - Geometry reduced = ov.getResult(); - return reduced; + try { + Geometry reduced = ov.getResult(); + return reduced; + } + catch (TopologyException ex) { + throw new IllegalArgumentException("Reduction failed, possible invalid input"); + } } private PrecisionReducer() { diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/UnaryUnionNG.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/UnaryUnionNG.java index f865bf396c..6835a4aaf4 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/UnaryUnionNG.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/UnaryUnionNG.java @@ -22,8 +22,14 @@ * Unions a collection of geometries in an * efficient way, using {@link OverlayNG} * to ensure robust computation. + *

    + * This class is most useful for performing UnaryUnion using  + * a fixed-precision model.  + * For unary union using floating precision,   + * {@link OverlayNGRobust#union(Geometry) should be used. * * @author Martin Davis + * @see OverlayNGRobust * */ public class UnaryUnionNG { diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/package.html b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/package.html index 22649b3cae..32614320b9 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/package.html +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/package.html @@ -63,10 +63,11 @@

    Semantics

  • Polygon edges which undergo topology collapse to lines (due to rounding or snapping) are included in the result. This means that all operations may produce a heterogeneous result. - Usually this only occurs when using a fixed-precision model. + Usually this only occurs when using a fixed-precision model, + but it can happen due to snapping performed to improve robustness.
  • -
  • The intersection operation result includes. - all the components of the intersection +
  • The intersection operation result includes + all components of the intersection for geometries which intersect in components of the same and/or lower dimension.
  • The difference operation produces a homogeneous result if no topology collapses are present. diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java b/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java index 3691d026b1..deed918096 100644 --- a/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java +++ b/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java @@ -28,10 +28,18 @@ * (i.e. {@link Geometry#isValid()} is true). * To ensure this a polygonal geometry is reduced in a topologically valid fashion * (technically, by using snap-rounding). - * It can be forced to be reduced pointwise by using {@link #setPointwise(boolean)}. - * Note that in this case the result geometry may be invalid. - * Linear and point geometry is always reduced pointwise (i.e. without further change to - * its topology or stucture), since this does not change validity. + * Note that this may change polygonal geometry structure + * (e.g. two polygons separated by a distance below the specified precision + * will be merged into a single polygon). + *

    + * In general input must be valid geometry, or an {@link IllegalArgumentException} + * will be thrown. However if the invalidity is "mild" or very small then it + * may be eliminated by precision reduction. + *

    + * Alternatively, geometry can be reduced pointwise by using {@link #setPointwise(boolean)}. + * In this case the result geometry topology may be invalid. + * Linear and point geometry are always reduced pointwise (i.e. without further change to + * topology or structure), since this does not change validity. *

    * By default the geometry precision model is not changed. * This can be overridden by using {@link #setChangePrecisionModel(boolean)}. @@ -54,6 +62,7 @@ public class GeometryPrecisionReducer * @param g the geometry to reduce * @param precModel the precision model to use * @return the reduced geometry + * @throws IllegalArgumentException if the reduction fails due to invalid input geometry is invalid */ public static Geometry reduce(Geometry g, PrecisionModel precModel) { @@ -132,6 +141,14 @@ public void setPointwise(boolean isPointwise) this.isPointwise = isPointwise; } + /** + * Reduces the precision of a geometry, + * according to the specified strategy of this reducer. + * + * @param geom the geometry to reduce + * @return the precision-reduced geometry + * @throws IllegalArgumentException if the reduction fails due to invalid input geometry is invalid + */ public Geometry reduce(Geometry geom) { if (!isPointwise && geom instanceof Polygonal) { diff --git a/modules/core/src/main/java/org/locationtech/jts/util/TestBuilderProxy.java b/modules/core/src/main/java/org/locationtech/jts/util/TestBuilderProxy.java index 798fd42c7d..07cf1824ea 100644 --- a/modules/core/src/main/java/org/locationtech/jts/util/TestBuilderProxy.java +++ b/modules/core/src/main/java/org/locationtech/jts/util/TestBuilderProxy.java @@ -11,6 +11,7 @@ */ package org.locationtech.jts.util; +import java.awt.Color; import java.lang.reflect.Method; import org.locationtech.jts.geom.Geometry; @@ -30,12 +31,14 @@ public class TestBuilderProxy { private static final String CLASS_FUNCTIONS_UTIL = "org.locationtech.jtstest.function.FunctionsUtil"; private static Class tbClass; private static Method methodShowIndicator; + private static Method methodShowIndicatorLine; private static void init() { if (tbClass != null) return; try { tbClass = TestBuilderProxy.class.getClassLoader().loadClass(CLASS_FUNCTIONS_UTIL); methodShowIndicator = tbClass.getMethod("showIndicator", Geometry.class); + methodShowIndicatorLine = tbClass.getMethod("showIndicator", Geometry.class, Color.class); } catch (Exception ex) { // Fail silently to avoid unexpected output in production @@ -68,4 +71,15 @@ public static void showIndicator(Geometry geom) { // Or perhaps should fail noisy, since at this point the function should be working? } } + public static void showIndicator(Geometry geom, Color lineClr) { + init(); + if (methodShowIndicatorLine == null) return; + + try { + methodShowIndicatorLine.invoke(null, geom, lineClr); + } catch (Exception e) { + // Fail silently to avoid unexpected output in production + // Or perhaps should fail noisy, since at this point the function should be working? + } + } } diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/GeometryOverlayTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/GeometryOverlayTest.java new file mode 100644 index 0000000000..363f0ffe30 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/geom/GeometryOverlayTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.geom; + +import junit.textui.TestRunner; +import test.jts.GeometryTestCase; + +/** + * Tests the behaviour of the {@link GeometryOverlay} class. + * + * Currently does not test the reading of the system property. + * + * @author mdavis + * + */ +public class GeometryOverlayTest extends GeometryTestCase { + public static void main(String args[]) { + TestRunner.run(GeometryOverlayTest.class); + } + + public GeometryOverlayTest(String name) { super(name); } + + public void testOverlayNGFixed() { + GeometryOverlay.setOverlayImpl(GeometryOverlay.OVERLAY_PROPERTY_VALUE_NG); + PrecisionModel pmFixed = new PrecisionModel(1); + Geometry expected = read("POLYGON ((1 2, 4 1, 1 1, 1 2))"); + + checkIntersectionPM(pmFixed, expected); + } + + public void testOverlayNGFloat() { + GeometryOverlay.setOverlayImpl(GeometryOverlay.OVERLAY_PROPERTY_VALUE_NG); + PrecisionModel pmFloat = new PrecisionModel(); + Geometry expected = read("POLYGON ((1 1, 1 2, 4 1.25, 4 1, 1 1))"); + + checkIntersectionPM(pmFloat, expected); + } + + private void checkIntersectionPM(PrecisionModel pmFixed, Geometry expected) { + GeometryFactory geomFactFixed = new GeometryFactory(pmFixed); + Geometry a = read(geomFactFixed, "POLYGON ((1 1, 1 2, 5 1, 1 1))"); + Geometry b = read(geomFactFixed, "POLYGON ((0 3, 4 3, 4 0, 0 0, 0 3))"); + Geometry actual = a.intersection(b); + checkEqual(expected, actual); + } + + public void testOverlayOld() { + // must set overlay method explicitly since order of tests is not deterministic + GeometryOverlay.setOverlayImpl(GeometryOverlay.OVERLAY_PROPERTY_VALUE_OLD); + checkIntersectionFails(); + } + + public void testOverlayNG() { + GeometryOverlay.setOverlayImpl(GeometryOverlay.OVERLAY_PROPERTY_VALUE_NG); + checkIntersectionSucceeds(); + } + + private void checkIntersectionFails() { + try { + tryIntersection(); + fail("Intersection operation should have failed but did not"); + } + catch (TopologyException ex) { + // ignore - expected result + } + } + + private void checkIntersectionSucceeds() { + try { + tryIntersection(); + } + catch (TopologyException ex) { + fail("Intersection operation failed."); + } + } + + private void tryIntersection() { + Geometry a = read("POLYGON ((-1120500.000000126 850931.058865365, -1120500.0000001257 851343.3885007716, -1120500.0000001257 851342.2386007707, -1120399.762684411 851199.4941312922, -1120500.000000126 850931.058865365))"); + Geometry b = read("POLYGON ((-1120500.000000126 851253.4627870625, -1120500.0000001257 851299.8179383819, -1120492.1498410008 851293.8417889411, -1120500.000000126 851253.4627870625))"); + Geometry result = a.intersection(b); + } +} diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/GeometryReverseTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/GeometryReverseTest.java index 2616a06b9d..9ee65c405b 100644 --- a/modules/core/src/test/java/org/locationtech/jts/geom/GeometryReverseTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/geom/GeometryReverseTest.java @@ -6,7 +6,7 @@ public class GeometryReverseTest extends GeometryTestCase { public static void main(String[] args) throws Exception { - junit.textui.TestRunner.run(GeometryCopyTest.class); + junit.textui.TestRunner.run(GeometryReverseTest.class); } public GeometryReverseTest(String name) { @@ -14,14 +14,9 @@ public GeometryReverseTest(String name) { } public void testReverse() { - checkReverse( read( GeometryTestData.WKT_POINT )); - checkReverse( read( GeometryTestData.WKT_LINESTRING )); - checkReverse( read( GeometryTestData.WKT_LINEARRING )); - checkReverse( read( GeometryTestData.WKT_POLY )); - checkReverse( read( GeometryTestData.WKT_MULTIPOINT )); - checkReverse( read( GeometryTestData.WKT_MULTILINESTRING )); - checkReverse( read( GeometryTestData.WKT_MULTIPOLYGON )); - checkReverse( read( GeometryTestData.WKT_GC )); + for (String wkt : GeometryTestData.WKT_ALL) { + checkReverse( read( wkt )); + } } private void checkReverse(final Geometry g) { @@ -73,6 +68,9 @@ else if (gt1 instanceof Polygon) { return false; } } + else if (gt1 instanceof GeometryCollection) { + checkSequences(gt1, gt2); + } else { return false; } diff --git a/modules/core/src/test/java/org/locationtech/jts/io/WKBWriterTest.java b/modules/core/src/test/java/org/locationtech/jts/io/WKBWriterTest.java index c6f963a3e9..1eb5ff2a96 100644 --- a/modules/core/src/test/java/org/locationtech/jts/io/WKBWriterTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/io/WKBWriterTest.java @@ -12,55 +12,108 @@ package org.locationtech.jts.io; import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.Point; -import junit.framework.TestCase; +import junit.textui.TestRunner; +import test.jts.GeometryTestCase; -public class WKBWriterTest extends TestCase { +/** + * Tests for WKB which test output explicitly. + * + * @author Martin Davis + * + */ +public class WKBWriterTest extends GeometryTestCase { - public WKBWriterTest(String name) { - super(name); - } + public static void main(String args[]) { + TestRunner.run(WKBWriterTest.class); + } + + public WKBWriterTest(String name) { + super(name); + } + + public void testSRID() throws Exception { + GeometryFactory gf = new GeometryFactory(); + Point p1 = gf.createPoint(new Coordinate(1,2)); + p1.setSRID(1234); + + //first write out without srid set + WKBWriter w = new WKBWriter(); + byte[] wkb = w.write(p1); + + //check the 3rd bit of the second byte, should be unset + byte b = (byte) (wkb[1] & 0x20); + assertEquals(0, b); + + //read geometry back in + WKBReader r = new WKBReader(gf); + Point p2 = (Point) r.read(wkb); + + assertTrue(p1.equalsExact(p2)); + assertEquals(0, p2.getSRID()); + + //not write out with srid set + w = new WKBWriter(2, true); + wkb = w.write(p1); + + //check the 3rd bit of the second byte, should be set + b = (byte) (wkb[1] & 0x20); + assertEquals(0x20, b); + + int srid = ((int) (wkb[5] & 0xff) << 24) | ( (int) (wkb[6] & 0xff) << 16) + | ( (int) (wkb[7] & 0xff) << 8) | (( int) (wkb[8] & 0xff) ); + + assertEquals(1234, srid); + + r = new WKBReader(gf); + p2 = (Point) r.read(wkb); + + //read the geometry back in + assertTrue(p1.equalsExact(p2)); + assertEquals(1234, p2.getSRID()); + } + + public void testPointEmpty2D() { + checkWKB("POINT EMPTY", 2, "0101000000000000000000F87F000000000000F87F" ); + } + + public void testPointEmpty3D() { + checkWKB("POINT EMPTY", 3, "0101000080000000000000F87F000000000000F87F000000000000F87F" ); + } + + public void testPolygonEmpty2DSRID() { + checkWKB("POLYGON EMPTY", 2, ByteOrderValues.LITTLE_ENDIAN, 4326, "0103000020E610000000000000" ); + } + + public void testPolygonEmpty2D() { + checkWKB("POLYGON EMPTY", 2, "010300000000000000" ); + } + + public void testPolygonEmpty3D() { + checkWKB("POLYGON EMPTY", 3, "010300008000000000" ); + } + + void checkWKB(String wkt, int dimension, String expectedWKBHex) { + checkWKB(wkt, dimension, ByteOrderValues.LITTLE_ENDIAN, -1, expectedWKBHex); + } - public void testSRID() throws Exception { - GeometryFactory gf = new GeometryFactory(); - Point p1 = gf.createPoint(new Coordinate(1,2)); - p1.setSRID(1234); - - //first write out without srid set - WKBWriter w = new WKBWriter(); - byte[] wkb = w.write(p1); - - //check the 3rd bit of the second byte, should be unset - byte b = (byte) (wkb[1] & 0x20); - assertEquals(0, b); - - //read geometry back in - WKBReader r = new WKBReader(gf); - Point p2 = (Point) r.read(wkb); - - assertTrue(p1.equalsExact(p2)); - assertEquals(0, p2.getSRID()); - - //not write out with srid set - w = new WKBWriter(2, true); - wkb = w.write(p1); - - //check the 3rd bit of the second byte, should be set - b = (byte) (wkb[1] & 0x20); - assertEquals(0x20, b); - - int srid = ((int) (wkb[5] & 0xff) << 24) | ( (int) (wkb[6] & 0xff) << 16) - | ( (int) (wkb[7] & 0xff) << 8) | (( int) (wkb[8] & 0xff) ); - - assertEquals(1234, srid); - - r = new WKBReader(gf); - p2 = (Point) r.read(wkb); - - //read the geometry back in - assertTrue(p1.equalsExact(p2)); - assertEquals(1234, p2.getSRID()); + void checkWKB(String wkt, int dimension, int byteOrder, int srid, String expectedWKBHex) { + Geometry geom = read(wkt); + + // set SRID if not -1 + boolean includeSRID = false; + if (srid >= 0) { + includeSRID = true; + geom.setSRID(srid); } + + WKBWriter wkbWriter = new WKBWriter(dimension, byteOrder, includeSRID); + byte[] wkb = wkbWriter.write(geom); + String wkbHex = WKBWriter.toHex(wkb); + + assertEquals(expectedWKBHex, wkbHex); + } } diff --git a/modules/core/src/test/java/org/locationtech/jts/io/WKTReaderTest.java b/modules/core/src/test/java/org/locationtech/jts/io/WKTReaderTest.java index 9f480e4b3c..3efe2d1d35 100644 --- a/modules/core/src/test/java/org/locationtech/jts/io/WKTReaderTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/io/WKTReaderTest.java @@ -88,11 +88,11 @@ public void testPoint() throws Exception { Point pt3DM = (Point) readerXYZM.read("POINT ZM(10 10 10 11)"); // assert - assertTrue(checkEqual(seqPt2D, pt2D.getCoordinateSequence())); - assertTrue(checkEqual(seqPt2DE, pt2DE.getCoordinateSequence())); - assertTrue(checkEqual(seqPt3D, pt3D.getCoordinateSequence())); - assertTrue(checkEqual(seqPt2DM, pt2DM.getCoordinateSequence())); - assertTrue(checkEqual(seqPt3DM, pt3DM.getCoordinateSequence())); + assertTrue(isEqual(seqPt2D, pt2D.getCoordinateSequence())); + assertTrue(isEqual(seqPt2DE, pt2DE.getCoordinateSequence())); + assertTrue(isEqual(seqPt3D, pt3D.getCoordinateSequence())); + assertTrue(isEqual(seqPt2DM, pt2DM.getCoordinateSequence())); + assertTrue(isEqual(seqPt3DM, pt3DM.getCoordinateSequence())); } public void testLineString() throws Exception { @@ -118,11 +118,11 @@ public void testLineString() throws Exception { .read("LINESTRING ZM(10 10 10 11, 20 20 10 11, 30 40 10 11)"); // assert - assertTrue(checkEqual(seqLs2D, ls2D.getCoordinateSequence())); - assertTrue(checkEqual(seqLs2DE, ls2DE.getCoordinateSequence())); - assertTrue(checkEqual(seqLs3D, ls3D.getCoordinateSequence())); - assertTrue(checkEqual(seqLs2DM, ls2DM.getCoordinateSequence())); - assertTrue(checkEqual(seqLs3DM, ls3DM.getCoordinateSequence())); + assertTrue(isEqual(seqLs2D, ls2D.getCoordinateSequence())); + assertTrue(isEqual(seqLs2DE, ls2DE.getCoordinateSequence())); + assertTrue(isEqual(seqLs3D, ls3D.getCoordinateSequence())); + assertTrue(isEqual(seqLs2DM, ls2DM.getCoordinateSequence())); + assertTrue(isEqual(seqLs3DM, ls3DM.getCoordinateSequence())); } public void testLinearRing() throws Exception { @@ -147,11 +147,11 @@ public void testLinearRing() throws Exception { .read("LINEARRING ZM(10 10 10 11, 20 20 10 11, 30 40 10 11, 10 10 10 11)"); // assert - assertTrue(checkEqual(seqLs2D, ls2D.getCoordinateSequence())); - assertTrue(checkEqual(seqLs2DE, ls2DE.getCoordinateSequence())); - assertTrue(checkEqual(seqLs3D, ls3D.getCoordinateSequence())); - assertTrue(checkEqual(seqLs2DM, ls2DM.getCoordinateSequence())); - assertTrue(checkEqual(seqLs3DM, ls3DM.getCoordinateSequence())); + assertTrue(isEqual(seqLs2D, ls2D.getCoordinateSequence())); + assertTrue(isEqual(seqLs2DE, ls2DE.getCoordinateSequence())); + assertTrue(isEqual(seqLs3D, ls3D.getCoordinateSequence())); + assertTrue(isEqual(seqLs2DM, ls2DM.getCoordinateSequence())); + assertTrue(isEqual(seqLs3DM, ls3DM.getCoordinateSequence())); } public void testLinearRingNotClosed() { @@ -214,20 +214,20 @@ public void testPolygon() throws Exception { (Polygon) rdr.read("POLYGON ZM((10 10 10 11, 10 20 10 11, 20 20 10 11, 20 15 10 11, 10 10 10 11), (11 11 10 11, 12 11 10 11, 12 12 10 11, 12 11 10 11, 11 11 10 11), (11 19 10 11, 11 18 10 11, 12 18 10 11, 12 19 10 11, 11 19 10 11))") }; // assert - assertTrue(checkEqual(csPoly2D[0], poly2D[2].getExteriorRing().getCoordinateSequence())); - assertTrue(checkEqual(csPoly2D[1], poly2D[2].getInteriorRingN(0).getCoordinateSequence())); - assertTrue(checkEqual(csPoly2D[2], poly2D[2].getInteriorRingN(1).getCoordinateSequence())); - assertTrue(checkEqual(csPoly2DE, poly2DE.getExteriorRing().getCoordinateSequence(), 2)); - - assertTrue(checkEqual(csPoly3D[0], poly3D[2].getExteriorRing().getCoordinateSequence())); - assertTrue(checkEqual(csPoly3D[1], poly3D[2].getInteriorRingN(0).getCoordinateSequence())); - assertTrue(checkEqual(csPoly3D[2], poly3D[2].getInteriorRingN(1).getCoordinateSequence())); - assertTrue(checkEqual(csPoly2DM[0], poly2DM[2].getExteriorRing().getCoordinateSequence())); - assertTrue(checkEqual(csPoly2DM[1], poly2DM[2].getInteriorRingN(0).getCoordinateSequence())); - assertTrue(checkEqual(csPoly2DM[2], poly2DM[2].getInteriorRingN(1).getCoordinateSequence())); - assertTrue(checkEqual(csPoly3DM[0], poly3DM[2].getExteriorRing().getCoordinateSequence())); - assertTrue(checkEqual(csPoly3DM[1], poly3DM[2].getInteriorRingN(0).getCoordinateSequence())); - assertTrue(checkEqual(csPoly3DM[2], poly3DM[2].getInteriorRingN(1).getCoordinateSequence())); + assertTrue(isEqual(csPoly2D[0], poly2D[2].getExteriorRing().getCoordinateSequence())); + assertTrue(isEqual(csPoly2D[1], poly2D[2].getInteriorRingN(0).getCoordinateSequence())); + assertTrue(isEqual(csPoly2D[2], poly2D[2].getInteriorRingN(1).getCoordinateSequence())); + assertTrue(isEqualDim(csPoly2DE, poly2DE.getExteriorRing().getCoordinateSequence(), 2)); + + assertTrue(isEqual(csPoly3D[0], poly3D[2].getExteriorRing().getCoordinateSequence())); + assertTrue(isEqual(csPoly3D[1], poly3D[2].getInteriorRingN(0).getCoordinateSequence())); + assertTrue(isEqual(csPoly3D[2], poly3D[2].getInteriorRingN(1).getCoordinateSequence())); + assertTrue(isEqual(csPoly2DM[0], poly2DM[2].getExteriorRing().getCoordinateSequence())); + assertTrue(isEqual(csPoly2DM[1], poly2DM[2].getInteriorRingN(0).getCoordinateSequence())); + assertTrue(isEqual(csPoly2DM[2], poly2DM[2].getInteriorRingN(1).getCoordinateSequence())); + assertTrue(isEqual(csPoly3DM[0], poly3DM[2].getExteriorRing().getCoordinateSequence())); + assertTrue(isEqual(csPoly3DM[1], poly3DM[2].getInteriorRingN(0).getCoordinateSequence())); + assertTrue(isEqual(csPoly3DM[2], poly3DM[2].getInteriorRingN(1).getCoordinateSequence())); } static double[][] mpCoords = new double[][] { @@ -406,15 +406,15 @@ public void testGeometryCollection() throws Exception { GeometryCollection gc3 = (GeometryCollection)rdr.read("GEOMETRYCOLLECTION EMPTY"); // assert - assertTrue(checkEqual(css[0], ((Point)gc0.getGeometryN(0)).getCoordinateSequence())); - assertTrue(checkEqual(css[1], ((Point)gc0.getGeometryN(1)).getCoordinateSequence())); - assertTrue(checkEqual(css[2], ((LineString)gc0.getGeometryN(2)).getCoordinateSequence())); - assertTrue(checkEqual(css[0], ((Point)gc1.getGeometryN(0)).getCoordinateSequence())); - assertTrue(checkEqual(css[3], ((LinearRing)gc1.getGeometryN(1)).getCoordinateSequence())); - assertTrue(checkEqual(css[2], ((LineString)gc1.getGeometryN(2)).getCoordinateSequence())); - assertTrue(checkEqual(css[0], ((Point)gc2.getGeometryN(0)).getCoordinateSequence())); - assertTrue(checkEqual(css[4], ((LinearRing)gc2.getGeometryN(1)).getCoordinateSequence())); - assertTrue(checkEqual(css[2], ((LineString)gc2.getGeometryN(2)).getCoordinateSequence())); + assertTrue(isEqual(css[0], ((Point)gc0.getGeometryN(0)).getCoordinateSequence())); + assertTrue(isEqual(css[1], ((Point)gc0.getGeometryN(1)).getCoordinateSequence())); + assertTrue(isEqual(css[2], ((LineString)gc0.getGeometryN(2)).getCoordinateSequence())); + assertTrue(isEqual(css[0], ((Point)gc1.getGeometryN(0)).getCoordinateSequence())); + assertTrue(isEqual(css[3], ((LinearRing)gc1.getGeometryN(1)).getCoordinateSequence())); + assertTrue(isEqual(css[2], ((LineString)gc1.getGeometryN(2)).getCoordinateSequence())); + assertTrue(isEqual(css[0], ((Point)gc2.getGeometryN(0)).getCoordinateSequence())); + assertTrue(isEqual(css[4], ((LinearRing)gc2.getGeometryN(1)).getCoordinateSequence())); + assertTrue(isEqual(css[2], ((LineString)gc2.getGeometryN(2)).getCoordinateSequence())); assertTrue(gc3.isEmpty()); } @@ -430,9 +430,9 @@ public void testNaN() throws Exception { Point pt3 = (Point)readerXYOld.read("POINT (10 10 NAN)"); // assert - assertTrue(checkEqual(seq, pt1.getCoordinateSequence())); - assertTrue(checkEqual(seq, pt2.getCoordinateSequence())); - assertTrue(checkEqual(seq, pt3.getCoordinateSequence())); + assertTrue(isEqual(seq, pt1.getCoordinateSequence())); + assertTrue(isEqual(seq, pt2.getCoordinateSequence())); + assertTrue(isEqual(seq, pt3.getCoordinateSequence())); } public void testLargeNumbers() throws Exception { @@ -459,7 +459,7 @@ public void testTurkishLocale() throws Exception { private void checkCS(CoordinateSequence cs, Geometry geom) { - assertTrue( checkEqual( cs, extractCS(geom))); + assertTrue( isEqual( cs, extractCS(geom))); } private CoordinateSequence extractCS(Geometry geom) { diff --git a/modules/core/src/test/java/org/locationtech/jts/io/kml/KMLReaderTest.java b/modules/core/src/test/java/org/locationtech/jts/io/kml/KMLReaderTest.java index 3855a6b292..eda1d31839 100644 --- a/modules/core/src/test/java/org/locationtech/jts/io/kml/KMLReaderTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/io/kml/KMLReaderTest.java @@ -12,9 +12,9 @@ package org.locationtech.jts.io.kml; -import junit.framework.Assert; import junit.framework.TestCase; import junit.textui.TestRunner; +import org.junit.Assert; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.PrecisionModel; diff --git a/modules/core/src/test/java/org/locationtech/jts/noding/NodedSegmentStringTest.java b/modules/core/src/test/java/org/locationtech/jts/noding/NodedSegmentStringTest.java new file mode 100644 index 0000000000..149e94e029 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/noding/NodedSegmentStringTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.noding; + +import java.util.List; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; + +import test.jts.GeometryTestCase; + +public class NodedSegmentStringTest extends GeometryTestCase { + + public static void main(String[] args) { + junit.textui.TestRunner.run(NodedSegmentStringTest.class); + } + public NodedSegmentStringTest(String name) { + super(name); + } + + /** + * Tests a case which involves nodes added when using the SnappingNoder. + * In this case one of the added nodes is relatively "far" from its segment, + * and "near" the start vertex of the segment. + * Computing the noding correctly requires the fix to {@link SegmentNode#compareTo(Object)} + * added in https://github.com/locationtech/jts/pull/399 + * + * See https://trac.osgeo.org/geos/ticket/1051 + */ + public void testSegmentNodeOrderingForSnappedNodes() { + checkNoding("LINESTRING (655103.6628454948 1794805.456674405, 655016.20226 1794940.10998, 655014.8317182435 1794941.5196832407)", + "MULTIPOINT((655016.29615051334 1794939.965427252), (655016.20226531825 1794940.1099718122), (655016.20226 1794940.10998), (655016.20225819293 1794940.1099794197))", + new int[] { 0, 0, 1, 1}, + "MULTILINESTRING ((655014.8317182435 1794941.5196832407, 655016.2022581929 1794940.1099794197), (655016.2022581929 1794940.1099794197, 655016.20226 1794940.10998), (655016.20226 1794940.10998, 655016.2022653183 1794940.1099718122), (655016.2022653183 1794940.1099718122, 655016.2961505133 1794939.965427252), (655016.2961505133 1794939.965427252, 655103.6628454948 1794805.456674405))"); + } + + private void checkNoding(String wktLine, String wktNodes, int[] segmentIndex, String wktExpected) { + Geometry line = read(wktLine); + Geometry pts = read(wktNodes); + + NodedSegmentString nss = new NodedSegmentString(line.getCoordinates(), null); + Coordinate[] node = pts.getCoordinates(); + + for (int i = 0; i < node.length; i++) { + nss.addIntersection(node[i], segmentIndex[i]); + } + + List nodedSS = NodingTestUtil.getNodedSubstrings(nss); + Geometry result = NodingTestUtil.toLines(nodedSS, line.getFactory()); + //System.out.println(result); + Geometry expected = read(wktExpected); + checkEqual(expected, result); + } + +} diff --git a/modules/core/src/test/java/org/locationtech/jts/noding/NodingTestUtil.java b/modules/core/src/test/java/org/locationtech/jts/noding/NodingTestUtil.java index 3bb3656be2..f741bf886d 100644 --- a/modules/core/src/test/java/org/locationtech/jts/noding/NodingTestUtil.java +++ b/modules/core/src/test/java/org/locationtech/jts/noding/NodingTestUtil.java @@ -46,6 +46,12 @@ public static List toSegmentStrings(List lines) return nssList; } + public static List getNodedSubstrings(NodedSegmentString nss) { + List resultEdgelist = new ArrayList(); + nss.getNodeList().addSplitEdges(resultEdgelist); + return resultEdgelist; + } + /** * Runs a noder on one or two sets of input geometries * and validates that the result is fully noded. @@ -71,4 +77,17 @@ public static Geometry nodeValidated(Geometry geom1, Geometry geom2, Noder noder Geometry result = toLines(nodedList, geom1.getFactory()); return result; } + + public NodedSegmentString createNSS(double... ords) { + if (ords.length % 2 != 0) { + throw new IllegalArgumentException("Must provide pairs of ordinates"); + } + Coordinate[] pts = new Coordinate[ ords.length / 2 ]; + for (int i = 0; i <= ords.length; i += 2) { + Coordinate p = new Coordinate(ords[i],ords[i+1]); + pts[i / 2] = p; + } + NodedSegmentString nss = new NodedSegmentString(pts, null); + return nss; + } } diff --git a/modules/core/src/test/java/org/locationtech/jts/noding/snapround/HotPixelTest.java b/modules/core/src/test/java/org/locationtech/jts/noding/snapround/HotPixelTest.java index 1d71ec487e..e66beca0c4 100644 --- a/modules/core/src/test/java/org/locationtech/jts/noding/snapround/HotPixelTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/noding/snapround/HotPixelTest.java @@ -139,7 +139,7 @@ public void testDiagonalUp() { } //----------------------------- - // Test segments entering through a corder and terminating inside pixel + // Test segments entering through a corner and terminating inside pixel public void testCornerULEndInside() { checkIntersects(true, 1, 1, 10, @@ -161,6 +161,43 @@ public void testCornerLRStartInside() { 1.02, 0.98, 1.3, 0.7 ); } + //----------------------------- + // Test segments tangent to a corner + + public void testCornerLLTangent() { + checkIntersects(true, 1, 1, 10, + 0.9, 1, 1, 0.9 ); + } + + public void testCornerLLTangentNoTouch() { + checkIntersects(false, 1, 1, 10, + 0.9, 0.9, 1, 0.9 ); + } + + public void testCornerULTangent() { + // does not intersect due to open top + checkIntersects(false, 1, 1, 10, + 0.9, 1, 1, 1.1 ); + } + + public void testCornerURTangent() { + // does not intersect due to open top + checkIntersects(false, 1, 1, 10, + 1, 1.1, 1.1, 1 ); + } + + public void testCornerLRTangent() { + // does not intersect due to open right side + checkIntersects(false, 1, 1, 10, + 1, 0.9, 1.1, 1 ); + } + + public void testCornerULTouchEnd() { + // does not intersect due to bounding box check for open top + checkIntersects(false, 1, 1, 10, + 0.9, 1.1, 0.95, 1.05 ); + } + //================================================ diff --git a/modules/core/src/test/java/org/locationtech/jts/noding/snapround/SnapRoundingTest.java b/modules/core/src/test/java/org/locationtech/jts/noding/snapround/SnapRoundingTest.java index 51e608ee14..3c4b062484 100644 --- a/modules/core/src/test/java/org/locationtech/jts/noding/snapround/SnapRoundingTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/noding/snapround/SnapRoundingTest.java @@ -44,7 +44,14 @@ public void testPolyWithCloseNode() { String[] polyWithCloseNode = { "POLYGON ((20 0, 20 160, 140 1, 160 160, 160 1, 20 0))" }; - runRounding(polyWithCloseNode); + checkRounding(polyWithCloseNode); + } + + public void testPolyWithCloseNodeFrac() { + String[] polyWithCloseNode = { + "POLYGON ((20 0, 20 160, 140 0.2, 160 160, 160 0, 20 0))" + }; + checkRounding(polyWithCloseNode); } public void testLineStringLongShort() { @@ -52,45 +59,45 @@ public void testLineStringLongShort() { "LINESTRING (0 0, 2 0)", "LINESTRING (0 0, 10 -1)" }; - runRounding(geoms); + checkRounding(geoms); } public void testBadLines1() { String[] badLines1 = { "LINESTRING ( 171 157, 175 154, 170 154, 170 155, 170 156, 170 157, 171 158, 171 159, 172 160, 176 156, 171 156, 171 159, 176 159, 172 155, 170 157, 174 161, 174 156, 173 156, 172 156 )" }; - runRounding(badLines1); + checkRounding(badLines1); } public void testBadLines2() { String[] badLines2 = { "LINESTRING ( 175 222, 176 222, 176 219, 174 221, 175 222, 177 220, 174 220, 174 222, 177 222, 175 220, 174 221 )" }; - runRounding(badLines2); + checkRounding(badLines2); } public void testCollapse1() { String[] collapse1 = { "LINESTRING ( 362 177, 375 164, 374 164, 372 161, 373 163, 372 165, 373 164, 442 58 )" }; - runRounding(collapse1); + checkRounding(collapse1); } public void testCollapse2() { String[] collapse2 = { "LINESTRING ( 393 175, 391 173, 390 175, 391 174, 391 173 )" }; - runRounding(collapse2); + checkRounding(collapse2); } public void testLineWithManySelfSnaps() { String[] line = { "LINESTRING (0 0, 6 4, 8 11, 13 13, 14 12, 11 12, 7 7, 7 3, 4 2)" }; - runRounding(line); + checkRounding(line); } public void testBadNoding1() { String[] badNoding1 = { "LINESTRING ( 76 47, 81 52, 81 53, 85 57, 88 62, 89 64, 57 80, 82 55, 101 74, 76 99, 92 67, 94 68, 99 71, 103 75, 139 111 )" }; - runRounding(badNoding1); + checkRounding(badNoding1); } public void testBadNoding1Extract() { @@ -99,7 +106,7 @@ public void testBadNoding1Extract() { "LINESTRING ( 94 68, 99 71 )", "LINESTRING ( 85 57, 88 62 )" }; - runRounding(badNoding1Extract); + checkRounding(badNoding1Extract); } public void testBadNoding1ExtractShift() { String[] badNoding1ExtractShift = { @@ -107,12 +114,12 @@ public void testBadNoding1ExtractShift() { "LINESTRING ( 12 13, 17 16 )", "LINESTRING ( 3 2, 6 7 )" }; - runRounding(badNoding1ExtractShift); + checkRounding(badNoding1ExtractShift); } static final double SNAP_TOLERANCE = 1.0; - void runRounding(String[] wkt) + void checkRounding(String[] wkt) { List geoms = fromWKT(wkt); PrecisionModel pm = new PrecisionModel(SNAP_TOLERANCE); diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferValidator.java b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferValidator.java index f9757d7c5a..67069083e6 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferValidator.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferValidator.java @@ -17,6 +17,7 @@ import java.util.Iterator; import java.util.Map; +import org.junit.Assert; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryCollection; import org.locationtech.jts.geom.GeometryFactory; @@ -29,16 +30,15 @@ import org.locationtech.jts.operation.buffer.validate.BufferResultValidator; import org.locationtech.jts.util.StringUtil; -import junit.framework.Assert; /** * @version 1.7 */ -public class BufferValidator +public class BufferValidator { - + public static void main(String[] args) throws Exception { Geometry g = new WKTReader().read( @@ -255,7 +255,7 @@ public void test() throws Exception { if (getOriginal().getClass() == GeometryCollection.class) { return; } - + Assert.assertTrue( supplement("BufferResultValidator failure"), BufferResultValidator.isValid(getOriginal(), bufferDistance, getBuffer())); diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/overlayng/CoverageUnionTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/overlayng/CoverageUnionTest.java index ae23fd8edf..15cbe36f99 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/overlayng/CoverageUnionTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/overlayng/CoverageUnionTest.java @@ -20,16 +20,21 @@ public void testPolygonsSimple( ) { "POLYGON ((1 5, 5 9, 9 5, 5 1, 1 5))"); } - public void testPolygonsNestedRings1( ) { + public void testPolygonsConcentricDonuts( ) { checkUnion("MULTIPOLYGON (((1 9, 9 9, 9 1, 1 1, 1 9), (2 8, 8 8, 8 2, 2 2, 2 8)), ((3 7, 7 7, 7 3, 3 3, 3 7), (4 6, 6 6, 6 4, 4 4, 4 6)))", "MULTIPOLYGON (((9 1, 1 1, 1 9, 9 9, 9 1), (8 8, 2 8, 2 2, 8 2, 8 8)), ((7 7, 7 3, 3 3, 3 7, 7 7), (4 4, 6 4, 6 6, 4 6, 4 4)))"); } - public void testPolygonsNestedRings2( ) { + public void testPolygonsConcentricHalfDonuts( ) { checkUnion("MULTIPOLYGON (((6 9, 1 9, 1 1, 6 1, 6 2, 2 2, 2 8, 6 8, 6 9)), ((6 9, 9 9, 9 1, 6 1, 6 2, 8 2, 8 8, 6 8, 6 9)), ((5 7, 3 7, 3 3, 5 3, 5 4, 4 4, 4 6, 5 6, 5 7)), ((5 4, 5 3, 7 3, 7 7, 5 7, 5 6, 6 6, 6 4, 5 4)))", "MULTIPOLYGON (((1 9, 6 9, 9 9, 9 1, 6 1, 1 1, 1 9), (2 8, 2 2, 6 2, 8 2, 8 8, 6 8, 2 8)), ((5 3, 3 3, 3 7, 5 7, 7 7, 7 3, 5 3), (5 4, 6 4, 6 6, 5 6, 4 6, 4 4, 5 4)))"); } + public void testPolygonsNested( ) { + checkUnion("GEOMETRYCOLLECTION (POLYGON ((1 9, 9 9, 9 1, 1 1, 1 9), (3 7, 3 3, 7 3, 7 7, 3 7)), POLYGON ((3 7, 7 7, 7 3, 3 3, 3 7)))", + "POLYGON ((1 1, 1 9, 9 9, 9 1, 1 1))"); + } + public void testPolygonsFormingHole( ) { checkUnion("MULTIPOLYGON (((1 1, 4 3, 5 6, 5 9, 1 1)), ((1 1, 9 1, 6 3, 4 3, 1 1)), ((9 1, 5 9, 5 6, 6 3, 9 1)))", "POLYGON ((9 1, 1 1, 5 9, 9 1), (6 3, 5 6, 4 3, 6 3))"); diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/overlayng/OverlayNGRobustTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/overlayng/OverlayNGRobustTest.java index c36b3d2a89..7befab041b 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/overlayng/OverlayNGRobustTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/overlayng/OverlayNGRobustTest.java @@ -13,6 +13,7 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.TopologyException; +import org.locationtech.jts.noding.SegmentNode; import junit.textui.TestRunner; import test.jts.GeometryTestCase; @@ -37,7 +38,7 @@ public static void main(String args[]) { public void testInvalidGeomUnion() { Geometry a = read("MULTIPOLYGON (((10 20, 20 20, 20 10, 10 10, 10 20)), ((15 25, 25 25, 25 14, 15 14, 15 25)))"); Geometry b = read("POLYGON ((10 30, 30 30, 30 10, 10 10, 10 30))"); - assertTrue(! isUnionSuccess(a, b)); + checkOverlayFail(a, b, OverlayNG.UNION); } /** @@ -46,19 +47,42 @@ public void testInvalidGeomUnion() { public void testSnappingUnion() { Geometry a = read("POLYGON ((305353.17217811686 254662.96357893807, 305381.46877743956 254662.96357893807, 305355.9999841164 254650.3999988427, 305348.6999841096 254646.59999883917, 305343.69998410495 254643.99999883675, 305337.3999840991 254640.79999883377, 305325.3999840879 254634.599998828, 305318.4999840815 254630.99999882464, 305311.1999840747 254627.1999988211, 305311.281112409 254627.0485592637, 305304.9999840689 254623.99999881812, 305292.2999840571 254617.49999881207, 305279.49998404516 254610.99999880602, 305267.0999840336 254604.59999880005, 305261.5999840285 254601.69999879735, 305261.63325920445 254601.6314910822, 305256.49998402374 254599.19999879503, 305251.399984019 254596.39999879242, 305251.0859505601 254595.24049063656, 305235.99998400465 254588.29999878487, 305226.5000159958 254583.3999987803, 305230.2274214533 254576.13633686322, 305178.9000159515 254558.49999875712, 305176.9000159496 254557.59999875628, 305168.2000159415 254552.8999987519, 305158.80001593276 254547.89999874725, 305162.74450293585 254540.37795376458, 305153.700015928 254545.29999874483, 305144.30001591926 254540.69999874054, 305143.00001591805 254538.99999873896, 305115.30311758886 254493.95272753935, 305115.83242013416 254502.95087080973, 305117.9000158947 254505.79999870804, 305117.8000158946 254507.89999871, 305116.4000158933 254507.9999987101, 305116.100015893 254507.89999871, 305116.50001589337 254514.09999871577, 305112.8539721245 254514.41431283377, 305113.90001589095 254515.39999871698, 305114.00001589104 254517.09999871856, 305114.3000158913 254520.39999872164, 305111.3136017478 254528.97389739184, 305113.90001589095 254530.29999873086, 305118.00001589477 254532.4999987329, 305117.9712506782 254532.6725900323, 305121.8000158983 254534.59999873486, 305112.20001588936 254552.999998752, 305100.6185283701 254554.56490414738, 305100.90727455297 254560.08316453052, 305136.6000159121 254551.09999875023, 305136.90001591237 254551.09999875023, 305137.30001591274 254551.19999875032, 305137.5000159129 254551.19999875032, 305138.0000159134 254551.29999875042, 305138.40001591376 254551.3999987505, 305139.0000159143 254551.5999987507, 305139.6000159149 254551.99999875107, 305148.300015923 254556.39999875517, 305147.69433216826 254574.07545846544, 305149.3000159239 254574.8999987724, 305156.5000159306 254560.69999875917, 305162.4000159361 254563.79999876206, 305173.30001594627 254569.39999876727, 305191.4000159631 254578.89999877612, 305194.17825385765 254580.89965313557, 305195.0000159665 254581.29999877836, 305195.30001596676 254580.7999987779, 305206.6000159773 254586.79999878348, 305210.8579817029 254589.6415565389, 305216.3000159863 254592.4999987888, 305216.7000159867 254592.0999987884, 305229.4000159985 254598.69999879456, 305230.9000159999 254599.4999987953, 305244.0045397212 254607.68546976737, 305245.7999840138 254607.09999880238, 305260.3999840274 254614.59999880937, 305282.2999840478 254626.09999882008, 305305.099984069 254637.99999883116, 305318.4999840815 254644.99999883768, 305336.39998409816 254654.29999884634, 305351.09998411185 254661.79999885333, 305353.17217811686 254662.96357893807))"); Geometry b = read("POLYGON ((305353.2092755222 254662.96357893807, 305381.9765468015 254662.96357893807, 305355.9999841164 254650.3999988427, 305348.6999841096 254646.59999883917, 305343.69998410495 254643.99999883675, 305337.3999840991 254640.79999883377, 305325.3999840879 254634.599998828, 305318.4999840815 254630.99999882464, 305311.1999840747 254627.1999988211, 305311.3154457364 254626.98447038594, 305304.9999840689 254623.99999881812, 305292.2999840571 254617.49999881207, 305279.49998404516 254610.99999880602, 305267.0999840336 254604.59999880005, 305261.5999840285 254601.69999879735, 305261.67110205657 254601.55357932782, 305256.49998402374 254599.19999879503, 305251.399984019 254596.39999879242, 305250.97991546144 254594.84897642606, 305235.99998400465 254588.29999878487, 305226.5000159958 254583.3999987803, 305230.40001599945 254575.79999877323, 305232.8243219372 254576.5620151067, 305237.9999840065 254575.19999877267, 305238.11680192675 254574.94040339434, 305178.9000159515 254558.49999875712, 305176.9000159496 254557.59999875628, 305168.2000159415 254552.8999987519, 305158.80001593276 254547.89999874725, 305163.10001593677 254539.6999987396, 305165.5226725255 254537.9669023306, 305164.3000159379 254537.29999873738, 305165.7000159392 254534.59999873486, 305188.8669453651 254513.64134592563, 305189.40001596126 254512.59999871437, 305189.960568551 254511.38880472587, 305153.700015928 254545.29999874483, 305144.30001591926 254540.69999874054, 305143.00001591805 254538.99999873896, 305118.93366550544 254491.23934128025, 305119.0000158957 254492.49999869565, 305117.1000158939 254492.89999869603, 305116.58729513956 254491.96878548746, 305115.20001589216 254492.19999869537, 305115.8237575254 254502.80360646098, 305117.9000158947 254505.79999870804, 305117.8000158946 254507.89999871, 305116.4000158933 254507.9999987101, 305116.100015893 254507.89999871, 305116.50001589337 254514.09999871577, 305112.90950390266 254514.40952561153, 305113.90001589095 254515.39999871698, 305114.00001589104 254517.09999871856, 305114.3000158913 254520.39999872164, 305111.4399527217 254528.61114782153, 305121.8000158983 254534.59999873486, 305112.20001588936 254552.999998752, 305100.5853566005 254553.9309547733, 305100.5969873565 254554.15323144238, 305136.6000159121 254551.09999875023, 305136.90001591237 254551.09999875023, 305137.30001591274 254551.19999875032, 305137.5000159129 254551.19999875032, 305138.0000159134 254551.29999875042, 305138.40001591376 254551.3999987505, 305139.0000159143 254551.5999987507, 305139.6000159149 254551.99999875107, 305148.300015923 254556.39999875517, 305147.5068930194 254573.97920592956, 305149.3000159239 254574.8999987724, 305156.5000159306 254560.69999875917, 305162.4000159361 254563.79999876206, 305173.30001594627 254569.39999876727, 305191.4000159631 254578.89999877612, 305194.8764979971 254581.23982335738, 305195.0000159665 254581.29999877836, 305195.30001596676 254580.7999987779, 305206.6000159773 254586.79999878348, 305212.2218734041 254590.35794409915, 305216.3000159863 254592.4999987888, 305216.7000159867 254592.0999987884, 305229.4000159985 254598.69999879456, 305230.9000159999 254599.4999987953, 305244.3482277927 254607.57339757012, 305245.7999840138 254607.09999880238, 305260.3999840274 254614.59999880937, 305282.2999840478 254626.09999882008, 305305.099984069 254637.99999883116, 305318.4999840815 254644.99999883768, 305336.39998409816 254654.29999884634, 305351.09998411185 254661.79999885333, 305353.2092755222 254662.96357893807))"); - assertTrue(isUnionSuccess(a, b)); + checkUnionSuccess(a, b); + } + + /** + * Tests correct ordering of {@link SegmentNode#compareTo(Object)}. + * + * See https://trac.osgeo.org/geos/ticket/1051 + */ + public void testSegmentNodeOrderingIntersection() { + Geometry a = read("POLYGON ((654948.3853299792 1794977.105854025, 655016.3812220972 1794939.918901604, 655016.2022581929 1794940.1099794197, 655014.9264068712 1794941.4254068714, 655014.7408834674 1794941.6101225375, 654948.3853299792 1794977.105854025))"); + Geometry b = read("POLYGON ((655103.6628454948 1794805.456674405, 655016.20226 1794940.10998, 655014.8317182435 1794941.5196832407, 655014.8295602322 1794941.5218318563, 655014.740883467 1794941.610122538, 655016.6029214273 1794938.7590508445, 655103.6628454948 1794805.456674405))"); + checkOverlaySuccess(a, b, OverlayNG.INTERSECTION); } // MD 2020-09-14 There is no known test case that requires Snap-Rounding to succeed. - public static boolean isUnionSuccess(Geometry a, Geometry b) { + public static void checkUnionSuccess(Geometry a, Geometry b) { + checkOverlaySuccess(a, b, OverlayNG.UNION ); + } + + public static void checkOverlaySuccess(Geometry a, Geometry b, int opCode) { try { - OverlayNGRobust.overlay(a, b, OverlayNG.UNION ); - return true; + OverlayNGRobust.overlay(a, b, opCode ); } - catch (TopologyException ex) { - return false; + catch (Throwable ex) { + fail("Overlay fails with an error: " + ex); } } + public static void checkOverlayFail(Geometry a, Geometry b, int opCode) { + try { + OverlayNGRobust.overlay(a, b, opCode ); + fail("Overlay was expected to fail"); + } + catch (Throwable ex) { + // do nothing - expected result + } + } } diff --git a/modules/core/src/test/java/org/locationtech/jts/simplify/GeometryOperationValidator.java b/modules/core/src/test/java/org/locationtech/jts/simplify/GeometryOperationValidator.java index 49d1c8c258..4c8305d207 100644 --- a/modules/core/src/test/java/org/locationtech/jts/simplify/GeometryOperationValidator.java +++ b/modules/core/src/test/java/org/locationtech/jts/simplify/GeometryOperationValidator.java @@ -12,11 +12,10 @@ package org.locationtech.jts.simplify; +import org.junit.Assert; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.io.WKTReader; -import junit.framework.Assert; - /** * Runs various validation tests on a the results of a geometry operation @@ -59,7 +58,7 @@ public boolean isAllTestsPassed() * Tests if the result is valid. * Throws an exception if result is not valid. * This allows chaining multiple tests together. - * + * * @throws Exception if the result is not valid. */ public void test() diff --git a/modules/core/src/test/java/test/jts/GeometryTestCase.java b/modules/core/src/test/java/test/jts/GeometryTestCase.java index f0d764737f..d635cb8b10 100644 --- a/modules/core/src/test/java/test/jts/GeometryTestCase.java +++ b/modules/core/src/test/java/test/jts/GeometryTestCase.java @@ -35,6 +35,8 @@ public abstract class GeometryTestCase extends TestCase{ + private static final String CHECK_EQUAL_FAIL = "FAIL - Expected = %s -- Actual = %s\n"; + final GeometryFactory geomFactory; final WKTReader readerWKT; @@ -50,13 +52,19 @@ protected GeometryTestCase(String name, CoordinateSequenceFactory coordinateSequ readerWKT = new WKTReader(geomFactory); } + /** + * Checks that the normalized values of the expected and actual + * geometries are exactly equals. + * + * @param expected the expected value + * @param actual the actual value + */ protected void checkEqual(Geometry expected, Geometry actual) { Geometry actualNorm = actual.norm(); Geometry expectedNorm = expected.norm(); boolean equal = actualNorm.equalsExact(expectedNorm); if (! equal) { - System.out.println("FAIL - Expected = " + expectedNorm - + " actual = " + actualNorm ); + System.out.format(CHECK_EQUAL_FAIL, expectedNorm, actualNorm ); } assertTrue(equal); } @@ -66,8 +74,7 @@ protected void checkEqual(Geometry expected, Geometry actual, double tolerance) Geometry expectedNorm = expected.norm(); boolean equal = actualNorm.equalsExact(expectedNorm, tolerance); if (! equal) { - System.out.println("FAIL - Expected = " + expectedNorm - + " actual = " + actualNorm ); + System.out.format(CHECK_EQUAL_FAIL, expectedNorm, actualNorm ); } assertTrue(equal); } @@ -201,7 +208,7 @@ else if (ordinateFlags.contains(Ordinate.M)) { } /** - * Checks two {@link CoordinateSequence}s for equality. The following items are checked: + * Tests two {@link CoordinateSequence}s for equality. The following items are checked: *

      *
    • size
    • dimension
    • ordinate values
    • *
    @@ -210,12 +217,12 @@ else if (ordinateFlags.contains(Ordinate.M)) { * @param seq2 another sequence * @return {@code true} if both sequences are equal */ - public static boolean checkEqual(CoordinateSequence seq1, CoordinateSequence seq2) { - return checkEqual(seq1, seq2, 0d); + public static boolean isEqual(CoordinateSequence seq1, CoordinateSequence seq2) { + return isEqualTol(seq1, seq2, 0d); } /** - * Checks two {@link CoordinateSequence}s for equality. The following items are checked: + * Tests two {@link CoordinateSequence}s for equality. The following items are checked: *
      *
    • size
    • dimension
    • ordinate values with {@code tolerance}
    • *
    @@ -224,14 +231,14 @@ public static boolean checkEqual(CoordinateSequence seq1, CoordinateSequence seq * @param seq2 another sequence * @return {@code true} if both sequences are equal */ - public static boolean checkEqual(CoordinateSequence seq1, CoordinateSequence seq2, double tolerance) { + public static boolean isEqualTol(CoordinateSequence seq1, CoordinateSequence seq2, double tolerance) { if (seq1.getDimension() != seq2.getDimension()) return false; - return checkEqual(seq1, seq2, seq1.getDimension(), tolerance); + return isEqual(seq1, seq2, seq1.getDimension(), tolerance); } /** - * Checks two {@link CoordinateSequence}s for equality. The following items are checked: + * Tests two {@link CoordinateSequence}s for equality. The following items are checked: *
      *
    • size
    • dimension up to {@code dimension}
    • ordinate values
    • *
    @@ -240,12 +247,12 @@ public static boolean checkEqual(CoordinateSequence seq1, CoordinateSequence seq * @param seq2 another sequence * @return {@code true} if both sequences are equal */ - public static boolean checkEqual(CoordinateSequence seq1, CoordinateSequence seq2, int dimension) { - return checkEqual(seq1, seq2, dimension, 0d); + public static boolean isEqualDim(CoordinateSequence seq1, CoordinateSequence seq2, int dimension) { + return isEqual(seq1, seq2, dimension, 0d); } /** - * Checks two {@link CoordinateSequence}s for equality. The following items are checked: + * Tests two {@link CoordinateSequence}s for equality. The following items are checked: *
      *
    • size
    • dimension up to {@code dimension}
    • ordinate values with {@code tolerance}
    • *
    @@ -254,7 +261,7 @@ public static boolean checkEqual(CoordinateSequence seq1, CoordinateSequence seq * @param seq2 another sequence * @return {@code true} if both sequences are equal */ - public static boolean checkEqual(CoordinateSequence seq1, CoordinateSequence seq2, int dimension, double tolerance) { + public static boolean isEqual(CoordinateSequence seq1, CoordinateSequence seq2, int dimension, double tolerance) { if (seq1 != null && seq2 == null) return false; if (seq1 == null && seq2 != null) return false; diff --git a/modules/core/src/test/java/test/jts/GeometryTestData.java b/modules/core/src/test/java/test/jts/GeometryTestData.java index 66e141cf52..9729a930a3 100644 --- a/modules/core/src/test/java/test/jts/GeometryTestData.java +++ b/modules/core/src/test/java/test/jts/GeometryTestData.java @@ -2,21 +2,46 @@ public class GeometryTestData { - public static String WKT_POINT = "POINT ( 10 10)"; + public static String WKT_POINT = "POINT (10 10)"; + public static String WKT_POINT_EMPTY = "POINT EMPTY"; public static String WKT_LINESTRING = "LINESTRING (10 10, 20 20, 30 40)"; + public static String WKT_LINESTRING_EMPTY = "LINESTRING EMPTY"; public static String WKT_LINEARRING = "LINEARRING (10 10, 20 20, 30 40, 10 10)"; + public static String WKT_LINEARRING_EMPTY = "LINEARRING EMPTY"; public static String WKT_POLY = "POLYGON ((50 50, 50 150, 150 150, 150 50, 50 50))"; + public static String WKT_POLY_HOLE = "POLYGON ((50 50, 50 150, 150 150, 150 50, 50 50), (70 130, 130 130, 130 70, 70 70, 70 130))"; + public static String WKT_POLY_HOLE2 = "POLYGON ((50 50, 50 150, 150 150, 150 50, 50 50), (60 140, 90 140, 90 110, 60 110, 60 140), (110 90, 140 90, 140 60, 110 60, 110 90))"; + public static String WKT_POLY_EMPTY = "POLYGON EMPTY"; public static String WKT_MULTIPOINT = "MULTIPOINT ((10 10), (20 20))"; + public static String WKT_MULTIPOINT_SINGLE = "MULTIPOINT ((10 10))"; + public static String WKT_MULTIPOINT_EMPTY = "MULTIPOINT EMPTY"; public static String WKT_MULTILINESTRING = "MULTILINESTRING ((10 10, 20 20), (15 15, 30 15))"; + public static String WKT_MULTILINESTRING_SINGLE = "MULTILINESTRING ((10 10, 20 20))"; + public static String WKT_MULTILINESTRING_EMPTY = "MULTILINESTRING EMPTY"; public static String WKT_MULTIPOLYGON = "MULTIPOLYGON (((10 10, 10 20, 20 20, 20 15, 10 10)), ((60 60, 70 70, 80 60, 60 60)))"; + public static String WKT_MULTIPOLYGON_SINGLE = "MULTIPOLYGON (((10 10, 10 20, 20 20, 20 15, 10 10)))"; + public static String WKT_MULTIPOLYGON_EMPTY = "MULTIPOLYGON EMPTY"; public static String WKT_GC = "GEOMETRYCOLLECTION (POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200)), LINESTRING (150 250, 250 250))"; + public static String WKT_GC_ALP = "GEOMETRYCOLLECTION (POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200)), LINESTRING (150 250, 250 250), POINT (1 1))"; + public static String WKT_GC_NESTED = "GEOMETRYCOLLECTION (LINESTRING (1 1, 2 2), GEOMETRYCOLLECTION (POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200)), LINESTRING (150 250, 250 250), POINT (1 1)))"; + public static String WKT_GC_EMPTY = "GEOMETRYCOLLECTION EMPTY"; + public static String[] WKT_ALL = { + WKT_POINT, WKT_POINT_EMPTY, + WKT_LINESTRING, WKT_LINESTRING_EMPTY, + WKT_LINEARRING, WKT_LINEARRING_EMPTY, + WKT_POLY, WKT_POLY_HOLE, WKT_POLY_HOLE2, WKT_POLY_EMPTY, + WKT_MULTIPOINT, WKT_MULTIPOINT_SINGLE, WKT_MULTIPOINT_EMPTY, + WKT_MULTILINESTRING, WKT_MULTILINESTRING_SINGLE, WKT_MULTILINESTRING_EMPTY, + WKT_MULTIPOLYGON, WKT_MULTIPOLYGON_SINGLE, WKT_MULTIPOLYGON_EMPTY, + WKT_GC, WKT_GC_ALP, WKT_GC_NESTED, WKT_GC_EMPTY + }; } diff --git a/modules/core/src/test/java/test/jts/perf/algorithm/IndexedPointInAreaStressTest.java b/modules/core/src/test/java/test/jts/perf/algorithm/IndexedPointInAreaStressTest.java index 231fd58ade..bc0e1d8f94 100644 --- a/modules/core/src/test/java/test/jts/perf/algorithm/IndexedPointInAreaStressTest.java +++ b/modules/core/src/test/java/test/jts/perf/algorithm/IndexedPointInAreaStressTest.java @@ -11,7 +11,6 @@ */ package test.jts.perf.algorithm; -import org.locationtech.jts.algorithm.PerturbedGridPolygonBuilder; import org.locationtech.jts.algorithm.locate.IndexedPointInAreaLocator; import org.locationtech.jts.algorithm.locate.PointOnGeometryLocator; import org.locationtech.jts.geom.Geometry; diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/PerturbedGridPolygonBuilder.java b/modules/core/src/test/java/test/jts/perf/algorithm/PerturbedGridPolygonBuilder.java similarity index 98% rename from modules/core/src/test/java/org/locationtech/jts/algorithm/PerturbedGridPolygonBuilder.java rename to modules/core/src/test/java/test/jts/perf/algorithm/PerturbedGridPolygonBuilder.java index 66bd6b8fb2..2efc78eb18 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/PerturbedGridPolygonBuilder.java +++ b/modules/core/src/test/java/test/jts/perf/algorithm/PerturbedGridPolygonBuilder.java @@ -10,7 +10,7 @@ * http://www.eclipse.org/org/documents/edl-v10.php. */ -package org.locationtech.jts.algorithm; +package test.jts.perf.algorithm; import java.util.Random; diff --git a/modules/core/src/test/java/test/jts/perf/algorithm/SimpleRayCrossingStressTest.java b/modules/core/src/test/java/test/jts/perf/algorithm/SimpleRayCrossingStressTest.java index f628708db1..d6aca1b82c 100644 --- a/modules/core/src/test/java/test/jts/perf/algorithm/SimpleRayCrossingStressTest.java +++ b/modules/core/src/test/java/test/jts/perf/algorithm/SimpleRayCrossingStressTest.java @@ -11,7 +11,6 @@ */ package test.jts.perf.algorithm; -import org.locationtech.jts.algorithm.PerturbedGridPolygonBuilder; import org.locationtech.jts.algorithm.RayCrossingCounter; import org.locationtech.jts.algorithm.locate.PointOnGeometryLocator; import org.locationtech.jts.geom.Coordinate; diff --git a/modules/core/src/test/java/test/jts/perf/operation/distance/DistanceGeomPairPerfTest.java b/modules/core/src/test/java/test/jts/perf/operation/distance/DistanceGeomPairPerfTest.java new file mode 100644 index 0000000000..fbc049f44f --- /dev/null +++ b/modules/core/src/test/java/test/jts/perf/operation/distance/DistanceGeomPairPerfTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2016 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +package test.jts.perf.operation.distance; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.Point; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.util.SineStarFactory; +import org.locationtech.jts.operation.distance.DistanceOp; +import org.locationtech.jts.operation.distance.IndexedFacetDistance; +import org.locationtech.jts.util.GeometricShapeFactory; +import org.locationtech.jts.util.Stopwatch; + +import test.jts.perf.PerformanceTestCase; +import test.jts.perf.PerformanceTestRunner; + +public class DistanceGeomPairPerfTest extends PerformanceTestCase +{ + + static final int MAX_ITER = 100; + + public static void main(String args[]) { + PerformanceTestRunner.run(DistanceGeomPairPerfTest.class); + } + + boolean testFailed = false; + boolean verbose = true; + + public DistanceGeomPairPerfTest(String name) { + super(name); + setRunSize(new int[] {10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10_000, 20_000, 50_000}); + setRunIterations(1000); + } + + static final double SIZE = 100; + static final double OFFSET = SIZE * 10; + + private Geometry geom1; + private Geometry geom2; + private Point pt2; + + public void startRun(int nPts) + { + //int nPts2 = nPts; + int nPts2 = 100; + + System.out.println("\nRunning with " + nPts + " points (size-product = " + nPts * nPts2); + + geom1 = createSineStar(nPts, 0); + geom2 = createSineStar(nPts2, OFFSET); + + pt2 = geom2.getCentroid(); + } + + public void runSimpleLines() + { + double dist = DistanceOp.distance(geom1, geom2); + } + + public void runIndexedLines() + { + double dist = IndexedFacetDistance.distance(geom1, geom2); + } + + + public void runSimpleLinePoint() + { + double dist = DistanceOp.distance(geom1, pt2); + } + + public void runIndexedLinePoint() + { + double dist = IndexedFacetDistance.distance(geom1, pt2); + } + + public void runCachedLinePoint() + { + double dist = CachedFastDistance.getDistance(geom1, pt2); + } + + + Geometry createSineStar(int nPts, double offset) + { + SineStarFactory gsf = new SineStarFactory(); + gsf.setCentre(new Coordinate(0, 0)); + gsf.setSize(SIZE); + gsf.setNumPoints(nPts); + gsf.setCentre(new Coordinate(0, offset)); + + Geometry g2 = gsf.createSineStar().getBoundary(); + + return g2; + } +} + + diff --git a/modules/core/src/test/java/test/jts/perf/operation/overlayng/OverlayNGSnapRoundingPerfTest.java b/modules/core/src/test/java/test/jts/perf/operation/overlayng/OverlayNGSnapRoundingPerfTest.java new file mode 100644 index 0000000000..508e429a91 --- /dev/null +++ b/modules/core/src/test/java/test/jts/perf/operation/overlayng/OverlayNGSnapRoundingPerfTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2016 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +package test.jts.perf.operation.overlayng; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.PrecisionModel; +import org.locationtech.jts.geom.util.SineStarFactory; +import org.locationtech.jts.operation.overlayng.OverlayNG; +import org.locationtech.jts.operation.overlayng.OverlayNGRobust; +import org.locationtech.jts.precision.GeometryPrecisionReducer; + +import test.jts.perf.PerformanceTestCase; +import test.jts.perf.PerformanceTestRunner; + +/** + * + * @author Martin Davis + * + */ +public class OverlayNGSnapRoundingPerfTest +extends PerformanceTestCase +{ + + private static final int N_ITER = 1; + + static double ORG_X = 100; + static double ORG_Y = 100; + static double SIZE = 100; + static int N_ARMS = 20; + static double ARM_RATIO = 0.3; + + public static void main(String args[]) { + PerformanceTestRunner.run(OverlayNGSnapRoundingPerfTest.class); + } + + private Geometry sineStar; + + private PrecisionModel pm; + + private Geometry sineStar2; + + + public OverlayNGSnapRoundingPerfTest(String name) + { + super(name); + setRunSize(new int[] { 100, 200, 400, 1000, 2000, 4000, 8000, 10000, + 100_000, 200_000, 400_000, 1000_000 }); + setRunIterations(N_ITER); + } + + public void setUp() + { + System.out.println("OverlayNG Snap-Rounding perf test"); + System.out.println("SineStar: origin: (" + + ORG_X + ", " + ORG_Y + ") size: " + SIZE + + " # arms: " + N_ARMS + " arm ratio: " + ARM_RATIO); + System.out.println("# Iterations: " + N_ITER); + } + + public void startRun(int npts) + { + iter = 0; + sineStar = SineStarFactory.create(new Coordinate(ORG_X, ORG_Y), SIZE, npts, N_ARMS, ARM_RATIO); + sineStar2 = SineStarFactory.create(new Coordinate(ORG_X + SIZE/8, ORG_Y + SIZE/8), SIZE, npts, N_ARMS, ARM_RATIO); + + double scale = npts / SIZE; + pm = new PrecisionModel(scale); + System.out.format("\n# pts = %d, Scale = %f\n", npts, scale); + + if (npts <= 1000) System.out.println(sineStar); + } + + private int iter = 0; + + public void runSR() + { + Geometry result = OverlayNG.overlay(sineStar, sineStar2, OverlayNG.INTERSECTION, pm); + } + + public void xrunRobust() + { + Geometry result = OverlayNGRobust.overlay(sineStar, sineStar2, OverlayNG.INTERSECTION); + } + + public void xrunClassic() + { + Geometry result = sineStar.intersection(sineStar2); + } + +} diff --git a/modules/core/src/test/java/test/jts/perf/precision/GeometryPrecisionReducerPerfTest.java b/modules/core/src/test/java/test/jts/perf/precision/GeometryPrecisionReducerPerfTest.java new file mode 100644 index 0000000000..2ecc05aeb0 --- /dev/null +++ b/modules/core/src/test/java/test/jts/perf/precision/GeometryPrecisionReducerPerfTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +package test.jts.perf.precision; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.PrecisionModel; +import org.locationtech.jts.geom.util.SineStarFactory; +import org.locationtech.jts.precision.GeometryPrecisionReducer; + +import test.jts.perf.PerformanceTestCase; +import test.jts.perf.PerformanceTestRunner; + +/** + * This test revealed a scaling issue with the {@link SnapRoundingNoder}: + * the {@link HotPixelIndex} could not handle very large numbers + * of points due to kdTree becoming unbalanced. + * + * @author Martin Davis + * + */ +public class GeometryPrecisionReducerPerfTest +extends PerformanceTestCase +{ + + private static final int N_ITER = 1; + + static double ORG_X = 100; + static double ORG_Y = 100; + static double SIZE = 100; + static int N_ARMS = 20; + static double ARM_RATIO = 0.3; + + public static void main(String args[]) { + PerformanceTestRunner.run(GeometryPrecisionReducerPerfTest.class); + } + + private Geometry sineStar; + + private PrecisionModel pm; + + + public GeometryPrecisionReducerPerfTest(String name) + { + super(name); + setRunSize(new int[] { 100, 200, 400, 1000, 2000, 4000, 8000, 10000, + 100_000, 200_000, 400_000 + , 1000_000, 2000_000 }); + setRunIterations(N_ITER); + } + + public void setUp() + { + System.out.println("Geometry Precision Reducer perf test"); + System.out.println("SineStar: origin: (" + + ORG_X + ", " + ORG_Y + ") size: " + SIZE + + " # arms: " + N_ARMS + " arm ratio: " + ARM_RATIO); + System.out.println("# Iterations: " + N_ITER); + } + + public void startRun(int npts) + { + iter = 0; + sineStar = SineStarFactory.create(new Coordinate(ORG_X, ORG_Y), SIZE, npts, N_ARMS, ARM_RATIO); + + double scale = npts / SIZE; + pm = new PrecisionModel(scale); + System.out.format("\n# pts = %d, Scale = %f\n", npts, scale); + + if (npts <= 1000) System.out.println(sineStar); + } + + private int iter = 0; + + public void runReduce() + { + Geometry sinePolyCrinkly = GeometryPrecisionReducer.reduce(sineStar, pm); + } + +} diff --git a/modules/lab/src/main/java/org/locationtech/jtslab/IteratedOverlayFunctions.java b/modules/lab/src/main/java/org/locationtech/jtslab/IteratedOverlayFunctions.java new file mode 100644 index 0000000000..16741741f2 --- /dev/null +++ b/modules/lab/src/main/java/org/locationtech/jtslab/IteratedOverlayFunctions.java @@ -0,0 +1,145 @@ +package org.locationtech.jtslab; + + +import java.util.ArrayList; +import java.util.List; + +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.MultiPolygon; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.PrecisionModel; +import org.locationtech.jts.geom.util.PolygonExtracter; +import org.locationtech.jts.index.quadtree.Quadtree; +import org.locationtech.jts.operation.overlayng.OverlayNG; +import org.locationtech.jts.operation.overlayng.OverlayNGRobust; + +public class IteratedOverlayFunctions { + + public static Geometry overlayOld(Geometry coll) { + return overlay(coll, false, null); + } + + public static Geometry overlayNG(Geometry coll) { + return overlay(coll, true, null); + } + + public static Geometry overlaySR(Geometry coll, double scale) { + PrecisionModel pm = new PrecisionModel(scale); + return overlay(coll, true, pm); + } + + private static Geometry overlay(Geometry coll, boolean useNG, PrecisionModel pm) { + List result = new ArrayList(); + for (int i = 0; i < coll.getNumGeometries(); i++) { + Geometry inGeom = coll.getGeometryN(i); + + int size = result.size(); + for (int j = 0; j < size; j++) { + Geometry resGeom = result.get(j); + if (resGeom.isEmpty()) continue; + + Geometry intGeom = extractPolygons(overlayIntersection(resGeom, inGeom, useNG, pm)); + if (! intGeom.isEmpty()) { + result.add(intGeom); + + Geometry resDiff = extractPolygons(overlayDifference(resGeom, intGeom, useNG, pm)); + result.set( j, resDiff ); + + inGeom = extractPolygons(overlayDifference(inGeom, intGeom, useNG, pm)); + } + } + // keep remainder of input (non-overlapped part) + if (! inGeom.isEmpty()) { + result.addAll( PolygonExtracter.getPolygons( inGeom ) ); + //result.add( inGeom ); + } + } + // TODO: return only non-empty polygons + List resultPolys = extractPolygonsNonEmpty(result); + return coll.getFactory().buildGeometry(resultPolys); + } + + + public static Geometry overlayIndexedNG(Geometry coll) { + return overlayIndexed(coll, true, null); + } + + private static Geometry overlayIndexed(Geometry coll, boolean useNG, PrecisionModel pm) { + Quadtree tree = new Quadtree(); + for (int i = 0; i < coll.getNumGeometries(); i++) { + + Geometry inGeom = coll.getGeometryN(i); + List results = tree.query( inGeom.getEnvelopeInternal() ); + + for (Polygon resPoly : results) { + + Geometry intGeom = extractPolygons(overlayIntersection(resPoly, inGeom, useNG, pm)); + List intList = PolygonExtracter.getPolygons( intGeom ); + + // resultant is overlapped by next input + if (! intGeom.isEmpty() && intList.size() > 0) { + tree.remove(resPoly.getEnvelopeInternal(), resPoly); + + for (Polygon intPoly : intList) { + tree.insert( intPoly.getEnvelopeInternal(), intPoly ); + Geometry resDiff = overlayDifference(resPoly, intGeom, useNG, pm); + insertPolys(resDiff, tree); + + inGeom = extractPolygons(overlayDifference(inGeom, intPoly, useNG, pm)); + } + } + } + // keep remainder of input + insertPolys(inGeom, tree); + } + List result = tree.queryAll(); + return coll.getFactory().buildGeometry(result); + } + + private static void insertPolys(Geometry geom, Quadtree tree) { + if (geom.isEmpty()) return; + List polyList = PolygonExtracter.getPolygons( geom ); + for (Polygon poly : polyList) { + tree.insert(poly.getEnvelopeInternal(), poly); + } + } + + private static Geometry overlayIntersection(Geometry a, Geometry b, boolean useNG, PrecisionModel pm) { + if (useNG) { + if (pm == null) + return OverlayNGRobust.overlay(a, b, OverlayNG.INTERSECTION); + return OverlayNG.overlay(a, b, OverlayNG.INTERSECTION, pm); + } + return a.intersection(b); + } + + private static Geometry overlayDifference(Geometry a, Geometry b, boolean useNG, PrecisionModel pm) { + if (useNG) { + if (pm == null) + return OverlayNGRobust.overlay(a, b, OverlayNG.DIFFERENCE); + return OverlayNG.overlay(a, b, OverlayNG.DIFFERENCE, pm); + } + return a.difference(b); + } + + private static Geometry extractPolygons(Geometry geom) { + List polys = PolygonExtracter.getPolygons(geom); + return geom.getFactory().buildGeometry(polys); + } + + private static List extractPolygonsNonEmpty(List geoms) { + List exPolys = new ArrayList(); + for (Geometry geom : geoms) { + if (! geom.isEmpty()) { + if (geom instanceof Polygon) { + exPolys.add((Polygon) geom); + } + else if (geom instanceof MultiPolygon) { + exPolys.addAll(PolygonExtracter.getPolygons(geom)); + } + } + } + return exPolys; + } +} diff --git a/modules/lab/src/main/java/org/locationtech/jtslab/SnapRoundFunctions.java b/modules/lab/src/main/java/org/locationtech/jtslab/SnapRoundFunctions.java deleted file mode 100644 index cb835bbc32..0000000000 --- a/modules/lab/src/main/java/org/locationtech/jtslab/SnapRoundFunctions.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2016 Vivid Solutions. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * and Eclipse Distribution License v. 1.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * - * http://www.eclipse.org/org/documents/edl-v10.php. - */ -package org.locationtech.jtslab; - -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.PrecisionModel; -import org.locationtech.jtslab.snapround.GeometrySnapRounder; - -public class SnapRoundFunctions { - /** - * Reduces precision pointwise, then snap-rounds. - * Note that output set may not contain non-unique linework - * (and thus cannot be used as input to Polygonizer directly). - * UnaryUnion is one way to make the linework unique. - * - * - * @param geom a geometry containing linework to node - * @param scaleFactor the precision model scale factor to use - * @return the noded, snap-rounded linework - */ - public static Geometry snapRoundLines( - Geometry geom, double scaleFactor) { - PrecisionModel pm = new PrecisionModel(scaleFactor); - - GeometrySnapRounder gsr = new GeometrySnapRounder(pm); - gsr.setLineworkOnly(true); - Geometry snapped = gsr.execute(geom); - return snapped; - } - - public static Geometry snapRound( - Geometry geomA, Geometry geomB, - double scaleFactor) { - PrecisionModel pm = new PrecisionModel(scaleFactor); - - Geometry geom = geomA; - - if (geomB != null) { - geom = geomA.getFactory().createGeometryCollection(new Geometry[] { geomA, geomB }); - } - - GeometrySnapRounder gsr = new GeometrySnapRounder(pm); - Geometry snapped = gsr.execute(geom); - return snapped; - } - - -} diff --git a/modules/lab/src/main/java/org/locationtech/jtslab/SnapRoundOverlayFunctions.java b/modules/lab/src/main/java/org/locationtech/jtslab/SnapRoundOverlayFunctions.java deleted file mode 100644 index 340ecebd81..0000000000 --- a/modules/lab/src/main/java/org/locationtech/jtslab/SnapRoundOverlayFunctions.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2016 Vivid Solutions. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * and Eclipse Distribution License v. 1.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * - * http://www.eclipse.org/org/documents/edl-v10.php. - */ -package org.locationtech.jtslab; - -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.Polygonal; - -public class SnapRoundOverlayFunctions { - - - public static Geometry intersection(Geometry geomA, Geometry geomB, double scaleFactor) { - Geometry[] geom = snapClean(geomA, geomB, scaleFactor); - return geom[0].intersection(geom[1]); - } - - public static Geometry difference(Geometry geomA, Geometry geomB, double scaleFactor) { - Geometry[] geom = snapClean(geomA, geomB, scaleFactor); - return geom[0].difference(geom[1]); - } - - public static Geometry symDifference(Geometry geomA, Geometry geomB, double scaleFactor) { - Geometry[] geom = snapClean(geomA, geomB, scaleFactor); - return geom[0].symDifference(geom[1]); - } - - public static Geometry union(Geometry geomA, Geometry geomB, double scaleFactor) { - Geometry[] geom = snapClean(geomA, geomB, scaleFactor); - return geom[0].union(geom[1]); - } - - public static Geometry unaryUnion(Geometry geomA, double scaleFactor) { - Geometry[] geom = snapClean(geomA, null, scaleFactor); - return geom[0].union(); - } - - private static Geometry[] snapClean( - Geometry geomA, Geometry geomB, - double scaleFactor) { - Geometry snapped = SnapRoundFunctions.snapRound(geomA, geomB, scaleFactor); - // TODO: don't need to clean once GeometrySnapRounder ensures all components are valid - Geometry aSnap = snapped; - Geometry bSnap = null; - if (geomB != null) { - aSnap = snapped.getGeometryN(0); - bSnap = snapped.getGeometryN(1); - } - return new Geometry[] { aSnap, bSnap }; - } - - private static Geometry clean(Geometry geom) { - // TODO: only buffer if it is a polygonal geometry - if (! (geom instanceof Polygonal) ) return geom; - return geom.buffer(0); - } - - -} diff --git a/modules/lab/src/main/java/org/locationtech/jtslab/snapround/GeometryCoordinateReplacer.java b/modules/lab/src/main/java/org/locationtech/jtslab/snapround/GeometryCoordinateReplacer.java deleted file mode 100644 index 9e1662a560..0000000000 --- a/modules/lab/src/main/java/org/locationtech/jtslab/snapround/GeometryCoordinateReplacer.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2016 Vivid Solutions. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * and Eclipse Distribution License v. 1.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * - * http://www.eclipse.org/org/documents/edl-v10.php. - */ -package org.locationtech.jtslab.snapround; - -import java.util.Map; - -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.CoordinateSequence; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.LineString; -import org.locationtech.jts.geom.LinearRing; -import org.locationtech.jtslab.geom.util.GeometryEditorEx.CoordinateSequenceOperation; - -class GeometryCoordinateReplacer extends CoordinateSequenceOperation { - - private Map geometryLinesMap; - - public GeometryCoordinateReplacer(Map linesMap) { - this.geometryLinesMap = linesMap; - } - - /** - * Gets the snapped coordinate array for an atomic geometry, - * or null if it has collapsed. - * - * @return the snapped coordinate array for this geometry - * @return null if the snapped coordinates have collapsed, or are missing - */ - public CoordinateSequence edit(CoordinateSequence coordSeq, Geometry geometry, GeometryFactory targetFactory) { - if (geometryLinesMap.containsKey(geometry)) { - Coordinate[] pts = (Coordinate[]) geometryLinesMap.get(geometry); - // Assert: pts should always have length > 0 - boolean isValidPts = isValidSize(pts, geometry); - if (! isValidPts) return null; - return targetFactory.getCoordinateSequenceFactory().create(pts); - } - //TODO: should this return null if no matching snapped line is found - // probably should never reach here? - return coordSeq; - } - - /** - * Tests if a coordinate array has a size which is - * valid for the containing geometry. - * - * @param pts the point list to validate - * @param geom the atomic geometry containing the point list - * @return true if the coordinate array is a valid size - */ - private static boolean isValidSize(Coordinate[] pts, Geometry geom) { - if (pts.length == 0) return true; - int minSize = minimumNonEmptyCoordinatesSize(geom); - if (pts.length < minSize) { - return false; - } - return true; - } - - private static int minimumNonEmptyCoordinatesSize(Geometry geom) { - if (geom instanceof LinearRing) - return 4; - if (geom instanceof LineString) - return 2; - return 0; - } -} diff --git a/modules/lab/src/main/java/org/locationtech/jtslab/snapround/GeometrySnapRounder.java b/modules/lab/src/main/java/org/locationtech/jtslab/snapround/GeometrySnapRounder.java deleted file mode 100644 index 734a1794ae..0000000000 --- a/modules/lab/src/main/java/org/locationtech/jtslab/snapround/GeometrySnapRounder.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2016 Vivid Solutions. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * and Eclipse Distribution License v. 1.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * - * http://www.eclipse.org/org/documents/edl-v10.php. - */ -package org.locationtech.jtslab.snapround; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.CoordinateList; -import org.locationtech.jts.geom.CoordinateSequence; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.GeometryComponentFilter; -import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.LineString; -import org.locationtech.jts.geom.PrecisionModel; -import org.locationtech.jts.noding.NodedSegmentString; -import org.locationtech.jts.noding.Noder; -import org.locationtech.jts.noding.snapround.MCIndexSnapRounder; -import org.locationtech.jtslab.geom.util.GeometryEditorEx; - -/** - * Nodes a {@link Geometry} using Snap-Rounding - * to a given {@link PrecisionModel}. - *
      - *
    • Point geometries are not handled. They are skipped if present in the input. - *
    • Linestrings which collapse to a point due to snapping are removed. - *
    • Polygonal output may not be valid. - * Invalid output is due to the introduction of topology collapses. - * This should be straightforward to clean using standard heuristics (e.g. buffer(0) ). - *
    - * The input geometry coordinates are expected to be rounded - * to the given precision model. - * This class does not perform that function. - * GeometryPrecisionReducer may be used to do this. - */ -public class GeometrySnapRounder -{ - private PrecisionModel pm; - private boolean isLineworkOnly = false; - - /** - * Creates a new snap-rounder which snap-rounds to a grid specified - * by the given {@link PrecisionModel}. - * - * @param pm the precision model for the grid to snap-round to - */ - public GeometrySnapRounder(PrecisionModel pm) { - this.pm = pm; - } - - public void setLineworkOnly(boolean isLineworkOnly) { - this.isLineworkOnly = isLineworkOnly; - } - - /** - * Snap-rounds the given geometry. - * - * - * @param geom - * @return - */ - public Geometry execute(Geometry geom) { - - // TODO: reduce precision of input automatically - // TODO: add switch to GeometryPrecisionReducer to NOT check & clean invalid polygonal geometry (not needed here) - // TODO: OR just do precision reduction with custom code here - - List segStrings = extractTaggedSegmentStrings(geom, pm); - snapRound(segStrings); - - if (isLineworkOnly) { - return toNodedLines(segStrings, geom.getFactory()); - } - - Geometry geomSnapped = replaceLines(geom, segStrings); - Geometry geomClean = ensureValid(geomSnapped); - return geomClean; - } - - private Geometry toNodedLines(Collection segStrings, GeometryFactory geomFact) { - List lines = new ArrayList(); - for (Iterator it = segStrings.iterator(); it.hasNext(); ) { - NodedSegmentString nss = (NodedSegmentString) it.next(); - // skip collapsed lines - if (nss.size() < 2) - continue; - //Coordinate[] pts = getCoords(nss); - Coordinate[] pts = nss.getNodeList().getSplitCoordinates(); - - lines.add(geomFact.createLineString(pts)); - } - return geomFact.buildGeometry(lines); - } - - private Geometry replaceLines(Geometry geom, List segStrings) { - Map nodedLinesMap = nodedLinesMap(segStrings); - GeometryCoordinateReplacer lineReplacer = new GeometryCoordinateReplacer(nodedLinesMap); - GeometryEditorEx geomEditor = new GeometryEditorEx(lineReplacer); - Geometry snapped = geomEditor.edit(geom); - return snapped; - } - - private void snapRound(List segStrings) { - //Noder sr = new SimpleSnapRounder(pm); - Noder sr = new MCIndexSnapRounder(pm); - sr.computeNodes(segStrings); - } - - private HashMap nodedLinesMap(Collection segStrings) { - HashMap ptsMap = new HashMap(); - for (Iterator it = segStrings.iterator(); it.hasNext(); ) { - NodedSegmentString nss = (NodedSegmentString) it.next(); - // skip collapsed lines - if (nss.size() < 2) - continue; - Coordinate[] pts = nss.getNodeList().getSplitCoordinates(); - ptsMap.put(nss.getData(), pts); - } - return ptsMap; - } - - static List extractTaggedSegmentStrings(Geometry geom, final PrecisionModel pm) - { - final List segStrings = new ArrayList(); - GeometryComponentFilter filter = new GeometryComponentFilter() { - public void filter(Geometry geom) { - // Extract linework for lineal components only - if (! (geom instanceof LineString) ) return; - // skip empty lines - if (geom.getNumPoints() <= 0) return; - Coordinate[] roundPts = round( ((LineString)geom).getCoordinateSequence(), pm); - segStrings.add(new NodedSegmentString(roundPts, geom)); - } - }; - geom.apply(filter); - return segStrings; - } - - static Coordinate[] round(CoordinateSequence seq, PrecisionModel pm) { - if (seq.size() == 0) return new Coordinate[0]; - - CoordinateList coordList = new CoordinateList(); - // copy coordinates and reduce - for (int i = 0; i < seq.size(); i++) { - Coordinate coord = new Coordinate( - seq.getOrdinate(i, Coordinate.X), - seq.getOrdinate(i, Coordinate.Y) ); - pm.makePrecise(coord); - coordList.add(coord, false); - } - Coordinate[] coord = coordList.toCoordinateArray(); - - //TODO: what if seq is too short? - return coord; - } - - private static Geometry ensureValid(Geometry geom) { - if (geom.isValid()) return geom; - return cleanPolygonal(geom); - } - - private static Geometry cleanPolygonal(Geometry geom) { - return PolygonCleaner.clean(geom); - } -} - - diff --git a/modules/lab/src/main/java/org/locationtech/jtslab/snapround/PolygonCleaner.java b/modules/lab/src/main/java/org/locationtech/jtslab/snapround/PolygonCleaner.java deleted file mode 100644 index e7429649fc..0000000000 --- a/modules/lab/src/main/java/org/locationtech/jtslab/snapround/PolygonCleaner.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2016 Vivid Solutions. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * and Eclipse Distribution License v. 1.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * - * http://www.eclipse.org/org/documents/edl-v10.php. - */ -package org.locationtech.jtslab.snapround; - -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.MultiPolygon; -import org.locationtech.jts.geom.Polygon; -import org.locationtech.jts.geom.util.GeometryTransformer; - -public class PolygonCleaner { - - public static Geometry clean(Geometry geom) { - PolygonCleanerTransformer trans = new PolygonCleanerTransformer(); - return trans.transform(geom); - } - - static class PolygonCleanerTransformer extends GeometryTransformer { - - PolygonCleanerTransformer() { - } - - protected Geometry transformPolygon(Polygon geom, Geometry parent) { - // if parent is a MultiPolygon, let it do the cleaning - if (parent instanceof MultiPolygon) { - return geom; - } - return createValidArea(geom); - } - - protected Geometry transformMultiPolygon(MultiPolygon geom, Geometry parent) { - Geometry roughGeom = super.transformMultiPolygon(geom, parent); - return createValidArea(roughGeom); - } - - /** - * Creates a valid area geometry from one that possibly has bad topology (i.e. - * self-intersections). - * - * @param area - * an area geometry possibly containing self-intersections - * @return a valid area geometry - */ - private Geometry createValidArea(Geometry area) { - if (area.isValid()) return area; - // TODO: this is slow and has potential errors (due to buffer robustness failure) - // TODO: replace with a proper polygon cleaner - /** - * Creates a valid area geometry from one that possibly has bad topology (i.e. - * self-intersections). Since buffer can handle invalid topology, but always - * returns valid geometry, constructing a 0-width buffer "corrects" the - * topology. Note this only works for area geometries, since buffer always - * returns areas. This also may return empty geometries, if the input has no - * actual area. - */ - return area.buffer(0.0); - } - } - -} diff --git a/modules/tests/src/test/resources/testxml/robust/overlay/TestOverlay-geos-1051.xml b/modules/tests/src/test/resources/testxml/robust/overlay/TestOverlay-geos-1051.xml new file mode 100644 index 0000000000..d0f4b3c14c --- /dev/null +++ b/modules/tests/src/test/resources/testxml/robust/overlay/TestOverlay-geos-1051.xml @@ -0,0 +1,16 @@ + + + https://trac.osgeo.org/geos/ticket/1051 + + POLYGON((655122.499 1794870,655122.4978576865 1794869.4764028857,655122.4944307676 1794868.9528157401,655122.4887193089 1794868.4292485313,655122.4807234188 1794867.9057112276,655122.4704432496 1794867.3822137958,655122.457878997 1794866.858766203,655122.4430309003 1794866.3353784147,655122.4258992423 1794865.8120603957,655122.4064843488 1794865.2888221089,655122.3847865898 1794864.765673516,655122.3608063782 1794864.2426245774,655122.3345441705 1794863.719685251,655122.3060004668 1794863.1968654925,655122.2751758106 1794862.674175256,655122.2420707886 1794862.1516244924,655122.2066860311 1794861.6292231507,655122.1690222119 1794861.1069811767,655122.129080048 1794860.5849085127,655122.0868602997 1794860.0630150985,655122.042363771 1794859.5413108703,655121.9955913089 1794859.0198057604,655121.946543804 1794858.4985096976,655121.8952221901 1794857.9774326065,655121.8416274441 1794857.456584408,655121.7857605866 1794856.9359750177,655121.7276226812 1794856.415614348,655121.6672148346 1794855.895512305,655121.6045381969 1794855.3756787914,655121.5395939616 1794854.8561237038,655121.4723833648 1794854.3368569335,655121.4029076864 1794853.8178883672,655121.3311682489 1794853.2992278847,655121.2571664184 1794852.7808853611,655121.1809036034 1794852.2628706645,655121.1023812561 1794851.7451936572,655121.0216008713 1794851.2278641951,655120.9385639871 1794850.7108921276,655120.8532721844 1794850.1942872966,655120.7657270869 1794849.6780595381,655120.6759303614 1794849.16221868,655120.5838837175 1794848.6467745432,655120.4895889076 1794848.131736941,655120.3930477271 1794847.617115679,655120.2942620136 1794847.1029205548,655120.1932336483 1794846.589161358,655120.0899645544 1794846.07584787,655119.9844566981 1794845.562989863,655119.876712088 1794845.050597102,655119.7667327755 1794844.5386793413,655119.6545208544 1794844.0272463274,655119.5400784609 1794843.5163077975,655119.4234077742 1794843.0058734787,655119.3045110152 1794842.4959530893,655119.1833904476 1794841.9865563372,655119.0600483775 1794841.477692921,655118.9344871531 1794840.9693725281,655118.8067091648 1794840.4616048366,655118.6767168454 1794839.9543995135,655118.5445126697 1794839.4477662153,655118.4100991547 1794838.9417145876,655118.2734788595 1794838.436254265,655118.134654385 1794837.9313948706,655117.9936283744 1794837.4271460162,655117.8504035126 1794836.923517302,655117.7049825263 1794836.4205183163,655117.5573681841 1794835.9181586355,655117.4075632965 1794835.4164478239,655117.2555707155 1794834.9153954333,655117.1013933348 1794834.415011003,655116.9450340897 1794833.9153040594,655116.7864959572 1794833.4162841167,655116.6257819553 1794832.917960675,655116.4628951441 1794832.4203432219,655116.2978386247 1794831.9234412315,655116.1306155394 1794831.4272641637,655115.9612290718 1794830.931821465,655115.7896824471 1794830.4371225685,655115.615978931 1794829.943176892,655115.4401218307 1794829.4499938395,655115.2621144942 1794828.9575828009,655115.0819603107 1794828.4659531508,655114.8996627098 1794827.975114249,655114.7152251622 1794827.4850754403,655114.5286511796 1794826.9958460545,655114.3399443139 1794826.5074354059,655114.1491081578 1794826.019852793,655113.9561463445 1794825.533107499,655113.7610625479 1794825.0472087902,655113.5638604821 1794824.5621659176,655113.3645439013 1794824.0779881163,655113.1631166004 1794823.5946846036,655112.9595824142 1794823.1122645813,655112.7539452178 1794822.630737234,655112.5462089261 1794822.150111729,655112.3363774943 1794821.670397217,655112.124454917 1794821.191602831,655111.9104452292 1794820.7137376864,655111.6943525051 1794820.2368108812,655111.4761808589 1794819.7608314955,655111.2559344444 1794819.2858085912,655111.0336174545 1794818.8117512118,655110.8092341219 1794818.338668383,655110.5827887186 1794817.8665691114,655110.3542855559 1794817.3954623854,655110.1237289839 1794816.9253571737,655109.8911233922 1794816.456262427,655109.6564732093 1794815.9881870756,655109.4197829026 1794815.5211400313,655109.1810569783 1794815.055130186,655108.9402999814 1794814.5901664118,655108.6975164955 1794814.126257561,655108.4527111431 1794813.6634124657,655108.2058885846 1794813.2016399377,655107.9570535194 1794812.7409487688,655107.7062106848 1794812.2813477297,655107.4533648567 1794811.8228455705,655107.1985208486 1794811.3654510204,655106.9416835128 1794810.9091727876,655106.6828577387 1794810.454019559,655106.422048454 1794810,655106.1592606244 1794809.5471227546,655105.8944992529 1794809.0953964447,655105.6277693801 1794808.6448296707,655105.3590760842 1794808.1954310108,655105.0884244808 1794807.7472090207,655104.8158197225 1794807.3001722342,655104.5412669994 1794806.8543291618,655104.2647715387 1794806.409688292,655103.9863386045 1794805.9662580902,655103.7059734975 1794805.5240469985,655103.4236815557 1794805.0830634357,655103.6628449125 1794805.4566734952,655102.92659 1794805.4068,655102.3491274725 1794805.3702204635,655102.09922 1794805.35439,655101.27163 1794805.30558,655100.44383 1794805.26039,655099.61584 1794805.21881,655098.78768 1794805.18084,655097.95936 1794805.14648,655097.1309 1794805.11574,655096.30232 1794805.08862,655095.47362 1794805.06511,655094.64483 1794805.04521,655093.81596 1794805.02894,655092.98702 1794805.01628,655092.15804 1794805.00723,655091.32903 1794805.00181,655090.5 1794805,655089.67097 1794805.00181,655088.84196 1794805.00723,655088.01298 1794805.01628,655087.18404 1794805.02894,655086.35517 1794805.04521,655085.52638 1794805.06511,655084.69768 1794805.08862,655083.8691 1794805.11574,655083.04064 1794805.14648,655082.21232 1794805.18084,655081.38416 1794805.21881,655080.55617 1794805.26039,655079.72837 1794805.30558,655078.90078 1794805.35439,655078.07341 1794805.4068,655077.24627 1794805.46283,655076.41939 1794805.52246,655075.59277 1794805.58571,655074.76644 1794805.65255,655073.94041 1794805.72301,655073.11469 1794805.79706,655072.28931 1794805.87472,655071.46427 1794805.95598,655070.63959 1794806.04084,655070.41921 1794806.06449,655070.419209293 1794806.064489258,655070.419208566 1794806.064489336,655070.4192085657 1794806.0644893358,655070.4192092925 1794806.0644892578,655070.01913 1794805.64474,655069.61087 1794805.22012,655069.20076 1794804.79729,655068.78881 1794804.37625,655068.37503 1794803.95701,655067.95942 1794803.53958,655067.54199 1794803.12397,655067.12275 1794802.71019,655066.70171 1794802.29824,655066.27888 1794801.88813,655065.85426 1794801.47987,655065.42787 1794801.07346,655064.9997 1794800.66892,655064.56978 1794800.26625,655064.1381 1794799.86546,655063.70468 1794799.46656,655063.26952 1794799.06955,655062.83263 1794798.67445,655062.39402 1794798.28125,655061.95371 1794797.88998,655061.51169 1794797.50063,655061.06797 1794797.11321,655060.98499 1794797.04139,655060.92135 1794796.80594,655060.77096 1794796.25901,655060.61818 1794795.71274,655060.46302 1794795.16714,655060.30548 1794794.62223,655060.14557 1794794.07801,655059.98328 1794793.53449,655059.81862 1794792.99168,655059.65159 1794792.4496,655059.4822 1794791.90825,655059.31045 1794791.36764,655059.13635 1794790.82779,655058.95989 1794790.28871,655058.78108 1794789.75039,655058.59992 1794789.21287,655058.41641 1794788.67614,655058.23057 1794788.14022,655058.04239 1794787.60511,655057.85188 1794787.07083,655057.65904 1794786.53738,655057.46387 1794786.00478,655057.26638 1794785.47304,655057.06658 1794784.94217,655056.86446 1794784.41217,655056.66002 1794783.88306,655056.45328 1794783.35484,655056.24424 1794782.82753,655056.0329 1794782.30114,655055.81927 1794781.77568,655055.60334 1794781.25115,655055.38513 1794780.72757,655055.16463 1794780.20495,655054.94186 1794779.6833,655054.71681 1794779.16262,655054.48949 1794778.64293,655054.25991 1794778.12424,655054.02807 1794777.60655,655053.79397 1794777.08988,655053.55761 1794776.57423,655053.31901 1794776.05963,655053.07817 1794775.54606,655052.83509 1794775.03356,655052.58977 1794774.52212,655052.34223 1794774.01175,655052.09246 1794773.50247,655051.84047 1794772.99428,655051.58626 1794772.4872,655051.32985 1794771.98124,655051.07123 1794771.47639,655050.81041 1794770.97268,655050.54739 1794770.47011,655050.28219 1794769.9687,655050.0148 1794769.46844,655049.74522 1794768.96936,655049.47348 1794768.47146,655049.19956 1794767.97475,655048.92348 1794767.47924,655048.64524 1794766.98494,655048.36485 1794766.49185,655048.0823 1794766,655047.79762 1794765.50938,655047.51079 1794765.02001,655047.22183 1794764.5319,655046.93075 1794764.04505,655046.63754 1794763.55948,655046.34222 1794763.07519,655046.04479 1794762.59219,655045.74525 1794762.1105,655045.44362 1794761.63011,655045.13989 1794761.15105,655044.83407 1794760.67332,655044.52617 1794760.19693,655044.2162 1794759.72188,655043.90416 1794759.24819,655043.59005 1794758.77587,655043.27388 1794758.30492,655042.95567 1794757.83536,655042.6354 1794757.36719,655042.3131 1794756.90042,655042.2182337849 1794756.7643026446,655042.0618774315 1794756.7093175529,655041.5671785348 1794756.5377709281,655041.0717358363 1794756.3683844607,655040.5755587686 1794756.2011613753,655040.078656778 1794756.0361048558,655039.581039325 1794755.8732180446,655039.0827158833 1794755.7125040428,655038.5836959404 1794755.55396591,655038.083988997 1794755.397606665,655037.5836045666 1794755.2434292845,655037.0825521761 1794755.0914367035,655036.5808413645 1794754.9416318159,655036.0784816836 1794754.7940174737,655035.575482698 1794754.6485964875,655035.0718539838 1794754.5053716255,655034.5676051293 1794754.364345615,655034.0627457349 1794754.2255211405,655033.5572854122 1794754.0889008453,655033.0512337846 1794753.9544873303,655032.5446004865 1794753.8222831546,655032.0373951634 1794753.692290835,655031.5296274719 1794753.564512847,655031.0213070791 1794753.4389516225,655030.5124436626 1794753.3156095522,655030.0030469106 1794753.1944889848,655029.4931265212 1794753.0755922259,655028.9826922026 1794752.958921539,655028.4717536726 1794752.8444791457,655027.9603206587 1794752.7322672245,655027.448402898 1794752.6222879118,655026.9360101367 1794752.514543302,655026.42315213 1794752.4090354454,655025.9098386419 1794752.3057663515,655025.3960794451 1794752.2047379862,655024.881884321 1794752.105952273,655024.367263059 1794752.0094110924,655023.8522254569 1794751.9151162824,655023.3367813199 1794751.8230696386,655022.8209404618 1794751.7332729131,655022.3047127032 1794751.6457278156,655021.9053975226 1794751.5798006123,655022.0512017534 1794752.1287369924,655022.199931451 1794752.6986994527,655022.3461728104 1794753.269305441,655022.4899230473 1794753.840544094,655022.6311794248 1794754.412404536,655022.7699392537 1794754.9848758795,655022.9061998922 1794755.5579472254,655023.039958746 1794756.1316076636,655023.1712132687 1794756.7058462722,655023.2999609612 1794757.2806521184,655023.4261993725 1794757.856014259,655023.549926099 1794758.4319217396,655023.6711387854 1794759.0083635962,655023.7898351238 1794759.5853288537,655023.9060128544 1794760.162806528,655024.0196697655 1794760.7407856241,655024.130803693 1794761.3192551387,655024.2394125211 1794761.8982040586,655024.3454941822 1794762.4776213611,655024.4490466566 1794763.057496015,655024.5500679729 1794763.6378169805,655024.6485562075 1794764.218573209,655024.7445094857 1794764.7997536436,655024.8379259803 1794765.3813472195,655024.9288039132 1794765.9633428643,655025.0171415538 1794766.5457294974,655025.1029372207 1794767.1284960313,655025.1861892801 1794767.7116313705,655025.2668961473 1794768.295124413,655025.3450562855 1794768.8789640502,655025.4206682068 1794769.4631391666,655025.4937304716 1794770.0476386403,655025.5642416889 1794770.6324513431,655025.6322005163 1794771.2175661414,655025.6976056601 1794771.802971895,655025.7604558747 1794772.3886574588,655025.8207499639 1794772.9746116824,655025.8784867795 1794773.5608234098,655025.9336652226 1794774.1472814805,655025.9862842424 1794774.733974729,655026.0139903675 1794775.058818106,655026.3311243049 1794775.1958899582,655026.8708031309 1794775.4319503189,655027.4094468153 1794775.6703632183,655027.9470451028 1794775.9111241172,655028.4835877586 1794776.1542284316,655029.0190645675 1794776.3996715338,655029.2585099832 1794776.5106914316,655029.553465335 1794776.6474487502,655030.0867798867 1794776.8975553638,655030.6189980691 1794777.1499866128,655031.1501097496 1794777.4047376916,655031.6801048166 1794777.6618037499,655032.2089731796 1794777.9211798932,655032.7367047698 1794778.1828611838,655033.2632895401 1794778.4468426395,655033.7887174649 1794778.7131192347,655034.3129785408 1794778.9816858994,655034.8360627868 1794779.252537521,655035.357960244 1794779.5256689428,655035.8786609761 1794779.8010749642,655036.3981550699 1794780.0787503424,655036.916432635 1794780.3586897908,655037.1060295089 1794780.4621687066,655037.4334838041 1794780.6408879797,655037.9492987333 1794780.9253395363,655038.463867602 1794781.2120390455,655038.977180614 1794781.5009810482,655039.4892279962 1794781.792160044,655040 1794782.085570489,655040.5094869011 1794782.3812067974,655041.0176789997 1794782.6790633404,655041.5245666204 1794782.9791344474,655042.0301401129 1794783.2814144052,655042.5343898516 1794783.5858974592,655043.0373062367 1794783.8925778123,655043.538879693 1794784.2014496257,655044.0391006715 1794784.5125070189,655044.5379596486 1794784.82574407,655045.0354471268 1794785.1411548154,655045.5315536347 1794785.45873325,655046.026269727 1794785.7784733274,655046.5195859852 1794786.1003689605,655046.876912722 1794786.3357588951,655047.0114930171 1794786.4244140212,655047.5019814577 1794786.7506023392,655047.9910419686 1794787.078927705,655048.4786652388 1794787.4093838679,655048.9648419849 1794787.741964536,655049.4495629505 1794788.076663378,655049.9328189074 1794788.413474021,655050.414600655 1794788.7523900534,655050.894899021 1794789.093405022,655051.373704861 1794789.4365124349,655051.8510090595 1794789.7817057595,655052.326802529 1794790.128978424,655052.8010762114 1794790.4783238168,655053.2738210771 1794790.8297352868,655053.7450281255 1794791.1832061438,655054.2146883858 1794791.538729658,655054.6827929162 1794791.896299061,655055.1493328046 1794792.2559075446,655055.6142991689 1794792.6175482632,655056.0776831568 1794792.9812143312,655056.5394759461 1794793.346898825,655056.9996687449 1794793.7145947828,655057.4582527918 1794794.0842952034,655057.915219356 1794794.455993049,655058.3705597376 1794794.8296812428,655058.8242652674 1794795.20535267,655059.2763273077 1794795.583000179,655059.7267372519 1794795.9626165796,655060.1754865246 1794796.3441946441,655060.6225665825 1794796.7277271082,655060.984991858 1794797.0413930381,655061.0131137115 1794797.1454367384,655061.0693574175 1794797.3535241366,655061.2149720588 1794797.9017467333,655061.3175272772 1794798.294759195,655061.358193249 1794798.450599473,655061.3996451481 1794798.612336904,655061.4990182618 1794799.0000719063,655061.6374444158 1794799.550153572,655061.7734690757 1794800.1008339976,655061.9070896517 1794800.6521026988,655062.0383035998 1794801.2039491802,655062.167108422 1794801.7563629353,655062.293501666 1794802.3093334471,655062.4174809256 1794802.862850188,655062.4832855016 1794803.1627704843,655062.53904384 1794803.41690262,655062.658188095 1794803.971480194,655062.774911423 1794804.526572352,655062.8011515326 1794804.6541216243,655062.8892116006 1794805.0821685258,655062.9359076326 1794805.3142776894,655063.001086452 1794805.638258138,655063.110533848 1794806.194830601,655063.217551704 1794806.751875319,655063.2572078878 1794806.9632660996,655063.257207888 1794806.9632661003,655063.2572078885 1794806.9632661003,655063.2739160497 1794807.0523304245,655063.30204 1794807.20226,655063.3120682817 1794807.2557042558,655063.322137983 1794807.3093816857,655063.405588232 1794807.7651863697,655063.4160941981 1794807.8225698846,655063.42429 1794807.86734,655063.4597080505 1794808.065669717,655063.5240078915 1794808.4257369032,655063.6212876776 1794808.9845644997,655063.7161281998 1794809.5438112381,655063.8085276528 1794810.1034664714,655063.8346280346 1794810.2659625537,655063.8571828533 1794810.4063846257,655063.8749147949 1794810.5167919982,655063.89848 1794810.6635200002,655063.94224 1794810.94374,655063.9463161206 1794810.9698417028,655063.9859963607 1794811.2239597952,655064.028529299 1794811.5043681741,655064.0341949249 1794811.541720143,655064.07106 1794811.78478,655064.15368 1794812.34596,655064.1887750614 1794812.591778927,655064.2039794393 1794812.6982773147,655064.233848936 1794812.907496875,655064.31156666 1794813.4693790649,655064.3389208699 1794813.673709203,655064.34958 1794813.75334,655064.38683 1794814.0316,655064.4027632441 1794814.1546794237,655064.4259462203 1794814.333790304,655064.4449891156 1794814.480915062,655064.4533165452 1794814.5452654045,655064.45964 1794814.59413,655064.49482 1794814.87556,655064.53 1794815.1569900003,655064.557741384 1794815.387071891,655064.597899404 1794815.720138331,655064.6169202882 1794815.8839051463,655064.63062 1794816.00186,655064.64698 1794816.14272,655064.66334 1794816.2835800003,655064.6948299999 1794816.565445,655064.72632 1794816.84731,655064.738967757 1794816.9651558362,655064.7595979495 1794817.1573782424,655064.7868463978 1794817.4112997751,655064.8449073726 1794817.9755519903,655064.9005057877 1794818.5400521723,655064.9470559272 1794819.0348052606,655064.95364 1794819.10479,655065.00431 1794819.66975,655065.0084078918 1794819.7177907908,655065.0525153247 1794820.2349330233,655065.0788789048 1794820.5608226706,655065.084069913 1794820.6249905813,655065.09825 1794820.80032,655065.1299 1794821.21397,655065.14152 1794821.3659,655065.1645265853 1794821.6848464767,655065.1823265336 1794821.9316584133,655065.220660021 1794822.4975932,655065.2222952689 1794822.523404995,655065.25652 1794823.0636900002,655065.26244 1794823.16404,655065.2732860195 1794823.347925025,655065.289917172 1794823.6299376169,655065.320839518 1794824.1963256889,655065.3227130708 1794824.2336323163,655065.3400964689 1794824.5798237238,655065.34521 1794824.68166,655065.3472500001 1794824.72225,655065.34929 1794824.7628400002,655065.3511734324 1794824.8039188355,655065.3752688055 1794825.3294796424,655065.3886182333 1794825.6513439538,655065.3916451923 1794825.7243261184,655065.39877 1794825.89622,655065.4127229034 1794826.2721327043,655065.419807512 1794826.463065429,655065.430688186 1794826.7954356226,655065.4383668087 1794827.0299932826,655065.4477601866 1794827.3611050346,655065.45445 1794827.597,655065.45979 1794827.81955,655065.46806 1794828.16406,655065.4792 1794828.73119,655065.48091 1794828.84321,655065.48786 1794829.29835,655065.4914 1794829.62298,655065.4940457667 1794829.8651625,655065.494049998 1794829.8655503849,655065.4940520284 1794829.865860597,655065.49589 1794830.1473,655065.49776 1794830.43277,655065.499 1794831,655065.49776 1794831.56723,655065.4966969339 1794831.7300334375,655065.4958981465 1794831.8520774096,655065.494049998 1794832.1344496151,655065.4940440354 1794832.134996209,655065.49139 1794832.37804,655065.48786 1794832.70165,655065.48091 1794833.15679,655065.4792 1794833.26881,655065.46806 1794833.83594,655065.46 1794834.1717399997,655065.45445 1794834.403,655065.4477601866 1794834.6388949654,655065.4383668087 1794834.9700067174,655065.430688186 1794835.2045643774,655065.419807512 1794835.536934571,655065.4127229034 1794835.7278672957,655065.39877 1794836.10378,655065.3921310415 1794836.2639597072,655065.389257846 1794836.3332345209,655065.3752688055 1794836.6705203576,655065.3511734324 1794837.1960811645,655065.34929 1794837.2371599998,655065.3492026834 1794837.2389002154,655065.3435750188 1794837.350959613,655065.320839518 1794837.803674312,655065.289917172 1794838.3700623831,655065.27888309 1794838.5571660763,655065.266 1794838.77557,655065.25652 1794838.9363099998,655065.2222952689 1794839.476595005,655065.220660021 1794839.5024068,655065.1823265336 1794840.0683415867,655065.1466674866 1794840.5627856317,655065.1446355775 1794840.5909598393,655065.14152 1794840.6341,655065.1252145083 1794840.8472812737,655065.1239996754 1794840.8631600093,655065.1218594966 1794840.8911336774,655065.09825 1794841.19968,655065.0916374373 1794841.281464542,655065.0824054048 1794841.3955850021,655065.0525153247 1794841.7650669767,655065.0084078918 1794842.2822092092,655065.00431 1794842.33025,655064.95364 1794842.89521,655064.9470559272 1794842.9651947394,655064.9005057877 1794843.4599478277,655064.8449073726 1794844.0244480097,655064.7868463978 1794844.5887002249,655064.7595979495 1794844.8426217576,655064.738967757 1794845.0348441638,655064.72632 1794845.15269,655064.6948299999 1794845.434555,655064.66334 1794845.7164199997,655064.61772 1794846.1092,655064.6032093227 1794846.2341441067,655064.597899404 1794846.27986167,655064.5577489454 1794846.6128653958,655064.5467877984 1794846.7037751155,655064.53 1794846.8430099997,655064.49482 1794847.12444,655064.45964 1794847.40587,655064.43155 1794847.62288,655064.38683 1794847.9684,655064.3436305844 1794848.2911102301,655064.3370893075 1794848.3399721803,655064.31156666 1794848.530620936,655064.233848936 1794849.092503125,655064.2051059277 1794849.2938322483,655064.1587376938 1794849.6186164753,655064.15368 1794849.65404,655064.1509119875 1794849.6728445122,655064.1348874248 1794849.781691255,655064.1051763323 1794849.9835036672,655064.07106 1794850.21522,655064.02092 1794850.54577,655064.0020664434 1794850.670094412,655063.9859963607 1794850.776040205,655063.9444051266 1794851.042396584,655063.9189527387 1794851.205397417,655063.89848 1794851.3364799998,655063.8847899882 1794851.421724853,655063.870824511 1794851.508684971,655063.8483867969 1794851.6483779724,655063.8085276528 1794851.8965335286,655063.7161281998 1794852.4561887619,655063.6212876776 1794853.0154355003,655063.5240078915 1794853.5742630968,655063.4597080505 1794853.934330283,655063.42429 1794854.13266,655063.4160941981 1794854.1774301154,655063.405588232 1794854.2348136303,655063.322137983 1794854.6906183143,655063.2724953296 1794854.9552428483,655063.21755 1794855.24812,655063.11053 1794855.80517,655063.0514708135 1794856.1055224438,655063.001086452 1794856.361741862,655062.941128199 1794856.6597727544,655062.9205390046 1794856.7621142273,655062.88921 1794856.91783,655062.8492470314 1794857.1120933052,655062.7905877141 1794857.3972275276,655062.7788537731 1794857.45426447,655062.77491 1794857.47343,655062.7372089647 1794857.6527263306,655062.658188095 1794858.028519806,655062.6137885435 1794858.2351852097,655062.53904 1794858.5831,655062.50021 1794858.76009,655062.41748 1794859.13715,655062.2935 1794859.69067,655062.2580079195 1794859.8459517134,655062.167108422 1794860.2436370647,655062.1189695523 1794860.4500934172,655062.0383 1794860.79605,655061.9342361456 1794861.233727296,655061.9070896517 1794861.3478973012,655061.7734690757 1794861.8991660024,655061.7257108495 1794862.0925097703,655061.63744 1794862.44985,655061.5525515288 1794862.7871961223,655061.4990182618 1794862.9999280937,655061.4681279579 1794863.1204561882,655061.39828 1794863.39297,655061.35819 1794863.5493999997,655061.3441506746 1794863.603209357,655061.3348493205 1794863.6388592103,655061.3175272794 1794863.7052407963,655061.2700163276 1794863.8873124295,655061.21497 1794864.09825,655061.1504809583 1794864.34105494,655061.0693574175 1794864.6464758634,655060.9645359598 1794865.0342885798,655060.92135 1794865.19406,655060.8908934855 1794865.3048259306,655060.8122170035 1794865.5909466685,655060.770958917 1794865.74098889,655060.7237004219 1794865.909965109,655060.69457 1794866.01412,655060.61818 1794866.28726,655060.6088899767 1794866.319928052,655060.5043065414 1794866.6876799944,655060.463020472 1794866.832856256,655060.4194623097 1794866.9835202834,655060.30548 1794867.37777,655060.2509094377 1794867.563489468,655060.2380104777 1794867.6073870822,655060.1455655328 1794867.9219948116,655059.9832769046 1794868.4655148576,655059.8451188998 1794868.9209607618,655059.818618275 1794869.008321614,655059.7965865926 1794869.0798256882,655059.79078 1794869.09867,655059.65159 1794869.5504,655059.51872 1794869.97505,655059.4822 1794870.09175,655059.44314 1794870.21471,655059.31045 1794870.63236,655059.2343639069 1794870.868290115,655059.1756656991 1794871.0502947478,655059.136347118 1794871.172209269,655058.959886406 1794871.711294843,655058.9322069172 1794871.7946238331,655058.89277 1794871.91334,655058.8584398233 1794872.0166993744,655058.7810751768 1794872.2496053327,655058.6343528961 1794872.6849533038,655058.62944 1794872.69953,655058.6274733942 1794872.705365863,655058.599916834 1794872.78713049,655058.5879829929 1794872.8220360794,655058.5709 1794872.87199,655058.41641 1794873.32386,655058.3157922134 1794873.6140313165,655058.3066788729 1794873.6403119822,655058.2991381043 1794873.6620577315,655058.23057 1794873.85978,655058.04239 1794874.39489,655057.9750763945 1794874.5836803482,655057.8996753128 1794874.795139172,655057.8704823833 1794874.877009384,655057.85188 1794874.92917,655057.65904 1794875.4626199997,655057.6448570719 1794875.5013248837,655057.4899396225 1794875.924085044,655057.4783120893 1794875.9558158643,655057.4761566692 1794875.961696763,655057.46387 1794875.9952199997,655057.44367 1794876.0496099999,655057.26638 1794876.52696,655057.1540716016 1794876.8253680484,655057.1032683544 1794876.9603490382,655057.0665772591 1794877.0578349398,655056.8644554446 1794877.5878334409,655056.7835940045 1794877.7971188226,655056.66002 1794878.11694,655056.45328 1794878.64516,655056.40102 1794878.776985,655056.34876 1794878.9088099997,655056.3454793321 1794878.9170958477,655056.3415462273 1794878.9270170864,655056.2553396706 1794879.1444727322,655056.24424 1794879.17247,655056.2233046287 1794879.2246156458,655056.142159128 1794879.4267261995,655056.0329010936 1794879.698857144,655055.9047197493 1794880.0141350147,655055.819265522 1794880.224320256,655055.7693209632 1794880.3456450682,655055.6033392265 1794880.7488462075,655055.413587309 1794881.2041358722,655055.385126317 1794881.272425013,655055.344363057 1794881.3690427516,655055.32776 1794881.40839,655055.1903910369 1794881.7339897258,655055.1646309488 1794881.7950467037,655055.1414043332 1794881.84943497,655055.05602 1794882.04937,655055.0053354213 1794882.1680587563,655054.941857319 1794882.31670133,655054.8736244871 1794882.4745670506,655054.82115 1794882.59597,655054.7507062613 1794882.7589546938,655054.71680967 1794882.8373789603,655054.6559524507 1794882.976510124,655054.48949 1794883.35707,655054.25991 1794883.87576,655054.2092991017 1794883.9887722323,655054.0280656649 1794884.3934508397,655053.7939652139 1794884.9101215454,655053.6971788473 1794885.1212778352,655053.65541 1794885.2124,655053.55761 1794885.42577,655053.5128036897 1794885.522408803,655053.4058979728 1794885.752980839,655053.3814401175 1794885.8057310344,655053.31901 1794885.94037,655053.19155 1794886.21216,655053.1268918801 1794886.3500418153,655053.078168909 1794886.453936187,655052.835086965 1794886.966442585,655052.7777646838 1794887.0859495695,655052.71074 1794887.2256799997,655052.650255 1794887.35178,655052.58977 1794887.47788,655052.4022 1794887.86459,655052.3704532285 1794887.9300528273,655052.342226019 1794887.988249082,655052.092456399 1794888.497529729,655051.840467008 1794889.005715704,655051.7592920293 1794889.16764188,655051.74509 1794889.19597,655051.58626 1794889.5127999997,655051.5150349999 1794889.653345,655051.44381 1794889.79389,655051.32985 1794890.01876,655051.2668584046 1794890.1417253814,655051.071228393 1794890.523608965,655050.9453007116 1794890.766807754,655050.8104083131 1794891.0273197205,655050.7409470155 1794891.1600458433,655050.593988924 1794891.440852254,655050.54739 1794891.5298899997,655050.3852 1794891.83654,655050.3792012009 1794891.8478818708,655050.2821870716 1794892.0313031622,655050.0147959667 1794892.5315567341,655049.8555438404 1794892.8263947952,655049.74522 1794893.03064,655049.5570343253 1794893.3754462393,655049.4734782419 1794893.5285399596,655049.3532714884 1794893.7465188617,655049.19956 1794894.02525,655049.06152 1794894.2730049998,655048.92348 1794894.5207599998,655048.64524 1794895.01506,655048.5789162378 1794895.1316960563,655048.3648458836 1794895.5081454779,655048.1587779059 1794895.866870791,655048.0823 1794896,655047.9993031728 1794896.1430374573,655047.7976156765 1794896.4906170159,655047.6870151944 1794896.6793195724,655047.61608 1794896.80034,655047.528597712 1794896.9496057706,655047.510790857 1794896.979987185,655047.4641063708 1794897.058847787,655047.22183 1794897.4681,655046.9895871028 1794897.8565411656,655046.930749091 1794897.954949738,655046.8818887186 1794898.035866655,655046.8310954794 1794898.119982726,655046.63754 1794898.4405199997,655046.48988 1794898.682665,655046.34222 1794898.92481,655046.1170049899 1794899.2905392468,655046.095029939 1794899.326224785,655046.044789249 1794899.407810075,655046.0090860169 1794899.4652255424,655045.74525 1794899.8895,655045.5913157366 1794900.1346628843,655045.443616821 1794900.369887069,655045.2966752034 1794900.6016534755,655045.1398879556 1794900.848949085,655045.0809076773 1794900.9410853786,655044.92172 1794901.18975,655044.83407 1794901.32668,655044.6399 1794901.62711,655044.52617 1794901.80307,655044.2162 1794902.27812,655044.2056703401 1794902.2941044718,655043.9041568687 1794902.7518080906,655043.6765650096 1794903.0940373177,655043.65062 1794903.13305,655043.6076964887 1794903.1975947153,655043.590049599 1794903.224130293,655043.5679616701 1794903.2570316002,655043.27388 1794903.69508,655043.1829827967 1794903.8292124975,655043.123980343 1794903.9162769034,655042.9556673865 1794904.1646406003,655042.8586380847 1794904.3064810461,655042.6354 1794904.63281,655042.519005 1794904.80138,655042.40261 1794904.9699499998,655042.3131 1794905.09958,655042.3098885749 1794905.1041896634,655042.2967659654 1794905.1230179854,655041.9887657575 1794905.5649367257,655041.8111303787 1794905.817452025,655041.6624 1794906.02887,655041.33402 1794906.49138,655041.307212661 1794906.5287901931,655041.003617655 1794906.9524565332,655040.906696145 1794907.0864714184,655040.85829 1794907.1534,655040.67121 1794907.41208,655040.3368 1794907.87025,655040.00039 1794908.32696,655039.88759 1794908.47871,655039.8459033759 1794908.5347917126,655039.66199565 1794908.7821980738,655039.4350760973 1794909.0847013514,655039.3216163061 1794909.2359530097,655038.9792603295 1794909.6882184455,655038.8209232871 1794909.8955020842,655038.63493 1794910.13899,655038.28864 1794910.58825,655038.1263528324 1794910.7969071085,655037.9694185598 1794910.998679626,655037.940397969 1794911.035991792,655037.889712484 1794911.1005753323,655037.8629422454 1794911.1346856002,655037.5902 1794911.4822099998,655037.43655 1794911.6762399997,655037.23806 1794911.9269,655036.88398 1794912.37005,655036.52797 1794912.81165,655036.23449 1794913.17246,655036.17004 1794913.25169,655036.08856 1794913.35098,655036.053529037 1794913.3936662779,655035.81019584 1794913.690168636,655035.6694586358 1794913.8601396354,655035.4484381696 1794914.1270702574,655035.0847776054 1794914.5623892592,655034.7192210717 1794914.9961173537,655034.3517755279 1794915.428246283,655033.9824479698 1794915.85876782,655033.6112454289 1794916.2876737688,655033.5403817961 1794916.3688347612,655033.26973 1794916.67881,655033.23817 1794916.71496,655033.2328887079 1794916.7209573293,655033.2254105242 1794916.7294471285,655032.925366566 1794917.0700796994,655032.86324 1794917.14061,655032.8320680889 1794917.1756893618,655032.486458758 1794917.56461658,655032.107827312 1794917.9869788266,655031.8115175562 1794918.3146238225,655031.72736 1794918.40768,655031.48473 1794918.67362,655031.34505 1794918.82673,655031.3060046234 1794918.8691554633,655031.1961467743 1794918.9885204027,655030.9609262265 1794919.2440969192,655030.8051797288 1794919.4118468359,655030.76346 1794919.45678,655030.6685851378 1794919.5589688038,655030.57498121 1794919.659786808,655030.4556279816 1794919.7872189537,655030.1872260846 1794920.0737887425,655029.9358277182 1794920.3398675255,655029.79767 1794920.48609,655029.6905854121 1794920.5984446765,655029.4063150676 1794920.8966972516,655029.1039639757 1794921.211161032,655029.01317 1794921.3055899998,655028.61825 1794921.71276,655028.22156 1794922.1182,655027.8231 1794922.52191,655027.6984821834 1794922.6470757592,655027.6320630058 1794922.7137853792,655027.6239778678 1794922.7219058727,655027.600923338 1794922.7450566483,655027.6274726089 1794922.683631383,655027.5236044631 1794922.9239441357,655027.4186887472 1794923.1638013916,655027.3127274585 1794923.4031985847,655027.2057226147 1794923.642131157,655027.0976762526 1794923.8805945595,655026.9885904295 1794924.1185842524,655026.8784672222 1794924.3560957045,655026.7673087273 1794924.593124394,655026.655117061 1794924.8296658085,655026.5418943594 1794925.0657154443,655026.4276427779 1794925.3012688074,655026.312364492 1794925.5363214132,655026.1960616962 1794925.7708687866,655026.0787366047 1794926.0049064623,655025.9603914513 1794926.2384299845,655025.8410284892 1794926.471434907,655025.7206499907 1794926.7039167942,655025.5992582478 1794926.9358712195,655025.4768555715 1794927.1672937672,655025.3534442923 1794927.3981800312,655025.2290267597 1794927.6285256157,655025.1036053424 1794927.8583261352,655024.9771824284 1794928.087577215,655024.8497604243 1794928.31627449,655024.7213417564 1794928.5444136062,655024.5919288694 1794928.7719902205,655024.461524227 1794928.999,655024.3301303122 1794929.2254386228,655024.1977496265 1794929.4513017777,655024.0643846901 1794929.6765851646,655023.9300380421 1794929.9012844947,655023.7947122403 1794930.1253954896,655023.6584098613 1794930.348913883,655023.5211334997 1794930.5718354192,655023.3828857694 1794930.794155854,655023.2436693022 1794931.015870955,655023.1034867488 1794931.2369765008,655022.9623407779 1794931.4574682822,655022.8202340767 1794931.677342101,655022.6771693509 1794931.8965937712,655022.533149324 1794932.1152191188,655022.3881767382 1794932.3332139812,655022.2422543532 1794932.5505742084,655022.0953849476 1794932.7672956618,655021.9475713173 1794932.9833742157,655021.7988162765 1794933.198805756,655021.6491226574 1794933.4135861811,655021.4984933097 1794933.6277114023,655021.3469311014 1794933.8411773427,655021.1944389179 1794934.0539799384,655021.0410196625 1794934.2661151376,655020.8866762561 1794934.477578902,655020.731411637 1794934.6883672052,655020.5752287614 1794934.8984760344,655020.4181306028 1794935.1079013892,655020.260120152 1794935.3166392827,655020.1012004175 1794935.5246857407,655019.9413744246 1794935.7320368022,655019.7806452163 1794935.9386885196,655019.6190158528 1794936.1446369586,655019.4564894112 1794936.3498781982,655019.2930689856 1794936.5544083312,655019.1287576874 1794936.758223463,655018.963558645 1794936.9613197139,655018.7974750033 1794937.1636932166,655018.6305099244 1794937.365340119,655018.4626665871 1794937.5662565813,655018.2939481869 1794937.7664387787,655018.124357936 1794937.9658829,655017.953899063 1794938.1645851478,655017.7825748133 1794938.3625417396,655017.6103884487 1794938.5597489062,655017.4373432474 1794938.756202893,655017.2634425038 1794938.9518999602,655017.0886895286 1794939.1468363816,655016.9130876492 1794939.3410084462,655016.7366402086 1794939.534412457,655016.5593505661 1794939.727044732,655016.3812220972 1794939.918901604,655016.2022581929 1794940.1099794197,655016.0224622608 1794940.3002745416,655015.8418377236 1794940.4897833471,655015.6603880203 1794940.6785022276,655015.4781166054 1794940.8664275906,655015.2950269493 1794941.053555858,655015.1111225374 1794941.2398834673,655014.9264068712 1794941.4254068714,655014.7408834674 1794941.6101225375,655014.554555858 1794941.7940269492,655014.3674275905 1794941.9771166055,655014.1795022276 1794942.1593880204,655013.9907833469 1794942.3408377236,655013.8012745416 1794942.5214622607,655013.6109794196 1794942.701258193,655013.4199016037 1794942.8802220973,655013.228044732 1794943.0583505663,655013.035412457 1794943.2356402087,655012.8420084461 1794943.4120876493,655012.6478363816 1794943.5876895287,655012.4528999601 1794943.7624425038,655012.2572028929 1794943.9363432473,655012.060748906 1794944.1093884488,655011.8635417394 1794944.2815748134,655011.6655851478 1794944.452899063,655011.4668828998 1794944.623357936,655011.2674387785 1794944.792948187,655011.0672565812 1794944.9616665873,655010.8663401188 1794945.1295099244,655010.6646932167 1794945.2964750032,655010.4623197138 1794945.462558645,655010.259223463 1794945.6277576876,655010.0554083311 1794945.7920689858,655009.8508781983 1794945.9554894113,655009.6456369585 1794946.118015853,655009.4396885196 1794946.2796452164,655009.233036802 1794946.4403744247,655009.0256857405 1794946.6002004175,655008.8176392826 1794946.759120152,655008.6089013892 1794946.917130603,655008.3994760342 1794947.0742287615,655008.1893672051 1794947.230411637,655007.9785789018 1794947.3856762561,655007.7671151375 1794947.5400196626,655007.5549799382 1794947.6934389179,655007.3421773426 1794947.8459311014,655007.1287114022 1794947.9974933097,655006.914586181 1794948.1481226573,655006.6998057558 1794948.2978162766,655006.4843742155 1794948.4465713175,655006.2682956617 1794948.5943849478,655006.0515742082 1794948.7412543534,655005.8342139812 1794948.8871767381,655005.6162191187 1794949.0321493242,655005.3975937712 1794949.176169351,655005.1783421009 1794949.319234077,655004.9584682821 1794949.461340778,655004.7379765008 1794949.602486749,655004.5168709549 1794949.7426693023,655004.295155854 1794949.8818857695,655004.0728354191 1794950.0201334998,655003.8499138829 1794950.1574098612,655003.6263954897 1794950.2937122404,655003.4022844946 1794950.4290380422,655003.1775851647 1794950.56338469,655002.9523017777 1794950.6967496267,655002.7264386227 1794950.8291303124,655002.5 1794950.9605242272,655002.2729902205 1794951.0909288693,655002.0454136062 1794951.2203417565,655001.8172744898 1794951.3487604244,655001.5885772147 1794951.4761824284,655001.3593261351 1794951.6026053426,655001.1295256156 1794951.7280267598,655000.899180031 1794951.8524442923,655000.6682937671 1794951.9758555717,655000.4368712195 1794952.0982582478,655000.2049167941 1794952.2196499908,654999.972434907 1794952.3400284892,654999.7394299844 1794952.4593914514,654999.5059064622 1794952.5777366047,654999.2718687865 1794952.6950616962,654999.0373214132 1794952.811364492,654998.8022688073 1794952.926642778,654998.5667154443 1794953.0408943594,654998.3306658085 1794953.154117061,654998.0941243941 1794953.2663087274,654997.8570957044 1794953.3774672223,654997.6195842522 1794953.4875904296,654997.3815945594 1794953.5966762525,654997.1431311568 1794953.7047226147,654996.9041985845 1794953.8117274586,654996.6648013915 1794953.9176887472,654996.4249441355 1794954.0226044632,654996.184631383 1794954.1264726091,654995.9438677094 1794954.2292912072,654995.7026576982 1794954.3310583003,654995.4610059418 1794954.4317719508,654995.2189170412 1794954.5314302412,654994.9763956049 1794954.6300312742,654994.7334462506 1794954.7275731724,654994.4900736035 1794954.824054079,654994.2462822971 1794954.919472157,654994.0020769727 1794955.0138255898,654993.7574622799 1794955.1071125811,654993.5124428755 1794955.199331355,654993.2670234246 1794955.2904801555,654993.0212085996 1794955.3805572472,654992.7750030803 1794955.4695609154,654992.528411554 1794955.5574894657,654992.2814387158 1794955.6443412236,654992.0340892675 1794955.7301145361,654991.7863679181 1794955.8148077698,654991.5382793843 1794955.8984193124,654991.2898283891 1794955.9809475723,654991.0410196625 1794956.0623909777,654990.7918579417 1794956.1427479787,654990.5423479703 1794956.222017045,654990.2924944985 1794956.3001966674,654990.0423022833 1794956.3772853578,654989.7917760881 1794956.4532816485,654989.5409206823 1794956.5281840921,654989.2897408418 1794956.6019912632,654989.038241349 1794956.6747017563,654988.7864269919 1794956.7463141873,654988.5343025647 1794956.8168271927,654988.2818728675 1794956.8862394297,654988.0291427062 1794956.9545495773,654987.7761168923 1794957.021756335,654987.5228002432 1794957.0878584227,654987.2691975817 1794957.1528545825,654987.0153137359 1794957.2167435766,654986.7611535395 1794957.279524189,654986.5067218314 1794957.3411952239,654986.2520234553 1794957.4017555078,654985.9970632606 1794957.4612038871,654985.7418461012 1794957.5195392305,654985.4863768363 1794957.5767604273,654985.2306603293 1794957.6328663877,654984.974701449 1794957.6878560442,654984.7185050684 1794957.7417283491,654984.462076065 1794957.7944822772,654984.205419321 1794957.8461168243,654983.9485397226 1794957.8966310068,654983.6914421605 1794957.9460238635,654983.4341315295 1794957.9942944539,654983.1766127284 1794958.0414418587,654982.91889066 1794958.0874651808,654982.6609702309 1794958.1323635436,654982.4028563517 1794958.1761360923,654982.1445539362 1794958.2187819937,654981.8860679024 1794958.2603004358,654981.6274031714 1794958.3006906281,654981.3685646678 1794958.3399518018,654981.1095573194 1794958.3780832093,654980.8503860576 1794958.4150841245,654980.5910558164 1794958.4509538433,654980.3315715332 1794958.4856916824,654980.0719381481 1794958.519296981,654979.8121606043 1794958.5517690985,654979.5522438475 1794958.5831074174,654979.292192826 1794958.6133113406,654979.0320124911 1794958.6423802935,654978.771707796 1794958.6703137222,654978.5112836967 1794958.697111095,654978.2507451512 1794958.722771902,654977.9900971198 1794958.7472956546,654977.7293445648 1794958.7706818855,654977.4684924508 1794958.79293015,654977.2075457437 1794958.814040024,654976.9465094117 1794958.834011106,654976.6853884247 1794958.8528430157,654976.4241877538 1794958.8705353944,654976.162912372 1794958.8870879053,654975.9015672537 1794958.9025002336,654975.6401573746 1794958.9167720852,654975.3786877113 1794958.9299031892,654975.117163242 1794958.941893295,654974.8555889456 1794958.9527421745,654974.5939698021 1794958.9624496212,654974.3323107926 1794958.9710154503,654974.0706168985 1794958.9784394987,654973.8088931021 1794958.9847216248,654973.5471443862 1794958.9898617095,654973.2853757343 1794958.9938596545,654973.0235921299 1794958.9967153838,654972.7617985571 1794958.9984288432,654972.5 1794958.999,654972.2382014429 1794958.9984288432,654971.9764078701 1794958.9967153838,654971.7146242657 1794958.9938596545,654971.4528556138 1794958.9898617095,654971.1911068979 1794958.9847216248,654970.9293831015 1794958.9784394987,654970.6676892074 1794958.9710154503,654970.4060301979 1794958.9624496212,654970.1444110544 1794958.9527421745,654969.882836758 1794958.941893295,654969.6213122887 1794958.9299031892,654969.3598426254 1794958.9167720852,654969.0984327463 1794958.9025002336,654968.837087628 1794958.8870879053,654968.5758122462 1794958.8705353944,654968.3146115753 1794958.8528430157,654968.0534905883 1794958.834011106,654967.7924542563 1794958.814040024,654967.5315075492 1794958.79293015,654967.2706554352 1794958.7706818855,654967.0099028802 1794958.7472956546,654966.7492548488 1794958.722771902,654966.4887163033 1794958.697111095,654966.228292204 1794958.6703137222,654965.9679875089 1794958.6423802935,654965.707807174 1794958.6133113406,654965.4477561525 1794958.5831074174,654965.1878393957 1794958.5517690985,654964.9280618519 1794958.519296981,654964.6684284668 1794958.4856916824,654964.4089441836 1794958.4509538433,654964.1496139424 1794958.4150841245,654963.8904426806 1794958.3780832093,654963.6314353322 1794958.3399518018,654963.3725968286 1794958.3006906281,654963.1139320976 1794958.2603004358,654962.8554460638 1794958.2187819937,654962.5971436483 1794958.1761360923,654962.5139866447 1794958.162033813,654961.9724276481 1794958.275911423,654961.4168314742 1794958.3902116008,654960.8607418621 1794958.5020864524,654960.3041693988 1794958.611533848,654959.7471246811 1794958.7185517044,654959.1896183139 1794958.8231379832,654958.6316609116 1794958.925290694,654958.0732630966 1794959.0250078917,654957.5144355004 1794959.1222876776,654956.9551887618 1794959.2171281998,654956.3955335284 1794959.3095276528,654955.8354804552 1794959.3994842775,654955.2750402046 1794959.4869963608,654954.7142234468 1794959.572062237,654954.1530408588 1794959.6546802865,654953.5915031248 1794959.7348489363,654953.0296209356 1794959.8125666603,654952.4674049886 1794959.8878319785,654951.9048659876 1794959.9606434584,654951.3420146427 1794960.0309997133,654950.7788616695 1794960.098899404,654950.2154177898 1794960.1643412379,654949.6516937307 1794960.227323969,654949.0877002247 1794960.2878463978,654948.5234480095 1794960.3459073727,654947.9589478276 1794960.4015057876,654947.3942104263 1794960.4546405848,654946.8292465572 1794960.505310752,654946.2640669765 1794960.5535153246,654945.6986824445 1794960.5992533853,654945.4038069287 1794960.6218133986,654945.697320294 1794960.4549051593,654945.4038069285 1794960.6218133986,654945.3938848083 1794960.6274556753,654945.0896993198 1794960.7986805658,654944.8406554153 1794960.9374389895,654945.08969932 1794960.79868057,654944.78476962 1794960.96857657,654944.47910151 1794961.13714046,654944.17270082 1794961.30436901,654943.86557337 1794961.47025906,654943.55772502 1794961.63480743,654943.24916163 1794961.798011,654942.93988906 1794961.95986665,654942.62991321 1794962.12037132,654942.31923998 1794962.27952194,654942.00787528 1794962.43731547,654941.69582505 1794962.59374893,654941.38309522 1794962.74881932,654941.06969174 1794962.9025237,654940.75562059 1794963.05485915,654940.44088774 1794963.20582275,654940.12549919 1794963.35541164,654939.80946094 1794963.50362296,654939.492779 1794963.65045391,654939.17545941 1794963.79590167,654938.85750821 1794963.93996349,654938.53893145 1794964.08263661,654938.21973519 1794964.22391833,654937.89992551 1794964.36380595,654937.57950851 1794964.50229681,654937.25849028 1794964.63938828,654936.93687693 1794964.77507773,654936.61467459 1794964.9093626,654936.29188939 1794965.04224032,654935.96852747 1794965.17370837,654935.644595 1794965.30376423,654935.32009814 1794965.43240544,654934.99504306 1794965.55962954,654934.66943596 1794965.68543412,654934.34328304 1794965.80981678,654934.0165905 1794965.93277514,654933.68936457 1794966.05430687,654933.36161147 1794966.17440966,654933.03333744 1794966.29308122,654932.70454874 1794966.41031929,654932.37525162 1794966.52612163,654932.04545236 1794966.64048605,654931.71515722 1794966.75341036,654931.6535978328 1794966.7741572917,654931.6868053456 1794966.762965595,654931.5778995979 1794966.799669337,654931.54184 1794966.77335,654931.12027 1794966.46282,654930.7527472321 1794966.189631166,654930.7000479315 1794966.1504575228,654931.1202655898 1794966.462823274,654931.5418421964 1794966.7733525122,654931.9647697249 1794967.082039325,654932.3890401234 1794967.3888778358,654932.8146453146 1794967.6938622028,654933.2415771955 1794967.9969866194,654933.6698276378 1794968.2982453147,654934.0993884885 1794968.597632553,654934.530251569 1794968.8951426346,654934.9624086765 1794969.1907698952,654935.3958515835 1794969.4845087067,654935.8305720376 1794969.7763534763,654936.2665617625 1794970.066298648,654936.7038124576 1794970.3543387018,654937.1423157982 1794970.6404681534,654937.5820634358 1794970.9246815557,654938.0230469983 1794971.2069734975,654938.4652580901 1794971.4873386044,654938.908688292 1794971.7657715387,654939.3533291618 1794972.0422669994,654939.799172234 1794972.3168197225,654940.2462090207 1794972.5894244807,654940.6944310108 1794972.8600760843,654941.1438296707 1794973.1287693803,654941.5943964446 1794973.395499253,654942.0461227545 1794973.6602606245,654942.499 1794973.923048454,654942.9530195589 1794974.1838577387,654943.4081727875 1794974.4426835128,654943.8644510204 1794974.6995208487,654944.3218455703 1794974.9543648567,654944.7803477297 1794975.207210685,654945.2399487688 1794975.4580535195,654945.7006399378 1794975.7068885847,654946.1624124657 1794975.953711143,654946.625257561 1794976.1985164955,654947.0891664118 1794976.4412999814,654947.554130186 1794976.6820569783,654948.0201400312 1794976.9207829025,654948.4871870754 1794977.1574732093,654948.9552624268 1794977.3921233923,654949.4243571737 1794977.624728984,654949.8944623852 1794977.8552855558,654950.3655691114 1794978.0837887188,654950.8376683829 1794978.310234122,654951.3107512118 1794978.5346174545,654951.7848085911 1794978.7569344444,654952.2598314955 1794978.977180859,654952.7358108812 1794979.1953525052,654953.2127376864 1794979.4114452293,654953.6906028308 1794979.625454917,654954.1693972169 1794979.8373774944,654954.6491117289 1794980.0472089262,654955.1297372339 1794980.2549452179,654955.6112645812 1794980.4605824144,654956.0936846036 1794980.6641166005,654956.5769881161 1794980.8655439015,654957.0611659177 1794981.064860482,654957.54620879 1794981.262062548,654958.0321074987 1794981.4571463447,654958.518852793 1794981.650108158,654959.0064354059 1794981.840944314,654959.4948460545 1794982.0296511797,654959.9840754402 1794982.2162251624,654960.4741142489 1794982.4006627097,654960.9649531506 1794982.5829603106,654961.4565828008 1794982.7631144943,654961.9489938396 1794982.9411218308,654962.442176892 1794983.116978931,654962.9361225684 1794983.2906824471,654963.4308214651 1794983.4622290719,654963.9262641636 1794983.6316155393,654964.4224412313 1794983.7988386247,654964.9193432219 1794983.9638951442,654965.4169606749 1794984.1267819554,654965.9152841166 1794984.2874959572,654966.4143040595 1794984.44603409,654966.9140110029 1794984.602393335,654967.4143954333 1794984.7565707155,654967.9154478238 1794984.9085632965,654968.4171586354 1794985.0583681841,654968.9195183163 1794985.2059825263,654969.4225173019 1794985.3514035125,654969.9261460161 1794985.4946283745,654970.4303948706 1794985.635654385,654970.935254265 1794985.7744788595,654971.4407145877 1794985.9110991547,654971.9467662153 1794986.0455126697,654972.4533995134 1794986.1777168454,654972.9606048365 1794986.307709165,654973.468372528 1794986.435487153,654973.9766929208 1794986.5610483775,654974.4855563373 1794986.6843904478,654974.9949530893 1794986.8055110152,654975.5048734787 1794986.9244077741,654976.0153077974 1794987.041078461,654976.5262463274 1794987.1555208543,654977.0376793412 1794987.2677327755,654977.5495971019 1794987.3777120882,654978.0619898632 1794987.485456698,654978.5748478699 1794987.5909645546,654979.088161358 1794987.6942336485,654979.6019205548 1794987.7952620138,654980.1161156789 1794987.894047727,654980.6307369409 1794987.9905889076,654981.145774543 1794988.0848837176,654981.66121868 1794988.1769303614,654982.1770595381 1794988.2667270869,654982.6932872967 1794988.3542721844,654983.2098921275 1794988.4395639873,654983.7268641951 1794988.5226008715,654984.2441936572 1794988.603381256,654984.7618706644 1794988.6819036035,654985.279885361 1794988.7581664184,654985.7982278847 1794988.832168249,654986.3168883671 1794988.9039076865,654986.8358569335 1794988.973383365,654987.3551237036 1794989.0405939615,654987.8746787914 1794989.1055381969,654988.394512305 1794989.1682148345,654988.9146143479 1794989.2286226812,654989.4349750177 1794989.2867605868,654989.9555844078 1794989.3426274443,654990.4764326065 1794989.39622219,654990.9975096976 1794989.447543804,654991.5188057603 1794989.4965913089,654992.0403108703 1794989.543363771,654992.5620150985 1794989.5878602997,654993.0839085126 1794989.630080048,654993.6059811765 1794989.670022212,654994.1282231506 1794989.7076860312,654994.6506244923 1794989.7430707887,654995.1731752558 1794989.7761758107,654995.6958654923 1794989.8070004669,654996.2186852508 1794989.8355441706,654996.7416245774 1794989.8618063782,654997.2646735162 1794989.88578659,654997.7878221088 1794989.9074843489,654998.3110603957 1794989.9268992422,654998.8343784148 1794989.9440309005,654999.357766203 1794989.958878997,654999.8812137959 1794989.9714432496,655000.4047112275 1794989.9817234187,655000.9282485314 1794989.989719309,655001.4518157402 1794989.9954307678,655001.9754028858 1794989.9988576865,655002.499 1794990,655003.0225971141 1794989.9988576865,655003.5461842598 1794989.9954307678,655004.0697514685 1794989.989719309,655004.5932887724 1794989.9817234187,655005.116786204 1794989.9714432496,655005.6402337969 1794989.958878997,655006.1636215851 1794989.9440309005,655006.6869396042 1794989.9268992422,655007.2101778911 1794989.9074843489,655007.7333264838 1794989.88578659,655008.2563754225 1794989.8618063782,655008.7793147491 1794989.8355441706,655009.3021345076 1794989.8070004669,655009.8248247441 1794989.7761758107,655010.3473755076 1794989.7430707887,655010.8697768493 1794989.7076860312,655011.3920188234 1794989.670022212,655011.9140914873 1794989.630080048,655012.4359849014 1794989.5878602997,655012.9576891296 1794989.543363771,655013.4791942396 1794989.4965913089,655014.0004903023 1794989.447543804,655014.5215673934 1794989.39622219,655015.0424155921 1794989.3426274443,655015.5630249822 1794989.2867605868,655016.083385652 1794989.2286226812,655016.6034876949 1794989.1682148345,655017.1233212085 1794989.1055381969,655017.6428762963 1794989.0405939615,655018.1621430664 1794988.973383365,655018.6811116328 1794988.9039076865,655019.1997721152 1794988.832168249,655019.7181146389 1794988.7581664184,655020.2361293355 1794988.6819036035,655020.7538063427 1794988.603381256,655021.2711358048 1794988.5226008715,655021.7881078724 1794988.4395639873,655022.3047127032 1794988.3542721844,655022.8209404618 1794988.2667270869,655023.3367813199 1794988.1769303614,655023.8522254569 1794988.0848837176,655024.367263059 1794987.9905889076,655024.881884321 1794987.894047727,655025.3960794451 1794987.7952620138,655025.9098386419 1794987.6942336485,655026.42315213 1794987.5909645546,655026.9360101367 1794987.485456698,655027.448402898 1794987.3777120882,655027.9603206587 1794987.2677327755,655028.4717536726 1794987.1555208543,655028.9826922026 1794987.041078461,655029.4931265212 1794986.9244077741,655030.0030469106 1794986.8055110152,655030.5124436626 1794986.6843904478,655031.0213070791 1794986.5610483775,655031.0993108527 1794986.5417805114,655031.1209167908 1794986.3904426806,655031.1354298714 1794986.2918626708,655031.12091679 1794986.39044268,655031.1590482 1794986.13143533,655031.19830937 1794985.87259683,655031.23869956 1794985.6139321,655031.28021801 1794985.35544606,655031.32286391 1794985.09714365,655031.36663646 1794984.83902977,655031.41153482 1794984.58110934,655031.45755814 1794984.32338727,655031.50470555 1794984.06586847,655031.55297614 1794983.80855784,655031.60236899 1794983.55146028,655031.65288318 1794983.29458068,655031.70451772 1794983.03792394,655031.75727165 1794982.78149493,655031.81114396 1794982.52529855,655031.86613361 1794982.26933967,655031.92223957 1794982.01362316,655031.97946077 1794981.7581539,655032.03779611 1794981.50293674,655032.09724449 1794981.24797654,655032.15780478 1794980.99327817,655032.21947581 1794980.73884646,655032.28225642 1794980.48468626,655032.34614542 1794980.23080242,655032.41114158 1794979.97719976,655032.47724367 1794979.72388311,655032.54445042 1794979.47085729,655032.61276057 1794979.21812713,655032.68217281 1794978.96569744,655032.75268581 1794978.71357301,655032.82429824 1794978.46175865,655032.89700874 1794978.21025916,655032.97081591 1794977.95907932,655033.04571835 1794977.70822391,655033.12171464 1794977.45769772,655033.19880333 1794977.2075055,655033.27698296 1794976.95765203,655033.35625202 1794976.70814206,655033.43660902 1794976.45898034,655033.51805243 1794976.21017161,655033.60058069 1794975.96172062,655033.68419223 1794975.71363208,655033.76888546 1794975.46591073,655033.85465878 1794975.21856128,655033.94151053 1794974.97158845,655034.02943908 1794974.72499692,655034.11844275 1794974.4787914,655034.20851984 1794974.23297658,655034.29966865 1794973.98755712,655034.39188742 1794973.74253772,655034.48517441 1794973.49792303,655034.57952784 1794973.2537177,655034.67494592 1794973.0099264,655034.77142683 1794972.76655375,655034.86896873 1794972.5236044,655034.96756976 1794972.28108296,655035.06722805 1794972.03899406,655035.1679417 1794971.7973423,655035.26970879 1794971.55613229,655035.37252739 1794971.31536862,655035.47639554 1794971.07505586,655035.58131125 1794970.83519861,655035.68727254 1794970.59580142,655035.79427739 1794970.35686884,655035.90232375 1794970.11840544,655036.01140957 1794969.88041575,655036.12153278 1794969.6429043,655036.23269127 1794969.40587561,655036.34488294 1794969.16933419,655036.45810564 1794968.93328456,655036.57235722 1794968.69773119,655036.68763551 1794968.46267859,655036.8039383 1794968.22813121,655036.9212634 1794967.99409354,655037.03960855 1794967.76057002,655037.15897151 1794967.52756509,655037.27935001 1794967.29508321,655037.40074175 1794967.06312878,655037.52314443 1794966.83170623,655037.64655571 1794966.60081997,655037.77097324 1794966.37047438,655037.89639466 1794966.14067387,655038.02281757 1794965.91142279,655038.15023958 1794965.68272551,655038.27865824 1794965.45458639,655038.40807113 1794965.22700978,655038.53847577 1794965,655038.66986969 1794964.77356138,655038.80225037 1794964.54769822,655038.93561531 1794964.32241484,655039.06996196 1794964.09771551,655039.20528776 1794963.87360451,655039.34159014 1794963.65008612,655039.4788665 1794963.42716458,655039.61711423 1794963.20484415,655039.7563307 1794962.98312905,655039.89651325 1794962.7620235,655040.03765922 1794962.54153172,655040.17976592 1794962.3216579,655040.32283065 1794962.10240623,655040.46685068 1794961.88378088,655040.61182326 1794961.66578602,655040.75774565 1794961.44842579,655040.90461505 1794961.23170434,655041.05242868 1794961.01562578,655041.20118372 1794960.80019424,655041.35087734 1794960.58541382,655041.50150669 1794960.3712886,655041.6530689 1794960.15782266,655041.80556108 1794959.94502006,655041.95898034 1794959.73288486,655042.11332374 1794959.5214211,655042.26858836 1794959.3106328,655042.42477124 1794959.10052397,655042.5818694 1794958.89109861,655042.73987985 1794958.68236072,655042.89879958 1794958.47431426,655043.05862558 1794958.2669632,655043.21935478 1794958.06031148,655043.38098415 1794957.85436304,655043.54351059 1794957.6491218,655043.70693101 1794957.44459167,655043.87124231 1794957.24077654,655044.03644136 1794957.03768029,655044.202525 1794956.83530678,655044.36949008 1794956.63365988,655044.53733341 1794956.43274342,655044.70605181 1794956.23256122,655044.87564206 1794956.0331171,655045.04610094 1794955.83441485,655045.21742519 1794955.63645826,655045.38961155 1794955.43925109,655045.56265675 1794955.24279711,655045.7365575 1794955.04710004,655045.91131047 1794954.85216362,655046.08691235 1794954.65799155,655046.26335979 1794954.46458754,655046.44064943 1794954.27195527,655046.6187779 1794954.0800984,655046.79774181 1794953.88902058,655046.97753774 1794953.69872546,655047.15816228 1794953.50921665,655047.33961198 1794953.32049777,655047.52188339 1794953.13257241,655047.70497305 1794952.94544414,655047.88887746 1794952.75911653,655048.07359313 1794952.57359313,655048.25911653 1794952.38887746,655048.44544414 1794952.20497305,655048.63257241 1794952.02188339,655048.82049777 1794951.83961198,655049.00921665 1794951.65816228,655049.19872546 1794951.47753774,655049.38902058 1794951.29774181,655049.5800984 1794951.1187779,655049.77195527 1794950.94064943,655049.96458754 1794950.76335979,655050.15799155 1794950.58691235,655050.35216362 1794950.41131047,655050.54710004 1794950.2365575,655050.74279711 1794950.06265675,655050.93925109 1794949.88961155,655051.13645826 1794949.71742519,655051.33441485 1794949.54610094,655051.5331171 1794949.37564206,655051.73256122 1794949.20605181,655051.93274342 1794949.03733341,655052.13365988 1794948.86949008,655052.33530678 1794948.702525,655052.53768029 1794948.53644136,655052.74077654 1794948.37124231,655052.94459167 1794948.20693101,655053.1491218 1794948.04351059,655053.35436304 1794947.88098415,655053.56031148 1794947.71935478,655053.7669632 1794947.55862558,655053.97431426 1794947.39879958,655054.18236072 1794947.23987985,655054.39109861 1794947.0818694,655054.60052397 1794946.92477124,655054.81063279 1794946.76858836,655055.0214211 1794946.61332374,655055.23288486 1794946.45898034,655055.44502006 1794946.30556108,655055.65782266 1794946.1530689,655055.8712886 1794946.00150669,655056.08541382 1794945.85087734,655056.30019424 1794945.70118372,655056.51562578 1794945.55242868,655056.73170434 1794945.40461505,655056.94842579 1794945.25774565,655057.16578602 1794945.11182326,655057.38378088 1794944.96685068,655057.60240623 1794944.82283065,655057.8216579 1794944.67976592,655058.04153172 1794944.53765922,655058.2620235 1794944.39651325,655058.48312905 1794944.2563307,655058.70484415 1794944.11711423,655058.92716458 1794943.9788665,655059.15008612 1794943.84159014,655059.37360451 1794943.70528776,655059.59771551 1794943.56996196,655059.82241484 1794943.43561531,655060.04769822 1794943.30225037,655060.27356138 1794943.16986969,655060.5 1794943.03847577,655060.72700978 1794942.90807113,655060.95458639 1794942.77865824,655061.18272551 1794942.65023958,655061.41142279 1794942.52281757,655061.64067386 1794942.39639466,655061.87047438 1794942.27097324,655062.10081997 1794942.14655571,655062.33170623 1794942.02314443,655062.56312878 1794941.90074175,655062.79508321 1794941.77935001,655063.02756509 1794941.65897151,655063.26057002 1794941.53960855,655063.49409354 1794941.4212634,655063.72813121 1794941.3039383,655063.96267859 1794941.18763551,655064.19773119 1794941.07235722,655064.43328456 1794940.95810564,655064.66933419 1794940.84488294,655064.90587561 1794940.73269127,655065.1429043 1794940.62153278,655065.38041575 1794940.51140957,655065.61840544 1794940.40232375,655065.85686884 1794940.29427739,655066.09580142 1794940.18727254,655066.33519861 1794940.08131125,655066.57505586 1794939.97639554,655066.81536862 1794939.87252739,655067.05613229 1794939.76970879,655067.2973423 1794939.6679417,655067.53899406 1794939.56722805,655067.78108296 1794939.46756976,655068.0236044 1794939.36896873,655068.26655375 1794939.27142683,655068.5099264 1794939.17494592,655068.7537177 1794939.07952784,655068.99792303 1794938.98517441,655069.24253772 1794938.89188742,655069.48755712 1794938.79966865,655069.73297658 1794938.70851984,655069.9787914 1794938.61844275,655070.22499692 1794938.52943908,655070.47158845 1794938.44151053,655070.71856128 1794938.35465878,655070.96591073 1794938.26888546,655071.21363208 1794938.18419223,655071.46172062 1794938.10058069,655071.71017161 1794938.01805243,655071.95898034 1794937.93660902,655072.20814206 1794937.85625202,655072.45765203 1794937.77698296,655072.7075055 1794937.69880333,655072.95769772 1794937.62171464,655073.20822391 1794937.54571835,655073.45907932 1794937.47081591,655073.71025916 1794937.39700874,655073.96175865 1794937.32429824,655074.21357301 1794937.25268581,655074.46569744 1794937.18217281,655074.71812713 1794937.11276057,655074.97085729 1794937.04445042,655075.22388311 1794936.97724367,655075.47719976 1794936.91114158,655075.73080242 1794936.84614542,655075.98468626 1794936.78225642,655076.23884646 1794936.71947581,655076.49327817 1794936.65780478,655076.74797654 1794936.59724449,655077.00293674 1794936.53779611,655077.2581539 1794936.47946077,655077.51362316 1794936.42223957,655077.76933967 1794936.36613361,655078.02529855 1794936.31114396,655078.28149493 1794936.25727165,655078.53792394 1794936.20451772,655078.79458068 1794936.15288318,655079.05146028 1794936.10236899,655079.30855784 1794936.05297614,655079.56586847 1794936.00470555,655079.82338727 1794935.95755814,655080.08110934 1794935.91153482,655080.33902977 1794935.86663646,655080.59714365 1794935.82286391,655080.85544606 1794935.78021801,655081.1139321 1794935.73869956,655081.37259683 1794935.69830937,655081.63143533 1794935.6590482,655081.89044268 1794935.62091679,655082.14961394 1794935.58391588,655082.40894418 1794935.54804616,655082.66842847 1794935.51330832,655082.92806185 1794935.47970302,655083.1878394 1794935.4472309,655083.44775615 1794935.41589258,655083.70780717 1794935.38568866,655083.96798751 1794935.35661971,655084.2282922 1794935.32868628,655084.4887163 1794935.30188891,655084.74925485 1794935.2762281,655085.00990288 1794935.25170435,655085.27065544 1794935.22831811,655085.53150755 1794935.20606985,655085.79245426 1794935.18495998,655086.05349059 1794935.16498889,655086.31461158 1794935.14615698,655086.57581225 1794935.12846461,655086.83708763 1794935.11191209,655087.09843275 1794935.09649977,655087.35984263 1794935.08222791,655087.62131229 1794935.06909681,655087.88283676 1794935.05710671,655088.14441105 1794935.04625783,655088.4060302 1794935.03655038,655088.66768921 1794935.02798455,655088.9293831 1794935.0205605,655089.1911069 1794935.01427838,655089.45285561 1794935.00913829,655089.71462427 1794935.00514035,655089.97640787 1794935.00228462,655090.23820144 1794935.00057116,655090.5 1794935,655090.76179856 1794935.00057116,655091.02359213 1794935.00228462,655091.28537573 1794935.00514035,655091.54714439 1794935.00913829,655091.8088931 1794935.01427838,655092.0706169 1794935.0205605,655092.33231079 1794935.02798455,655092.5939698 1794935.03655038,655092.85558895 1794935.04625783,655093.11716324 1794935.05710671,655093.37868771 1794935.06909681,655093.64015737 1794935.08222791,655093.90156725 1794935.09649977,655094.16291237 1794935.11191209,655094.42418775 1794935.12846461,655094.68538842 1794935.14615698,655094.94650941 1794935.16498889,655095.20754574 1794935.18495998,655095.46849245 1794935.20606985,655095.72934456 1794935.22831811,655095.99009712 1794935.25170435,655096.25074515 1794935.2762281,655096.5112837 1794935.30188891,655096.7717078 1794935.32868628,655097.03201249 1794935.35661971,655097.29219283 1794935.38568866,655097.55224385 1794935.41589258,655097.8121606 1794935.4472309,655098.07193815 1794935.47970302,655098.33157153 1794935.51330832,655098.59105582 1794935.54804616,655098.85038606 1794935.58391588,655099.10955732 1794935.62091679,655099.36856467 1794935.6590482,655099.62740317 1794935.69830937,655099.8860679 1794935.73869956,655100.14455394 1794935.78021801,655100.40285635 1794935.82286391,655100.66097023 1794935.86663646,655100.91889066 1794935.91153482,655101.17661273 1794935.95755814,655101.43413153 1794936.00470555,655101.69144216 1794936.05297614,655101.94853972 1794936.10236899,655102.20541932 1794936.15288318,655102.2821919993 1794936.1683284098,655102.2703289517 1794936.2100430354,655102.1067303421 1794936.775916894,655102.0706530997 1794936.8986946119,655102.0316450528 1794937.0314464143,655102.2753534763 1794936.6684279623,655102.565298648 1794936.2324382374,655102.8533387017 1794935.7951875424,655103.1394681534 1794935.3566842019,655103.4236815557 1794934.9169365643,655103.7059734975 1794934.4759530015,655103.9863386045 1794934.0337419098,655104.2647715387 1794933.590311708,655104.5412669994 1794933.1456708382,655104.8158197225 1794932.6998277658,655105.0884244808 1794932.2527909793,655105.3590760842 1794931.8045689892,655105.6277693801 1794931.3551703293,655105.8944992529 1794930.9046035553,655106.1592606244 1794930.4528772454,655106.422048454 1794930,655106.6828577387 1794929.545980441,655106.9416835128 1794929.0908272124,655107.1985208486 1794928.6345489796,655107.4533648567 1794928.1771544295,655107.7062106848 1794927.7186522703,655107.9570535194 1794927.2590512312,655108.2058885846 1794926.7983600623,655108.4527111431 1794926.3365875343,655108.6975164955 1794925.873742439,655108.9402999814 1794925.4098335882,655109.1810569782 1794924.944869814,655109.4197829026 1794924.4788599687,655109.6564732093 1794924.0118129244,655109.8911233922 1794923.543737573,655110.1237289839 1794923.0746428263,655110.3542855559 1794922.6045376146,655110.5827887186 1794922.1334308886,655110.8092341219 1794921.661331617,655111.0336174545 1794921.1882487882,655111.2559344444 1794920.7141914088,655111.4761808589 1794920.2391685045,655111.6943525051 1794919.7631891188,655111.9104452292 1794919.2862623136,655112.124454917 1794918.808397169,655112.3363774943 1794918.329602783,655112.5462089261 1794917.849888271,655112.7539452178 1794917.369262766,655112.9595824142 1794916.8877354187,655113.1631166004 1794916.4053153964,655113.3645439013 1794915.922011884,655113.5638604821 1794915.4378340824,655113.7610625479 1794914.95279121,655113.9561463445 1794914.466892501,655114.1491081578 1794913.980147207,655114.3399443139 1794913.4925645941,655114.5286511796 1794913.0041539455,655114.7152251622 1794912.5149245597,655114.8996627098 1794912.024885751,655115.0819603107 1794911.5340468492,655115.2621144942 1794911.0424171991,655115.4401218307 1794910.5500061605,655115.615978931 1794910.056823108,655115.7896824471 1794909.5628774315,655115.9612290718 1794909.068178535,655116.1306155394 1794908.5727358365,655116.2978386247 1794908.0765587685,655116.4628951441 1794907.5796567781,655116.6257819553 1794907.082039325,655116.7864959572 1794906.5837158833,655116.9450340897 1794906.0846959406,655117.1013933348 1794905.584988997,655117.2555707155 1794905.0846045667,655117.4075632965 1794904.5835521761,655117.5573681841 1794904.0818413645,655117.7049825263 1794903.5794816837,655117.8504035126 1794903.076482698,655117.9936283744 1794902.5728539838,655118.134654385 1794902.0686051294,655118.2734788595 1794901.563745735,655118.4100991547 1794901.0582854124,655118.5445126697 1794900.5522337847,655118.6767168454 1794900.0456004865,655118.8067091648 1794899.5383951634,655118.9344871531 1794899.0306274719,655119.0600483775 1794898.522307079,655119.1833904476 1794898.0134436628,655119.3045110152 1794897.5040469107,655119.4234077742 1794896.9941265213,655119.5400784609 1794896.4836922025,655119.6545208544 1794895.9727536726,655119.7667327755 1794895.4613206587,655119.876712088 1794894.949402898,655119.9844566981 1794894.437010137,655120.0899645544 1794893.92415213,655120.1932336483 1794893.410838642,655120.2942620136 1794892.8970794452,655120.3930477271 1794892.382884321,655120.4895889076 1794891.868263059,655120.5838837175 1794891.3532254568,655120.6759303614 1794890.83778132,655120.7657270869 1794890.3219404619,655120.8532721844 1794889.8057127034,655120.9385639871 1794889.2891078724,655121.0216008713 1794888.7721358049,655121.1023812561 1794888.2548063428,655121.1809036034 1794887.7371293355,655121.2571664184 1794887.2191146389,655121.3311682489 1794886.7007721153,655121.4029076864 1794886.1821116328,655121.4723833648 1794885.6631430665,655121.5395939616 1794885.1438762962,655121.6045381969 1794884.6243212086,655121.6672148346 1794884.104487695,655121.7276226812 1794883.584385652,655121.7857605866 1794883.0640249823,655121.8416274441 1794882.543415592,655121.8952221901 1794882.0225673935,655121.946543804 1794881.5014903024,655121.9955913089 1794880.9801942396,655122.042363771 1794880.4586891297,655122.0868602997 1794879.9369849015,655122.129080048 1794879.4150914873,655122.1690222119 1794878.8930188233,655122.2066860311 1794878.3707768493,655122.2420707886 1794877.8483755076,655122.2751758106 1794877.3258247443,655122.3060004668 1794876.8031345075,655122.3345441705 1794876.280314749,655122.3608063782 1794875.7573754226,655122.3847865898 1794875.234326484,655122.4064843488 1794874.7111778911,655122.4258992423 1794874.1879396043,655122.4430309003 1794873.6646215853,655122.457878997 1794873.141233797,655122.4704432496 1794872.6177862042,655122.4807234188 1794872.0942887724,655122.4887193089 1794871.5707514687,655122.4944307676 1794871.0471842599,655122.4978576865 1794870.5235971143,655122.499 1794870)) + + + POLYGON((655109.3109558484 1794924.6912980557,655109.3109558481 1794924.6912980557,655109.3472126318 1794924.6205222565,655109.4197829026 1794924.4788599687,655109.6564732093 1794924.0118129244,655109.7742570823 1794923.7768600807,655109.891123392 1794923.543737573,655110.1218190717 1794923.0784945386,655110.1237289839 1794923.0746428263,655110.3542855559 1794922.6045376146,655110.5827887186 1794922.1334308886,655110.8092341219 1794921.661331617,655111.0336174545 1794921.1882487882,655111.181077731 1794920.8738120361,655111.255934444 1794920.714191409,655111.3485819502 1794920.5143711874,655111.4761808589 1794920.2391685045,655111.6881236966 1794919.7767783478,655111.694352505 1794919.763189119,655111.910445229 1794919.286262313,655112.124454917 1794918.808397169,655112.336377494 1794918.329602783,655112.546208926 1794917.849888271,655112.6154543567 1794917.6896797693,655112.7539452178 1794917.369262766,655112.8846788395 1794917.0631322842,655112.959582414 1794916.887735419,655113.1631166 1794916.405315396,655113.364543901 1794915.922011884,655113.4794251217 1794915.6429435972,655113.5638604821 1794915.4378340824,655113.7610625479 1794914.95279121,655113.9561463445 1794914.466892501,655114.1491081578 1794913.980147207,655114.3399443139 1794913.4925645941,655114.5286511796 1794913.0041539455,655114.675167185 1794912.619963533,655114.715225162 1794912.51492456,655114.7860666763 1794912.3267031722,655114.8996627098 1794912.024885751,655115.0819603107 1794911.5340468492,655115.1636463337 1794911.3111307328,655115.262114494 1794911.042417199,655115.329726979 1794910.85538483,655115.4401218307 1794910.5500061605,655115.615978931 1794910.056823108,655115.7896824471 1794909.5628774315,655115.9612290718 1794909.068178535,655116.0255747897 1794908.879972142,655116.130615539 1794908.572735837,655116.1766550645 1794908.4361293595,655116.2978386247 1794908.0765587685,655116.41705678 1794907.7176529898,655116.462895144 1794907.579656778,655116.625781955 1794907.082039325,655116.786495957 1794906.583715883,655116.9364336986 1794906.11176682,655116.9450340897 1794906.0846959406,655117.1013933348 1794905.584988997,655117.2555707155 1794905.0846045667,655117.4075632965 1794904.5835521761,655117.5573681841 1794904.0818413645,655117.5736729337 1794904.0263532004,655117.704982526 1794903.579481684,655117.7517413112 1794903.4177469716,655117.8504035126 1794903.076482698,655117.9172141682 1794902.8415530624,655117.993628374 1794902.572853984,655118.134654385 1794902.068605129,655118.1596865534 1794901.977571287,655118.2734788595 1794901.563745735,655118.4100991547 1794901.0582854124,655118.5445126697 1794900.5522337847,655118.6295071669 1794900.226517579,655118.676716845 1794900.045600487,655118.7637480702 1794899.7060211888,655118.8067091648 1794899.5383951634,655118.891894492 1794899.1998833623,655118.934487153 1794899.030627472,655118.948586423 1794898.9735481753,655119.0600483775 1794898.522307079,655119.1833904476 1794898.0134436628,655119.2675824467 1794897.6593573855,655119.304511015 1794897.504046911,655119.423407774 1794896.994126521,655119.4889873773 1794896.7072157217,655119.5400784609 1794896.4836922025,655119.5943224002 1794896.2415151761,655119.654520854 1794895.972753673,655119.6908184815 1794895.8073184437,655119.7667327755 1794895.4613206587,655119.876712088 1794894.949402898,655119.984456698 1794894.437010137,655120.089964554 1794893.92415213,655120.193233648 1794893.410838642,655120.2472912214 1794893.135939852,655120.2942620136 1794892.8970794452,655120.3657268982 1794892.5250935184,655120.393047727 1794892.382884321,655120.4123559631 1794892.2799600686,655120.4895889076 1794891.868263059,655120.5838837175 1794891.3532254568,655120.6383968388 1794891.0479620863,655120.675930361 1794890.83778132,655120.7378853904 1794890.4818781936,655120.7657270869 1794890.3219404619,655120.8010221333 1794890.113815927,655120.853272184 1794889.805712704,655120.938563987 1794889.289107873,655121.021600871 1794888.772135805,655121.102381256 1794888.254806343,655121.1110593956 1794888.1975936708,655121.1809036034 1794887.7371293355,655121.2322903145 1794887.3880854193,655121.257166418 1794887.219114639,655121.3157489131 1794886.8087761658,655121.3311682489 1794886.7007721153,655121.3428383684 1794886.6163996973,655121.402907686 1794886.182111633,655121.4528042986 1794885.809394534,655121.4723833648 1794885.6631430665,655121.5395939616 1794885.1438762962,655121.6045381969 1794884.6243212086,655121.6672148346 1794884.104487695,655121.7034595427 1794883.7924264674,655121.727622681 1794883.584385652,655121.7519657463 1794883.3665041588,655121.7857605866 1794883.0640249823,655121.8268688539 1794882.680947234,655121.841627444 1794882.543415592,655121.89522219 1794882.022567393,655121.9312643382 1794881.6566253225,655121.946543804 1794881.5014903024,655121.9955913089 1794880.9801942396,655122.042363771 1794880.4586891297,655122.0868602997 1794879.9369849015,655122.1271201707 1794879.4393182306,655122.129080048 1794879.415091487,655122.1362958709 1794879.3207755184,655122.1690222119 1794878.8930188233,655122.1834629934 1794878.6927846894,655122.206686031 1794878.370776849,655122.2158041857 1794878.236161367,655122.2420707886 1794877.8483755076,655122.2751758106 1794877.3258247443,655122.3060004668 1794876.8031345075,655122.3345441705 1794876.280314749,655122.352658791 1794875.919612118,655122.360806378 1794875.757375423,655122.3722203603 1794875.5084171835,655122.3847865898 1794875.234326484,655122.4064843488 1794874.7111778911,655122.4158315982 1794874.459266194,655122.425899242 1794874.187939604,655122.4430309 1794873.664621585,655122.457878997 1794873.141233797,655122.4704432496 1794872.6177862042,655122.4807234188 1794872.0942887724,655122.4887193089 1794871.5707514687,655122.4944307676 1794871.0471842599,655122.4978576865 1794870.5235971143,655122.499 1794870,655122.4978576865 1794869.4764028857,655122.4944307676 1794868.9528157401,655122.4887193089 1794868.4292485313,655122.4807234188 1794867.9057112276,655122.4704432496 1794867.3822137958,655122.457878997 1794866.858766203,655122.4430309 1794866.335378415,655122.425899242 1794865.812060396,655122.4158315982 1794865.540733806,655122.4064843488 1794865.2888221089,655122.3847865898 1794864.765673516,655122.3722203603 1794864.4915828165,655122.360806378 1794864.242624577,655122.352658791 1794864.080387882,655122.3345441705 1794863.719685251,655122.3060004668 1794863.1968654925,655122.2751758106 1794862.674175256,655122.2420707886 1794862.1516244924,655122.2158041857 1794861.763838633,655122.206686031 1794861.629223151,655122.1834629934 1794861.3072153106,655122.1690222119 1794861.1069811767,655122.1362958709 1794860.6792244816,655122.129080048 1794860.584908513,655122.1266732723 1794860.5551574978,655122.0868602997 1794860.0630150985,655122.042363771 1794859.5413108703,655121.9955913089 1794859.0198057604,655121.9570755789 1794858.6104455187,655121.946543804 1794858.498509698,655121.89522219 1794857.977432607,655121.841627444 1794857.456584408,655121.8268688539 1794857.319052766,655121.7857605866 1794856.9359750177,655121.7519657463 1794856.6334958412,655121.727622681 1794856.415614348,655121.7034595427 1794856.2075735326,655121.6672148346 1794855.895512305,655121.6045381969 1794855.3756787914,655121.5395939616 1794854.8561237038,655121.4723833648 1794854.3368569335,655121.4528042986 1794854.190605466,655121.402907686 1794853.817888367,655121.3428383684 1794853.3836003027,655121.3311682489 1794853.2992278847,655121.3157489131 1794853.1912238342,655121.257166418 1794852.780885361,655121.228173454 1794852.5839508309,655121.1809036034 1794852.2628706645,655121.1131283039 1794851.8160460927,655121.102381256 1794851.745193657,655121.021600871 1794851.227864195,655120.938563987 1794850.710892128,655120.853272184 1794850.194287297,655120.789092822 1794849.8158404238,655120.7657270869 1794849.6780595381,655120.7378853904 1794849.5181218064,655120.675930361 1794849.16221868,655120.6383968388 1794848.9520379137,655120.5838837175 1794848.6467745432,655120.4895889076 1794848.131736941,655120.4123559631 1794847.7200399314,655120.393047727 1794847.617115679,655120.3657268982 1794847.4749064816,655120.2942620136 1794847.1029205548,655120.2472912214 1794846.864060148,655120.193233648 1794846.589161358,655120.089964554 1794846.07584787,655119.984456698 1794845.562989863,655119.876712088 1794845.050597102,655119.7667327755 1794844.5386793413,655119.6908184815 1794844.1926815563,655119.654520854 1794844.027246327,655119.5454717437 1794843.5403866016,655119.5400784609 1794843.5163077975,655119.5361457351 1794843.4991021196,655119.423407774 1794843.005873479,655119.304511015 1794842.495953089,655119.2675824467 1794842.3406426145,655119.1833904476 1794841.9865563372,655119.0600483775 1794841.477692921,655118.948586423 1794841.0264518247,655118.934487153 1794840.969372528,655118.891894492 1794840.8001166377,655118.8067091648 1794840.4616048366,655118.7813649608 1794840.36271657,655118.676716845 1794839.954399514,655118.606054927 1794839.6836086658,655118.5445126697 1794839.4477662153,655118.4100991547 1794838.9417145876,655118.2734788595 1794838.436254265,655118.1596865534 1794838.022428713,655118.134654385 1794837.931394871,655117.993628374 1794837.427146016,655117.9172141682 1794837.1584469376,655117.8504035126 1794836.923517302,655117.7517413112 1794836.5822530284,655117.704982526 1794836.420518316,655117.557368184 1794835.918158636,655117.4986377251 1794835.7214647445,655117.4075632965 1794835.4164478239,655117.2555707155 1794834.9153954333,655117.1013933348 1794834.415011003,655116.9450340897 1794833.9153040594,655116.9364336986 1794833.88823318,655116.786495957 1794833.416284117,655116.625781955 1794832.917960675,655116.462895144 1794832.420343222,655116.380539404 1794832.1724115964,655116.2978386247 1794831.9234412315,655116.2459723952 1794831.7695460105,655116.130615539 1794831.427264164,655115.9973935658 1794831.0375998945,655115.9612290718 1794830.931821465,655115.9105817105 1794830.7857667478,655115.789682447 1794830.437122569,655115.615978931 1794829.943176892,655115.4401218307 1794829.4499938395,655115.375445068 1794829.2710823826,655115.262114494 1794828.957582801,655115.1636463337 1794828.6888692672,655115.0819603107 1794828.4659531508,655114.8996627098 1794827.975114249,655114.7860666763 1794827.6732968278,655114.715225162 1794827.48507544,655114.6301004239 1794827.2618635923,655114.5286511796 1794826.9958460545,655114.3399443139 1794826.5074354059,655114.1491081578 1794826.019852793,655113.9561463445 1794825.533107499,655113.7610625479 1794825.0472087902,655113.6805215562 1794824.849108259,655113.563860482 1794824.562165918,655113.364543901 1794824.077988116,655113.1631166 1794823.594684604,655112.959582414 1794823.112264581,655112.8846788395 1794822.9368677158,655112.7539452178 1794822.630737234,655112.6154543567 1794822.3103202307,655112.546208926 1794822.150111729,655112.336377494 1794821.670397217,655112.124454917 1794821.191602831,655111.910445229 1794820.713737687,655111.694352505 1794820.236810881,655111.476180859 1794819.760831496,655111.255934444 1794819.285808591,655111.181077731 1794819.1261879639,655111.0336174545 1794818.8117512118,655110.8092341219 1794818.338668383,655110.5827887186 1794817.8665691114,655110.418003307 1794817.5268297624,655110.354285556 1794817.395462386,655110.1259770029 1794816.929940888,655110.1237289839 1794816.9253571737,655110.1218190717 1794816.9215054614,655109.891123392 1794816.456262427,655109.656473209 1794815.988187076,655109.5510408303 1794815.7801435709,655109.4197829026 1794815.5211400313,655109.2703052845 1794815.229349176,655109.181056978 1794815.055130186,655108.940299981 1794814.590166412,655108.8182039362 1794814.3568662112,655108.6975164955 1794814.126257561,655108.5358708798 1794813.8206397318,655108.452711143 1794813.663412466,655108.3250408027 1794813.4245580588,655108.2058885846 1794813.2016399377,655108.1376462225 1794813.0752965964,655107.957053519 1794812.740948769,655107.706210685 1794812.28134773,655107.6772019401 1794812.2287442433,655107.4533648567 1794811.8228455705,655107.1985208486 1794811.3654510204,655107.0063000246 1794811.0239657077,655106.941683513 1794810.909172788,655106.919977169 1794810.8710015025,655106.6828577387 1794810.454019559,655106.422048454 1794810,655106.1592606244 1794809.5471227546,655105.9106240155 1794809.1229079335,655105.894499253 1794809.095396445,655105.62776938 1794808.644829671,655105.359076084 1794808.195431011,655105.1420359869 1794807.8359942178,655105.0884244808 1794807.7472090207,655104.8158197225 1794807.3001722342,655104.5412669994 1794806.8543291618,655104.2647715387 1794806.409688292,655103.9863386045 1794805.9662580902,655103.7059734975 1794805.5240469985,655103.6628454948 1794805.456674405,655102.926594554 1794805.406804585,655102.349145231 1794805.370221589,655102.09922 1794805.35439,655101.27163 1794805.30558,655100.44383 1794805.26039,655099.61584 1794805.21881,655098.78768 1794805.18084,655097.95936 1794805.14648,655097.1309 1794805.11574,655096.30232 1794805.08862,655095.47362 1794805.06511,655094.64483 1794805.04521,655093.81596 1794805.02894,655092.98702 1794805.01628,655092.15804 1794805.00723,655091.32903 1794805.00181,655090.5 1794805,655089.67097 1794805.00181,655088.84196 1794805.00723,655088.01298 1794805.01628,655087.18404 1794805.02894,655086.35517 1794805.04521,655085.52638 1794805.06511,655084.69768 1794805.08862,655083.8691 1794805.11574,655083.04064 1794805.14648,655082.21232 1794805.18084,655081.38416 1794805.21881,655080.55617 1794805.26039,655079.72837 1794805.30558,655078.90078 1794805.35439,655078.07341 1794805.4068,655077.24627 1794805.46283,655076.41939 1794805.52246,655075.59277 1794805.58571,655074.76644 1794805.65255,655073.94041 1794805.72301,655073.11469 1794805.79706,655072.28931 1794805.87472,655071.46427 1794805.95598,655070.63959 1794806.04084,655070.41921 1794806.06449,655070.419209293 1794806.064489258,655070.419208566 1794806.064489336,655070.419209273 1794806.064490078,655069.81529 1794806.1293,655068.99139 1794806.22135,655068.16789 1794806.31699,655067.34482 1794806.41623,655066.5222 1794806.51906,655065.70002 1794806.62548,655064.87832 1794806.73548,655064.05711 1794806.84907,655063.25721 1794806.96327,655063.273914169 1794807.052320398,655063.27391605 1794807.052330425,655063.30204 1794807.20226,655063.312068282 1794807.255704256,655063.3120690807 1794807.2557085147,655063.3120701712 1794807.255714328,655063.32214 1794807.30938,655063.40559 1794807.76519,655063.42429 1794807.86734,655063.459708051 1794808.065669717,655063.459732467 1794808.065806441,655063.52401 1794808.42574,655063.62129 1794808.98456,655063.71613 1794809.54381,655063.80853 1794810.10347,655063.83463 1794810.26596,655063.870211657 1794810.487508056,655063.89848 1794810.66352,655063.94224 1794810.94374,655063.946316121 1794810.969841703,655063.946320344 1794810.969868747,655063.986 1794811.22396,655064.02853 1794811.50437,655064.034193555 1794811.541711108,655064.07106 1794811.78478,655064.15368 1794812.34596,655064.1887750614 1794812.591778927,655064.188775813 1794812.5917841916,655064.20398 1794812.69828,655064.23385 1794812.9075,655064.31157 1794813.46938,655064.338920152 1794813.673703841,655064.33892087 1794813.673709203,655064.34958 1794813.75334,655064.38683 1794814.0316,655064.42595 1794814.33379,655064.444986375 1794814.480893886,655064.444989116 1794814.480915062,655064.450676992 1794814.524868215,655064.45964 1794814.59413,655064.49482 1794814.87556,655064.53 1794815.15699,655064.5979 1794815.72014,655064.6169195662 1794815.8838989295,655064.6169202882 1794815.8839051463,655064.63062 1794816.00186,655064.64698 1794816.14272,655064.66334 1794816.28358,655064.69483 1794816.565445,655064.72632 1794816.84731,655064.738967757 1794816.965155836,655064.7389734247 1794816.9652086447,655064.7452354332 1794817.0235550879,655064.75959795 1794817.157378242,655064.759602639 1794817.157421933,655064.78685 1794817.4113,655064.84491 1794817.97555,655064.90051 1794818.54005,655064.95364 1794819.10479,655065.00431 1794819.66975,655065.008407892 1794819.717790791,655065.008408531 1794819.717798283,655065.05252 1794820.23493,655065.07888 1794820.56082,655065.0840685127 1794820.6249732715,655065.0840699108 1794820.6249905543,655065.084069913 1794820.624990581,655065.09825 1794820.80032,655065.1299 1794821.21397,655065.14152 1794821.3659,655065.18233 1794821.93166,655065.22066 1794822.49759,655065.222294043 1794822.523385637,655065.25652 1794823.06369,655065.26244 1794823.16404,655065.27328602 1794823.347925025,655065.273289374 1794823.347981894,655065.28992 1794823.62994,655065.32084 1794824.19633,655065.322711118 1794824.233593419,655065.34521 1794824.68166,655065.3472500001 1794824.72225,655065.3489173473 1794824.7554253058,655065.34929 1794824.76284,655065.37527 1794825.32948,655065.38862 1794825.65134,655065.39877 1794825.89622,655065.4127229034 1794826.2721327043,655065.4127229857 1794826.2721349227,655065.41981 1794826.46307,655065.43069 1794826.79544,655065.43837 1794827.02999,655065.447759342 1794827.36107523,655065.45445 1794827.597,655065.45979 1794827.81955,655065.46806 1794828.16406,655065.4792 1794828.73119,655065.48091 1794828.84321,655065.48786 1794829.29835,655065.4914 1794829.62298,655065.494045986 1794829.865182533,655065.49405 1794829.86555,655065.4940520141 1794829.8658584107,655065.4940520284 1794829.865860597,655065.49589 1794830.1473,655065.49776 1794830.43277,655065.499 1794831,655065.49776 1794831.56723,655065.496696934 1794831.730033438,655065.496696878 1794831.730041977,655065.4959 1794831.85208,655065.49405 1794832.13445,655065.494044595 1794832.134944979,655065.494044036 1794832.134996209,655065.49139 1794832.37804,655065.48786 1794832.70165,655065.48091 1794833.15679,655065.4792 1794833.26881,655065.46806 1794833.83594,655065.46 1794834.17174,655065.45445 1794834.403,655065.447760187 1794834.638894966,655065.447759247 1794834.638928099,655065.43837 1794834.97001,655065.43069 1794835.20456,655065.41981 1794835.53693,655065.4127229857 1794835.7278650773,655065.4127229034 1794835.7278672957,655065.39877 1794836.10378,655065.392131042 1794836.263959707,655065.392130402 1794836.263975128,655065.38926 1794836.33323,655065.37527 1794836.67052,655065.34929 1794837.23716,655065.34358 1794837.35096,655065.32084 1794837.80367,655065.28992 1794838.37006,655065.266 1794838.77557,655065.25652 1794838.93631,655065.222294043 1794839.476614363,655065.22066 1794839.50241,655065.18233 1794840.06834,655065.14667 1794840.56279,655065.144635983 1794840.590954222,655065.144635578 1794840.590959839,655065.14152 1794840.6341,655065.124 1794840.86316,655065.121862733 1794840.891091382,655065.121859497 1794840.891133678,655065.09825 1794841.19968,655065.08241 1794841.39559,655065.05252 1794841.76507,655065.008408531 1794842.282201718,655065.00431 1794842.33025,655064.95364 1794842.89521,655064.90051 1794843.45995,655064.84491 1794844.02445,655064.78685 1794844.5887,655064.759602639 1794844.842578067,655064.75959795 1794844.842621758,655064.7452354332 1794844.9764449121,655064.7389734247 1794845.0347913553,655064.738967757 1794845.034844164,655064.72632 1794845.15269,655064.69483 1794845.434555,655064.66334 1794845.71642,655064.61772 1794846.1092,655064.603209323 1794846.234144107,655064.603204534 1794846.234185337,655064.5979 1794846.27986,655064.5478882306 1794846.6946483343,655064.5467877984 1794846.7037751155,655064.5447409408 1794846.7207513726,655064.53 1794846.84301,655064.49482 1794847.12444,655064.45964 1794847.40587,655064.43155 1794847.62288,655064.38683 1794847.9684,655064.33709 1794848.33997,655064.31157 1794848.53062,655064.23385 1794849.0925,655064.20511 1794849.29383,655064.1587417726 1794849.6185879053,655064.1587396299 1794849.6186029138,655064.158737694 1794849.618616475,655064.15368 1794849.65404,655064.150911988 1794849.672844512,655064.150909442 1794849.672861804,655064.13489 1794849.78169,655064.1051768772 1794849.9834999659,655064.1051763323 1794849.9835036672,655064.07106 1794850.21522,655064.02092 1794850.54577,655064.002066444 1794850.670094412,655064.002063967 1794850.670110745,655063.986 1794850.77604,655063.94441 1794851.0424,655063.918953554 1794851.205392196,655063.918952739 1794851.205397417,655063.89848 1794851.33648,655063.8859090371 1794851.4147567684,655063.8847899882 1794851.421724853,655063.8839915509 1794851.4266965557,655063.84839 1794851.64838,655063.80853 1794851.89653,655063.71613 1794852.45619,655063.62129 1794853.01544,655063.52401 1794853.57426,655063.459732467 1794853.934193559,655063.459708051 1794853.934330283,655063.42429 1794854.13266,655063.40559 1794854.23481,655063.32214 1794854.69062,655063.272501619 1794854.955209324,655063.27249533 1794854.955242848,655063.21755 1794855.24812,655063.11053 1794855.80517,655063.051470814 1794856.105522444,655063.051461216 1794856.105571255,655063.00109 1794856.36174,655062.94113 1794856.65977,655062.920543874 1794856.762090027,655062.88921 1794856.91783,655062.79059 1794857.39723,655062.77491 1794857.47343,655062.737208965 1794857.652726331,655062.737192121 1794857.652806434,655062.65819 1794858.02852,655062.613800721 1794858.235128529,655062.613788544 1794858.23518521,655062.53904 1794858.5831,655062.50021 1794858.76009,655062.41748 1794859.13715,655062.2935 1794859.69067,655062.25800792 1794859.845951714,655062.257982037 1794859.846064954,655062.16711 1794860.24364,655062.0383 1794860.79605,655061.934224938 1794861.233774436,655061.90709 1794861.3479,655061.77347 1794861.89917,655061.725719063 1794862.092476523,655061.63744 1794862.44985,655061.552551529 1794862.787196122,655061.55254603 1794862.787217974,655061.49902 1794862.99993,655061.4681309408 1794863.1204445495,655061.468128027 1794863.1204559184,655061.468127958 1794863.120456188,655061.39828 1794863.39297,655061.35819 1794863.5494,655061.344150675 1794863.603209357,655061.334849321 1794863.63885921,655061.334846184 1794863.638871231,655061.31753 1794863.70524,655061.27002457 1794863.887280845,655061.270016328 1794863.88731243,655061.21497 1794864.09825,655061.06936 1794864.64648,655060.964543065 1794865.034262293,655060.96453596 1794865.03428858,655060.92135 1794865.19406,655060.890893486 1794865.304825931,655060.890881793 1794865.304868456,655060.81222 1794865.59095,655060.77096 1794865.74099,655060.7237043751 1794865.909950974,655060.7237039079 1794865.9099526443,655060.723700422 1794865.909965109,655060.69457 1794866.01412,655060.61818 1794866.28726,655060.608889977 1794866.319928052,655060.6088872004 1794866.3199378145,655060.6088787417 1794866.3199675581,655060.50431 1794866.68768,655060.46302 1794866.83286,655060.419479261 1794866.983461652,655060.30548 1794867.37777,655060.250909438 1794867.563489468,655060.250898757 1794867.563525817,655060.23801 1794867.60739,655060.14557 1794867.92199,655059.98328 1794868.46551,655059.84512 1794868.92096,655059.81862 1794869.00832,655059.796592342 1794869.079807029,655059.796586593 1794869.079825688,655059.79078 1794869.09867,655059.65159 1794869.5504,655059.51872 1794869.97505,655059.4822 1794870.09175,655059.44314 1794870.21471,655059.31045 1794870.63236,655059.234363907 1794870.868290115,655059.234361212 1794870.868298471,655059.17567 1794871.05029,655059.13635 1794871.17221,655058.95989 1794871.71129,655058.89277 1794871.91334,655058.78108 1794872.24961,655058.62944 1794872.69953,655058.59992 1794872.78713,655058.5709 1794872.87199,655058.41641 1794873.32386,655058.30668 1794873.64031,655058.23057 1794873.85978,655058.04239 1794874.39489,655057.975076395 1794874.583680348,655057.975071062 1794874.583695305,655057.89968 1794874.79514,655057.85188 1794874.92917,655057.65904 1794875.46262,655057.644857072 1794875.501324884,655057.644848514 1794875.5013482377,655057.6448338063 1794875.501388374,655057.48994 1794875.92409,655057.46387 1794875.99522,655057.44367 1794876.04961,655057.26638 1794876.52696,655057.154071602 1794876.825368049,655057.154054352 1794876.825413881,655057.10327 1794876.96035,655057.06658 1794877.05783,655056.86446 1794877.58783,655056.783612824 1794877.797070117,655056.783594005 1794877.797118823,655056.66002 1794878.11694,655056.45328 1794878.64516,655056.34876 1794878.90881,655056.3459398382 1794878.9159327664,655056.3454793321 1794878.9170958477,655056.3454793202 1794878.917095878,655056.34155 1794878.92702,655056.255346047 1794879.14445665,655056.24424 1794879.17247,655056.223304629 1794879.224615646,655056.2232937552 1794879.224642729,655056.2232895045 1794879.224653316,655056.14216 1794879.42673,655056.0329 1794879.69886,655055.90472 1794880.01414,655055.81927 1794880.22432,655055.76932 1794880.34565,655055.60334 1794880.74885,655055.41359 1794881.20414,655055.38513 1794881.27243,655055.344365369 1794881.369037272,655055.34436386 1794881.3690408485,655055.344363057 1794881.369042752,655055.32776 1794881.40839,655055.190391037 1794881.733989726,655055.190379857 1794881.734016225,655055.16463 1794881.79505,655055.05602 1794882.04937,655054.94186 1794882.3167,655054.873625641 1794882.474564382,655054.82115 1794882.59597,655054.71681 1794882.83738,655054.655983203 1794882.976439819,655054.655952451 1794882.976510124,655054.48949 1794883.35707,655054.25991 1794883.87576,655054.209292672 1794883.98878659,655054.02807 1794884.39345,655053.79397 1794884.91012,655053.65541 1794885.2124,655053.55761 1794885.42577,655053.51280369 1794885.522408803,655053.5127904596 1794885.5224373376,655053.5127715048 1794885.5224782187,655053.4059 1794885.75298,655053.381442779 1794885.805725295,655053.31901 1794885.94037,655053.19155 1794886.21216,655053.07817 1794886.45394,655052.83509 1794886.96644,655052.777779484 1794887.085918715,655052.777764684 1794887.08594957,655052.71074 1794887.22568,655052.650255 1794887.35178,655052.58977 1794887.47788,655052.4022 1794887.86459,655052.370453229 1794887.930052827,655052.370450409 1794887.930058642,655052.34223 1794887.98825,655052.09246 1794888.49753,655051.84047 1794889.00572,655051.74509 1794889.19597,655051.58626 1794889.5128,655051.515035 1794889.653345,655051.44381 1794889.79389,655051.32985 1794890.01876,655051.266858405 1794890.141725381,655051.2668536211 1794890.1417347193,655051.2668207069 1794890.14179897,655051.07123 1794890.52361,655050.9453 1794890.76681,655050.81041 1794891.02732,655050.74095 1794891.16005,655050.54739 1794891.52989,655050.3852 1794891.83654,655050.379201201 1794891.847881871,655050.379185625 1794891.84791132,655050.28219 1794892.0313,655050.0148 1794892.53156,655049.8555543 1794892.826375432,655049.74522 1794893.03064,655049.47348 1794893.52854,655049.353322092 1794893.7464271,655049.353271489 1794893.746518862,655049.19956 1794894.02525,655049.06152 1794894.273005,655048.92348 1794894.52076,655048.64524 1794895.01506,655048.578914769 1794895.13169864,655048.36485 1794895.50815,655048.1587822422 1794895.8668632421,655048.1587796728 1794895.866867715,655048.158777906 1794895.866870791,655048.0823 1794896,655047.999301929 1794896.143039602,655047.79762 1794896.49062,655047.687033193 1794896.679288866,655047.687015195 1794896.679319573,655047.61608 1794896.80034,655047.528594871 1794896.949610619,655047.51079 1794896.97999,655047.464113662 1794897.058835472,655047.22183 1794897.4681,655046.989587103 1794897.856541166,655046.989562195 1794897.856582826,655046.93075 1794897.95495,655046.881907269 1794898.035835935,655046.881888719 1794898.035866655,655046.8615191779 1794898.0695996035,655046.8310954794 1794898.119982726,655046.7375748762 1794898.2748573867,655046.63754 1794898.44052,655046.48988 1794898.682665,655046.34222 1794898.92481,655046.095021096 1794899.326239146,655046.04479 1794899.40781,655046.00910456 1794899.465195723,655046.009086017 1794899.465225543,655045.74525 1794899.8895,655045.591315737 1794900.134662884,655045.591310097 1794900.134671866,655045.44362 1794900.36989,655045.29668 1794900.60165,655045.13989 1794900.84895,655044.92172 1794901.18975,655044.83407 1794901.32668,655044.6399 1794901.62711,655044.52617 1794901.80307,655044.2162 1794902.27812,655044.20566808 1794902.294107903,655043.90416 1794902.75181,655043.67658954 1794903.094000433,655043.65062 1794903.13305,655043.607696489 1794903.197594715,655043.607691071 1794903.197602862,655043.59005 1794903.22413,655043.27388 1794903.69508,655043.182982797 1794903.829212498,655043.18296159 1794903.829243792,655043.12398 1794903.91628,655042.95567 1794904.16464,655042.858643332 1794904.306473376,655042.6354 1794904.63281,655042.519005 1794904.80138,655042.40261 1794904.96995,655042.3131 1794905.09958,655042.309888575 1794905.104189664,655042.309887603 1794905.104191058,655042.29677 1794905.12302,655041.98877 1794905.56494,655041.811140152 1794905.817438132,655041.811130379 1794905.817452025,655041.6624 1794906.02887,655041.33402 1794906.49138,655041.00362 1794906.95246,655040.906706686 1794907.086456844,655040.906696145 1794907.086471419,655040.85829 1794907.1534,655040.67121 1794907.41208,655040.3368 1794907.87025,655040.00039 1794908.32696,655039.88759 1794908.47871,655039.845903376 1794908.534791713,655039.845888676 1794908.534811489,655039.662 1794908.7822,655039.43508 1794909.0847,655039.32162 1794909.23595,655038.97926 1794909.68822,655038.63493 1794910.13899,655038.28864 1794910.58825,655037.96942 1794910.99868,655037.9404 1794911.03599,655037.878013217 1794911.115482377,655037.862942246 1794911.1346856,655037.5902 1794911.48221,655037.43655 1794911.67624,655037.23806 1794911.9269,655036.88398 1794912.37005,655036.52797 1794912.81165,655036.23449 1794913.17246,655036.17004 1794913.25169,655036.08856 1794913.35098,655036.053529037 1794913.393666278,655036.053523696 1794913.393672786,655035.8102 1794913.69017,655035.66946 1794913.86014,655035.44844 1794914.12707,655035.08478 1794914.56239,655034.71922 1794914.99612,655034.35178 1794915.42825,655033.98245 1794915.85877,655033.61125 1794916.28767,655033.26973 1794916.67881,655033.23817 1794916.71496,655033.22541 1794916.72945,655032.925448543 1794917.069986634,655032.925366566 1794917.0700797,655032.86324 1794917.14061,655032.832050667 1794917.175708968,655032.48646 1794917.56462,655032.10783 1794917.98698,655031.811538034 1794918.31460118,655031.72736 1794918.40768,655031.48473 1794918.67362,655031.34505 1794918.82673,655031.3060046234 1794918.8691554633,655031.3060040202 1794918.8691561187,655031.19615 1794918.98852,655030.96093 1794919.2441,655030.805190924 1794919.411834778,655030.805179729 1794919.411846836,655030.76346 1794919.45678,655030.668585138 1794919.558968804,655030.6685731211 1794919.5589817467,655030.6685504183 1794919.5590061992,655030.57498 1794919.65979,655030.45563 1794919.78722,655030.18723 1794920.07379,655029.935843107 1794920.339851238,655029.935827718 1794920.339867526,655029.79767 1794920.48609,655029.690585412 1794920.598444677,655029.69057223 1794920.598458507,655029.40632 1794920.8967,655029.103975442 1794921.211149107,655029.103963976 1794921.211161032,655029.01317 1794921.30559,655028.61825 1794921.71276,655028.22156 1794922.1182,655027.8231 1794922.52191,655027.6984821834 1794922.6470757592,655027.6984801579 1794922.6470777935,655027.63206 1794922.71379,655027.623980695 1794922.721903034,655027.623977868 1794922.721905873,655027.6085975022 1794922.7373504466,655027.600923338 1794922.7450566483,655027.6009233382 1794922.7450566478,655027.600923338 1794922.745056648,655027.5236044631 1794922.9239441357,655027.507067934 1794922.9617497823,655027.428459203 1794923.141464274,655027.41869 1794923.1638,655027.31273 1794923.4032,655027.258875829 1794923.523444623,655027.205722615 1794923.642131157,655027.135351634 1794923.797443221,655027.09768 1794923.88059,655027.05290193 1794923.978277532,655026.98859043 1794924.118584253,655026.95170462 1794924.198138804,655026.87847 1794924.3561,655026.783004833 1794924.559654821,655026.78081679 1794924.564320492,655026.7673087273 1794924.593124394,655026.7534780905 1794924.6222844778,655026.751448281 1794924.626564065,655026.65512 1794924.82967,655026.576449341 1794924.993674319,655026.5418943594 1794925.0657154443,655026.4843346254 1794925.18438679,655026.427642778 1794925.301268808,655026.3353305776 1794925.4894935286,655026.312364492 1794925.5363214132,655026.2539693535 1794925.6540866503,655026.196061696 1794925.770868787,655026.175657337 1794925.811570983,655026.07874 1794926.00491,655025.986151402 1794926.187599391,655025.986150208 1794926.187601747,655025.960391451 1794926.238429985,655025.954804342 1794926.249336416,655025.8410284892 1794926.471434907,655025.826434095 1794926.4996204413,655025.79509181 1794926.560150467,655025.72065 1794926.70392,655025.59926 1794926.93587,655025.47686 1794927.16729,655025.432536557 1794927.250208812,655025.353444292 1794927.398180032,655025.299384285 1794927.498266279,655025.22903 1794927.62853,655025.10361 1794927.85833,655024.9918729863 1794928.0609378489,655024.9771824284 1794928.087577215,655024.8497604243 1794928.31627449,655024.7213417564 1794928.5444136062,655024.6661060487 1794928.6415473279,655024.618962922 1794928.724449993,655024.59193 1794928.77199,655024.566901658 1794928.815557855,655024.566901261 1794928.815558546,655024.461524227 1794928.999,655024.343701396 1794929.202050801,655024.33013 1794929.22544,655024.273804455 1794929.321539772,655024.197749627 1794929.451301778,655024.06438469 1794929.676585165,655023.930038042 1794929.901284495,655023.84747718 1794930.038012279,655023.79471 1794930.1254,655023.769326336 1794930.167025113,655023.6871268596 1794930.3018216947,655023.6584098613 1794930.348913883,655023.5855530619 1794930.4672252182,655023.5211335 1794930.571835419,655023.509061253 1794930.591249172,655023.38289 1794930.79416,655023.24367 1794931.01587,655023.10349 1794931.23698,655022.96234 1794931.45747,655022.954157534 1794931.470129762,655022.820234077 1794931.677342101,655022.6903730781 1794931.8763585999,655022.6771693509 1794931.8965937712,655022.6703008367 1794931.9070203165,655022.591673741 1794932.026377857,655022.53315 1794932.11522,655022.38818 1794932.33321,655022.377112135 1794932.349695378,655022.242254353 1794932.550574209,655022.095384948 1794932.767295662,655021.9877729994 1794932.9246061486,655021.9475713173 1794932.9833742157,655021.9095818017 1794933.038391779,655021.889773849 1794933.067078254,655021.79882 1794933.19881,655021.64912 1794933.41359,655021.648117466 1794933.4150150982,655021.5306302255 1794933.5820275808,655021.49849331 1794933.627711402,655021.370301583 1794933.808261474,655021.34693 1794933.84118,655021.19444 1794934.05398,655021.04102 1794934.26612,655020.88668 1794934.47758,655020.73141 1794934.68837,655020.57523 1794934.89848,655020.462242965 1794935.049095818,655020.418130603 1794935.107901389,655020.3831781971 1794935.1540748638,655020.29032144 1794935.2767422176,655020.290320262 1794935.276743774,655020.2903191724 1794935.2767452132,655020.2902967231 1794935.2767748698,655020.26012 1794935.31664,655020.1012 1794935.52469,655020.006288114 1794935.6478207,655019.941374425 1794935.732036802,655019.901815221 1794935.782898607,655019.78065 1794935.93869,655019.61902 1794936.14464,655019.45649 1794936.34988,655019.29307 1794936.55441,655019.156498211 1794936.723813544,655019.1562699191 1794936.724096722,655019.1287576874 1794936.758223463,655019.1035638961 1794936.7891967937,655019.088927094 1794936.807191327,655018.96356 1794936.96132,655018.79748 1794937.16369,655018.632132339 1794937.363380699,655018.6310293071 1794937.3647128504,655018.6305099244 1794937.365340119,655018.6302654473 1794937.3656327697,655018.62987728 1794937.366097425,655018.46267 1794937.56626,655018.29395 1794937.76644,655018.145305048 1794937.941248357,655018.124357936 1794937.9658829,655018.111277456 1794937.981130689,655017.9539 1794938.16459,655017.877081945 1794938.253343482,655017.782574813 1794938.36254174,655017.659209329 1794938.503833751,655017.61039 1794938.55975,655017.558028882 1794938.619191443,655017.4769558443 1794938.7112316801,655017.4373432474 1794938.756202893,655017.2754624585 1794938.9383734516,655017.263442504 1794938.95189996,655017.193680874 1794939.02971885,655017.193680448 1794939.029719325,655017.1936708008 1794939.0297300862,655017.08869 1794939.14684,655016.91309 1794939.34101,655016.806167776 1794939.458203302,655016.736640209 1794939.534412457,655016.6036729733 1794939.678886667,655016.5593505661 1794939.727044732,655016.3812220972 1794939.918901604,655016.3152360705 1794939.9893541676,655016.272957484 1794940.034494555,655016.20226 1794940.10998,655016.154726293 1794940.160286948,655016.022462261 1794940.300274542,655015.841837724 1794940.489783347,655015.66038802 1794940.678502228,655015.654808336 1794940.684254991,655015.654807747 1794940.6842555976,655015.6548069621 1794940.684256407,655015.654803364 1794940.684260117,655015.47812 1794940.86643,655015.29503 1794941.05356,655015.194797095 1794941.155106381,655015.1577632194 1794941.192628229,655015.1111225374 1794941.2398834673,655015.0782749417 1794941.2728747013,655015.020683292 1794941.330718193,655014.92641 1794941.42541,655014.8317182435 1794941.5196832407,655014.8295602322 1794941.5218318563,655014.740883467 1794941.610122538,655014.656106863 1794941.693796619,655014.55456 1794941.79403,655014.36743 1794941.97712,655014.185284124 1794942.153780081,655014.179502228 1794942.159388021,655013.990783347 1794942.340837724,655013.801274542 1794942.521462261,655013.661300148 1794942.653713821,655013.61098 1794942.70126,655013.53549917 1794942.771953162,655013.4227590051 1794942.877545849,655013.4199016037 1794942.8802220973,655013.228044732 1794943.0583505663,655013.1317285944 1794943.1469953875,655013.035412457 1794943.235640209,655012.9665015519 1794943.2985093952,655012.9591898319 1794943.305180065,655012.84201 1794943.41209,655012.64784 1794943.58769,655012.53071461 1794943.692684675,655012.45289996 1794943.762442504,655012.257202893 1794943.936343248,655012.154866847 1794944.02648528,655012.120191288 1794944.057029019,655012.06075 1794944.10939,655012.004823214 1794944.158218529,655011.863541739 1794944.281574814,655011.754347434 1794944.376078525,655011.66559 1794944.4529,655011.482137811 1794944.610271346,655011.4668829 1794944.623357936,655011.442253412 1794944.64430075,655011.442253041 1794944.644301065,655011.44221729 1794944.644331465,655011.26744 1794944.79295,655011.06726 1794944.96167,655010.867111928 1794945.128865165,655010.866340119 1794945.129509925,655010.864334172 1794945.131170864,655010.66469 1794945.29648,655010.46232 1794945.46256,655010.308265696 1794945.587866603,655010.259223463 1794945.627757688,655010.22478358 1794945.655522368,655010.05541 1794945.79207,655009.85088 1794945.95549,655009.64564 1794946.11802,655009.43969 1794946.27965,655009.283899072 1794946.400814859,655009.233036802 1794946.440374425,655009.148811028 1794946.505295568,655009.02569 1794946.6002,655008.81764 1794946.75912,655008.777735534 1794946.7893265,655008.6513601702 1794946.8849901466,655008.6089013892 1794946.917130603,655008.5948290633 1794946.9276868056,655008.550092661 1794946.961245333,655008.550090752 1794946.961246765,655008.5500900113 1794946.96124732,655008.5500830762 1794946.9612525224,655008.39948 1794947.07423,655008.18937 1794947.23041,655007.97858 1794947.38568,655007.76712 1794947.54002,655007.55498 1794947.69344,655007.34218 1794947.84593,655007.309247768 1794947.869311314,655007.309242581 1794947.869314997,655007.309242163 1794947.869315294,655007.128711402 1794947.99749331,655006.916033237 1794948.147104706,655006.91459 1794948.14812,655006.69981 1794948.29782,655006.568092444 1794948.388764051,655006.484374216 1794948.446571318,655006.268295662 1794948.594384948,655006.051574208 1794948.741254354,655005.850698226 1794948.876110223,655005.850697956 1794948.8761104038,655005.8506799078 1794948.8761225203,655005.83421 1794948.88718,655005.61622 1794949.03215,655005.527388419 1794949.090666783,655005.4688119999 1794949.1292541542,655005.3975937712 1794949.176169351,655005.251425986 1794949.2715458383,655005.178342101 1794949.319234077,655005.1339346452 1794949.3479350703,655004.9711025327 1794949.4531751324,655004.95847 1794949.46134,655004.73798 1794949.60249,655004.51687 1794949.74267,655004.29516 1794949.88189,655004.092269994 1794950.008048305,655004.072835419 1794950.0201335,655003.961374651 1794950.0887716804,655003.8499138829 1794950.1574098612,655003.8282973068 1794950.1705917327,655003.667993845 1794950.268345404,655003.6264 1794950.29371,655003.539020035 1794950.346472497,655003.4063797841 1794950.4265651677,655003.4022844946 1794950.4290380422,655003.3791051413 1794950.442896866,655003.177585165 1794950.56338469,655002.952301778 1794950.696749627,655002.822406691 1794950.772882455,655002.72644 1794950.82913,655002.703080284 1794950.842684289,655002.5 1794950.960524228,655002.382630942 1794951.0279463006,655002.3430625566 1794951.0506761633,655002.316547524 1794951.065907593,655002.27299 1794951.09093,655002.225459315 1794951.117957621,655002.045413606 1794951.220341757,655001.81727449 1794951.348760425,655001.588577215 1794951.476182429,655001.561930605 1794951.490876982,655001.35933 1794951.60261,655001.12953 1794951.72803,655000.999263924 1794951.798385557,655000.9681547626 1794951.8151886796,655000.899180031 1794951.8524442923,655000.8180309919 1794951.8958193574,655000.751209723 1794951.93153607,655000.66829 1794951.97586,655000.43687 1794952.09826,655000.20492 1794952.21965,655000.061142198 1794952.294096092,654999.972434907 1794952.34002849,654999.739429984 1794952.459391452,654999.688592302 1794952.485154995,654999.50591 1794952.57774,654999.312568248 1794952.674658708,654999.271868787 1794952.695061697,654999.0510374929 1794952.8045632287,654999.0373214132 1794952.811364492,654998.8022688073 1794952.926642778,654998.5667154443 1794953.0408943594,654998.5186618813 1794953.0639435549,654998.494677841 1794953.075447652,654998.33067 1794953.15412,654998.127574018 1794953.250443561,654998.094124394 1794953.266308728,654998.060648212 1794953.282007933,654997.8571 1794953.37747,654997.699120002 1794953.450713337,654997.619584252 1794953.48759043,654997.479326065 1794953.551879684,654997.38159 1794953.59668,654997.298409214 1794953.634367043,654997.143131157 1794953.704722615,654997.024469303 1794953.757864776,654997.024460702 1794953.757868628,654996.9042 1794953.81173,654996.6648 1794953.91769,654996.642458518 1794953.927461721,654996.4372687783 1794954.017213554,654996.4249441355 1794954.0226044632,654996.184631383 1794954.1264726091,654995.9438677094 1794954.2292912072,654995.9314682846 1794954.2345225548,654995.923919243 1794954.237707514,654995.70266 1794954.33106,654995.482509467 1794954.422809887,654995.461005942 1794954.431771951,654995.406974032 1794954.454014721,654995.21892 1794954.53143,654994.9764 1794954.63003,654994.921193853 1794954.652194263,654994.733446251 1794954.727573173,654994.490073604 1794954.824054079,654994.425913636 1794954.849165807,654994.23668 1794955.02181,654993.91306 1794955.31448,654993.58817 1794955.60574,654993.262 1794955.89557,654992.93458 1794956.18398,654992.6059 1794956.47096,654992.27598 1794956.7565,654991.9448 1794957.0406,654991.6124 1794957.32325,654991.27876 1794957.60444,654990.9439 1794957.88418,654990.60782 1794958.16246,654990.27053 1794958.43926,654989.93204 1794958.7146,654989.59235 1794958.98845,654989.25146 1794959.26082,654988.90939 1794959.53169,654988.56615 1794959.80108,654988.22173 1794960.06896,654987.87614 1794960.33533,654987.5294 1794960.6002,654987.1815 1794960.86355,654986.83246 1794961.12538,654986.48228 1794961.38569,654986.13096 1794961.64446,654985.77853 1794961.9017,654985.42497 1794962.1574,654985.0703 1794962.41155,654984.71452 1794962.66416,654984.35764 1794962.9152,654983.99968 1794963.16469,654983.64062 1794963.41262,654983.28049 1794963.65897,654982.91929 1794963.90376,654982.55702 1794964.14696,654982.1937 1794964.38858,654981.82932 1794964.62862,654981.4639 1794964.86706,654981.09745 1794965.1039,654980.72996 1794965.33914,654980.36145 1794965.57278,654979.99193 1794965.80481,654979.62139 1794966.03522,654979.24986 1794966.26402,654978.87733 1794966.49119,654978.50381 1794966.71673,654978.12931 1794966.94064,654977.75384 1794967.16292,654977.3774 1794967.38355,654977 1794967.60254,654976.62165 1794967.81988,654976.24236 1794968.03557,654975.86212 1794968.2496,654975.48096 1794968.46197,654975.09888 1794968.67268,654974.71588 1794968.88171,654974.33197 1794969.08907,654973.94716 1794969.29476,654973.56145 1794969.49876,654973.17486 1794969.70108,654972.78739 1794969.90171,654972.39905 1794970.10065,654972.00984 1794970.29789,654971.61978 1794970.49344,654971.22887 1794970.68727,654970.83711 1794970.8794,654970.44453 1794971.06982,654970.05111 1794971.25853,654969.65687 1794971.44551,654969.26183 1794971.63078,654968.86597 1794971.81432,654968.46932 1794971.99613,654968.07189 1794972.1762,654967.67366 1794972.35455,654967.27467 1794972.53115,654966.87491 1794972.70601,654966.47439 1794972.87912,654966.07311 1794973.05049,654965.6711 1794973.2201,654965.26834 1794973.38795,654964.86486 1794973.55405,654964.46066 1794973.71839,654964.05574 1794973.88096,654963.65012 1794974.04176,654963.2438 1794974.20079,654962.83679 1794974.35804,654962.4291 1794974.51352,654962.02074 1794974.66722,654961.61171 1794974.81913,654961.20201 1794974.96926,654960.79167 1794975.1176,654960.38069 1794975.26415,654959.96906 1794975.4089,654959.55682 1794975.55186,654959.14395 1794975.69301,654958.73047 1794975.83237,654958.31638 1794975.96991,654957.9017 1794976.10565,654957.48643 1794976.23958,654957.07058 1794976.3717,654956.65416 1794976.50199,654956.23717 1794976.63048,654955.81963 1794976.75714,654955.40153 1794976.88197,654954.9829 1794977.00499,654954.56374 1794977.12617,654954.14404 1794977.24552,654953.72384 1794977.36305,654953.30312 1794977.47873,654952.8819 1794977.59258,654952.46019 1794977.70459,654952.038 1794977.81476,654951.61533 1794977.92309,654951.19219 1794978.02957,654950.76859 1794978.13421,654950.5706902393 1794978.182176366,654950.7936282847 1794978.2891100103,654950.837668383 1794978.310234122,654950.8937642276 1794978.3368403986,654951.3107512118 1794978.5346174545,654951.7848085911 1794978.7569344444,654952.0974540133 1794978.9018938404,654952.259831496 1794978.977180859,654952.735810881 1794979.195352505,654953.212737686 1794979.411445229,654953.690602831 1794979.625454917,654953.7435834646 1794979.648905051,654954.1693972169 1794979.8373774944,654954.4542349889 1794979.9619680915,654954.649111729 1794980.047208926,654954.9413585624 1794980.1735240568,654955.1297372339 1794980.2549452179,654955.2902463475 1794980.323490949,654955.611264581 1794980.460582414,654955.8896933922 1794980.57805221,654956.0936846036 1794980.6641166005,654956.5769881161 1794980.8655439015,654956.9552711231 1794981.0212678425,654957.061165918 1794981.064860482,654957.54620879 1794981.262062548,654958.0321074987 1794981.4571463447,654958.518852793 1794981.650108158,654959.006435406 1794981.840944314,654959.4216111044 1794982.0013554455,654959.4948460545 1794982.0296511797,654959.9840754402 1794982.2162251624,654960.4741142489 1794982.4006627097,654960.9649531506 1794982.5829603106,654961.235700283 1794982.6821736689,654961.456582801 1794982.763114494,654961.8554273393 1794982.9072973994,654961.9489938396 1794982.9411218308,654962.442176892 1794983.116978931,654962.936122569 1794983.290682447,654963.2398960856 1794983.3960219212,654963.4308214651 1794983.4622290719,654963.6340411718 1794983.5317076782,654963.926264164 1794983.631615539,654964.1875009018 1794983.7196583294,654964.4224412313 1794983.7988386247,654964.7018673235 1794983.891655919,654964.919343222 1794983.963895144,654965.416960675 1794984.126781955,654965.915284117 1794984.287495957,654966.41430406 1794984.44603409,654966.914011003 1794984.602393335,654966.9453201557 1794984.6120402443,654967.4143954333 1794984.7565707155,654967.6649216285 1794984.832567006,654967.915447824 1794984.908563296,654968.417158636 1794985.058368184,654968.919518316 1794985.205982526,654969.0588522524 1794985.2462650696,654969.4225173019 1794985.3514035125,654969.9261460161 1794985.4946283745,654970.32589975 1794985.6064296681,654970.430394871 1794985.635654385,654970.539278824 1794985.6655949145,654970.935254265 1794985.7744788595,654971.4407145877 1794985.9110991547,654971.9467662153 1794986.0455126697,654972.1693943818 1794986.103606706,654972.453399513 1794986.177716845,654972.960604837 1794986.307709165,654973.468372528 1794986.435487153,654973.9766929208 1794986.5610483775,654974.4855563373 1794986.6843904478,654974.8008149961 1794986.7593503047,654974.994953089 1794986.805511015,654975.504873479 1794986.924407774,654975.911490657 1794987.017348832,654976.0153077974 1794987.041078461,654976.1058505969 1794987.0613586593,654976.526246328 1794987.155520854,654976.7427348611 1794987.2030199277,654977.0376793412 1794987.2677327755,654977.3732561988 1794987.3398273871,654977.549597102 1794987.377712088,654978.017477503 1794987.4760967554,654978.0619898632 1794987.485456698,654978.5748478699 1794987.5909645546,654979.088161358 1794987.6942336485,654979.6019205548 1794987.7952620138,654980.0614740218 1794987.8835501263,654980.116115679 1794987.894047727,654980.1402509745 1794987.8985754256,654980.6307369409 1794987.9905889076,654981.145774543 1794988.0848837176,654981.66121868 1794988.1769303614,654982.1770595381 1794988.2667270869,654982.6932872967 1794988.3542721844,654982.9869635922 1794988.402758337,654983.209892128 1794988.439563987,654983.726864195 1794988.522600871,654984.2053209462 1794988.5973113282,654984.2441936572 1794988.603381256,654984.7618706644 1794988.6819036035,654985.279885361 1794988.7581664184,654985.7636922336 1794988.8272377227,654985.798227885 1794988.832168249,654985.8454946755 1794988.8387060382,654986.3168883671 1794988.9039076865,654986.7763407368 1794988.9654157762,654986.835856934 1794988.973383365,654986.9009608728 1794988.9818100058,654987.3551237036 1794989.0405939615,654987.8746787914 1794989.1055381969,654988.394512305 1794989.1682148345,654988.7346624768 1794989.2077219654,654988.914614348 1794989.228622681,654989.2055835992 1794989.2611315618,654989.4349750177 1794989.2867605868,654989.6594003702 1794989.310843785,654989.955584408 1794989.342627444,654990.476432607 1794989.39622219,654990.997509698 1794989.447543804,654991.0713038138 1794989.4544869168,654991.5188057603 1794989.4965913089,654992.0403108703 1794989.543363771,654992.5620150985 1794989.5878602997,654993.0179093163 1794989.6247408937,654993.083908513 1794989.630080048,654993.605981177 1794989.670022212,654994.128223151 1794989.707686031,654994.3894238215 1794989.72537841,654994.6506244923 1794989.7430707887,654995.1731752558 1794989.7761758107,654995.6958654923 1794989.8070004669,654996.2186852508 1794989.8355441706,654996.2547228772 1794989.8373539937,654996.31574 1794989.69786,654996.51 1794989.25897,654996.70618 1794988.82093,654996.90426 1794988.38374,654997.10425 1794987.94743,654997.30614 1794987.51199,654997.50993 1794987.07744,654997.71562 1794986.64378,654997.92319 1794986.21102,654998.13265 1794985.77917,654998.344 1794985.34824,654998.55722 1794984.91824,654998.77232 1794984.48917,654998.98928 1794984.06105,654999.20811 1794983.63387,654999.42881 1794983.20765,654999.65136 1794982.7824,654999.87576 1794982.35813,655000.10202 1794981.93484,655000.33012 1794981.51254,655000.56006 1794981.09124,655000.79183 1794980.67094,655001.02544 1794980.25166,655001.26087 1794979.83341,655001.49813 1794979.41618,655001.73721 1794979,655001.97809 1794978.58486,655002.22079 1794978.17078,655002.46529 1794977.75776,655002.7116 1794977.34581,655002.95969 1794976.93494,655003.20958 1794976.52516,655003.46126 1794976.11647,655003.71471 1794975.70888,655003.96994 1794975.3024,655004.22694 1794974.89704,655004.48571 1794974.49281,655004.74624 1794974.08971,655005.00852 1794973.68774,655005.27256 1794973.28693,655005.53834 1794972.88727,655005.80587 1794972.48878,655006.07513 1794972.09146,655006.34612 1794971.69531,655006.61884 1794971.30036,655006.89328 1794970.90659,655007.16943 1794970.51403,655007.44729 1794970.12267,655007.72686 1794969.73254,655008.00813 1794969.34362,655008.29109 1794968.95594,655008.57575 1794968.56949,655008.86208 1794968.18429,655009.15009 1794967.80035,655009.43978 1794967.41766,655009.73113 1794967.03624,655010.02415 1794966.6561,655010.31882 1794966.27724,655010.61514 1794965.89967,655010.9131 1794965.52339,655011.21271 1794965.14842,655011.51394 1794964.77476,655011.81681 1794964.40241,655012.1213 1794964.0314,655012.4274 1794963.66171,655012.73511 1794963.29336,655013.04443 1794962.92636,655013.35534 1794962.56071,655013.66785 1794962.19643,655013.98195 1794961.83351,655014.29762 1794961.47196,655014.61487 1794961.11179,655014.93369 1794960.75302,655015.25407 1794960.39563,655015.57601 1794960.03965,655015.89949 1794959.68508,655016.22452 1794959.33192,655016.55109 1794958.98018,655016.87919 1794958.62987,655017.20882 1794958.281,655017.53996 1794957.93356,655017.87262 1794957.58758,655018.20679 1794957.24305,655018.54245 1794956.89998,655018.87961 1794956.55838,655019.21825 1794956.21825,655019.55838 1794955.87961,655019.89998 1794955.54245,655020.24305 1794955.20679,655020.58758 1794954.87262,655020.93356 1794954.53996,655021.281 1794954.20882,655021.62987 1794953.87919,655021.98018 1794953.55109,655022.33192 1794953.22452,655022.68508 1794952.89949,655023.03965 1794952.57601,655023.39563 1794952.25407,655023.75302 1794951.93369,655024.11179 1794951.61487,655024.47196 1794951.29762,655024.83351 1794950.98195,655025.19643 1794950.66785,655025.56071 1794950.35534,655025.92636 1794950.04443,655026.29336 1794949.73511,655026.66171 1794949.4274,655027.0314 1794949.1213,655027.40241 1794948.81681,655027.77476 1794948.51394,655028.14842 1794948.21271,655028.52339 1794947.9131,655028.89967 1794947.61514,655029.27724 1794947.31882,655029.6561 1794947.02415,655030.03624 1794946.73113,655030.41766 1794946.43978,655030.80035 1794946.15009,655031.18429 1794945.86208,655031.56949 1794945.57575,655031.95594 1794945.29109,655032.34362 1794945.00813,655032.73254 1794944.72686,655033.12267 1794944.44729,655033.51403 1794944.16943,655033.90659 1794943.89328,655034.30036 1794943.61884,655034.69531 1794943.34612,655035.09146 1794943.07513,655035.48878 1794942.80587,655035.88727 1794942.53834,655036.28693 1794942.27256,655036.68774 1794942.00852,655037.08971 1794941.74624,655037.49281 1794941.48571,655037.89704 1794941.22694,655038.3024 1794940.96994,655038.70888 1794940.71471,655039.11647 1794940.46126,655039.52516 1794940.20958,655039.93494 1794939.95969,655040.34581 1794939.7116,655040.75776 1794939.46529,655041.17078 1794939.22079,655041.58486 1794938.97809,655042 1794938.73721,655042.41618 1794938.49813,655042.83341 1794938.26087,655043.25166 1794938.02544,655043.67094 1794937.79183,655044.09124 1794937.56006,655044.51254 1794937.33012,655044.93484 1794937.10202,655045.35813 1794936.87576,655045.7824 1794936.65136,655046.20765 1794936.42881,655046.63387 1794936.20811,655047.06105 1794935.98928,655047.48917 1794935.77232,655047.91824 1794935.55722,655048.34824 1794935.344,655048.77917 1794935.13265,655049.21102 1794934.92319,655049.64378 1794934.71562,655050.07744 1794934.50993,655050.51199 1794934.30614,655050.94743 1794934.10425,655051.38374 1794933.90426,655051.82093 1794933.70618,655052.25897 1794933.51,655052.69786 1794933.31574,655053.1376 1794933.12339,655053.57818 1794932.93297,655054.01958 1794932.74447,655054.46179 1794932.55789,655054.90482 1794932.37325,655055.34865 1794932.19054,655055.79327 1794932.00978,655056.23868 1794931.83095,655056.68487 1794931.65407,655057.13182 1794931.47913,655057.57953 1794931.30615,655058.02799 1794931.13513,655058.47719 1794930.96606,655058.92712 1794930.79895,655059.37778 1794930.63381,655059.82916 1794930.47064,655060.28125 1794930.30944,655060.73403 1794930.15021,655061.1875 1794929.99296,655061.64166 1794929.83769,655062.09649 1794929.6844,655062.55198 1794929.5331,655063.00813 1794929.38378,655063.46493 1794929.23646,655063.92236 1794929.09114,655064.38043 1794928.94781,655064.83911 1794928.80648,655065.29841 1794928.66715,655065.75831 1794928.52983,655066.21881 1794928.39452,655066.67989 1794928.26121,655067.14155 1794928.12992,655067.60378 1794928.00065,655068.06657 1794927.87339,655068.52991 1794927.74816,655068.99379 1794927.62495,655069.4582 1794927.50376,655069.92314 1794927.3846,655070.38859 1794927.26747,655070.85455 1794927.15237,655071.32101 1794927.03931,655071.78796 1794926.92828,655072.25538 1794926.81929,655072.72328 1794926.71234,655073.19164 1794926.60744,655073.66046 1794926.50458,655074.12971 1794926.40376,655074.59941 1794926.305,655075.06953 1794926.20828,655075.54006 1794926.11362,655076.01101 1794926.02101,655076.48236 1794925.93046,655076.95409 1794925.84196,655077.42621 1794925.75552,655077.8987 1794925.67115,655078.37155 1794925.58883,655078.84476 1794925.50858,655079.31832 1794925.4304,655079.79221 1794925.35428,655080.26643 1794925.28023,655080.74096 1794925.20826,655081.21581 1794925.13835,655081.69096 1794925.07051,655082.1664 1794925.00475,655082.64212 1794924.94107,655083.11811 1794924.87946,655083.59437 1794924.81992,655084.07089 1794924.76247,655084.54765 1794924.7071,655085.02464 1794924.6538,655085.50187 1794924.60259,655085.97931 1794924.55346,655086.45697 1794924.50642,655086.93482 1794924.46146,655087.41287 1794924.41858,655087.8911 1794924.37779,655088.3695 1794924.33909,655088.84807 1794924.30248,655089.32679 1794924.26795,655089.80566 1794924.23552,655090.28466 1794924.20517,655090.76379 1794924.17692,655091.24304 1794924.15075,655091.72241 1794924.12668,655092.20187 1794924.1047,655092.68142 1794924.08481,655093.16106 1794924.06701,655093.64076 1794924.05131,655094.12054 1794924.03769,655094.60036 1794924.02618,655095.08024 1794924.01675,655095.56014 1794924.00942,655096.04008 1794924.00419,655096.52004 1794924.00105,655097 1794924,655097.47996 1794924.00105,655097.95992 1794924.00419,655098.43986 1794924.00942,655098.91976 1794924.01675,655099.39964 1794924.02618,655099.87946 1794924.03769,655100.35924 1794924.05131,655100.83894 1794924.06701,655101.31858 1794924.08481,655101.79813 1794924.1047,655102.27759 1794924.12668,655102.75696 1794924.15075,655103.23621 1794924.17692,655103.71534 1794924.20517,655104.19434 1794924.23552,655104.67321 1794924.26795,655105.1078351649 1794924.299299651,655105.0196697656 1794924.758214376,655105.1078351649 1794924.2992996513,655105.10784 1794924.2993,655105.15193 1794924.30248,655105.6305 1794924.33909,655106.1089 1794924.37779,655106.58713 1794924.41858,655107.06518 1794924.46146,655107.54303 1794924.50642,655108.02069 1794924.55346,655108.49813 1794924.60259,655108.97536 1794924.6538,655109.31095 1794924.6913,655109.2318636344 1794924.845691637,655109.3109558484 1794924.6912980557)) + + + +true + + + + diff --git a/modules/tests/src/test/resources/testxml/robust/overlay/TestOverlay-rsf-794.xml b/modules/tests/src/test/resources/testxml/robust/overlay/TestOverlay-rsf-794.xml new file mode 100644 index 0000000000..3cbfc9d272 --- /dev/null +++ b/modules/tests/src/test/resources/testxml/robust/overlay/TestOverlay-rsf-794.xml @@ -0,0 +1,20 @@ + + +.01 + + + +Failed in OverlayNGRobust due to Self-snapping Mixed-dimension result. +Test extracted from R-sf n-ary intersection process. +https://github.com/r-spatial/sf/issues/794 + + +POLYGON ((39.58506999999997 3.4819800000000214, 39.58951000000002 3.4859900000000152, 39.61214000000001 3.5064499999999725, 39.61971999999997 3.5163200000000074, 39.61992848394774 3.5165334478512795, 39.619932000000006 3.5165380000000255, 39.65879771026121 3.556443380555404, 39.708230000000015 3.6071999999999775, 39.71647999999999 3.615659999999991, 39.71649000000002 3.6156799999999976, 39.72417285071445 3.6235650309963834, 39.76284499999997 3.6632639999999697, 39.7781185487038 3.696391365802514, 39.76283999999998 3.6632599999999798, 39.78296 3.706889999999987, 39.83488999999997 3.819470000000024, 39.836329999999975 3.821419999999989, 39.85753 3.8500399999999786, 39.86507999999998 3.8679199999999696, 39.86957000000001 3.8747299999999996, 39.925850000000025 3.903189999999995, 40 3.9406700000000114, 40.03841 3.9600899999999797, 40.10611 3.9943000000000097, 40.12223 4.0001700000000255, 40.12959000000001 4.006250000000023, 40.150710000000004 4.016770000000008, 40.17223999999999 4.0274800000000255, 40.190530000000024 4.042719999999974, 40.191709108532955 4.043703945741277, 40.19198058345965 4.043929987880649, 40.19878955523256 4.04378855523254, 40.212197 4.043505999999979, 40.23405100000002 4.050077999999985, 40.272850737563104 4.065018262436912, 40.32418000000001 4.084780000000023, 40.32702049086561 4.085968509102104, 40.33037200000001 4.087366999999972, 40.375789999999995 4.106326000000024, 40.38777099999999 4.1048490000000015, 40.393451 4.110392679999965, 40.393520000000024 4.110459999999989, 40.40301999999997 4.119730000000004, 40.415364666666655 4.12625202866896, 40.41766799999999 4.127468000000022, 40.42426234096692 4.130210636132334, 40.442589999999996 4.137830000000008, 40.51317999999998 4.1671799999999735, 40.513182460649126 4.167182460649126, 40.513202999999976 4.1671910000000025, 40.559055 4.1862540000000195, 40.65215599999999 4.2249459999999885, 40.673591999999985 4.233179000000007, 40.675932999999986 4.233425000000011, 40.690327000000025 4.234929000000022, 40.69032831125232 4.234929824088802, 40.69033000000002 4.23493000000002, 40.69412985846674 4.237319000000022, 40.709213999999974 4.24679900000001, 40.71264500000001 4.247337000000016, 40.72429599999998 4.249158000000023, 40.72429946916032 4.249159917070561, 40.72429999999997 4.249160000000018, 40.72465792307687 4.249358000000001, 40.72805999999997 4.251238000000001, 40.737892400135856 4.256675200407579, 40.74009000000001 4.257889999999975, 40.74711000000002 4.261770000000013, 40.74734935991566 4.261943430503433, 40.775104 4.281868999999972, 40.77838700000001 4.280996000000016, 40.7840403736852 4.2737765622808634, 40.784640000000024 4.273009999999999, 40.78464124911354 4.273009235906666, 40.78464300000002 4.273007000000007, 40.78980578076557 4.2698500480860595, 40.801379999999995 4.262769999999989, 40.81383999999997 4.255150000000015, 40.82825436363733 4.249401363582017, 40.830692 4.248428999999987, 40.84409699999998 4.246258000000012, 40.849469 4.237258999999995, 40.85576800000001 4.226705999999979, 40.86478600003295 4.223699999989027, 40.86865 4.222410000000025, 40.87322 4.220889999999997, 40.88055953205502 4.214439865388316, 40.882380408022115 4.21283852060694, 40.88238415674897 4.212824928597307, 40.883894 4.206572999999992, 40.88366180751159 4.205850694835194, 40.88353000000001 4.205449999999985, 40.881399999999985 4.198860000000025, 40.882041674184244 4.196662000031867, 40.88320699999997 4.192656999999997, 40.89355790840825 4.1778151814756805, 40.89558999999997 4.17489999999998, 40.89848999999998 4.170740000000023, 40.90291000000002 4.158180000000016, 40.90415999999999 4.154620000000023, 40.90416476271502 4.154615674040522, 40.90416499999998 4.154614999999978, 40.90644900000001 4.152535999999998, 40.90769399999999 4.152296999999976, 40.91378099999997 4.151126999999974, 40.929805999999985 4.144165999999984, 40.947910999999976 4.140875999999992, 40.95747299999999 4.135967999999991, 40.96507600000001 4.132069000000001, 40.972155999999984 4.125368999999978, 40.977168000000006 4.1163760000000025, 40.982192999999995 4.110605000000021, 40.993416000000025 4.107800999999995, 41.00142009670338 4.102709846695355, 41.00936624291501 4.090112757085003, 41.00985800000001 4.0893330000000105, 41.009858272454004 4.0893327386926055, 41.00986 4.089330000000018, 41.01167028597364 4.087594857972452, 41.023105999999984 4.076626999999974, 41.026066000000014 4.070863999999972, 41.02606783832608 4.070864213803544, 41.026070000000004 4.070859999999982, 41.031799999999976 4.071529999999996, 41.032863230599624 4.071170423470682, 41.035233000000005 4.070365999999979, 41.03575884895419 4.069834395675993, 41.04070999999999 4.064819999999997, 41.04435999999998 4.05883, 41.046627342494695 4.0568994143763275, 41.05418900000001 4.050458999999989, 41.056165666666665 4.048774509374996, 41.05739 4.047730000000001, 41.05860850954199 4.046003471374043, 41.0607 4.043039000000022, 41.065368999999976 4.036428000000001, 41.07265000000001 4.022595000000024, 41.0918709239774 4.013317484728118, 41.09852999999998 3.9951899999999796, 41.100364481300055 3.9931884439002707, 41.10402155233937 3.9891955805848154, 41.10417632933017 3.98875493996816, 41.10509999999999 3.9861099999999965, 41.105108612028154 3.986099679919586, 41.10521 3.985810000000015, 41.10539 3.985279999999989, 41.10568999999998 3.9853899999999953, 41.10569831215443 3.985393022601613, 41.10570000000001 3.985390999999993, 41.11181499999998 3.98756800000001, 41.11402151304608 3.987399008695463, 41.117999999999995 3.9870900000000233, 41.12257999999997 3.9824800000000096, 41.12336544524003 3.979627593601933, 41.12485600000002 3.974195000000009, 41.13333899999998 3.974177999999995, 41.134409442455244 3.972880721227607, 41.13562000000002 3.9714099999999917, 41.132630000000006 3.9665899999999965, 41.13263433125908 3.966587534514066, 41.132633999999996 3.966587000000004, 41.13305609116021 3.9663474558011096, 41.135879999999986 3.964740000000006, 41.148889999999994 3.9573500000000195, 41.14889098762412 3.957349575420548, 41.14889199999999 3.957349000000022, 41.16286500000001 3.9513370000000236, 41.166278413800505 3.9466651182287094, 41.16687999999999 3.9458399999999756, 41.16738537014314 3.9451506666666583, 41.16926599999999 3.9425790000000234, 41.18157100000002 3.939518000000021, 41.18404627192984 3.940608407894752, 41.18587000000002 3.941410000000019, 41.19499336774398 3.9394174709758434, 41.20098300000001 3.9381060000000048, 41.202520888283395 3.9388111907356933, 41.20276000000001 3.938919999999996, 41.20303259503887 3.9444186313557443, 41.20311600000002 3.9460770000000025, 41.20321000000001 3.9479460000000017, 41.21454899999998 3.9535060000000044, 41.226699419057944 3.94999995235515, 41.22708 3.949889999999982, 41.230110000000025 3.9560299999999984, 41.235299999999995 3.958880000000022, 41.252189999999985 3.9547000000000025, 41.27087999999998 3.9577600000000075, 41.27577705537251 3.9554114122192776, 41.27806800000002 3.949793999999997, 41.278068429636654 3.9497938539048403, 41.278070000000014 3.9497900000000072, 41.28174000000001 3.94853999999998, 41.28654999999998 3.9484299999999735, 41.28708307916612 3.9484171767897926, 41.30982599999999 3.947866999999974, 41.31406399999997 3.9448869999999943, 41.31493499999999 3.944276000000002, 41.32013138873777 3.943838219990903, 41.32180999998985 3.9416866666796864, 41.322648000000015 3.94061099999999, 41.32266790217393 3.9406009510869433, 41.32630999999998 3.938760000000002, 41.32974999999999 3.9392100000000028, 41.33893999999998 3.946550000000002, 41.34834999999998 3.95089999999999, 41.35052000000002 3.954180000000008, 41.35419999999999 3.959760000000017, 41.35438345549175 3.9600376623658993, 41.3593804765297 3.9609955162886497, 41.3596 3.9589300000000094, 41.35960268182998 3.9589279951368206, 41.35960299999999 3.958925000000022, 41.36339498273348 3.9560929740730297, 41.36577999999997 3.954310000000021, 41.3657844469914 3.954308413012066, 41.36578500000002 3.954307999999969, 41.37357400000002 3.951527999999996, 41.37861700000002 3.9517450000000167, 41.3866780668076 3.958768933192381, 41.39125634578519 3.962756817017016, 41.39469700000001 3.9622889999999984, 41.405448999999976 3.953286999999989, 41.40855899999998 3.9516295113636217, 41.411609999999996 3.9499999999999886, 41.41241122222221 3.949576276709387, 41.41368899999998 3.948894999999993, 41.41717027369826 3.9483051895861228, 41.419190000000015 3.9479600000000232, 41.41996787286065 3.9483859779951276, 41.421715000000006 3.9493360000000166, 41.42181940487704 3.949254690244706, 41.42674999999997 3.945409999999981, 41.43018999999998 3.944939999999974, 41.43547000000001 3.947459999999978, 41.43593947579344 3.9495996463152094, 41.43660399999999 3.9526089999999954, 41.43709200000001 3.954817999999989, 41.44534199999998 3.9536469999999895, 41.45214999999999 3.9549949999999967, 41.45895999999999 3.9563400000000115, 41.46025657899923 3.9565993157998247, 41.46827000000002 3.953126999999995, 41.47286300000002 3.9551849999999718, 41.476245000000006 3.952680999999984, 41.47625131629782 3.9526800873239236, 41.476290000000006 3.9526500000000055, 41.47940378305135 3.9522245703389154, 41.47973300000001 3.952177000000006, 41.48162000000002 3.9565059999999903, 41.481774649006624 3.9568598013244687, 41.482039999999984 3.957459999999969, 41.48207995173575 3.9574800630987177, 41.48445399999997 3.9585890000000177, 41.4845101632572 3.9585811738788603, 41.484579999999994 3.958570000000009, 41.48937999999998 3.957899999999995, 41.495665290214816 3.9613864194512196, 41.49970999999999 3.963626999999974, 41.504923615660324 3.9646184875329267, 41.50567000000001 3.9647600000000125, 41.50680999999997 3.961999999999989, 41.505619415613445 3.956759958841996, 41.505187999999976 3.9548689999999738, 41.50977399999999 3.954628000000014, 41.51826999999997 3.9596670000000245, 41.52121780681816 3.9655416477272607, 41.52319 3.969470000000001, 41.52343185714283 3.969953714285682, 41.524033375131495 3.971151755451765, 41.54516000000001 3.9821400000000153, 41.55065999999999 3.9816599999999767, 41.554449999999974 3.9782500000000027, 41.55477370775705 3.9779596514392757, 41.55706500000002 3.9758959999999774, 41.558445000000006 3.9737030000000004, 41.56026200000002 3.970825999999988, 41.56689899999998 3.9666669999999726, 41.57218529068946 3.968978453212946, 41.57630999999998 3.9707799999999907, 41.577894664254686 3.9709044500723496, 41.582043 3.971225000000004, 41.58799799999997 3.9693699999999694, 41.58799941787794 3.969370181668943, 41.58800000000002 3.9693699999999694, 41.588283189196844 3.9694065405414976, 41.59327400000001 3.970046000000025, 41.60050688262912 3.973926901408453, 41.60223000000002 3.9748500000000035, 41.60462129197081 3.974106489051105, 41.60818499999999 3.972995000000026, 41.617818 3.973887999999988, 41.61796099380164 3.9740240082644385, 41.62770999999998 3.983290000000011, 41.63159999999999 3.9823599999999715, 41.64865303498496 3.9701616898665106, 41.655167000000006 3.9655010000000175, 41.655168551259216 3.965501036307599, 41.65517 3.96550000000002, 41.656688065769806 3.9655366008968778, 41.663883 3.965705000000014, 41.66761592252036 3.9636557747965084, 41.67441000000002 3.959920000000011, 41.67853714502166 3.9661704350649285, 41.68063000000001 3.969338999999991, 41.68407297739042 3.970709195065964, 41.687509999999975 3.9697800000000143, 41.68751028456421 3.969780464170876, 41.68751199999997 3.9697800000000143, 41.6922009557919 3.977431716843949, 41.692578189629764 3.9780470469897313, 41.71368799999999 3.9832779999999843, 41.713689390466456 3.983279848988119, 41.713689999999986 3.9832799999999793, 41.71466889719625 3.984582358878481, 41.72059128096779 3.992457714097913, 41.72207499998595 3.9898100000251113, 41.72355699999997 3.987160000000017, 41.73020700000001 3.9866799999999785, 41.730207166062904 3.9866802045548364, 41.73021 3.9866799999999785, 41.73412000000002 3.9914999999999736, 41.73570999999998 3.9868900000000167, 41.73918953548386 3.985295212903227, 41.74074999999999 3.984577999999999, 41.74419 3.9859500000000025, 41.74693000000002 3.983409999999992, 41.74693202232768 3.9834109053181623, 41.74693300000001 3.983409999999992, 41.751491999999985 3.9854500000000144, 41.75467082816294 3.9848967383044513, 41.755419819424965 3.984760032958422, 41.75501600000001 3.983654999999999, 41.75357500000001 3.9797100000000114, 41.75376899999998 3.9784010000000194, 41.75402500000001 3.9767180000000053, 41.769183002481384 3.973101250620339, 41.772580000000005 3.9722899999999868, 41.77258135317764 3.9722903929372317, 41.772583 3.9722899999999868, 41.778088000000025 3.973885999999993, 41.779023815390595 3.9751289688228706, 41.782159999999976 3.979289999999992, 41.78219827217914 3.979337840223942, 41.784290999999996 3.977316999999971, 41.78726999999998 3.9770770000000084, 41.790980042553166 3.9786820638297664, 41.792550000000006 3.9793599999999856, 41.79413999999997 3.9754500000000235, 41.793424103792404 3.9714755417441263, 41.79297700000001 3.9690089999999714, 41.79396700000001 3.9642359999999712, 41.79478899999998 3.960262, 41.79478974258739 3.9602612493928224, 41.79478999999998 3.960260000000005, 41.795271735180464 3.959774051392285, 41.79821499999997 3.9567989999999895, 41.80210899999997 3.955407999999977, 41.80578200000002 3.9574680000000058, 41.80990600000001 3.9560749999999985, 41.81713100000002 3.955898999999988, 41.8182658033241 3.9558712049861353, 41.82067999999998 3.9558099999999854, 41.82188000000002 3.9544500000000085, 41.821941729106655 3.9543804322766545, 41.82250848897457 3.9537367161966355, 41.820922500000016 3.9502899999999883, 41.81997000000001 3.9482229999999845, 41.81997057599624 3.9482212517713196, 41.81997000000001 3.948219999999992, 41.82065 3.9461499999999887, 41.821529715270934 3.945761678817723, 41.82431500000001 3.944527999999991, 41.8243197075217 3.944530129104849, 41.82432 3.944529999999986, 41.82936999999998 3.946809999999971, 41.83348999999998 3.9458799999999883, 41.83433970588234 3.9463520588235173, 41.835555 3.9470249999999965, 41.83555645542168 3.9470280307898147, 41.83555999999999 3.947029999999984, 41.83594984098939 3.947847222222227, 41.83691399999998 3.9498550000000137, 41.83773042403725 3.9515550909860466, 41.83812 3.9523599999999988, 41.840558560705276 3.955018430932809, 41.84265424168085 3.9528914966965214, 41.84544999999997 3.950049999999976, 41.84619549999926 3.950066410377317, 41.84863300000001 3.9501169999999775, 41.852074000000016 3.9501869999999712, 41.855624999999975 3.953448999999978, 41.85668199999998 3.954421000000025, 41.86045899999999 3.961215999999979, 41.863864999999976 3.963754999999992, 41.878152 3.967767999999978, 41.88538591773479 3.974230857090358, 41.89009805377673 3.9784382622424492, 41.89383177808946 3.9787588950687045, 41.894583896289234 3.9782915176023024, 41.89907099999999 3.975498000000016, 41.899071307165144 3.9754980984403283, 41.89808082617185 3.9740123749999725, 41.8924232338112 3.965614640656319, 41.87288000000001 3.9366099999999733, 41.87135999999998 3.9337400000000002, 41.86018999999999 3.9126600000000167, 41.83537999999999 3.8792199999999752, 41.82974999999999 3.873800000000017, 41.828802248123175 3.8724842973945326, 41.82805061328122 3.8714427949218475, 41.82465365812802 3.8667346267358598, 41.823800000000006 3.865550999999982, 41.820345994999414 3.860761302239723, 41.82034599496808 3.8607613021962672, 41.7905788417969 3.8194789882812756, 41.775857925781224 3.796812056640647, 41.77299770769298 3.79240734075199, 41.76862080227858 3.785666372030967, 41.764549999999986 3.7794000000000096, 41.758579999999995 3.772040000000004, 41.755420000000015 3.7681499999999915, 41.749000000000024 3.7602299999999786, 41.68094000000002 3.6661399999999844, 41.663897109089795 3.6425737655192143, 41.66145133984372 3.639192582031228, 41.6106472011719 3.5641384121094006, 41.610400853529434 3.563807804705901, 41.60917999999998 3.562180000000012, 41.59263137410677 3.5399958960998497, 41.5925312050781 3.5398616796874762, 41.592530566911094 3.5398607599672656, 41.59253000000001 3.539859999999976, 41.5904859763054 3.5369141154768107, 41.53639030468747 3.458951949218772, 41.498787064702746 3.4047446290723746, 41.496283504058084 3.4011353720304345, 41.46438999999998 3.355160000000012, 41.392409999999984 3.251359999999977, 41.34108312724763 3.1773161152718465, 41.328130722656226 3.158632279296853, 41.3236598964844 3.1540622714843494, 41.321362704917135 3.151713676370416, 41.31871699999999 3.1490079999999807, 41.30695366688958 3.1369765739769613, 41.28568000000001 3.115220000000022, 41.28397566344093 3.1136903461353724, 41.28396382520851 3.1136797372956857, 41.28206825195315 3.1119785312499744, 41.2601299277344 3.0923328398437206, 41.22793769921873 3.0614185332030956, 41.1369380957031 2.9740085605468494, 41.136406379167084 2.97349835178965, 41.13353999999998 2.9707500000000095, 41.08870999999999 2.9245199999999727, 41.08383911875953 2.9194928337999486, 41.081232070312524 2.9168033593750238, 41.070674016601764 2.9064703073086786, 41.04811000000001 2.8843899999999962, 40.992110000000025 2.8295699999999897, 40.992110000000025 2.7904697752688157, 40.9921092988281 2.7816638945312206, 40.99210704209844 2.7442573345931938, 40.99209999999999 2.655210000000011, 40.99209000000002 2.528759999999977, 40.99207999999999 2.4023199999999747, 40.99207999999999 2.3680400000000077, 40.99207999999999 2.3333299999999895, 40.990909999999985 2.3196399999999926, 40.99093234269909 2.3120434823153757, 40.99093818749998 2.3094387050781506, 40.99097696403657 2.2939613096901934, 40.99101999999999 2.2759399999999914, 40.991219780605526 2.197014174523503, 40.991338730468726 2.1495189667968475, 40.99143791210935 2.1093273164062225, 40.99032314988336 2.095899161417678, 40.99027000000001 2.095259999999996, 40.99164999999999 2.0231699999999933, 40.99176 2.0175100000000157, 40.99031000000002 1.9419899999999757, 40.99070999999998 1.8967799999999784, 40.99116420084227 1.8457920399296106, 40.991289138671846 1.8316707617187262, 40.99382963956081 1.823885231466147, 40.991480000000024 1.7927999999999997, 40.991150000000005 1.770879999999977, 40.99095 1.7576399999999808, 40.99148352712286 1.6714067733128468, 40.9916477207031 1.644502640624978, 40.99170316733052 1.6355324435849834, 40.99180000000001 1.619199999999978, 40.991510000000005 1.518129999999985, 40.991150000000005 1.3917700000000082, 40.99092999999999 1.3140300000000025, 40.99086229069553 1.2905203622137496, 40.990789414062476 1.2654170996093512, 40.9904270175781 1.1390819550780975, 40.99032704877596 1.104160816645765, 40.990099999999984 1.027150000000006, 40.990369999999984 1.0127600000000143, 40.99111548105521 0.9724878169080781, 40.991289138671846 0.9630603789062206, 40.99109587677684 0.9267921002592653, 40.990880000000004 0.8864599999999996, 40.990200000000016 0.7601700000000164, 40.990069046580714 0.7341033772782363, 40.990007400390596 0.7223491660156469, 40.991094961753596 0.6657023253756724, 40.99137999999999 0.6508200000000102, 40.991240000000005 0.6339100000000144, 40.99048746358357 0.5379350020818205, 40.99024772656247 0.5076503750000256, 40.989731648919616 0.4422133596140218, 40.98924999999997 0.3814100000000167, 40.98906999999997 0.3582299999999918, 40.98954857527574 0.2835069967197366, 40.98954857527631 0.2835069966307305, 40.98972892773435 0.2551822656250238, 40.98979144344426 0.245285898712502, 40.99009000000001 0.1972099999999841, 40.98937000000001 0.1289800000000128, 40.989357654737915 0.1277862131562512, 40.98926734960935 0.1193141933594006, 40.98975927966687 0.1156418500712479, 40.99095999999997 0.1066399999999703, 40.989428422044824 0.0941553190927544, 40.9893093105469 0.0931873320312206, 40.98921451654904 0.0820717946380041, 40.98863 0.0140099999999848, 41.00051000000002 0, 41.00048868909863 -0.0622633501464197, 41.00047874414065 -0.0876541132812463, 41.00014752511312 -0.0933535832704122, 40.999059999999986 -0.1120099999999979, 41.00031999999999 -0.1280699999999797, 40.999480000000005 -0.1377200000000016, 41.000850000000014 -0.2133299999999849, 41.001377882832905 -0.2418545259339681, 41.001409531249976 -0.2435855859375238, 41.00115460028239 -0.2557284062837806, 40.999379999999974 -0.3400700000000256, 41.000009999999975 -0.3940499999999929, 40.997799999999984 -0.4037099999999896, 40.999990000000025 -0.4220700000000193, 40.99824000000001 -0.4321899999999914, 40.99824002914697 -0.4321907990121363, 40.9982395175781 -0.4321937558593731, 40.999444960937524 -0.465362548828125, 41.00081825195315 -0.5029315957031031, 41.00009616138168 -0.5170738066018506, 40.99882000000002 -0.5420100000000048, 40.999959848263344 -0.5718660255646748, 40.99986710961163 -0.5727676510426798, 40.99889000000002 -0.582209999999975, 40.998879999999986 -0.5915400000000091, 40.998789999999985 -0.6614799999999832, 40.99950999999999 -0.7178900000000112, 41.0009594140633 -0.8316494172925255, 41.00098991406247 -0.8340492246093731, 41.00098999138679 -0.8340493239791331, 41.00099 -0.8340499999999906, 41.00317989494919 -0.8368635770307074, 41.00723838867185 -0.8420791621093713, 41.01266943334065 -0.8490582046742746, 41.025890000000004 -0.8660500000000297, 41.03347000000002 -0.8731099999999969, 41.08244000000002 -0.9430100000000152, 41.08634000000001 -0.9485799999999927, 41.159069999999986 -1.0431199999999876, 41.16845000000001 -1.05531000000002, 41.190618052853125 -1.0871965665901222, 41.1954784394531 -1.0941886894531194, 41.20345904695357 -1.1046031219775763, 41.23403000000002 -1.1444999999999936, 41.27184999999997 -1.1938499999999976, 41.29149000000001 -1.222440000000006, 41.29318999999998 -1.2247199999999907, 41.30282089662207 -1.237646640713292, 41.30896949804685 -1.2459030156250037, 41.332956816437026 -1.2781159930038901, 41.33295681646438 -1.2781159930406296, 41.35176000000001 -1.3033700000000294, 41.38558999999998 -1.3460600000000227, 41.38959999999997 -1.351130000000012, 41.391557323059196 -1.3558443306367352, 41.392168044921846 -1.3573188789062556, 41.3983860019531 -1.3634777070312794, 41.42655416607092 -1.4027228210162264, 41.44598000000002 -1.4297900000000254, 41.459630000000004 -1.447810000000004, 41.45967000000002 -1.4478599999999915, 41.46397489888458 -1.4535351906422012, 41.46676826562498 -1.4572219843749963, 41.4761369469963 -1.4679729715002585, 41.481299999999976 -1.4739000000000146, 41.48162000382251 -1.474530286009967, 41.485246658203096 -1.4816837304687738, 41.491246855689596 -1.489263467733027, 41.49194 -1.4901399999999967, 41.491941425624056 -1.4901408817892625, 41.4919414511719 -1.490140914062522, 41.49195532661093 -1.4901494799473571, 41.49561 -1.4924100000000067, 41.50074000000001 -1.500819999999976, 41.493780000000015 -1.5120299999999816, 41.44144 -1.4426900000000273, 41.387879999999996 -1.5137700000000223, 41.32416999999998 -1.5950500000000147, 41.301199999999994 -1.6127900000000182, 41.27361000000002 -1.6378799999999956, 41.23230000000001 -1.6517800000000307, 41.15890000000002 -1.6577100000000087, 41.101560000000006 -1.662869999999998, 41.041920000000005 -1.672900000000027, 40.98225000000002 -1.6900499999999852, 40.92257000000001 -1.7143199999999865, 40.88121000000001 -1.7424599999999941, 40.85129999999998 -1.7819299999999885, 40.80061999999998 -1.811179999999979, 40.76997 -1.8530000000000086, 40.74950000000001 -1.8942099999999868, 40.756039999999985 -1.9370400000000245, 40.736159999999984 -1.9585400000000277, 40.694709999999986 -2.0175800000000095, 40.616600000000005 -2.066500000000019, 40.52944000000002 -2.0802400000000034, 40.50495000000001 -2.0885900000000106, 40.453070000000025 -2.085399999999993, 40.220939999999985 -2.0681200000000217, 40.21751999999998 -2.0389599999999746, 40.20794000000001 -2.0262500000000045, 40.17860000000002 -1.9332699999999932, 40.18117999999998 -1.8502900000000295, 40.15841999999998 -1.8087600000000066, 40.142510000000016 -1.7763699999999858, 40.15643 -1.7259900000000243, 40.154499999999985 -1.624270000000024, 40.125009999999975 -1.5427999999999997, 40.07076999999998 -1.4600700000000302, 40.04090000000002 -1.1627000000000294, 40.01411999999999 -1.1316899999999919, 39.98608999999999 -1.076880000000017, 39.930079999999975 -0.9673399999999788, 39.92345 -0.9061100000000124, 39.884979999999985 -0.7706200000000081, 39.85113999999999 -0.6278100000000109, 39.810249999999996 -0.5303400000000238, 39.764580000000024 -0.4970000000000141, 39.71886999999998 -0.4707799999999907, 39.64339000000001 -0.44598000000002, 39.64071000000001 -0.3953099999999949, 39.62999000000002 -0.3499800000000164, 39.47998000000001 -0.1799800000000005, 39.41998000000001 -0.1499800000000278, 39.36998 -0.1399900000000116, 39.309979999999996 -0.1099800000000073, 39.22998000000001 -0.0899800000000255, 39.15998000000002 -0.0699799999999868, 39.109969999999976 -0.0399800000000141, 39.06851 -0.0319400000000201, 39.032939999999996 -0.0441599999999767, 38.991089999999986 -0.0477799999999888, 38.96361000000002 -0.0616600000000176, 38.96451999999999 -0.0386500000000183, 38.98777000000001 -0.0272199999999998, 39.04111 -0.0183299999999917, 39.05604999999997 -0.0024700000000166, 39.07072999999997 0.0214199999999778, 39.075049999999976 0.042759999999987, 39.07418000000001 0.1135899999999879, 39.075049999999976 0.1315200000000232, 39.08801 0.1492200000000139, 39.09471000000002 0.1572100000000205, 39.075829999999996 0.171100000000024, 39.02053999999998 0.1947099999999864, 39.00887999999998 0.2047200000000089, 38.97886999999997 0.2505499999999756, 38.96888000000001 0.2555600000000027, 38.87358999999998 0.2641699999999787, 38.73665999999997 0.3088900000000194, 38.72496000000001 0.3148499999999785, 38.68997999999999 0.4499799999999823, 38.686770000000024 0.4891499999999951, 38.77998000000002 0.5199799999999755, 38.809979999999996 0.5799799999999777, 39.01763 0.6551000000000045, 39.15998999999999 0.7299800000000118, 39.21998000000002 0.8199900000000184, 39.27998000000002 0.8699799999999982, 39.309979999999996 0.9299800000000005, 39.44997999999998 0.9799800000000118, 39.45997999999997 1.529989999999998, 39.17998 1.7399899999999775, 39.16998000000001 1.799980000000005, 39.115880000000004 1.8714899999999943, 39.06876 1.9315799999999967, 39.06443999999999 2.0335099999999784, 39.18641000000002 2.217620000000011, 39.56025999999997 3.406659999999988, 39.549030000000016 3.4065800000000195, 39.55702000000002 3.411479999999983, 39.55761000000001 3.4118500000000154, 39.56207999999998 3.41631000000001, 39.567319999999995 3.428580000000011, 39.567920000000015 3.4299800000000005, 39.572378882465934 3.444894303770105, 39.58181400000001 3.476434999999981, 39.584835474836524 3.481580224798322, 39.58506999999997 3.4819800000000214)) + + +MULTIPOLYGON (((41.823800000000006 3.865550999999982, 41.820345994999414 3.860761302239723, 41.82034599496808 3.8607613021962672, 41.8238010410156 3.8655529023437225, 41.82465365812802 3.8667346267358598, 41.823800000000006 3.865550999999982)), ((41.77299770769298 3.79240734075199, 41.76862080227858 3.785666372030967, 41.76774805113227 3.7843229062964685, 41.77299770769298 3.79240734075199)), ((41.496283504058084 3.4011353720304345, 41.49628350403736 3.4011353720005517, 41.48927593188359 3.3910337620861157, 41.498787064702746 3.4047446290723746, 41.496283504058084 3.4011353720304345)), ((41.28397566344093 3.1136903461353724, 41.28396382520851 3.1136797372956857, 41.284325811019194 3.1140046060117075, 41.28397566344093 3.1136903461353724)), ((41.30695366688958 3.1369765739769613, 41.28568000000001 3.115220000000022, 41.28567743555821 3.1152176983957487, 41.31871604882815 3.1490077968750256, 41.321362704917135 3.151713676370416, 41.31871699999999 3.1490079999999807, 41.30695366688958 3.1369765739769613))) + + true + + + diff --git a/pom.xml b/pom.xml old mode 100755 new mode 100644 index fba65c56ef..5bd73d405c --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ UTF-8 - 3.7 + 4.13.1 2.0.6 1.2 1.1.1