001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/ogcwebservices/wms/operation/GetMap.java $
002 /*---------------- FILE HEADER ------------------------------------------
003
004 This file is part of deegree.
005 Copyright (C) 2001-2006 by:
006 EXSE, Department of Geography, University of Bonn
007 http://www.giub.uni-bonn.de/deegree/
008 lat/lon GmbH
009 http://www.lat-lon.de
010
011 This library is free software; you can redistribute it and/or
012 modify it under the terms of the GNU Lesser General Public
013 License as published by the Free Software Foundation; either
014 version 2.1 of the License, or (at your option) any later version.
015
016 This library is distributed in the hope that it will be useful,
017 but WITHOUT ANY WARRANTY; without even the implied warranty of
018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019 Lesser General Public License for more details.
020
021 You should have received a copy of the GNU Lesser General Public
022 License along with this library; if not, write to the Free Software
023 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024
025 Contact:
026
027 Andreas Poth
028 lat/lon GmbH
029 Aennchenstr. 19
030 53177 Bonn
031 Germany
032 E-Mail: poth@lat-lon.de
033
034 Prof. Dr. Klaus Greve
035 Department of Geography
036 University of Bonn
037 Meckenheimer Allee 166
038 53115 Bonn
039 Germany
040 E-Mail: greve@giub.uni-bonn.de
041
042
043 ---------------------------------------------------------------------------*/
044 package org.deegree.ogcwebservices.wms.operation;
045
046 import java.awt.Color;
047 import java.io.Serializable;
048 import java.io.UnsupportedEncodingException;
049 import java.net.MalformedURLException;
050 import java.net.URL;
051 import java.net.URLDecoder;
052 import java.net.URLEncoder;
053 import java.util.ArrayList;
054 import java.util.Arrays;
055 import java.util.HashMap;
056 import java.util.Iterator;
057 import java.util.List;
058 import java.util.Map;
059 import java.util.StringTokenizer;
060
061 import org.deegree.datatypes.values.Values;
062 import org.deegree.framework.log.ILogger;
063 import org.deegree.framework.log.LoggerFactory;
064 import org.deegree.framework.util.CharsetUtils;
065 import org.deegree.framework.util.ColorUtils;
066 import org.deegree.framework.util.IDGenerator;
067 import org.deegree.framework.util.MimeTypeMapper;
068 import org.deegree.framework.util.NetWorker;
069 import org.deegree.framework.util.StringTools;
070 import org.deegree.framework.xml.Marshallable;
071 import org.deegree.framework.xml.NamespaceContext;
072 import org.deegree.framework.xml.XMLFragment;
073 import org.deegree.framework.xml.XMLParsingException;
074 import org.deegree.framework.xml.XMLTools;
075 import org.deegree.graphics.sld.SLDFactory;
076 import org.deegree.graphics.sld.StyledLayerDescriptor;
077 import org.deegree.i18n.Messages;
078 import org.deegree.model.crs.CRSFactory;
079 import org.deegree.model.crs.CoordinateSystem;
080 import org.deegree.model.crs.UnknownCRSException;
081 import org.deegree.model.spatialschema.Envelope;
082 import org.deegree.model.spatialschema.GMLGeometryAdapter;
083 import org.deegree.model.spatialschema.GeometryFactory;
084 import org.deegree.ogcbase.CommonNamespaces;
085 import org.deegree.ogcbase.InvalidGMLException;
086 import org.deegree.ogcwebservices.InconsistentRequestException;
087 import org.deegree.ogcwebservices.OGCWebServiceException;
088 import org.deegree.ogcwebservices.wmps.operation.PrintMap;
089 import org.deegree.ogcwebservices.wms.InvalidCRSException;
090 import org.deegree.ogcwebservices.wms.InvalidFormatException;
091 import org.deegree.ogcwebservices.wms.InvalidSRSException;
092 import org.deegree.ogcwebservices.wms.configuration.AbstractDataSource;
093 import org.deegree.ogcwebservices.wms.configuration.RemoteWMSDataSource;
094 import org.w3c.dom.Document;
095 import org.w3c.dom.Element;
096
097 /**
098 * This interface describes the access to the parameters of a GeMap request. It is excpected that
099 * there are two kinds of request. The first is the 'normal' HTTP GET request with name-value-pair
100 * enconding and the second is a HTTP POST request containing a SLD.
101 * <p>
102 * </p>
103 * Even it is possible to access the values of a HTTP GET request throught their bean accessor
104 * methods the request shall be mapped to a SLD data structure that is accessible using the
105 * <tt>getSLD()</tt>.
106 *
107 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
108 * @author last edited by: $Author:wanhoff$
109 *
110 * @version $Revision: 7933 $, $Date:20.03.2007$
111 */
112 public class GetMap extends WMSRequestBase {
113
114 private static final long serialVersionUID = 887256882709344021L;
115
116 private static final ILogger LOG = LoggerFactory.getLogger( GetMap.class );
117
118 private Values elevation = null;
119
120 private Values time = null;
121
122 private Map<String, Values> sampleDimension = null;
123
124 private List<Layer> layers = null;
125
126 private Color bGColor = null;
127
128 private Envelope boundingBox = null;
129
130 private String exceptions = null;
131
132 private String format = null;
133
134 private String srs = null;
135
136 private StyledLayerDescriptor sld = null;
137
138 private URL sLD_URL = null;
139
140 private URL wFS_URL = null;
141
142 private boolean transparency = false;
143
144 private int height = 0;
145
146 private int width = 0;
147
148 /**
149 * creates a <tt>WTSGetViewRequest</tt> from a set of parameters and builds up the complete
150 * SLD.
151 *
152 * @return an instance of <tt>GetMapRequest</tt>
153 * @param version
154 * Request version.
155 * @param layers
156 * list of one or more map layers. Optional if SLD parameter is present. Contains
157 * list of one rendering style per requested layer. Optional if SLD parameter is
158 * present.
159 * @param elevation
160 * Elevation of layer desired.
161 * @param sampleDimension
162 * Value of other dimensions as appropriate.
163 * @param format
164 * Output format of map.
165 * @param width
166 * Width in pixels of map picture.
167 * @param height
168 * Height in pixels of map picture.
169 * @param srs
170 * the requested Spatial Reference System.
171 * @param boundingBox
172 * Bounding box corners (lower left, upper right) in SRS units.
173 * @param transparency
174 * Background transparency of map.
175 * @param bGColor
176 * Hexadecimal red-green-blue color value for the background color.
177 * @param exceptions
178 * The format in which exceptions are to be reported by the WMS.
179 * @param time
180 * Time value of layer desired
181 * @param sld
182 * Styled Layer Descriptor
183 * @param id
184 * an unique ID of the request
185 * @param sldURL
186 * @param vendorSpecificParameter
187 * Vendor Specific Parameter
188 */
189 public static GetMap create( String version, String id, Layer[] layers, Values elevation,
190 Map<String, Values> sampleDimension, String format, int width,
191 int height, String srs, Envelope boundingBox,
192 boolean transparency, Color bGColor, String exceptions,
193 Values time, URL sldURL, StyledLayerDescriptor sld,
194 Map<String, String> vendorSpecificParameter ) {
195 return new GetMap( version, id, layers, elevation, sampleDimension, format, width, height,
196 srs, boundingBox, transparency, bGColor, exceptions, time, sldURL, sld,
197 vendorSpecificParameter );
198 }
199
200 /**
201 * creates a getMap request for requesting a cascaded remote WMS considering the getMap request
202 * and the filterconditions defined in the submitted <tt>DataSource</tt> object The request
203 * will be encapsualted within a <tt>OGCWebServiceEvent</tt>.
204 *
205 * @param ds
206 * @param request
207 * @param style
208 * @param layer
209 * @return GetMap request object
210 */
211 public static GetMap createGetMapRequest( AbstractDataSource ds, GetMap request, String style,
212 String layer ) {
213
214 GetMap gmr = ( (RemoteWMSDataSource) ds ).getGetMapRequest();
215
216 String format = request.getFormat();
217
218 if ( gmr != null && !"%default%".equals( gmr.getFormat() ) ) {
219 format = gmr.getFormat();
220 }
221
222 GetMap.Layer[] lys = null;
223 lys = new GetMap.Layer[1];
224
225 if ( style != null ) {
226 lys[0] = PrintMap.createLayer( layer, style );
227 } else {
228 lys[0] = PrintMap.createLayer( layer, "$DEFAULT" );
229 }
230 if ( gmr != null && gmr.getLayers() != null
231 && !( gmr.getLayers()[0].getName().equals( "%default%" ) ) ) {
232 lys = gmr.getLayers();
233 }
234 Color bgColor = request.getBGColor();
235 if ( gmr != null && gmr.getBGColor() != null ) {
236 bgColor = gmr.getBGColor();
237 }
238 Values time = request.getTime();
239 if ( gmr != null && gmr.getTime() != null ) {
240 time = gmr.getTime();
241 }
242
243 Map<String, String> vendorSpecificParameter = request.getVendorSpecificParameters();
244 if ( gmr != null && gmr.getVendorSpecificParameters() != null
245 && gmr.getVendorSpecificParameters().size() > 0 ) {
246 vendorSpecificParameter.putAll( gmr.getVendorSpecificParameters() );
247 }
248 String version = "1.1.0";
249 if ( gmr != null && gmr.getVersion() != null ) {
250 version = gmr.getVersion();
251 }
252
253 Values elevation = request.getElevation();
254 if ( gmr != null && gmr.getElevation() != null ) {
255 elevation = gmr.getElevation();
256 }
257 Map<String, Values> sampleDim = null;
258 if ( gmr != null && gmr.getSampleDimension() != null ) {
259 sampleDim = gmr.getSampleDimension();
260 }
261
262 boolean tranparency = false;
263 if ( gmr != null ) {
264 tranparency = gmr.getTransparency();
265 }
266
267 // now filter out the unwanted vendor specific parameters and put in
268 // the wanted additional ones
269 Map<String, String> vsp = new HashMap<String, String>( 10 );
270 vsp.putAll( ( (RemoteWMSDataSource) ds ).getAddedParameters() );
271 for ( String name : ( (RemoteWMSDataSource) ds ).getPassedParameters() ) {
272 if ( vendorSpecificParameter.containsKey( name ) ) {
273 vsp.put( name, vendorSpecificParameter.get( name ).toString() );
274 }
275 }
276
277 IDGenerator idg = IDGenerator.getInstance();
278 gmr = GetMap.create( version, "" + idg.generateUniqueID(), lys, elevation, sampleDim,
279 format, request.getWidth(), request.getHeight(), request.getSrs(),
280 request.getBoundingBox(), tranparency, bgColor,
281 request.getExceptions(), time, null, null, vsp );
282
283 return gmr;
284 }
285
286 /**
287 * creates a <tt>GetMapRequest</tt> from a <tt>HashMap</tt> that contains the request
288 * parameters as key-value-pairs. Keys are expected to be in upper case notation.
289 *
290 * @param model
291 * <tt>HashMap</tt> containing the request parameters
292 * @return an instance of <tt>GetMapRequest</tt>
293 * @throws InconsistentRequestException
294 * @throws XMLParsingException
295 * @throws MalformedURLException
296 */
297 public static GetMap create( Map<String, String> model )
298 throws InconsistentRequestException, XMLParsingException,
299 MalformedURLException {
300
301 LOG.logDebug( "Request parameters: " + model );
302
303 // use model.remove(..) so at the end of the method the vendor
304 // specific parameters remains in the model HashMap
305 model.remove( "REQUEST" );
306
307 // Version
308 String version = model.remove( "VERSION" );
309
310 if ( version == null ) {
311 version = model.remove( "WMTVER" );
312 }
313
314 if ( version == null ) {
315 throw new InconsistentRequestException( "VERSION-value must be set" );
316 }
317
318 // LAYERS & STYLES & SLD (URL, XML)
319 StyledLayerDescriptor sld = null;
320 String sld_body = model.remove( "SLD_BODY" );
321 String sld_urlstring = model.remove( "SLD" );
322
323 // The SLD is complete in the Maprequest
324 URL sLD_URL = null;
325
326 if ( sld_body != null ) {
327 try {
328 sld_body = URLDecoder.decode( sld_body, CharsetUtils.getSystemCharset() );
329 sld = SLDFactory.createSLD( sld_body );
330 } catch ( Exception ee ) {
331 throw new XMLParsingException( "Could not decode SLD_BODY: " + ee.toString() );
332 }
333 } else if ( sld_urlstring != null ) {
334 // The SLD is as url in the Maprequest
335 sLD_URL = new URL( sld_urlstring );
336
337 try {
338 sld = SLDFactory.createSLD( sLD_URL );
339 } catch ( Exception ioex ) {
340 ioex.printStackTrace();
341 LOG.logError( ioex.getMessage(), ioex );
342 throw new InconsistentRequestException( "IOException occured during the access "
343 + "to the SLD-URL. Wrong URL? Server down?"
344 + ioex.getMessage() );
345 }
346 }
347
348 // LAYERS & STYLES
349 String layersstring = model.remove( "LAYERS" );
350 if ( ( layersstring == null || layersstring.trim().length() == 0 ) && ( sld == null ) ) {
351 throw new InconsistentRequestException(
352 "At least one layer must be defined within a GetMap request" );
353 }
354 String stylesstring = model.remove( "STYLES" );
355
356 // normalize styles parameter
357 if ( stylesstring == null ) {
358 stylesstring = "";
359 }
360 if ( stylesstring.startsWith( "," ) ) {
361 stylesstring = "$DEFAULT" + stylesstring;
362 }
363
364 stylesstring = StringTools.replace( stylesstring, ",,", ",$DEFAULT,", true );
365
366 if ( stylesstring.endsWith( "," ) ) {
367 stylesstring = stylesstring + "$DEFAULT";
368 }
369
370 List<String> layers = StringTools.toList( layersstring, ",", false );
371
372 List<String> styles = null;
373 if ( stylesstring == null || stylesstring.length() == 0 ) {
374 styles = new ArrayList<String>( layers.size() );
375 for ( int i = 0; i < layers.size(); i++ ) {
376 styles.add( "$DEFAULT" );
377 }
378 } else {
379 styles = StringTools.toList( stylesstring, ",", false );
380 }
381
382 // At last, build up the Layer object
383 GetMap.Layer[] ls = new GetMap.Layer[layers.size()];
384
385 for ( int i = 0; i < layers.size(); i++ ) {
386 try {
387 String l = URLDecoder.decode( layers.get( i ), CharsetUtils.getSystemCharset() );
388 ls[i] = GetMap.createLayer( l, styles.get( i ) );
389 } catch ( UnsupportedEncodingException e2 ) {
390 e2.printStackTrace();
391 }
392 }
393
394 // ELEVATION
395 Values elevation = null;
396 // TODO
397 // read elevations
398
399 // SAMPLE DIMENSION
400 Map<String, Values> sampleDimension = null;
401 // TODO
402 // read sampleDimensions
403
404 // FORMAT
405 String format = model.remove( "FORMAT" );
406 if ( format == null ) {
407 throw new InconsistentRequestException( "FORMAT-value must be set" );
408 }
409 try {
410 format = URLDecoder.decode( format, CharsetUtils.getSystemCharset() );
411 } catch ( UnsupportedEncodingException e1 ) {
412 e1.printStackTrace();
413 }
414 if ( !MimeTypeMapper.isKnownImageType( format ) ) {
415 throw new InvalidFormatException( format + " is not a valid image/result format" );
416 }
417
418 // width
419 String tmp = model.remove( "WIDTH" );
420 if ( tmp == null ) {
421 throw new InconsistentRequestException( "WIDTH must be set" );
422 }
423 int width = 0;
424 try {
425 width = Integer.parseInt( tmp );
426 } catch ( Exception e ) {
427 throw new InconsistentRequestException( "WIDTH must be a valid integer number" );
428 }
429
430 // height
431 tmp = model.remove( "HEIGHT" );
432 if ( tmp == null ) {
433 throw new InconsistentRequestException( "HEIGHT must be set" );
434 }
435 int height = 0;
436 try {
437 height = Integer.parseInt( tmp );
438 } catch ( Exception e ) {
439 throw new InconsistentRequestException( "HEIGHT must be a valid integer number" );
440 }
441
442 double minx, miny, maxx, maxy;
443 Envelope boundingBox = null;
444 String srs;
445 boolean isLongLat = false;
446
447 boolean is130 = false;
448
449 if ( "1.3.0".compareTo( version ) <= 0 ) {
450 is130 = true;
451
452 // SRS or rather CRS
453 srs = model.remove( "CRS" );
454 if ( srs == null ) {
455 throw new InvalidCRSException( "CRS-value must be set" );
456 }
457
458 // check for geographic coordinate system
459 try {
460 CoordinateSystem crs = CRSFactory.create( srs );
461 isLongLat = crs.getUnits().equals( "°" ) && srs.toLowerCase().startsWith( "epsg" );
462 } catch ( UnknownCRSException e ) {
463 LOG.logDebug( e.getLocalizedMessage(), e );
464 throw new InvalidCRSException( srs );
465 }
466
467 } else {
468 // SRS
469 srs = model.remove( "SRS" );
470 if ( srs == null ) {
471 throw new InvalidSRSException( "SRS-value must be set" );
472 }
473
474 try {
475 // check for crs validity - yes, this method is bad. Is there a better one?
476 CRSFactory.create( srs );
477 } catch ( UnknownCRSException e ) {
478 LOG.logDebug( e.getLocalizedMessage(), e );
479 if ( is130 ) {
480 throw new InvalidCRSException( Messages.getMessage( "WMS_UNKNOWN_CRS", srs ) );
481 }
482 throw new InvalidSRSException( Messages.getMessage( "WMS_UNKNOWN_CRS", srs ) );
483 }
484
485 }
486
487 // BBOX
488 String boxstring = model.remove( "BBOX" );
489 if ( boxstring == null ) {
490 throw new InconsistentRequestException( "BBOX-value must be set" );
491 }
492 StringTokenizer st = new StringTokenizer( boxstring, "," );
493
494 if ( isLongLat ) {
495 // parse first y, then x
496 String s = st.nextToken().replace( ' ', '+' );
497 miny = Double.parseDouble( s );
498 s = st.nextToken().replace( ' ', '+' );
499 minx = Double.parseDouble( s );
500 s = st.nextToken().replace( ' ', '+' );
501 maxy = Double.parseDouble( s );
502 s = st.nextToken().replace( ' ', '+' );
503 maxx = Double.parseDouble( s );
504 } else {
505 // old method
506 String s = st.nextToken().replace( ' ', '+' );
507 minx = Double.parseDouble( s );
508 s = st.nextToken().replace( ' ', '+' );
509 miny = Double.parseDouble( s );
510 s = st.nextToken().replace( ' ', '+' );
511 maxx = Double.parseDouble( s );
512 s = st.nextToken().replace( ' ', '+' );
513 maxy = Double.parseDouble( s );
514
515 }
516
517 // check for consistency
518 if ( minx >= maxx ) {
519 throw new InvalidFormatException( "minx must be lower than maxx" );
520 }
521
522 if ( miny >= maxy ) {
523 throw new InvalidFormatException( "miny must be lower than maxy" );
524 }
525
526 boundingBox = GeometryFactory.createEnvelope( minx, miny, maxx, maxy, null );
527
528 // TRANSPARENCY
529 boolean transparency = false;
530 String tp = model.remove( "TRANSPARENT" );
531 if ( tp != null ) {
532 transparency = tp.toUpperCase().trim().equals( "TRUE" );
533 }
534
535 String mime = MimeTypeMapper.toMimeType( format );
536 if ( mime.equals( "image/jpg" ) || mime.equals( "image/jpeg" ) || mime.equals( "image/bmp" )
537 || mime.equals( "image/tif" ) || mime.equals( "image/tiff" ) ) {
538 transparency = false;
539 }
540
541 // BGCOLOR
542 tmp = model.remove( "BGCOLOR" );
543 Color bgColor = Color.white;
544 if ( tmp != null ) {
545 bgColor = Color.decode( tmp );
546 }
547
548 // EXCEPTIONS
549 String exceptions = model.remove( "EXCEPTIONS" );
550
551 if ( exceptions == null ) {
552 if ( is130 ) {
553 exceptions = "XML";
554 } else {
555 exceptions = "application/vnd.ogc.se_xml";
556 }
557 }
558
559 // TIME
560 Values time = null;
561 // TODO read time
562
563 // WFS
564 /*
565 * URL wFS_URL = null; if ((String)model.get( "WFS" ) != null) { wFS_URL = new
566 * URL((String)model.remove( "WFS" )); }
567 */
568
569 // ID
570 String id = model.remove( "ID" );
571 if ( id == null ) {
572 throw new InconsistentRequestException( "ID-value must be set" );
573 }
574
575 // VendorSpecificParameter; because all defined parameters has been
576 // removed
577 // from the model the vendorSpecificParameters are what left
578 Map<String, String> vendorSpecificParameter = new HashMap<String, String>();
579 for ( Object str : model.keySet() ) {
580 vendorSpecificParameter.put( str.toString(), model.get( str ).toString() );
581 }
582
583 LOG.exiting();
584 return new GetMap( version, id, ls, elevation, sampleDimension, format, width, height, srs,
585 boundingBox, transparency, bgColor, exceptions, time, sLD_URL, sld,
586 vendorSpecificParameter );
587 }
588
589 /**
590 * creates a <tt>GetMapRequest</tt> from its XML representation as defined in SLD 1.0.0
591 * specification
592 *
593 * <p>
594 * This method does not yet cope with 1.3.0.
595 * </p>
596 *
597 * @param id
598 * an unique id of the request
599 * @param doc
600 * the document tree
601 * @return an instance of <tt>GetMapRequest</tt>
602 * @throws XMLParsingException
603 * @throws InvalidSRSException
604 * @throws InconsistentRequestException
605 */
606 public static GetMap create( String id, Document doc )
607 throws XMLParsingException, InvalidSRSException,
608 InconsistentRequestException {
609 String PSLD = CommonNamespaces.SLD_PREFIX + ':';
610
611 Element root = doc.getDocumentElement();
612 NamespaceContext nsContext = CommonNamespaces.getNamespaceContext();
613 Element sldElem = (Element) XMLTools.getRequiredNode( root, PSLD + "StyledLayerDescriptor",
614 nsContext );
615 XMLFragment xml = new XMLFragment();
616 xml.setRootElement( sldElem );
617
618 StyledLayerDescriptor sld = SLDFactory.createSLD( xml );
619 String version = root.getAttribute( "version" );
620
621 boolean is130 = false;
622
623 if ( "1.3.0".compareTo( version ) <= 0 ) {
624 is130 = true;
625 }
626
627 Element bboxElem = (Element) XMLTools.getRequiredNode( root, PSLD + "BoundingBox",
628 nsContext );
629
630 Envelope bbox;
631 try {
632 bbox = GMLGeometryAdapter.wrapBox( bboxElem, null );
633 // check for consistency
634 if ( bbox.getMin().getX() >= bbox.getMax().getX() ) {
635 throw new InvalidFormatException( "minx must be lower than maxx" );
636 }
637
638 if ( bbox.getMin().getY() >= bbox.getMax().getY() ) {
639 throw new InvalidFormatException( "miny must be lower than maxy" );
640 }
641 } catch ( InvalidGMLException e ) {
642 LOG.logDebug( e.getLocalizedMessage(), e );
643 if ( bboxElem == null ) {
644 throw new InconsistentRequestException( Messages.getMessage( "WMS_NO_BOUNDINGBOX" ) );
645 }
646 if ( is130 ) {
647 throw new InvalidCRSException(
648 Messages.getMessage(
649 "WMS_UNKNOWN_CRS",
650 bboxElem.getAttribute( "srsName" ) ) );
651 }
652 throw new InvalidSRSException( Messages.getMessage( "WMS_UNKNOWN_CRS",
653 bboxElem.getAttribute( "srsName" ) ) );
654 } catch ( UnknownCRSException e ) {
655 LOG.logDebug( e.getLocalizedMessage(), e );
656 if ( bboxElem == null ) {
657 throw new InconsistentRequestException( Messages.getMessage( "WMS_NO_BOUNDINGBOX" ) );
658 }
659 if ( is130 ) {
660 throw new InvalidCRSException(
661 Messages.getMessage(
662 "WMS_UNKNOWN_CRS",
663 bboxElem.getAttribute( "srsName" ) ) );
664 }
665 throw new InvalidSRSException( Messages.getMessage( "WMS_UNKNOWN_CRS",
666 bboxElem.getAttribute( "srsName" ) ) );
667 }
668
669 String srs = bbox.getCoordinateSystem().getName().toString();
670
671 Element output = (Element) XMLTools.getRequiredNode( root, PSLD + "Output", nsContext );
672
673 boolean transparent = XMLTools.getNodeAsBoolean( output, PSLD + "Transparent", nsContext,
674 false );
675
676 int width = 0;
677 int height = 0;
678 try {
679 Element node = (Element) XMLTools.getRequiredNode( output, PSLD + "Size", nsContext );
680 width = XMLTools.getRequiredNodeAsInt( node, PSLD + "Width", nsContext );
681 height = XMLTools.getRequiredNodeAsInt( node, PSLD + "Height", nsContext );
682 } catch ( XMLParsingException e ) {
683 throw new InconsistentRequestException( Messages.getMessage( "WMS_REQUEST_SIZE" ) );
684 }
685
686 String exception = XMLTools.getNodeAsString( output, PSLD + "Exceptions", nsContext,
687 "application/vnd.ogc.se_xml" );
688 String sbgColor = XMLTools.getNodeAsString( output, PSLD + "BGColor", nsContext, "#FFFFFF" );
689 Color bgColor = Color.decode( sbgColor );
690
691 String format = XMLTools.getRequiredNodeAsString( output, PSLD + "Format", nsContext );
692 if ( format == null ) {
693 throw new InconsistentRequestException( "FORMAT-value must be set" );
694 }
695 try {
696 format = URLDecoder.decode( format, CharsetUtils.getSystemCharset() );
697 } catch ( UnsupportedEncodingException e1 ) {
698 e1.printStackTrace();
699 }
700 if ( !MimeTypeMapper.isKnownImageType( format ) ) {
701 throw new InvalidFormatException( format + " is not a valid image/result format" );
702 }
703
704 GetMap req = new GetMap( version, id, null, null, null, format, width, height, srs, bbox,
705 transparent, bgColor, exception, null, null, sld, null );
706
707 return req;
708 }
709
710 /**
711 * Creates a new GetMapRequest object.
712 *
713 * @param version
714 * @param id
715 * @param layers
716 * @param elevation
717 * @param sampleDimension
718 * @param format
719 * @param width
720 * @param height
721 * @param srs
722 * @param boundingBox
723 * @param transparency
724 * @param bGColor
725 * @param exceptions
726 * @param time
727 * @param sldURL
728 * @param sld
729 * @param vendorSpecificParameter
730 *
731 */
732 GetMap( String version, String id, Layer[] layers, Values elevation,
733 Map<String, Values> sampleDimension, String format, int width, int height, String srs,
734 Envelope boundingBox, boolean transparency, Color bGColor, String exceptions,
735 Values time, URL sldURL, StyledLayerDescriptor sld,
736 Map<String, String> vendorSpecificParameter ) {
737 super( version, id, vendorSpecificParameter );
738
739 if ( layers != null ) {
740 this.layers = Arrays.asList( layers );
741 } else {
742 this.layers = new ArrayList<Layer>();
743 }
744 this.sld = sld;
745 this.elevation = elevation;
746 this.sampleDimension = sampleDimension;
747 this.format = format;
748 this.width = width;
749 this.height = height;
750 this.srs = srs;
751 this.boundingBox = boundingBox;
752 this.transparency = transparency;
753 this.bGColor = bGColor;
754 this.exceptions = exceptions;
755 this.time = time;
756 this.sLD_URL = sldURL;
757 // setWFS_URL( wFS_URL );
758 }
759
760 /**
761 * The FORMAT parameter specifies the output format of the response to an operation.
762 * <p>
763 * </p>
764 * An OGC Web CapabilitiesService may offer only a subset of the formats known for that type of
765 * operation, but the server shall advertise in its Capabilities XML those formats it does
766 * support and shall accept requests for any format it advertises. A CapabilitiesService
767 * Instance may optionally offer a new format not previously offered by other instances, with
768 * the recognition that clients are not required to accept or process an unknown format. If a
769 * request contains a Format not offered by a particular server, the server shall throw a
770 * CapabilitiesService Exception (with code "InvalidFormat").
771 *
772 * @return the output format
773 */
774 public String getFormat() {
775 return format;
776 }
777
778 /**
779 * sets the format
780 *
781 * @param format
782 * the requested output-format
783 */
784 public void setFormat( String format ) {
785 this.format = format;
786 }
787
788 /**
789 * The required LAYERS parameter lists the map layer(s) to be returned by this GetMap request.
790 * The value of the LAYERS parameter is a comma-separated list of one or more valid layer names.
791 * Allowed layer names are the character data content of any <Layer><Name> element in the
792 * Capabilities XML.
793 * <p>
794 * </p>
795 * A WMS shall render the requested layers by drawing the leftmost in the list bottommost, the
796 * next one over that, and so on.
797 * <p>
798 * </p>
799 * Each layer is associated to a style. Styles are also is encoded as a comma- seperated list
800 * within the GetMap request.
801 * <p>
802 * </p>
803 * The required STYLES parameter lists the style in which each layer is to be rendered. There is
804 * a one-to-one correspondence between the values in the LAYERS parameter and the values in the
805 * STYLES parameter. Because of this layer-style combinations are returned coupled within an
806 * array of Layer- objects. Each map in the list of LAYERS is drawn using the corresponding
807 * style in the same position in the list of STYLES. Each style Name shall be one that was
808 * defined in the <Name> element of a <Style> element that is either directly contained within,
809 * or inherited by, the associated <Layer> element in Capabilities XML.
810 *
811 * @return The required LAYERS
812 */
813 public Layer[] getLayers() {
814 return layers.toArray( new Layer[layers.size()] );
815 }
816
817 /**
818 * adds the <Layer>
819 *
820 * @param layers
821 */
822 public void addLayers( Layer layers ) {
823 this.layers.add( layers );
824 }
825
826 /**
827 * sets the <Layer>
828 *
829 * @param layers
830 * a set of layer
831 */
832 public void setLayers( Layer[] layers ) {
833 this.layers.clear();
834
835 if ( layers != null ) {
836 for ( int i = 0; i < layers.length; i++ ) {
837 this.layers.add( layers[i] );
838 }
839 }
840 }
841
842 /**
843 * The required SRS parameter states which Spatial Reference System applies to the values in the
844 * BBOX parameter. The value of the SRS parameter shall be one of the values defined in the
845 * character data section of an <SRS> element defined or inherited by the requested layer. The
846 * same SRS applies to all layers in a single request.
847 * <p>
848 * </p>
849 * If the WMS server has declared SRS=NONE for a Layer, as discussed in the Basic
850 * CapabilitiesService Elements section, then the Layer does not have a well-defined spatial
851 * reference system and should not be shown in conjunction with other layers. The Client shall
852 * specify SRS=NONE (case-insensitive) in the GetMap request and the Server may issue a
853 * CapabilitiesService Exception otherwise.
854 *
855 * @return the spatial reference system
856 */
857 public String getSrs() {
858 return srs;
859 }
860
861 /**
862 * sets the srs
863 *
864 * @param srs
865 * the spatial reference system
866 */
867 public void setSrs( String srs ) {
868 this.srs = srs;
869 }
870
871 /**
872 * The required BBOX parameter allows a Client to request a particular Bounding Box. Bounding
873 * Boxes are defined in the Basic CapabilitiesService Elements section. The value of the BBOX
874 * parameter in a GetMap request is a list of comma-separated numbers of the form
875 * "minx,miny,maxx,maxy".
876 * <p>
877 * </p>
878 * If the WMS server has declared that a Layer is not subsettable then the Client shall specify
879 * exactly the declared Bounding Box values in the GetMap request and the Server may issue a
880 * CapabilitiesService Exception otherwise.
881 *
882 * @return the bounding box
883 */
884 public Envelope getBoundingBox() {
885 return boundingBox;
886 }
887
888 /**
889 * WIDTH specifies the number of pixels to be used between the minimum and maximum X values
890 * (inclusive) in the BBOX parameter. The returned picture, regardless of its return format,
891 * shall have exactly the specified width and height in pixels. In the case where the aspect
892 * ratio of the BBOX and the ratio width/height are different, the WMS shall stretch the
893 * returned map so that the resulting pixels could themselves be rendered in the aspect ratio of
894 * the BBOX. In other words, it should be possible using this definition to request a map for a
895 * device whose output pixels are themselves non-square, or to stretch a map into an image area
896 * of a different aspect ratio.
897 *
898 * @return the width
899 */
900 public int getWidth() {
901 return width;
902 }
903
904 /**
905 * HEIGHT specifies the number of pixels between the minimum and maximum Y values. The returned
906 * picture, regardless of its return format, shall have exactly the specified width and height
907 * in pixels. In the case where the aspect ratio of the BBOX and the ratio width/height are
908 * different, the WMS shall stretch the returned map so that the resulting pixels could
909 * themselves be rendered in the aspect ratio of the BBOX. In other words, it should be possible
910 * using this definition to request a map for a device whose output pixels are themselves
911 * non-square, or to stretch a map into an image area of a different aspect ratio.
912 *
913 * @return the height
914 */
915 public int getHeight() {
916 return height;
917 }
918
919 /**
920 * The optional TRANSPARENT parameter specifies whether the map background is to be made
921 * transparent or not. TRANSPARENT can take on two values, "TRUE" or "FALSE". The default value
922 * is FALSE if this parameter is absent from the request.
923 * <p>
924 * </p>
925 * The ability to return pictures drawn with transparent pixels allows results of different Map
926 * requests to be overlaid, producing a composite map. It is strongly recommended that every WMS
927 * offer a format that provides transparency for layers which could sensibly be overlaid above
928 * others.
929 *
930 * @return the transparency setting
931 */
932 public boolean getTransparency() {
933 return transparency;
934 }
935
936 /**
937 * The optional BGCOLOR parameter specifies the color to be used as the background of the map.
938 * The general format of BGCOLOR is a hexadecimal encoding of an RGB value where two hexadecimal
939 * characters are used for each of Red, Green, and Blue color values. The values can range
940 * between 00 and FF for each (0 and 255, base 10). The format is 0xRRGGBB; either upper or
941 * lower case characters are allowed for RR, GG, and BB values. The "0x" prefix shall have a
942 * lower case 'x'. The default value is 0xFFFFFF (corresponding to the color white) if this
943 * parameter is absent from the request.
944 *
945 * @return the background color
946 */
947 public Color getBGColor() {
948 return bGColor;
949 }
950
951 /**
952 * The optional EXCEPTIONS parameter states the manner in which errors are to be reported to the
953 * client. The default value is application/vnd.ogc.se_xml if this parameter is absent from the
954 * request.
955 * <p>
956 * </p>
957 * A Web Map CapabilitiesService shall offer one or more of the following exception reporting
958 * formats by listing them in separate <Format> elements inside the <Exceptions> element of its
959 * Capabilities XML. The entire MIME type string in <Format> is used as the value of the
960 * EXCEPTIONS parameter. The first of these formats is required to be offered by every WMS; the
961 * others are optional.
962 *
963 * @return the exceptions parameter
964 */
965 public String getExceptions() {
966 return exceptions;
967 }
968
969 /**
970 * This specification is based on [ISO 8601:1988(E)]; it extends ISO 8601 in the following ways:
971 * <UL>
972 * <li>It defines a syntax for expressing the start, end and periodicity of a data collection.
973 * <li>It defines terms to represent the 7 days of the week.
974 * <li>It allows years before 0001 AD.
975 * <li>It allows times in the distant geologic past (thousands, millions or billions of years
976 * before present).
977 * </UL>
978 *
979 * @return the time setting
980 */
981 public Values getTime() {
982 return time;
983 }
984
985 /**
986 * Some geospatial information may be available at multiple elevations. An OWS may announce
987 * available elevations in its Capabilities XML, and some operations include a parameter for
988 * requesting a particular elevation. A single elevation value is an integer or real number
989 * whose units are declared by naming an EPSG datum. When providing elevation information,
990 * Servers should declare a default value in Capabilities XML unless there is compelling reason
991 * to behave otherwise, and Servers shall respond with the default value if one has been
992 * declared and the Client request does not include a value.
993 *
994 * @return the elevation
995 */
996 public Values getElevation() {
997 return elevation;
998 }
999
1000 /**
1001 * Some geospatial information may be available at other dimensions (for example, satellite
1002 * images in different wavelength bands). The dimensions other than the four space-time
1003 * dimensions are referred to as "sample dimensions". An OWS may announce available sample
1004 * dimensions in its Capabilities XML, and some operations include a mechanism for including
1005 * dimensional parameters. Each sample dimension has a Name and one or more valid values.
1006 *
1007 * @return the map
1008 */
1009 public Map<String, Values> getSampleDimension() {
1010 return sampleDimension;
1011 }
1012
1013 /**
1014 * @return the URL of Styled Layer Descriptor (as defined in SLD Specification). This parameter
1015 * is optional. If no sld URL is defined <tt>null</tt> will be returned.
1016 */
1017 public URL getSLD_URL() {
1018 return sLD_URL;
1019 }
1020
1021 /**
1022 * @return the URL of Web Feature CapabilitiesService providing features to be symbolized using
1023 * SLD. This parameter is optional. If no WFS URL is defined <tt>null</tt> will be
1024 * returned.
1025 */
1026 public URL getWFS_URL() {
1027 return wFS_URL;
1028 }
1029
1030 /**
1031 * @return the SLD the request is made of. This implies that a 'simple' HTTP GET-Request will be
1032 * transformed into a valid SLD. This is mandatory within a JaGo WMS.
1033 * <p>
1034 * </p>
1035 * This mean even if a GetMap request is send using the HTTP GET method, an implementing
1036 * class has to map the request to a SLD data structure.
1037 */
1038 public StyledLayerDescriptor getStyledLayerDescriptor() {
1039 return sld;
1040 }
1041
1042 /**
1043 * @return the parameter of a HTTP GET request.
1044 *
1045 */
1046 @Override
1047 public String getRequestParameter()
1048 throws OGCWebServiceException {
1049
1050 // indicates if the request parameters are decoded as SLD. deegree won't
1051 // perform SLD requests through HTTP GET
1052 if ( boundingBox == null ) {
1053 throw new OGCWebServiceException( "Operations can't be expressed as HTTP GET request " );
1054 }
1055
1056 StringBuffer sb = new StringBuffer();
1057
1058 if ( getVersion().compareTo( "1.0.0" ) <= 0 ) {
1059 sb.append( "VERSION=" ).append( getVersion() ).append( "&REQUEST=map" );
1060 String f = StringTools.replace( getFormat(), "image/", "", false );
1061 sb.append( "&FORMAT=" );
1062 try {
1063 sb.append( URLEncoder.encode( f, CharsetUtils.getSystemCharset() ) );
1064 } catch ( UnsupportedEncodingException e ) {
1065 // system encoding should be supported...
1066 }
1067 } else {
1068 sb.append( "&VERSION=" ).append( getVersion() ).append( "&REQUEST=GetMap" );
1069 sb.append( "&FORMAT=" );
1070 try {
1071 sb.append( URLEncoder.encode( getFormat(), CharsetUtils.getSystemCharset() ) );
1072 } catch ( UnsupportedEncodingException e ) {
1073 // system encoding should be supported...
1074 }
1075 }
1076
1077 sb.append( "&TRANSPARENT=" ).append( Boolean.toString( getTransparency() ).toUpperCase() );
1078 sb.append( "&WIDTH=" ).append( getWidth() );
1079 sb.append( "&HEIGHT=" ).append( getHeight() );
1080 sb.append( "&EXCEPTIONS=" ).append( getExceptions() );
1081 sb.append( "&BGCOLOR=" ).append( ColorUtils.toHexCode( "0x", bGColor ) );
1082
1083 if ( "1.3.0".compareTo( getVersion() ) <= 0 ) {
1084 sb.append( "&BBOX=" ).append( boundingBox.getMin().getY() );
1085 sb.append( ',' ).append( boundingBox.getMin().getX() );
1086 sb.append( ',' ).append( boundingBox.getMax().getY() );
1087 sb.append( ',' ).append( boundingBox.getMax().getX() );
1088 } else {
1089 sb.append( "&BBOX=" ).append( boundingBox.getMin().getX() );
1090 sb.append( ',' ).append( boundingBox.getMin().getY() );
1091 sb.append( ',' ).append( boundingBox.getMax().getX() );
1092 sb.append( ',' ).append( boundingBox.getMax().getY() );
1093 }
1094
1095 Layer[] layers = getLayers();
1096 StringBuffer l = new StringBuffer( 500 );
1097 StringBuffer s = new StringBuffer( 500 );
1098
1099 if ( sLD_URL == null ) {
1100 for ( int i = 0; i < layers.length; i++ ) {
1101 try {
1102 l.append( URLEncoder.encode( layers[i].getName(),
1103 CharsetUtils.getSystemCharset() ) );
1104 l.append( ',' );
1105 if ( !layers[i].getStyleName().equals( "$DEFAULT" ) ) {
1106 s.append( URLEncoder.encode( layers[i].getStyleName(),
1107 CharsetUtils.getSystemCharset() ) );
1108 }
1109 s.append( ',' );
1110 } catch ( Exception e ) {
1111 throw new OGCWebServiceException( e.toString() );
1112 }
1113 }
1114
1115 if ( l.length() != 0 ) {
1116 sb.append( "&LAYERS=" ).append( l.substring( 0, l.length() - 1 ) );
1117 }
1118
1119 if ( s.length() != 0 ) {
1120 sb.append( "&STYLES=" ).append( s.substring( 0, s.length() - 1 ) );
1121 }
1122 } else if ( sLD_URL != null ) {
1123 sb.append( "&SLD=" ).append( NetWorker.url2String( sLD_URL ) );
1124 } else if ( sld != null ) {
1125 String tmp = ( (Marshallable) sld ).exportAsXML();
1126 try {
1127 tmp = URLEncoder.encode( tmp, CharsetUtils.getSystemCharset() );
1128 } catch ( Exception e ) {
1129 throw new OGCWebServiceException( e.toString() );
1130 }
1131 sb.append( "&SLD_BODY=" ).append( tmp );
1132 }
1133
1134 if ( "1.3.0".compareTo( getVersion() ) <= 0 ) {
1135 sb.append( "&CRS=" ).append( getSrs() );
1136 } else {
1137 sb.append( "&SRS=" ).append( getSrs() );
1138 }
1139
1140 // TODO
1141 // append time, elevation and sampleDimensions
1142
1143 if ( getVendorSpecificParameters() != null ) {
1144 Iterator<String> iterator = getVendorSpecificParameters().keySet().iterator();
1145 while ( iterator.hasNext() ) {
1146 String key = iterator.next();
1147 String value = getVendorSpecificParameters().get( key );
1148 try {
1149 value = URLEncoder.encode( value, CharsetUtils.getSystemCharset() );
1150 } catch ( UnsupportedEncodingException e ) {
1151 // system encoding should be supported...
1152 }
1153 sb.append( '&' ).append( key ).append( '=' ).append( value );
1154 }
1155 }
1156
1157 return sb.toString();
1158 }
1159
1160 @Override
1161 public String toString() {
1162 String s = "An unknown " + this.getClass().getName() + " request";
1163 try {
1164 s = getRequestParameter();
1165 } catch ( OGCWebServiceException e ) {
1166 // in that case, we just don't have the parameters...
1167 }
1168 return s;
1169 }
1170
1171 /**
1172 * creates a Layer object beacuse of the inner class construct.
1173 *
1174 * @param name
1175 * the name of the layer
1176 * @param style
1177 * the corresponding style of the layer
1178 * @return Layer a layer object constaining name and style
1179 */
1180 public static Layer createLayer( String name, String style ) {
1181 return new Layer( name, style );
1182 }
1183
1184 /**
1185 * A Layer object. It contains the name of the layer and the corresponding style.
1186 *
1187 * @version $Revision: 7933 $
1188 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
1189 */
1190 public static class Layer implements Serializable {
1191
1192 private static final long serialVersionUID = -98575941104285931L;
1193
1194 private String name = null;
1195
1196 private String styleName = null;
1197
1198 /**
1199 * constructor initializing the class with the <Layer>
1200 *
1201 * @param name
1202 * @param styleName
1203 */
1204 public Layer( String name, String styleName ) {
1205 this.name = name;
1206 this.styleName = styleName;
1207 }
1208
1209 /**
1210 * @return the <Name>
1211 */
1212 public String getName() {
1213 return name;
1214 }
1215
1216 /**
1217 * @return the <StyleName>
1218 */
1219 public String getStyleName() {
1220 return styleName;
1221 }
1222
1223 }
1224
1225 }