/* * The JTS Topology Suite is a collection of Java classes that * implement the fundamental operations required to validate a given * geo-spatial data set to a known topological specification. * * Copyright (C) 2001 Vivid Solutions * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * For more information, contact: * * Vivid Solutions * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ package com.revolsys.geometry.noding; import java.util.ArrayList; import java.util.Collection; import java.util.List; import com.revolsys.geometry.model.LineString; import com.revolsys.geometry.model.coordinates.list.CoordinatesListUtil; import com.revolsys.geometry.model.impl.LineStringDouble; /** * Wraps a {@link Noder} and transforms its input * into the integer domain. * This is intended for use with Snap-Rounding noders, * which typically are only intended to work in the integer domain. * Offsets can be provided to increase the number of digits of available precision. * <p> * Clients should be aware that rescaling can involve loss of precision, * which can cause zero-length line segments to be created. * These in turn can cause problems when used to build a planar graph. * This situation should be checked for and collapsed segments removed if necessary. * * @version 1.7 */ public class ScaledNoder implements Noder { private boolean isScaled = false; private final Noder noder; private double offsetX; private double offsetY; private final double scaleFactor; public ScaledNoder(final Noder noder, final double scaleFactor) { this(noder, scaleFactor, 0, 0); } public ScaledNoder(final Noder noder, final double scaleFactor, final double offsetX, final double offsetY) { this.noder = noder; this.scaleFactor = scaleFactor; // no need to scale if input precision is already integral this.isScaled = !isIntegerPrecision(); } @Override public void computeNodes(final Collection<NodedSegmentString> inputSegStrings) { Collection<NodedSegmentString> intSegStrings = inputSegStrings; if (this.isScaled) { intSegStrings = scale(inputSegStrings); } this.noder.computeNodes(intSegStrings); } @Override public Collection<NodedSegmentString> getNodedSubstrings() { final Collection<NodedSegmentString> segments = this.noder.getNodedSubstrings(); if (this.isScaled) { return rescale(segments); } return segments; } public boolean isIntegerPrecision() { return this.scaleFactor == 1.0; } private Collection<NodedSegmentString> rescale(final Collection<NodedSegmentString> segments) { final List<NodedSegmentString> newSegments = new ArrayList<>(); for (final NodedSegmentString segment : segments) { final NodedSegmentString newSegment = rescale(segment); newSegments.add(newSegment); } return newSegments; } private NodedSegmentString rescale(final NodedSegmentString segment) { final LineString points = segment.getPoints(); final int axisCount = points.getAxisCount(); final int vertexCount = points.getVertexCount(); final double[] coordinates = new double[vertexCount * axisCount]; for (int i = 0; i < vertexCount; i++) { final double x = points.getX(i) / this.scaleFactor + this.offsetX; final double y = points.getY(i) / this.scaleFactor + this.offsetY; CoordinatesListUtil.setCoordinates(coordinates, axisCount, i, x, y); for (int axisIndex = 2; axisIndex < axisCount; axisIndex++) { final double value = points.getCoordinate(i, axisIndex); coordinates[i * axisCount + axisIndex] = value; } } final LineStringDouble newPoints = new LineStringDouble(axisCount, coordinates); final Object data = segment.getData(); return new NodedSegmentString(newPoints, data); } private Collection<NodedSegmentString> scale(final Collection<NodedSegmentString> segments) { final List<NodedSegmentString> result = new ArrayList<>(); for (final NodedSegmentString segment : segments) { final Object data = segment.getData(); final LineString scale = scale(segment); final NodedSegmentString nodedSegmentString = new NodedSegmentString(scale, data); result.add(nodedSegmentString); } return result; } private LineString scale(final NodedSegmentString segment) { final int vertexCount = segment.size(); final int axisCount = segment.getPoints().getAxisCount(); final double[] coordinates = new double[vertexCount * axisCount]; double previousX = Double.NaN; double previousY = Double.NaN; int j = 0; for (int i = 0; i < vertexCount; i++) { final double x = Math.round((segment.getX(i) - this.offsetX) * this.scaleFactor); final double y = Math.round((segment.getY(i) - this.offsetY) * this.scaleFactor); final double z = segment.getZ(i); if (i == 0 || x != previousX && y != previousY) { CoordinatesListUtil.setCoordinates(coordinates, axisCount, j++, x, y, z); } previousX = x; previousY = y; } final LineString points = LineStringDouble.newLineStringDouble(axisCount, j, coordinates); return points; } }