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 }