Statistics
| Revision:

gvsig-lrs / org.gvsig.lrs / trunk / org.gvsig.lrs / org.gvsig.lrs.lib / org.gvsig.lrs.lib.impl / src / main / java / org / gvsig / lrs / lib / impl / LrsCreateRouteAlgorithm.java @ 34

History | View | Annotate | Download (48.6 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.lrs.lib.impl;
24

    
25
import java.util.ArrayList;
26
import java.util.Collections;
27
import java.util.Iterator;
28
import java.util.List;
29
import java.util.Map.Entry;
30
import java.util.SortedMap;
31
import java.util.TreeMap;
32

    
33
import org.apache.commons.lang3.mutable.MutableInt;
34
import org.slf4j.Logger;
35
import org.slf4j.LoggerFactory;
36

    
37
import org.gvsig.fmap.dal.feature.EditableFeature;
38
import org.gvsig.fmap.dal.feature.Feature;
39
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
40
import org.gvsig.fmap.dal.feature.FeatureSet;
41
import org.gvsig.fmap.dal.feature.FeatureStore;
42
import org.gvsig.fmap.dal.feature.NewFeatureStoreParameters;
43
import org.gvsig.fmap.geom.Geometry;
44
import org.gvsig.fmap.geom.Geometry.DIMENSIONS;
45
import org.gvsig.fmap.geom.GeometryLocator;
46
import org.gvsig.fmap.geom.GeometryManager;
47
import org.gvsig.fmap.geom.aggregate.MultiLine;
48
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
49
import org.gvsig.fmap.geom.exception.CreateGeometryException;
50
import org.gvsig.fmap.geom.operation.GeometryOperationException;
51
import org.gvsig.fmap.geom.operation.GeometryOperationNotSupportedException;
52
import org.gvsig.fmap.geom.primitive.Envelope;
53
import org.gvsig.fmap.geom.primitive.Line;
54
import org.gvsig.fmap.geom.primitive.Point;
55
import org.gvsig.lrs.lib.api.LrsAlgorithm;
56
import org.gvsig.lrs.lib.api.LrsAlgorithmParams;
57
import org.gvsig.lrs.lib.api.LrsCoordinatesPriority;
58
import org.gvsig.lrs.lib.api.LrsCreateRouteAlgorithmParams;
59
import org.gvsig.lrs.lib.api.LrsSourceOfMeasures;
60
import org.gvsig.lrs.lib.api.exceptions.LrsCreateRouteException;
61
import org.gvsig.lrs.lib.api.exceptions.LrsException;
62
import org.gvsig.tools.ToolsLocator;
63
import org.gvsig.tools.dataTypes.DataType;
64
import org.gvsig.tools.exception.BaseException;
65
import org.gvsig.tools.i18n.I18nManager;
66
import org.gvsig.tools.locator.LocatorException;
67
import org.gvsig.tools.service.Manager;
68
import org.gvsig.tools.task.SimpleTaskStatus;
69
import org.gvsig.tools.visitor.VisitCanceledException;
70
import org.gvsig.tools.visitor.Visitor;
71

    
72
/**
73
 * @author fdiaz
74
 *
75
 */
76
public class LrsCreateRouteAlgorithm implements LrsAlgorithm {
77

    
78
    private static final Logger logger = LoggerFactory.getLogger(LrsCreateRouteAlgorithm.class);
79

    
80
    private LrsCreateRouteAlgorithmParams parameters;
81

    
82
    /**
83
     *
84
     */
85
    public LrsCreateRouteAlgorithm(LrsCreateRouteAlgorithmParams parameters) {
86
        this.parameters = parameters;
87

    
88
    }
89

    
90
    /*
91
     * (non-Javadoc)
92
     *
93
     * @see org.gvsig.tools.service.Service#getManager()
94
     */
95
    public Manager getManager() {
96
        return null;
97
    }
98

    
99
    /*
100
     * (non-Javadoc)
101
     *
102
     * @see org.gvsig.lrs.lib.api.LrsAlgorithm#getParams()
103
     */
104
    public LrsAlgorithmParams getParams() {
105
        return this.parameters;
106
    }
107

    
108
    /*
109
     * (non-Javadoc)
110
     *
111
     * @see org.gvsig.lrs.lib.api.LrsAlgorithm#execute(org.gvsig.tools.task.
112
     * SimpleTaskStatus)
113
     */
114
    public void execute(SimpleTaskStatus taskStatus) throws LrsException {
115
        NewFeatureStoreParameters newFeatureStoreParameters = parameters.getNewFeatureStoreParameters();
116
        FeatureStore sourceFeatureStore = parameters.getSourceFeatureStore();
117
        FeatureAttributeDescriptor idRouteField = parameters.getIdRouteField();
118
        FeatureAttributeDescriptor fromMeasureField = parameters.getFromMeasureField();
119
        FeatureAttributeDescriptor toMeasureField = parameters.getToMeasureField();
120

    
121
        logger.info(parameters.toString());
122

    
123
        taskStatus.setTitle(parameters.getName());
124
        I18nManager i18nManager = ToolsLocator.getI18nManager();
125
        taskStatus.message(i18nManager.getTranslation("grouping_features"));
126

    
127
        try {
128
            final String routeFieldName = idRouteField.getName();
129
            final String fromFieldName;
130
            final DataType fromDataType;
131
            final DataType toDataType;
132
            if (fromMeasureField != null) {
133
                fromFieldName = fromMeasureField.getName();
134
                fromDataType = fromMeasureField.getDataType();
135
            } else {
136
                fromFieldName = null;
137
                fromDataType = null;
138
            }
139
            final String toFieldName;
140
            if (toMeasureField != null) {
141
                toFieldName = toMeasureField.getName();
142
                toDataType = toMeasureField.getDataType();
143
            } else {
144
                toFieldName = null;
145
                toDataType = null;
146
            }
147

    
148
            FeatureStore newFeatureStore =
149
                LrsAlgorithmUtils.createNewDataStore(newFeatureStoreParameters, idRouteField);
150

    
151
            FeatureSet sourceFeatures;
152
            if (sourceFeatureStore.getFeatureSelection().getSize() > 0) {
153
                sourceFeatures = sourceFeatureStore.getFeatureSelection();
154
            } else {
155
                sourceFeatures = sourceFeatureStore.getFeatureSet();
156
            }
157

    
158
            final SortedMap<String, List<MSegment>> featuresMap = new TreeMap<String, List<MSegment>>();
159
            final MutableInt contId = new MutableInt(0);
160
            sourceFeatures.accept(new Visitor() {
161

    
162
                public void visit(Object obj) throws VisitCanceledException, BaseException {
163
                    Feature feature = (Feature) obj;
164
                    String routeName = (String) feature.get(routeFieldName);
165
                    Object objFrom = null;
166
                    Object objTo = null;
167
                    if (fromFieldName != null) {
168
                        objFrom = feature.get(fromFieldName);
169
                    }
170
                    if (toFieldName != null) {
171
                        objTo = feature.get(toFieldName);
172
                    }
173
                    if (!featuresMap.containsKey(routeName)) {
174
                        featuresMap.put(routeName, new ArrayList<MSegment>());
175
                    }
176
                    List<MSegment> mList = featuresMap.get(routeName);
177
                    MSegment mSegment = new MSegment();
178
                    mSegment.geometry = feature.getDefaultGeometry().cloneGeometry();
179
                    mSegment.fromValue = LrsAlgorithmUtils.getAsDouble(objFrom, fromDataType);
180
                    mSegment.toValue = LrsAlgorithmUtils.getAsDouble(objTo, toDataType);
181
                    mSegment.id = contId.getValue();
182
                    contId.increment();
183
                    mList.add(mSegment);
184
                    featuresMap.put(routeName, mList);
185
                }
186
            });
187

    
188
            taskStatus.setRangeOfValues(0, featuresMap.size());
189
            int taskCount = 0;
190

    
191
            newFeatureStore.edit(FeatureStore.MODE_FULLEDIT);
192

    
193
            for (Entry<String, List<MSegment>> entry : featuresMap.entrySet()) {
194
                String routeName = entry.getKey();
195
                List<MSegment> mList = entry.getValue();
196

    
197
                EditableFeature newFeature = newFeatureStore.createNewFeature(true);
198
                newFeature.set(routeFieldName, routeName);
199
                Geometry route = createGeometryRoute(mList);
200
                newFeature.setDefaultGeometry(route);
201

    
202
                newFeatureStore.update(newFeature);
203

    
204
                taskCount++;
205
                taskStatus.setCurValue(taskCount);
206

    
207
            }
208
            newFeatureStore.finishEditing();
209

    
210
        } catch (Exception e1) {
211
            taskStatus.abort();
212
            throw new LrsCreateRouteException("Error creating routes", e1);
213
        }
214

    
215
        taskStatus.terminate();
216

    
217
    }
218

    
219
    private Geometry createGeometryRoute(List<MSegment> mList) throws CreateGeometryException,
220
        GeometryOperationNotSupportedException, GeometryOperationException, CreateEnvelopeException {
221
        LrsSourceOfMeasures sourceOfMeasures = parameters.getSourceOfMeasures();
222
        Geometry geometryResult = null;
223
        simplifyMultilines(mList);
224
        switch (sourceOfMeasures) {
225
        case ONE_FIELD:
226
            geometryResult = calculateGeometryByOneField(mList);
227
            break;
228
        case TWO_FIELDS:
229
            geometryResult = calculateGeometryByTwoField(mList);
230
            break;
231
        case LENGTH:
232
        default:
233
            geometryResult = calculateGeometryByLength(mList);
234
            break;
235
        }
236
        geometryResult = applyOffsetAndFactor(geometryResult);
237
        return geometryResult;
238
    }
239

    
240
    private Geometry calculateGeometryByLength(List<MSegment> mList) throws CreateGeometryException,
241
        GeometryOperationNotSupportedException, GeometryOperationException, CreateEnvelopeException {
242
        boolean ignoreSpatialGaps = parameters.ignoreSpatialGaps();
243
        for (MSegment mSegment : mList) {
244
            mSegment.fromValue = LrsAlgorithmUtils.getGeometryLength(mSegment.geometry, ignoreSpatialGaps);
245
        }
246
        return calculateGeometryByOneField(mList);
247
    }
248

    
249
    private Geometry calculateGeometryByOneField(List<MSegment> mList) throws CreateGeometryException,
250
        GeometryOperationNotSupportedException, GeometryOperationException, CreateEnvelopeException {
251
        boolean ignoreSpatialGaps = parameters.ignoreSpatialGaps();
252
        GeometryManager gmanager = GeometryLocator.getGeometryManager();
253
        try {
254
            mList = sortMList(mList);
255
        } catch (Exception e) {
256
            logger.warn("Geometries couldn't be ordered");
257
        }
258

    
259
        MultiLine auxMultiLine = (MultiLine) gmanager.create(Geometry.TYPES.MULTILINE, Geometry.SUBTYPES.GEOM2DM);
260

    
261
        Point previousPoint = null;
262
        double previousM = 0.0;
263
        for (MSegment mSegment : mList) {
264
            Geometry geom = mSegment.geometry;
265
            Double geometryLength = mSegment.getLength(ignoreSpatialGaps);
266
            if (geom instanceof Line) {
267
                Line line = (Line) geom;
268
                Line auxLine = (Line) gmanager.create(Geometry.TYPES.LINE, Geometry.SUBTYPES.GEOM2DM);
269
                double distance = 0.0;
270
                double firstM = previousM;
271
                for (int i = 0; i < line.getNumVertices(); i++) {
272
                    Point vertex = line.getVertex(i);
273
                    Point point = (Point) gmanager.create(Geometry.TYPES.POINT, Geometry.SUBTYPES.GEOM2DM);
274
                    point.setX(vertex.getX());
275
                    point.setY(vertex.getY());
276
                    if (i == 0 && previousPoint != null) {
277
                        boolean gap = false;
278
                        if (areInSame2DLocation(previousPoint, vertex)) {
279
                            // Buscamos si ha sido una bifurcaci?n
280
                            previousPoint = getPossibleFork(auxMultiLine, vertex);
281
                            // OJO, aqu? previousPoint pasa a ser 2DM
282
                        } else {
283
                            // En caso de salto, calculamos el previousPoint
284
                            // // y si no, buscamos el v?rtice m?s cercano
285
                            previousPoint = getClosestVertex(auxMultiLine, vertex);
286
                            // OJO, aqu? previousPoint pasa a ser 2DM
287
                            gap = true;
288
                        }
289
                        previousM = previousPoint.getCoordinateAt(previousPoint.getDimension() - 1);
290
                        if (i == 0) {
291
                            firstM = previousM;
292
                        }
293
                        if (gap && !ignoreSpatialGaps) {
294
                            Point previousVertex = getPreviousVertexToPoint(auxMultiLine, previousPoint);
295
                            previousVertex.getCoordinateAt(previousVertex.getDimension() - 1);
296
                            previousM =
297
                                strightLineThroughTwoPointsEquation(0, previousVertex.distance(previousPoint),
298
                                    previousVertex.getCoordinateAt(previousVertex.getDimension() - 1), previousM,
299
                                    previousVertex.distance(previousPoint) + previousPoint.distance(point));
300
                            firstM = previousM;
301
                        }
302
                    }
303
                    if (i != 0) {
304
                        distance += previousPoint.distance(vertex);
305
                    }
306
                    double m =
307
                        strightLineThroughTwoPointsEquation(0, geometryLength, firstM, firstM + mSegment.fromValue,
308
                            distance);
309
                    point.setCoordinateAt(point.getDimension() - 1, m);
310
                    auxLine.addVertex(point);
311
                    previousM = m;
312
                    previousPoint = vertex;
313
                }
314
                auxMultiLine.addPrimitive(auxLine);
315
            } else if (geom instanceof MultiLine) {
316
                MultiLine multiline = (MultiLine) geom;
317
                for (int i = 0; i < multiline.getPrimitivesNumber(); i++) {
318
                    Line line = (Line) multiline.getPrimitiveAt(i);
319
                    Line auxLine = (Line) gmanager.create(Geometry.TYPES.LINE, Geometry.SUBTYPES.GEOM2DM);
320
                    double distance = 0.0;
321
                    double firstM = previousM;
322
                    for (int j = 0; j < line.getNumVertices(); j++) {
323
                        Point vertex = line.getVertex(j);
324
                        Point point = (Point) gmanager.create(Geometry.TYPES.POINT, Geometry.SUBTYPES.GEOM2DM);
325
                        point.setX(vertex.getX());
326
                        point.setY(vertex.getY());
327
                        if (j == 0 && previousPoint != null) {
328
                            boolean gap = false;
329
                            if (areInSame2DLocation(previousPoint, vertex)) {
330
                                // Buscamos si ha sido una bifurcaci?n
331
                                previousPoint = getPossibleFork(auxMultiLine, vertex);
332
                                // OJO, aqu? previousPoint pasa a ser 2DM
333
                            } else {
334
                                // En caso de salto, calculamos el previousPoint
335
                                previousPoint = getClosestVertex(auxMultiLine, vertex);
336
                                // OJO, aqu? previousPoint pasa a ser 2DM
337
                                gap = true;
338
                            }
339
                            previousM = previousPoint.getCoordinateAt(previousPoint.getDimension() - 1);
340
                            if (j == 0) {
341
                                firstM = previousM;
342
                            }
343
                            if (gap && !ignoreSpatialGaps) {
344
                                Point previousVertex = getPreviousVertexToPoint(auxMultiLine, previousPoint);
345
                                previousVertex.getCoordinateAt(previousVertex.getDimension() - 1);
346
                                previousM =
347
                                    strightLineThroughTwoPointsEquation(0, previousVertex.distance(previousPoint),
348
                                        previousVertex.getCoordinateAt(previousVertex.getDimension() - 1), previousM,
349
                                        previousVertex.distance(previousPoint) + previousPoint.distance(point));
350
                                firstM = previousM;
351
                            }
352
                        }
353
                        if (j != 0) {
354
                            distance += previousPoint.distance(vertex);
355
                        }
356
                        double m =
357
                            strightLineThroughTwoPointsEquation(0, geometryLength, firstM, firstM + mSegment.fromValue,
358
                                distance);
359
                        point.setCoordinateAt(point.getDimension() - 1, m);
360
                        auxLine.addVertex(point);
361
                        previousM = m;
362
                        previousPoint = vertex;
363
                    }
364
                    multiline.addPrimitive(auxLine);
365
                }
366
            } else {
367
                // NO deber?a entrar por aqu?
368
                logger.warn("A not LINE nor MULTILINE geometry found in CreateRoute process");
369
            }
370
        }
371

    
372
        return compactMultiLine(auxMultiLine);
373
    }
374

    
375
    /**
376
     * Joins consecutive adjacent lines into the multiLine that are not involved in a fork
377
     *
378
     * @param auxMultiLine
379
     * @return
380
     * @throws CreateGeometryException
381
     * @throws GeometryOperationException
382
     * @throws GeometryOperationNotSupportedException
383
     */
384
    private MultiLine compactMultiLine(MultiLine multiLine) throws CreateGeometryException, GeometryOperationNotSupportedException, GeometryOperationException {
385
        GeometryManager geomManager = GeometryLocator.getGeometryManager();
386
        MultiLine result =  (MultiLine) geomManager.create(multiLine.getGeometryType());
387

    
388
        int primitivesNumber = multiLine.getPrimitivesNumber();
389
        List<Line> lines = new ArrayList<Line>(primitivesNumber);
390
        for(int i = 0; i < primitivesNumber; i++){
391
            lines.add((Line)multiLine.getPrimitiveAt(i).cloneGeometry());
392
        }
393
        if (lines.size() > 0) {
394
            Line line = lines.get(0);
395
            while (lines.size() > 1) {
396
                Point lastVertex = line.getVertex(line.getNumVertices() - 1);
397
                lines.remove(0);
398
                // Borramos la primera linea de la lista ==> lines.get(0) es la siguiente
399
                Line line2 = lines.get(0);
400
                // Si el ultimo punto de la primera
401
                if (lastVertex.equals(line2.getVertex(0)) && !bifurcation(lastVertex, lines)) {
402
                    for(int i = 1; i<line2.getNumVertices(); i++){
403
                        line.addVertex(line2.getVertex(i));
404
                    }
405
                } else {
406
                    result.addPrimitive(line);
407
                    line = line2;
408
                }
409
            }
410
            result.addPrimitive(line);
411
        }
412
        return result;
413
    }
414

    
415
    /**
416
     * Checks if a bifurcation occurs at one point.
417
     *
418
     * @param point
419
     * @param lines
420
     * @return
421
     */
422
    private boolean bifurcation(Point point, List<org.gvsig.fmap.geom.primitive.Line> lines) {
423
        // Saltamos la primera linea
424
        for(int i = 1; i<lines.size(); i++){
425
            if(point.equals(lines.get(i).getVertex(0))){
426
                return true;
427
            }
428
        }
429
        return false;
430
    }
431

    
432
    /**
433
     * Returns the previous vertex to a point.
434
     *
435
     * @param auxMultiLine
436
     * @param previousPoint
437
     * @return
438
     * @throws GeometryOperationException
439
     * @throws GeometryOperationNotSupportedException
440
     * @throws CreateGeometryException
441
     */
442
    private Point getPreviousVertexToPoint(MultiLine multiLine, Point point) throws CreateGeometryException,
443
        GeometryOperationNotSupportedException, GeometryOperationException {
444
        GeometryManager geomManager = GeometryLocator.getGeometryManager();
445
        for (int i = 0; i < multiLine.getPrimitivesNumber(); i++) {
446
            Line line = (Line) multiLine.getPrimitiveAt(i);
447
            if (line.intersects(point)) {
448
                for (int j = 0; j < line.getNumVertices() - 1; j++) {
449
                    if (point.equals(line.getVertex(j + 1))) {
450
                        return line.getVertex(j);
451
                    }
452
                    Line auxLine = (Line) geomManager.create(Geometry.TYPES.LINE, Geometry.SUBTYPES.GEOM2DM);
453
                    auxLine.addVertex(line.getVertex(j));
454
                    auxLine.addVertex(line.getVertex(j + 1));
455
                    if (auxLine.intersects(point)) {
456
                        return line.getVertex(j);
457
                    }
458
                }
459
            }
460
        }
461
        return null;
462
    }
463

    
464
    /**
465
     * Checks if a bifurcation occurs at one point and return the vertex with maximum M
466
     *
467
     * @param multiLine
468
     * @param vertex
469
     * @return
470
     */
471
    private Point getPossibleFork(MultiLine multiLine, Point point) {
472

    
473
        List<Point> vertices = new ArrayList<Point>();
474
        for (int i = 0; i < multiLine.getPrimitivesNumber(); i++) {
475
            Line line = (Line) multiLine.getPrimitiveAt(i);
476
            for (int j = 0; j < line.getNumVertices(); j++) {
477
                Point vertex = line.getVertex(j);
478
                if (areInSame2DLocation(vertex, point)) {
479
                    vertices.add(vertex);
480
                }
481
            }
482
        }
483
        if (vertices.size() > 0) {
484
            Double maxM = Double.NEGATIVE_INFINITY;
485
            Point forked = null;
486
            for (Iterator<Point> iterator = vertices.iterator(); iterator.hasNext();) {
487
                Point vertex = (Point) iterator.next();
488
                double m = vertex.getCoordinateAt(vertex.getDimension() - 1);
489
                if (m > maxM) {
490
                    maxM = m;
491
                    forked = vertex;
492
                }
493
            }
494
            return forked;
495
        }
496
        return null;
497
    }
498

    
499
    /**
500
     * Compares x & y coordinates of a point.
501
     *
502
     * @param p1
503
     * @param p2
504
     * @return
505
     */
506
    private boolean areInSame2DLocation(Point p1, Point p2) {
507
        return ((p1.getX() == p2.getX()) && (p1.getY() == p2.getY()));
508
    }
509

    
510
    /**
511
     * Returns the vertex of the multiline closest to a point
512
     *
513
     * @param mGeometry
514
     * @param vertex
515
     * @return
516
     * @throws GeometryOperationException
517
     * @throws GeometryOperationNotSupportedException
518
     * @throws CreateEnvelopeException
519
     */
520
    private Point getClosestVertex(MultiLine multiLine, Point point) throws CreateEnvelopeException,
521
        GeometryOperationNotSupportedException, GeometryOperationException {
522
        Point result = null;
523
        double minDistance = Double.POSITIVE_INFINITY;
524

    
525
        for (int i = 0; i < multiLine.getPrimitivesNumber(); i++) {
526
            Line line = (Line) multiLine.getPrimitiveAt(i);
527
            for (int j = 0; j < line.getNumVertices(); j++) {
528
                Point vertex = line.getVertex(j);
529
                double distance = point.distance(vertex);
530
                if (distance <= minDistance) {
531
                    minDistance = distance;
532
                    result = vertex;
533
                }
534
            }
535
        }
536

    
537
        return result;
538
    }
539

    
540
    /**
541
     * Returns a multiline calculate by two field method
542
     *
543
     * @param mList
544
     * @return
545
     * @throws CreateGeometryException
546
     * @throws GeometryOperationNotSupportedException
547
     * @throws GeometryOperationException
548
     */
549
    private MultiLine calculateGeometryByTwoField(List<MSegment> mList) throws CreateGeometryException,
550
        GeometryOperationNotSupportedException, GeometryOperationException {
551
        GeometryManager geomanager = GeometryLocator.getGeometryManager();
552
        MultiLine mGeometry = (MultiLine) geomanager.create(Geometry.TYPES.MULTILINE, Geometry.SUBTYPES.GEOM2DM);
553

    
554
        for (MSegment mSegment : mList) {
555
            Geometry geometry = mSegment.geometry;
556
            List<Line> geometryLines = extractLines(geometry);
557
            Double geometryLength = LrsAlgorithmUtils.getGeometryLength(geometry);
558

    
559
            Double geometryMFirstPoint = mSegment.fromValue;
560
            Double geometryMLastPoint = mSegment.toValue;
561

    
562
            Double distance = Double.valueOf(0);
563
            for (Line line : geometryLines) {
564
                Double lineLength = getLineLength(line);
565
                Double mFirstPoint = calculateM(geometryMLastPoint, geometryMFirstPoint, distance, geometryLength);
566
                distance += lineLength;
567
                Double mLastPoint = calculateM(geometryMLastPoint, geometryMFirstPoint, distance, geometryLength);
568
                Line mLine = lineToMLine(line, mFirstPoint, mLastPoint);
569
                mGeometry.addPrimitive(mLine);
570
            }
571
        }
572

    
573
        return mGeometry;
574
    }
575

    
576
    /**
577
     * Converts a Line2D in a Line2DM filled it with proportional calculated M's
578
     *
579
     * @param line
580
     * @param minMValue
581
     * @param maxMValue
582
     * @return
583
     * @throws CreateGeometryException
584
     * @throws GeometryOperationNotSupportedException
585
     * @throws GeometryOperationException
586
     */
587
    private Line lineToMLine(Line line, Double minMValue, Double maxMValue) throws CreateGeometryException,
588
        GeometryOperationNotSupportedException, GeometryOperationException {
589
        GeometryManager geomanager = GeometryLocator.getGeometryManager();
590
        Double lineLength = getLineLength(line);
591
        Line lineM = (Line) geomanager.create(Geometry.TYPES.LINE, Geometry.SUBTYPES.GEOM2DM);
592
        Double inLineDistance = Double.valueOf(0);
593
        for (int i = 0; i < line.getNumVertices(); i++) {
594
            Point vertex = line.getVertex(i);
595
            Point mVertex = (Point) geomanager.create(Geometry.TYPES.POINT, Geometry.SUBTYPES.GEOM2DM);
596
            mVertex.setX(vertex.getX());
597
            mVertex.setY(vertex.getY());
598

    
599
            Double mValue;
600
            if (i == 0)
601
                mValue = minMValue;
602
            else if (i == line.getNumVertices() - 1)
603
                mValue = maxMValue;
604
            else {
605
                Point previousVertex = line.getVertex(i - 1);
606
                inLineDistance += vertex.distance(previousVertex);
607
                mValue = calculateM(maxMValue, minMValue, inLineDistance, lineLength);
608
            }
609

    
610
            mVertex.setCoordinateAt(mVertex.getDimension() - 1, mValue);
611
            lineM.addVertex(mVertex);
612
        }
613
        return lineM;
614
    }
615

    
616
    /**
617
     * Reduced versi?n of stright line through two points equation to calculate M's
618
     *
619
     * @param maxValue
620
     * @param minValue
621
     * @param relativeDistance
622
     * @param totalLength
623
     * @return
624
     */
625
    private Double calculateM(Double maxValue, Double minValue, Double relativeDistance, Double totalLength) {
626
        if (totalLength.equals(Double.valueOf(0)))
627
            return Double.POSITIVE_INFINITY;
628
        return ((maxValue - minValue) * (relativeDistance) / (totalLength)) + minValue;
629
    }
630

    
631
    /**
632
     * Stright line through two points equation.
633
     *
634
     * @param x1
635
     * @param x2
636
     * @param y1
637
     * @param y2
638
     * @param x
639
     * @return
640
     */
641
    private double strightLineThroughTwoPointsEquation(double x1, double x2, double y1, double y2, double x) {
642
        if (x2 - x1 == 0.0) {
643
            return Double.POSITIVE_INFINITY;
644
        }
645
        return ((y2 - y1) * (x - x1) / (x2 - x1)) + y1;
646
    }
647

    
648
    /**
649
     * Simplify the multilines in mList calling simplifyMultiline method
650
     *
651
     * @param mList
652
     * @throws CreateGeometryException
653
     * @throws LocatorException
654
     */
655
    private void simplifyMultilines(List<MSegment> mList) throws CreateGeometryException, LocatorException {
656
        for (MSegment mSegment : mList) {
657
            mSegment.geometry = simplifyMultiline(mSegment.geometry);
658
        }
659
    }
660

    
661
    /**
662
     * Simplify a Multiline ordering and joining her lines if can.
663
     *
664
     * @param geometry
665
     * @return
666
     * @throws CreateGeometryException
667
     * @throws LocatorException
668
     */
669
    private Geometry simplifyMultiline(Geometry geometry) throws CreateGeometryException, LocatorException {
670
        if (geometry instanceof MultiLine) {
671
            MultiLine multiline = (MultiLine) geometry;
672

    
673
            if (multiline.getPrimitivesNumber() == 1) {
674
                return multiline.getPrimitiveAt(0);
675
            } else {
676
                List<Line> simplifiedLines = new ArrayList<Line>();
677
                List<Line> complexLines = new ArrayList<Line>();
678
                for (int i = 0; i < multiline.getPrimitivesNumber(); i++) {
679
                    complexLines.add((Line) multiline.getPrimitiveAt(i));
680
                }
681

    
682
                Line line = null;
683
                while (complexLines.size() > 0) {
684
                    line = complexLines.remove(0);
685
                    int i = 0;
686
                    while (i < complexLines.size()) {
687
                        Line auxLine = complexLines.get(i);
688
                        Line unitedLine = unionAdjacentLines(line, auxLine);
689
                        if (unitedLine != null) {
690
                            line = unitedLine;
691
                            complexLines.remove(i);
692
                            i = 0;
693
                        } else {
694
                            i++;
695
                        }
696
                    }
697
                    simplifiedLines.add(line);
698
                }
699

    
700
                if (simplifiedLines.size() == 1) {
701
                    geometry = simplifiedLines.get(0);
702
                } else {
703
                    MultiLine simplifiedMultiLine =
704
                        (MultiLine) GeometryLocator.getGeometryManager().create(multiline.getGeometryType());
705
                    for (Line simpleLine : simplifiedLines) {
706
                        simplifiedMultiLine.addPrimitive(simpleLine);
707
                    }
708
                    return simplifiedMultiLine;
709
                }
710
            }
711
        }
712
        return geometry;
713
    }
714

    
715
    /**
716
     * Join two adjacent lines flipping it if necessary
717
     *
718
     * @param line1
719
     * @param line2
720
     * @return
721
     */
722
    private Line unionAdjacentLines(Line line1, Line line2) {
723
        if (line1 == null || line2 == null) {
724
            return null;
725
        }
726
        Line resultLine;
727
        try {
728
            resultLine = (Line) GeometryLocator.getGeometryManager().create(line1.getGeometryType());
729
        } catch (Exception e) {
730
            return null;
731
        }
732

    
733
        Point firstPointL1 = line1.getVertex(0);
734
        Point lastPointL1 = line1.getVertex(line1.getNumVertices() - 1);
735
        Point firstPointL2 = line2.getVertex(0);
736
        Point lastPointL2 = line2.getVertex(line2.getNumVertices() - 1);
737
        if (lastPointL1.equals(firstPointL2)) {
738
            resultLine = (Line) line1.cloneGeometry();
739
            for (int i = 1; i < line2.getNumVertices(); i++) {
740
                resultLine.addVertex((Point) line2.getVertex(i).cloneGeometry());
741
            }
742
            return resultLine;
743
        }
744
        if (lastPointL2.equals(firstPointL1)) {
745
            resultLine = (Line) line2.cloneGeometry();
746
            for (int i = 1; i < line1.getNumVertices(); i++) {
747
                resultLine.addVertex((Point) line1.getVertex(i).cloneGeometry());
748
            }
749
            return resultLine;
750
        }
751
        if (firstPointL1.equals(firstPointL2)) {
752
            for (int i = line1.getNumVertices() - 1; i >= 0; i--) {
753
                resultLine.addVertex((Point) line1.getVertex(i).cloneGeometry());
754
            }
755
            for (int i = 1; i < line2.getNumVertices(); i++) {
756
                resultLine.addVertex((Point) line2.getVertex(i).cloneGeometry());
757
            }
758
            return resultLine;
759
        }
760
        if (lastPointL1.equals(lastPointL2)) {
761
            resultLine = (Line) line1.cloneGeometry();
762
            for (int i = line2.getNumVertices() - 2; i >= 0; i--) {
763
                resultLine.addVertex((Point) line2.getVertex(i).cloneGeometry());
764
            }
765
            return resultLine;
766
        }
767
        return null;
768
    }
769

    
770
    /**
771
     * Sorts mList
772
     *
773
     * @param mList
774
     * @return
775
     * @throws CreateEnvelopeException
776
     * @throws GeometryOperationNotSupportedException
777
     * @throws GeometryOperationException
778
     */
779
    private List<MSegment> sortMList(List<MSegment> mList) throws CreateEnvelopeException,
780
        GeometryOperationNotSupportedException, GeometryOperationException {
781
        LrsCoordinatesPriority coordinatePriority = parameters.getCoordinatePriority();
782
        GeometryManager geomanager = GeometryLocator.getGeometryManager();
783
        Envelope envelope = geomanager.createEnvelope(Geometry.SUBTYPES.GEOM2D);
784
        for (MSegment mSegment : mList) {
785
            envelope.add(mSegment.geometry.getEnvelope());
786
        }
787
        Point origin = envelope.getLowerCorner();
788
        switch (coordinatePriority) {
789
        case DOWN_LEFT:
790
            break;
791
        case DOWN_RIGHT:
792
            Double maxX = envelope.getMaximum(DIMENSIONS.X);
793
            origin.setX(maxX);
794
            break;
795
        case UP_LEFT:
796
            Double maxY = envelope.getMaximum(DIMENSIONS.Y);
797
            origin.setY(maxY);
798
            break;
799
        case UP_RIGHT:
800
            origin = envelope.getUpperCorner();
801
            break;
802
        default:
803
            break;
804
        }
805

    
806
        List<Stretch> stretches = createStretches(mList, origin);
807
        stretches = sortStretches(stretches, origin);
808
        return extractSegmentsFromStretches(stretches);
809
    }
810

    
811
    /**
812
     * Extracts segment from stretches and returns a list of segment.
813
     *
814
     * @param stretches
815
     * @return
816
     */
817
    private List<MSegment> extractSegmentsFromStretches(List<Stretch> stretches) {
818
        List<MSegment> result = new ArrayList<MSegment>();
819
        for (Stretch stretch : stretches) {
820
            for (int i = 0; i < stretch.getSegmentNumber(); i++) {
821
                result.add(stretch.getSegment(i));
822
            }
823
        }
824
        return result;
825
    }
826

    
827
    /**
828
     * Sort the stretches
829
     *
830
     * @param stretches
831
     * @param origin
832
     * @return
833
     * @throws GeometryOperationNotSupportedException
834
     * @throws GeometryOperationException
835
     */
836
    private List<Stretch> sortStretches(List<Stretch> stretches, Point origin)
837
        throws GeometryOperationNotSupportedException, GeometryOperationException {
838
        List<Stretch> result = new ArrayList<Stretch>();
839

    
840
        Point originPoint = origin;
841
        while (result.size() < stretches.size()) {
842
            // Buscamos tramos que sean adyacentes al origin
843
            if (result.size() == 0) {
844
                addUnvisitedAdjacentStretches(stretches, result, originPoint);
845
            }
846
            if (result.size() == 0) {
847
                result.add(getNearestUnvisitedStretch(stretches, originPoint, result));
848
            }
849

    
850
            boolean addedAdjacentStretches = true;
851
            while (addedAdjacentStretches) {
852
                addedAdjacentStretches = false;
853
                int visitedNumber = result.size();
854
                for (int i = 0; i < visitedNumber; i++) {
855
                    Stretch visited = result.get(i);
856
                    addUnvisitedAdjacentStretches(stretches, result, visited.getFinalPoint());
857
                }
858
                addedAdjacentStretches = (visitedNumber < result.size());
859
            }
860

    
861
            int visitedNumber = result.size();
862
            for (int i = visitedNumber - 1; i >= 0; i--) {
863
                Stretch visited = result.get(i);
864
                Stretch nearest = getNearestUnvisitedStretch(stretches, visited.getFinalPoint(), result);
865
                if (nearest != null) {
866
                    result.add(nearest);
867
                    break;
868
                }
869
            }
870
        }
871
        return result;
872
    }
873

    
874
    /**
875
     * Adds unvisited adjacent stretches to the visited list
876
     *
877
     * @param stretches
878
     * @param visited
879
     * @param originPoint
880
     * @throws GeometryOperationNotSupportedException
881
     * @throws GeometryOperationException
882
     */
883
    private void addUnvisitedAdjacentStretches(List<Stretch> stretches, List<Stretch> visited, Point originPoint)
884
        throws GeometryOperationNotSupportedException, GeometryOperationException {
885
        for (Stretch stretch : stretches) {
886
            if (!visited.contains(stretch)) {
887
                if (areInSame2DLocation(stretch.getInitialPoint(), originPoint)) {
888
                    visited.add(stretch);
889
                } else if (areInSame2DLocation(stretch.getFinalPoint(), originPoint)) {
890
                    visited.add(stretch.flip());
891
                }
892
            }
893
        }
894
    }
895

    
896
    /**
897
     * Returns the unvisited stretch nearest to a point
898
     *
899
     * @param stretches
900
     * @param origin
901
     * @param visited
902
     * @return
903
     * @throws GeometryOperationNotSupportedException
904
     * @throws GeometryOperationException
905
     */
906
    private Stretch getNearestUnvisitedStretch(List<Stretch> stretches, Point origin, List<Stretch> visited)
907
        throws GeometryOperationNotSupportedException, GeometryOperationException {
908

    
909
        double minDistance = Double.POSITIVE_INFINITY;
910
        Stretch nearest = null;
911
        boolean needFlip = false;
912
        for (Stretch stretch : stretches) {
913
            if (!visited.contains(stretch)) {
914
                Double initialDistance = origin.distance(stretch.getInitialPoint());
915
                if (initialDistance < minDistance) {
916
                    minDistance = initialDistance;
917
                    nearest = stretch;
918
                    needFlip = false;
919
                }
920
                Double finalDistance = origin.distance(stretch.getFinalPoint());
921
                if (finalDistance < minDistance) {
922
                    minDistance = finalDistance;
923
                    nearest = stretch;
924
                    needFlip = true;
925
                }
926
            }
927
        }
928
        if (needFlip && nearest != null) {
929
            nearest.flip();
930
        }
931
        return nearest;
932
    }
933

    
934
    /**
935
     * Creates stretches from mList and returns a list of stretches
936
     *
937
     * @param mList
938
     * @param origin
939
     * @return
940
     * @throws GeometryOperationNotSupportedException
941
     * @throws GeometryOperationException
942
     */
943
    private List<Stretch> createStretches(List<MSegment> mList, Point origin)
944
        throws GeometryOperationNotSupportedException, GeometryOperationException {
945

    
946
        List<MSegment> addedMSegment = new ArrayList<LrsCreateRouteAlgorithm.MSegment>();
947
        List<Stretch> stretches = new ArrayList<Stretch>();
948
        while (addedMSegment.size() < mList.size()) {
949
            for (MSegment mSegment : mList) {
950
                if (!addedMSegment.contains(mSegment)) {
951
                    Stretch stretch = new Stretch();
952
                    stretches.add(stretch);
953
                    stretch.addSegment(mSegment);
954
                    addedMSegment.add(mSegment);
955

    
956
                    List<MSegment> initialNextSegments =
957
                        mSegment.searchNextSegments(stretch.getInitialPoint(), mList);
958
                    do {
959
                        if (initialNextSegments.size() == 1) {
960
                            MSegment previousSegment = initialNextSegments.get(0);
961
                            if (!addedMSegment.contains(previousSegment)) {
962
                                if (!previousSegment.getFinalPoint().equals(stretch.getInitialPoint())) {
963
                                    previousSegment.flip();
964
                                }
965
                                stretch.addSegment(0, previousSegment);
966
                                addedMSegment.add(previousSegment);
967
                                initialNextSegments =
968
                                    previousSegment.searchNextSegments(stretch.getInitialPoint(), mList);
969
                            } else {
970
                                break;
971
                            }
972
                        } else {
973
                            break;
974
                        }
975
                    } while (initialNextSegments.size() == 1);
976

    
977
                    List<MSegment> finalNextSegments =
978
                        mSegment.searchNextSegments(stretch.getFinalPoint(), mList);
979
                    do {
980
                        if (finalNextSegments.size() == 1) {
981
                            MSegment nextSegment = finalNextSegments.get(0);
982
                            if (!addedMSegment.contains(nextSegment)) {
983
                                if (!nextSegment.getInitialPoint().equals(stretch.getFinalPoint())) {
984
                                    nextSegment.flip();
985
                                }
986
                                stretch.addSegment(nextSegment);
987
                                addedMSegment.add(nextSegment);
988
                                finalNextSegments =
989
                                    nextSegment.searchNextSegments(stretch.getFinalPoint(), mList);
990
                            } else {
991
                                break;
992
                            }
993
                        } else {
994
                            break;
995
                        }
996
                    } while ((finalNextSegments.size() == 1));
997
                }
998
            }
999
        }
1000
        return stretches;
1001
    }
1002

    
1003
    /**
1004
     * Extracts lines of a geometry.
1005
     *
1006
     * @param geometry
1007
     * @return
1008
     */
1009
    private List<Line> extractLines(Geometry geometry) {
1010
        List<Line> lines = new ArrayList<Line>();
1011
        if (geometry instanceof Line) {
1012
            lines.add((Line) geometry);
1013
        }
1014
        if (geometry instanceof MultiLine) {
1015
            MultiLine multiline = (MultiLine) geometry;
1016
            for (int i = 0; i < multiline.getPrimitivesNumber(); i++) {
1017
                lines.add((Line) multiline.getPrimitiveAt(i));
1018
            }
1019
        }
1020
        return lines;
1021
    }
1022

    
1023
    /**
1024
     * Applies offsetAndFactor to a route
1025
     *
1026
     * @param geometry
1027
     * @return
1028
     */
1029
    private Geometry applyOffsetAndFactor(Geometry geometry) {
1030
        double measureFactor = parameters.getMeasureFactor();
1031
        double measureOffset = parameters.getMeasureOffset();
1032
        if (geometry instanceof Line) {
1033
            Line line = (Line) geometry;
1034
            for (int i = 0; i < line.getNumVertices(); i++) {
1035
                Point mVertex = line.getVertex(i);
1036
                Double mValue = mVertex.getCoordinateAt(mVertex.getDimension() - 1);
1037
                Double newMValue = (mValue * measureFactor) + measureOffset;
1038
                mVertex.setCoordinateAt(mVertex.getDimension() - 1, newMValue);
1039
                line.setVertex(i, mVertex);
1040
            }
1041
        }
1042
        if (geometry instanceof MultiLine) {
1043
            MultiLine multiLine = (MultiLine) geometry;
1044
            for (int i = 0; i < multiLine.getPrimitivesNumber(); i++) {
1045
                Line line = (Line) multiLine.getPrimitiveAt(i);
1046
                for (int j = 0; j < line.getNumVertices(); j++) {
1047
                    Point mVertex = line.getVertex(j);
1048
                    Double mValue = mVertex.getCoordinateAt(mVertex.getDimension() - 1);
1049
                    Double newMValue = (mValue * measureFactor) + measureOffset;
1050
                    mVertex.setCoordinateAt(mVertex.getDimension() - 1, newMValue);
1051
                    line.setVertex(j, mVertex);
1052
                }
1053
            }
1054
        }
1055
        return geometry;
1056
    }
1057

    
1058
    /**
1059
     * Returns the geometric length of a line
1060
     *
1061
     * @param line
1062
     * @return lenght
1063
     * @throws GeometryOperationException
1064
     * @throws GeometryOperationNotSupportedException
1065
     */
1066
    private double getLineLength(Line line) throws GeometryOperationNotSupportedException, GeometryOperationException {
1067
        double lenght = 0;
1068
        Point previousVertex = null;
1069
        for (int i = 0; i < line.getNumVertices(); i++) {
1070
            Point vertex = line.getVertex(i);
1071
            if (previousVertex != null) {
1072
                lenght += previousVertex.distance(vertex);
1073
            }
1074
            previousVertex = vertex;
1075
        }
1076
        return lenght;
1077
    }
1078

    
1079
    /*
1080
     * (non-Javadoc)
1081
     *
1082
     * @see org.gvsig.lrs.lib.api.LrsAlgorithm#setParams(org.gvsig.lrs.lib.api.
1083
     * LrsAlgorithmParams)
1084
     */
1085
    public void setParams(LrsAlgorithmParams params) throws IllegalArgumentException {
1086
        if (!(params instanceof LrsCreateRouteAlgorithmParams)) {
1087
            throw new IllegalArgumentException("params should be LrsCreateRouteAlgorithmParams type.");
1088
        }
1089
    }
1090

    
1091
    private class MSegment {
1092

    
1093
        Geometry geometry;
1094
        Double fromValue;
1095
        Double toValue;
1096
        int id;
1097

    
1098
        protected MSegment clone() {
1099
            MSegment cloned = new MSegment();
1100
            cloned.geometry = geometry.cloneGeometry();
1101

    
1102
            cloned.fromValue = null;
1103
            if (fromValue != null) {
1104
                cloned.fromValue = new Double(fromValue);
1105
            }
1106

    
1107
            cloned.toValue = null;
1108
            if (toValue != null) {
1109
                cloned.toValue = new Double(toValue);
1110
            }
1111

    
1112
            cloned.id = id;
1113

    
1114
            return cloned;
1115
        }
1116

    
1117
        protected Point getInitialPoint() {
1118
            if (geometry instanceof Line) {
1119
                Line line = ((Line) geometry);
1120
                return line.getVertex(0);
1121
            } else if (geometry instanceof MultiLine) {
1122
                MultiLine multiLine = (MultiLine) geometry;
1123
                Line firstLine = ((Line) multiLine.getPrimitiveAt(0));
1124
                return firstLine.getVertex(0);
1125
            }
1126
            return null;
1127
        }
1128

    
1129
        protected Point getFinalPoint() {
1130
            if (geometry instanceof Line) {
1131
                Line line = ((Line) geometry);
1132
                return line.getVertex(line.getNumVertices() - 1);
1133
            } else if (geometry instanceof MultiLine) {
1134
                MultiLine multiLine = (MultiLine) geometry;
1135
                Line lastLine = ((Line) multiLine.getPrimitiveAt(multiLine.getPrimitivesNumber() - 1));
1136
                return lastLine.getVertex(lastLine.getNumVertices() - 1);
1137
            }
1138
            return null;
1139
        }
1140

    
1141
        protected void flip() throws GeometryOperationNotSupportedException, GeometryOperationException {
1142
            geometry.flip();
1143
        }
1144

    
1145
        /**
1146
         * @param point
1147
         * @param mList
1148
         * @return
1149
         */
1150
        protected List<MSegment> searchNextSegments(Point point, List<MSegment> mList) {
1151
            List<MSegment> result = new ArrayList<LrsCreateRouteAlgorithm.MSegment>();
1152
            for (Iterator<MSegment> iterator = mList.iterator(); iterator.hasNext();) {
1153
                MSegment mSegment = (MSegment) iterator.next();
1154
                if (mSegment != this) { // !addedSegments.contains(mSegment)) {
1155
                    Geometry geom = mSegment.geometry;
1156
                    if (geom instanceof Line) {
1157
                        Line line = ((Line) geom);
1158
                        if (line.getVertex(0).equals(point) || line.getVertex(line.getNumVertices() - 1).equals(point)) {
1159
                            result.add(mSegment);
1160
                        }
1161
                        ;
1162
                    } else if (geom instanceof MultiLine) {
1163
                        MultiLine multiLine = (MultiLine) geom;
1164
                        Line firstLine = ((Line) multiLine.getPrimitiveAt(0));
1165
                        Line lastLine = ((Line) multiLine.getPrimitiveAt(multiLine.getPrimitivesNumber() - 1));
1166
                        if (firstLine.getVertex(0).equals(point)
1167
                            || lastLine.getVertex(lastLine.getNumVertices() - 1).equals(point)) {
1168
                            result.add(mSegment);
1169
                        }
1170
                    }
1171
                }
1172
            }
1173
            return result;
1174
        }
1175

    
1176
        public double getLength(boolean ignoreSpatialGaps) throws GeometryOperationNotSupportedException,
1177
            GeometryOperationException {
1178
            return LrsAlgorithmUtils.getGeometryLength(geometry, ignoreSpatialGaps);
1179
        }
1180
    }
1181

    
1182
    private class Stretch {
1183

    
1184
        private List<MSegment> segments;
1185
        private Point initialPoint;
1186
        private Point finalPoint;
1187

    
1188
        public Stretch() {
1189
            segments = new ArrayList<MSegment>();
1190
        }
1191

    
1192
        public MSegment getSegment(int index) {
1193
            return segments.get(index);
1194
        }
1195

    
1196
        public int getSegmentNumber() {
1197
            return segments.size();
1198
        }
1199

    
1200
        public void addSegment(MSegment segment) {
1201
            if (segments.isEmpty()) {
1202
                initialPoint = segment.getInitialPoint();
1203
            }
1204
            finalPoint = segment.getFinalPoint();
1205
            segments.add(segment);
1206
        }
1207

    
1208
        public void addSegment(int index, MSegment segment) {
1209
            if (index == 0) {
1210
                initialPoint = segment.getInitialPoint();
1211
            }
1212
            if (index >= segments.size()) {
1213
                finalPoint = segment.getFinalPoint();
1214
            }
1215
            segments.add(index, segment);
1216
        }
1217

    
1218
        public double getLength(boolean ignoreSpatialGaps) throws GeometryOperationNotSupportedException,
1219
            GeometryOperationException {
1220
            double result = 0.0;
1221
            for (MSegment segment : segments) {
1222
                result += segment.getLength(ignoreSpatialGaps);
1223
            }
1224
            return result;
1225
        }
1226

    
1227
        public Stretch flip() throws GeometryOperationNotSupportedException, GeometryOperationException {
1228
            Collections.reverse(segments);
1229
            for (Iterator<MSegment> iterator = segments.iterator(); iterator.hasNext();) {
1230
                MSegment segment = (MSegment) iterator.next();
1231
                segment.flip();
1232
            }
1233
            Point aux = initialPoint;
1234
            initialPoint = finalPoint;
1235
            finalPoint = aux;
1236
            return this;
1237
        }
1238

    
1239
        public Point getInitialPoint() {
1240
            return initialPoint;
1241
        }
1242

    
1243
        public Point getFinalPoint() {
1244
            return finalPoint;
1245
        }
1246

    
1247
    }
1248

    
1249
}