/* * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI * for visualizing and manipulating spatial features with geometry and attributes. * * Copyright (C) 2003 Vivid Solutions * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For more information, contact: * * Vivid Solutions * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ package com.vividsolutions.jump.workbench.ui.plugin; import java.util.*; import com.vividsolutions.jts.geom.*; import com.vividsolutions.jts.util.Assert; import com.vividsolutions.jump.util.CollectionUtil; import com.vividsolutions.jump.util.StringUtil; public class WKTDisplayHelper { private static final int LINE_SPLIT_THRESHOLD = -1; /** * @param wkt may contain syntax errors */ public String format(String wkt) { String formattedWKT = format(wkt, false); if (formattedWKT.length() > LINE_SPLIT_THRESHOLD) { formattedWKT = format(wkt, true); } return formattedWKT; } private String format(String wkt, boolean splitting) { int level = 0; String lastNonBlankToken = ""; StringBuffer formattedWKT = new StringBuffer(); StringTokenizer tokenizer = new StringTokenizer(wkt, " \t\n\r\f,()", true); while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (token.trim().length() == 0) { continue; } if (token.equals(",")) { formattedWKT.append(", "); } else if (token.equals("(")) { level++; if (wordToken(lastNonBlankToken)) { formattedWKT.append(" "); } formattedWKT.append("("); } else if (token.equals(")")) { int oldLevel = level; level = Math.max(0, level - 1); if (wordToken(lastNonBlankToken)) { newLineAndIndentIfSplitting(formattedWKT, level, splitting); } formattedWKT.append(")"); if (oldLevel == 1) { formattedWKT.append(newLine()); } } else { if (wordToken(lastNonBlankToken)) { formattedWKT.append(" "); } else { newLineAndIndentIfSplitting(formattedWKT, level, splitting); } formattedWKT.append(token); } lastNonBlankToken = token; } return formattedWKT.toString().trim(); } private String newLine() { //Not System.getProperty("line.separator"); otherwise, when you copy //into, say, Notepad, you get garbage characters at the end of each line //(\r\r\n). [Jon Aquino] return "\n"; } private void newLineAndIndentIfSplitting(StringBuffer formattedWKT, int level, boolean splitting) { if (splitting) { formattedWKT.append(newLine() + indent(level)); } } private boolean wordToken(String token) { return !token.equals("(") && !token.equals(")") && !token.equals(","); } private Integer inc(Object i) { return new Integer(((Integer) i).intValue() + 1); } public String annotate(String wkt) { int lineIndex = 0; Stack stack = new Stack(); stack.push(new Integer(0)); ArrayList annotations = new ArrayList(); StringTokenizer tokenizer = new StringTokenizer(wkt, " \t\n\r\f,()", true); while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (token.equals("\n")) { lineIndex++; } else if (token.trim().length() == 0) { continue; } else if (token.equals(",")) { stack.push(inc(stack.pop())); } else if (token.equals("(")) { stack.push(new Integer(0)); } else if (token.equals(")")) { if (stack.size() != 1) { stack.pop(); //Handle inconsistency: geometries are separated by whitespace, //not commas. [Jon Aquino] if (stack.size() == 1) { stack.push(inc(stack.pop())); } } } else { if (StringUtil.isNumber(token)) { CollectionUtil.setIfNull(lineIndex, annotations, annotation(stack)); } } } //Ensure that there are the same number of annotation lines as WKT lines, //for the sake of the scrollbars. [Jon Aquino] CollectionUtil.resize(annotations, lineIndex + 1); return StringUtil.toDelimitedString(annotations, newLine()); } private String annotation(List indices) { String annotation = ""; for (Iterator i = indices.subList(1, indices.size()).iterator(); i.hasNext();) { Integer index = (Integer) i.next(); if (annotation.trim().length() != 0) { annotation += ":"; } annotation += index; } return annotation; } private String indent(int level) { return StringUtil.repeat(' ', level * 4); } public static void main(String[] args) { String wkt = new WKTDisplayHelper().format("POINT(5 5)POINT(10 10)", false); System.out.println(wkt); System.out.println(new WKTDisplayHelper().annotate(wkt)); } public String annotation(Geometry geometry, Coordinate c) { Stack stack = new Stack(); stack.push(new Integer(0)); Assert.isTrue(annotation(geometry, c, stack)); return annotation(stack); } private boolean annotation(Geometry geometry, Coordinate c, Stack stack) { stack.push(new Integer(0)); if (geometry instanceof GeometryCollection) { for (int i = 0; i < ((GeometryCollection) geometry).getNumGeometries(); i++) { if (annotation(((GeometryCollection) geometry).getGeometryN(i), c, stack)) { return true; } } } else if (geometry instanceof Polygon) { if (annotation(((Polygon) geometry).getExteriorRing(), c, stack)) { return true; } for (int i = 0; i < ((Polygon) geometry).getNumInteriorRing(); i++) { if (annotation(((Polygon) geometry).getInteriorRingN(i), c, stack)) { return true; } } } else if (geometry instanceof LineString || geometry instanceof Point) { Coordinate[] coordinates = geometry.getCoordinates(); for (int i = 0; i < coordinates.length; i++) { if (coordinates[i] == c) { return true; } stack.push(inc(stack.pop())); } } else { Assert.shouldNeverReachHere(); } stack.pop(); stack.push(inc(stack.pop())); return false; } }