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 @ 39

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

    
375
        return compactMultiLine(auxMultiLine);
376
    }
377

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

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

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

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

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

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

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

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

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

    
540
        return result;
541
    }
542

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

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

    
562
            Double geometryMFirstPoint = mSegment.fromValue;
563
            Double geometryMLastPoint = mSegment.toValue;
564

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

    
576
        return mGeometry;
577
    }
578

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

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

    
613
            mVertex.setCoordinateAt(mVertex.getDimension() - 1, mValue);
614
            lineM.addVertex(mVertex);
615
        }
616
        return lineM;
617
    }
618

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

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

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

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

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

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

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

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

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

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

    
809
        List<Stretch> stretches = createStretches(mList, origin);
810
        stretches = sortStretches(stretches, origin);
811
        return extractSegmentsFromStretches(stretches);
812
    }
813

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1094
    private class MSegment {
1095

    
1096
        Geometry geometry;
1097
        Double fromValue;
1098
        Double toValue;
1099
        int id;
1100

    
1101
        protected MSegment clone() {
1102
            MSegment cloned = new MSegment();
1103
            cloned.geometry = geometry.cloneGeometry();
1104

    
1105
            cloned.fromValue = null;
1106
            if (fromValue != null) {
1107
                cloned.fromValue = new Double(fromValue);
1108
            }
1109

    
1110
            cloned.toValue = null;
1111
            if (toValue != null) {
1112
                cloned.toValue = new Double(toValue);
1113
            }
1114

    
1115
            cloned.id = id;
1116

    
1117
            return cloned;
1118
        }
1119

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

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

    
1144
        protected void flip() throws GeometryOperationNotSupportedException, GeometryOperationException {
1145
            geometry.flip();
1146
        }
1147

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

    
1179
        public double getLength(boolean ignoreSpatialGaps) throws GeometryOperationNotSupportedException,
1180
            GeometryOperationException {
1181
            return LrsAlgorithmUtils.getGeometryLength(geometry, ignoreSpatialGaps);
1182
        }
1183
    }
1184

    
1185
    private class Stretch {
1186

    
1187
        private List<MSegment> segments;
1188
        private Point initialPoint;
1189
        private Point finalPoint;
1190

    
1191
        public Stretch() {
1192
            segments = new ArrayList<MSegment>();
1193
        }
1194

    
1195
        public MSegment getSegment(int index) {
1196
            return segments.get(index);
1197
        }
1198

    
1199
        public int getSegmentNumber() {
1200
            return segments.size();
1201
        }
1202

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

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

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

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

    
1242
        public Point getInitialPoint() {
1243
            return initialPoint;
1244
        }
1245

    
1246
        public Point getFinalPoint() {
1247
            return finalPoint;
1248
        }
1249

    
1250
    }
1251

    
1252
}