001: package com.vividsolutions.jump.workbench.imagery.graphic;
002:
003: /*
004: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
005: * for visualizing and manipulating spatial features with geometry and attributes.
006: *
007: * Copyright (C) 2003 Vivid Solutions
008: *
009: * This program is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU General Public License
011: * as published by the Free Software Foundation; either version 2
012: * of the License, or (at your option) any later version.
013: *
014: * This program is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
017: * GNU General Public License for more details.
018: *
019: * You should have received a copy of the GNU General Public License
020: * along with this program; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
022: *
023: * For more information, contact:
024: *
025: * Vivid Solutions
026: * Suite #1A
027: * 2328 Government Street
028: * Victoria BC V8T 5G5
029: * Canada
030: *
031: * (250)385-6040
032: * www.vividsolutions.com
033: */
034: /*
035: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
036: * for visualizing and manipulating spatial features with geometry and attributes.
037: *
038: * JUMP is Copyright (C) 2003 Vivid Solutions
039: *
040: * This program implements extensions to JUMP and is
041: * Copyright (C) 2004 Integrated Systems Analysts, Inc.
042: *
043: * This program is free software; you can redistribute it and/or
044: * modify it under the terms of the GNU General Public License
045: * as published by the Free Software Foundation; either version 2
046: * of the License, or (at your option) any later version.
047: *
048: * This program is distributed in the hope that it will be useful,
049: * but WITHOUT ANY WARRANTY; without even the implied warranty of
050: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
051: * GNU General Public License for more details.
052: *
053: * You should have received a copy of the GNU General Public License
054: * along with this program; if not, write to the Free Software
055: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
056: *
057: * For more information, contact:
058: *
059: * Integrated Systems Analysts, Inc.
060: * 630C Anchors St., Suite 101
061: * Fort Walton Beach, Florida 32548
062: * USA
063: *
064: * (850)862-7321
065: * www.ashs.isa.com
066: */
067: import java.awt.AlphaComposite;
068: import java.awt.Composite;
069: import java.awt.RenderingHints;
070: import java.io.File;
071:
072: import javax.media.jai.RenderedOp;
073: import javax.media.jai.operator.FileLoadDescriptor;
074:
075: import com.sun.image.codec.jpeg.JPEGDecodeParam;
076: import com.vividsolutions.jts.geom.Envelope;
077: import com.vividsolutions.jump.feature.Feature;
078: import com.vividsolutions.jump.workbench.imagery.ReferencedImage;
079: import com.vividsolutions.jump.workbench.ui.Viewport;
080:
081: /**
082: * An image whose source is a bitmap
083: *
084: * Much of this code was donated by Larry Becker and Robert Littlefield of Integrated Systems Analysts, Inc.
085: */
086: public class GraphicImage implements ReferencedImage
087:
088: {
089: private File file;
090: private WorldFile wf;
091: private boolean initialload;
092: private Envelope env;
093: private String type = null;
094:
095: public GraphicImage(File file, WorldFile wf) {
096: this .wf = wf;
097: this .file = file;
098: this .initialload = true;
099: if (wf == null)
100: this .wf = WorldFile.DEFAULT;
101:
102: int i = file.getName().lastIndexOf(".");
103: if (i > -1 && i < file.getName().length()) {
104: type = file.getName().substring(i + 1).toUpperCase();
105: } else {
106: type = file.getName();
107: }
108: }
109:
110: public Envelope getEnvelope() {
111: if (env == null)
112: env = computeEnvelope();
113: return env;
114: }
115:
116: private Envelope computeEnvelope() {
117:
118: double xm, xM, ym, yM;
119:
120: RenderedOp image = null;
121: if (file.exists() && file.isFile() && file.canRead()) {
122: image = FileLoadDescriptor.create(file.getPath(), null,
123: null, null);
124: } else {
125: return new Envelope();
126: }
127:
128: xm = 0;
129: xM = image.getWidth() * wf.getXSize();
130: ym = 0;
131: yM = image.getHeight() * wf.getYSize();
132:
133: xm = xm + wf.getXUpperLeft();
134: xM = xM + wf.getXUpperLeft();
135:
136: ym = ym + wf.getYUpperLeft();
137: yM = yM + wf.getYUpperLeft();
138:
139: return new Envelope(xm, xM, ym, yM);
140: }
141:
142: // public void paint(Feature f, java.awt.Graphics2D g, Viewport viewport)
143: // {
144: // // update the control frame, in case it has changed
145: //
146: // AffineTransform originalTransform;
147: // originalTransform = g.getTransform();
148: // try {
149: // transformByBaseline(g, viewport, (new GeometryFactory()).toGeometry(getEnvelope()));
150: // //transformGraphics(g, viewport, env);
151: // g.drawImage(image, 0, 0, null);
152: // }
153: // finally {
154: // g.setTransform(originalTransform);
155: // }
156: // }
157: //
158: // private void transformByBaseline(java.awt.Graphics2D g, Viewport viewport, Geometry geom)
159: // {
160: // Coordinate[] pts = geom.getCoordinates();
161: // Coordinate anchorPt = pts[0];
162: // Coordinate controlPt = pts[1];
163: //
164: // // scale
165: // double frameSize = anchorPt.distance(controlPt);
166: // double imageWorldScale = frameSize/image.getWidth(null);
167: // double imageViewScale = viewport.getScale() * imageWorldScale;
168: // g.scale(imageViewScale, imageViewScale);
169: //
170: // // translate
171: // double maxYWorld = imageWorldScale * image.getHeight(null);
172: // Coordinate topLeftWorld = new Coordinate(anchorPt.x, anchorPt.y + maxYWorld);
173: // Point2D topLeftView = null;
174: // try {
175: // topLeftView = viewport.toViewPoint(topLeftWorld);
176: // } catch(java.awt.geom.NoninvertibleTransformException e) {
177: // e.printStackTrace();
178: // }
179: // g.translate(topLeftView.getX()/imageViewScale, topLeftView.getY()/imageViewScale);
180: //
181: // // rotate
182: // double dx = controlPt.x - anchorPt.x;
183: // double dy = controlPt.y - anchorPt.y;
184: // double theta = Math.atan2(dy, dx);
185: // g.rotate(-theta, 0.0, image.getHeight(null));
186: //
187: //
188: // }
189:
190: public void paint(Feature f, java.awt.Graphics2D g,
191: Viewport viewport) {
192:
193: // BufferedImage image = null;
194:
195: // if (((jpgFile.exists()) && (jpgFile.isFile()) && (jpgFile.canRead())))
196: // {
197: // FileInputStream in = new FileInputStream(jpgFilename);
198: // JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(in);
199: // image = decoder.decodeAsBufferedImage();
200: // jpg_colorspace = decoder.getJPEGDecodeParam().getEncodedColorID();
201: // in.close();
202: // }
203: // else
204: // {
205: // return;
206: // }
207:
208: RenderedOp image = null;
209: if (file.exists() && file.isFile() && file.canRead()) {
210: image = FileLoadDescriptor.create(file.getPath(), null,
211: null, null);
212: } else {
213: return;
214: }
215:
216: int jpgPixelWidth = image.getWidth();
217: int jpgPixelHeight = image.getHeight();
218:
219: // Use features internal envelope for upper left coordinates [Ed Deen : Dec 1, 2006]
220: // Note: This solves the problem where the user moves the image but only the bounding box moves ... a default worldfile
221: // variable was anchoring it. Now, use the World File (if it exists) only on the initial load of the image.
222: // TODO: Add these world file attributes into the actual image feature. Then copy the world file information into
223: // the feature at load and extract the following information always from the feature.
224: // This will allow for sizing the images, sizing to fit, etc, etc.
225: // and later the ability to write the actual World File out for
226: // future referencing. [Ed Deen : Dec 1, 2006]
227: // Note: Will also need to add the jpg_yres support as well, rotation, etc.
228: double jpg_xres = wf.getXSize(); // Default wf.Xsize is always 1.0
229: double jpg_ulx = f.getGeometry().getEnvelopeInternal()
230: .getMinX();
231: double jpg_uly = f.getGeometry().getEnvelopeInternal()
232: .getMaxY();
233: // Check for initial load
234: if (this .initialload == true) {
235: // If Initial Load; check if World File exists
236: if (wf.getFilename() != null) {
237: // If World File exists then use worldfile for initial upper left coordinates
238: jpg_xres = wf.getXSize();
239: jpg_ulx = wf.getXUpperLeft(); //realworld coords
240: jpg_uly = wf.getYUpperLeft(); //realworld coords
241: }
242: // Set Inital Load to false
243: this .initialload = false;
244: }
245:
246: int image_x = 0; //x position of raster in final image in pixels
247: int image_y = 0; //y position of raster in final image in pixels
248: int image_w = viewport.getPanel().getWidth(); //width of raster in final image in pixels
249: int image_h = viewport.getPanel().getHeight(); //height of raster in final image in pixels
250:
251: Envelope vpEnvelope = viewport.getEnvelopeInModelCoordinates();
252: double view_res = 1 / viewport.getScale(); //panel resolution
253: double rwViewLeft = vpEnvelope.getMinX();
254: double rwViewRight = vpEnvelope.getMaxX();
255: double rwViewTop = vpEnvelope.getMaxY();
256: double rwViewBot = vpEnvelope.getMinY();
257:
258: //Here calculate the real world jpg edges.
259: //NOTE: world file coordinates are center of pixels
260: double halfPixel = 0.5 * jpg_xres;
261: double rwJpgFileLeftEdge = jpg_ulx - halfPixel;
262: double rwJpgFileRightEdge = rwJpgFileLeftEdge
263: + (jpgPixelWidth * jpg_xres);
264: double rwJpgFileTopEdge = jpg_uly + halfPixel;
265: double rwJpgFileBotEdge = rwJpgFileTopEdge
266: - (jpgPixelHeight * jpg_xres);
267:
268: double rwRasterLeft = Math.max(rwViewLeft, rwJpgFileLeftEdge);
269: double rwRasterRight = Math
270: .min(rwViewRight, rwJpgFileRightEdge);
271: double rwRasterTop = Math.min(rwViewTop, rwJpgFileTopEdge);
272: double rwRasterBot = Math.max(rwViewBot, rwJpgFileBotEdge);
273:
274: //check to see if this jpg is inside the view area
275: if (!((rwJpgFileRightEdge <= rwViewLeft)
276: || (rwJpgFileLeftEdge >= rwViewRight)
277: || (rwJpgFileTopEdge <= rwViewBot) || (rwJpgFileBotEdge >= rwViewTop))) {
278: //calculate which pixels in the jpg file fit inside the view
279: int jpgLeftPixel = (int) ((rwRasterLeft - rwJpgFileLeftEdge) / jpg_xres); //trunc
280: int jpgRightPixel = (int) ((rwRasterRight - rwJpgFileLeftEdge) / jpg_xres); //trunc
281: if (jpgRightPixel == jpgPixelWidth)
282: jpgRightPixel = jpgPixelWidth - 1;
283: int jpgTopPixel = (int) ((rwJpgFileTopEdge - rwRasterTop) / jpg_xres); //trunc
284: int jpgBotPixel = (int) ((rwJpgFileTopEdge - rwRasterBot) / jpg_xres); //trunc
285: if (jpgBotPixel == jpgPixelHeight)
286: jpgBotPixel = jpgPixelHeight - 1;
287:
288: //calculate the real world coords of the included pixels
289: double rwJpgLeft = rwJpgFileLeftEdge
290: + (jpgLeftPixel * jpg_xres);
291: double rwJpgRight = rwJpgFileLeftEdge
292: + (jpgRightPixel * jpg_xres) + jpg_xres;
293: double rwJpgTop = rwJpgFileTopEdge
294: - (jpgTopPixel * jpg_xres);
295: double rwJpgBot = rwJpgFileTopEdge
296: - (jpgBotPixel * jpg_xres) - jpg_xres;
297:
298: //calculate the pixel offset on the panel of the included portion of the jpg file
299: int leftOffset = round((rwRasterLeft - rwJpgLeft)
300: / view_res);
301: int rightOffset = round((rwJpgRight - rwRasterRight)
302: / view_res);
303: int topOffset = round((rwJpgTop - rwRasterTop) / view_res);
304: int botOffset = round((rwRasterBot - rwJpgBot) / view_res);
305:
306: image_x = round(rwRasterLeft / view_res)
307: - round(rwViewLeft / view_res);
308: image_w = round(rwRasterRight / view_res)
309: - round(rwRasterLeft / view_res);
310: if (image_w <= 0)
311: image_w = 1;
312:
313: image_y = round(rwViewTop / view_res)
314: - round(rwRasterTop / view_res);
315: image_h = round(rwRasterTop / view_res)
316: - round(rwRasterBot / view_res);
317: if (image_h <= 0)
318: image_h = 1;
319:
320: image_x -= leftOffset;
321: image_y -= topOffset;
322: image_w += (leftOffset + rightOffset);
323: image_h += (topOffset + botOffset);
324:
325: int jpg_colorspace = image.getColorModel().getColorSpace()
326: .getType();
327: if (jpg_colorspace != JPEGDecodeParam.COLOR_ID_GRAY) {
328: RenderingHints rh = new RenderingHints(
329: RenderingHints.KEY_INTERPOLATION,
330: RenderingHints.VALUE_INTERPOLATION_BILINEAR);
331: g.setRenderingHints(rh);
332: }
333:
334: //parameters: destination corners then source corners
335: //source corners are defined in terms of infinitely thin coordinates
336: //which define the edges of the pixel space so that we have
337: //to add 1 to the right bottom coordinate of the source rectangle
338: //since jpgRightPixel & jpgBotPixel are defined in terms of array element position
339: //any questions, see Java documentation for Graphics object
340: Composite composite = g.getComposite();
341: g.setComposite(AlphaComposite.Src);
342: g.drawImage(image.getAsBufferedImage(), image_x, image_y,
343: image_x + image_w, image_y + image_h, jpgLeftPixel,
344: jpgTopPixel, jpgRightPixel + 1, jpgBotPixel + 1,
345: viewport.getPanel());
346: g.setComposite(composite);
347: }
348: }
349:
350: private int round(double num) {
351: return (int) Math.round(num);
352: }
353:
354: public String getType() {
355: return type;
356: }
357:
358: }
|