001    //$HeadURL: http://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/security/owsrequestvalidator/wms/GetMapRequestValidator.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.security.owsrequestvalidator.wms;
037    
038    import static org.deegree.security.drm.model.RightType.GETMAP;
039    import static org.deegree.security.owsrequestvalidator.Messages.getString;
040    
041    import java.net.URL;
042    import java.util.ArrayList;
043    import java.util.HashMap;
044    import java.util.List;
045    import java.util.Map;
046    import java.util.regex.Matcher;
047    import java.util.regex.Pattern;
048    
049    import org.deegree.datatypes.QualifiedName;
050    import org.deegree.datatypes.Types;
051    import org.deegree.framework.log.ILogger;
052    import org.deegree.framework.log.LoggerFactory;
053    import org.deegree.framework.util.ColorUtils;
054    import org.deegree.framework.util.MapUtils;
055    import org.deegree.framework.util.StringTools;
056    import org.deegree.framework.xml.XMLParsingException;
057    import org.deegree.graphics.sld.AbstractStyle;
058    import org.deegree.graphics.sld.NamedLayer;
059    import org.deegree.graphics.sld.NamedStyle;
060    import org.deegree.graphics.sld.SLDFactory;
061    import org.deegree.graphics.sld.StyledLayerDescriptor;
062    import org.deegree.model.crs.CRSFactory;
063    import org.deegree.model.crs.CoordinateSystem;
064    import org.deegree.model.crs.GeoTransformer;
065    import org.deegree.model.feature.Feature;
066    import org.deegree.model.feature.FeatureFactory;
067    import org.deegree.model.feature.FeatureProperty;
068    import org.deegree.model.feature.schema.FeatureType;
069    import org.deegree.model.feature.schema.PropertyType;
070    import org.deegree.model.spatialschema.Envelope;
071    import org.deegree.model.spatialschema.GeometryFactory;
072    import org.deegree.ogcwebservices.InvalidParameterValueException;
073    import org.deegree.ogcwebservices.OGCWebServiceRequest;
074    import org.deegree.ogcwebservices.wms.operation.GetMap;
075    import org.deegree.security.UnauthorizedException;
076    import org.deegree.security.drm.SecurityAccess;
077    import org.deegree.security.drm.SecurityAccessManager;
078    import org.deegree.security.drm.model.Role;
079    import org.deegree.security.drm.model.Service;
080    import org.deegree.security.drm.model.User;
081    import org.deegree.security.owsproxy.Condition;
082    import org.deegree.security.owsproxy.OperationParameter;
083    import org.deegree.security.owsproxy.Request;
084    import org.deegree.security.owsrequestvalidator.Messages;
085    import org.deegree.security.owsrequestvalidator.Policy;
086    
087    /**
088     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
089     * @author last edited by: $Author: aschmitz $
090     * 
091     * @version 1.1, $Revision: 32121 $, $Date: 2011-10-10 16:51:09 +0200 (Mo, 10 Okt 2011) $
092     * 
093     * @since 1.1
094     */
095    
096    public class GetMapRequestValidator extends AbstractWMSRequestValidator {
097    
098        private static final ILogger LOG = LoggerFactory.getLogger( GetMapRequestValidator.class );
099    
100        // known condition parameter
101        private static final String BBOX = "bbox";
102    
103        private static final String LAYERS = "layers";
104    
105        private static final String BGCOLOR = "bgcolor";
106    
107        private static final String TRANSPARENCY = "transparency";
108    
109        private static final String RESOLUTION = "resolution";
110    
111        private static final String SLD = "sld";
112    
113        private static final String INVALIDBBOX = Messages.getString( "GetMapRequestValidator.INVALIDBBOX" );
114    
115        private static final String INVALIDLAYER = Messages.getString( "GetMapRequestValidator.INVALIDLAYER" );
116    
117        private static final String INVALIDSTYLE = Messages.getString( "GetMapRequestValidator.INVALIDSTYLE" );
118    
119        private static final String INVALIDBGCOLOR = Messages.getString( "GetMapRequestValidator.INVALIDBGCOLOR" );
120    
121        private static final String INVALIDTRANSPARENCY = Messages.getString( "GetMapRequestValidator.INVALIDTRANSPARENCY" );
122    
123        private static final String INVALIDRESOLUTION = Messages.getString( "GetMapRequestValidator.INVALIDRESOLUTION" );
124    
125        private static final String INVALIDSLD = Messages.getString( "GetMapRequestValidator.INVALIDSLD" );
126    
127        private static final String MISSINGCRS = Messages.getString( "GetMapRequestValidator.MISSINGCRS" );
128    
129        private List<String> accessdRes = new ArrayList<String>();
130    
131        private static FeatureType mapFT = null;
132    
133        private GeoTransformer gt = null;
134    
135        static {
136            if ( mapFT == null ) {
137                mapFT = GetMapRequestValidator.createFeatureType();
138            }
139        }
140    
141        /**
142         * @param policy
143         */
144        public GetMapRequestValidator( Policy policy ) {
145            super( policy );
146            try {
147                gt = new GeoTransformer( "EPSG:4326" );
148            } catch ( Exception e ) {
149                e.printStackTrace();
150            }
151        }
152    
153        /**
154         * validates the incoming GetMap request against the policy assigned to a validator
155         * 
156         * @param request
157         *            request to validate
158         * @param user
159         *            name of the user who likes to perform the request (can be null)
160         */
161        @Override
162        public void validateRequest( OGCWebServiceRequest request, User user )
163                                throws InvalidParameterValueException, UnauthorizedException {
164    
165            accessdRes.clear();
166            userCoupled = false;
167            Request req = policy.getRequest( "WMS", "GetMap" );
168            // request is valid because no restrictions are made
169            if ( req.isAny() || req.getPreConditions().isAny() ) {
170                return;
171            }
172            Condition condition = req.getPreConditions();
173    
174            GetMap wmsreq = (GetMap) request;
175    
176            validateVersion( condition, wmsreq.getVersion() );
177            Envelope env = wmsreq.getBoundingBox();
178            try {
179                env = gt.transform( env, wmsreq.getSrs() );
180            } catch ( Exception e ) {
181                throw new InvalidParameterValueException( "condition envelope isn't in the right CRS ", e );
182            }
183            validateBBOX( condition, env );
184            validateLayers( condition, wmsreq.getLayers() );
185            validateBGColor( condition, ColorUtils.toHexCode( "0x", wmsreq.getBGColor() ) );
186            validateTransparency( condition, wmsreq.getTransparency() );
187            validateExceptions( condition, wmsreq.getExceptions() );
188            validateFormat( condition, wmsreq.getFormat() );
189            validateMaxWidth( condition, wmsreq.getWidth() );
190            validateMaxHeight( condition, wmsreq.getHeight() );
191            validateResolution( condition, wmsreq );
192            validateSLD( condition, wmsreq.getSLD_URL() );
193            validateSLD_Body( condition, wmsreq.getStyledLayerDescriptor() );
194    
195            if ( userCoupled ) {
196                validateAgainstRightsDB( wmsreq, user );
197            }
198    
199        }
200    
201        /**
202         * checks if the passed envelope is valid against the maximum bounding box defined in the policy. If
203         * <tt>user</ff> != <tt>null</tt> the maximu valid BBOX will be read from the user/rights repository
204         * 
205         * @param condition
206         *            condition containing the definition of the valid BBOX
207         * @param envelope
208         * @throws InvalidParameterValueException
209         */
210        private void validateBBOX( Condition condition, Envelope envelope )
211                                throws InvalidParameterValueException {
212    
213            OperationParameter op = condition.getOperationParameter( BBOX );
214    
215            // version is valid because no restrictions are made
216            if ( op.isAny() )
217                return;
218    
219            String v = op.getFirstAsString();
220            String[] d = StringTools.toArray( v, ",", false );
221            Envelope env = GeometryFactory.createEnvelope( Double.parseDouble( d[0] ), Double.parseDouble( d[1] ),
222                                                           Double.parseDouble( d[2] ), Double.parseDouble( d[3] ), null );
223    
224            try {
225                env = gt.transform( env, d[4] );
226            } catch ( Exception e ) {
227                throw new InvalidParameterValueException( MISSINGCRS, e );
228            }
229    
230            if ( !env.contains( envelope ) ) {
231                if ( !op.isUserCoupled() ) {
232                    // if not user coupled the validation has failed
233                    throw new InvalidParameterValueException( INVALIDBBOX + op.getFirstAsString() );
234                }
235                userCoupled = true;
236                accessdRes.add( "BBOX: " + v );
237            }
238        }
239    
240        /**
241         * checks if the passed layres/styles are valid against the layers/styles list defined in the policy. If
242         * <tt>user</ff> != <tt>null</tt> the valid layers/styles will be read from the user/rights repository
243         * 
244         * @param condition
245         *            condition containing the definition of the valid layers/styles
246         * @param layers
247         * @throws InvalidParameterValueException
248         */
249        private void validateLayers( Condition condition, GetMap.Layer[] layers )
250                                throws InvalidParameterValueException {
251    
252            OperationParameter op = condition.getOperationParameter( LAYERS );
253    
254            // version is valid because no restrictions are made
255            if ( op.isAny() ) {
256                return;
257            }
258    
259            List<String> v = op.getValues();
260            // seperate layers from assigned styles
261            Map<String, String> map = new HashMap<String, String>();
262            for ( int i = 0; i < v.size(); i++ ) {
263                String[] tmp = StringTools.toArray( v.get( i ), "|", false );
264                map.put( tmp[0], tmp[1] );
265            }
266    
267            for ( int i = 0; i < layers.length; i++ ) {
268                String style = layers[i].getStyleName();
269                String vs = map.get( layers[i].getName() );
270                if ( vs == null ) {
271                    if ( !op.isUserCoupled() ) {
272                        throw new InvalidParameterValueException( INVALIDLAYER + layers[i].getName() );
273                    }
274                    accessdRes.add( "Layers: " + layers[i].getName() );
275                    userCoupled = true;
276                } else if ( !style.equalsIgnoreCase( "default" ) && vs.indexOf( "$any$" ) < 0 && vs.indexOf( style ) < 0 ) {
277                    // a style is valid for a layer if it's the default style
278                    // or the layer accepts any style or a style is explicit defined
279                    // to be valid
280                    if ( !op.isUserCoupled() ) {
281                        throw new InvalidParameterValueException( INVALIDSTYLE + layers[i].getName() + ':' + style );
282                    }
283                    userCoupled = true;
284                    accessdRes.add( "Styles: " + style );
285                }
286            }
287    
288        }
289    
290        /**
291         * checks if the passed bgcolor is valid against the bgcolor(s) defined in the policy. If
292         * <tt>user</ff> != <tt>null</tt> the valid bgcolors will be read from the user/rights repository
293         * 
294         * @param condition
295         *            condition containing the definition of the valid bgcolors
296         * @param bgcolor
297         * @throws InvalidParameterValueException
298         */
299        private void validateBGColor( Condition condition, String bgcolor )
300                                throws InvalidParameterValueException {
301    
302            OperationParameter op = condition.getOperationParameter( BGCOLOR );
303    
304            // version is valid because no restrictions are made
305            if ( op.isAny() )
306                return;
307    
308            List<String> list = op.getValues();
309    
310            if ( !list.contains( bgcolor ) ) {
311                if ( !op.isUserCoupled() ) {
312                    throw new InvalidParameterValueException( INVALIDBGCOLOR + bgcolor );
313                }
314                accessdRes.add( "BGCOLOR" + bgcolor );
315                userCoupled = true;
316            }
317    
318        }
319    
320        /**
321         * checks if the passed transparency is valid against the transparency defined in the policy. If
322         * <tt>user</ff> != <tt>null</tt> the valid transparency will be read from the user/rights repository
323         * 
324         * @param condition
325         *            condition containing the definition of the valid transparency
326         * @param transparency
327         * @throws InvalidParameterValueException
328         */
329        private void validateTransparency( Condition condition, boolean transparency )
330                                throws InvalidParameterValueException {
331    
332            OperationParameter op = condition.getOperationParameter( TRANSPARENCY );
333    
334            // version is valid because no restrictions are made
335            if ( op.isAny() )
336                return;
337    
338            List<String> v = op.getValues();
339            String s = "" + transparency;
340            if ( !v.get( 0 ).equals( s ) && !v.get( v.size() - 1 ).equals( s ) ) {
341                if ( !op.isUserCoupled() ) {
342                    throw new InvalidParameterValueException( INVALIDTRANSPARENCY + transparency );
343                }
344                userCoupled = true;
345                accessdRes.add( "Transparency: " + transparency );
346            }
347    
348        }
349    
350        /**
351         * checks if the requested map area/size is valid against the minimum resolution defined in the policy. If
352         * <tt>user</ff> != <tt>null</tt> the valid resolution will be read from the user/rights repository
353         * 
354         * @param condition
355         *            condition containing the definition of the valid resolution
356         * @throws InvalidParameterValueException
357         */
358        private void validateResolution( Condition condition, GetMap gmr )
359                                throws InvalidParameterValueException {
360    
361            OperationParameter op = condition.getOperationParameter( RESOLUTION );
362    
363            // version is valid because no restrictions are made
364            if ( op.isAny() )
365                return;
366    
367            double scale = 0;
368            try {
369                scale = calcScale( gmr );
370            } catch ( Exception e ) {
371                throw new InvalidParameterValueException( StringTools.stackTraceToString( e ) );
372            }
373            double compareRes = 0;
374            compareRes = op.getFirstAsDouble();
375            if ( scale < compareRes ) {
376                if ( !op.isUserCoupled() ) {
377                    throw new InvalidParameterValueException( INVALIDRESOLUTION + scale );
378                }
379                userCoupled = true;
380                accessdRes.add( "resolution: " + scale );
381            }
382        }
383    
384        /**
385         * checks if the passed reference to a SLD document is valid against the defined in the policy. If
386         * <tt>user</ff> != <tt>null</tt> the valid sld reference addresses will be read from the user/rights repository
387         * 
388         * @param condition
389         *            condition containing the definition of the valid sldRef
390         * @param sldRef
391         * @throws InvalidParameterValueException
392         */
393        private void validateSLD( Condition condition, URL sldRef )
394                                throws InvalidParameterValueException {
395    
396            OperationParameter op = condition.getOperationParameter( SLD );
397            OperationParameter gmop = condition.getOperationParameter( LAYERS );
398    
399            if ( op == null && sldRef != null ) {
400                throw new InvalidParameterValueException( INVALIDSLD + sldRef );
401            }
402            // sldRef is valid because no restrictions are made
403            if ( sldRef == null || op.isAny() ) {
404                return;
405            }
406    
407            // validate reference base of the SLD
408            List<String> list = op.getValues();
409            String port = null;
410            if ( sldRef.getPort() != -1 ) {
411                port = ":" + sldRef.getPort();
412            } else {
413                port = ":80";
414            }
415            String addr = sldRef.getProtocol() + "://" + sldRef.getHost() + port;
416            if ( !list.contains( addr ) ) {
417                if ( !op.isUserCoupled() ) {
418                    throw new InvalidParameterValueException( INVALIDSLD + sldRef );
419                }
420                userCoupled = true;
421            }
422    
423            // validate referenced dacument to be a valid SLD
424            StyledLayerDescriptor sld = null;
425            try {
426                sld = SLDFactory.createSLD( sldRef );
427            } catch ( XMLParsingException e ) {
428                String s = org.deegree.i18n.Messages.getMessage( "WMS_SLD_IS_NOT_VALID", sldRef );
429                throw new InvalidParameterValueException( s );
430            }
431    
432            // validate NamedLayers referenced by the SLD
433            NamedLayer[] nl = sld.getNamedLayers();
434            List<String> v = gmop.getValues();
435            // seperate layers from assigned styles
436            Map<String, String> map = new HashMap<String, String>();
437            for ( int i = 0; i < v.size(); i++ ) {
438                String[] tmp = StringTools.toArray( v.get( i ), "|", false );
439                map.put( tmp[0], tmp[1] );
440            }
441            if ( !userCoupled ) {
442                for ( int i = 0; i < nl.length; i++ ) {
443                    AbstractStyle st = nl[i].getStyles()[0];
444                    String style = null;
445                    if ( st instanceof NamedStyle ) {
446                        style = ( (NamedStyle) st ).getName();
447                    } else {
448                        // use default as name if a UserStyle is defined
449                        // to ensure that the style will be accepted by
450                        // the validator
451                        style = "default";
452                    }
453                    String vs = map.get( nl[i].getName() );
454                    if ( vs == null ) {
455                        if ( !op.isUserCoupled() ) {
456                            throw new InvalidParameterValueException( INVALIDLAYER + nl[i].getName() );
457                        }
458                        accessdRes.add( "Layers: " + nl[i].getName() );
459                        userCoupled = true;
460                    } else if ( !style.equalsIgnoreCase( "default" ) && vs.indexOf( "$any$" ) < 0
461                                && vs.indexOf( style ) < 0 ) {
462                        // a style is valid for a layer if it's the default style
463                        // or the layer accepts any style or a style is explicit defined
464                        // to be valid
465                        if ( !op.isUserCoupled() ) {
466                            throw new InvalidParameterValueException( INVALIDSTYLE + nl[i].getName() + ':' + style );
467                        }
468                        userCoupled = true;
469                        accessdRes.add( "Styles: " + style );
470                    }
471                }
472            }
473    
474        }
475    
476        /**
477         * checks if the passed user is allowed to perform a GetMap request containing a SLD_BODY parameter.
478         * 
479         * @param condition
480         *            condition containing when SLD_BODY is valid or nots
481         * @param sld_body
482         */
483        private void validateSLD_Body( Condition condition, StyledLayerDescriptor sld_body ) {
484    
485            /*
486             * 
487             * OperationParameter op = condition.getOperationParameter( SLD_BODY ); // version is valid because no
488             * restrictions are made if ( sld_body == null ||op.isAny() ) return; // at the moment it is just evaluated if
489             * the user is allowed // to perform a SLD request or not. no content validation will // be made boolean
490             * isAllowed = false; if ( op.isUserCoupled() ) { //TODO // get comparator list from security registry } if
491             * (!isAllowed ) { throw new InvalidParameterValueException( INVALIDSLD_BODY ); }
492             */
493        }
494    
495        /**
496         * validates the passed WMS GetMap request against a User- and Rights-Management DB.
497         * 
498         * @param wmsreq
499         * @param user
500         * @throws InvalidParameterValueException
501         */
502        private void validateAgainstRightsDB( GetMap wmsreq, User user )
503                                throws InvalidParameterValueException, UnauthorizedException {
504    
505            if ( user == null ) {
506                StringBuffer sb = new StringBuffer( 1000 );
507                sb.append( ' ' );
508                for ( int i = 0; i < accessdRes.size(); i++ ) {
509                    sb.append( accessdRes.get( i ) ).append( "; " );
510                }
511                throw new UnauthorizedException( Messages.format( "RequestValidator.NOACCESS", sb ) );
512            }
513    
514            Double scale = null;
515            try {
516                scale = new Double( calcScale( wmsreq ) );
517            } catch ( Exception e ) {
518                throw new InvalidParameterValueException( e );
519            }
520    
521            // create feature that describes the map request
522            FeatureProperty[] fps = new FeatureProperty[11];
523            fps[0] = FeatureFactory.createFeatureProperty( new QualifiedName( "version" ), wmsreq.getVersion() );
524            fps[1] = FeatureFactory.createFeatureProperty( new QualifiedName( "width" ), new Integer( wmsreq.getWidth() ) );
525            fps[2] = FeatureFactory.createFeatureProperty( new QualifiedName( "height" ), new Integer( wmsreq.getHeight() ) );
526            Envelope env = wmsreq.getBoundingBox();
527            try {
528                env = gt.transform( env, wmsreq.getSrs() );
529            } catch ( Exception e ) {
530                throw new InvalidParameterValueException( "A:condition envelope isn't in the right CRS ", e );
531            }
532            Object geom = null;
533            try {
534                geom = GeometryFactory.createSurface( env, null );
535            } catch ( Exception e1 ) {
536                e1.printStackTrace();
537            }
538            fps[3] = FeatureFactory.createFeatureProperty( new QualifiedName( "GEOM" ), geom );
539            fps[4] = FeatureFactory.createFeatureProperty( new QualifiedName( "format" ), wmsreq.getFormat() );
540            fps[5] = FeatureFactory.createFeatureProperty( new QualifiedName( "bgcolor" ),
541                                                           ColorUtils.toHexCode( "0x", wmsreq.getBGColor() ) );
542            fps[6] = FeatureFactory.createFeatureProperty( new QualifiedName( "transparent" ),
543                                                           "" + wmsreq.getTransparency() );
544            fps[7] = FeatureFactory.createFeatureProperty( new QualifiedName( "exceptions" ), wmsreq.getExceptions() );
545            fps[8] = FeatureFactory.createFeatureProperty( new QualifiedName( "resolution" ), scale );
546            fps[9] = FeatureFactory.createFeatureProperty( new QualifiedName( "sld" ), wmsreq.getSLD_URL() );
547    
548            GetMap.Layer[] layers = wmsreq.getLayers();
549            for ( int i = 0; i < layers.length; i++ ) {
550                fps[10] = FeatureFactory.createFeatureProperty( new QualifiedName( "style" ), layers[i].getStyleName() );
551                Feature feature = FeatureFactory.createFeature( "id", mapFT, fps );
552    
553                if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
554                    for ( int ii = 0; ii < fps.length; ii++ ) {
555                        LOG.logDebug( "compared property: ", fps[ii] );
556                    }
557                }
558    
559                if ( securityConfig.getProxiedUrl() == null ) {
560                    handleUserCoupledRules( user, feature, layers[i].getName(), "Layer", GETMAP );
561                } else {
562                    handleUserCoupledRules( user, feature,
563                                            "[" + securityConfig.getProxiedUrl() + "]:" + layers[i].getName(), "Layer",
564                                            GETMAP );
565                    // handle custom constraints (currently only maxwidth/height)
566                    try {
567                        SecurityAccessManager sam = SecurityAccessManager.getInstance();
568                        SecurityAccess access = sam.acquireAccess( user );
569                        Service service = access.getServiceByAddress( securityConfig.getProxiedUrl() );
570                        boolean authorized = false;
571                        for ( Role r : user.getRoles( access ) ) {
572                            String constr = access.getConstraints( r, service );
573                            if ( constr == null ) {
574                                authorized = true;
575                                break;
576                            }
577                            Pattern p = Pattern.compile( "maxWidth: ([0-9]+), maxHeight: ([0-9]+)" );
578                            Matcher m = p.matcher( constr );
579                            if ( m.find() ) {
580                                int width = Integer.valueOf( m.group( 1 ) );
581                                int height = Integer.valueOf( m.group( 2 ) );
582                                if ( width == 0 && height == 0 ) {
583                                    authorized = true;
584                                    break;
585                                }
586                                if ( width >= wmsreq.getWidth() && height >= wmsreq.getHeight() ) {
587                                    authorized = true;
588                                    break;
589                                }
590                            }
591                        }
592                        if ( !authorized ) {
593                            throw new UnauthorizedException( getString( "RequestValidator.UNAUTORIZEDACCESS" ) );
594                        }
595                    } catch ( UnauthorizedException e ) {
596                        throw e;
597                    } catch ( Throwable e ) {
598                        throw new UnauthorizedException( e );
599                    }
600                }
601            }
602    
603        }
604    
605        /**
606         * calculates the map scale as defined in the OGC WMS 1.1.1 specifications
607         * 
608         * @return scale of the map
609         */
610        private double calcScale( GetMap request )
611                                throws Exception {
612    
613            Envelope bbox = request.getBoundingBox();
614    
615            CoordinateSystem crs = CRSFactory.create( request.getSrs() );
616            return MapUtils.calcScale( request.getWidth(), request.getHeight(), bbox, crs, 1 );
617            // would return scale denominator
618            // return MapUtils.calcScale( request.getWidth(), request.getHeight(), bbox, crs, DEFAULT_PIXEL_SIZE );
619        }
620    
621        private static FeatureType createFeatureType() {
622            PropertyType[] ftps = new PropertyType[11];
623            ftps[0] = FeatureFactory.createSimplePropertyType( new QualifiedName( "version" ), Types.VARCHAR, false );
624            ftps[1] = FeatureFactory.createSimplePropertyType( new QualifiedName( "width" ), Types.INTEGER, false );
625            ftps[2] = FeatureFactory.createSimplePropertyType( new QualifiedName( "height" ), Types.INTEGER, false );
626            ftps[3] = FeatureFactory.createSimplePropertyType( new QualifiedName( "GEOM" ), Types.GEOMETRY, false );
627            ftps[4] = FeatureFactory.createSimplePropertyType( new QualifiedName( "format" ), Types.VARCHAR, false );
628            ftps[5] = FeatureFactory.createSimplePropertyType( new QualifiedName( "bgcolor" ), Types.VARCHAR, false );
629            ftps[6] = FeatureFactory.createSimplePropertyType( new QualifiedName( "transparent" ), Types.VARCHAR, false );
630            ftps[7] = FeatureFactory.createSimplePropertyType( new QualifiedName( "exceptions" ), Types.VARCHAR, false );
631            ftps[8] = FeatureFactory.createSimplePropertyType( new QualifiedName( "resolution" ), Types.DOUBLE, false );
632            ftps[9] = FeatureFactory.createSimplePropertyType( new QualifiedName( "sld" ), Types.VARCHAR, false );
633            ftps[10] = FeatureFactory.createSimplePropertyType( new QualifiedName( "style" ), Types.VARCHAR, false );
634    
635            return FeatureFactory.createFeatureType( "GetMap", false, ftps );
636        }
637    
638    }