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