1 /*
2  * Copyright (c) 2016 Vivid Solutions.
3  *
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
7  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
8  * and the Eclipse Distribution License is available at
9  *
10  * http://www.eclipse.org/org/documents/edl-v10.php.
11  */
12 
13 package org.locationtech.jts.noding;
14 
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Iterator;
18 import java.util.List;
19 
20 import org.locationtech.jts.geom.Coordinate;
21 import org.locationtech.jts.geom.CoordinateArrays;
22 
23 /**
24  * Wraps a {@link Noder} and transforms its input
25  * into the integer domain.
26  * This is intended for use with Snap-Rounding noders,
27  * which typically are only intended to work in the integer domain.
28  * Offsets can be provided to increase the number of digits of available precision.
29  * <p>
30  * Clients should be aware that rescaling can involve loss of precision,
31  * which can cause zero-length line segments to be created.
32  * These in turn can cause problems when used to build a planar graph.
33  * This situation should be checked for and collapsed segments removed if necessary.
34  *
35  * @version 1.7
36  */
37 public class ScaledNoder
38     implements Noder
39 {
40   private Noder noder;
41   private double scaleFactor;
42   private double offsetX;
43   private double offsetY;
44   private boolean isScaled = false;
45 
ScaledNoder(Noder noder, double scaleFactor)46   public ScaledNoder(Noder noder, double scaleFactor) {
47     this(noder, scaleFactor, 0, 0);
48   }
49 
ScaledNoder(Noder noder, double scaleFactor, double offsetX, double offsetY)50   public ScaledNoder(Noder noder, double scaleFactor, double offsetX, double offsetY) {
51     this.noder = noder;
52     this.scaleFactor = scaleFactor;
53     // no need to scale if input precision is already integral
54     isScaled = ! isIntegerPrecision();
55   }
56 
isIntegerPrecision()57   public boolean isIntegerPrecision() { return scaleFactor == 1.0; }
58 
getNodedSubstrings()59   public Collection getNodedSubstrings()
60   {
61     Collection splitSS = noder.getNodedSubstrings();
62     if (isScaled) rescale(splitSS);
63     return splitSS;
64   }
65 
computeNodes(Collection inputSegStrings)66   public void computeNodes(Collection inputSegStrings)
67   {
68     Collection intSegStrings = inputSegStrings;
69     if (isScaled)
70       intSegStrings = scale(inputSegStrings);
71     noder.computeNodes(intSegStrings);
72   }
73 
scale(Collection segStrings)74   private Collection scale(Collection segStrings)
75   {
76     List nodedSegmentStrings = new ArrayList(segStrings.size());
77     for (Iterator i = segStrings.iterator(); i.hasNext(); ) {
78       SegmentString ss = (SegmentString) i.next();
79       nodedSegmentStrings.add(new NodedSegmentString(scale(ss.getCoordinates()), ss.getData()));
80     }
81     return nodedSegmentStrings;
82   }
83 
scale(Coordinate[] pts)84   private Coordinate[] scale(Coordinate[] pts)
85   {
86     Coordinate[] roundPts = new Coordinate[pts.length];
87     for (int i = 0; i < pts.length; i++) {
88       roundPts[i] = new Coordinate(
89           Math.round((pts[i].x - offsetX) * scaleFactor),
90           Math.round((pts[i].y - offsetY) * scaleFactor),
91           pts[i].getZ()
92         );
93     }
94     Coordinate[] roundPtsNoDup = CoordinateArrays.removeRepeatedPoints(roundPts);
95     return roundPtsNoDup;
96   }
97 
98   //private double scale(double val) { return (double) Math.round(val * scaleFactor); }
99 
rescale(Collection segStrings)100   private void rescale(Collection segStrings)
101   {
102     for (Iterator i = segStrings.iterator(); i.hasNext(); ) {
103       SegmentString ss = (SegmentString) i.next();
104       rescale(ss.getCoordinates());
105     }
106   }
107 
rescale(Coordinate[] pts)108   private void rescale(Coordinate[] pts)
109   {
110     for (int i = 0; i < pts.length; i++) {
111       pts[i].x = pts[i].x / scaleFactor + offsetX;
112       pts[i].y = pts[i].y / scaleFactor + offsetY;
113     }
114 
115     if (pts.length == 2 && pts[0].equals2D(pts[1])) {
116       System.out.println(pts);
117     }
118   }
119 
120   //private double rescale(double val) { return val / scaleFactor; }
121 }
122