001: /*
002: * Geotools2 - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002, 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: */
017: package org.geotools.renderer.shape.shapehandler.simple;
018:
019: import java.awt.Rectangle;
020: import java.awt.geom.Point2D;
021: import java.nio.ByteBuffer;
022:
023: import org.geotools.data.shapefile.shp.ShapeHandler;
024: import org.geotools.data.shapefile.shp.ShapeType;
025: import org.geotools.renderer.shape.GeometryHandlerUtilities;
026: import org.geotools.renderer.shape.ScreenMap;
027: import org.geotools.renderer.shape.ShapefileRenderer;
028: import org.geotools.renderer.shape.SimpleGeometry;
029: import org.opengis.referencing.operation.MathTransform;
030: import org.opengis.referencing.operation.TransformException;
031:
032: import com.vividsolutions.jts.geom.Envelope;
033:
034: /**
035: * Creates Geometry line objects for use by the ShapeRenderer.
036: *
037: * @author jeichar
038: *
039: * @since 2.1.x
040: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/extension/shapefile-renderer/src/main/java/org/geotools/renderer/shape/shapehandler/simple/MultiLineHandler.java $
041: */
042: public class MultiLineHandler implements ShapeHandler {
043: static final int TOP = 0x8;
044: static final int BOTTOM = 0x4;
045: static final int RIGHT = 0x2;
046: static final int LEFT = 0x1;
047: private ShapeType type;
048: private Envelope bbox;
049: private MathTransform mt;
050: ScreenMap screenMap;
051: private Point2D span;
052:
053: /**
054: * Create new instance
055: *
056: * @param type the type of shape.
057: * @param env the area that is visible. If shape is not in area then skip.
058: * @param mt the transform to go from data to the envelope (and that should
059: * be used to transform the shape coords)
060: * @param hasOpacity
061: * @param screenSize
062: *
063: * @throws TransformException
064: */
065: public MultiLineHandler(ShapeType type, Envelope env,
066: MathTransform mt, boolean hasOpacity, Rectangle screenSize)
067: throws TransformException {
068: if (mt == null) {
069: throw new NullPointerException();
070: }
071: this .type = type;
072: this .bbox = env;
073: this .mt = mt;
074: screenMap = GeometryHandlerUtilities.calculateScreenSize(
075: screenSize, hasOpacity);
076: span = GeometryHandlerUtilities.calculateSpan(mt, 0, 0);
077:
078: }
079:
080: /**
081: * @see org.geotools.data.shapefile.shp.ShapeHandler#getShapeType()
082: */
083: public ShapeType getShapeType() {
084: return type;
085: }
086:
087: /**
088: * @see org.geotools.data.shapefile.shp.ShapeHandler#read(java.nio.ByteBuffer,
089: * org.geotools.data.shapefile.shp.ShapeType)
090: */
091: public Object read(ByteBuffer buffer, ShapeType type) {
092: if (type == ShapeType.NULL) {
093: return null;
094: }
095:
096: // read bounding box
097: Envelope geomBBox = GeometryHandlerUtilities.readBounds(buffer);
098:
099: if (!bbox.intersects(geomBBox)) {
100: return null;
101: }
102:
103: boolean bboxdecimate = (geomBBox.getWidth() <= span.getX())
104: && (geomBBox.getHeight() <= span.getY());
105: int numParts = buffer.getInt();
106: int numPoints = buffer.getInt(); // total number of points
107:
108: int[] partOffsets = new int[numParts];
109:
110: for (int i = 0; i < numParts; i++) {
111: partOffsets[i] = buffer.getInt();
112: }
113:
114: double[][] coords = new double[numParts][];
115: double[][] transformed = new double[numParts][];
116:
117: int finish;
118: int start = 0;
119: int length = 0;
120:
121: // if bbox is less than a pixel then decimate the geometry. But orientation must
122: // remain the same so geometry data must be parsed.
123: if (bboxdecimate) {
124: coords = new double[1][];
125: coords[0] = new double[4];
126: transformed = new double[1][];
127: transformed[0] = new double[4];
128: coords[0][0] = buffer.getDouble();
129: coords[0][1] = buffer.getDouble();
130: buffer
131: .position((buffer.position() + ((numPoints - 2) * 16)));
132: coords[0][2] = buffer.getDouble();
133: coords[0][3] = buffer.getDouble();
134:
135: if (!bbox.contains(coords[0][0], coords[0][1])
136: && !bbox.contains(coords[0][2], coords[0][3])) {
137: return null;
138: }
139:
140: try {
141: mt.transform(coords[0], 0, transformed[0], 0, 1);
142:
143: if (screenMap.get((int) transformed[0][0],
144: (int) transformed[0][1])) {
145: return null;
146: }
147:
148: screenMap.set((int) transformed[0][0],
149: (int) transformed[0][1], true);
150: mt.transform(coords[0], 2, transformed[0], 2, 1);
151:
152: double minx = (int) transformed[0][0];
153: double miny = (int) transformed[0][1];
154: double maxx = minx + 1;
155: double maxy = transformed[0][3];
156: transformed[0][0] = minx;
157: transformed[0][1] = miny;
158: transformed[0][2] = maxx;
159: transformed[0][3] = maxy;
160: } catch (Exception e) {
161: ShapefileRenderer.LOGGER
162: .severe("could not transform coordinates "
163: + e.getLocalizedMessage());
164: transformed[0] = coords[0];
165: }
166: } else {
167: boolean intersection = false;
168: int partsInBBox = 0;
169:
170: for (int part = 0; part < numParts; part++) {
171: intersection = false;
172: start = partOffsets[part];
173:
174: if (part == (numParts - 1)) {
175: finish = numPoints;
176: } else {
177: finish = partOffsets[part + 1];
178: }
179:
180: length = finish - start;
181: coords[part] = new double[length * 2];
182:
183: int readDoubles = 0;
184: int currentDoubles = 0;
185: int totalDoubles = length * 2;
186:
187: while (currentDoubles < totalDoubles) {
188: coords[part][readDoubles] = buffer.getDouble();
189: readDoubles++;
190: currentDoubles++;
191: coords[part][readDoubles] = buffer.getDouble();
192: readDoubles++;
193: currentDoubles++;
194: intersection = bboxIntersectSegment(intersection,
195: coords[part], readDoubles);
196:
197: if ((readDoubles > 3)
198: && (currentDoubles < (totalDoubles - 1))) {
199: if ((Math.abs(coords[part][readDoubles - 4]
200: - coords[part][readDoubles - 2]) <= span
201: .getX())
202: && (Math
203: .abs(coords[part][readDoubles - 3]
204: - coords[part][readDoubles - 1]) <= span
205: .getY())) {
206: readDoubles -= 2;
207: }
208: }
209: }
210:
211: if (!intersection) {
212: continue;
213: }
214:
215: if (!mt.isIdentity()) {
216: try {
217: transformed[partsInBBox] = new double[readDoubles];
218: GeometryHandlerUtilities.transform(type, mt,
219: coords[part], transformed[partsInBBox]);
220: } catch (Exception e) {
221: ShapefileRenderer.LOGGER
222: .severe("could not transform coordinates "
223: + e.getLocalizedMessage());
224: transformed[partsInBBox] = coords[part];
225: }
226: } else {
227: transformed[partsInBBox] = new double[readDoubles];
228: System.arraycopy(coords[part], 0,
229: transformed[partsInBBox], 0,
230: readDoubles / 2);
231: }
232:
233: partsInBBox++;
234: }
235:
236: if (partsInBBox == 0) {
237: return null;
238: }
239:
240: if (partsInBBox != numParts) {
241: double[][] tmp = new double[partsInBBox][];
242: System.arraycopy(transformed, 0, tmp, 0, partsInBBox);
243: transformed = tmp;
244: }
245: }
246:
247: return createGeometry(type, geomBBox, transformed);
248: }
249:
250: protected Object createGeometry(ShapeType type, Envelope geomBBox,
251: double[][] transformed) {
252: return new SimpleGeometry(type, transformed, geomBBox);
253: }
254:
255: public boolean bboxIntersectSegment(boolean intersection,
256: double[] coords, int index) {
257: if (intersection) {
258: return true;
259: }
260:
261: if (bbox.contains(coords[index - 2], coords[index - 1])) {
262: return true;
263: }
264:
265: if (index < 4) {
266: return false;
267: }
268:
269: return intersect(coords[index - 4], coords[index - 3],
270: coords[index - 2], coords[index - 1], bbox.getMinX(),
271: bbox.getMinY(), bbox.getMaxX(), bbox.getMaxY());
272: }
273:
274: /**
275: * Uses Cohen Sutherland line clipping algorithm to determine intersection.
276: * See Computer Graphics: Principles and Practice Foley, van Dam,
277: * Feiner, Hughes
278: *
279: * @param x0 DOCUMENT ME!
280: * @param y0 DOCUMENT ME!
281: * @param x1 DOCUMENT ME!
282: * @param y1 DOCUMENT ME!
283: * @param xmin DOCUMENT ME!
284: * @param ymin DOCUMENT ME!
285: * @param xmax DOCUMENT ME!
286: * @param ymax DOCUMENT ME!
287: *
288: * @return DOCUMENT ME!
289: */
290: public static boolean intersect(double x0, double y0, double x1,
291: double y1, double xmin, double ymin, double xmax,
292: double ymax) {
293: boolean accept = false;
294: boolean done = false;
295: int outcode0 = compOutCode(x0, y0, xmin, xmax, ymin, ymax);
296: int outcode1 = compOutCode(x1, y1, xmin, xmax, ymin, ymax);
297:
298: do {
299: if ((outcode0 == 0) || (outcode1 == 0)) { // trivial accept and exit
300: accept = done = true;
301: } else if ((outcode0 & outcode1) != 0) { // logical and is true so trivial reject and exit
302: done = true;
303: } else {
304: // failed both tests so calculate the line segment to clip:
305: //from an otside poin to the intersectin with clip edge.
306: double x;
307:
308: // failed both tests so calculate the line segment to clip:
309: //from an otside poin to the intersectin with clip edge.
310: double y;
311:
312: //at least one endpoint is outside the clip rectangle so pick it.
313: int outcodeOut = (outcode0 > 0) ? outcode0 : outcode1;
314:
315: //Now find intersection point
316: if ((outcodeOut & TOP) > 0) {
317: x = x0 + (((x1 - x0) * (ymax - y0)) / (y1 - y0));
318: y = ymax;
319: } else if ((outcodeOut & BOTTOM) > 0) {
320: x = x0 + (((x1 - x0) * (ymin - y0)) / (y1 - y0));
321: y = ymin;
322: } else if ((outcodeOut & RIGHT) > 0) {
323: y = y0 + (((y1 - y0) * (xmax - x0)) / (x1 - x0));
324: x = xmax;
325: } else {
326: y = y0 + (((y1 - y0) * (xmin - x0)) / (x1 - x0));
327: x = xmin;
328: }
329:
330: if (outcodeOut == outcode0) {
331: x0 = x;
332: y0 = y;
333: outcode0 = compOutCode(x0, y0, xmin, xmax, ymin,
334: ymax);
335: } else {
336: x1 = x;
337: y1 = y;
338: outcode1 = compOutCode(x1, y1, xmin, xmax, ymin,
339: ymax);
340: }
341: }
342: } while (done == false);
343:
344: return accept;
345: }
346:
347: private static int compOutCode(double x, double y, double xmin,
348: double xmax, double ymin, double ymax) {
349: int outcode = 0;
350:
351: if (y > ymax) {
352: outcode |= TOP;
353: }
354:
355: if (y < ymin) {
356: outcode |= BOTTOM;
357: }
358:
359: if (x > xmax) {
360: outcode |= RIGHT;
361: }
362:
363: if (x < xmin) {
364: outcode |= LEFT;
365: }
366:
367: return outcode;
368: }
369:
370: // /**
371: // * @return
372: // */
373: // private SimpleGeometry decimateBasedOnEnvelope(Envelope geomBBox) {
374: // if (geomBBox.getWidth() <= span.getX() && geomBBox.getHeight() <= span.getY()) {
375: // double[][] coords = new double[1][];
376: // coords[0] = new double[] { geomBBox.getMinX(), geomBBox.getMinY() };
377: // double[][] transformed = new double[1][];
378: // transformed[0] = new double[4];
379: // try {
380: // mt.transform(coords[0], 0, transformed[0], 0, 1);
381: // } catch (Exception e) {
382: // ShapefileRenderer.LOGGER.severe("could not transform coordinates "
383: // + e.getLocalizedMessage());
384: // transformed = coords;
385: // }
386: // transformed[0][2]=transformed[0][0];
387: // transformed[0][3]=transformed[0][1];
388: // return new SimpleGeometry(type, transformed, geomBBox);
389: // }
390: // return null;
391: // }
392:
393: /**
394: * @see org.geotools.data.shapefile.shp.ShapeHandler#write(java.nio.ByteBuffer,
395: * java.lang.Object)
396: */
397: public void write(ByteBuffer buffer, Object geometry) {
398: // This handler doesnt write
399: throw new UnsupportedOperationException(
400: "This handler is only for reading");
401: }
402:
403: /**
404: * @see org.geotools.data.shapefile.shp.ShapeHandler#getLength(java.lang.Object)
405: */
406: public int getLength(Object geometry) {
407: // TODO Auto-generated method stub
408: return 0;
409: }
410: }
|