001: /*
002: * uDig - User Friendly Desktop Internet GIS client
003: * http://udig.refractions.net
004: * (C) 2004, Refractions Research Inc.
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 net.refractions.udig.project.ui.internal.commands.draw;
018:
019: import java.awt.Color;
020: import java.awt.Dimension;
021: import java.awt.Rectangle;
022: import java.awt.geom.AffineTransform;
023: import java.io.IOException;
024: import java.util.Map;
025: import java.util.concurrent.ConcurrentHashMap;
026:
027: import net.refractions.udig.project.ILayer;
028: import net.refractions.udig.project.render.displayAdapter.IMapDisplay;
029: import net.refractions.udig.project.ui.commands.AbstractDrawCommand;
030: import net.refractions.udig.ui.Drawing;
031: import net.refractions.udig.ui.PlatformGIS;
032: import net.refractions.udig.ui.graphics.AWTGraphics;
033: import net.refractions.udig.ui.graphics.SWTGraphics;
034:
035: import org.eclipse.core.runtime.IProgressMonitor;
036: import org.eclipse.swt.graphics.Image;
037: import org.eclipse.swt.graphics.Point;
038: import org.eclipse.swt.widgets.Display;
039: import org.eclipse.ui.views.properties.IPropertyDescriptor;
040: import org.geotools.feature.Feature;
041: import org.geotools.geometry.jts.JTS;
042: import org.geotools.referencing.CRS;
043: import org.geotools.referencing.crs.DefaultGeographicCRS;
044: import org.geotools.styling.Symbolizer;
045: import org.opengis.referencing.crs.CoordinateReferenceSystem;
046: import org.opengis.referencing.operation.MathTransform;
047: import org.opengis.referencing.operation.TransformException;
048:
049: import com.vividsolutions.jts.geom.Envelope;
050:
051: /**
052: * Draws a feature on the screen.
053: *
054: * @author jeichar
055: * @since 0.9
056: */
057: public class DrawFeatureCommand extends AbstractDrawCommand {
058:
059: private Feature feature;
060: private static final Map<MathTransformKey, MathTransform> mtCache = new ConcurrentHashMap<MathTransformKey, MathTransform>();
061:
062: private Drawing drawing = Drawing.create();
063: private CoordinateReferenceSystem featureCRS;
064:
065: Symbolizer[] syms;
066:
067: private Color color = Color.ORANGE;
068:
069: private MathTransform mt;
070: /**
071: * The location that the image should be drawn at.
072: */
073: // private Point imageLocation;
074: /**
075: * The image of the drawn feature.
076: * @see #preRender()
077: */
078: private Image image;
079:
080: /**
081: * @param feature
082: * @param layer layer that feature is from
083: * @throws IOException
084: */
085: public DrawFeatureCommand(Feature feature, ILayer layer)
086: throws IOException {
087: this (feature, layer.getCRS());
088: }
089:
090: /**
091: * @param feature
092: * @param crs
093: */
094: public DrawFeatureCommand(Feature feature,
095: CoordinateReferenceSystem crs) {
096: this .feature = feature;
097: if (crs == null)
098: this .featureCRS = DefaultGeographicCRS.WGS84;
099: else
100: this .featureCRS = crs;
101: }
102:
103: /**
104: * @param feature
105: */
106: public DrawFeatureCommand(Feature feature) {
107: this (feature, feature.getFeatureType().getDefaultGeometry()
108: .getCoordinateSystem());
109: }
110:
111: /**
112: * Renders the feature to a image buffer so that drawing command will be fast.
113: * If feature is large you should call this so that there isn't a big delay in the display
114: * thread.
115: * <p>
116: * setMap() must be called before calling this method.
117: * </p>
118: * <p>
119: * If this method is called then this object must be sent to the ViewportPane or be disposed
120: * because a Image object is created that needs to be disposed.
121: * </p>
122: *
123: */
124: public void preRender() {
125:
126: if (BUFFER_READY) {
127:
128: PlatformGIS.syncInDisplayThread(new Runnable() {
129: public void run() {
130: renderInternal();
131: }
132: });
133: }
134: }
135:
136: private void renderInternal() {
137: if (syms != null)
138: syms = Drawing.getSymbolizers(feature.getDefaultGeometry()
139: .getClass(), color, false);
140: MathTransform mt = getMathTransform(featureCRS);
141: AffineTransform toScreen = getMap().getViewportModel()
142: .worldToScreenTransform();
143:
144: // calculate the size of the image and where it will be in the display
145: Envelope envelope;
146: try {
147: envelope = JTS.transform(feature.getBounds(), null, mt, 10);
148: } catch (Exception e) {
149: envelope = feature.getBounds();
150: }
151: double[] screenbounds = new double[] { envelope.getMinX(),
152: envelope.getMinY(), envelope.getMaxX(),
153: envelope.getMaxY(), };
154: toScreen.transform(screenbounds, 0, screenbounds, 0, 2);
155:
156: // imageLocation=new Point((int)(Math.min(screenbounds[0], screenbounds[2])), (int)(Math.min(screenbounds[1], screenbounds[3])) );
157:
158: int width = (int) Math.abs(screenbounds[2] - screenbounds[0]);
159: int height = (int) Math.abs(screenbounds[3] - screenbounds[1]);
160: //create transparent image
161: image = SWTGraphics.createDefaultImage(Display.getDefault(),
162: width, height);
163:
164: // draw feature
165: SWTGraphics graphics = new SWTGraphics(image, Display
166: .getDefault());
167:
168: drawing.drawFeature(graphics, feature, getMap()
169: .getViewportModel().worldToScreenTransform(envelope,
170: new Dimension(width, height)), false, syms, mt);
171: graphics.dispose();
172: }
173:
174: /** I haven't been able to get the SWT image buffer going yet
175: * So this flag is so I can quickly enable the unstable code for
176: * development and disable it for committing my changes.
177: */
178: private static final boolean BUFFER_READY = false;
179:
180: /**
181: * @see net.refractions.udig.project.command.MapCommand#run()
182: */
183: public void run(IProgressMonitor monitor) throws Exception {
184: if (!BUFFER_READY || graphics instanceof AWTGraphics) {
185: if (syms == null)
186: syms = Drawing.getSymbolizers(feature
187: .getDefaultGeometry().getClass(), color, false);
188: MathTransform mt = getMathTransform(featureCRS);
189: drawing.drawFeature(graphics, feature, getMap()
190: .getViewportModel().worldToScreenTransform(),
191: false, syms, mt);
192: } else {
193: if (image == null) {
194: preRender();
195: }
196: // graphics.drawImage(image, imageLocation.x, imageLocation.y);
197: }
198: }
199:
200: /**
201: *
202: * @return
203: */
204: private MathTransform getMathTransform(
205: CoordinateReferenceSystem featureCRS) {
206: MathTransformKey key = new MathTransformKey(featureCRS,
207: getMap().getViewportModel().getCRS());
208: mt = mtCache.get(key);
209: if (mt == null) {
210: try {
211: mt = CRS.findMathTransform(featureCRS, getMap()
212: .getViewportModel().getCRS(), true);
213: } catch (Exception e) {
214: mt = null;
215: }
216: mtCache.put(key, mt);
217: }
218: return mt;
219: }
220:
221: /**
222: * @return Returns the color.
223: */
224: public Color getColor() {
225: return color;
226: }
227:
228: /**
229: * @param color The color to set.
230: */
231: public void setColor(Color color) {
232: this .color = color;
233: }
234:
235: /**
236: * Allows the symbolizers to be set
237: *
238: * @param syms symbolizers to use to draw features.
239: */
240: public void setSymbolizers(Symbolizer[] syms) {
241: if (syms == null)
242: this .syms = new Symbolizer[0];
243: else {
244:
245: this .syms = new Symbolizer[syms.length];
246: System.arraycopy(syms, 0, this .syms, 0, this .syms.length);
247: }
248: }
249:
250: public Rectangle getValidArea() {
251: if (feature != null) {
252: try {
253: Envelope bounds = JTS.transform(feature.getBounds(),
254: getMathTransform(featureCRS));
255: double[] points = new double[] { bounds.getMinX(),
256: bounds.getMinY(), bounds.getMaxX(),
257: bounds.getMaxY() };
258: getMap().getViewportModel().worldToScreenTransform()
259: .transform(points, 0, points, 0, 2);
260: return new Rectangle((int) points[0], (int) points[1],
261: (int) Math.abs(points[2] - points[0]),
262: (int) Math.abs(points[3] - points[1]));
263: } catch (TransformException e) {
264: return null;
265: }
266: }
267: return null;
268:
269: }
270:
271: public void setValid(boolean valid) {
272: super .setValid(valid);
273: if (!valid)
274: dispose();
275: }
276:
277: protected void finalize() {
278: dispose();
279: }
280:
281: /**
282: * Diposes of the image if it has been created. Only needs to be called if the
283: * command has not been sent to the {@link IMapDisplay}.
284: */
285: public void dispose() {
286: if (image != null && !image.isDisposed()) {
287: image.dispose();
288: image = null;
289: }
290: }
291:
292: private static class MathTransformKey {
293: final CoordinateReferenceSystem from, to;
294:
295: protected MathTransformKey(CoordinateReferenceSystem from,
296: CoordinateReferenceSystem to) {
297: this .from = from;
298: this .to = to;
299: }
300:
301: @Override
302: public int hashCode() {
303: final int PRIME = 31;
304: int result = 1;
305: result = PRIME * result
306: + ((from == null) ? 0 : from.hashCode());
307: result = PRIME * result
308: + ((to == null) ? 0 : to.hashCode());
309: return result;
310: }
311:
312: @Override
313: public boolean equals(Object obj) {
314: if (this == obj)
315: return true;
316: if (obj == null)
317: return false;
318: if (getClass() != obj.getClass())
319: return false;
320: final MathTransformKey other = (MathTransformKey) obj;
321: if (from == null) {
322: if (other.from != null)
323: return false;
324: } else if (!from.equals(other.from))
325: return false;
326: if (to == null) {
327: if (other.to != null)
328: return false;
329: } else if (!to.equals(other.to))
330: return false;
331: return true;
332: }
333:
334: }
335: }
|