001: /*
002: * The JTS Topology Suite is a collection of Java classes that
003: * implement the fundamental operations required to validate a given
004: * geo-spatial data set to a known topological specification.
005: *
006: * Copyright (C) 2001 Vivid Solutions
007: *
008: * This library is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU Lesser General Public
010: * License as published by the Free Software Foundation; either
011: * version 2.1 of the License, or (at your option) any later version.
012: *
013: * This library is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public
019: * License along with this library; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021: *
022: * For more information, contact:
023: *
024: * Vivid Solutions
025: * Suite #1A
026: * 2328 Government Street
027: * Victoria BC V8T 5G5
028: * Canada
029: *
030: * (250)385-6040
031: * www.vividsolutions.com
032: */
033: package com.vividsolutions.jts.operation;
034:
035: import java.util.*;
036: import com.vividsolutions.jts.algorithm.BoundaryNodeRule;
037: import com.vividsolutions.jts.geom.*;
038: import com.vividsolutions.jts.geomgraph.GeometryGraph;
039:
040: /**
041: * Computes the boundary of a {@link Geometry}.
042: * Allows specifying the {@link BoundaryNodeRule} to be used.
043: * This operation will always return a {@link Geometry} of the appropriate
044: * dimension for the boundary (even if the input geometry is empty).
045: * The boundary of zero-dimensional geometries (Points) is
046: * always the empty {@link GeometryCollection}.
047: *
048: * @author Martin Davis
049: * @version 1.7
050: */
051:
052: public class BoundaryOp {
053: private Geometry geom;
054: private GeometryFactory geomFact;
055: private BoundaryNodeRule bnRule;
056:
057: public BoundaryOp(Geometry geom) {
058: this (geom, BoundaryNodeRule.MOD2_BOUNDARY_RULE);
059: }
060:
061: public BoundaryOp(Geometry geom, BoundaryNodeRule bnRule) {
062: this .geom = geom;
063: geomFact = geom.getFactory();
064: this .bnRule = bnRule;
065: }
066:
067: public Geometry getBoundary() {
068: if (geom instanceof LineString)
069: return boundaryLineString((LineString) geom);
070: if (geom instanceof MultiLineString)
071: return boundaryMultiLineString((MultiLineString) geom);
072: return geom.getBoundary();
073: }
074:
075: private MultiPoint getEmptyMultiPoint() {
076: return geomFact.createMultiPoint((CoordinateSequence) null);
077: }
078:
079: private Geometry boundaryMultiLineString(MultiLineString mLine) {
080: if (geom.isEmpty()) {
081: return getEmptyMultiPoint();
082: }
083:
084: Coordinate[] bdyPts = computeBoundaryCoordinates(mLine);
085:
086: // return Point or MultiPoint
087: if (bdyPts.length == 1) {
088: return geomFact.createPoint(bdyPts[0]);
089: }
090: // this handles 0 points case as well
091: return geomFact.createMultiPoint(bdyPts);
092: }
093:
094: /*
095: // MD - superseded
096: private Coordinate[] computeBoundaryFromGeometryGraph(MultiLineString mLine)
097: {
098: GeometryGraph g = new GeometryGraph(0, mLine, bnRule);
099: Coordinate[] bdyPts = g.getBoundaryPoints();
100: return bdyPts;
101: }
102: */
103:
104: private Map endpointMap;
105:
106: private Coordinate[] computeBoundaryCoordinates(
107: MultiLineString mLine) {
108: List bdyPts = new ArrayList();
109: endpointMap = new TreeMap();
110: for (int i = 0; i < mLine.getNumGeometries(); i++) {
111: LineString line = (LineString) mLine.getGeometryN(i);
112: if (line.getNumPoints() == 0)
113: continue;
114: addEndpoint(line.getCoordinateN(0));
115: addEndpoint(line.getCoordinateN(line.getNumPoints() - 1));
116: }
117:
118: for (Iterator it = endpointMap.entrySet().iterator(); it
119: .hasNext();) {
120: Map.Entry entry = (Map.Entry) it.next();
121: Counter counter = (Counter) entry.getValue();
122: int valence = counter.count;
123: if (bnRule.isInBoundary(valence)) {
124: bdyPts.add(entry.getKey());
125: }
126: }
127:
128: return CoordinateArrays.toCoordinateArray(bdyPts);
129: }
130:
131: private void addEndpoint(Coordinate pt) {
132: Counter counter = (Counter) endpointMap.get(pt);
133: if (counter == null) {
134: counter = new Counter();
135: endpointMap.put(pt, counter);
136: }
137: counter.count++;
138: }
139:
140: private Geometry boundaryLineString(LineString line) {
141: if (geom.isEmpty()) {
142: return getEmptyMultiPoint();
143: }
144:
145: if (line.isClosed()) {
146: // check whether endpoints of valence 2 are on the boundary or not
147: boolean closedEndpointOnBoundary = bnRule.isInBoundary(2);
148: if (closedEndpointOnBoundary) {
149: return line.getStartPoint();
150: } else {
151: return geomFact.createMultiPoint((Coordinate[]) null);
152: }
153: }
154: return geomFact.createMultiPoint(new Point[] {
155: line.getStartPoint(), line.getEndPoint() });
156: }
157: }
158:
159: /**
160: * Stores an integer count, for use as a Map entry.
161: *
162: * @author Martin Davis
163: * @version 1.7
164: */
165: class Counter {
166: /**
167: * The value of the count
168: */
169: int count;
170: }
|