001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032: package com.vividsolutions.jump.workbench.ui.plugin;
033:
034: import java.util.*;
035:
036: import com.vividsolutions.jts.geom.*;
037: import com.vividsolutions.jts.util.Assert;
038: import com.vividsolutions.jump.util.CollectionUtil;
039: import com.vividsolutions.jump.util.StringUtil;
040:
041: public class WKTDisplayHelper {
042: private static final int LINE_SPLIT_THRESHOLD = -1;
043:
044: /**
045: * @param wkt may contain syntax errors
046: */
047: public String format(String wkt) {
048: String formattedWKT = format(wkt, false);
049:
050: if (formattedWKT.length() > LINE_SPLIT_THRESHOLD) {
051: formattedWKT = format(wkt, true);
052: }
053:
054: return formattedWKT;
055: }
056:
057: private String format(String wkt, boolean splitting) {
058: int level = 0;
059: String lastNonBlankToken = "";
060: StringBuffer formattedWKT = new StringBuffer();
061: StringTokenizer tokenizer = new StringTokenizer(wkt,
062: " \t\n\r\f,()", true);
063:
064: while (tokenizer.hasMoreTokens()) {
065: String token = tokenizer.nextToken();
066:
067: if (token.trim().length() == 0) {
068: continue;
069: }
070:
071: if (token.equals(",")) {
072: formattedWKT.append(", ");
073: } else if (token.equals("(")) {
074: level++;
075: if (wordToken(lastNonBlankToken)) {
076: formattedWKT.append(" ");
077: }
078: formattedWKT.append("(");
079: } else if (token.equals(")")) {
080: int oldLevel = level;
081: level = Math.max(0, level - 1);
082: if (wordToken(lastNonBlankToken)) {
083: newLineAndIndentIfSplitting(formattedWKT, level,
084: splitting);
085: }
086:
087: formattedWKT.append(")");
088:
089: if (oldLevel == 1) {
090: formattedWKT.append(newLine());
091: }
092: } else {
093: if (wordToken(lastNonBlankToken)) {
094: formattedWKT.append(" ");
095: } else {
096: newLineAndIndentIfSplitting(formattedWKT, level,
097: splitting);
098: }
099:
100: formattedWKT.append(token);
101: }
102:
103: lastNonBlankToken = token;
104: }
105:
106: return formattedWKT.toString().trim();
107: }
108:
109: private String newLine() {
110: //Not System.getProperty("line.separator"); otherwise, when you copy
111: //into, say, Notepad, you get garbage characters at the end of each line
112: //(\r\r\n). [Jon Aquino]
113: return "\n";
114: }
115:
116: private void newLineAndIndentIfSplitting(StringBuffer formattedWKT,
117: int level, boolean splitting) {
118: if (splitting) {
119: formattedWKT.append(newLine() + indent(level));
120: }
121: }
122:
123: private boolean wordToken(String token) {
124: return !token.equals("(") && !token.equals(")")
125: && !token.equals(",");
126: }
127:
128: private Integer inc(Object i) {
129: return new Integer(((Integer) i).intValue() + 1);
130: }
131:
132: public String annotate(String wkt) {
133: int lineIndex = 0;
134: Stack stack = new Stack();
135: stack.push(new Integer(0));
136:
137: ArrayList annotations = new ArrayList();
138: StringTokenizer tokenizer = new StringTokenizer(wkt,
139: " \t\n\r\f,()", true);
140:
141: while (tokenizer.hasMoreTokens()) {
142: String token = tokenizer.nextToken();
143:
144: if (token.equals("\n")) {
145: lineIndex++;
146: } else if (token.trim().length() == 0) {
147: continue;
148: } else if (token.equals(",")) {
149: stack.push(inc(stack.pop()));
150: } else if (token.equals("(")) {
151: stack.push(new Integer(0));
152: } else if (token.equals(")")) {
153: if (stack.size() != 1) {
154: stack.pop();
155:
156: //Handle inconsistency: geometries are separated by whitespace,
157: //not commas. [Jon Aquino]
158: if (stack.size() == 1) {
159: stack.push(inc(stack.pop()));
160: }
161: }
162: } else {
163: if (StringUtil.isNumber(token)) {
164: CollectionUtil.setIfNull(lineIndex, annotations,
165: annotation(stack));
166: }
167: }
168: }
169:
170: //Ensure that there are the same number of annotation lines as WKT lines,
171: //for the sake of the scrollbars. [Jon Aquino]
172: CollectionUtil.resize(annotations, lineIndex + 1);
173:
174: return StringUtil.toDelimitedString(annotations, newLine());
175: }
176:
177: private String annotation(List indices) {
178: String annotation = "";
179:
180: for (Iterator i = indices.subList(1, indices.size()).iterator(); i
181: .hasNext();) {
182: Integer index = (Integer) i.next();
183:
184: if (annotation.trim().length() != 0) {
185: annotation += ":";
186: }
187:
188: annotation += index;
189: }
190:
191: return annotation;
192: }
193:
194: private String indent(int level) {
195: return StringUtil.repeat(' ', level * 4);
196: }
197:
198: public static void main(String[] args) {
199: String wkt = new WKTDisplayHelper().format(
200: "POINT(5 5)POINT(10 10)", false);
201: System.out.println(wkt);
202: System.out.println(new WKTDisplayHelper().annotate(wkt));
203: }
204:
205: public String annotation(Geometry geometry, Coordinate c) {
206: Stack stack = new Stack();
207: stack.push(new Integer(0));
208: Assert.isTrue(annotation(geometry, c, stack));
209:
210: return annotation(stack);
211: }
212:
213: private boolean annotation(Geometry geometry, Coordinate c,
214: Stack stack) {
215: stack.push(new Integer(0));
216:
217: if (geometry instanceof GeometryCollection) {
218: for (int i = 0; i < ((GeometryCollection) geometry)
219: .getNumGeometries(); i++) {
220: if (annotation(((GeometryCollection) geometry)
221: .getGeometryN(i), c, stack)) {
222: return true;
223: }
224: }
225: } else if (geometry instanceof Polygon) {
226: if (annotation(((Polygon) geometry).getExteriorRing(), c,
227: stack)) {
228: return true;
229: }
230:
231: for (int i = 0; i < ((Polygon) geometry)
232: .getNumInteriorRing(); i++) {
233: if (annotation(
234: ((Polygon) geometry).getInteriorRingN(i), c,
235: stack)) {
236: return true;
237: }
238: }
239: } else if (geometry instanceof LineString
240: || geometry instanceof Point) {
241: Coordinate[] coordinates = geometry.getCoordinates();
242:
243: for (int i = 0; i < coordinates.length; i++) {
244: if (coordinates[i] == c) {
245: return true;
246: }
247:
248: stack.push(inc(stack.pop()));
249: }
250: } else {
251: Assert.shouldNeverReachHere();
252: }
253:
254: stack.pop();
255: stack.push(inc(stack.pop()));
256:
257: return false;
258: }
259: }
|