001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
002: * This code is licensed under the GPL 2.0 license, availible at the root
003: * application directory.
004: */
005: package org.geoserver.wms;
006:
007: import com.vividsolutions.jts.geom.Envelope;
008:
009: import org.geotools.geometry.jts.ReferencedEnvelope;
010: import org.geotools.referencing.CRS;
011: import org.geotools.referencing.crs.DefaultGeographicCRS;
012: import org.opengis.referencing.FactoryException;
013: import org.opengis.referencing.NoSuchAuthorityCodeException;
014: import org.opengis.referencing.crs.CoordinateReferenceSystem;
015: import org.opengis.referencing.operation.TransformException;
016: import org.springframework.beans.BeansException;
017: import org.springframework.context.ApplicationContext;
018: import org.springframework.context.ApplicationContextAware;
019: import org.vfny.geoserver.global.FeatureTypeInfo;
020: import org.vfny.geoserver.global.MapLayerInfo;
021: import org.vfny.geoserver.wms.requests.DescribeLayerRequest;
022: import org.vfny.geoserver.wms.requests.GetFeatureInfoRequest;
023: import org.vfny.geoserver.wms.requests.GetLegendGraphicRequest;
024: import org.vfny.geoserver.wms.requests.GetMapRequest;
025: import org.vfny.geoserver.wms.requests.WMSCapabilitiesRequest;
026: import org.vfny.geoserver.wms.responses.DescribeLayerResponse;
027: import org.vfny.geoserver.wms.responses.GetFeatureInfoResponse;
028: import org.vfny.geoserver.wms.responses.GetLegendGraphicResponse;
029: import org.vfny.geoserver.wms.responses.GetMapResponse;
030: import org.vfny.geoserver.wms.responses.WMSCapabilitiesResponse;
031: import org.vfny.geoserver.wms.servlets.Capabilities;
032: import org.vfny.geoserver.wms.servlets.DescribeLayer;
033: import org.vfny.geoserver.wms.servlets.GetFeatureInfo;
034: import org.vfny.geoserver.wms.servlets.GetLegendGraphic;
035: import org.vfny.geoserver.wms.servlets.GetMap;
036:
037: import java.io.IOException;
038: import java.util.ArrayList;
039: import java.util.Collections;
040: import java.util.List;
041:
042: public class DefaultWebMapService implements WebMapService,
043: ApplicationContextAware {
044: /**
045: * default for 'format' parameter.
046: */
047: public static String FORMAT = "image/png";
048:
049: /**
050: * default for 'styles' parameter.
051: */
052: public static List STYLES = Collections.EMPTY_LIST;
053:
054: /**
055: * default for 'height' parameter.
056: */
057: public static int HEIGHT = 512;
058:
059: /**
060: * default for 'height' parameter.
061: */
062: public static int WIDTH = 512;
063:
064: /**
065: * default for 'srs' parameter.
066: */
067: public static String SRS = "EPSG:4326";
068:
069: /**
070: * default for 'transparent' parameter.
071: */
072: public static Boolean TRANSPARENT = Boolean.TRUE;
073:
074: /**
075: * default for 'bbox' paramter
076: */
077: public static ReferencedEnvelope BBOX = new ReferencedEnvelope(
078: new Envelope(-180, 180, -90, 90),
079: DefaultGeographicCRS.WGS84);
080:
081: /**
082: * The current getMap request
083: */
084: GetMapRequest getMap = null;
085:
086: /**
087: * Application context
088: */
089: ApplicationContext context;
090:
091: public void setApplicationContext(ApplicationContext context)
092: throws BeansException {
093: this .context = context;
094: }
095:
096: public WMSCapabilitiesResponse getCapabilities(
097: WMSCapabilitiesRequest request) {
098: Capabilities capabilities = (Capabilities) context
099: .getBean("wmsGetCapabilities");
100:
101: return (WMSCapabilitiesResponse) capabilities.getResponse();
102: }
103:
104: public WMSCapabilitiesResponse capabilities(
105: WMSCapabilitiesRequest request) {
106: return getCapabilities(request);
107: }
108:
109: public DescribeLayerResponse describeLayer(
110: DescribeLayerRequest request) {
111: DescribeLayer describeLayer = (DescribeLayer) context
112: .getBean("wmsDescribeLayer");
113:
114: return (DescribeLayerResponse) describeLayer.getResponse();
115: }
116:
117: public GetMapResponse getMap(GetMapRequest request) {
118: GetMap getMap = (GetMap) context.getBean("wmsGetMap");
119:
120: return (GetMapResponse) getMap.getResponse();
121: }
122:
123: public GetMapResponse map(GetMapRequest request) {
124: return getMap(request);
125: }
126:
127: public GetFeatureInfoResponse getFeatureInfo(
128: GetFeatureInfoRequest request) {
129: GetFeatureInfo getFeatureInfo = (GetFeatureInfo) context
130: .getBean("wmsGetFeatureInfo");
131:
132: return (GetFeatureInfoResponse) getFeatureInfo.getResponse();
133: }
134:
135: public GetLegendGraphicResponse getLegendGraphic(
136: GetLegendGraphicRequest request) {
137: GetLegendGraphic getLegendGraphic = (GetLegendGraphic) context
138: .getBean("wmsGetLegendGraphic");
139:
140: return (GetLegendGraphicResponse) getLegendGraphic
141: .getResponse();
142: }
143:
144: //refector operations
145: public GetMapResponse reflect(GetMapRequest request) {
146: return getMapReflect(request);
147: }
148:
149: public GetMapResponse getMapReflect(GetMapRequest request) {
150: getMap = (GetMapRequest) request;
151:
152: //set the defaults
153: if (getMap.getFormat() == null) {
154: getMap.setFormat(FORMAT);
155: }
156:
157: if ((getMap.getStyles() == null)
158: || getMap.getStyles().isEmpty()) {
159: //set styles to be the defaults for the specified layers
160: //TODO: should this be part of core WMS logic? is so lets throw this
161: // into the GetMapKvpRequestReader
162: if ((getMap.getLayers() != null)
163: && (getMap.getLayers().length > 0)) {
164: ArrayList styles = new ArrayList(
165: getMap.getLayers().length);
166:
167: for (int i = 0; i < getMap.getLayers().length; i++) {
168: styles.add(getMap.getLayers()[i].getDefaultStyle());
169: }
170:
171: getMap.setStyles(styles);
172: } else {
173: getMap.setStyles(STYLES);
174: }
175: }
176:
177: this .autoSetBoundsAndSize();
178:
179: return getMap(getMap);
180: }
181:
182: /**
183: * This method tries to automatically determine SRS, bounding box and output
184: * size based on the layers provided by the user and any other parameters.
185: *
186: * If bounds are not specified by the user, they are automatically se to the
187: * union of the bounds of all layers.
188: *
189: * The size of the output image defaults to 512 pixels, the height is
190: * automatically determined based on the width to height ratio of the
191: * requested layers. This is also true if either height or width are
192: * specified by the user. If both height and width are specified by the user,
193: * the automatically determined bounding box will be adjusted to fit inside
194: * these bounds.
195: */
196: private void autoSetBoundsAndSize() {
197: // Get the layers
198: MapLayerInfo[] layers = getMap.getLayers();
199:
200: // Determine the SRS first
201: String reqSRS = getMap.getSRS();
202: CoordinateReferenceSystem reqCRS = getMap.getCrs();
203: boolean useNativeBounds = true;
204: if (getMap.getSRS() == null
205: || reqSRS.equalsIgnoreCase("EPSG:4326")) {
206: useNativeBounds = false;
207: reqSRS = "EPSG:4326";
208: getMap.setSRS(reqSRS);
209: try {
210: reqCRS = CRS.decode(reqSRS);
211: } catch (NoSuchAuthorityCodeException e) {
212: e.printStackTrace();
213: } catch (FactoryException e) {
214: e.printStackTrace();
215: }
216: getMap.setCrs(reqCRS);
217: } else {
218: for (int i = 0; useNativeBounds && i < layers.length; i++) {
219: useNativeBounds = layers[i].getFeature().getSRS()
220: .equalsIgnoreCase(reqSRS);
221: }
222: }
223:
224: // Ready to determine the bounds based on the layers, if not specified
225: Envelope layerbbox = null;
226:
227: boolean specifiedBbox = (getMap.getBbox() != null);
228:
229: if (specifiedBbox) {
230: layerbbox = getMap.getBbox();
231: } else {
232: // Get the bounding box from the layers
233: for (int i = 0; i < layers.length; i++) {
234: Envelope curbbox = null;
235:
236: FeatureTypeInfo curFTI = layers[i].getFeature();
237: try {
238: if (curFTI != null) {
239: // Local feature type
240: if (useNativeBounds) {
241: curbbox = curFTI.getLatLongBoundingBox();
242: } else {
243: curbbox = curFTI.getBoundingBox();
244: }
245: } else {
246: curbbox = layers[i].getRemoteFeatureSource()
247: .getBounds();
248: }
249: } catch (IOException e) {
250: e.printStackTrace();
251: }
252:
253: if (i == 0) {
254: layerbbox = new Envelope(curbbox);
255: } else {
256: layerbbox.expandToInclude(curbbox);
257: }
258: }
259: // Reproject if we have to
260: if (!useNativeBounds) {
261: try {
262: ReferencedEnvelope ref = new ReferencedEnvelope(
263: layerbbox, CRS.decode("EPSG:4326"));
264: layerbbox = ref.transform(reqCRS, true);
265: } catch (NoSuchAuthorityCodeException e) {
266: e.printStackTrace();
267: } catch (TransformException e) {
268: e.printStackTrace();
269: } catch (FactoryException e) {
270: e.printStackTrace();
271: }
272: }
273: }
274:
275: // Just in case
276: if (layerbbox == null)
277: layerbbox = BBOX;
278:
279: double bbheight = layerbbox.getHeight();
280: double bbwidth = layerbbox.getWidth();
281: double bbratio = bbwidth / bbheight;
282:
283: if (!specifiedBbox) {
284: // Zoom out 4%, accomodates for reprojection etc
285: layerbbox.expandBy(layerbbox.getWidth() / 50, layerbbox
286: .getHeight() / 50);
287: }
288:
289: double mheight = getMap.getHeight();
290: double mwidth = getMap.getWidth();
291:
292: if (mheight > 0.5 && mwidth > 0.5 && specifiedBbox) {
293: // This person really doesnt want our help
294: } else {
295: if (mheight > 0.5 && mwidth > 0.5) {
296: // Fully specified, need to adjust bbox
297: double mratio = mwidth / mheight;
298: // Adjust bounds to be less than ideal to meet spec
299: if (bbratio > mratio) {
300: // Too wide, need to increase height of bb
301: double diff = ((bbwidth / mratio) - bbheight) / 2;
302: layerbbox.expandBy(0, diff);
303: } else {
304: // Too tall, need to increase width of bb
305: double diff = ((bbheight * mratio) - bbwidth) / 2;
306: layerbbox.expandBy(diff, 0);
307: }
308: } else if (mheight > 0.5) {
309: mwidth = bbratio * mheight;
310: } else {
311: if (mwidth > 0.5) {
312: // We're set
313: } else {
314: // Fall through to the default
315: mwidth = WIDTH;
316: }
317: mheight = mwidth / bbratio;
318: }
319:
320: // Actually set the bounding box and size of image
321: getMap.setBbox(layerbbox);
322: getMap.setWidth((int) mwidth);
323: getMap.setHeight((int) mheight);
324: }
325: }
326: }
|