001: package net.refractions.udig.tool.info.internal;
002:
003: import java.awt.Point;
004: import java.io.BufferedReader;
005: import java.io.IOException;
006: import java.io.InputStream;
007: import java.io.InputStreamReader;
008: import java.net.URL;
009: import java.util.ArrayList;
010: import java.util.Arrays;
011: import java.util.Collections;
012: import java.util.HashMap;
013: import java.util.HashSet;
014: import java.util.List;
015: import java.util.Map;
016: import java.util.Set;
017:
018: import net.refractions.udig.project.ILayer;
019: import net.refractions.udig.project.render.ICompositeRenderContext;
020: import net.refractions.udig.project.render.IRenderContext;
021: import net.refractions.udig.style.wms.WMSStyleContent;
022: import net.refractions.udig.tool.info.InfoPlugin;
023: import net.refractions.udig.tool.info.LayerPointInfo;
024:
025: import org.eclipse.core.runtime.IProgressMonitor;
026: import org.geotools.data.ows.Layer;
027: import org.geotools.data.wms.WebMapServer;
028: import org.geotools.data.wms.request.GetFeatureInfoRequest;
029: import org.geotools.data.wms.request.GetMapRequest;
030: import org.geotools.data.wms.response.GetFeatureInfoResponse;
031: import org.geotools.geometry.jts.ReferencedEnvelope;
032: import org.geotools.referencing.CRS;
033: import org.opengis.layer.Style;
034: import org.opengis.metadata.Identifier;
035: import org.opengis.referencing.NoSuchAuthorityCodeException;
036: import org.opengis.referencing.crs.CoordinateReferenceSystem;
037: import org.xml.sax.SAXException;
038:
039: import com.vividsolutions.jts.geom.Envelope;
040:
041: public class WMSDescribeLayer {
042:
043: /** Figures out the mapping from wms layers to udig layers */
044: private Map<Layer, ILayer> getLayerMap(
045: ICompositeRenderContext composite, IProgressMonitor monitor)
046: throws IOException {
047: Map<Layer, ILayer> mapping = new HashMap<Layer, ILayer>();
048: for (IRenderContext context : composite.getContexts()) {
049: ILayer layer = context.getLayer();
050: if (context.getLayer().isVisible()
051: //&& layer.isApplicable("information" )
052: ) {
053: Layer wmslayer = layer
054: .getResource(Layer.class, monitor);
055: mapping.put(wmslayer, layer);
056: }
057: }
058: return mapping;
059: }
060:
061: public WebMapServer getWMS(ICompositeRenderContext context,
062: IProgressMonitor monitor) throws IOException {
063: return context.getLayer().getResource(WebMapServer.class,
064: monitor);
065: }
066:
067: /** Get list of applicable wms layers from composite */
068: private List<Layer> getLayerList(ICompositeRenderContext composite)
069: throws IOException {
070: List<Layer> layers = new ArrayList<Layer>();
071: for (IRenderContext context : composite.getContexts()) {
072: ILayer layer = context.getLayer();
073: if (layer.isVisible()
074: // && layer.isApplicable("information")
075: ) {
076: Layer wmslayer = layer.getResource(
077: org.geotools.data.ows.Layer.class, null);
078: layers.add(wmslayer);
079: }
080: }
081: return layers;
082: }
083:
084: /**
085: * Implementation is forgiving codes must be in uppercase etc ...
086: *
087: * @param crs CoordinateReferenceSystem
088: * @param codes Set of valid vodes
089: *
090: * @return Code common to both or null
091: */
092: private static String commonCode(CoordinateReferenceSystem crs,
093: Set<String> codes) {
094: // First pass based on string identity
095: //
096: Set<String> crsCodes = new HashSet<String>();
097: for (Identifier id : crs.getIdentifiers()) {
098: String code = id.toString();
099: if (codes.contains(code))
100: return code;
101: crsCodes.add(code);
102: }
103: // Second pass based on CoordinateReferenceSystem equality
104: for (String code : codes) {
105: try {
106: CoordinateReferenceSystem check = CRS.decode(code);
107: if (crs.equals(check)) {
108: // note we are trusting the code of the matched crs
109: // (because if the id provided by crs worked we would
110: // not get this far
111: //
112: return check.getIdentifiers().iterator().next()
113: .getCode();
114: }
115: } catch (NoSuchAuthorityCodeException e) {
116: // could not understand code
117: }
118: }
119: // last pass do the power set lookup based on id
120: //
121: for (String code : codes) {
122: try {
123: CoordinateReferenceSystem check = CRS.decode(code);
124: for (Identifier checkId : check.getIdentifiers()) {
125: String checkCode = checkId.toString();
126: if (crsCodes.contains(checkCode)) {
127: return code;
128: }
129: }
130: } catch (NoSuchAuthorityCodeException e) {
131: // could not understand code
132: }
133: }
134: return null;
135: }
136:
137: @SuppressWarnings("unchecked")
138: public static List<LayerPointInfo> info(ILayer layer,
139: ReferencedEnvelope bbox) throws IOException {
140: LayerPointInfo info = info2(layer, bbox);
141: if (info == null)
142: return Collections.EMPTY_LIST;
143:
144: return Collections.singletonList(info);
145: }
146:
147: /**
148: * Aquire info for the provided bbox
149: *
150: * @param layer Must be associated with a wms layer
151: * @param bbox
152: * @return LayerPointInfo object or null if information is unavailable.
153: * @throws IOException
154: * @throws IOException if Response could not be understood
155: *
156: * TODO: possible problem with client side reprojection - may need to
157: * properly reproject the center point that was clicked on
158: *
159: * TODO: this requires some testing
160: */
161: public static LayerPointInfo info2(ILayer layer,
162: ReferencedEnvelope bbox) throws IOException {
163:
164: Envelope reprojected = null;
165: try {
166: reprojected = bbox.transform(layer.getMap()
167: .getViewportModel().getCRS(), true);
168: } catch (Exception e) {
169: InfoPlugin.log("", e); //$NON-NLS-1$
170: return null;
171: }
172:
173: Point centre = layer.getMap().getViewportModel().worldToPixel(
174: reprojected.centre());
175:
176: Envelope sanebbox = layer.getMap().getViewportModel()
177: .getBounds();
178: bbox = new ReferencedEnvelope(sanebbox, bbox
179: .getCoordinateReferenceSystem());
180:
181: Layer wmslayer;
182: wmslayer = layer.getResource(Layer.class, null);
183:
184: if (wmslayer == null) {
185: throw new IllegalArgumentException(
186: "Provided layer cannot resolve to a wms layer"); //$NON-NLS-1$
187: }
188: // TODO: Fix wmslayer so we can ask who its "source" is.
189: final WebMapServer wms = layer.getResource(WebMapServer.class,
190: null);
191: if (wms == null) {
192: throw new IllegalArgumentException(
193: "Provided layer cannot resolve to a wms"); //$NON-NLS-1$
194: }
195: if (!wmslayer.isQueryable())
196: return null;
197:
198: String desiredFormat = desiredInfoFormat(wms);
199: if (desiredFormat == null)
200: return null;
201:
202: GetMapRequest getmap = wms.createGetMapRequest();
203: Set srs = wmslayer.getSrs();
204: @SuppressWarnings("unchecked")
205: String code = commonCode(layer.getMap().getViewportModel()
206: .getCRS(), srs);
207:
208: // if( code != null ) {
209: // // we have a common crs! Use the bbox as is
210: // }
211: // else {
212: // // lets reproject our bounding box then
213: // for( Iterator i= srs.iterator(); code == null && i.hasNext(); ) {
214: // try {
215: // String tryCode = (String) i.next();
216: // CoordinateReferenceSystem crs = CRS.decode( tryCode );
217: // bbox = bbox.transform(crs, true);
218: // code = tryCode;
219: // break;
220: // }
221: // catch (Throwable t ) {
222: // // could not use tryCode
223: // }
224: // }
225: // if( code == null ) {
226: // return null; // we really can't get there from here
227: // }
228: // try {
229: // CoordinateReferenceSystem crs = CRS.decode(code);
230: // bbox = bbox.transform(crs, true);
231: // } catch (Exception e) {
232: // return null; // we cannot transform this
233: // }
234: // }
235:
236: // okay we got the bbox
237: // lets fake a request to query against
238: // 3x3 pixels the dimension of the bbox
239: getmap.setBBox(bbox.getMinX() + "," + bbox.getMinY() + "," + //$NON-NLS-1$ //$NON-NLS-2$
240: bbox.getMaxX() + "," + bbox.getMaxY()); //$NON-NLS-1$
241: getmap.setProperty(GetMapRequest.LAYERS, wmslayer.getName());
242: int width = layer.getMap().getRenderManager().getMapDisplay()
243: .getWidth();
244: int height = layer.getMap().getRenderManager().getMapDisplay()
245: .getHeight();
246: getmap.setDimensions(width, height);
247: getmap.setSRS(code);
248:
249: List formats = Arrays.asList(wms.getCapabilities().getRequest()
250: .getGetMap().getFormatStrings());
251: if (formats.contains("image/png")) { //$NON-NLS-1$
252: getmap.setProperty(GetMapRequest.FORMAT, "image/png"); //$NON-NLS-1$
253: } else if (formats.contains("image/gif")) { //$NON-NLS-1$
254: getmap.setProperty(GetMapRequest.FORMAT, "image/gif"); //$NON-NLS-1$
255: } else if (formats.contains("image/jpeg")) { //$NON-NLS-1$
256: getmap.setProperty(GetMapRequest.FORMAT, "image/jpeg"); //$NON-NLS-1$
257: } else if (formats.contains("image/bmp")) { //$NON-NLS-1$
258: getmap.setProperty(GetMapRequest.FORMAT, "image/bmp"); //$NON-NLS-1$
259: }
260:
261: Style wmsStyle = (Style) layer.getStyleBlackboard().get(
262: WMSStyleContent.WMSSTYLE);
263: if (wmsStyle != null) {
264: getmap
265: .setProperty(GetMapRequest.STYLES, wmsStyle
266: .getName());
267: }
268:
269: final GetFeatureInfoRequest request = wms
270: .createGetFeatureInfoRequest(getmap);
271: request.setInfoFormat(desiredFormat);
272: request.setQueryPoint(centre.x, centre.y);
273: request.setQueryLayers(Collections.singleton(wmslayer));
274:
275: LayerPointInfo info = new LayerPointInfo(layer) {
276:
277: private GetFeatureInfoResponse response;
278:
279: /** Lazy request */
280:
281: protected GetFeatureInfoResponse getResponse()
282: throws IOException {
283: if (this .response == null) {
284: try {
285: System.out.println(request.getFinalURL());
286: this .response = wms.issueRequest(request);
287: } catch (SAXException e) {
288: throw new IOException(
289: "Unable to parse the returned response from the server. Reason unknown."); //$NON-NLS-1$
290: }
291: }
292: return this .response;
293: }
294:
295: public URL getRequestURL() {
296: return request.getFinalURL();
297: }
298:
299: /** Only acquire the value if needed */
300: public String getMimeType() {
301: try {
302: return getResponse().getContentType();
303: } catch (IOException e) {
304: return null; // unavailable
305: }
306: }
307:
308: /** Only acquire the value if needed */
309: public Object acquireValue() throws IOException {
310: // final String CONTENT_TYPE = response.getContentType();
311: // if ("text/plain".equals( CONTENT_TYPE ) ||
312: // "text/html".equals( CONTENT_TYPE )) {
313: String result = ""; //$NON-NLS-1$
314: InputStream input = getResponse().getInputStream();
315: BufferedReader reader = new BufferedReader(
316: new InputStreamReader(input));
317: String line = null;
318: while ((line = reader.readLine()) != null) {
319: result = result + line;
320: }
321: reader.close();
322: input.close();
323: return result;
324: // } else if ( LayerPointInfo.GML.equals( CONTENT_TYPE ) ) {
325: // // parse w/ GML parser from WFS !
326: // return Collections.singletonList(new LayerPointInfo( getContext().getLayer() ) {
327: // protected Object acquireValue() {
328: // return null; // should perform lazy parsing here
329: // }
330: // public String getMimeType() {
331: // return LayerPointInfo.GML;
332: // }
333: // });
334: // }
335: // else {
336: // return Collections.EMPTY_LIST;
337: // }
338: }
339: };
340: return info;
341: }
342:
343: /**
344: * This assumes the same formats are available for all layers? Hope that is a good assumption.
345: *
346: * @param wms
347: * @return MIME type of prefered content, or null if we can't get our info on
348: */
349: private static String desiredInfoFormat(WebMapServer wms) {
350: String array[] = wms.getCapabilities().getRequest()
351: .getGetFeatureInfo().getFormatStrings();
352: List formats = Arrays.asList(array);
353:
354: String desiredFormat;
355:
356: if (formats.contains("text/html")) { //$NON-NLS-1$
357: desiredFormat = "text/html"; //$NON-NLS-1$
358: } else if (formats.contains("text/plain")) { //$NON-NLS-1$
359: desiredFormat = "text/plain"; //$NON-NLS-1$
360: } else if (formats.contains(LayerPointInfo.GML)) {
361: desiredFormat = LayerPointInfo.GML;
362: } else {
363: desiredFormat = null;
364: }
365: return desiredFormat;
366: }
367: }
|