001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.gml.producer;
017:
018: import java.text.FieldPosition;
019: import java.text.NumberFormat;
020: import java.util.Locale;
021:
022: import org.xml.sax.ContentHandler;
023: import org.xml.sax.SAXException;
024: import org.xml.sax.helpers.AttributesImpl;
025:
026: import com.vividsolutions.jts.geom.Coordinate;
027: import com.vividsolutions.jts.geom.CoordinateSequence;
028:
029: //import org.geotools.feature.*;
030:
031: /**
032: * Handles the writing of coordinates for GML.
033: *
034: * @author Chris Holmes
035: * @author Ian Schneider
036: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/gml/producer/CoordinateWriter.java $
037: */
038: public class CoordinateWriter {
039:
040: /**
041: * Internal representation of coordinate delimiter (',' for GML is default)
042: */
043: private final String coordinateDelimiter;
044:
045: /** Internal representation of tuple delimiter (' ' for GML is default) */
046: private final String tupleDelimiter;
047:
048: /** To be used for formatting numbers, uses US locale. */
049: private final NumberFormat coordFormatter = NumberFormat
050: .getInstance(Locale.US);
051:
052: private final AttributesImpl atts = new org.xml.sax.helpers.AttributesImpl();
053:
054: private final StringBuffer coordBuff = new StringBuffer();
055:
056: private final FieldPosition zero = new FieldPosition(0);
057:
058: private char[] buff = new char[200];
059:
060: /**
061: * True of dummyZ should be used.
062: */
063: private final boolean useDummyZ;
064:
065: /** Dummy Z value (used to override coordinate.Z value) */
066: private final double dummyZ;
067:
068: /** Dimension of expected coordinates */
069: private final int D;
070:
071: /**
072: * Flag controlling wether namespaces should be ignored.
073: */
074: private boolean namespaceAware = true;
075: /**
076: * Namepsace prefix + uri, default to gml
077: */
078: private String prefix = "gml";
079: private String namespaceUri = GMLUtils.GML_URL;
080:
081: public CoordinateWriter() {
082: this (4);
083: }
084:
085: public CoordinateWriter(int numDecimals, boolean isDummyZEnabled) {
086: this (numDecimals, " ", ",", isDummyZEnabled);
087: }
088:
089: public CoordinateWriter(int numDecimals) {
090: this (numDecimals, false);
091: }
092:
093: //TODO: check gml spec - can it be strings? Or just chars?
094: public CoordinateWriter(int numDecimals, String tupleDelim,
095: String coordDelim) {
096: this (numDecimals, tupleDelim, coordDelim, false);
097: }
098:
099: public CoordinateWriter(int numDecimals, String tupleDelim,
100: String coordDelim, boolean useDummyZ) {
101: this (numDecimals, tupleDelim, coordDelim, useDummyZ, 0);
102: }
103:
104: public CoordinateWriter(int numDecimals, String tupleDelim,
105: String coordDelim, boolean useDummyZ, double zValue) {
106: this (numDecimals, tupleDelim, coordDelim, useDummyZ, 0, 2);
107: }
108:
109: public CoordinateWriter(int numDecimals, boolean useDummyZ,
110: int dimension) {
111: this (numDecimals, " ", ",", useDummyZ, 0, dimension);
112: }
113:
114: /**
115: * Create a CoordinateWriter for outputting GML coordinates.
116: * <p>
117: * The use of dimension, z and useZ is based on your needs:
118: * <ul>
119: * <li>dimension: is from your CoordinateReferenceSystem; it is the number of axis used by the coordinate
120: * <li>useZ: is used to force the use of 3 dimensions (if needed the z value below will be used for 2D data)
121: * <li>z: the dummy z value to use if the coordinate does not have one
122: * </ul>
123: *
124: * @param numDecimals Number of decimals to use (a speed vs accuracy trade off)
125: * @param tupleDelim delimiter to use between ordinates (usually ',')
126: * @param coordDelim delimiter to use between coordinates (usually ' ')
127: * @param useZ true if the provided zValue should be forced
128: * @param z Dummy z value to use if needed
129: * @param dimension Dimension of coordinates (usually 2 or 3)
130: */
131: public CoordinateWriter(int numDecimals, String tupleDelim,
132: String coordDelim, boolean useZ, double z, int dimension) {
133: if (tupleDelim == null || tupleDelim.length() == 0) {
134: throw new IllegalArgumentException(
135: "Tuple delimeter cannot be null or zero length");
136: }
137: if ((coordDelim != null) && coordDelim.length() == 0) {
138: throw new IllegalArgumentException(
139: "Coordinate delimeter cannot be null or zero length");
140: }
141: D = dimension;
142:
143: tupleDelimiter = tupleDelim;
144: coordinateDelimiter = coordDelim;
145:
146: coordFormatter.setMaximumFractionDigits(numDecimals);
147: coordFormatter.setGroupingUsed(false);
148:
149: String uri = namespaceUri;
150: if (!namespaceAware) {
151: uri = null;
152: }
153:
154: atts.addAttribute(uri, "decimal", "decimal", "decimal", ".");
155: atts.addAttribute(uri, "cs", "cs", "cs", coordinateDelimiter);
156: atts.addAttribute(uri, "ts", "ts", "ts", tupleDelimiter);
157:
158: this .useDummyZ = useZ;
159: this .dummyZ = z;
160: }
161:
162: public int getNumDecimals() {
163: return coordFormatter.getMaximumFractionDigits();
164: }
165:
166: public boolean isDummyZEnabled() {
167: return useDummyZ;
168: }
169:
170: public void setNamespaceAware(boolean namespaceAware) {
171: this .namespaceAware = namespaceAware;
172: }
173:
174: public void setPrefix(String prefix) {
175: this .prefix = prefix;
176: }
177:
178: public void setNamespaceUri(String namespaceUri) {
179: this .namespaceUri = namespaceUri;
180: }
181:
182: /**
183: * Write the provided list of coordinates out.
184: * <p>
185: * There are a range of constants that control exactly what
186: * is written:
187: * <ul>
188: * <li>useDummyZ: if true dummyZ will be added to each coordiante
189: * <li>namespaceAware: is true the prefix and namespaceUri will be used
190: * <li>
191: * </ul>
192: *
193: * @param c
194: * @param output
195: * @throws SAXException
196: */
197: public void writeCoordinates(Coordinate[] c, ContentHandler output)
198: throws SAXException {
199:
200: String prefix = this .prefix + ":";
201: String namespaceUri = this .namespaceUri;
202:
203: if (!namespaceAware) {
204: prefix = "";
205: namespaceUri = null;
206: }
207:
208: output.startElement(namespaceUri, "coordinates", prefix
209: + "coordinates", atts);
210:
211: for (int i = 0, n = c.length; i < n; i++) {
212: // clear the buffer
213: coordBuff.delete(0, coordBuff.length());
214:
215: // format x into buffer and append delimiter
216: coordFormatter.format(c[i].x, coordBuff, zero).append(
217: coordinateDelimiter);
218:
219: // format y into buffer
220: if (D == 3 || useDummyZ) {
221: coordFormatter.format(c[i].y, coordBuff, zero).append(
222: coordinateDelimiter);
223: } else {
224: coordFormatter.format(c[i].y, coordBuff, zero);
225: }
226:
227: // format dummy z into buffer if required
228: if (D == 3) {
229: coordFormatter.format(c[i].z, coordBuff, zero);
230: } else if (useDummyZ) {
231: // 2D data being forced into 3D
232: coordFormatter.format(dummyZ, coordBuff, zero);
233: } else {
234: // 2D data; no z required
235: }
236:
237: // if there is another coordinate, tack on a tuple delimiter
238: if (i + 1 < c.length) {
239: coordBuff.append(tupleDelimiter);
240: }
241:
242: // make sure our character buffer is big enough
243: if (coordBuff.length() > buff.length) {
244: buff = new char[coordBuff.length()];
245: }
246: // copy the characters
247: coordBuff.getChars(0, coordBuff.length(), buff, 0);
248:
249: // finally, output
250: output.characters(buff, 0, coordBuff.length());
251: }
252: output.endElement(namespaceUri, "coordinates", prefix
253: + "coordinates");
254: }
255:
256: }
|