0001: /*
0002: * uDig - User Friendly Desktop Internet GIS client
0003: * http://udig.refractions.net
0004: * (C) 2004, Refractions Research Inc.
0005: *
0006: * This library is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU Lesser General Public
0008: * License as published by the Free Software Foundation;
0009: * version 2.1 of the License.
0010: *
0011: * This library is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * Lesser General Public License for more details.
0015: *
0016: */
0017: package net.refractions.udig.render.internal.wms.basic;
0018:
0019: import java.awt.Dimension;
0020: import java.awt.Graphics2D;
0021: import java.awt.Point;
0022: import java.awt.Rectangle;
0023: import java.awt.image.BufferedImage;
0024: import java.io.IOException;
0025: import java.io.InputStream;
0026: import java.io.StringWriter;
0027: import java.io.UnsupportedEncodingException;
0028: import java.net.URLEncoder;
0029: import java.util.ArrayList;
0030: import java.util.Collection;
0031: import java.util.HashMap;
0032: import java.util.HashSet;
0033: import java.util.List;
0034: import java.util.Map;
0035: import java.util.Set;
0036: import java.util.logging.Handler;
0037: import java.util.logging.Level;
0038: import java.util.logging.LogRecord;
0039: import java.util.logging.Logger;
0040:
0041: import javax.imageio.ImageIO;
0042: import javax.naming.OperationNotSupportedException;
0043:
0044: import net.refractions.udig.project.ILayer;
0045: import net.refractions.udig.project.ProjectBlackboardConstants;
0046: import net.refractions.udig.project.internal.StyleBlackboard;
0047: import net.refractions.udig.project.internal.render.impl.RendererImpl;
0048: import net.refractions.udig.project.render.ICompositeRenderContext;
0049: import net.refractions.udig.project.render.IMultiLayerRenderer;
0050: import net.refractions.udig.project.render.IRenderContext;
0051: import net.refractions.udig.project.render.RenderException;
0052: import net.refractions.udig.render.gridcoverage.basic.GridCoverageRendererUtils;
0053: import net.refractions.udig.render.gridcoverage.basic.State;
0054: import net.refractions.udig.render.internal.gridcoverage.basic.BasicGridCoverageRenderer;
0055: import net.refractions.udig.render.wms.basic.WMSPlugin;
0056: import net.refractions.udig.render.wms.basic.internal.Messages;
0057: import net.refractions.udig.render.wms.basic.preferences.PreferenceConstants;
0058: import net.refractions.udig.style.wms.WMSStyleContent;
0059:
0060: import org.eclipse.core.runtime.IProgressMonitor;
0061: import org.eclipse.core.runtime.IStatus;
0062: import org.eclipse.core.runtime.Status;
0063: import org.eclipse.core.runtime.jobs.Job;
0064: import org.eclipse.jface.preference.IPreferenceStore;
0065: import org.geotools.coverage.grid.GridCoverage2D;
0066: import org.geotools.coverage.grid.GridCoverageFactory;
0067: import org.geotools.data.Query;
0068: import org.geotools.data.ows.Layer;
0069: import org.geotools.data.ows.Service;
0070: import org.geotools.data.shapefile.ShapefileDataStore;
0071: import org.geotools.data.wms.WebMapServer;
0072: import org.geotools.data.wms.request.GetMapRequest;
0073: import org.geotools.filter.Filter;
0074: import org.geotools.geometry.GeneralEnvelope;
0075: import org.geotools.geometry.jts.ReferencedEnvelope;
0076: import org.geotools.ows.ServiceException;
0077: import org.geotools.referencing.CRS;
0078: import org.geotools.referencing.crs.DefaultGeographicCRS;
0079: import org.geotools.renderer.lite.GridCoverageRenderer;
0080: import org.geotools.styling.Rule;
0081: import org.geotools.styling.Style;
0082: import org.geotools.xml.DocumentWriter;
0083: import org.geotools.xml.filter.FilterSchema;
0084: import org.opengis.metadata.Identifier;
0085: import org.opengis.referencing.FactoryException;
0086: import org.opengis.referencing.NoSuchAuthorityCodeException;
0087: import org.opengis.referencing.crs.CoordinateReferenceSystem;
0088: import org.opengis.referencing.operation.TransformException;
0089: import org.opengis.spatialschema.geometry.MismatchedDimensionException;
0090: import org.opengis.util.InternationalString;
0091:
0092: import com.vividsolutions.jts.geom.Coordinate;
0093: import com.vividsolutions.jts.geom.Envelope;
0094:
0095: /**
0096: * The basic renderer for a WMS Layer
0097: * <p>
0098: * </p>
0099: */
0100: public class BasicWMSRenderer2 extends RendererImpl implements
0101: IMultiLayerRenderer {
0102:
0103: private static final String REFRESH_JOB = Messages.BasicWMSRenderer2_refreshJob_title;
0104: private static final String EPSG_4326 = "EPSG:4326"; //$NON-NLS-1$
0105: private static final String EPSG_4269 = "EPSG:4269"; //$NON-NLS-1$
0106: private static final ReferencedEnvelope NILL_BOX = new ReferencedEnvelope(
0107: 0, 0, 0, 0, DefaultGeographicCRS.WGS84);
0108:
0109: /**
0110: * Construct a new BasicWMSRenderer
0111: */
0112: public BasicWMSRenderer2() {
0113: super ();
0114: if (WMSPlugin.getDefault().isDebugging()) {
0115: ClassLoader current = getClass().getClassLoader();
0116: try {
0117: Thread.currentThread().setContextClassLoader(
0118: ShapefileDataStore.class.getClassLoader());
0119: Logger logger = Logger.global;
0120: logger.setLevel(Level.FINEST);
0121: logger.addHandler(new Handler() {
0122:
0123: @Override
0124: public void publish(LogRecord record) {
0125: System.err.println(record.getMessage());
0126: }
0127:
0128: @Override
0129: public void flush() {
0130: System.err.flush();
0131: }
0132:
0133: @Override
0134: public void close() throws SecurityException {
0135:
0136: }
0137:
0138: });
0139: } finally {
0140: Thread.currentThread().setContextClassLoader(current);
0141: }
0142: }
0143: }
0144:
0145: @Override
0146: public void render(Graphics2D destination, IProgressMonitor monitor)
0147: throws RenderException {
0148: render(destination, getViewportBBox(), monitor);
0149: }
0150:
0151: @Override
0152: public void render(IProgressMonitor monitor) throws RenderException {
0153: Graphics2D graphics = (Graphics2D) getContext().getImage()
0154: .getGraphics();
0155: render(graphics, getRenderBounds(), monitor);
0156: }
0157:
0158: public synchronized void render(Graphics2D destination,
0159: Envelope bounds2, IProgressMonitor monitor)
0160: throws RenderException {
0161:
0162: Envelope bounds = bounds2;
0163: int endLayerStatus = ILayer.DONE;
0164: try {
0165: if (bounds == null || bounds.isNull()) {
0166: bounds = getViewportBBox();
0167: }
0168: if (monitor.isCanceled())
0169: return;
0170:
0171: getContext().setStatus(ILayer.WAIT);
0172:
0173: WebMapServer wms = getWMS();
0174: GetMapRequest request = wms.createGetMapRequest();
0175: request.setExceptions(GetMapRequest.EXCEPTION_XML);
0176: setImageFormat(wms, request);
0177:
0178: if (monitor.isCanceled())
0179: return;
0180:
0181: double currScale = getContext().getViewportModel()
0182: .getScaleDenominator();
0183: List<ILayer> layers = getLayers();
0184: for (int i = layers.size() - 1; i >= 0; i--) {
0185: ILayer ilayer = layers.get(i);
0186: Layer layer;
0187: double minScale = 0;
0188: double maxScale = Double.MAX_VALUE;
0189: layer = ilayer.getResource(
0190: org.geotools.data.ows.Layer.class, null);
0191: // check if there are min/max scale rules
0192: StyleBlackboard sb = (StyleBlackboard) ilayer
0193: .getStyleBlackboard();
0194: Style style = (Style) sb.lookup(Style.class);
0195: if (style != null) {
0196: Rule rule = style.getFeatureTypeStyles()[0]
0197: .getRules()[0];
0198: minScale = rule.getMinScaleDenominator();
0199: maxScale = rule.getMaxScaleDenominator();
0200: }
0201:
0202: if (currScale >= minScale && currScale <= maxScale) {
0203: // check for a wms style
0204: org.opengis.layer.Style wmsStyle = (org.opengis.layer.Style) ilayer
0205: .getStyleBlackboard().get(
0206: WMSStyleContent.WMSSTYLE);
0207: if (wmsStyle != null) {
0208: request.addLayer(layer, wmsStyle);
0209: } else {
0210: request.addLayer(layer);
0211: }
0212: }
0213: }
0214:
0215: if (monitor.isCanceled())
0216: return;
0217:
0218: List<Layer> wmsLayers = getWMSLayers();
0219:
0220: // figure out request CRS
0221: String requestCRScode = findRequestCRS(wmsLayers,
0222: getViewportCRS());
0223: // TODO: make findRequestCRS more efficient (we are running CRS.decode at *least* twice)
0224: CoordinateReferenceSystem requestCRS = CRS
0225: .decode(requestCRScode);
0226:
0227: // figure out viewport
0228: ReferencedEnvelope viewport;
0229: Envelope viewportBBox = getViewportBBox();
0230: CoordinateReferenceSystem viewportCRS = getViewportCRS();
0231: if (viewportBBox == null) {
0232: // change viewport to world
0233: viewportBBox = new Envelope(-180, 180, -90, 90);
0234: if (!DefaultGeographicCRS.WGS84.equals(viewportCRS)) { // reproject
0235: viewport = new ReferencedEnvelope(viewportBBox,
0236: DefaultGeographicCRS.WGS84);
0237: viewportBBox = viewport
0238: .transform(viewportCRS, true);
0239: }
0240: }
0241:
0242: ReferencedEnvelope requestBBox = null;
0243: Envelope backprojectedBBox = null; // request bbox projected to the viewport crs
0244: viewport = new ReferencedEnvelope(viewportBBox, viewportCRS);
0245: requestBBox = calculateRequestBBox(wmsLayers, viewport,
0246: requestCRS);
0247:
0248: // check that a request is needed (not out of a bounds, invalid, etc)
0249: if (requestBBox == NILL_BOX) {
0250: endLayerStatus = ILayer.WARNING;
0251: return;
0252: }
0253: assert requestBBox.getCoordinateReferenceSystem().equals(
0254: requestCRS);
0255:
0256: if (requestBBox.getCoordinateReferenceSystem().equals(
0257: getViewportCRS())) {
0258: backprojectedBBox = (Envelope) requestBBox;
0259: } else {
0260: backprojectedBBox = (Envelope) requestBBox.transform(
0261: getViewportCRS(), true);
0262: }
0263:
0264: if (WMSPlugin.isDebugging(Trace.RENDER)) {
0265: WMSPlugin
0266: .trace("Viewport CRS: " + getViewportCRS().getIdentifiers().toArray()[0]); //$NON-NLS-1$
0267: WMSPlugin
0268: .trace("Request CRS: " + requestCRS.getIdentifiers().toArray()[0]); //$NON-NLS-1$
0269: WMSPlugin
0270: .trace("Viewport bounds: " + getViewportBBox()); //$NON-NLS-1$
0271: WMSPlugin.trace("Request bounds: " + requestBBox); //$NON-NLS-1$
0272: WMSPlugin
0273: .trace("Backprojected request bounds: " + backprojectedBBox); //$NON-NLS-1$
0274: }
0275:
0276: Service wmsService = wms.getCapabilities().getService();
0277: Dimension maxDimensions = new Dimension(wmsService
0278: .getMaxWidth(), wmsService.getMaxHeight());
0279: Dimension imageDimensions = calculateImageDimensions(
0280: getContext().getMapDisplay().getDisplaySize(),
0281: maxDimensions, getViewportBBox(), backprojectedBBox);
0282: if (imageDimensions.height < 1 || imageDimensions.width < 1) {
0283: endLayerStatus = ILayer.WARNING;
0284: return;
0285: }
0286:
0287: request
0288: .setDimensions(
0289: imageDimensions.width + "", imageDimensions.height + ""); //$NON-NLS-1$ //$NON-NLS-2$
0290: request.setBBox(requestBBox.getMinX()
0291: + "," + requestBBox.getMinY() //$NON-NLS-1$
0292: + "," + requestBBox.getMaxX() //$NON-NLS-1$
0293: + "," + requestBBox.getMaxY()); //$NON-NLS-1$
0294:
0295: // epsg could be under identifiers or authority.
0296: Set<Identifier> identifiers = requestCRS.getIdentifiers();
0297: if (identifiers.isEmpty())
0298: request.setSRS(EPSG_4326);
0299: else
0300: request
0301: .setSRS(identifiers.iterator().next()
0302: .toString());
0303:
0304: if (monitor.isCanceled())
0305: return;
0306:
0307: setFilter(wms, request);
0308:
0309: // request.setProperty("DACS_ACS", null);
0310: BufferedImage image = readImage(wms, request, monitor);
0311:
0312: if (monitor.isCanceled())
0313: return;
0314:
0315: if (image == null) {
0316: Exception e = new RuntimeException(
0317: Messages.BasicWMSRenderer2_unable_to_decode_image);
0318: throw wrapException(e);
0319: }
0320:
0321: // backprojectedBBox or viewportBBox
0322: renderGridCoverage(destination, backprojectedBBox,
0323: imageDimensions, requestBBox, image);
0324:
0325: } catch (Exception e) {
0326: if (e instanceof RenderException)
0327: throw (RenderException) e;
0328: throw new RenderException(e);
0329: } finally {
0330: getContext().setStatus(endLayerStatus);
0331: if (endLayerStatus == ILayer.DONE) {
0332: // clear the status message (rendering was successful)
0333: getContext().setStatusMessage(null);
0334: }
0335: }
0336: }
0337:
0338: private void setFilter(WebMapServer wms, GetMapRequest request) {
0339: Object mapFilterUncast = getContext().getMap().getBlackboard()
0340: .get(ProjectBlackboardConstants.MAP__DATA_QUERY);
0341: Filter mapFilter = null;
0342: if (mapFilterUncast instanceof Query)
0343: mapFilter = ((Query) mapFilterUncast).getFilter();
0344: if (mapFilterUncast instanceof Filter)
0345: mapFilter = (Filter) mapFilterUncast;
0346:
0347: Map<ILayer, Filter> filters = new HashMap<ILayer, Filter>();
0348: List<ILayer> layers = getContext().getLayers();
0349: for (ILayer layer : layers) {
0350: Object layerFilter = layer.getBlackboard().get(
0351: ProjectBlackboardConstants.MAP__DATA_QUERY);
0352: Filter filter;
0353: if (layerFilter instanceof Query) {
0354: filter = ((Query) layerFilter).getFilter();
0355: } else if (layerFilter instanceof Filter) {
0356: filter = (Filter) layerFilter;
0357: } else {
0358: filter = mapFilter;
0359: }
0360: if (filter != null)
0361: filters.put(layer, filter);
0362: }
0363:
0364: if (filters.isEmpty())
0365: return;
0366:
0367: StringBuilder builder = new StringBuilder();
0368: HashMap hashMap = new HashMap();
0369: for (Map.Entry<ILayer, Filter> entry : filters.entrySet()) {
0370: if (entry.getValue() == null)
0371: builder.append('(');
0372: try {
0373: StringWriter writer = new StringWriter();
0374: DocumentWriter.writeDocument(entry.getValue(),
0375: FilterSchema.getInstance(), writer, hashMap);
0376: builder.append(writer.toString());
0377: } catch (OperationNotSupportedException e) {
0378: WMSPlugin
0379: .log(
0380: "Error writing filter for layer: " + entry.getKey().getID(), e); //$NON-NLS-1$
0381: builder.append("<Filter/>"); //$NON-NLS-1$
0382: } catch (IOException e) {
0383: // SHOULDN'T Happen I don't think...
0384: assert false;
0385: WMSPlugin
0386: .log(
0387: "Error writing filter for layer: " + entry.getKey().getID(), e); //$NON-NLS-1$
0388: builder.append("<Filter/>"); //$NON-NLS-1$
0389: }
0390: builder.append(')');
0391: }
0392:
0393: try {
0394: String encode = URLEncoder.encode(builder.toString(),
0395: "UTF-8"); //$NON-NLS-1$
0396: request.setVendorSpecificParameter("filter", encode); //$NON-NLS-1$
0397: } catch (UnsupportedEncodingException e) {
0398: // better not happen!
0399: throw (RuntimeException) new RuntimeException()
0400: .initCause(e);
0401: }
0402: }
0403:
0404: private void renderGridCoverage(Graphics2D graphics,
0405: Envelope bounds, Dimension dimension,
0406: ReferencedEnvelope requestBBox, BufferedImage image)
0407: throws Exception {
0408: CoordinateReferenceSystem destinationCRS = getContext()
0409: .getCRS();
0410:
0411: Envelope envelope = bounds;
0412: if (envelope == null || envelope.isNull()) {
0413: envelope = getContext().getViewportModel().getBounds();
0414: }
0415: Point upperLeft = getContext().worldToPixel(
0416: new Coordinate(envelope.getMinX(), envelope.getMinY()));
0417: Point bottomRight = getContext().worldToPixel(
0418: new Coordinate(envelope.getMaxX(), envelope.getMaxY()));
0419: Rectangle screenSize = new Rectangle(upperLeft);
0420: screenSize.add(bottomRight);
0421:
0422: GridCoverage2D coverage = convertImageToGridCoverage(
0423: requestBBox, image);
0424:
0425: GridCoverageRenderer renderer = GridCoverageRendererUtils
0426: .createRenderer(coverage, destinationCRS);
0427:
0428: // renderer.paint(graphics);
0429: State renderState = GridCoverageRendererUtils
0430: .getRenderState(context);
0431: renderState.bounds = bounds;
0432: renderState.displayArea = screenSize;
0433: BasicGridCoverageRenderer.doRender(renderer, graphics,
0434: renderState);
0435: }
0436:
0437: // private void renderGridCoverageOld( Graphics2D destination, Envelope bounds, Dimension
0438: // dimension, ReferencedEnvelope requestBBox, BufferedImage image ) throws RenderException {
0439: // CoordinateReferenceSystem viewportCRS = getViewportCRS();
0440: //
0441: // Rectangle paintArea = new Rectangle(calculateImageOffset().x, calculateImageOffset().y,
0442: // dimension.width, dimension.height);
0443: //
0444: // // if( !viewportCRS.equals(requestBBox.getCoordinateReferenceSystem()) )
0445: // // rasterReprojection=true;
0446: //
0447: // GridCoverage2D gc = convertImageToGridCoverage(requestBBox, image);
0448: // //GridCoverageRenderer renderer = new GridCoverageRenderer(gc, crs);
0449: //
0450: //
0451: // Envelope projEnv = new Envelope(gc.getEnvelope().getMinimum(0), gc.getEnvelope()
0452: // .getMaximum(0), gc.getEnvelope().getMinimum(1), gc.getEnvelope().getMaximum(1));
0453: //
0454: // try {
0455: // projEnv = org.geotools.geometry.jts.JTS.transform(
0456: // projEnv, null,
0457: // CRS.findMathTransform(gc.getCoordinateReferenceSystem(), viewportCRS, true),
0458: // 10 );
0459: // } catch (Exception e) {
0460: // throw wrapException(e);
0461: // }
0462: // Envelope imageAreaBBox = bounds;
0463: // if (imageAreaBBox.contains(projEnv)) {
0464: // imageAreaBBox = projEnv;
0465: // }
0466: // try {
0467: // GridCoverageRenderer renderer = new GridCoverageRenderer(
0468: // viewportCRS, imageAreaBBox, paintArea );
0469: //
0470: // // streamingRenderer.setConcatTransforms(true);
0471: // trace("Preparing to render image returned from server."); //$NON-NLS-1$
0472: // trace("PaintArea: " + paintArea); //$NON-NLS-1$
0473: // trace("ImageAreaBBox: " + imageAreaBBox); //$NON-NLS-1$
0474: // trace("Map's viewport: " + bounds); //$NON-NLS-1$
0475: //
0476: // RasterSymbolizer rasterSymbolizer;
0477: // rasterSymbolizer = factory.createRasterSymbolizer();
0478: //
0479: // renderer.paint( destination, gc, rasterSymbolizer );
0480: // } catch (FactoryException e) {
0481: // throw (RuntimeException) new RuntimeException( ).initCause( e );
0482: // } catch (TransformException e) {
0483: // throw (RuntimeException) new RuntimeException( ).initCause( e );
0484: // } catch (NoninvertibleTransformException e) {
0485: // throw (RuntimeException) new RuntimeException( ).initCause( e );
0486: // }
0487: //
0488: // //State renderState = BasicGridCoverageRenderer.getRenderState(context);
0489: // //renderState.bounds = imageAreaBBox;
0490: // //renderState.displayArea = paintArea;
0491: // //BasicGridCoverageRenderer.doRender(renderer, destination, renderState);
0492: // }
0493:
0494: private GridCoverage2D convertImageToGridCoverage(
0495: ReferencedEnvelope requestBBox, BufferedImage image)
0496: throws RenderException {
0497: Envelope env = requestBBox;
0498: GeneralEnvelope gtEnv = new GeneralEnvelope(new double[] {
0499: env.getMinX(), env.getMinY() }, new double[] {
0500: env.getMaxX(), env.getMaxY() });
0501:
0502: try {
0503: gtEnv.setCoordinateReferenceSystem(requestBBox
0504: .getCoordinateReferenceSystem());
0505: } catch (Exception e) {
0506: throw wrapException(e);
0507: }
0508:
0509: GridCoverageFactory factory = new GridCoverageFactory();
0510:
0511: GridCoverage2D gc = (GridCoverage2D) factory.create(
0512: "GridCoverage", image, gtEnv); //$NON-NLS-1$
0513: return gc;
0514: }
0515:
0516: private void setImageFormat(WebMapServer wms, GetMapRequest request) {
0517: List formats = wms.getCapabilities().getRequest().getGetMap()
0518: .getFormats();
0519: String str;
0520: if (getPreferencesStore().getBoolean(
0521: PreferenceConstants.P_USE_DEFAULT_ORDER)) {
0522: str = getPreferencesStore().getDefaultString(
0523: PreferenceConstants.P_IMAGE_TYPE_ORDER);
0524: } else {
0525: str = getPreferencesStore().getString(
0526: PreferenceConstants.P_IMAGE_TYPE_ORDER);
0527: }
0528: String[] preferredFormats = str.split(","); //$NON-NLS-1$
0529: // Select one of the available formats from the WMS server
0530: // the order of preferred formats is set in the preferences
0531: for (String format : preferredFormats) {
0532: if (formats.contains(format)) {
0533: request.setProperty(GetMapRequest.FORMAT, format);
0534: request
0535: .setTransparent(formatSupportsTransparency(format));
0536: break;
0537: }
0538: }
0539: }
0540:
0541: private BufferedImage readImage(final WebMapServer wms,
0542: final GetMapRequest request, IProgressMonitor monitor)
0543: throws RenderException {
0544: final BufferedImage[] image = new BufferedImage[1];
0545: final RenderException[] exception = new RenderException[1];
0546: final Object condition = new Object();
0547:
0548: Thread thread = new Thread() {
0549: @Override
0550: public void run() {
0551: InputStream inputStream = null;
0552: try {
0553: inputStream = wms.issueRequest(request)
0554: .getInputStream();
0555: image[0] = ImageIO.read(inputStream);
0556: } catch (IOException e1) {
0557: exception[0] = wrapException(e1);
0558: } catch (ServiceException e) {
0559: exception[0] = wrapException(e);
0560: } catch (Throwable t) {
0561: String message = Messages.BasicWMSRenderer2_errorObtainingImage;
0562: image[0] = getContext().getImage();
0563: exception[0] = new RenderException(message, t);
0564: } finally {
0565: synchronized (condition) {
0566: condition.notify();
0567: }
0568: if (inputStream != null)
0569: try {
0570: inputStream.close();
0571: } catch (IOException e) {
0572: WMSPlugin
0573: .log(
0574: "failed to close input stream!!!", e); //$NON-NLS-1$
0575: }
0576: }
0577: }
0578: };
0579: thread.start();
0580:
0581: int time = 0;
0582:
0583: while (image[0] == null && !monitor.isCanceled()
0584: && exception[0] == null) {
0585: synchronized (condition) {
0586: try {
0587: time += 200;
0588: condition.wait(200);
0589: if (time == 1000) {
0590: setState(RENDERING);
0591: time = 0;
0592: }
0593: } catch (InterruptedException e) {
0594: thread.interrupt();
0595: throw wrapException(e);
0596: }
0597: }
0598: }
0599:
0600: if (exception[0] != null)
0601: throw exception[0];
0602: return image[0];
0603: }
0604:
0605: private boolean formatSupportsTransparency(String format) {
0606: if (format.equalsIgnoreCase("image/png")) //$NON-NLS-1$
0607: return true;
0608: if (format.equalsIgnoreCase("image/gif")) //$NON-NLS-1$
0609: return true;
0610: if (format.equalsIgnoreCase("image/tiff")) //$NON-NLS-1$
0611: return true;
0612: if (format.equalsIgnoreCase("image/bmp")) //$NON-NLS-1$
0613: return true;
0614: return false;
0615: }
0616:
0617: /**
0618: * Determines the dimensions of the image to request, usually 1:1 to display, but sometimes more
0619: * (too fuzzy) or less (image too large) when reprojecting.
0620: *
0621: * @param maxDimensions TODO
0622: * @param viewport
0623: * @param request
0624: * @param context
0625: * @return
0626: * @throws RenderException
0627: */
0628: public static Dimension calculateImageDimensions(
0629: Dimension displaySize, Dimension maxDimensions,
0630: Envelope viewport, Envelope request) throws RenderException {
0631: double xScale = request.getWidth() / viewport.getWidth();
0632: double yScale = request.getHeight() / viewport.getHeight();
0633: // TODO: adjust height and width when we are reprojecting to make things less fuzzy
0634:
0635: int width = (int) (xScale * displaySize.getWidth());
0636: int height = (int) (yScale * displaySize.getHeight());
0637:
0638: // ensure we don't exceed the dimensions the server will allow
0639: int maxWidth = (int) maxDimensions.getWidth();
0640: if ((maxWidth > 0) && (width > maxWidth)) {
0641: width = maxWidth;
0642: }
0643: int maxHeight = (int) maxDimensions.getHeight();
0644: if ((maxHeight > 0) && (height > maxHeight)) {
0645: height = maxHeight;
0646: }
0647:
0648: WMSPlugin
0649: .trace("WMS request image dimensions: " + width + ", " + height); //$NON-NLS-1$ //$NON-NLS-2$
0650: return new Dimension(width, height);
0651: }
0652:
0653: public Point calculateImageOffset(Point min, Point max)
0654: throws RenderException {
0655: return new Point(Math.min(min.x, max.x), max.y);
0656: }
0657:
0658: /**
0659: * Using the viewport bounds and combined wms layer extents, determines an appropriate bounding
0660: * box by projecting the viewport into the request CRS, intersecting the bounds, and returning
0661: * the result.
0662: *
0663: * @param wmsLayers all adjacent wms layers we are requesting
0664: * @param viewport map editor bounds and crs
0665: * @param requestCRS coordinate reference system supported by the server
0666: * @return the bbox to ask the server for
0667: * @throws MismatchedDimensionException
0668: * @throws TransformException
0669: * @throws FactoryException
0670: */
0671: public static ReferencedEnvelope calculateRequestBBox(
0672: List<Layer> wmsLayers, ReferencedEnvelope viewport,
0673: CoordinateReferenceSystem requestCRS)
0674: throws MismatchedDimensionException, TransformException,
0675: FactoryException {
0676: /* The bounds of all wms layers on this server combined */
0677: Envelope layersBBox = getLayersBoundingBox(requestCRS,
0678: wmsLayers);
0679: if (isEnvelopeNull(layersBBox)) {
0680: // the wms server has no bounds
0681: WMSPlugin
0682: .log("Zero width/height envelope: wmsLayers = " + layersBBox); //$NON-NLS-1$
0683: layersBBox = null;
0684: // alternatively, we could impose a reprojected -180,180,-90,90
0685: }
0686:
0687: /* The viewport bounds projected to the request crs */
0688: ReferencedEnvelope reprojectedViewportBBox = viewport
0689: .transform(requestCRS, true);
0690: if (isEnvelopeNull(reprojectedViewportBBox)) {
0691: // viewport couldn't be reprojected
0692: WMSPlugin
0693: .log("Zero width/height envelope: reprojected viewport from " + viewport //$NON-NLS-1$
0694: + " to " + requestCRS + " returned " + reprojectedViewportBBox); //$NON-NLS-1$ //$NON-NLS-2$
0695: }
0696: // alternative for better accuracy: new ReferencedEnvelope(JTS.transform(viewport, null,
0697: // CRS.findMathTransform(viewportCRS, crs, true), 4), crs);
0698:
0699: /* The intersection of the viewport and the combined wms layers */
0700: Envelope interestBBox;
0701: if (layersBBox == null) {
0702: interestBBox = reprojectedViewportBBox;
0703: } else {
0704: interestBBox = reprojectedViewportBBox
0705: .intersection(layersBBox);
0706: }
0707: if (isEnvelopeNull(interestBBox)) {
0708: // outside of bounds, do not draw
0709: WMSPlugin
0710: .trace("Bounds of the data are outside the bounds of the viewscreen."); //$NON-NLS-1$
0711: return NILL_BOX;
0712: }
0713:
0714: /* The bounds of the request we are going to make */
0715: ReferencedEnvelope requestBBox = new ReferencedEnvelope(
0716: interestBBox, requestCRS);
0717: return requestBBox;
0718: }
0719:
0720: private static boolean isEnvelopeNull(Envelope bbox) {
0721: if (bbox.getWidth() <= 0 || bbox.getHeight() <= 0) {
0722: return true;
0723: }
0724: return false;
0725: }
0726:
0727: // private static ReferencedEnvelope calculateOldRequestBoundingBox(
0728: // final boolean clientSideReprojection, final Envelope viewportBBox,
0729: // final CoordinateReferenceSystem viewportCRS, final List<Layer> wmsLayers,
0730: // final boolean[] isFullSizeOut ) throws RenderException {
0731: // isFullSizeOut[0] = false;
0732: //
0733: // Envelope viewportBBox2 = viewportBBox;
0734: // if (viewportBBox2 == null) {
0735: // viewportBBox2 = new Envelope(-180, 180, -90, 90);
0736: // }
0737: //
0738: // Envelope layersBBox = getLayersBoundingBox(viewportCRS, wmsLayers);
0739: // if (layersBBox == null)
0740: // try {
0741: // return new ReferencedEnvelope(viewportBBox, viewportCRS).transform(
0742: // DefaultGeographicCRS.WGS84, true);
0743: // } catch (TransformException e1) {
0744: // throw (RuntimeException) new RuntimeException(Messages.BasicWMSRenderer2_error)
0745: // .initCause(e1);
0746: // } catch (FactoryException e1) {
0747: // throw (RuntimeException) new RuntimeException(Messages.BasicWMSRenderer2_error)
0748: // .initCause(e1);
0749: // }
0750: //
0751: // if (!layersBBox.intersects(viewportBBox2))
0752: // return new ReferencedEnvelope(new Envelope(0, 0, 0, 0), viewportCRS);
0753: //
0754: // double minx, miny, maxx, maxy;
0755: // minx = layersBBox.getMinX();
0756: // maxx = layersBBox.getMaxX();
0757: // miny = layersBBox.getMinY();
0758: // maxy = layersBBox.getMaxY();
0759: // boolean noClipping = false;
0760: //
0761: // int i = 0;
0762: // if (viewportBBox2.getMinX() > minx || noClipping) {
0763: // minx = viewportBBox2.getMinX();
0764: // i++;
0765: // }
0766: // if (viewportBBox2.getMinY() > miny || noClipping) {
0767: // miny = viewportBBox2.getMinY();
0768: // i++;
0769: // }
0770: // if (viewportBBox2.getMaxX() < maxx || noClipping) {
0771: // maxx = viewportBBox2.getMaxX();
0772: // i++;
0773: // }
0774: // if (viewportBBox2.getMaxY() < maxy || noClipping) {
0775: // maxy = viewportBBox2.getMaxY();
0776: // i++;
0777: // }
0778: //
0779: // if (i == 4)
0780: // isFullSizeOut[0] = true;
0781: //
0782: // ReferencedEnvelope clippedBBox = new ReferencedEnvelope(
0783: // new Envelope(minx, maxx, miny, maxy), viewportCRS);
0784: //
0785: // if (clientSideReprojection) {
0786: // // Convert the clipped bounding box to the request CRS. This is the
0787: // // BBox to be used in the request.
0788: // try {
0789: // String code = findRequestCRS(wmsLayers);
0790: // if (code == null)
0791: // throw new RenderException(
0792: // "Error has occurred in the framework! There is no common CRS in layers in renderer");
0793: // //$NON-NLS-1$
0794: // CoordinateReferenceSystem crs = CRS.decode(code);
0795: //
0796: // clippedBBox = new ReferencedEnvelope(JTS.transform(clippedBBox, null, CRS
0797: // .findMathTransform(viewportCRS, crs, true), 4), crs);
0798: // } catch (NoSuchAuthorityCodeException e) {
0799: // WMSPlugin.log(e.getLocalizedMessage(), e);
0800: // return null;
0801: // } catch (FactoryException e) {
0802: // WMSPlugin.log(e.getLocalizedMessage(), e);
0803: // return null;
0804: // } catch (MismatchedDimensionException e) {
0805: // WMSPlugin.log(e.getLocalizedMessage(), e);
0806: // return null;
0807: // } catch (TransformException e) {
0808: // e.printStackTrace();
0809: // WMSPlugin.log(e.getLocalizedMessage(), e);
0810: // return null;
0811: // }
0812: // }
0813: //
0814: // return clippedBBox;
0815: // }
0816:
0817: public static Envelope getLayersBoundingBox(
0818: CoordinateReferenceSystem crs, List<Layer> layers) {
0819: Envelope envelope = null;
0820:
0821: for (Layer layer : layers) {
0822:
0823: GeneralEnvelope temp = layer.getEnvelope(crs);
0824:
0825: if (temp != null) {
0826: Envelope jtsTemp = new Envelope(temp.getMinimum(0),
0827: temp.getMaximum(0), temp.getMinimum(1), temp
0828: .getMaximum(1));
0829: if (envelope == null) {
0830: envelope = jtsTemp;
0831: } else {
0832: envelope.expandToInclude(jtsTemp);
0833: }
0834: }
0835: }
0836:
0837: return envelope;
0838: }
0839:
0840: // private boolean supportsCRS( CoordinateReferenceSystem crs ) throws IOException {
0841: // boolean result = false;
0842: //
0843: // EPSG_CODE: for( Identifier id : crs.getIdentifiers() ) {
0844: // String epsgCode = id.toString();
0845: // for( ILayer ilayer : getLayers() ) {
0846: // Layer layer = ilayer.getResource(Layer.class, null);
0847: //
0848: // if (!layer.getSrs().contains(epsgCode)) {
0849: // continue EPSG_CODE;
0850: // }
0851: // }
0852: // return true;
0853: // }
0854: //
0855: // return result;
0856: // }
0857:
0858: static String findRequestCRS(List<Layer> layers,
0859: CoordinateReferenceSystem viewportCRS) {
0860: String requestCRS = null;
0861:
0862: if (layers == null || layers.isEmpty()) {
0863: return null;
0864: }
0865:
0866: Collection<String> viewportEPSG = extractEPSG(viewportCRS);
0867: // TODO need to compare based on crs rather than hoping that the CRS has a EPSG identifier
0868: if (viewportEPSG != null) {
0869:
0870: String match = matchEPSG(layers, viewportEPSG);
0871: if (match != null)
0872: return match;
0873: }
0874:
0875: String string = EPSG_4326;
0876: if (matchEPSG(layers, EPSG_4326))
0877: return EPSG_4326;
0878:
0879: if (matchEPSG(layers, EPSG_4269)) {
0880: return EPSG_4269;
0881: }
0882:
0883: Layer firstLayer = layers.get(0);
0884: for (Object object : firstLayer.getSrs()) {
0885: String epsgCode = (String) object;
0886:
0887: boolean canUseThisEPSG = true;
0888:
0889: try {
0890: // Check to see if *we* can actually use this code first.
0891: CRS.decode(epsgCode);
0892: } catch (NoSuchAuthorityCodeException e) {
0893: canUseThisEPSG = false;
0894: continue;
0895: }
0896:
0897: if (matchEPSG(layers, epsgCode)) {
0898: requestCRS = epsgCode;
0899: return requestCRS;
0900: }
0901: }
0902:
0903: if (requestCRS == null) {
0904: // Hmm. Our layers have no SRS in common - we are in an illegal state
0905: WMSPlugin
0906: .log("ERROR: Illegal State: Basic WMS Renderer contains layers with no common CRS. Unable to perform request."); //$NON-NLS-1$
0907: return null;
0908: }
0909: return requestCRS;
0910: }
0911:
0912: private static String matchEPSG(List<Layer> layers,
0913: Collection<String> epsgCodes) {
0914: for (String epsg : epsgCodes) {
0915: if (matchEPSG(layers, epsg))
0916: return epsg;
0917: }
0918: return null;
0919: }
0920:
0921: private static Collection<String> extractEPSG(
0922: CoordinateReferenceSystem crs) {
0923: Set<Identifier> identifiers = crs.getIdentifiers();
0924:
0925: Set<String> codes = new HashSet<String>();
0926: for (Identifier identifier : identifiers) {
0927: InternationalString title = identifier.getAuthority()
0928: .getTitle();
0929: if (title.toString().equalsIgnoreCase("EPSG")) { //$NON-NLS-1$
0930: codes.add(identifier.toString());
0931: break;
0932: }
0933: Collection<InternationalString> alternateTitles = identifier
0934: .getAuthority().getAlternateTitles();
0935:
0936: for (InternationalString alternateTitle : alternateTitles) {
0937: if (alternateTitle.toString().equalsIgnoreCase("EPSG")) { //$NON-NLS-1$
0938: codes.add(identifier.toString());
0939: break;
0940: }
0941: }
0942:
0943: }
0944: return codes;
0945: }
0946:
0947: private static boolean matchEPSG(List<Layer> layers, String epsgCode) {
0948: boolean match = true;
0949: for (Layer layer : layers) {
0950: Set srs = layer.getSrs();
0951: if (!srs.contains(epsgCode)) {
0952: match = false;
0953: break;
0954: }
0955: }
0956: return match;
0957: }
0958:
0959: private List<ILayer> getLayers() {
0960: List<ILayer> layers = new ArrayList<ILayer>();
0961:
0962: ICompositeRenderContext context1 = getContext();
0963: IRenderContext[] contexts = context1.getContexts().toArray(
0964: new IRenderContext[context1.getContexts().size()]);
0965:
0966: if (contexts.length == 0)
0967: throw new RuntimeException(
0968: Messages.BasicWMSRenderer2_no_layers_to_render);
0969:
0970: for (IRenderContext renderContext : contexts) {
0971: if (renderContext.getLayer().isVisible()) {
0972: layers.add(renderContext.getLayer());
0973: }
0974: }
0975:
0976: if (layers.isEmpty()) {
0977: WMSPlugin.log("WARNING: WMS Renderer contains no layers."); //$NON-NLS-1$
0978: }
0979:
0980: return layers;
0981: }
0982:
0983: private List<Layer> getWMSLayers() throws IOException {
0984: List<Layer> layers = new ArrayList<Layer>();
0985:
0986: for (ILayer iLayer : getLayers()) {
0987: Layer layer = iLayer.getResource(Layer.class, null);
0988: layers.add(layer);
0989: }
0990:
0991: return layers;
0992: }
0993:
0994: private WebMapServer getWMS() throws IOException {
0995: return getContext().getLayer().getResource(WebMapServer.class,
0996: null);
0997: }
0998:
0999: @Override
1000: public void dispose() {
1001: // TODO: make sure there is nothing needing disposal
1002: }
1003:
1004: Job refreshJob = new Job(REFRESH_JOB) {
1005: @Override
1006: protected IStatus run(IProgressMonitor monitor) {
1007: getContext().clearImage();
1008: try {
1009: render(getContext().getImage().createGraphics(),
1010: monitor);
1011: } catch (Throwable e) {
1012: WMSPlugin.log(e.getLocalizedMessage(), e);
1013: }
1014: return Status.OK_STATUS;
1015: }
1016:
1017: };
1018:
1019: public void refreshImage() throws RenderException {
1020: if (needRefresh()) {
1021: refreshJob.schedule();
1022: try {
1023: refreshJob.join();
1024: } catch (InterruptedException e) {
1025: // TODO Catch e
1026: }
1027: }
1028: }
1029:
1030: /**
1031: * Determine whether the image needs to be refreshed
1032: *
1033: * @return Whether the image needs to be refreshed
1034: */
1035: protected boolean needRefresh() {
1036: return false; // TODO
1037: }
1038:
1039: public RenderException wrapException(Throwable t) {
1040: getContext().setStatus(ILayer.ERROR);
1041: RenderException renderException = new RenderException(t
1042: .getLocalizedMessage());
1043: renderException.initCause(t);
1044: return renderException;
1045: }
1046:
1047: @Override
1048: public ICompositeRenderContext getContext() {
1049: return (ICompositeRenderContext) super .getContext();
1050: }
1051:
1052: private CoordinateReferenceSystem getViewportCRS() {
1053: return getContext().getViewportModel().getCRS();
1054: }
1055:
1056: private Envelope getViewportBBox() {
1057: return getContext().getViewportModel().getBounds();
1058: }
1059:
1060: private IPreferenceStore getPreferencesStore() {
1061: return WMSPlugin.getDefault().getPreferenceStore();
1062: }
1063:
1064: }
|