root / org.gvsig.legend.urbanhorizontalsignage / trunk / org.gvsig.legend.urbanhorizontalsignage / org.gvsig.legend.urbanhorizontalsignage.lib / org.gvsig.legend.urbanhorizontalsignage.lib.impl / src / main / java / org / gvsig / legend / urbanhorizontalsignage / lib / impl / DefaultUrbanHorizontalSignageManager.java @ 5130
History | View | Annotate | Download (16.7 KB)
1 |
/* gvSIG. Desktop Geographic Information System.
|
---|---|
2 |
*
|
3 |
* Copyright ? 2007-2015 gvSIG Association
|
4 |
*
|
5 |
* This program is free software; you can redistribute it and/or
|
6 |
* modify it under the terms of the GNU General Public License
|
7 |
* as published by the Free Software Foundation; either version 2
|
8 |
* of the License, or (at your option) any later version.
|
9 |
*
|
10 |
* This program is distributed in the hope that it will be useful,
|
11 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13 |
* GNU General Public License for more details.
|
14 |
*
|
15 |
* You should have received a copy of the GNU General Public License
|
16 |
* along with this program; if not, write to the Free Software
|
17 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
18 |
* MA 02110-1301, USA.
|
19 |
*
|
20 |
* For any additional information, do not hesitate to contact us
|
21 |
* at info AT gvsig.com, or visit our website www.gvsig.com.
|
22 |
*/
|
23 |
package org.gvsig.legend.urbanhorizontalsignage.lib.impl; |
24 |
|
25 |
import java.util.ArrayList; |
26 |
import java.util.Iterator; |
27 |
import java.util.List; |
28 |
import org.apache.commons.lang3.tuple.MutablePair; |
29 |
import org.gvsig.fmap.geom.Geometry; |
30 |
import static org.gvsig.fmap.geom.Geometry.JOIN_STYLE_BEVEL; |
31 |
import static org.gvsig.fmap.geom.Geometry.JOIN_STYLE_MITRE; |
32 |
import static org.gvsig.fmap.geom.Geometry.JOIN_STYLE_ROUND; |
33 |
import org.gvsig.fmap.geom.GeometryLocator; |
34 |
import org.gvsig.fmap.geom.GeometryManager; |
35 |
import org.gvsig.fmap.geom.aggregate.MultiLine; |
36 |
import org.gvsig.fmap.geom.aggregate.MultiPolygon; |
37 |
import org.gvsig.fmap.geom.exception.CreateGeometryException; |
38 |
import org.gvsig.fmap.geom.operation.GeometryOperationException; |
39 |
import org.gvsig.fmap.geom.operation.GeometryOperationNotSupportedException; |
40 |
import org.gvsig.fmap.geom.primitive.Line; |
41 |
import org.gvsig.fmap.geom.primitive.Point; |
42 |
import org.gvsig.fmap.geom.primitive.Primitive; |
43 |
import org.gvsig.fmap.geom.type.GeometryType; |
44 |
import org.gvsig.legend.urbanhorizontalsignage.lib.api.UrbanHorizontalSignageData; |
45 |
import static org.gvsig.legend.urbanhorizontalsignage.lib.api.UrbanHorizontalSignageData.CONTINUITY_MODE_CONT; |
46 |
import static org.gvsig.legend.urbanhorizontalsignage.lib.api.UrbanHorizontalSignageData.CONTINUITY_MODE_CONT_CONT; |
47 |
import static org.gvsig.legend.urbanhorizontalsignage.lib.api.UrbanHorizontalSignageData.CONTINUITY_MODE_CONT_DISC; |
48 |
import static org.gvsig.legend.urbanhorizontalsignage.lib.api.UrbanHorizontalSignageData.CONTINUITY_MODE_DISC; |
49 |
import static org.gvsig.legend.urbanhorizontalsignage.lib.api.UrbanHorizontalSignageData.CONTINUITY_MODE_DISC_CONT; |
50 |
import static org.gvsig.legend.urbanhorizontalsignage.lib.api.UrbanHorizontalSignageData.CONTINUITY_MODE_DISC_DISC; |
51 |
import org.gvsig.legend.urbanhorizontalsignage.lib.api.UrbanHorizontalSignageLegend; |
52 |
import org.gvsig.legend.urbanhorizontalsignage.lib.api.UrbanHorizontalSignageManager; |
53 |
import org.slf4j.LoggerFactory; |
54 |
|
55 |
public class DefaultUrbanHorizontalSignageManager implements UrbanHorizontalSignageManager { |
56 |
|
57 |
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(DefaultUrbanHorizontalSignageManager.class); |
58 |
|
59 |
@Override
|
60 |
public UrbanHorizontalSignageLegend create() {
|
61 |
return new DefaultUrbanHorizontalSignageLegend(); |
62 |
} |
63 |
|
64 |
@Override
|
65 |
public Class<? extends UrbanHorizontalSignageLegend> getLegendClass() { |
66 |
return DefaultUrbanHorizontalSignageLegend.class;
|
67 |
} |
68 |
|
69 |
@Override
|
70 |
public UrbanHorizontalSignageData createUrbanHorizontalSignageData() {
|
71 |
return new DefaultUrbanHorizontalSignageData(); |
72 |
} |
73 |
|
74 |
@Override
|
75 |
public void calculateGeometries(Geometry originalGeometry, UrbanHorizontalSignageData data) { |
76 |
GeometryManager geomManager = GeometryLocator.getGeometryManager(); |
77 |
try {
|
78 |
MultiPolygon segments = geomManager.createMultiPolygon(originalGeometry.getGeometryType().getSubType()); |
79 |
MultiPolygon holes = geomManager.createMultiPolygon(originalGeometry.getGeometryType().getSubType()); |
80 |
MultiLine lines = originalGeometry.toLines(); |
81 |
final double offsetValueInMeters = data.getGapWidth()/2.0+data.getWidth()/2.0; |
82 |
final double bufferValueInMeters = data.getWidth() / 2.0; |
83 |
for (Geometry geom : lines) {
|
84 |
Line line = (Line) geom; |
85 |
switch (data.getContinuity()) {
|
86 |
case CONTINUITY_MODE_CONT:
|
87 |
default:
|
88 |
Geometry buffer = line.buffer( |
89 |
bufferValueInMeters, |
90 |
data.isRoundVertex() ? JOIN_STYLE_ROUND : JOIN_STYLE_MITRE, |
91 |
true
|
92 |
); |
93 |
segments.addPrimitives(buffer); |
94 |
break;
|
95 |
|
96 |
case CONTINUITY_MODE_DISC:
|
97 |
SplittedLine splittedLine = splitLine(line, data.getSegmentsLength(), data.getHolesLength()); |
98 |
List<Line> splittedSegments = splittedLine.getSegments(); |
99 |
List<Line> splittedHoles = splittedLine.getHoles(); |
100 |
for (Line segment : splittedSegments) { |
101 |
buffer = segment.buffer(bufferValueInMeters, |
102 |
data.isRoundVertex() ? JOIN_STYLE_ROUND : JOIN_STYLE_MITRE, |
103 |
true
|
104 |
); |
105 |
segments.addPrimitives(buffer); |
106 |
} |
107 |
for (Line hole : splittedHoles) { |
108 |
buffer = hole.buffer( |
109 |
bufferValueInMeters, |
110 |
data.isRoundVertex() ? JOIN_STYLE_ROUND : JOIN_STYLE_MITRE, |
111 |
true
|
112 |
); |
113 |
holes.addPrimitives(buffer); |
114 |
} |
115 |
break;
|
116 |
|
117 |
case CONTINUITY_MODE_CONT_CONT:
|
118 |
//Left
|
119 |
addOffsetedAndBufferedSegment(segments, line, -offsetValueInMeters, bufferValueInMeters, data); |
120 |
//Right
|
121 |
addOffsetedAndBufferedSegment(segments, line, offsetValueInMeters, bufferValueInMeters, data); |
122 |
break;
|
123 |
|
124 |
case CONTINUITY_MODE_CONT_DISC:
|
125 |
//Left
|
126 |
Geometry leftOffset; |
127 |
|
128 |
if (line.isClosed() && line.getNumVertices() > 2 && line.isCCW()) { |
129 |
Line cloned = line.cloneGeometry();
|
130 |
cloned.flip(); |
131 |
addOffsetedAndBufferedSegment(segments, cloned, offsetValueInMeters, bufferValueInMeters, data); |
132 |
} else {
|
133 |
addOffsetedAndBufferedSegment(segments, line, -offsetValueInMeters, bufferValueInMeters, data); |
134 |
} |
135 |
//Right
|
136 |
splittedLine = splitLine(line, data.getSegmentsLength(), data.getHolesLength()); |
137 |
splittedSegments = splittedLine.getSegments(); |
138 |
splittedHoles = splittedLine.getHoles(); |
139 |
for (Line segment : splittedSegments) { |
140 |
addOffsetedAndBufferedSegment(segments, segment, offsetValueInMeters, bufferValueInMeters, data); |
141 |
} |
142 |
for (Line hole : splittedHoles) { |
143 |
addOffsetedAndBufferedSegment(holes, hole, offsetValueInMeters, bufferValueInMeters, data); |
144 |
} |
145 |
break;
|
146 |
|
147 |
case CONTINUITY_MODE_DISC_CONT:
|
148 |
//Left
|
149 |
splittedLine = splitLine(line, data.getSegmentsLength(), data.getHolesLength()); |
150 |
splittedSegments = splittedLine.getSegments(); |
151 |
splittedHoles = splittedLine.getHoles(); |
152 |
for (Line segment : splittedSegments) { |
153 |
addOffsetedAndBufferedSegment(segments, segment, -offsetValueInMeters, bufferValueInMeters, data); |
154 |
} |
155 |
for (Line hole : splittedHoles) { |
156 |
addOffsetedAndBufferedSegment(holes, hole, -offsetValueInMeters, bufferValueInMeters, data); |
157 |
} |
158 |
//Right
|
159 |
Geometry rightOffset; |
160 |
if (line.isClosed() && line.getNumVertices() > 2 && line.isCCW()) { |
161 |
Line cloned = line.cloneGeometry();
|
162 |
cloned.flip(); |
163 |
addOffsetedAndBufferedSegment(segments, cloned, -offsetValueInMeters, bufferValueInMeters, data); |
164 |
} else {
|
165 |
addOffsetedAndBufferedSegment(segments, line, offsetValueInMeters, bufferValueInMeters, data); |
166 |
} |
167 |
break;
|
168 |
|
169 |
|
170 |
case CONTINUITY_MODE_DISC_DISC:
|
171 |
splittedLine = splitLine(line, data.getSegmentsLength(), data.getHolesLength()); |
172 |
splittedSegments = splittedLine.getSegments(); |
173 |
splittedHoles = splittedLine.getHoles(); |
174 |
//Left
|
175 |
for (Line segment : splittedSegments) { |
176 |
addOffsetedAndBufferedSegment(segments, segment, -offsetValueInMeters, bufferValueInMeters, data); |
177 |
} |
178 |
for (Line hole : splittedHoles) { |
179 |
addOffsetedAndBufferedSegment(holes, hole, -offsetValueInMeters, bufferValueInMeters, data); |
180 |
} |
181 |
//Right
|
182 |
for (Line segment : splittedSegments) { |
183 |
addOffsetedAndBufferedSegment(segments, segment, offsetValueInMeters, bufferValueInMeters, data); |
184 |
} |
185 |
for (Line hole : splittedHoles) { |
186 |
addOffsetedAndBufferedSegment(holes, hole, offsetValueInMeters, bufferValueInMeters, data); |
187 |
} |
188 |
break;
|
189 |
|
190 |
} |
191 |
|
192 |
} |
193 |
data.setSegmentsGeometry(segments); |
194 |
data.setHolesGeometry(holes); |
195 |
} catch (Exception ex) { |
196 |
LOGGER.warn("Can't calculate geometries.", ex);
|
197 |
// Logger.getLogger(DefaultUrbanHorizontalSignageManager.class.getName()).log(Level.SEVERE, null, ex);
|
198 |
} |
199 |
|
200 |
} |
201 |
|
202 |
protected void addOffsetedAndBufferedSegment(MultiPolygon segments, Line segment, final double offsetValueInMeters, final double bufferValueInMeters, UrbanHorizontalSignageData data) throws GeometryOperationException, GeometryOperationNotSupportedException { |
203 |
Geometry buffer; |
204 |
Geometry segmentOffset = segment.cloneGeometry().offset(offsetValueInMeters); |
205 |
if(segmentOffset == null){ |
206 |
return;
|
207 |
} |
208 |
buffer = segmentOffset.buffer( |
209 |
bufferValueInMeters, |
210 |
data.isRoundVertex() ? JOIN_STYLE_ROUND : JOIN_STYLE_MITRE, |
211 |
true
|
212 |
); |
213 |
segments.addPrimitives(buffer); |
214 |
} |
215 |
|
216 |
/*
|
217 |
segmentLength & holesLenght in meters
|
218 |
*/
|
219 |
/*friend*/SplittedLine splitLine(Line line, double segmentLength, double holesLength) throws CreateGeometryException, GeometryOperationNotSupportedException, GeometryOperationException, CloneNotSupportedException { |
220 |
GeometryManager geomManager = GeometryLocator.getGeometryManager(); |
221 |
SplittedLine res = new SplittedLine();
|
222 |
|
223 |
Point previousPoint = null; |
224 |
double previousLength = 0; |
225 |
Line currentSegment = geomManager.createLine(line.getGeometryType().getSubType());
|
226 |
boolean isHole = false; |
227 |
boolean advanceToNext = true; |
228 |
Iterator<Point> it = line.iterator(); |
229 |
Point currentPoint = null; |
230 |
while (it.hasNext() || !advanceToNext) {
|
231 |
if (advanceToNext) {
|
232 |
currentPoint = it.next(); |
233 |
} |
234 |
if (previousPoint == null) { |
235 |
previousPoint = currentPoint.clone(); |
236 |
currentSegment.addVertex(previousPoint); |
237 |
advanceToNext = true;
|
238 |
continue;
|
239 |
} |
240 |
double distance = previousPoint.distance(currentPoint);
|
241 |
if (!isHole) {
|
242 |
if (previousLength + distance < segmentLength) {
|
243 |
previousLength += distance; |
244 |
if(distance > 0.0){ |
245 |
currentSegment.addVertex(currentPoint); |
246 |
} |
247 |
previousPoint = currentPoint.cloneGeometry(); |
248 |
advanceToNext = true;
|
249 |
continue;
|
250 |
} else {
|
251 |
//buscar punto dentro del segmento a una distancia = segmentLengthMeters-previousLength
|
252 |
Point point = calculateIntermediatePoint(previousPoint, currentPoint, (segmentLength - previousLength) / distance);
|
253 |
//a?adirlo al currentSegment,
|
254 |
currentSegment.addVertex(point); |
255 |
//a?adir el currentSegment a la lista de segmentos
|
256 |
res.addSegment(currentSegment.cloneGeometry()); |
257 |
//crear un nuevo currentSegment y meter el punto como primero
|
258 |
currentSegment = geomManager.createLine(line.getGeometryType().getSubType()); |
259 |
currentSegment.addVertex(point); |
260 |
//cambiar modo ==> isHole = true
|
261 |
isHole = !isHole; |
262 |
previousPoint = point.clone(); |
263 |
previousLength = 0;
|
264 |
advanceToNext = false;
|
265 |
continue;
|
266 |
} |
267 |
} else {
|
268 |
if (previousLength + distance < holesLength) {
|
269 |
previousLength += distance; |
270 |
if(distance > 0.0){ |
271 |
currentSegment.addVertex(currentPoint); |
272 |
} |
273 |
previousPoint = currentPoint.cloneGeometry(); |
274 |
advanceToNext = true;
|
275 |
continue;
|
276 |
} else {
|
277 |
//buscar punto dentro del segmento a una distancia = segmentLengthMeters-previousLength
|
278 |
Point point = calculateIntermediatePoint(previousPoint, currentPoint, (holesLength - previousLength) / distance);
|
279 |
//a?adirlo al currentSegment,
|
280 |
currentSegment.addVertex(point); |
281 |
//a?adir el surrentSegment a la lista de segmentos
|
282 |
res.addHole(currentSegment.cloneGeometry()); |
283 |
//crear un nuevo currentSegment y meter el punto como primero
|
284 |
currentSegment = geomManager.createLine(line.getGeometryType().getSubType()); |
285 |
currentSegment.addVertex(point); |
286 |
//Cambiar modo Segment <==> Hole
|
287 |
isHole = !isHole; |
288 |
previousPoint = point.clone(); |
289 |
previousLength = 0;
|
290 |
advanceToNext = false;
|
291 |
continue;
|
292 |
} |
293 |
} |
294 |
} |
295 |
// currentSegment.addVertex(currentPoint);
|
296 |
if (currentSegment.getNumVertices() > 1) { |
297 |
if (isHole) {
|
298 |
res.addHole(currentSegment.cloneGeometry()); |
299 |
} else {
|
300 |
res.addSegment(currentSegment.cloneGeometry()); |
301 |
} |
302 |
} |
303 |
|
304 |
return res;
|
305 |
} |
306 |
|
307 |
Point calculateIntermediatePoint(Point p1, Point p2, double lambda) throws CreateGeometryException, GeometryOperationNotSupportedException, GeometryOperationException { |
308 |
GeometryManager geomManager = GeometryLocator.getGeometryManager(); |
309 |
GeometryType geomType = p1.getGeometryType(); |
310 |
int subtype = geomType.getSubType();
|
311 |
int dimension = geomType.getDimension();
|
312 |
double[] coords = new double[dimension]; |
313 |
Point p = geomManager.createPoint(0, 0, subtype); |
314 |
double distance = p1.distance(p2);
|
315 |
for (int d = 0; d < dimension; d++) { |
316 |
p.setCoordinateAt( |
317 |
d, |
318 |
p1.getCoordinateAt(d) + (p2.getCoordinateAt(d) - p1.getCoordinateAt(d)) * lambda); |
319 |
} |
320 |
|
321 |
return p;
|
322 |
} |
323 |
|
324 |
/*friend*/ static class SplittedLine { |
325 |
|
326 |
List<Line> segments; |
327 |
List<Line> holes; |
328 |
|
329 |
public SplittedLine() {
|
330 |
segments = new ArrayList<>(); |
331 |
holes = new ArrayList<>(); |
332 |
} |
333 |
|
334 |
public List<Line> getSegments() { |
335 |
return this.segments; |
336 |
} |
337 |
|
338 |
public List<Line> getHoles() { |
339 |
return this.holes; |
340 |
} |
341 |
|
342 |
public void addSegment(Line segment) { |
343 |
this.segments.add(segment);
|
344 |
} |
345 |
|
346 |
public void addHole(Line hole) { |
347 |
this.holes.add(hole);
|
348 |
} |
349 |
|
350 |
} |
351 |
|
352 |
} |