001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/ogcwebservices/wmps/operation/PrintMap.java $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005       Department of Geography, University of Bonn
006     and
007       lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035    ----------------------------------------------------------------------------*/
036    package org.deegree.ogcwebservices.wmps.operation;
037    
038    import java.awt.Color;
039    import java.io.Serializable;
040    import java.io.UnsupportedEncodingException;
041    import java.net.URLDecoder;
042    import java.sql.Timestamp;
043    import java.util.ArrayList;
044    import java.util.Arrays;
045    import java.util.HashMap;
046    import java.util.List;
047    import java.util.Map;
048    import java.util.StringTokenizer;
049    
050    import org.deegree.framework.log.ILogger;
051    import org.deegree.framework.log.LoggerFactory;
052    import org.deegree.framework.util.CharsetUtils;
053    import org.deegree.framework.util.StringTools;
054    import org.deegree.framework.xml.NamespaceContext;
055    import org.deegree.framework.xml.XMLParsingException;
056    import org.deegree.framework.xml.XMLTools;
057    import org.deegree.model.crs.CRSFactory;
058    import org.deegree.model.crs.CoordinateSystem;
059    import org.deegree.model.crs.UnknownCRSException;
060    import org.deegree.model.spatialschema.Envelope;
061    import org.deegree.model.spatialschema.GMLGeometryAdapter;
062    import org.deegree.model.spatialschema.GeometryException;
063    import org.deegree.model.spatialschema.GeometryFactory;
064    import org.deegree.model.spatialschema.Point;
065    import org.deegree.ogcbase.CommonNamespaces;
066    import org.deegree.ogcbase.InvalidGMLException;
067    import org.deegree.ogcwebservices.InconsistentRequestException;
068    import org.deegree.ogcwebservices.wms.InvalidFormatException;
069    import org.deegree.ogcwebservices.wms.operation.GetMap;
070    import org.deegree.ogcwebservices.wms.operation.GetMap.Layer;
071    import org.w3c.dom.Element;
072    import org.w3c.dom.Node;
073    
074    /**
075     * This interface describes the access to the parameters of a PrintMap request. It is expected that
076     * there are two kinds of request. The first is the 'normal' HTTP GET request with name-value-pair
077     * enconding and the second is a HTTP POST request containing a SLD. It is possible to access the
078     * values of a HTTP GET request throught its bean accessor methods. The request shall be mapped to a
079     * SLD data structure, accessible using the <code>getSLD()</code> method.
080     *
081     * @author <a href="mailto:deshmukh@lat-lon.de">Anup Deshmukh</a>
082     * @version 2.0
083     */
084    public class PrintMap extends WMPSRequestBase implements Serializable {
085    
086        private static final long serialVersionUID = 6898492018448337645L;
087    
088        private static final ILogger LOG = LoggerFactory.getLogger( PrintMap.class );
089    
090        private static final NamespaceContext nsContext = CommonNamespaces.getNamespaceContext();
091    
092        private List<Layer> layers;
093    
094        private String srs;
095    
096        private Envelope boundingBox;
097    
098        private Point center;
099    
100        private int scaleDenominator = -1;
101    
102        private boolean transparent;
103    
104        private Color bgColor;
105    
106        private String title;
107    
108        private String copyright;
109    
110        private boolean legend;
111    
112        private boolean scaleBar;
113    
114        private String note;
115    
116        private String template;
117    
118        private String emailaddress;
119    
120        private Timestamp timestamp;
121    
122        private TextArea[] textAreas;
123    
124        /**
125         * Create a new PrintMap instance.
126         *
127         * @param id
128         * @param version
129         * @param layers
130         * @param srs
131         * @param boundingBox
132         * @param center
133         * @param scaleDenominator
134         * @param transparent
135         * @param bgColor
136         * @param title
137         * @param copyright
138         * @param legend
139         * @param scaleBar
140         * @param note
141         * @param template
142         * @param emailaddress
143         * @param timestamp
144         * @param textAreas
145         * @param vendorSpecificParameter
146         */
147        PrintMap( String id, String version, Layer[] layers, String srs, Envelope boundingBox, Point center,
148                  int scaleDenominator, boolean transparent, Color bgColor, String title, String copyright, boolean legend,
149                  boolean scaleBar, String note, String template, String emailaddress, Timestamp timestamp,
150                  TextArea[] textAreas, Map<String, String> vendorSpecificParameter ) {
151    
152            super( version, id, vendorSpecificParameter );
153    
154            setLayers( layers );
155            this.srs = srs;
156            this.boundingBox = boundingBox;
157            this.center = center;
158            this.scaleDenominator = scaleDenominator;
159            this.transparent = transparent;
160            this.bgColor = bgColor;
161            this.title = title;
162            this.copyright = copyright;
163            this.legend = legend;
164            this.scaleBar = scaleBar;
165            this.note = note;
166            this.template = template;
167            this.emailaddress = emailaddress;
168            setTimestamp( timestamp );
169            this.textAreas = textAreas;
170        }
171    
172        /**
173         * Set the time stamp.
174         *
175         * @param timestamp
176         */
177        private void setTimestamp( Timestamp timestamp ) {
178    
179            if ( timestamp != null ) {
180                this.timestamp = timestamp;
181            } else {
182                this.timestamp = setCurrentTime();
183            }
184        }
185    
186        /**
187         * Sets the Current System Time where the request was recieved.
188         *
189         * @return Date
190         */
191        private static Timestamp setCurrentTime() {
192            long now = System.currentTimeMillis();
193            return new Timestamp( now );
194        }
195    
196        /**
197         * The required LAYERS parameter lists the map layer(s) to be returned by this PrintMapRequest
198         * request. The value of the LAYERS parameter is a comma-separated list of one or more valid
199         * layer names. Allowed layer names are the character data content of any <Layer><Name> element
200         * in the Capabilities XML.
201         * <p>
202         * </p>
203         * A WMS shall render the requested layers by drawing the leftmost in the list bottommost, the
204         * next one over that, and so on.
205         * <p>
206         * </p>
207         * Each layer is associated to a style. Styles are also is encoded as a comma- seperated list
208         * within the PrintMapRequest request.
209         * <p>
210         * </p>
211         * The required STYLES parameter lists the style in which each layer is to be rendered. There is
212         * a one-to-one correspondence between the values in the LAYERS parameter and the values in the
213         * STYLES parameter. Because of this layer-style combinations are returned coupled within an
214         * array of Layer- objects. Each map in the list of LAYERS is drawn using the corresponding
215         * style in the same position in the list of STYLES. Each style Name shall be one that was
216         * defined in the <Name> element of a <Style> element that is either directly contained within,
217         * or inherited by, the associated <Layer> element in Capabilities XML.
218         *
219         * @return The required LAYERS
220         */
221        public Layer[] getLayers() {
222            return this.layers.toArray( new Layer[this.layers.size()] );
223        }
224    
225        /**
226         * adds the <Layer>
227         *
228         * @param layer
229         */
230        protected void addLayers( Layer layer ) {
231            this.layers.add( layer );
232        }
233    
234        /**
235         * sets the <Layer>
236         *
237         * @param layers
238         *            a set of layer
239         */
240        private void setLayers( Layer[] layers ) {
241            this.layers = new ArrayList<Layer>( layers.length );
242            this.layers.clear();
243            if ( layers != null ) {
244                this.layers = Arrays.asList( layers );
245            }
246        }
247    
248        /**
249         * creates a <tt>PrintMapRequest</tt> from its XML representation as defined in the
250         * specification.
251         *
252         * @param root
253         *            Element
254         * @return an instance of <tt>PrintMapRequest</tt>
255         * @throws InconsistentRequestException
256         * @throws XMLParsingException
257         */
258        public static PrintMap create( Element root )
259                                throws InconsistentRequestException, XMLParsingException {
260    
261            LOG.logInfo( "Validating PrintMapRequest request." );
262            // Validation
263            if ( !root.getLocalName().equals( "PrintMap" ) ) {
264                StringBuffer sb = new StringBuffer( 50 );
265                sb.append( "Unable to create a 'PrintMapRequest' operation for node '" );
266                sb.append( root.getLocalName() + "'. Please check the node to be parsed." );
267                throw new InconsistentRequestException( sb.toString() );
268            }
269            // VERSION
270            String version;
271            try {
272                version = XMLTools.getRequiredAttrValue( "version", null, root );
273            } catch ( XMLParsingException e ) {
274                throw new XMLParsingException( "Error parsing required attribute parameter 'Version'. " + e.getMessage() );
275            }
276    
277            // LAYERS & STYLES
278            List<Layer> layerList = new ArrayList<Layer>();
279            List layerElements = null;
280            try {
281                layerElements = XMLTools.getNodes( root, "deegreewmps:Layers", nsContext );
282            } catch ( XMLParsingException e ) {
283                throw new XMLParsingException( "Error parsing required parameter 'Layer(s)'. " + e.getMessage() );
284            }
285    
286            for ( int i = 0; i < layerElements.size(); i++ ) {
287                Element layer = (Element) layerElements.get( i );
288                List namedLayers = null;
289                try {
290                    namedLayers = XMLTools.getNodes( layer, "sld:NamedLayer", nsContext );
291                    layerList = createLayers( namedLayers, layerList );
292                } catch ( XMLParsingException e ) {
293                    throw new XMLParsingException( "Error parsing parameter 'NamedLayer'." );
294                }
295                List userLayers = null;
296                try {
297                    userLayers = XMLTools.getNodes( layer, "sld:UserLayer", nsContext );
298                    layerList = createLayers( userLayers, layerList );
299                } catch ( XMLParsingException e ) {
300                    throw new XMLParsingException( "Error parsing  parameter 'UserLayer'." );
301                }
302                if ( ( layerList == null ) || ( layerList.size() == 0 ) ) {
303                    throw new InconsistentRequestException( "Atleast one 'NamedLayer' or one "
304                                                            + "'UserLayer' has to be specified." );
305                }
306            }
307            Layer[] layers = layerList.toArray( new Layer[layerList.size()] );
308    
309            // BBOX
310            Element bbox = null;
311            String srsName = null;
312            Envelope boundingBox = null;
313            try {
314                bbox = (Element) XMLTools.getNode( root, "gml:Envelope", nsContext );
315                if ( bbox != null ) {
316                    try {
317                        srsName = XMLTools.getAttrValue( bbox, null, "srsName", null );
318                        boundingBox = GMLGeometryAdapter.wrapBox( bbox, null );
319                    } catch ( InvalidGMLException e ) {
320                        throw new XMLParsingException( "Error creating a bounding box for the " + "'BBOX' parameter." );
321                    } catch ( UnknownCRSException e ) {
322                        throw new InconsistentRequestException( e.getMessage() );
323                    }
324                }
325            } catch ( XMLParsingException e ) {
326                throw new XMLParsingException( "Error parsing optional parameter 'BoundingBox'. " + e.getMessage() );
327            }
328    
329            // Center
330            Point center = null;
331            try {
332                Element centerElement = (Element) XMLTools.getNode( root, "deegreewmps:Center", nsContext );
333                if ( centerElement != null ) {
334                    try {
335                        srsName = XMLTools.getAttrValue( centerElement, null, "srsName", null );
336                        center = (Point) GMLGeometryAdapter.wrap( centerElement, null );
337                    } catch ( GeometryException e ) {
338                        throw new XMLParsingException( "Error creating a Point for the 'Center' " + "parameter. "
339                                                       + e.getMessage() );
340                    }
341                }
342            } catch ( XMLParsingException e ) {
343                throw new XMLParsingException( "Error parsing optional parameter 'Center'. " + e.getMessage() );
344            }
345    
346            // ScaleDenominator
347            int scaleDenominator = -1;
348            try {
349                scaleDenominator = XMLTools.getNodeAsInt( root, "deegreewmps:ScaleDenominator", nsContext, -1 );
350            } catch ( XMLParsingException e ) {
351                throw new XMLParsingException( "Error parsing optional parameter 'Center'. " + e.getMessage() );
352            }
353    
354            if ( boundingBox == null ) {
355                if ( center == null ) {
356                    throw new InconsistentRequestException( "Both 'BoundingBox' and 'Center' are not specified. Either of "
357                                                            + "the two must be set. Both values cannot be null" );
358                }
359                if ( scaleDenominator == -1 ) {
360                    throw new InconsistentRequestException( "Scale Denominator must be specified if the Bounding Box has "
361                                                            + "not been specified. Please check the 'SCALEDENOMINATOR' "
362                                                            + "parameter." );
363                }
364            }
365    
366            // TRANSPARENT
367            boolean transparent = XMLTools.getNodeAsBoolean( root, "deegreewmps:Transparent", nsContext, false );
368    
369            // BGCOLOR
370            Color bgColor = null;
371            String colorstring = XMLTools.getNodeAsString( root, "deegreewmps:BGColor", nsContext, null );
372            if ( colorstring == null ) {
373                bgColor = Color.WHITE;
374            } else {
375                try {
376                    bgColor = Color.decode( colorstring );
377                } catch ( Exception e ) {
378                    throw new InconsistentRequestException( "Error parsing 'BGCOLOR' parameter. The color '" + colorstring
379                                                            + "' is not a hexadecimal definition of a valid color. "
380                                                            + e.getMessage() );
381                }
382            }
383    
384            boolean legend = XMLTools.getNodeAsBoolean( root, "deegreewmps:Legend", nsContext, false );
385    
386            boolean scaleBar = XMLTools.getNodeAsBoolean( root, "deegreewmps:ScaleBar", nsContext, false );
387    
388            String template = XMLTools.getNodeAsString( root, "deegreewmps:Template", nsContext, "default" );
389    
390            String emailAdd = XMLTools.getNodeAsString( root, "deegreewmps:EMailAddress", nsContext, null );
391    
392            List list = XMLTools.getNodes( root, "deegreewmps:TextAreas/deegreewmps:TextArea", nsContext );
393    
394            TextArea[] textAreas = null;
395            String title = null;
396            String copyright = null;
397            String note = null;
398            if ( list != null ) {
399                textAreas = new TextArea[list.size()];
400                for ( int i = 0; i < list.size(); i++ ) {
401                    Node textArea = (Node) list.get( i );
402                    String name = XMLTools.getRequiredNodeAsString( textArea, "deegreewmps:Name", nsContext );
403                    String value = XMLTools.getRequiredNodeAsString( textArea, "deegreewmps:Text", nsContext );
404                    if ( name.equalsIgnoreCase( "TITLE" ) ) {
405                        title = value;
406                    }
407                    if ( name.equalsIgnoreCase( "COPYRIGHT" ) ) {
408                        copyright = value;
409                    }
410                    if ( name.equalsIgnoreCase( "NOTE" ) ) {
411                        note = value;
412                    }
413                    textAreas[i] = new TextArea( name, value );
414    
415                }
416            }
417            Map<String, String> vendorSpecificParameter = getVendorSpecificParameter( root );
418    
419            String id = "" + System.currentTimeMillis();
420    
421            LOG.logInfo( "Created PrintMap request request with id '" + id + "'." );
422    
423            return new PrintMap( id, version, layers, srsName, boundingBox, center, scaleDenominator, transparent, bgColor,
424                                 title, copyright, legend, scaleBar, note, template, emailAdd, setCurrentTime(), textAreas,
425                                 vendorSpecificParameter );
426    
427        }
428    
429        /**
430         * Returns the vendorspecific parameters as a map. Currently handles only the 'session id'
431         *
432         * @param root
433         * @return Map
434         */
435        private static Map<String, String> getVendorSpecificParameter( Element root ) {
436    
437            Map<String, String> vendorspecific = new HashMap<String, String>();
438    
439            String sessionID = XMLTools.getAttrValue( root, null, "sessionID", null );
440            if ( sessionID != null ) {
441                LOG.logInfo( "vendor specific parameter 'sessionid' retrieved" );
442                vendorspecific.put( "SESSIONID", sessionID );
443            }
444    
445            return vendorspecific;
446        }
447    
448        /**
449         * Create Layer objects for each of namedLayer and userLayer.
450         *
451         * @param layerNodes
452         * @param layers
453         * @return List list of layer objects
454         * @throws XMLParsingException
455         */
456        private static List<Layer> createLayers( List layerNodes, List<Layer> layers )
457                                throws XMLParsingException {
458    
459            if ( layerNodes != null ) {
460                for ( int i = 0; i < layerNodes.size(); i++ ) {
461                    Node layerNode = (Node) layerNodes.get( i );
462                    try {
463                        String layerName = XMLTools.getRequiredNodeAsString( layerNode, "sld:Name", nsContext );
464                        String styleName = XMLTools.getRequiredNodeAsString( layerNode, "sld:NamedStyle/sld:Name",
465                                                                             nsContext );
466                        layers.add( new Layer( layerName, styleName ) );
467                    } catch ( XMLParsingException e ) {
468                        throw new XMLParsingException( "Error creating a Layer from the Node '" + layerNode.getNodeName()
469                                                       + "'. " + e.getMessage() );
470                    }
471                }
472            }
473    
474            return layers;
475        }
476    
477        /**
478         * creates a <tt>PrintMapRequest</tt> from a <tt>HashMap</tt> that contains the request
479         * parameters as key-value-pairs. Keys are expected to be in upper case notation.
480         *
481         * @param model
482         *            <tt>HashMap</tt> containing the request parameters
483         * @return an instance of <tt>PrinttMapRequest</tt>
484         * @throws InconsistentRequestException
485         */
486        public static PrintMap create( Map<String, String> model )
487                                throws InconsistentRequestException {
488    
489            retrieveRequestParameter( model );
490    
491            String version = retrieveVersionParameter( model );
492    
493            Layer[] layers = retrieveLayerAndStyleParameters( model );
494    
495            String srs = retrieveSRSParameter( model );
496    
497            Envelope boundingBox = null;
498            Point center = null;
499            try {
500                boundingBox = retrieveBBOXParameter( model, srs );
501                center = retrieveCenterParameter( model, srs );
502            } catch ( UnknownCRSException e ) {
503                throw new InconsistentRequestException( e.getMessage() );
504            }
505    
506            int scaleDenominator = retrieveScaleDenominatorParameter( model );
507    
508            if ( boundingBox == null ) {
509                if ( center == null ) {
510                    throw new InconsistentRequestException( "Both 'BoundingBox' and 'Center' are not specified. Either of "
511                                                            + "the 2 must be set.  Both values cannot be null" );
512                }
513                if ( scaleDenominator == -1 ) {
514                    throw new InconsistentRequestException( "Scale Denominator must be specified if the Bounding Box has "
515                                                            + "not been specified. Please check the 'SCALEDENOMINATOR' "
516                                                            + "parameter." );
517                }
518            }
519    
520            boolean transparent = retrieveTransparentParameter( model );
521    
522            Color bgColor = retrieveBGColorParameter( model );
523    
524            String title = retrieveTitleParameter( model );
525    
526            String copyRightNote = retrieveCopyrightParameter( model );
527    
528            boolean legend = retrieveLegendParameter( model );
529    
530            boolean scaleBar = retrieveScaleBarParameter( model );
531    
532            String note = retrieveNoteParameter( model );
533    
534            String template = retrieveTemplateParameter( model );
535    
536            String emailaddress = retrieveEmailParameter( model );
537    
538            TextArea[] textAreas = retrieveTextAreas( model );
539    
540            Map<String, String> vendorSpecificParameter = model;
541    
542            String id = "" + System.currentTimeMillis();
543    
544            return new PrintMap( id, version, layers, srs, boundingBox, center, scaleDenominator, transparent, bgColor,
545                                 title, copyRightNote, legend, scaleBar, note, template, emailaddress, setCurrentTime(),
546                                 textAreas, vendorSpecificParameter );
547    
548        }
549    
550        /**
551         * Retrieve the Text Areas to be displayed on the PDF output file. Extract the comma seperated
552         * list of name, value pairs. The name and value should be seperated with a ':'. E.g.->
553         * name:value,name:value,name:value
554         *
555         * @param model
556         * @return TextArea
557         */
558        private static TextArea[] retrieveTextAreas( Map model ) {
559    
560            List<TextArea> texts = new ArrayList<TextArea>();
561            if ( model.containsKey( "TEXTAREAS" ) ) {
562                String textstring = (String) model.remove( "TEXTAREAS" );
563                if ( textstring != null ) {
564                    String[] nameValue = StringTools.toArray( textstring, ",", true );
565                    if ( nameValue != null ) {
566                        for ( int i = 0; i < nameValue.length; i++ ) {
567                            String tmp = nameValue[i].trim();
568                            int idx = tmp.indexOf( ":" );
569                            if ( idx != -1 ) {
570                                String name = tmp.substring( 0, idx );
571                                String value = tmp.substring( idx + 1, tmp.length() );
572                                if ( ( name != null ) && ( value != null ) ) {
573                                    TextArea area = new TextArea( name.toUpperCase(), value );
574                                    texts.add( area );
575                                }
576                            }
577                        }
578                    }
579                }
580            }
581    
582            return texts.toArray( new TextArea[texts.size()] );
583    
584        }
585    
586        /**
587         * Parse 'Template' Parameter.
588         *
589         * @param model
590         * @return String
591         * @throws InconsistentRequestException
592         */
593        private static String retrieveTemplateParameter( Map model )
594                                throws InconsistentRequestException {
595    
596            String templatestring = null;
597            if ( model.containsKey( "TEMPLATE" ) ) {
598                templatestring = (String) model.remove( "TEMPLATE" );
599    
600            }
601            if ( templatestring == null ) {
602                throw new InconsistentRequestException( "No Template defined. A Template name has to be specified "
603                                                        + "along with the 'PrintMap' request." );
604            }
605    
606            return templatestring;
607        }
608    
609        /**
610         * Retrieve Email parameter
611         *
612         * @param model
613         * @return String
614         * @throws InconsistentRequestException
615         */
616        private static String retrieveEmailParameter( Map model )
617                                throws InconsistentRequestException {
618    
619            String email = null;
620            if ( model.containsKey( "EMAIL" ) ) {
621                email = (String) model.remove( "EMAIL" );
622            }
623    
624            if ( email == null ) {
625                throw new InconsistentRequestException( "EMail parameter must be set." );
626            }
627    
628            return email;
629        }
630    
631        /**
632         * Parse 'Note' Parameter.
633         *
634         * @param model
635         * @return String
636         */
637        private static String retrieveNoteParameter( Map model ) {
638    
639            String note = null;
640            if ( model.containsKey( "NOTE" ) ) {
641                note = (String) model.remove( "NOTE" );
642            }
643    
644            return note;
645        }
646    
647        /**
648         * Parse 'ScaleBar' Parameter.
649         *
650         * @param model
651         * @return boolean
652         * @throws InconsistentRequestException
653         */
654        private static boolean retrieveScaleBarParameter( Map model )
655                                throws InconsistentRequestException {
656    
657            boolean showScaleBar = false;
658            if ( model.containsKey( "SCALEBAR" ) ) {
659                String scaleBar = (String) model.remove( "SCALEBAR" );
660                if ( scaleBar == null ) {
661                    showScaleBar = false;
662                } else if ( scaleBar.equalsIgnoreCase( "True" ) ) {
663                    showScaleBar = true;
664                } else if ( scaleBar.equalsIgnoreCase( "False" ) ) {
665                    showScaleBar = false;
666                } else {
667                    throw new InconsistentRequestException( "The 'ScaleBar' parameter can only have 'True', 'False' "
668                                                            + "values. Here it is '" + scaleBar + "'." );
669                }
670            }
671    
672            return showScaleBar;
673        }
674    
675        /**
676         * Parse 'Legend' Parameter.
677         *
678         * @param model
679         * @return boolean
680         * @throws InconsistentRequestException
681         */
682        private static boolean retrieveLegendParameter( Map model )
683                                throws InconsistentRequestException {
684    
685            boolean showLegend = false;
686            if ( model.containsKey( "LEGEND" ) ) {
687                String legend = (String) model.remove( "LEGEND" );
688                if ( legend == null ) {
689                    showLegend = false;
690                } else if ( legend.equalsIgnoreCase( "True" ) ) {
691                    showLegend = true;
692                } else if ( legend.equalsIgnoreCase( "False" ) ) {
693                    showLegend = false;
694                } else {
695                    throw new InconsistentRequestException( "The 'Legend' parameter can only have 'True', 'False' values. "
696                                                            + "Here it is '" + legend + "'." );
697                }
698            }
699    
700            return showLegend;
701    
702        }
703    
704        /**
705         * Parse 'Copyright' Parameter.
706         *
707         * @param model
708         * @return String
709         */
710        private static String retrieveCopyrightParameter( Map model ) {
711    
712            String copyright = null;
713            if ( model.containsKey( "COPYRIGHT" ) ) {
714                copyright = (String) model.remove( "COPYRIGHT" );
715            }
716    
717            return copyright;
718        }
719    
720        /**
721         * Parse 'Title' Parameter.
722         *
723         * @param model
724         * @return String
725         */
726        private static String retrieveTitleParameter( Map model ) {
727    
728            String title = null;
729            if ( model.containsKey( "TITLE" ) ) {
730                title = (String) model.remove( "TITLE" );
731            }
732    
733            return title;
734        }
735    
736        /**
737         * Parse 'BGColor' Parameter.
738         *
739         * @param model
740         * @return Color
741         * @throws InconsistentRequestException
742         */
743        private static Color retrieveBGColorParameter( Map model )
744                                throws InconsistentRequestException {
745    
746            Color bgColor = Color.WHITE;
747            if ( model.containsKey( "BGCOLOR" ) ) {
748                String colorstring = (String) model.remove( "BGCOLOR" );
749                if ( ( colorstring != null ) || ( colorstring == "" ) ) {
750                    try {
751                        bgColor = Color.decode( colorstring );
752                    } catch ( Exception e ) {
753                        throw new InconsistentRequestException( "Error parsing 'BGCOLOR' parameter. The color '"
754                                                                + colorstring + "' is not a hexadecimal "
755                                                                + "definition of a valid color. " + e.getMessage() );
756                    }
757                }
758            }
759    
760            return bgColor;
761        }
762    
763        /**
764         * Parse 'Transparent' Parameter.
765         *
766         * @param model
767         * @return boolean
768         * @throws InconsistentRequestException
769         */
770        private static boolean retrieveTransparentParameter( Map model )
771                                throws InconsistentRequestException {
772    
773            boolean isTransparent = false;
774            if ( model.containsKey( "TRANSPARENT" ) ) {
775                String transparent = (String) model.remove( "TRANSPARENT" );
776                if ( transparent == null ) {
777                    isTransparent = false;
778                } else if ( transparent.equalsIgnoreCase( "True" ) ) {
779                    isTransparent = true;
780                } else if ( transparent.equalsIgnoreCase( "False" ) ) {
781                    isTransparent = false;
782                } else {
783                    throw new InconsistentRequestException( "The 'Transparent' parameter can only have "
784                                                            + "'True', 'False' values. Here it has '" + transparent + "'." );
785                }
786            }
787    
788            return isTransparent;
789        }
790    
791        /**
792         * Parse 'ScaleDenominator' Parameter.
793         *
794         * @param model
795         * @return String
796         */
797        private static int retrieveScaleDenominatorParameter( Map model ) {
798    
799            int scale = -1;
800            if ( model.containsKey( "SCALE" ) ) {
801                String value = (String) model.remove( "SCALE" );
802                if ( value != null ) {
803                    scale = Integer.parseInt( value );
804                }
805            }
806    
807            return scale;
808    
809        }
810    
811        /**
812         * Parse 'Center' Parameter and create a point.
813         *
814         * @param model
815         * @param srs
816         * @return Point to represent the x,y coordinates.
817         * @throws InconsistentRequestException
818         * @throws UnknownCRSException
819         */
820        private static Point retrieveCenterParameter( Map model, String srs )
821                                throws InconsistentRequestException, UnknownCRSException {
822    
823            Point center = null;
824            if ( model.containsKey( "CENTER" ) ) {
825                String centerstring = (String) model.remove( "CENTER" );
826    
827                String[] values = centerstring.split( "," );
828                if ( values.length != 2 ) {
829                    throw new InconsistentRequestException( "Centre should be defined as a Point with 'X' and 'Y' "
830                                                            + "values. The current length is '" + values.length
831                                                            + "'. It should " + "be '2'." );
832                }
833                double x = -1;
834                double y = -1;
835                try {
836                    x = Double.parseDouble( values[0] );
837                    y = Double.parseDouble( values[1] );
838                } catch ( Exception e ) {
839                    throw new InconsistentRequestException( "Error converting 'X','Y' coordinates in the request "
840                                                            + "parameter 'CENTER' to double. Please check the "
841                                                            + "values entered." );
842                }
843    
844                CoordinateSystem crs = CRSFactory.create( srs );
845                center = GeometryFactory.createPoint( x, y, crs );
846    
847            }
848    
849            return center;
850        }
851    
852        /**
853         * Parse 'Envelope' Parameter and create an envelope.
854         *
855         * @param model
856         * @param srs
857         * @return Envelope
858         * @throws InconsistentRequestException
859         * @throws InvalidFormatException
860         * @throws UnknownCRSException
861         */
862        private static Envelope retrieveBBOXParameter( Map model, String srs )
863                                throws InconsistentRequestException, InvalidFormatException, UnknownCRSException {
864    
865            Envelope bbox = null;
866    
867            if ( model.containsKey( "BBOX" ) ) {
868                String boxstring = (String) model.remove( "BBOX" );
869                StringTokenizer st = new StringTokenizer( boxstring, "," );
870                String s = st.nextToken().replace( ' ', '+' );
871                double minx = Double.parseDouble( s );
872                s = st.nextToken().replace( ' ', '+' );
873                double miny = Double.parseDouble( s );
874                s = st.nextToken().replace( ' ', '+' );
875                double maxx = Double.parseDouble( s );
876                s = st.nextToken().replace( ' ', '+' );
877                double maxy = Double.parseDouble( s );
878    
879                if ( minx >= maxx ) {
880                    throw new InvalidFormatException( "minx must be lesser than maxx" );
881                }
882    
883                if ( miny >= maxy ) {
884                    throw new InvalidFormatException( "miny must be lesser than maxy" );
885                }
886    
887                CoordinateSystem crs = CRSFactory.create( srs );
888                bbox = GeometryFactory.createEnvelope( minx, miny, maxx, maxy, crs );
889            }
890    
891            return bbox;
892        }
893    
894        /**
895         * Parse 'SRS' Parameter.
896         *
897         * @param model
898         * @return String
899         * @throws InconsistentRequestException
900         */
901        private static String retrieveSRSParameter( Map model )
902                                throws InconsistentRequestException {
903    
904            String srs = null;
905            if ( model.containsKey( "SRS" ) ) {
906                srs = (String) model.remove( "SRS" );
907            }
908            if ( srs == null ) {
909                throw new InconsistentRequestException( "SRS-value must be set" );
910            }
911    
912            return srs;
913        }
914    
915        /**
916         * Parse 'Layer' and 'Style' Parameter.
917         *
918         * @param model
919         * @return Layer[]
920         * @throws InconsistentRequestException
921         */
922        private static Layer[] retrieveLayerAndStyleParameters( Map model )
923                                throws InconsistentRequestException {
924    
925            String layersstring = null;
926            if ( model.containsKey( "LAYERS" ) ) {
927                layersstring = (String) model.remove( "LAYERS" );
928            }
929            String stylesstring = null;
930            if ( model.containsKey( "STYLES" ) ) {
931                stylesstring = (String) model.remove( "STYLES" );
932            }
933    
934            // normalize styles parameter
935            if ( ( stylesstring == null ) || stylesstring.trim().equals( "" ) ) {
936                stylesstring = "$DEFAULT";
937            }
938            if ( stylesstring.startsWith( "," ) ) {
939                stylesstring = "$DEFAULT" + stylesstring;
940            }
941            String tmp = ",$DEFAULT,";
942            stylesstring = StringTools.replace( stylesstring, ",,", tmp, true );
943            if ( stylesstring.endsWith( "," ) ) {
944                stylesstring = stylesstring + "$DEFAULT";
945            }
946            List<String> layers = new ArrayList<String>();
947            List<String> styles = new ArrayList<String>();
948            GetMap.Layer[] ls = null;
949            if ( ( layersstring != null ) && !layersstring.trim().equals( "" ) ) {
950                StringTokenizer st = new StringTokenizer( layersstring, "," );
951                int a = 0;
952                while ( st.hasMoreTokens() ) {
953                    String s = st.nextToken();
954                    layers.add( s );
955                }
956                st = new StringTokenizer( stylesstring, "," );
957                for ( int i = 0; i < layers.size(); i++ ) {
958                    styles.add( "$DEFAULT" );
959                }
960                a = 0;
961                while ( st.hasMoreTokens() ) {
962                    String s = st.nextToken();
963                    styles.set( a++, s );
964                }
965    
966                // At last, build up the Layer object
967                ls = new GetMap.Layer[layers.size()];
968    
969                for ( int i = 0; i < layers.size(); i++ ) {
970                    try {
971                        String l = URLDecoder.decode( layers.get( i ), CharsetUtils.getSystemCharset() );
972                        ls[i] = PrintMap.createLayer( l, styles.get( i ) );
973                    } catch ( UnsupportedEncodingException e2 ) {
974                        e2.printStackTrace();
975                    }
976                }
977            }
978    
979            if ( ( ls == null || ls.length == 0 ) ) {
980                throw new InconsistentRequestException( "No layers defined in PrintMapRequest request" );
981            }
982    
983            return ls;
984        }
985    
986        /**
987         * Parse the Request parameter to check if the request is actually a 'PrintMapRequest' request
988         *
989         * @param model
990         * @throws InconsistentRequestException
991         */
992        private static void retrieveRequestParameter( Map model )
993                                throws InconsistentRequestException {
994    
995            String request = null;
996            if ( model.containsKey( "REQUEST" ) ) {
997                request = (String) model.remove( "REQUEST" );
998            } else {
999                throw new InconsistentRequestException( "Unable to create a PrintMapRequest operation. "
1000                                                        + "The request parameter is missing." );
1001            }
1002            if ( request == null || !( request.equals( "PrintMap" ) ) ) {
1003                throw new InconsistentRequestException( "Unable to create a PrintMapRequest operation for request '"
1004                                                        + request + "'." );
1005            }
1006    
1007        }
1008    
1009        /**
1010         * Parse 'Version' Parameter.
1011         *
1012         * @param model
1013         * @return String version (default=1.0.0)
1014         */
1015        private static String retrieveVersionParameter( Map model ) {
1016    
1017            String version = null;
1018            if ( model.containsKey( "VERSION" ) ) {
1019                version = (String) model.remove( "VERSION" );
1020            }
1021            if ( version == null ) {
1022                /** default value set as per the WMPS draft specifications. */
1023                version = "1.0.0";
1024            }
1025    
1026            return version;
1027        }
1028    
1029        /**
1030         * Get TimeStamp
1031         *
1032         * @return TimeStamp
1033         */
1034        public Timestamp getTimestamp() {
1035            return this.timestamp;
1036        }
1037    
1038        /**
1039         * Get SRS
1040         *
1041         * @return String
1042         */
1043        public String getSRS() {
1044            return this.srs;
1045        }
1046    
1047        /**
1048         * Get bounding box
1049         *
1050         * @return Envelope maybe null
1051         */
1052        public Envelope getBBOX() {
1053            return this.boundingBox;
1054        }
1055    
1056        /**
1057         * Get center
1058         *
1059         * @return Point maybe null
1060         */
1061        public Point getCenter() {
1062            return this.center;
1063        }
1064    
1065        /**
1066         * Get ScaleDenominator
1067         *
1068         * @return String maybe null
1069         */
1070        public int getScaleDenominator() {
1071            return this.scaleDenominator;
1072        }
1073    
1074        /**
1075         * Get Transparency
1076         *
1077         * @return boolean
1078         */
1079        public boolean getTransparent() {
1080            return this.transparent;
1081        }
1082    
1083        /**
1084         * Get BGColor
1085         *
1086         * @return Color default is White.
1087         */
1088        public Color getBGColor() {
1089            return this.bgColor;
1090        }
1091    
1092        /**
1093         * Get Map Title
1094         *
1095         * @return String maybe null
1096         */
1097        public String getTitle() {
1098            return this.title;
1099        }
1100    
1101        /**
1102         * Get Copyright
1103         *
1104         * @return String maybe null
1105         */
1106        public String getCopyright() {
1107            return this.copyright;
1108        }
1109    
1110        /**
1111         * Get Legend
1112         *
1113         * @return boolean
1114         */
1115        public boolean getLegend() {
1116            return this.legend;
1117        }
1118    
1119        /**
1120         * Get Scale Bar
1121         *
1122         * @return boolean
1123         */
1124        public boolean getScaleBar() {
1125            return this.scaleBar;
1126        }
1127    
1128        /**
1129         * Get Note (extra descriptive text)
1130         *
1131         * @return String maybe null
1132         */
1133        public String getNote() {
1134            return this.note;
1135        }
1136    
1137        /**
1138         * Get Template
1139         *
1140         * @return String
1141         */
1142        public String getTemplate() {
1143            return this.template;
1144        }
1145    
1146        /**
1147         * Get Email Address
1148         *
1149         * @return String
1150         */
1151        public String getEmailAddress() {
1152            return this.emailaddress;
1153        }
1154    
1155        /**
1156         * Get Text Areas
1157         *
1158         * @return TextArea[]
1159         */
1160        public TextArea[] getTextAreas() {
1161            return this.textAreas;
1162        }
1163    
1164        /**
1165         * Retrieve ( if available ) the current text area identified by 'name' from the list of text
1166         * areas defined. May return null.
1167         *
1168         * @param name
1169         * @return TextArea
1170         */
1171        public TextArea getTextArea( String name ) {
1172    
1173            TextArea textArea = null;
1174            if ( this.textAreas != null && this.textAreas.length > 0 ) {
1175                for ( int i = 0; i < this.textAreas.length; i++ ) {
1176                    TextArea tmp = this.textAreas[i];
1177                    if ( tmp.getName().equalsIgnoreCase( name ) ) {
1178                        textArea = tmp;
1179                        break;
1180                    }
1181                }
1182            }
1183    
1184            return textArea;
1185        }
1186    
1187        /**
1188         * creates a Layer object beacuse of the inner class construct.
1189         *
1190         * @param name
1191         *            the name of the layer
1192         * @param style
1193         *            the corresponding style of the layer
1194         * @return Layer a layer object constaining name and style
1195         */
1196        public static Layer createLayer( String name, String style ) {
1197            return new Layer( name, style );
1198        }
1199    
1200        /**
1201         * Returns a new PrintMap instance.
1202         *
1203         * @param id
1204         * @param version
1205         * @param layers
1206         * @param srs
1207         * @param bbox
1208         * @param center
1209         * @param scaleDenominator
1210         * @param transparent
1211         * @param bgColor
1212         * @param title
1213         * @param copyright
1214         * @param legend
1215         * @param scaleBar
1216         * @param note
1217         * @param template
1218         * @param emailAddress
1219         * @param timestamp
1220         * @param textAreas
1221         * @param vendorSpecificParameters
1222         * @return PrintMap
1223         */
1224        public static PrintMap create( String id, String version, Layer[] layers, String srs, Envelope bbox, Point center,
1225                                       int scaleDenominator, boolean transparent, Color bgColor, String title,
1226                                       String copyright, boolean legend, boolean scaleBar, String note, String template,
1227                                       String emailAddress, Timestamp timestamp, TextArea[] textAreas,
1228                                       Map<String, String> vendorSpecificParameters ) {
1229    
1230            return new PrintMap( id, version, layers, srs, bbox, center, scaleDenominator, transparent, bgColor, title,
1231                                 copyright, legend, scaleBar, note, template, emailAddress, timestamp, textAreas,
1232                                 vendorSpecificParameters );
1233        }
1234    
1235        /**
1236         * Overwrite the toString() method to export the current request as a readable statement.
1237         * Currently only the id, version and layer names will be given out. TODO the rest
1238         *
1239         * @return String
1240         */
1241        @Override
1242        public String toString() {
1243    
1244            StringBuffer sb = new StringBuffer( 200 );
1245            sb.append( "id: " );
1246            sb.append( getId() );
1247            sb.append( "\n" );
1248            sb.append( "version: " );
1249            sb.append( getVersion() );
1250            sb.append( "\n" );
1251            if ( this.layers != null ) {
1252                sb.append( "layer(s): " );
1253                for ( int i = 0; i < this.layers.size(); i++ ) {
1254                    sb.append( this.layers.get( i ).getName() );
1255                    sb.append( "\n" );
1256                }
1257            }
1258            return sb.toString();
1259        }
1260    }