summaryrefslogtreecommitdiff
path: root/misc/openlayers/tests
diff options
context:
space:
mode:
Diffstat (limited to 'misc/openlayers/tests')
-rw-r--r--misc/openlayers/tests/Animation.html96
-rw-r--r--misc/openlayers/tests/BaseTypes.html387
-rw-r--r--misc/openlayers/tests/BaseTypes/Bounds.html738
-rw-r--r--misc/openlayers/tests/BaseTypes/Class.html350
-rw-r--r--misc/openlayers/tests/BaseTypes/Date.html191
-rw-r--r--misc/openlayers/tests/BaseTypes/Element.html195
-rw-r--r--misc/openlayers/tests/BaseTypes/LonLat.html241
-rw-r--r--misc/openlayers/tests/BaseTypes/Pixel.html123
-rw-r--r--misc/openlayers/tests/BaseTypes/Size.html67
-rw-r--r--misc/openlayers/tests/Console.html39
-rw-r--r--misc/openlayers/tests/Control.html107
-rw-r--r--misc/openlayers/tests/Control/ArgParser.html26
-rw-r--r--misc/openlayers/tests/Control/Attribution.html60
-rw-r--r--misc/openlayers/tests/Control/Button.html17
-rw-r--r--misc/openlayers/tests/Control/CacheRead.html108
-rw-r--r--misc/openlayers/tests/Control/CacheWrite.html90
-rw-r--r--misc/openlayers/tests/Control/DragFeature.html383
-rw-r--r--misc/openlayers/tests/Control/DragPan.html104
-rw-r--r--misc/openlayers/tests/Control/DrawFeature.html160
-rw-r--r--misc/openlayers/tests/Control/EditingToolbar.html33
-rw-r--r--misc/openlayers/tests/Control/Geolocate.html129
-rw-r--r--misc/openlayers/tests/Control/GetFeature.html177
-rw-r--r--misc/openlayers/tests/Control/Graticule.html66
-rw-r--r--misc/openlayers/tests/Control/KeyboardDefaults.html173
-rw-r--r--misc/openlayers/tests/Control/LayerSwitcher.html249
-rw-r--r--misc/openlayers/tests/Control/Measure.html386
-rw-r--r--misc/openlayers/tests/Control/ModifyFeature.html828
-rw-r--r--misc/openlayers/tests/Control/MousePosition.html109
-rw-r--r--misc/openlayers/tests/Control/NavToolbar.html45
-rw-r--r--misc/openlayers/tests/Control/Navigation.html200
-rw-r--r--misc/openlayers/tests/Control/NavigationHistory.html245
-rw-r--r--misc/openlayers/tests/Control/OverviewMap.html266
-rw-r--r--misc/openlayers/tests/Control/Pan.html201
-rw-r--r--misc/openlayers/tests/Control/PanPanel.html61
-rw-r--r--misc/openlayers/tests/Control/PanZoom.html244
-rw-r--r--misc/openlayers/tests/Control/PanZoomBar.html245
-rw-r--r--misc/openlayers/tests/Control/Panel.html382
-rw-r--r--misc/openlayers/tests/Control/Permalink.html453
-rw-r--r--misc/openlayers/tests/Control/PinchZoom.html134
-rw-r--r--misc/openlayers/tests/Control/SLDSelect.html239
-rw-r--r--misc/openlayers/tests/Control/Scale.html54
-rw-r--r--misc/openlayers/tests/Control/ScaleLine.html187
-rw-r--r--misc/openlayers/tests/Control/SelectFeature.html632
-rw-r--r--misc/openlayers/tests/Control/Snapping.html448
-rw-r--r--misc/openlayers/tests/Control/Split.html319
-rw-r--r--misc/openlayers/tests/Control/TouchNavigation.html155
-rw-r--r--misc/openlayers/tests/Control/TransformFeature.html129
-rw-r--r--misc/openlayers/tests/Control/UTFGrid.html120
-rw-r--r--misc/openlayers/tests/Control/WMSGetFeatureInfo.html644
-rw-r--r--misc/openlayers/tests/Control/WMTSGetFeatureInfo.html334
-rw-r--r--misc/openlayers/tests/Control/Zoom.html83
-rw-r--r--misc/openlayers/tests/Control/ZoomBox.html54
-rw-r--r--misc/openlayers/tests/Control/ZoomIn.html101
-rw-r--r--misc/openlayers/tests/Control/ZoomOut.html100
-rw-r--r--misc/openlayers/tests/Control/ZoomToMaxExtent.html102
-rw-r--r--misc/openlayers/tests/Events.html487
-rw-r--r--misc/openlayers/tests/Events/buttonclick.html214
-rw-r--r--misc/openlayers/tests/Events/featureclick.html91
-rw-r--r--misc/openlayers/tests/Extras.html21
-rw-r--r--misc/openlayers/tests/Feature.html205
-rw-r--r--misc/openlayers/tests/Feature/Vector.html170
-rw-r--r--misc/openlayers/tests/Filter.html31
-rw-r--r--misc/openlayers/tests/Filter/Comparison.html373
-rw-r--r--misc/openlayers/tests/Filter/FeatureId.html67
-rw-r--r--misc/openlayers/tests/Filter/Logical.html144
-rw-r--r--misc/openlayers/tests/Filter/Spatial.html112
-rw-r--r--misc/openlayers/tests/Format.html23
-rw-r--r--misc/openlayers/tests/Format/ArcXML.html277
-rw-r--r--misc/openlayers/tests/Format/ArcXML/Features.html69
-rw-r--r--misc/openlayers/tests/Format/Atom.html450
-rw-r--r--misc/openlayers/tests/Format/CQL.html364
-rw-r--r--misc/openlayers/tests/Format/CSWGetDomain.html23
-rw-r--r--misc/openlayers/tests/Format/CSWGetDomain/v2_0_2.html56
-rw-r--r--misc/openlayers/tests/Format/CSWGetDomain/v2_0_2.js18
-rw-r--r--misc/openlayers/tests/Format/CSWGetRecords.html23
-rw-r--r--misc/openlayers/tests/Format/CSWGetRecords/v2_0_2.html88
-rw-r--r--misc/openlayers/tests/Format/CSWGetRecords/v2_0_2.js50
-rw-r--r--misc/openlayers/tests/Format/EncodedPolyline.html372
-rw-r--r--misc/openlayers/tests/Format/Filter.html21
-rw-r--r--misc/openlayers/tests/Format/Filter/v1.html404
-rw-r--r--misc/openlayers/tests/Format/Filter/v1_0_0.html295
-rw-r--r--misc/openlayers/tests/Format/Filter/v1_1_0.html402
-rw-r--r--misc/openlayers/tests/Format/GML.html462
-rw-r--r--misc/openlayers/tests/Format/GML/cases.js232
-rw-r--r--misc/openlayers/tests/Format/GML/v2.html684
-rw-r--r--misc/openlayers/tests/Format/GML/v3.html828
-rw-r--r--misc/openlayers/tests/Format/GPX.html179
-rw-r--r--misc/openlayers/tests/Format/GeoJSON.html468
-rw-r--r--misc/openlayers/tests/Format/GeoRSS.html122
-rw-r--r--misc/openlayers/tests/Format/JSON.html53
-rw-r--r--misc/openlayers/tests/Format/KML.html1437
-rw-r--r--misc/openlayers/tests/Format/OGCExceptionReport.html100
-rw-r--r--misc/openlayers/tests/Format/OSM.html115
-rw-r--r--misc/openlayers/tests/Format/OWSCommon/v1_0_0.html34
-rw-r--r--misc/openlayers/tests/Format/OWSCommon/v1_1_0.html34
-rw-r--r--misc/openlayers/tests/Format/OWSContext/v0_3_1.html278
-rw-r--r--misc/openlayers/tests/Format/QueryStringFilter.html306
-rw-r--r--misc/openlayers/tests/Format/SLD.html36
-rw-r--r--misc/openlayers/tests/Format/SLD/v1_0_0.html1028
-rw-r--r--misc/openlayers/tests/Format/SLD/v1_0_0_GeoServer.html228
-rw-r--r--misc/openlayers/tests/Format/SOSCapabilities/v1_0_0.html80
-rw-r--r--misc/openlayers/tests/Format/SOSCapabilities/v1_0_0.js484
-rw-r--r--misc/openlayers/tests/Format/SOSGetFeatureOfInterest.html80
-rw-r--r--misc/openlayers/tests/Format/SOSGetObservation.html183
-rw-r--r--misc/openlayers/tests/Format/Text.html49
-rw-r--r--misc/openlayers/tests/Format/WCSCapabilities.html43
-rw-r--r--misc/openlayers/tests/Format/WCSCapabilities/v1.html87
-rw-r--r--misc/openlayers/tests/Format/WCSGetCoverage.html80
-rw-r--r--misc/openlayers/tests/Format/WFS.html81
-rw-r--r--misc/openlayers/tests/Format/WFSCapabilities.html43
-rw-r--r--misc/openlayers/tests/Format/WFSCapabilities/v1.html179
-rw-r--r--misc/openlayers/tests/Format/WFSDescribeFeatureType.html436
-rw-r--r--misc/openlayers/tests/Format/WFST.html23
-rw-r--r--misc/openlayers/tests/Format/WFST/v1.html455
-rw-r--r--misc/openlayers/tests/Format/WFST/v1_0_0.html135
-rw-r--r--misc/openlayers/tests/Format/WFST/v1_1_0.html236
-rw-r--r--misc/openlayers/tests/Format/WKT.html297
-rw-r--r--misc/openlayers/tests/Format/WMC.html315
-rw-r--r--misc/openlayers/tests/Format/WMC/v1.html266
-rw-r--r--misc/openlayers/tests/Format/WMC/v1_1_0.html86
-rw-r--r--misc/openlayers/tests/Format/WMSCapabilities.html20
-rw-r--r--misc/openlayers/tests/Format/WMSCapabilities/v1_1_1.html5209
-rw-r--r--misc/openlayers/tests/Format/WMSCapabilities/v1_1_1_WMSC.html348
-rw-r--r--misc/openlayers/tests/Format/WMSCapabilities/v1_3_0.html614
-rw-r--r--misc/openlayers/tests/Format/WMSDescribeLayer.html65
-rw-r--r--misc/openlayers/tests/Format/WMSGetFeatureInfo.html319
-rw-r--r--misc/openlayers/tests/Format/WMTSCapabilities.html20
-rw-r--r--misc/openlayers/tests/Format/WMTSCapabilities/v1_0_0.html1042
-rw-r--r--misc/openlayers/tests/Format/WPSCapabilities/v1_0_0.html30
-rw-r--r--misc/openlayers/tests/Format/WPSCapabilities/v1_0_0.js112
-rw-r--r--misc/openlayers/tests/Format/WPSDescribeProcess.html206
-rw-r--r--misc/openlayers/tests/Format/WPSExecute.html549
-rw-r--r--misc/openlayers/tests/Format/XLS/v1_1_0.html98
-rw-r--r--misc/openlayers/tests/Format/XML.html900
-rw-r--r--misc/openlayers/tests/Format/XML/VersionedOGC.html51
-rw-r--r--misc/openlayers/tests/Geometry.html356
-rw-r--r--misc/openlayers/tests/Geometry/Collection.html431
-rw-r--r--misc/openlayers/tests/Geometry/Curve.html157
-rw-r--r--misc/openlayers/tests/Geometry/LineString.html443
-rw-r--r--misc/openlayers/tests/Geometry/LinearRing.html362
-rw-r--r--misc/openlayers/tests/Geometry/MultiLineString.html267
-rw-r--r--misc/openlayers/tests/Geometry/MultiPoint.html130
-rw-r--r--misc/openlayers/tests/Geometry/MultiPolygon.html34
-rw-r--r--misc/openlayers/tests/Geometry/Point.html244
-rw-r--r--misc/openlayers/tests/Geometry/Polygon.html420
-rw-r--r--misc/openlayers/tests/Handler.html265
-rw-r--r--misc/openlayers/tests/Handler/Box.html106
-rw-r--r--misc/openlayers/tests/Handler/Click.html735
-rw-r--r--misc/openlayers/tests/Handler/Drag.html603
-rw-r--r--misc/openlayers/tests/Handler/Feature.html698
-rw-r--r--misc/openlayers/tests/Handler/Hover.html136
-rw-r--r--misc/openlayers/tests/Handler/Keyboard.html150
-rw-r--r--misc/openlayers/tests/Handler/MouseWheel.html182
-rw-r--r--misc/openlayers/tests/Handler/Path.html1464
-rw-r--r--misc/openlayers/tests/Handler/Pinch.html285
-rw-r--r--misc/openlayers/tests/Handler/Point.html600
-rw-r--r--misc/openlayers/tests/Handler/Polygon.html1161
-rw-r--r--misc/openlayers/tests/Handler/RegularPolygon.html235
-rw-r--r--misc/openlayers/tests/Icon.html68
-rw-r--r--misc/openlayers/tests/Kinetic.html132
-rw-r--r--misc/openlayers/tests/Lang.html106
-rw-r--r--misc/openlayers/tests/Layer.html910
-rw-r--r--misc/openlayers/tests/Layer/ArcGIS93Rest.html324
-rw-r--r--misc/openlayers/tests/Layer/ArcGISCache.html256
-rw-r--r--misc/openlayers/tests/Layer/ArcGISCache.json334
-rw-r--r--misc/openlayers/tests/Layer/ArcIMS.html123
-rw-r--r--misc/openlayers/tests/Layer/Bing.html200
-rw-r--r--misc/openlayers/tests/Layer/EventPane.html172
-rw-r--r--misc/openlayers/tests/Layer/FixedZoomLevels.html137
-rw-r--r--misc/openlayers/tests/Layer/GeoRSS.html210
-rw-r--r--misc/openlayers/tests/Layer/Google.html369
-rw-r--r--misc/openlayers/tests/Layer/Google/v3.html337
-rw-r--r--misc/openlayers/tests/Layer/Grid.html1593
-rw-r--r--misc/openlayers/tests/Layer/HTTPRequest.html229
-rw-r--r--misc/openlayers/tests/Layer/Image.html164
-rw-r--r--misc/openlayers/tests/Layer/KaMap.html287
-rw-r--r--misc/openlayers/tests/Layer/MapGuide.html177
-rw-r--r--misc/openlayers/tests/Layer/MapServer.html238
-rw-r--r--misc/openlayers/tests/Layer/Markers.html156
-rw-r--r--misc/openlayers/tests/Layer/OSM.html16
-rw-r--r--misc/openlayers/tests/Layer/PointGrid.html232
-rw-r--r--misc/openlayers/tests/Layer/PointTrack.html79
-rw-r--r--misc/openlayers/tests/Layer/SphericalMercator.html126
-rw-r--r--misc/openlayers/tests/Layer/TMS.html262
-rw-r--r--misc/openlayers/tests/Layer/Text.html211
-rw-r--r--misc/openlayers/tests/Layer/TileCache.html203
-rw-r--r--misc/openlayers/tests/Layer/UTFGrid.html131
-rw-r--r--misc/openlayers/tests/Layer/Vector.html879
-rw-r--r--misc/openlayers/tests/Layer/Vector/RootContainer.html63
-rw-r--r--misc/openlayers/tests/Layer/WMS.html583
-rw-r--r--misc/openlayers/tests/Layer/WMTS.html1491
-rw-r--r--misc/openlayers/tests/Layer/WrapDateLine.html188
-rw-r--r--misc/openlayers/tests/Layer/XYZ.html266
-rw-r--r--misc/openlayers/tests/Layer/atom-1.0.xml34
-rw-r--r--misc/openlayers/tests/Layer/data_Layer_Text_textfile.txt3
-rw-r--r--misc/openlayers/tests/Layer/data_Layer_Text_textfile_2.txt3
-rw-r--r--misc/openlayers/tests/Layer/data_Layer_Text_textfile_overflow.txt3
-rw-r--r--misc/openlayers/tests/Layer/georss.txt378
-rw-r--r--misc/openlayers/tests/Map.html2255
-rw-r--r--misc/openlayers/tests/Marker.html163
-rw-r--r--misc/openlayers/tests/Marker/Box.html183
-rw-r--r--misc/openlayers/tests/OLLoader.js26
-rw-r--r--misc/openlayers/tests/OpenLayers1.html18
-rw-r--r--misc/openlayers/tests/OpenLayers2.html19
-rw-r--r--misc/openlayers/tests/OpenLayers3.html19
-rw-r--r--misc/openlayers/tests/OpenLayers4.html18
-rw-r--r--misc/openlayers/tests/OpenLayersJsFiles.html27
-rw-r--r--misc/openlayers/tests/Popup.html219
-rw-r--r--misc/openlayers/tests/Popup/Anchored.html37
-rw-r--r--misc/openlayers/tests/Popup/FramedCloud.html18
-rw-r--r--misc/openlayers/tests/Projection.html87
-rw-r--r--misc/openlayers/tests/Protocol.html63
-rw-r--r--misc/openlayers/tests/Protocol/CSW.html90
-rw-r--r--misc/openlayers/tests/Protocol/HTTP.html842
-rw-r--r--misc/openlayers/tests/Protocol/SOS.html57
-rw-r--r--misc/openlayers/tests/Protocol/Script.html282
-rw-r--r--misc/openlayers/tests/Protocol/WFS.html471
-rw-r--r--misc/openlayers/tests/README.txt16
-rw-r--r--misc/openlayers/tests/Renderer.html96
-rw-r--r--misc/openlayers/tests/Renderer/Canvas.html501
-rw-r--r--misc/openlayers/tests/Renderer/Elements.html651
-rw-r--r--misc/openlayers/tests/Renderer/SVG.html441
-rw-r--r--misc/openlayers/tests/Renderer/VML.html454
-rw-r--r--misc/openlayers/tests/Request.html524
-rw-r--r--misc/openlayers/tests/Request/XMLHttpRequest.html59
-rw-r--r--misc/openlayers/tests/Rule.html123
-rw-r--r--misc/openlayers/tests/SingleFile1.html15
-rw-r--r--misc/openlayers/tests/SingleFile2.html15
-rw-r--r--misc/openlayers/tests/SingleFile3.html15
-rw-r--r--misc/openlayers/tests/Strategy.html94
-rw-r--r--misc/openlayers/tests/Strategy/BBOX.html361
-rw-r--r--misc/openlayers/tests/Strategy/Cluster.html148
-rw-r--r--misc/openlayers/tests/Strategy/Filter.html135
-rw-r--r--misc/openlayers/tests/Strategy/Fixed.html253
-rw-r--r--misc/openlayers/tests/Strategy/Paging.html113
-rw-r--r--misc/openlayers/tests/Strategy/Refresh.html177
-rw-r--r--misc/openlayers/tests/Strategy/Save.html127
-rw-r--r--misc/openlayers/tests/Style.html426
-rw-r--r--misc/openlayers/tests/Style2.html56
-rw-r--r--misc/openlayers/tests/StyleMap.html44
-rw-r--r--misc/openlayers/tests/Symbolizer.html31
-rw-r--r--misc/openlayers/tests/Symbolizer/Line.html42
-rw-r--r--misc/openlayers/tests/Symbolizer/Point.html52
-rw-r--r--misc/openlayers/tests/Symbolizer/Polygon.html44
-rw-r--r--misc/openlayers/tests/Symbolizer/Raster.html32
-rw-r--r--misc/openlayers/tests/Symbolizer/Text.html42
-rw-r--r--misc/openlayers/tests/Test.AnotherWay.baseadditions.js191
-rw-r--r--misc/openlayers/tests/Test.AnotherWay.css243
-rw-r--r--misc/openlayers/tests/Test.AnotherWay.geom_eq.js139
-rw-r--r--misc/openlayers/tests/Test.AnotherWay.js2498
-rw-r--r--misc/openlayers/tests/Test.AnotherWay.xml_eq.js311
-rw-r--r--misc/openlayers/tests/Tile.html130
-rw-r--r--misc/openlayers/tests/Tile/Image.html490
-rw-r--r--misc/openlayers/tests/Tile/Image/IFrame.html183
-rw-r--r--misc/openlayers/tests/Tile/UTFGrid.html306
-rw-r--r--misc/openlayers/tests/TileManager.html137
-rw-r--r--misc/openlayers/tests/Tween.html116
-rw-r--r--misc/openlayers/tests/Util.html1180
-rw-r--r--misc/openlayers/tests/Util/vendorPrefix.html117
-rw-r--r--misc/openlayers/tests/Util_common.js64
-rw-r--r--misc/openlayers/tests/Util_w3c.html35
-rw-r--r--misc/openlayers/tests/WPSClient.html108
-rw-r--r--misc/openlayers/tests/WPSProcess.html188
-rw-r--r--misc/openlayers/tests/atom-1.0.xml34
-rw-r--r--misc/openlayers/tests/auto-tests.html2447
-rw-r--r--misc/openlayers/tests/data/geos_wkt_intersects.js495
-rw-r--r--misc/openlayers/tests/data/osm.js14
-rw-r--r--misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/0/0.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/0/1.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/0/2.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/1/0.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/1/1.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/1/2.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/2/0.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/2/1.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/2/2.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/demo-1.1.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/world_utfgrid/1/0/0.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/world_utfgrid/1/0/1.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/world_utfgrid/1/0/2.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/world_utfgrid/1/1/0.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/world_utfgrid/1/1/1.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/world_utfgrid/1/1/2.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/world_utfgrid/1/2/0.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/world_utfgrid/1/2/1.json1
-rw-r--r--misc/openlayers/tests/data/utfgrid/world_utfgrid/1/2/2.json1
-rw-r--r--misc/openlayers/tests/data_Layer_Text_textfile.txt3
-rw-r--r--misc/openlayers/tests/data_Layer_Text_textfile_2.txt3
-rw-r--r--misc/openlayers/tests/data_Layer_Text_textfile_overflow.txt3
-rw-r--r--misc/openlayers/tests/deprecated/Ajax.html28
-rw-r--r--misc/openlayers/tests/deprecated/BaseTypes/Class.html142
-rw-r--r--misc/openlayers/tests/deprecated/BaseTypes/Element.html56
-rw-r--r--misc/openlayers/tests/deprecated/Control/MouseToolbar.html60
-rw-r--r--misc/openlayers/tests/deprecated/Geometry/Rectangle.html77
-rw-r--r--misc/openlayers/tests/deprecated/Layer/GML.html61
-rw-r--r--misc/openlayers/tests/deprecated/Layer/MapServer.html59
-rw-r--r--misc/openlayers/tests/deprecated/Layer/MapServer/Untiled.html158
-rw-r--r--misc/openlayers/tests/deprecated/Layer/WFS.html178
-rw-r--r--misc/openlayers/tests/deprecated/Layer/WMS.html60
-rw-r--r--misc/openlayers/tests/deprecated/Layer/WMS/Post.html89
-rwxr-xr-xmisc/openlayers/tests/deprecated/Layer/Yahoo.html121
-rw-r--r--misc/openlayers/tests/deprecated/Layer/mice.xml156
-rw-r--r--misc/openlayers/tests/deprecated/Layer/owls.xml156
-rw-r--r--misc/openlayers/tests/deprecated/Popup/AnchoredBubble.html61
-rw-r--r--misc/openlayers/tests/deprecated/Protocol/SQL.html24
-rw-r--r--misc/openlayers/tests/deprecated/Protocol/SQL/Gears.html474
-rw-r--r--misc/openlayers/tests/deprecated/Renderer/SVG2.html399
-rw-r--r--misc/openlayers/tests/deprecated/Tile/WFS.html215
-rw-r--r--misc/openlayers/tests/deprecated/Util.html20
-rw-r--r--misc/openlayers/tests/georss.txt378
-rw-r--r--misc/openlayers/tests/grid_inittiles.html30
-rw-r--r--misc/openlayers/tests/index.html6
-rw-r--r--misc/openlayers/tests/list-tests.html260
-rw-r--r--misc/openlayers/tests/manual/ajax.html49
-rw-r--r--misc/openlayers/tests/manual/ajax.txt1
-rw-r--r--misc/openlayers/tests/manual/alloverlays-mixed.html55
-rw-r--r--misc/openlayers/tests/manual/arcims-2117.html103
-rw-r--r--misc/openlayers/tests/manual/arkansas.rss9
-rw-r--r--misc/openlayers/tests/manual/big-georss.html33
-rw-r--r--misc/openlayers/tests/manual/box-quirks.html52
-rw-r--r--misc/openlayers/tests/manual/box-strict.html46
-rw-r--r--misc/openlayers/tests/manual/clip-features-svg.html128
-rw-r--r--misc/openlayers/tests/manual/dateline-sketch.html66
-rw-r--r--misc/openlayers/tests/manual/dateline-smallextent.html61
-rw-r--r--misc/openlayers/tests/manual/draw-feature.html73
-rw-r--r--misc/openlayers/tests/manual/feature-handler.html126
-rw-r--r--misc/openlayers/tests/manual/geodesic.html160
-rw-r--r--misc/openlayers/tests/manual/geojson-geomcoll-reprojection.html74
-rw-r--r--misc/openlayers/tests/manual/google-fullscreen-overlay.html54
-rw-r--r--misc/openlayers/tests/manual/google-panning.html122
-rw-r--r--misc/openlayers/tests/manual/google-resize.html55
-rw-r--r--misc/openlayers/tests/manual/google-tilt.html37
-rw-r--r--misc/openlayers/tests/manual/google-v3-resize.html54
-rw-r--r--misc/openlayers/tests/manual/loadend.html73
-rw-r--r--misc/openlayers/tests/manual/map-events.html38
-rw-r--r--misc/openlayers/tests/manual/memory/Marker-2258.html60
-rw-r--r--misc/openlayers/tests/manual/memory/PanZoom-2323.html41
-rw-r--r--misc/openlayers/tests/manual/memory/RemoveChild-2170.html56
-rw-r--r--misc/openlayers/tests/manual/memory/VML-2170.html49
-rw-r--r--misc/openlayers/tests/manual/multiple-google-layers.html135
-rw-r--r--misc/openlayers/tests/manual/overviewmap-projection.html70
-rw-r--r--misc/openlayers/tests/manual/page-position.html103
-rw-r--r--misc/openlayers/tests/manual/pan-redraw-svg.html58
-rw-r--r--misc/openlayers/tests/manual/popup-keepInMap.html100
-rw-r--r--misc/openlayers/tests/manual/reflow.html59
-rw-r--r--misc/openlayers/tests/manual/renderedDimensions.html113
-rw-r--r--misc/openlayers/tests/manual/select-feature-right-click.html86
-rw-r--r--misc/openlayers/tests/manual/select-feature.html75
-rw-r--r--misc/openlayers/tests/manual/tiles-loading.html122
-rw-r--r--misc/openlayers/tests/manual/tween.html82
-rw-r--r--misc/openlayers/tests/manual/vector-features-performance.html149
-rw-r--r--misc/openlayers/tests/manual/vector-layer-zindex.html143
-rw-r--r--misc/openlayers/tests/mice.xml156
-rw-r--r--misc/openlayers/tests/node.js/mockdom.js104
-rw-r--r--misc/openlayers/tests/node.js/node-tests.cfg12
-rw-r--r--misc/openlayers/tests/node.js/node.js1
-rw-r--r--misc/openlayers/tests/node.js/run-test.js26
-rwxr-xr-xmisc/openlayers/tests/node.js/run.sh10
-rw-r--r--misc/openlayers/tests/owls.xml156
-rw-r--r--misc/openlayers/tests/run-tests.html155
-rw-r--r--misc/openlayers/tests/selenium/remotecontrol/config.cfg48
-rw-r--r--misc/openlayers/tests/selenium/remotecontrol/selenium.py1846
-rw-r--r--misc/openlayers/tests/selenium/remotecontrol/setup.txt8
-rw-r--r--misc/openlayers/tests/selenium/remotecontrol/test_ol.py95
-rw-r--r--misc/openlayers/tests/speed/geometry.html43
-rw-r--r--misc/openlayers/tests/speed/string_format.html29
-rw-r--r--misc/openlayers/tests/speed/vector-renderers.html25
-rw-r--r--misc/openlayers/tests/speed/vector-renderers.js70
-rw-r--r--misc/openlayers/tests/speed/wmc_speed.html30
-rw-r--r--misc/openlayers/tests/speed/wmscaps.html52
-rw-r--r--misc/openlayers/tests/speed/wmscaps.js4956
-rw-r--r--misc/openlayers/tests/speed/wmscaps.xml4954
-rw-r--r--misc/openlayers/tests/throws.js82
373 files changed, 96172 insertions, 0 deletions
diff --git a/misc/openlayers/tests/Animation.html b/misc/openlayers/tests/Animation.html
new file mode 100644
index 0000000..c24ae49
--- /dev/null
+++ b/misc/openlayers/tests/Animation.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Animation.js Tests</title>
+ <script>
+
+ // dependencies for tests
+ var OpenLayers = [
+ "OpenLayers/Util/vendorPrefix.js",
+ "OpenLayers/Animation.js"
+ ];
+
+ </script>
+ <script src="OLLoader.js"></script>
+
+ <script>
+
+ function test_all(t) {
+ t.plan(8);
+ t.ok(OpenLayers.Animation.isNative !== undefined, "isNative is set.");
+
+ function doIt(win) {
+ win.requestFrame(t);
+ win.start(t);
+ win.startDuration(t);
+ win.stop(t);
+ }
+
+ // Test in an extra window in Firefox, and directly in other browsers.
+ // This is needed because requestAnimationFrame does not work
+ // correctly in Firefox in a hidden IFrame.
+ if (window.mozRequestAnimationFrame) {
+ t.open_window("Animation.html", doIt);
+ } else {
+ doIt(window);
+ }
+ }
+
+ function requestFrame(t) {
+
+ t.eq(typeof OpenLayers.Animation.requestFrame, "function", "requestFrame is a function");
+
+ var calls = 0;
+ OpenLayers.Animation.requestFrame(function() {
+ ++calls;
+ });
+ t.delay_call(0.1, function() {
+ t.ok(calls > 0, "callback called: " + calls);
+ });
+ }
+
+ function start(t) {
+
+ var calls = 0;
+ var id = OpenLayers.Animation.start(function() {
+ ++calls;
+ });
+ t.delay_call(0.1, function() {
+ t.ok(calls > 1, "looped: " + calls);
+ OpenLayers.Animation.stop(id);
+ });
+ }
+
+ function startDuration(t) {
+
+ var calls = 0;
+ var id = OpenLayers.Animation.start(function() {
+ ++calls;
+ }, 100);
+ var first;
+ t.delay_call(0.2, function() {
+ first = calls;
+ t.ok(calls > 1, "looped: " + calls);
+ });
+ t.delay_call(0.3, function() {
+ t.eq(calls, first, "not being called any more");
+ });
+ }
+
+ function stop(t) {
+
+ var calls = 0;
+ var id = OpenLayers.Animation.start(function() {
+ ++calls;
+ });
+ var first;
+ t.delay_call(0.2, function() {
+ first = calls;
+ t.ok(calls > 1, "looped: " + calls);
+ OpenLayers.Animation.stop(id);
+ });
+ t.delay_call(0.3, function() {
+ t.eq(calls, first, "not being called any more");
+ });
+ }
+ </script> \ No newline at end of file
diff --git a/misc/openlayers/tests/BaseTypes.html b/misc/openlayers/tests/BaseTypes.html
new file mode 100644
index 0000000..38878dc
--- /dev/null
+++ b/misc/openlayers/tests/BaseTypes.html
@@ -0,0 +1,387 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_String_startsWith(t) {
+ t.plan(3);
+
+ var str = "chickenHead";
+
+ var test1 = "chicken";
+ var test2 = "beet";
+
+ t.ok(OpenLayers.String.startsWith(str, "chicken"),
+ "'chickenHead' starts with 'chicken'");
+ t.ok(!OpenLayers.String.startsWith(str, "Head"),
+ "'chickenHead' does not start with 'Head'");
+ t.ok(!OpenLayers.String.startsWith(str, "beet"),
+ "'chickenHead' doesnt start with 'beet'");
+ }
+
+ function test_String_contains(t) {
+ t.plan(4);
+
+ var str = "chickenHead";
+
+ t.ok(OpenLayers.String.contains(str, "chicken"),
+ "(beginning) 'chickenHead' contains with 'chicken'");
+ t.ok(OpenLayers.String.contains(str, "ick"),
+ "(middle) 'chickenHead' contains with 'ick'");
+ t.ok(OpenLayers.String.contains(str, "Head"),
+ "(end) 'chickenHead' contains with 'Head'");
+ t.ok(!OpenLayers.String.startsWith(str, "beet"),
+ "'chickenHead' doesnt start with 'beet'");
+ }
+
+ function test_String_trim(t) {
+ t.plan(6);
+
+ var str = "chickenHead";
+ t.eq(OpenLayers.String.trim(str),
+ "chickenHead", "string with no extra whitespace is left alone");
+
+ str = " chickenHead";
+ t.eq(OpenLayers.String.trim(str),
+ "chickenHead", "string with extra whitespace at beginning is trimmed correctly");
+
+ str = "chickenHead ";
+ t.eq(OpenLayers.String.trim(str),
+ "chickenHead", "string with extra whitespace at end is trimmed correctly");
+
+ str = " chickenHead ";
+ t.eq(OpenLayers.String.trim(str),
+ "chickenHead", "string with extra whitespace at beginning and end is trimmed correctly");
+
+ str = "chicken\nHead ";
+ t.eq(OpenLayers.String.trim(str),
+ "chicken\nHead", "multi-line string with extra whitespace at end is trimmed correctly");
+ str = " ";
+ t.eq(OpenLayers.String.trim(str), "", "whitespace string is trimmed correctly");
+ }
+
+ function test_String_camelize(t) {
+ t.plan(7);
+
+ var str = "chickenhead";
+ t.eq(OpenLayers.String.camelize(str), "chickenhead", "string with no hyphens is left alone");
+
+ str = "chicken-head";
+ t.eq(OpenLayers.String.camelize(str), "chickenHead", "string with one middle hyphen is camelized correctly");
+
+ str = "chicken-head-man";
+ t.eq(OpenLayers.String.camelize(str), "chickenHeadMan", "string with multiple middle hyphens is camelized correctly");
+
+ str = "-chickenhead";
+ t.eq(OpenLayers.String.camelize(str), "Chickenhead", "string with starting hyphen is camelized correctly (capitalized)");
+
+ str = "-chicken-head-man";
+ t.eq(OpenLayers.String.camelize(str), "ChickenHeadMan", "string with starting hypen and multiple middle hyphens is camelized correctly");
+
+ str = "chicken-";
+ t.eq(OpenLayers.String.camelize(str), "chicken", "string ending in hyphen is camelized correctly (hyphen dropped)");
+
+ str = "chicken-head-man-";
+ t.eq(OpenLayers.String.camelize(str), "chickenHeadMan", "string with multiple middle hyphens and end hyphen is camelized correctly (end hyphen dropped)");
+
+
+ }
+
+ function test_String_format(t) {
+ var unchanged = [
+ "", "${ ", "${", " ${", "${${", "${}", "${${}}", " ${ ${",
+ "}", "${${} }"
+ ]
+ t.plan(7 + unchanged.length);
+
+ var format = OpenLayers.String.format;
+
+ var expected;
+ for(var i=0; i<unchanged.length; ++i) {
+ expected = unchanged[i];
+ t.eq(format(expected), expected,
+ "'" + expected + "' left unchanged");
+ }
+
+ t.eq(format("${foo} none"),
+ "undefined none", "undefined properties don't bomb");
+
+ window.foo = "bar";
+ t.eq(format("${foo} none"),
+ "bar none", "window context used if none passed");
+
+ var context = {bar: "foo"};
+ t.eq(format("${bar} foo", context), "foo foo",
+ "properties accessed from context");
+
+ var context = {bar: "foo", foo: "bar"};
+ t.eq(format("a ${bar} is a ${foo}", context), "a foo is a bar",
+ "multiple properties replaced correctly");
+
+ // test context with properties that are functions
+ var context = {
+ bar: "church",
+ getDrunk: function() {
+ return arguments[0];
+ }
+ };
+ t.eq(
+ format("I go to the ${bar} to ${getDrunk}.", context, ["eat pretzels"]),
+ "I go to the church to eat pretzels.",
+ "function correctly called in context with arguments"
+ );
+
+ // test that things don't break
+ var context = {
+ meaning: function(truth) {
+ return truth;
+ }
+ };
+ t.eq(
+ format("In life, truth is ${meaning}.", context),
+ "In life, truth is undefined.",
+ "still works if arguments are not supplied"
+ );
+
+ // test contexts where attribute values can be objects
+ var context = {
+ a: {
+ b: {
+ c: 'd',
+ e: function() {
+ return 'f';
+ }
+ }
+ }
+ };
+ t.eq(
+ format("${a.b.c} ${a.b.e} ${a.b.q} ${a} ${a...b...c} ${aa.b} ${a.bb.c}", context),
+ "d f undefined [object Object] d undefined undefined",
+ "attribute values that are objects are supported"
+ );
+
+ }
+
+ function test_String_isNumeric(t) {
+ var cases = [
+ {value: "3", expect: true},
+ {value: "+3", expect: true},
+ {value: "-3", expect: true},
+ {value: "3.0", expect: true},
+ {value: "+3.0", expect: true},
+ {value: "-3.0", expect: true},
+ {value: "6.02e23", expect: true},
+ {value: "+1.0e-100", expect: true},
+ {value: "-1.0e+100", expect: true},
+ {value: "1E100", expect: true},
+ {value: null, expect: false},
+ {value: true, expect: false},
+ {value: false, expect: false},
+ {value: undefined, expect: false},
+ {value: "", expect: false},
+ {value: "3 ", expect: false},
+ {value: " 3", expect: false},
+ {value: "1e", expect: false},
+ {value: "1+e", expect: false},
+ {value: "1-e", expect: false}
+ ];
+ t.plan(cases.length);
+
+ var func = OpenLayers.String.isNumeric;
+ var obj, val, got, exp;
+ for(var i=0; i<cases.length; ++i) {
+ obj = cases[i];
+ val = obj.value;
+ exp = obj.expect;
+ got = func(val);
+ t.eq(got, exp, "'" + val + "' returns " + exp);
+ }
+
+ }
+
+ function test_Number_numericIf(t) {
+ var cases = [
+ {value: "3", expect: 3, expectWithTrim: 3},
+ {value: "+3", expect: 3, expectWithTrim: 3},
+ {value: "-3", expect: -3, expectWithTrim: -3},
+ {value: "3.0", expect: 3, expectWithTrim: 3},
+ {value: "+3.0", expect: 3, expectWithTrim: 3},
+ {value: "-3.0", expect: -3, expectWithTrim: -3},
+ {value: "6.02e23", expect: 6.02e23, expectWithTrim: 6.02e23},
+ {value: "+1.0e-100", expect: 1e-100, expectWithTrim: 1e-100},
+ {value: "-1.0e+100", expect: -1e100, expectWithTrim: -1e100},
+ {value: "1E100", expect: 1e100, expectWithTrim: 1e100},
+ {value: null, expect: null, expectWithTrim: null},
+ {value: true, expect: true, expectWithTrim: true},
+ {value: false, expect: false, expectWithTrim: false},
+ {value: undefined, expect: undefined, expectWithTrim: undefined},
+ {value: "", expect: "", expectWithTrim: ""},
+ {value: "3 ", expect: "3 ", expectWithTrim: 3},
+ {value: " 3", expect: " 3", expectWithTrim: 3},
+ {value: "1e", expect: "1e", expectWithTrim: "1e"},
+ {value: "1+e", expect: "1+e", expectWithTrim: "1+e"},
+ {value: "1-e", expect: "1-e", expectWithTrim: "1-e"},
+ {value: " 27 ", expect: " 27 ", expectWithTrim: 27},
+ {value: " abc ", expect: " abc ", expectWithTrim: " abc "}
+ ];
+ t.plan(cases.length*2);
+
+ var func = OpenLayers.String.numericIf;
+ var obj, val, got, exp;
+ for(var i=0; i<cases.length; ++i) {
+ obj = cases[i];
+ val = obj.value;
+ exp = obj.expect;
+ got = func(val);
+ t.eq(got, exp, "'" + val + "' returns " + exp);
+ got = func(val, true);
+ exp = obj.expectWithTrim;
+ t.eq(got, exp, "'" + val + "' returns " + exp + " with trimWhitespace true");
+ }
+ }
+
+
+ function test_Number_limitSigDigs(t) {
+ t.plan(9);
+
+ var num = 123456789;
+ t.eq(OpenLayers.Number.limitSigDigs(num), 0, "passing 'null' as sig returns 0");
+ t.eq(OpenLayers.Number.limitSigDigs(num, -1), 0, "passing -1 as sig returns 0");
+ t.eq(OpenLayers.Number.limitSigDigs(num, 0), 0, "passing 0 as sig returns 0");
+
+ t.eq(OpenLayers.Number.limitSigDigs(num, 15), 123456789, "passing sig greater than num digits in number returns number unmodified");
+
+ t.eq(OpenLayers.Number.limitSigDigs(num, 1), 100000000, "passing sig 1 works");
+ t.eq(OpenLayers.Number.limitSigDigs(num, 3), 123000000, "passing middle sig works (rounds down)");
+ t.eq(OpenLayers.Number.limitSigDigs(num, 5), 123460000, "passing middle sig works (rounds up)");
+ t.eq(OpenLayers.Number.limitSigDigs(num, 9), 123456789, "passing sig equal to num digits in number works");
+
+ num = 1234.56789;
+ t.eq(OpenLayers.Number.limitSigDigs(num, 5), 1234.6, "running limSigDig() on a floating point number works fine");
+
+ }
+
+ function test_Number_format(t) {
+ t.plan(9);
+ var format = OpenLayers.Number.format;
+ t.eq(format(12345), "12,345", "formatting an integer number works");
+ t.eq(format(12345, 3), "12,345.000", "zero padding an integer works");
+ t.eq(format(12345, null, ","), "12,345", "adding thousands separator to an integer works");
+ t.eq(format(12345, 0, ","), "12,345", "adding thousands separator to an integer with defined 0 decimal places works");
+
+ var num = 12345.6789
+ t.eq(format(num, null, "", ","), "12345,6789", "only changing decimal separator and leaving everything else untouched works");
+ t.eq(format(num, 5), "12,345.67890", "filling up decimals with trailing zeroes works");
+ t.eq(format(num, 3, ".", ","), "12.345,679", "rounding and changing decimal/thousands separator in function call works");
+ t.eq(format(num, 0, ""), "12346", "empty thousands separator in function call works");
+ OpenLayers.Number.thousandsSeparator = ".";
+ OpenLayers.Number.decimalSeparator = ",";
+ t.eq(format(num, 3), "12.345,679", "changing thousands/decimal separator globally works");
+ }
+
+ function test_Number_zeroPad(t) {
+ t.plan(6);
+ var pad = OpenLayers.Number.zeroPad;
+ t.eq(pad(15, 4), "0015", "left padding works");
+ t.eq(pad(15, 2), "15", "no left padding when equal to number of digits");
+ t.eq(pad(15, 1), "15", "no left padding when less than number of digits");
+ t.eq(pad(10, 5, 2), "01010", "radix modified and padding works");
+ t.eq(pad(10, 5, 8), "00012", "radix modified and padding works");
+ t.eq(pad(10, 5, 36), "0000a", "radix modified and padding works");
+ }
+
+ function test_Function_bind(t) {
+ t.plan(12);
+
+ g_obj = {};
+ g_Arg1 = {};
+ g_Arg2 = {};
+ g_Arg3 = {};
+ g_Arg4 = {};
+ var foo = function(x,y,z,a) {
+ t.ok(this == g_obj, "context correctly set");
+ t.ok(x == g_Arg1, "arg1 passed correctly");
+ t.ok(y == g_Arg2, "arg2 passed correctly");
+ t.ok(z == g_Arg3, "arg3 passed correctly");
+ t.ok(a == g_Arg4, "arg4 passed correctly");
+ t.eq(arguments.length, 4, "correct number of arguments ((regression test for #876))");
+ };
+
+ var newFoo = OpenLayers.Function.bind(foo, g_obj, g_Arg1, g_Arg2);
+
+ newFoo(g_Arg3, g_Arg4);
+
+ //run again to make sure the arguments are handled correctly
+ newFoo(g_Arg3, g_Arg4);
+ }
+
+ function test_Function_Void(t) {
+
+ t.plan(1);
+ t.eq(OpenLayers.Function.Void(), undefined, "returns undefined");
+
+ }
+
+ function test_Function_bindAsEventListener(t) {
+ t.plan(4);
+
+ g_obj = {};
+ g_Event = {};
+ g_WindowEvent = {};
+
+ var foo = function(x) {
+ t.ok(this == g_obj, "context correctly set");
+ g_X = x;
+ };
+
+ var newFoo = OpenLayers.Function.bindAsEventListener(foo, g_obj);
+
+
+ g_X = null;
+ newFoo(g_Event);
+ t.ok(g_X == g_Event, "event properly passed as first argument when event specified");
+
+ g_X = null;
+ newFoo();
+ t.ok(g_X == window.event, "window.event properly passed as first argument when nothing specified");
+ }
+
+ function test_Array_filter(t) {
+
+ t.plan(8);
+
+ OpenLayers.Array.filter(["foo"], function(item, index, array) {
+ t.eq(item, "foo", "callback called with proper item");
+ t.eq(index, 0, "callback called with proper index");
+ t.eq(array, ["foo"], "callback called with proper array");
+ t.eq(this, {"foo": "bar"}, "callback called with this set properly");
+ }, {"foo": "bar"});
+
+ var array = [0, 1, 2, 3];
+ var select = OpenLayers.Array.filter(array, function(value) {
+ return value > 1;
+ });
+ t.eq(select, [2, 3], "filter works for basic callback");
+ t.eq(array, [0, 1, 2, 3], "filter doesn't modify original");
+
+ var obj = {
+ test: function(value) {
+ if(value > 1) {
+ return true;
+ }
+ }
+ };
+ var select = OpenLayers.Array.filter(array, function(value) {
+ return this.test(value);
+ }, obj);
+ t.eq(select, [2, 3], "filter works for callback and caller");
+ t.eq(array, [0, 1, 2, 3], "filter doesn't modify original");
+
+
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/BaseTypes/Bounds.html b/misc/openlayers/tests/BaseTypes/Bounds.html
new file mode 100644
index 0000000..bdfeaf2
--- /dev/null
+++ b/misc/openlayers/tests/BaseTypes/Bounds.html
@@ -0,0 +1,738 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var bounds;
+ function test_Bounds_constructor (t) {
+ t.plan( 26 );
+
+ bounds = new OpenLayers.Bounds();
+ t.ok( bounds instanceof OpenLayers.Bounds, "new OpenLayers.Bounds returns Bounds object" );
+ t.eq( bounds.CLASS_NAME, "OpenLayers.Bounds", "bounds.CLASS_NAME is set correctly" );
+ t.eq( bounds.left, null, "bounds.left is initialized to null" );
+ t.eq( bounds.bottom, null, "bounds.bottom is initialized to null" );
+ t.eq( bounds.right, null, "bounds.right is initialized to null" );
+ t.eq( bounds.top, null, "bounds.top is initialized to null" );
+
+
+ bounds = new OpenLayers.Bounds(0,2,10,4);
+ t.ok( bounds instanceof OpenLayers.Bounds, "new OpenLayers.Bounds returns Bounds object" );
+ t.eq( bounds.CLASS_NAME, "OpenLayers.Bounds", "bounds.CLASS_NAME is set correctly" );
+ t.eq( bounds.left, 0, "bounds.left is set correctly" );
+ t.eq( bounds.bottom, 2, "bounds.bottom is set correctly" );
+ t.eq( bounds.right, 10, "bounds.right is set correctly" );
+ t.eq( bounds.top, 4, "bounds.top is set correctly" );
+ t.eq( bounds.getWidth(), 10, "bounds.getWidth() returns correct value" );
+ t.eq( bounds.getHeight(), 2, "bounds.getHeight() returns correct value" );
+
+ var sz = bounds.getSize();
+ var size = new OpenLayers.Size(10,2);
+ t.ok(sz.equals(size),"bounds.getSize() has correct value" );
+
+ var center = new OpenLayers.Pixel(5,3);
+ var boundsCenter = bounds.getCenterPixel();
+ t.ok( boundsCenter.equals(center), "bounds.getCenterLonLat() has correct value" );
+
+ var center = new OpenLayers.LonLat(5,3);
+ var boundsCenter = bounds.getCenterLonLat();
+ t.ok( boundsCenter.equals(center), "bounds.getCenterLonLat() has correct value" );
+
+ // This is an actual use case with Mercator projection at global scale
+ bounds = new OpenLayers.Bounds(-40075016.67999999,-20037508.339999992,
+ 40075016.67999999,20037508.339999992);
+ t.eq( bounds.left, -40075016.68, "bounds.left adjusted for floating precision");
+ t.eq( bounds.bottom, -20037508.34, "bounds.bottom adjusted for floating precision");
+ t.eq( bounds.right, 40075016.68, "bounds.right adjusted for floating precision");
+ t.eq( bounds.top, 20037508.34, "bounds.top adjusted for floating precision");
+
+ // allow construction from a single arg
+ bounds = new OpenLayers.Bounds([-180, -90, 180, 90]);
+ t.ok(bounds instanceof OpenLayers.Bounds, "(array) correct instance");
+ t.eq(bounds.left, -180, "(array) left");
+ t.eq(bounds.bottom, -90, "(array) bottom");
+ t.eq(bounds.right, 180, "(array) right");
+ t.eq(bounds.top, 90, "(array) top");
+
+ }
+
+ function test_Bounds_constructorFromStrings(t) {
+ t.plan( 6 );
+ bounds = new OpenLayers.Bounds("0","2","10","4");
+ t.ok( bounds instanceof OpenLayers.Bounds, "new OpenLayers.Bounds returns Bounds object" );
+ t.eq( bounds.CLASS_NAME, "OpenLayers.Bounds", "bounds.CLASS_NAME is set correctly" );
+ t.eq( bounds.left, 0, "bounds.left is set correctly" );
+ t.eq( bounds.bottom, 2, "bounds.bottom is set correctly" );
+ t.eq( bounds.right, 10, "bounds.right is set correctly" );
+ t.eq( bounds.top, 4, "bounds.top is set correctly" );
+
+ }
+
+ function test_Bounds_toBBOX(t) {
+ t.plan( 5 );
+ bounds = new OpenLayers.Bounds(1,2,3,4);
+ t.eq( bounds.toBBOX(), "1,2,3,4", "toBBOX() returns correct value." );
+ bounds = new OpenLayers.Bounds(1.00000001,2,3,4);
+ t.eq( bounds.toBBOX(), "1,2,3,4", "toBBOX() rounds off small differences." );
+ bounds = new OpenLayers.Bounds(1.00000001,2.5,3,4);
+ t.eq( bounds.toBBOX(), "1,2.5,3,4", "toBBOX() returns correct value. for a half number" );
+ bounds = new OpenLayers.Bounds(1,2.5555555,3,4);
+ t.eq( bounds.toBBOX(), "1,2.555556,3,4", "toBBOX() rounds to correct value." );
+ bounds = new OpenLayers.Bounds(1,2.5555555,3,4);
+ t.eq( bounds.toBBOX(1), "1,2.6,3,4", "toBBOX() rounds to correct value with power provided." );
+ bounds = new OpenLayers.Bounds(1,2.5555555,3,4);
+ }
+
+ function test_Bounds_toString(t) {
+ t.plan( 1 );
+ bounds = new OpenLayers.Bounds(1,2,3,4);
+ t.eq( bounds.toString(), "1,2,3,4", "toString() returns correct value." );
+ }
+ function test_Bounds_toArray(t) {
+ t.plan( 1 );
+ bounds = new OpenLayers.Bounds(1,2,3,4);
+ t.eq( bounds.toArray(), [1,2,3,4], "toArray() returns correct value." );
+ }
+
+ function test_Bounds_toGeometry(t) {
+ t.plan(7);
+ var minx = Math.random();
+ var miny = Math.random();
+ var maxx = Math.random();
+ var maxy = Math.random();
+ var bounds = new OpenLayers.Bounds(minx, miny, maxx, maxy);
+ var poly = bounds.toGeometry();
+ t.eq(poly.CLASS_NAME, "OpenLayers.Geometry.Polygon",
+ "polygon instance created");
+ t.eq(poly.components.length, 1,
+ "polygon with one ring created");
+ var ring = poly.components[0];
+ t.eq(ring.components.length, 5,
+ "four sided polygon created");
+ t.eq(ring.components[0].x, OpenLayers.Util.toFloat(minx),
+ "bounds left preserved");
+ t.eq(ring.components[0].y, OpenLayers.Util.toFloat(miny),
+ "bounds bottom preserved");
+ t.eq(ring.components[2].x, OpenLayers.Util.toFloat(maxx),
+ "bounds left preserved");
+ t.eq(ring.components[2].y, OpenLayers.Util.toFloat(maxy),
+ "bounds bottom preserved");
+ }
+
+ function test_Bounds_contains(t) {
+ t.plan( 6 );
+ bounds = new OpenLayers.Bounds(10,10,40,40);
+ t.eq( bounds.contains(20,20), true, "bounds(10,10,40,40) correctly contains LonLat(20,20)" );
+ t.eq( bounds.contains(0,0), false, "bounds(10,10,40,40) correctly does not contain LonLat(0,0)" );
+ t.eq( bounds.contains(40,40), true, "bounds(10,10,40,40) correctly contains LonLat(40,40) with inclusive set to true" );
+ t.eq( bounds.contains(40,40, false), false, "bounds(10,10,40,40) correctly does not contain LonLat(40,40) with inclusive set to false" );
+
+ var px = new OpenLayers.Pixel(15,30);
+ t.eq( bounds.containsPixel(px), bounds.contains(px.x, px.y), "containsPixel works");
+
+ var ll = new OpenLayers.LonLat(15,30);
+ t.eq( bounds.containsLonLat(ll), bounds.contains(ll.lon, ll.lat), "containsLonLat works");
+
+ }
+
+ function test_containsLonLat_wraped(t) {
+
+ var worldBounds = new OpenLayers.Bounds(-180, -90, 180, 90);
+
+ var cases = [{
+ ll: [0, 0], bbox: [-10, -10, 10, 10], contained: true
+ }, {
+ ll: [20, 0], bbox: [-10, -10, 10, 10], contained: false
+ }, {
+ ll: [360, 0], bbox: [-10, -10, 10, 10], contained: true
+ }, {
+ ll: [380, 0], bbox: [-10, -10, 10, 10], contained: false
+ }, {
+ ll: [725, 5], bbox: [-10, -10, 10, 10], contained: true
+ }, {
+ ll: [-355, -5], bbox: [-10, -10, 10, 10], contained: true
+ }, {
+ ll: [-715, 5], bbox: [-10, -10, 10, 10], contained: true
+ }, {
+ ll: [-735, 5], bbox: [-10, -10, 10, 10], contained: false
+ }, {
+ ll: [-180 * 50, 5], bbox: [-10, -10, 10, 10], contained: true
+ }];
+
+ var len = cases.length;
+ t.plan(len);
+
+ var c, bounds, loc;
+ for (var i=0; i<len; ++i) {
+ c = cases[i];
+ loc = new OpenLayers.LonLat(c.ll[0], c.ll[1]);
+ bounds = new OpenLayers.Bounds.fromArray(c.bbox);
+ t.eq(bounds.containsLonLat(loc, {worldBounds: worldBounds}), c.contained, "case " + i);
+ }
+
+ }
+
+ function test_Bounds_fromString(t) {
+ t.plan( 12 );
+ bounds = OpenLayers.Bounds.fromString("1,2,3,4");
+ t.ok( bounds instanceof OpenLayers.Bounds, "new OpenLayers.Bounds returns Bounds object" );
+ t.eq( bounds.left, 1, "bounds.left is set correctly" );
+ t.eq( bounds.bottom, 2, "bounds.bottom is set correctly" );
+ t.eq( bounds.right, 3, "bounds.right is set correctly" );
+ t.eq( bounds.top, 4, "bounds.top is set correctly" );
+
+ // reverse axis order
+ var reverseBbox = bounds.toBBOX(null, true);
+ t.eq(reverseBbox, "2,1,4,3", "toBBOX with reverseAxisOrder set to true works as expected");
+ var boundsFromReverse = OpenLayers.Bounds.fromString(reverseBbox, true);
+ t.ok(bounds.equals(boundsFromReverse), "Bounds created from string with reverseAxisOrder are correct");
+
+ bounds = OpenLayers.Bounds.fromString("1.1,2.2,3.3,4.4");
+ t.ok( bounds instanceof OpenLayers.Bounds, "new OpenLayers.Bounds returns Bounds object" );
+ t.eq( bounds.left, 1.1, "bounds.left is set correctly" );
+ t.eq( bounds.bottom, 2.2, "bounds.bottom is set correctly" );
+ t.eq( bounds.right, 3.3, "bounds.right is set correctly" );
+ t.eq( bounds.top, 4.4, "bounds.top is set correctly" );
+ }
+
+ function test_Bounds_getSize(t) {
+ t.plan( 1 );
+ var bounds = new OpenLayers.Bounds(0,10,100,120);
+
+ t.ok( bounds.getSize().equals(new OpenLayers.Size(100, 110)), "getCenterPixel() works correctly");
+ }
+
+ function test_Bounds_clone(t) {
+ t.plan( 6 );
+ var oldBounds = new OpenLayers.Bounds(1,2,3,4);
+ var bounds = oldBounds.clone();
+ t.ok( bounds instanceof OpenLayers.Bounds, "clone returns new OpenLayers.Bounds object" );
+ t.eq( bounds.left, 1, "bounds.left is set correctly" );
+ t.eq( bounds.bottom, 2, "bounds.bottom is set correctly" );
+ t.eq( bounds.right, 3, "bounds.right is set correctly" );
+ t.eq( bounds.top, 4, "bounds.top is set correctly" );
+
+ oldBounds.left = 100;
+ t.eq( bounds.left, 1, "changing olBounds.left does not change bounds.left" );
+ }
+
+ function test_Bounds_intersectsBounds(t) {
+ t.plan(21);
+
+ var aBounds = new OpenLayers.Bounds(-180, -90, 180, 90);
+
+ //inside
+ var bBounds = new OpenLayers.Bounds(-20, -10, 20, 10);
+ var cBounds = new OpenLayers.Bounds(-181,-90,180,90);
+ t.eq( aBounds.intersectsBounds(bBounds), true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + ")" );
+ t.eq( aBounds.intersectsBounds(bBounds, true), true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + "), inclusive is true" );
+ t.eq( aBounds.intersectsBounds(bBounds, false), true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + "), inclusive is false" );
+ t.eq( aBounds.intersectsBounds(cBounds, false), true, "aBounds with cBounds adjusted one degree left passes intersect bounds. (3 sides match, 1 side different)." );
+ t.eq( cBounds.intersectsBounds(aBounds, false), true, "cBounds with aBounds adjusted one degree left passes intersect bounds. (3 sides match, 1 side different)." );
+
+ //outside
+ bBounds = new OpenLayers.Bounds(-181, -91, 181, 91);
+ t.eq( aBounds.intersectsBounds(bBounds), true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + ")" );
+ t.eq( aBounds.intersectsBounds(bBounds, true), true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + "), inclusive is true" );
+ t.eq( aBounds.intersectsBounds(bBounds, false), true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + "), inclusive is false" );
+
+ //total intersect
+ bBounds = new OpenLayers.Bounds(-185, -100, 20, 50);
+ t.eq( aBounds.intersectsBounds(bBounds), true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + ")" );
+ t.eq( aBounds.intersectsBounds(bBounds, true), true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + "), inclusive is true" );
+ t.eq( aBounds.intersectsBounds(bBounds, false), true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + "), inclusive is false" );
+
+ //border intersect
+ bBounds = new OpenLayers.Bounds(-360, -180, -180, -90);
+ t.eq( aBounds.intersectsBounds(bBounds), true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + ")" );
+ t.eq( aBounds.intersectsBounds(bBounds, true), true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + "), inclusive is true" );
+ t.eq( aBounds.intersectsBounds(bBounds, false), false, "(" + aBounds.toBBOX() + ") does not intersect (" + bBounds.toBBOX() + "), inclusive is false" );
+
+ //no intersect
+ bBounds = new OpenLayers.Bounds(-360, -180, -185, -95);
+ t.eq( aBounds.intersectsBounds(bBounds), false, "(" + aBounds.toBBOX() + ") does not intersect (" + bBounds.toBBOX() + ")" );
+ t.eq( aBounds.intersectsBounds(bBounds, true), false, "(" + aBounds.toBBOX() + ") does not intersect (" + bBounds.toBBOX() + "), inclusive is true" );
+ t.eq( aBounds.intersectsBounds(bBounds, false), false, "(" + aBounds.toBBOX() + ") does not intersect (" + bBounds.toBBOX() + "), inclusive is false" );
+
+ // This is an actual use case with Mercator tiles at global scale
+ var merc_aBounds = new OpenLayers.Bounds(-40075016.67999999,20037508.339999992,
+ -20037508.339999992,40075016.67999999),
+ merc_bBounds = new OpenLayers.Bounds(-20037508.34,-20037508.34,
+ 20037508.34,20037508.34);
+ t.eq( merc_aBounds.intersectsBounds(merc_bBounds, true), true, "intersect shouldn't fall prey to floating point errors, inclusive is true");
+ t.eq( merc_aBounds.intersectsBounds(merc_bBounds, false), false, "intersect shouldn't fall prey to floating point errors, inclusive is false");
+
+ // test for bounds intersection where none of the corners are contained within the other bounds
+ var b1 = new OpenLayers.Bounds(-1, -2, 1, 2);
+ var b2 = new OpenLayers.Bounds(-2, -1, 2, 1);
+ t.eq(b1.intersectsBounds(b2), true, "vertical rectangle intersects horizontal rectangle");
+ t.eq(b2.intersectsBounds(b1), true, "horizontal rectangle intersects vertical rectangle");
+
+ }
+
+ function test_Bounds_containsBounds(t) {
+ t.plan( 35 );
+ containerBounds = new OpenLayers.Bounds(10,10,40,40);
+
+ //totally outside
+ bounds = new OpenLayers.Bounds(0,0,5,5);
+ t.eq( containerBounds.containsBounds(bounds) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ")");
+ t.eq( containerBounds.containsBounds(bounds, false) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false" );
+ t.eq( containerBounds.containsBounds(bounds, false, true) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false, inclusive is true" );
+ t.eq( containerBounds.containsBounds(bounds, false, false), false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false, inclusive is false" );
+ t.eq( containerBounds.containsBounds(bounds, true) , false , "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is true" );
+ t.eq( containerBounds.containsBounds(bounds, true, true) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is true, inclusive is true" );
+ t.eq( containerBounds.containsBounds(bounds, true, false) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is true, inclusive is false" );
+
+ //totally outside on border
+ bounds = new OpenLayers.Bounds(15,0,30,10);
+ t.eq( containerBounds.containsBounds(bounds) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ")");
+ t.eq( containerBounds.containsBounds(bounds, false) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false" );
+ t.eq( containerBounds.containsBounds(bounds, false, true) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false, inclusive is true" );
+ t.eq( containerBounds.containsBounds(bounds, false, false), false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false, inclusive is false" );
+ t.eq( containerBounds.containsBounds(bounds, true) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true" );
+ t.eq( containerBounds.containsBounds(bounds, true, true) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true, inclusive is true" );
+ t.eq( containerBounds.containsBounds(bounds, true, false) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is true, inclusive is false" );
+
+ //partially inside
+ bounds = new OpenLayers.Bounds(20,20,50,30);
+ t.eq( containerBounds.containsBounds(bounds) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ")");
+ t.eq( containerBounds.containsBounds(bounds, false) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false" );
+ t.eq( containerBounds.containsBounds(bounds, false, true) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false, inclusive is true" );
+ t.eq( containerBounds.containsBounds(bounds, false, false), false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false, inclusive is false" );
+ t.eq( containerBounds.containsBounds(bounds, true) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true" );
+ t.eq( containerBounds.containsBounds(bounds, true, true) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true, inclusive is true" );
+ t.eq( containerBounds.containsBounds(bounds, true, false) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true, inclusive is false" );
+
+ //totally inside on border
+ bounds = new OpenLayers.Bounds(10,20,30,30);
+ t.eq( containerBounds.containsBounds(bounds) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ")");
+ t.eq( containerBounds.containsBounds(bounds, false) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is false" );
+ t.eq( containerBounds.containsBounds(bounds, false, true) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is false, inclusive is true" );
+ t.eq( containerBounds.containsBounds(bounds, false, false), false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false, inclusive is false" );
+ t.eq( containerBounds.containsBounds(bounds, true) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true" );
+ t.eq( containerBounds.containsBounds(bounds, true, true) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true, inclusive is true" );
+ t.eq( containerBounds.containsBounds(bounds, true, false) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true, inclusive is false" );
+
+ //totally inside
+ bounds = new OpenLayers.Bounds(20,20,30,30);
+ t.eq( containerBounds.containsBounds(bounds) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ")");
+ t.eq( containerBounds.containsBounds(bounds, false) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is false" );
+ t.eq( containerBounds.containsBounds(bounds, false, true) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is false, inclusive is true" );
+ t.eq( containerBounds.containsBounds(bounds, false, false), true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is false, inclusive is false" );
+ t.eq( containerBounds.containsBounds(bounds, true) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true" );
+ t.eq( containerBounds.containsBounds(bounds, true, true) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true, inclusive is true" );
+ t.eq( containerBounds.containsBounds(bounds, true, false) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true, inclusive is false" );
+
+ }
+
+ function test_Bounds_determineQuadrant(t) {
+
+ t.plan( 4 );
+ var bounds = new OpenLayers.Bounds(0,0,100,100);
+
+ var tl = new OpenLayers.LonLat(25, 75);
+ var tr = new OpenLayers.LonLat(75, 75);
+ var bl = new OpenLayers.LonLat(25, 25);
+ var br = new OpenLayers.LonLat(75, 25);
+
+ t.eq( bounds.determineQuadrant(tl), "tl", "bounds.determineQuadrant correctly identifies a coordinate in the top left quadrant");
+ t.eq( bounds.determineQuadrant(tr), "tr", "bounds.determineQuadrant correctly identifies a coordinate in the top right quadrant");
+ t.eq( bounds.determineQuadrant(bl), "bl", "bounds.determineQuadrant correctly identifies a coordinate in the bottom left quadrant");
+ t.eq( bounds.determineQuadrant(br), "br", "bounds.determineQuadrant correctly identifies a coordinate in the bottom right quadrant");
+ }
+
+ function test_Bounds_oppositeQuadrant(t) {
+
+ t.plan( 4 );
+
+ t.eq( OpenLayers.Bounds.oppositeQuadrant("tl"), "br", "OpenLayers.Bounds.oppositeQuadrant returns 'br' for 'tl'");
+ t.eq( OpenLayers.Bounds.oppositeQuadrant("tr"), "bl", "OpenLayers.Bounds.oppositeQuadrant returns 'bl' for 'tr'");
+ t.eq( OpenLayers.Bounds.oppositeQuadrant("bl"), "tr", "OpenLayers.Bounds.oppositeQuadrant returns 'tr' for 'bl'");
+ t.eq( OpenLayers.Bounds.oppositeQuadrant("br"), "tl", "OpenLayers.Bounds.oppositeQuadrant returns 'tl' for 'br'");
+ }
+
+ function test_Bounds_equals(t) {
+ t.plan( 3 );
+ var boundsA = new OpenLayers.Bounds(1,2,3,4);
+ var boundsB = new OpenLayers.Bounds(1,2,3,4);
+ var boundsC = new OpenLayers.Bounds(1,5,3,4);
+
+ t.ok( boundsA.equals(boundsB), "equals() returns true on two equal bounds." );
+ t.ok( !boundsA.equals(boundsC), "equals() returns false on two different bounds." );
+ t.ok( !boundsA.equals(null), "equals() returns false on comparison to null");
+ }
+
+ function test_Bounds_getHeight_getWidth(t) {
+ t.plan( 2 );
+ var bounds = new OpenLayers.Bounds(10,20,100,120);
+
+ t.eq( bounds.getWidth(), 90, "getWidth() works" );
+ t.eq( bounds.getHeight(), 100, "getHeight() works" );
+
+ }
+
+ function test_Bounds_getCenters(t) {
+ t.plan( 2 );
+ var bounds = new OpenLayers.Bounds(0,20,100,120);
+
+ t.ok( bounds.getCenterPixel().equals(new OpenLayers.Pixel(50, 70)), "getCenterPixel() works correctly");
+ t.ok( bounds.getCenterLonLat().equals(new OpenLayers.LonLat(50, 70)), "getCenterLonLat() works correctly");
+ }
+
+ function test_getCenterLonLat(t) {
+ t.plan(7);
+ var bounds = new OpenLayers.Bounds(0, 10, 20, 60);
+
+ // set private centerLonLat to confirm that it is getting returned if set
+ bounds.centerLonLat = "foo";
+ t.eq(bounds.getCenterLonLat(), "foo", "returns cached value");
+ bounds.centerLonLat = null;
+
+ // unmodified
+ var center = bounds.getCenterLonLat();
+ t.eq(center.lon, 10, "unmodified: correct x");
+ t.eq(center.lat, 35, "unmodified: correct y");
+
+ // transformed
+ bounds.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"));
+ center = bounds.getCenterLonLat();
+ t.eq(Math.round(center.lon), 1113195, "transformed: correct x");
+ t.eq(Math.round(center.lat), 4759314, "transformed: correct y");
+
+ // extended
+ bounds.extend(new OpenLayers.Bounds(-10000000, -10000000, 10000000, 10000000));
+ center = bounds.getCenterLonLat();
+ t.eq(center.lon, 0, "extended: correct x");
+ t.eq(center.lat, 0, "extended: correct y");
+
+
+ }
+
+ function test_Bounds_fromArray(t) {
+ t.plan( 7 );
+
+ var bbox = [1,2,3,4];
+ bounds = OpenLayers.Bounds.fromArray(bbox);
+ t.ok( bounds instanceof OpenLayers.Bounds, "new OpenLayers.Bounds returns Bounds object" );
+ t.eq( bounds.left, 1, "bounds.left is set correctly" );
+ t.eq( bounds.bottom, 2, "bounds.bottom is set correctly" );
+ t.eq( bounds.right, 3, "bounds.right is set correctly" );
+ t.eq( bounds.top, 4, "bounds.top is set correctly" );
+
+ // reverse axis order
+ var reverseBbox = bounds.toArray(true);
+ t.eq(reverseBbox, [2,1,4,3], "toArray with reverseAxisOrder set to true works as expected");
+ var boundsFromReverse = OpenLayers.Bounds.fromArray(reverseBbox, true);
+ t.ok(bounds.equals(boundsFromReverse), "Bounds created from array with reverseAxisOrder are correct");
+ }
+
+ function test_Bounds_fromSize(t) {
+ t.plan( 5 );
+
+ var height = 15;
+ var width = 16;
+ var size = new OpenLayers.Size(width, height);
+ bounds = OpenLayers.Bounds.fromSize(size);
+ t.ok( bounds instanceof OpenLayers.Bounds, "new OpenLayers.Bounds returns Bounds object" );
+ t.eq( bounds.left, 0, "bounds.left is set correctly" );
+ t.eq( bounds.bottom, height, "bounds.bottom is set correctly" );
+ t.eq( bounds.right, width, "bounds.right is set correctly" );
+ t.eq( bounds.top, 0, "bounds.top is set correctly" );
+ }
+
+
+ function test_Bounds_extend(t) {
+ t.plan( 9 );
+
+ // null bounds to start
+ var originalBounds = new OpenLayers.Bounds();
+ var bounds = originalBounds.clone();
+
+ bounds.extend(new OpenLayers.LonLat(4,5));
+ t.ok(bounds.equals(new OpenLayers.Bounds(4,5,4,5)), "uninitialized bounds can be safely extended");
+
+ // extend with null obj
+ originalBounds = new OpenLayers.Bounds(10,20,50,80);
+ bounds = originalBounds.clone();
+
+ bounds.extend(null);
+ t.ok(bounds.equals(originalBounds), "null to extend does not crash or change original bounds");
+
+ // obj with no classname
+ var object = {};
+ bounds.extend(object);
+ t.ok(bounds.equals(originalBounds), "extend() passing object with no classname does not crash or change original bounds")
+
+
+ // obj is bounds
+
+ // pushing all limits with bounds obj
+ var testBounds = new OpenLayers.Bounds(5, 10, 60, 90);
+ object = testBounds.clone();
+
+ bounds.extend(object);
+ t.ok(bounds.equals(testBounds), "extend by valid bounds, pushing all limits, correctly extends bounds");
+
+ // pushing no limits with bounds obj
+ bounds = originalBounds.clone();
+
+ testBounds = new OpenLayers.Bounds(15, 30, 40, 70);
+ object = testBounds.clone();
+
+ bounds.extend(object);
+ t.ok(bounds.equals(originalBounds), "extend by valid bounds, pushing no limits, correctly does not extend bounds");
+
+
+ // obj is lonlat
+
+ // left, bottom
+ bounds = originalBounds.clone();
+
+ object = new OpenLayers.LonLat(5, 10);
+
+ bounds.extend(object);
+
+ t.ok( ((bounds.left == object.lon) &&
+ (bounds.bottom == object.lat) &&
+ (bounds.right == originalBounds.right) &&
+ (bounds.top == originalBounds.top)), "obj lonlat to extends correctly modifies left and bottom");
+
+ // right, top
+ bounds = originalBounds.clone();
+
+ object = new OpenLayers.LonLat(60,90);
+
+ bounds.extend(object);
+
+ t.ok( ((bounds.left == originalBounds.left) &&
+ (bounds.bottom == originalBounds.bottom) &&
+ (bounds.right == object.lon) &&
+ (bounds.top == object.lat)), "obj lonlat to extends correctly modifies right and top");
+
+
+ // obj is point
+
+ // left, bottom
+ bounds = originalBounds.clone();
+
+ object = new OpenLayers.Geometry.Point(5, 10);
+
+ bounds.extend(object);
+
+ t.ok( ((bounds.left == object.x) &&
+ (bounds.bottom == object.y) &&
+ (bounds.right == originalBounds.right) &&
+ (bounds.top == originalBounds.top)), "obj Point to extends correctly modifies left and bottom");
+
+ // right, top
+ bounds = originalBounds.clone();
+
+ object = new OpenLayers.Geometry.Point(60,90);
+
+ bounds.extend(object);
+
+ t.ok( ((bounds.left == originalBounds.left) &&
+ (bounds.bottom == originalBounds.bottom) &&
+ (bounds.right == object.x) &&
+ (bounds.top == object.y)), "obj Point to extends correctly modifies right and top");
+
+ }
+
+
+ function test_Bounds_extendXY(t) {
+ t.plan(3);
+
+ // null bounds to start
+ var originalBounds = new OpenLayers.Bounds();
+
+ var bounds = originalBounds.clone();
+ bounds.extendXY(4, 5);
+
+ t.ok(bounds.equals(new OpenLayers.Bounds(4,5,4,5)), "uninitialized bounds can be safely extended");
+
+ // left, bottom
+ originalBounds = new OpenLayers.Bounds(10,20,50,80);
+
+ bounds = originalBounds.clone();
+ bounds.extendXY(5, 10);
+
+ t.ok( ((bounds.left == 5) &&
+ (bounds.bottom == 10) &&
+ (bounds.right == originalBounds.right) &&
+ (bounds.top == originalBounds.top)), "extendXY correctly modifies left and bottom");
+
+ // right, top
+ bounds = originalBounds.clone();
+ bounds.extendXY(60, 90);
+
+ t.ok( ((bounds.left == originalBounds.left) &&
+ (bounds.bottom == originalBounds.bottom) &&
+ (bounds.right == 60) &&
+ (bounds.top == 90)), "extendXY correctly modifies right and top");
+ }
+
+
+ function test_Bounds_wrapDateLine(t) {
+ t.plan( 13 );
+
+ var testBounds, wrappedBounds, desiredBounds;
+
+ var maxExtent = new OpenLayers.Bounds(-10,-10,10,10);
+ var exactBounds = maxExtent.clone();
+ var simpleBounds = new OpenLayers.Bounds( -5,-5,5,5);
+
+
+
+ //bad maxextent
+ testBounds = simpleBounds.clone();
+ wrappedBounds = testBounds.wrapDateLine(null);
+ t.ok(wrappedBounds.equals(simpleBounds), "wrapping a bounds with a bad maxextent does nothing");
+
+
+
+ //exactly inside
+ testBounds = exactBounds.clone();
+ wrappedBounds = testBounds.wrapDateLine(maxExtent);
+ t.ok(wrappedBounds.equals(exactBounds), "wrapping a bounds precisely within (equal to) maxextent does nothing");
+
+
+ //inside
+ testBounds = simpleBounds.clone();
+ wrappedBounds = testBounds.wrapDateLine(maxExtent);
+ t.ok(wrappedBounds.equals(simpleBounds), "wrapping a bounds within maxextent does nothing");
+
+// LEFT //
+
+ //straddling left
+ testBounds = simpleBounds.add(-10,0);
+ wrappedBounds = testBounds.wrapDateLine(maxExtent);
+ t.ok(wrappedBounds.equals(testBounds), "wrapping a bounds that straddles the left of maxextent does nothing");
+
+ //left rightTolerance
+ testBounds = simpleBounds.add(-14,0);
+ wrappedBounds =
+ testBounds.wrapDateLine(maxExtent, {'rightTolerance': 1} );
+ desiredBounds = simpleBounds.add(6,0);
+ t.ok(wrappedBounds.equals(desiredBounds), "wrapping a bounds rightTolerance left of maxextent works");
+
+ //exactly left
+ testBounds = exactBounds.add(-20,0);
+ wrappedBounds = testBounds.wrapDateLine(maxExtent);
+ t.ok(wrappedBounds.equals(exactBounds), "wrapping an exact bounds once left of maxextent works");
+
+ //left
+ testBounds = simpleBounds.add(-20,0);
+ wrappedBounds = testBounds.wrapDateLine(maxExtent);
+ t.ok(wrappedBounds.equals(simpleBounds), "wrapping a bounds once left of maxextent works");
+
+ //way left
+ testBounds = simpleBounds.add(-200,0);
+ wrappedBounds = testBounds.wrapDateLine(maxExtent);
+ t.ok(wrappedBounds.equals(simpleBounds), "wrapping a bounds way left of maxextent works");
+
+// RIGHT //
+
+ //straddling right
+ testBounds = simpleBounds.add(10,0);
+ wrappedBounds = testBounds.wrapDateLine(maxExtent);
+ desiredBounds = testBounds.add(-maxExtent.getWidth(), 0)
+ t.ok(wrappedBounds.equals(desiredBounds), "wrapping a bounds that straddles the right of maxextent moves extent to left side of the world");
+
+ //right leftTolerance
+ testBounds = simpleBounds.add(14,0);
+ wrappedBounds =
+ testBounds.wrapDateLine(maxExtent, {'leftTolerance': 1} );
+ desiredBounds = simpleBounds.add(-6,0);
+ t.ok(wrappedBounds.equals(desiredBounds), "wrapping a bounds leftTolerance right of maxextent works");
+
+ //exactly right
+ testBounds = exactBounds.add(20,0);
+ wrappedBounds = testBounds.wrapDateLine(maxExtent);
+ t.ok(wrappedBounds.equals(exactBounds), "wrapping an exact bounds once right of maxextent works");
+
+ //right
+ testBounds = simpleBounds.add(20,0);
+ wrappedBounds = testBounds.wrapDateLine(maxExtent);
+ t.ok(wrappedBounds.equals(simpleBounds), "wrapping a bounds once right of maxextent works");
+
+ //way right
+ testBounds = simpleBounds.add(200,0);
+ wrappedBounds = testBounds.wrapDateLine(maxExtent);
+ t.ok(wrappedBounds.equals(simpleBounds), "wrapping a bounds way right of maxextent works");
+
+
+
+ }
+ function test_Bounds_transform(t) {
+ t.plan( 5 );
+ bounds = new OpenLayers.Bounds(10, -10, 20, 10);
+ bounds.transform(new OpenLayers.Projection("foo"), new OpenLayers.Projection("Bar"));
+ t.eq(bounds.toBBOX(), "10,-10,20,10", "null transform okay");
+ bounds.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"));
+ t.eq(bounds.toBBOX(), "1113194.907778,-1118889.974702,2226389.815556,1118889.974702", "bounds for spherical mercator transform are correct");
+ bounds.transform(new OpenLayers.Projection("EPSG:900913"), new OpenLayers.Projection("EPSG:4326"));
+ t.eq(bounds.toBBOX(), "10,-10,20,10", "bounds for inverse spherical mercator transform are correct");
+
+ // transform with string
+ bounds = new OpenLayers.Bounds(10, -10, 20, 10);
+ bounds.transform("EPSG:4326", "EPSG:900913");
+ t.eq(bounds.toBBOX(), "1113194.907778,-1118889.974702,2226389.815556,1118889.974702", "(string) bounds for spherical mercator transform are correct");
+ bounds.transform("EPSG:900913", "EPSG:4326");
+ t.eq(bounds.toBBOX(), "10,-10,20,10", "(string) bounds for inverse spherical mercator transform are correct");
+
+ }
+
+ function test_Bounds_add(t) {
+ t.plan( 6 );
+
+ origBounds = new OpenLayers.Bounds(1,2,3,4);
+ testBounds = origBounds.clone();
+
+ var bounds = testBounds.add(5, 50);
+ t.ok( testBounds.equals(origBounds), "testBounds is not modified by add operation");
+
+ var b = new OpenLayers.Bounds(6,52,8,54);
+ t.ok( bounds.equals(b), "bounds is set correctly");
+
+ //null values
+ try {
+ bounds = testBounds.add(null, 50);
+ } catch(e) {
+ t.ok("exception thrown when passing null value to add()");
+ }
+ t.ok( testBounds.equals(origBounds), "testBounds is not modified by erroneous add operation (null x)");
+
+ try {
+ bounds = testBounds.add(5, null);
+ } catch(e) {
+ t.ok("exception thrown when passing null value to add()");
+ }
+ t.ok( testBounds.equals(origBounds), "testBounds is not modified by erroneous add operation (null y)");
+ }
+
+ function test_Bounds_scale(t) {
+ t.plan(3);
+
+ origBounds = new OpenLayers.Bounds(1,2,3,4);
+ bounds = origBounds.scale(2);
+ var b = new OpenLayers.Bounds(0,1,4,5);
+ t.ok(bounds.equals(b), "Bounds scale correctly with default origin at center")
+
+ var origin = new OpenLayers.Pixel(0,1);
+ bounds = origBounds.scale(2,origin);
+ b = new OpenLayers.Bounds(2,3,6,7);
+ t.ok(bounds.equals(b), "Bounds scale correctly with offset origin");
+
+ origin = new OpenLayers.Pixel(5,1);
+ bounds = bounds.scale(2, origin);
+ b = new OpenLayers.Bounds(-1, 5, 7, 13);
+ t.ok(bounds.equals(b), "Bounds scale correctly with offset origin");
+
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
+
diff --git a/misc/openlayers/tests/BaseTypes/Class.html b/misc/openlayers/tests/BaseTypes/Class.html
new file mode 100644
index 0000000..11d5826
--- /dev/null
+++ b/misc/openlayers/tests/BaseTypes/Class.html
@@ -0,0 +1,350 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_Class(t) {
+ t.plan(1);
+ var MyClass = OpenLayers.Class({
+ initialize: function () {
+ t.ok(false, "initialize should not be called");
+ }
+ });
+ t.ok(true,
+ "defining a class does not call the constructor for the class");
+ }
+
+ function test_Class_constructor(t) {
+ t.plan(7);
+
+ var MyClass = OpenLayers.Class({
+ prop: null,
+ classProp: {'bad': 'practice'},
+ initialize: function(a1, a2) {
+ this.prop = "instance property";
+ t.ok(true,
+ "initialize is called when a new instance is created");
+ t.eq(a1, arg1,
+ "initialize is called with the proper first argument");
+ t.eq(a2, arg2,
+ "initialize is called with the proper second argument");
+ },
+ CLASS_NAME: "MyClass"
+ });
+
+ var arg1 = "anArg";
+ var arg2 = {"another": "arg"};
+ var myObj = new MyClass(arg1, arg2);
+ t.eq(MyClass.prop, null,
+ "creating a new instance doesn't modify the class");
+ t.eq(myObj.prop, "instance property",
+ "the new instance is assigned a property in the constructor");
+ t.eq(myObj["CLASS_NAME"], "MyClass",
+ "the new object is an instance of MyClass");
+
+ // allow for modification of class properties
+ MyClass.prototype.classProp.bad = "good";
+ t.eq(myObj.classProp.bad, "good",
+ "modifying a class property modifies properties of the instance");
+ }
+
+ function test_Class_inheritance(t) {
+ t.plan(7);
+
+ var BaseClass = OpenLayers.Class({
+ prop: "base",
+ initialize: function() {
+ t.ok(false,
+ "base class constructor is not called during inheritance");
+ },
+ toString: function() {
+ return "toString inherited";
+ },
+ CLASS_NAME: "BaseClass"
+ });
+
+ var ChildClass = OpenLayers.Class(BaseClass, {
+ initialize: function() {
+ t.ok(true,
+ "child class constructor is called in creating an instance");
+ },
+ CLASS_NAME: "ChildClass"
+ });
+
+ var child = new ChildClass();
+ t.eq(child.prop, "base",
+ "instance of child inherits properties from base");
+ t.eq(child.toString(), "toString inherited",
+ "instance of child inherits toString method from base");
+ t.eq(child["CLASS_NAME"],
+ "ChildClass",
+ "new object is an instance of the child class");
+
+ var F = OpenLayers.Class(Object, {});
+ t.ok(!("initialize" in Object.prototype), "no messing with non OL prototypes");
+
+ // test with an abstract class (i.e. a class that doesn't have an initialize
+ // method) as the parent class
+ var Vehicule = OpenLayers.Class({
+ numWheels: null
+ });
+ var Bike = OpenLayers.Class(Vehicule, {
+ initialize: function() {
+ this.numWheels = 2;
+ }
+ });
+ var b = new Bike();
+ t.ok(b instanceof Vehicule, "a bike is a vehicule");
+
+ // test inheritance with something that has a non-function initialize property
+ var P = OpenLayers.Class({
+ initialize: "foo"
+ });
+ var C = OpenLayers.Class(P, {
+ initialize: function() {
+ // pass
+ }
+ });
+ var c = new C();
+ t.eq(P.prototype.initialize, "foo", "Class restores custom initialize property.");
+
+ }
+
+ function test_Class_multiple_inheritance(t) {
+ t.plan(7);
+ var BaseClass1 = OpenLayers.Class({
+ override: "base1",
+ prop: "base1",
+ variable: null,
+ initialize: function() {
+ t.ok(true,
+ "only called when an instance of this class is created");
+ },
+ CLASS_NAME: "BaseClass1"
+ });
+
+ var BaseClass2 = OpenLayers.Class({
+ override: "base2",
+ initialize: function() {
+ t.ok(false,
+ "base class constructor is not called during inheritance");
+ },
+ CLASS_NAME: "BaseClass1"
+ });
+
+ var ChildClass = OpenLayers.Class(BaseClass1, BaseClass2, {
+ initialize: function(arg) {
+ if(this.prop == "base1") {
+ this.variable = "child";
+ }
+ t.ok(true,
+ "only child class constructor is called on initialization");
+ },
+ CLASS_NAME: "ChildClass"
+ });
+
+ var arg = "child";
+ var child = new ChildClass(arg);
+ t.eq(child.variable, arg,
+ "inheritance works before construction");
+ t.eq(child.prop, "base1",
+ "properties are inherited with multiple classes")
+ t.eq(child.override, "base2",
+ "properties are inherited in the expected order");
+ t.eq(child["CLASS_NAME"],
+ "ChildClass",
+ "object is an instance of child class");
+
+ var base1 = new BaseClass1();
+ t.eq(base1.override, "base1",
+ "inheritance doesn't mess with parents");
+
+ }
+
+ function test_inheritance_chain(t) {
+ t.plan(1);
+ var A = new OpenLayers.Class({
+ initialize: function() {
+ this.a = 'foo';
+ }
+ });
+ var B = new OpenLayers.Class(A, {});
+ var C = new OpenLayers.Class(B, {
+ initialize: function() {
+ B.prototype.initialize.apply(this, arguments);
+ this.a = this.a + 'bar';
+ }
+ });
+ var c = new C;
+ t.eq(c.a, 'foobar', 'constructor at the root is called');
+ }
+
+
+ function test_Class_isInstanceOf(t) {
+ t.plan(7);
+ var wms = new OpenLayers.Layer.WMS({});
+ var drag = new OpenLayers.Control.DragFeature({});
+ t.ok(wms instanceof OpenLayers.Layer.WMS, "isInstanceOf(WMS)");
+ t.ok(wms instanceof OpenLayers.Layer, "isInstanceOf(Layer)");
+ t.ok(!(wms instanceof OpenLayers.Format), "not isInstanceOf(Format)");
+ t.ok(drag instanceof OpenLayers.Control, "drag is a control");
+ t.ok(!(drag instanceof OpenLayers.Layer), "drag is not a layer");
+
+ //test a class with multiple inheritance
+ var BadClass=OpenLayers.Class(OpenLayers.Layer.WMS, OpenLayers.Control.DragFeature);
+ var bad = new BadClass({});
+ t.ok(!(bad instanceof OpenLayers.Control), "bad is a control, but it is also a layer and we cannot have two superclasses");
+ t.ok(bad instanceof OpenLayers.Layer, "bad is a layer, it inherits from the layer first");
+ }
+
+ //
+ // IGN's GeoPortal API overwrite prototypes of OpenLayers constructors.
+ // The tests below aim to cover their usage pattens.
+ //
+
+ // the overwrite function under test
+ function overwrite(C, o) {
+ if(typeof o.initialize === "function" &&
+ C === C.prototype.initialize) {
+ // OL 2.11
+
+ var proto = C.prototype;
+ var staticProps = OpenLayers.Util.extend({}, C);
+
+ C = o.initialize;
+
+ C.prototype = proto;
+ OpenLayers.Util.extend(C, staticProps);
+ }
+ OpenLayers.Util.extend(C.prototype, o);
+ return C;
+ }
+
+ function test_overwrite_1(t) {
+ // overwrite constructor
+ t.plan(1);
+ var A = OpenLayers.Class({
+ initialize: function() {
+ this.a = "foo";
+ }
+ });
+ A = overwrite(A, {
+ initialize: function() {
+ this.a = "bar";
+ }
+ });
+ var a = new A;
+ t.eq(a.a, "bar", "ctor overwritten");
+ }
+
+ function test_overwrite_2(t) {
+ // overwrite regular method
+ t.plan(1);
+ var A = OpenLayers.Class({
+ initialize: function() {
+ },
+ method: function() {
+ this.a = "foo";
+ }
+ });
+ A = overwrite(A, {
+ method: function() {
+ this.a = "bar";
+ }
+ });
+ var a = new A;
+ a.method();
+ t.eq(a.a, "bar", "method overwritten");
+ }
+
+ function test_overwrite_3(t) {
+ // overwrite constructor of subclass
+ t.plan(1);
+ var A = OpenLayers.Class({
+ initialize: function() {
+ this.a = "foo";
+ }
+ });
+ var B = OpenLayers.Class(A, {
+ initialize: function() {
+ A.prototype.initialize.call(this);
+ }
+ });
+ B = overwrite(B, {
+ initialize: function() {
+ A.prototype.initialize.call(this);
+ this.a = "bar";
+ }
+ });
+ var b = new B;
+ t.eq(b.a, "bar", "ctor overwritten");
+ }
+
+ function test_overwrite_4(t) {
+ // overwrite constructor of parent class
+ t.plan(1);
+ var A = OpenLayers.Class({
+ initialize: function() {
+ this.a = "foo";
+ }
+ });
+ var B = OpenLayers.Class(A, {
+ initialize: function() {
+ A.prototype.initialize.call(this);
+ }
+ });
+ A = overwrite(A, {
+ initialize: function() {
+ this.a = "bar";
+ }
+ });
+ var b = new B;
+ t.eq(b.a, "bar", "ctor overwritten");
+ }
+
+ function test_overwrite_5(t) {
+ // overwrite constructor of parent class, which itself
+ // doesn't defined "initialize"
+ t.plan(2);
+ var A = OpenLayers.Class({
+ initialize: function() {
+ this.a = "foo";
+ }
+ });
+ var B = OpenLayers.Class(A, {});
+ var _A = A;
+ A = overwrite(A, {
+ initialize: function() {
+ this.a = "bar";
+ }
+ });
+ var b = new B;
+ t.ok(A.prototype === _A.prototype, "A and _A share the prototype");
+ t.eq(b.a, "bar", "ctor overwritten");
+ }
+
+ function test_overwrite_6(t) {
+ // with static methods
+ t.plan(1);
+ var A = OpenLayers.Class({
+ initialize: function() {
+ }
+ });
+ A.staticMethod = function() {};
+ A = overwrite(A, {
+ initialize: function() {
+ }
+ });
+ var exc = false;
+ try {
+ A.staticMethod();
+ } catch(e) {
+ exc = true;
+ }
+ t.ok(!exc, "static method still there");
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/BaseTypes/Date.html b/misc/openlayers/tests/BaseTypes/Date.html
new file mode 100644
index 0000000..e54fb31
--- /dev/null
+++ b/misc/openlayers/tests/BaseTypes/Date.html
@@ -0,0 +1,191 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_Date_toISOString(t) {
+ t.plan(3);
+
+ var date, str;
+
+ // check valid date
+ date = new Date(Date.UTC(2010, 10, 27, 18, 19, 15, 123));
+ str = OpenLayers.Date.toISOString(date);
+ t.eq(str, "2010-11-27T18:19:15.123Z", "valid date");
+
+ // check zero padding
+ date = new Date(Date.UTC(2010, 7, 7, 18, 9, 5, 12));
+ str = OpenLayers.Date.toISOString(date);
+ t.eq(str, "2010-08-07T18:09:05.012Z", "zero padding");
+
+ // check invalid date
+ date = new Date("foo");
+ try {
+ str = OpenLayers.Date.toISOString(date);
+ } catch (err) {
+ // some implementations throw RangeError
+ // see https://bugzilla.mozilla.org/show_bug.cgi?id=649575
+ if (err instanceof RangeError) {
+ str = "Invalid Date";
+ }
+ }
+ t.eq(str, "Invalid Date", "invalid date");
+
+ }
+
+ function test_Date_parse(t) {
+
+ t.plan(114);
+
+ var cases = {
+ "2000": {
+ year: 2000,
+ month: 0,
+ date: 1
+ },
+ "2005-10": {
+ year: 2005,
+ month: 9,
+ date: 1
+ },
+ "1971-07-23": {
+ year: 1971,
+ month: 6,
+ date: 23
+ },
+ "1801-11-20T04:30:15Z": {
+ year: 1801,
+ month: 10,
+ date: 20,
+ hour: 4,
+ minutes: 30,
+ seconds: 15
+ },
+ "1989-06-15T18:30:15.91Z": {
+ year: 1989,
+ month: 5,
+ date: 15,
+ hour: 18,
+ minutes: 30,
+ seconds: 15,
+ milliseconds: 910
+ },
+ "1989-06-15T18:30:15.091Z": {
+ year: 1989,
+ month: 5,
+ date: 15,
+ hour: 18,
+ minutes: 30,
+ seconds: 15,
+ milliseconds: 91
+ },
+ "1989-06-15T13:30:15.091-05": {
+ year: 1989,
+ month: 5,
+ date: 15,
+ hour: 18,
+ minutes: 30,
+ seconds: 15,
+ milliseconds: 91
+ },
+ "2010-08-06T15:21:25-06": { // MDT
+ year: 2010,
+ month: 7,
+ date: 6,
+ hour: 21,
+ minutes: 21,
+ seconds: 25
+ },
+ "2010-08-07T06:21:25+9": { // JSP
+ year: 2010,
+ month: 7,
+ date: 6,
+ hour: 21,
+ minutes: 21,
+ seconds: 25
+ },
+ "2010-08-07T02:51:25+05:30": { // IST
+ year: 2010,
+ month: 7,
+ date: 6,
+ hour: 21,
+ minutes: 21,
+ seconds: 25
+ },
+ "T21:51:25Z": {
+ hour: 21,
+ minutes: 51,
+ seconds: 25
+ },
+ "T02:51:25+05:30": { // IST
+ hour: 21,
+ minutes: 21,
+ seconds: 25
+ },
+ "T2:51:25.1234-7": { // lenient
+ hour: 9,
+ minutes: 51,
+ seconds: 25,
+ milliseconds: 123
+ },
+ "2000Z": { // lenient (Chrome accepts this)
+ year: 2000
+ },
+ "2000-02Z": { // lenient (Chrome accepts this)
+ year: 2000,
+ month: 1
+ },
+ "2000-04-15Z": { // lenient (Chrome accepts this)
+ year: 2000,
+ month: 3,
+ date: 15
+ }
+ };
+
+ var o, got, exp;
+ for (var str in cases) {
+ o = cases[str];
+ got = OpenLayers.Date.parse(str);
+ exp = new Date(Date.UTC(o.year || 0, o.month || 0, o.date || 1, o.hour || 0, o.minutes || 0, o.seconds || 0, o.milliseconds || 0));
+ if ("year" in o) {
+ t.eq(got.getUTCFullYear(), exp.getUTCFullYear(), str + ": correct UTCFullYear");
+ t.eq(got.getUTCMonth(), exp.getUTCMonth(), str + ": correct UTCMonth");
+ t.eq(got.getUTCDate(), exp.getUTCDate(), str + ": correct UTCDate");
+ } else {
+ t.ok(true, str + ": ECMA doesn't specify how years are handled in time only strings");
+ t.ok(true, str + ": ECMA doesn't specify how months are handled in time only strings");
+ t.ok(true, str + ": ECMA doesn't specify how days are handled in time only strings");
+ }
+ if ("hour" in o) {
+ t.eq(got.getUTCHours(), exp.getUTCHours(), str + ": correct UTCHours");
+ t.eq(got.getUTCMinutes(), exp.getUTCMinutes(), str + ": correct UTCMinutes");
+ t.eq(got.getUTCSeconds(), exp.getUTCSeconds(), str + ": correct UTCSeconds");
+ t.eq(got.getUTCMilliseconds(), exp.getUTCMilliseconds(), str + ": correct UTCMilliseconds");
+ } else {
+ t.ok(true, str + ": ECMA doesn't specify how hours are handled in date only strings");
+ t.ok(true, str + ": ECMA doesn't specify how minutes are handled in date only strings");
+ t.ok(true, str + ": ECMA doesn't specify how seconds are handled in date only strings");
+ t.ok(true, str + ": ECMA doesn't specify how milliseconds are handled in date only strings");
+ }
+ }
+
+ // check invalid date parsing
+ var invalid = OpenLayers.Date.parse("foo");
+ t.ok(invalid instanceof Date, "invalid is a date");
+ t.ok(isNaN(invalid.getTime()), "invalid has no time");
+ }
+
+ function test_regex(t) {
+ t.plan(1);
+ var regex = OpenLayers.Date.dateRegEx;
+ OpenLayers.Date.dateRegEx = /^(?:(-?\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/;
+ var date = OpenLayers.Date.parse("-0501-03-01T00:00:00.000Z");
+ t.ok(!isNaN(date.getTime()), "date with negative year is parsed when providing alternative regex");
+ OpenLayers.Date.dateRegEx = regex;
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/BaseTypes/Element.html b/misc/openlayers/tests/BaseTypes/Element.html
new file mode 100644
index 0000000..6e094cc
--- /dev/null
+++ b/misc/openlayers/tests/BaseTypes/Element.html
@@ -0,0 +1,195 @@
+<html>
+ <head>
+
+ <script src="../OLLoader.js"></script>
+
+ <script type="text/javascript">
+
+ function test_Element_visible(t) {
+ t.plan(3);
+
+ var elem = {
+ style: {
+ 'display': ""
+ }
+ };
+
+ elem.style.display = "";
+ t.ok(OpenLayers.Element.visible(elem), "element with style.display == '' is visible");
+
+ elem.style.display = "block";
+ t.ok(OpenLayers.Element.visible(elem), "element with style.display == block is visible");
+
+ elem.style.display = "none";
+ t.ok(!OpenLayers.Element.visible(elem), "element with style.display == none is not visible");
+ }
+
+ function test_Element_toggle(t) {
+ t.plan(2);
+
+ var elem1 = {
+ style: {
+ 'display': "none"
+ }
+ };
+
+ var elem2 = {
+ style: {
+ 'display': ""
+ }
+ };
+
+ OpenLayers.Element.toggle(elem1, elem2);
+
+ t.eq(elem1.style.display, "", "hidden element toggled to display");
+ t.eq(elem2.style.display, "none", "shown element toggled to hidden");
+ }
+
+ function test_Element_remove(t) {
+ t.plan(1);
+
+ var elem = {
+ parentNode: {
+ 'removeChild': function(elem) {
+ t.ok(true, "removeChild called");
+ }
+ }
+ };
+ OpenLayers.Element.remove(elem);
+ }
+
+ function test_Element_getHeight(t) {
+ t.plan(1);
+
+ var elem = {
+ 'offsetHeight': {}
+ };
+
+ t.ok(OpenLayers.Element.getHeight(elem) == elem.offsetHeight, "offsetHeight returned");
+ }
+
+ function test_hasClass(t) {
+ t.plan(14);
+ var has = OpenLayers.Element.hasClass;
+
+ var element = document.createElement("div");
+ element.className = "fe fi fo fum one.part two-part three:part four";
+
+ t.ok(has(element, "fe"), "has fe");
+ t.ok(has(element, "fi"), "has fi");
+ t.ok(has(element, "fo"), "has fo");
+ t.ok(!has(element, "f"), "hasn't f");
+ t.ok(!has(element, "o"), "hasn't o");
+ t.ok(!has(element, "fumb"), "hasn't fumb");
+ t.ok(!has(element, "one"), "hasn't one");
+ t.ok(has(element, "one.part"), "has one.part");
+ t.ok(!has(element, "two"), "hasn't two");
+ t.ok(has(element, "two-part"), "has two-part");
+ t.ok(!has(element, "three"), "hasn't three");
+ t.ok(has(element, "three:part"), "has three:part");
+ t.ok(has(element, "four"), "has four");
+
+ element.className = "";
+ t.ok(!has(element, "nada"), "hasn't nada");
+ }
+
+ function test_addClass(t) {
+ t.plan(6);
+ var add = OpenLayers.Element.addClass;
+
+ var element = document.createElement("div");
+ element.id = "foo";
+ t.eq(element.className, "", "starts with no class name");
+
+ var mod = add(element, "first");
+ t.eq(mod.id, element.id, "returns the same element");
+ t.eq(element.className, "first", "properly adds first class name");
+ t.eq(add(element, "second").className, "first second",
+ "properly adds second class name");
+ t.eq(add(element, "second").className, "first second",
+ "doesn't do anything for duplicated names");
+ t.eq(add(element, "third").className, "first second third",
+ "properly adds third class name");
+ }
+
+ function test_removeClass(t) {
+ t.plan(5);
+ var remove = OpenLayers.Element.removeClass;
+
+ var element = document.createElement("div");
+ element.id = "foo";
+ element.className = "first second middle fourth last";
+
+ var mod = remove(element, "last");
+ t.eq(mod.id, element.id, "returns the same element");
+ t.eq(element.className, "first second middle fourth",
+ "properly removes last class name");
+ t.eq(remove(element, "first").className, "second middle fourth",
+ "properly removes first class name");
+ t.eq(remove(element, "middle").className, "second fourth",
+ "properly removes middle class name");
+ t.eq(remove(element, "nada").className, "second fourth",
+ "doesn't do anything for bogus class name");
+ }
+
+ function test_toggleClass(t) {
+ t.plan(5);
+ var toggle = OpenLayers.Element.toggleClass;
+
+ var element = document.createElement("div");
+ element.id = "foo";
+
+ var mod = toggle(element, "first");
+ t.eq(mod.id, element.id, "returns the same element");
+ t.eq(element.className, "first", "adds first new class name");
+ t.eq(toggle(element, "second").className, "first second",
+ "adds second new class name");
+ t.eq(toggle(element, "first").className, "second",
+ "removes first existing class name");
+ t.eq(toggle(element, "second").className, "",
+ "removes second existing class name");
+ }
+
+ function test_Element_getStyle(t) {
+ t.plan(5);
+
+ //tests for this function are not 100% complete... there is some funky
+ // business going on in there with
+ // * document.defaultView (moz/ff I believe)
+ // but I cant seem to find a good way to test them.
+ //
+ t.ok(OpenLayers.Element.getStyle(null, null) == null, "passing null values in to getStyle() doesnt bomb, returns null");
+
+ var elem = document.getElementById("elemID");
+ elem.style.chickenHead = {}
+
+ var style = "chickenHead";
+ t.ok(OpenLayers.Element.getStyle(elem, style) == elem.style.chickenHead, "get style on basic stylename returns correctly");
+
+ elem.style.chickenHead = "auto";
+ style = "chickenHead";
+ t.ok(OpenLayers.Element.getStyle(elem, style) == null, "get style on 'auto' style returns null");
+
+ if (OpenLayers.BROWSER_NAME == "opera") {
+ elem.style.top = "15px";
+ style = "top";
+
+ elem.style.position = "static";
+ t.ok(OpenLayers.Element.getStyle(elem, style) == null, "in opera: get (top/left/right/bottom) style when position == 'static' returns null");
+
+ elem.style.position = "";
+ t.ok(OpenLayers.Element.getStyle(elem, style) == null, "in opera: get (top/left/right/bottom) style when position != 'static', gets computedStyle as static and returns null");
+
+ } else {
+ t.ok(true, "browser is not opera.");
+ t.ok(true, "trust me. browser is not opera.");
+ }
+
+ }
+
+ </script>
+ </head>
+ <body>
+ <div id="elemID" style="width:50px; height:100px; background-color:red">test</div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/BaseTypes/LonLat.html b/misc/openlayers/tests/BaseTypes/LonLat.html
new file mode 100644
index 0000000..a1801f6
--- /dev/null
+++ b/misc/openlayers/tests/BaseTypes/LonLat.html
@@ -0,0 +1,241 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var lonlat;
+
+ function test_LonLat_constructor (t) {
+ t.plan( 8 );
+ lonlat = new OpenLayers.LonLat(6, 5);
+ t.ok( lonlat instanceof OpenLayers.LonLat, "new OpenLayers.LonLat returns LonLat object" );
+ t.eq( lonlat.CLASS_NAME, "OpenLayers.LonLat", "lonlat.CLASS_NAME is set correctly");
+ t.eq( lonlat.lon, 6, "lonlat.lon is set correctly");
+ t.eq( lonlat.lat, 5, "lonlat.lat is set correctly");
+
+ // possible global Mercator projection values
+ lonlat = new OpenLayers.LonLat(20037508.33999999, -20037508.33999999);
+ t.eq( lonlat.lon, 20037508.34, "lonlat.lon rounds correctly");
+ t.eq( lonlat.lat, -20037508.34, "lonlat.lat rounds correctly");
+
+ // allow construction from single arg
+ lonlat = new OpenLayers.LonLat([1, 2]);
+ t.eq(lonlat.lon, 1, "lon from array");
+ t.eq(lonlat.lat, 2, "lat from array");
+
+ }
+
+ function test_LonLat_constructorFromStrings (t) {
+ t.plan( 4 );
+ lonlat = new OpenLayers.LonLat("6", "5");
+ t.ok( lonlat instanceof OpenLayers.LonLat, "new OpenLayers.LonLat returns LonLat object" );
+ t.eq( lonlat.CLASS_NAME, "OpenLayers.LonLat", "lonlat.CLASS_NAME is set correctly");
+ t.eq( lonlat.lon, 6, "lonlat.lon is set correctly");
+ t.eq( lonlat.lat, 5, "lonlat.lat is set correctly");
+ }
+
+ function test_LonLat_toString(t) {
+ t.plan( 1 );
+ lonlat = new OpenLayers.LonLat(5,6);
+ t.eq( lonlat.toString(), "lon=5,lat=6", "lonlat.toString() returns correctly");
+ }
+
+ function test_LonLat_toShortString(t) {
+ t.plan( 1 );
+ lonlat = new OpenLayers.LonLat(5,6);
+ t.eq( lonlat.toShortString(), "5, 6", "lonlat.toShortString() returns correctly");
+ }
+
+ function test_LonLat_clone(t) {
+ t.plan( 3 );
+ oldLonLat = new OpenLayers.LonLat(5,6);
+ lonlat = oldLonLat.clone();
+ t.ok( lonlat instanceof OpenLayers.LonLat, "clone returns new OpenLayers.LonLat object" );
+ t.ok( lonlat.equals(oldLonLat), "lonlat is set correctly");
+
+ oldLonLat.lon = 100;
+ t.eq( lonlat.lon, 5, "changing oldLonLat.lon doesn't change lonlat.lon");
+ }
+
+ function test_LonLat_add(t) {
+ t.plan(8);
+
+ origLL = new OpenLayers.LonLat(10,100);
+ lonlatA = origLL.clone();
+
+ addpx = lonlatA.add(5, 50);
+ t.ok( lonlatA.equals(origLL), "lonlatA is not modified by add operation");
+
+ var ll = new OpenLayers.LonLat(15,150);
+ t.ok( addpx.equals(ll), "addpx is set correctly");
+
+ //null values
+ try {
+ addpx = lonlatA.add(null, 50);
+ } catch(e) {
+ t.ok("exception thrown when passing null value to add()");
+ }
+ t.ok( lonlatA.equals(origLL), "lonlatA is not modified by erroneous add operation (null lon)");
+
+ try {
+ addpx = lonlatA.add(5, null);
+ } catch(e) {
+ t.ok("exception thrown when passing null value to add()");
+ }
+ t.ok( lonlatA.equals(origLL), "lonlatA is not modified by erroneous add operation (null lat)");
+
+ // string values
+ addpx = origLL.clone().add("5", "50");
+ t.eq(addpx.lon, 15, "addpx.lon is set correctly");
+ t.eq(addpx.lat, 150, "addpx.lat is set correctly");
+ }
+
+ function test_LonLat_equals(t) {
+ t.plan( 5 );
+ lonlat = new OpenLayers.LonLat(5,6);
+
+ ll = new OpenLayers.LonLat(5,6);
+ t.eq( lonlat.equals(ll), true, "(5,6) equals (5,6)");
+
+ ll = new OpenLayers.LonLat(1,6);
+ t.eq( lonlat.equals(ll), false, "(5,6) does not equal (1,6)");
+
+ ll = new OpenLayers.LonLat(5,2);
+ t.eq( lonlat.equals(ll), false, "(5,6) does not equal (5,2)");
+
+ ll = new OpenLayers.LonLat(1,2);
+ t.eq( lonlat.equals(ll), false, "(5,6) does not equal (1,2)");
+
+ t.ok( !lonlat.equals(null), "equals() returns false on comparison to null");
+
+ }
+
+ function test_LonLat_fromString(t) {
+ t.plan( 2 );
+ lonlat = OpenLayers.LonLat.fromString("6,5");
+ t.ok( lonlat instanceof OpenLayers.LonLat, "OpenLayers.LonLat.fromString() returns LonLat object" );
+
+ var ll = new OpenLayers.LonLat(6, 5);
+ t.ok( lonlat.equals(ll), "lonlat is set correctly");
+ }
+
+ function test_LonLat_fromArray(t) {
+ t.plan( 3 );
+
+ // (1 test) must return a OpenLayers.LonLat-instance
+ lonlat = OpenLayers.LonLat.fromArray([6,5]);
+ t.ok( lonlat instanceof OpenLayers.LonLat, "OpenLayers.LonLat.fromArray returns LonLat object" );
+
+ var ll = new OpenLayers.LonLat(6, 5);
+ // (1 test) must return correct LonLat-object
+ t.ok( lonlat.equals(ll), "lonlat is set correctly");
+
+
+ // (1 test) check how function deals with illegal arguments, it should
+ // never throw an exception and always return an instance of
+ // OpenLayers.LonLat.
+ var unexpectedResult = false,
+ undef,
+ checkArgs = [
+ {},
+ '',
+ 6,
+ false,
+ true,
+ [undef, 5],
+ [6, undef]
+ ],
+ returnedVal;
+
+ try {
+ for(var i = 0, len = checkArgs.length; i < len; i++ ){
+ returnedVal = OpenLayers.LonLat.fromArray( checkArgs[i] );
+ if (!(returnedVal instanceof OpenLayers.LonLat) ) {
+ unexpectedResult = true;
+ break;
+ }
+ }
+ // no arguments at all
+ returnedVal = OpenLayers.LonLat.fromArray();
+ unexpectedResult = !(returnedVal instanceof OpenLayers.LonLat);
+ } catch(e) {
+ unexpectedResult = true;
+ } finally {
+ t.ok(!unexpectedResult, "OpenLayers.LonLat.fromArray always returns an instance of OpenLayers.LonLat and doesn't throw an exception when called with unexpected argument.");
+ }
+ }
+
+ function test_LonLat_transform(t) {
+ t.plan(10);
+ lonlat = new OpenLayers.LonLat(10, -10);
+ lonlat.transform(new OpenLayers.Projection("foo"), new OpenLayers.Projection("Bar"));
+ t.eq(lonlat.lon, 10, "lon for null transform is the same")
+ t.eq(lonlat.lat, -10, "lat for null transform is the same")
+ lonlat.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"));
+ t.eq(Math.round(lonlat.lon), 1113195, "lon for spherical mercator transform is correct");
+ t.eq(Math.round(lonlat.lat), -1118890, "lat for spherical mercator correct")
+ lonlat.transform(new OpenLayers.Projection("EPSG:900913"), new OpenLayers.Projection("EPSG:4326"));
+ t.eq(lonlat.lon, 10, "lon for inverse spherical mercator transform is correct");
+ t.eq(Math.round(lonlat.lat), -10, "lat for inverse spherical mercator correct")
+
+ // transform with string
+ lonlat = new OpenLayers.LonLat(10, -10);
+ lonlat.transform("EPSG:4326", "EPSG:900913");
+ t.eq(Math.round(lonlat.lon), 1113195, "(string) lon for spherical mercator transform is correct");
+ t.eq(Math.round(lonlat.lat), -1118890, "(string) lat for spherical mercator correct")
+ lonlat.transform("EPSG:900913", "EPSG:4326");
+ t.eq(lonlat.lon, 10, "(string) lon for inverse spherical mercator transform is correct");
+ t.eq(Math.round(lonlat.lat), -10, "(string) lat for inverse spherical mercator correct")
+
+ }
+
+ function test_LonLat_wrapDateLine(t) {
+ t.plan( 6 );
+
+ var goodLL = new OpenLayers.LonLat(0,0);
+ var testLL, wrappedLL;
+
+ //bad maxextent
+ var maxExtent = null;
+
+ testLL = goodLL.clone();
+ wrappedLL = testLL.wrapDateLine(maxExtent);
+ t.ok(wrappedLL.equals(goodLL), "wrapping a ll with a bad maxextent does nothing");
+
+
+ //good maxextent
+ maxExtent = new OpenLayers.Bounds(-10,-10,10,10);
+
+ //inside
+ testLL = goodLL.clone();
+ wrappedLL = testLL.wrapDateLine(maxExtent);
+ t.ok(wrappedLL.equals(goodLL), "wrapping a ll within the maxextent does nothing");
+
+ //left
+ testLL = goodLL.add(-20,0);
+ wrappedLL = testLL.wrapDateLine(maxExtent);
+ t.ok(wrappedLL.equals(goodLL), "wrapping a ll once left of maxextent works");
+
+ //way left
+ testLL = goodLL.add(-200,0);
+ wrappedLL = testLL.wrapDateLine(maxExtent);
+ t.ok(wrappedLL.equals(goodLL), "wrapping a ll way left of maxextent works");
+
+ //right
+ testLL = goodLL.add(20,0);
+ wrappedLL = testLL.wrapDateLine(maxExtent);
+ t.ok(wrappedLL.equals(goodLL), "wrapping a ll once right of maxextent works");
+
+ //way right
+ testLL = goodLL.add(200,0);
+ wrappedLL = testLL.wrapDateLine(maxExtent);
+ t.ok(wrappedLL.equals(goodLL), "wrapping a ll way right of maxextent works");
+
+ }
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/BaseTypes/Pixel.html b/misc/openlayers/tests/BaseTypes/Pixel.html
new file mode 100644
index 0000000..8f9f5c9
--- /dev/null
+++ b/misc/openlayers/tests/BaseTypes/Pixel.html
@@ -0,0 +1,123 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var pixel;
+
+ function test_Pixel_constructor (t) {
+ t.plan( 4 );
+ pixel = new OpenLayers.Pixel(5,6);
+ t.ok( pixel instanceof OpenLayers.Pixel, "new OpenLayers.Pixel returns Pixel object" );
+ t.eq( pixel.CLASS_NAME, "OpenLayers.Pixel", "pixel.CLASS_NAME is set correctly");
+ t.eq( pixel.x, 5, "pixel.x is set correctly");
+ t.eq( pixel.y, 6, "pixel.y is set correctly");
+ }
+
+ function test_Pixel_constructorFromString (t) {
+ t.plan( 4 );
+ pixel = new OpenLayers.Pixel("5","6");
+ t.ok( pixel instanceof OpenLayers.Pixel, "new OpenLayers.Pixel returns Pixel object" );
+ t.eq( pixel.CLASS_NAME, "OpenLayers.Pixel", "pixel.CLASS_NAME is set correctly");
+ t.eq( pixel.x, 5, "pixel.x is set correctly");
+ t.eq( pixel.y, 6, "pixel.y is set correctly");
+ }
+
+ function test_Pixel_toString(t) {
+ t.plan( 1 );
+ pixel = new OpenLayers.Pixel(5,6);
+ t.eq( pixel.toString(), "x=5,y=6", "pixel.toString() returns correctly");
+ }
+
+ function test_Pixel_clone(t) {
+ t.plan( 4 );
+ oldPixel = new OpenLayers.Pixel(5,6);
+ pixel = oldPixel.clone();
+ t.ok( pixel instanceof OpenLayers.Pixel, "clone returns new OpenLayers.Pixel object" );
+ t.eq( pixel.x, 5, "pixel.x is set correctly");
+ t.eq( pixel.y, 6, "pixel.y is set correctly");
+
+ oldPixel.x = 100;
+ t.eq( pixel.x, 5, "changing oldPixel.x doesn't change pixel.x");
+ }
+
+ function test_Pixel_distanceTo(t) {
+ t.plan( 2 );
+ var px = new OpenLayers.Pixel(0,-2);
+ pixel = new OpenLayers.Pixel(0,0);
+ t.eq( pixel.distanceTo(px), 2, "(0,0) distanceTo (0,-2) = 2");
+
+ px = new OpenLayers.Pixel(-4,6);
+ pixel = new OpenLayers.Pixel(4,6);
+ t.eq( pixel.distanceTo(px), 8, "(4,6) distanceTo (-4,6) = 8");
+ }
+
+ function test_Pixel_equals(t) {
+ t.plan( 5 );
+ pixel = new OpenLayers.Pixel(5,6);
+
+ var px = new OpenLayers.Pixel(5,6);
+ t.eq( pixel.equals(px), true, "(5,6) equals (5,6)");
+
+ px = new OpenLayers.Pixel(1,6);
+ t.eq( pixel.equals(px), false, "(5,6) does not equal (1,6)");
+
+ px = new OpenLayers.Pixel(5,2);
+ t.eq( pixel.equals(px), false, "(5,6) does not equal (5,2)");
+
+ px = new OpenLayers.Pixel(1,2);
+ t.eq( pixel.equals(px), false, "(5,6) does not equal (1,2)");
+
+ t.ok( !pixel.equals(null), "equals() returns false on comparison to null");
+
+ }
+
+ function test_Pixel_add(t) {
+ t.plan( 6 );
+
+ var origPX = new OpenLayers.Pixel(5,6);
+ var oldPixel = origPX.clone();
+
+ var pixel = oldPixel.add(10,20);
+
+ t.ok( oldPixel.equals(origPX), "oldPixel not modified by add operation");
+
+ var px = new OpenLayers.Pixel(15,26);
+ t.ok( pixel.equals(px), "returned pixel is correct");
+
+ //null values
+ try {
+ pixel = oldPixel.add(null, 50);
+ } catch(e) {
+ t.ok("exception thrown when passing null value to add()");
+ }
+ t.ok( oldPixel.equals(origPX), "oldPixel is not modified by erroneous add operation (null x)");
+
+ try {
+ addpx = oldPixel.add(5, null);
+ } catch(e) {
+ t.ok("exception thrown when passing null value to add()");
+ }
+ t.ok( oldPixel.equals(origPX), "oldPixel is not modified by erroneous add operation (null y)");
+ }
+
+ function test_Pixel_offset(t) {
+ t.plan( 4 );
+
+ var oldPixel = new OpenLayers.Pixel(5,6);
+ var offset = new OpenLayers.Pixel(10,20);
+
+ pixel = oldPixel.offset(offset);
+
+ t.eq( oldPixel.x, 5, "oldPixel.x not modified by offset operation");
+ t.eq( oldPixel.y, 6, "oldPixel.y not modified by offset operation");
+
+ t.eq( pixel.x, 15, "pixel.x is set correctly");
+ t.eq( pixel.y, 26, "pixel.y is set correctly");
+ }
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/BaseTypes/Size.html b/misc/openlayers/tests/BaseTypes/Size.html
new file mode 100644
index 0000000..b52906c
--- /dev/null
+++ b/misc/openlayers/tests/BaseTypes/Size.html
@@ -0,0 +1,67 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var Size;
+
+ function test_Size_constructor (t) {
+ t.plan( 4 );
+ size = new OpenLayers.Size(5,6);
+ t.ok( size instanceof OpenLayers.Size, "new OpenLayers.Size returns size object" );
+ t.eq( size.CLASS_NAME, "OpenLayers.Size", "size.CLASS_NAME is set correctly");
+ t.eq( size.w, 5, "size.w is set correctly");
+ t.eq( size.h, 6, "size.h is set correctly");
+ }
+
+ function test_Size_constructorFromString (t) {
+ t.plan( 4 );
+ size = new OpenLayers.Size("5","6");
+ t.ok( size instanceof OpenLayers.Size, "new OpenLayers.Size returns size object" );
+ t.eq( size.CLASS_NAME, "OpenLayers.Size", "size.CLASS_NAME is set correctly");
+ t.eq( size.w, 5, "size.w is set correctly");
+ t.eq( size.h, 6, "size.h is set correctly");
+ }
+
+ function test_Size_toString(t) {
+ t.plan( 1 );
+ size = new OpenLayers.Size(5,6);
+ t.eq( size.toString(), "w=5,h=6", "size.toString() returns correctly");
+ }
+
+ function test_Size_clone(t) {
+ t.plan( 3 );
+
+ oldSize = new OpenLayers.Size(5,6);
+ size = oldSize.clone();
+ t.ok( size instanceof OpenLayers.Size, "clone returns new OpenLayers.Size object" );
+ t.ok( size.equals(oldSize), "new size is equal to old size correctly");
+
+ oldSize.w = 100;
+ t.eq( size.w, 5, "changing oldSize.w doesn't change size.w");
+ }
+
+ function test_Size_equals(t) {
+ t.plan( 5 );
+ size = new OpenLayers.Size(5,6);
+
+ sz = new OpenLayers.Size(5,6);
+ t.eq( size.equals(sz), true, "(5,6) equals (5,6)");
+
+ sz = new OpenLayers.Size(1,6);
+ t.eq( size.equals(sz), false, "(5,6) does not equal (1,6)");
+
+ sz = new OpenLayers.Size(5,2);
+ t.eq( size.equals(sz), false, "(5,6) does not equal (5,2)");
+
+ sz = new OpenLayers.Size(1,2);
+ t.eq( size.equals(sz), false, "(5,6) does not equal (1,2)");
+
+ t.ok( !size.equals(null), "equals() returns false on comparison to null");
+ }
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Console.html b/misc/openlayers/tests/Console.html
new file mode 100644
index 0000000..48b27c2
--- /dev/null
+++ b/misc/openlayers/tests/Console.html
@@ -0,0 +1,39 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Console(t) {
+
+ /**
+ * These tests only assure that OpenLayers works when not
+ * in debug mode. It means that calls to OpenLayers.Console
+ * will not do anything in this case. When OpenLayers is in
+ * debug mode, we assume that the Firebug extension or Firebug Lite
+ * works as advertised.
+ */
+
+ // supported OpenLayers.Console methods
+ var methods = ['log', 'debug', 'info', 'warn', 'error', 'assert',
+ 'dir', 'dirxml', 'trace', 'group', 'groupEnd', 'time',
+ 'timeEnd', 'profile', 'profileEnd', 'count'];
+
+ t.plan(methods.length * 2);
+
+ var nothing, method;
+ for(var i=0; i<methods.length; ++i) {
+ method = OpenLayers.Console[methods[i]];
+ t.ok(method,
+ "OpenLayers.Console." + methods[i] + " exists");
+ nothing = OpenLayers.Console[methods[i]]();
+ t.eq(nothing, null,
+ "OpenLayers.Console." + methods[i] + "() " +
+ "call is harmless when not in debug mode");
+ }
+
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control.html b/misc/openlayers/tests/Control.html
new file mode 100644
index 0000000..06f057c
--- /dev/null
+++ b/misc/openlayers/tests/Control.html
@@ -0,0 +1,107 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Control_constructor(t) {
+ t.plan(4);
+
+ var control = new OpenLayers.Control();
+
+ t.ok(control instanceof OpenLayers.Control, "new OpenLayers.Control returns object");
+ t.eq(control.displayClass, "olControl", "displayClass set correctly");
+ t.ok(control.id != null, "default id assigned to control");
+
+ var testID = {};
+ control = new OpenLayers.Control({ 'id': testID });
+ t.ok(control.id == testID, "if id specified in options, no default assigned.");
+ }
+
+ function test_Control_addControl(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+
+ t.ok(control.map === map, "Control.map is set to the map object" );
+ t.ok(map.controls[map.controls.length - 1] === control, "map.controls contains control");
+ }
+
+ function test_Control_title(t) {
+ t.plan( 1 );
+ var titleText = 'Title test';
+ control = new OpenLayers.Control({title:titleText});
+ t.eq( control.title, titleText, "control.title set correctly" );
+ }
+
+ function test_eventListeners(t) {
+ t.plan(1);
+
+ var method = OpenLayers.Events.prototype.on;
+ // test that events.on is called at control construction
+ var options = {
+ eventListeners: {foo: "bar"}
+ };
+ OpenLayers.Events.prototype.on = function(obj) {
+ t.eq(obj, options.eventListeners, "events.on called with eventListeners");
+ }
+ var control = new OpenLayers.Control(options);
+ OpenLayers.Events.prototype.on = method;
+ control.destroy();
+
+ // if events.on is called again, this will fail due to an extra test
+ // test control without eventListeners
+ OpenLayers.Events.prototype.on = function(obj) {
+ t.fail("events.on called without eventListeners");
+ }
+ var control2 = new OpenLayers.Control();
+ OpenLayers.Events.prototype.on = method;
+ control2.destroy();
+ }
+
+ function test_Control_destroy(t) {
+ t.plan(4);
+
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+
+ control.destroy();
+ t.ok(map.controls[map.controls.length - 1] != control, "map.controls doesn't contains control");
+
+ t.ok(control.map == null, "Control.map is null");
+ t.ok(control.div == null, "Control.div is null");
+ t.ok(control.handler == null, "Control.handler is null");
+ }
+
+ function test_autoActivate(t) {
+
+ t.plan(3);
+
+ var control, map = new OpenLayers.Map("map");
+
+ // confirm that a control is not activated by default
+ control = new OpenLayers.Control();
+ map.addControl(control);
+ t.ok(!control.active, "control is not activated by default");
+
+ // confirm that control is activated with autoActivate true
+ control = new OpenLayers.Control({autoActivate: true});
+ map.addControl(control);
+ t.ok(control.active, "control is activated with autoActivate true");
+
+ // confirm that control is not activated with autoActivate false
+ control = new OpenLayers.Control({autoActivate: false});
+ map.addControl(control);
+ t.ok(!control.active, "control is not activated with autoActivate false");
+
+ map.destroy();
+
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/ArgParser.html b/misc/openlayers/tests/Control/ArgParser.html
new file mode 100644
index 0000000..34e9f5b
--- /dev/null
+++ b/misc/openlayers/tests/Control/ArgParser.html
@@ -0,0 +1,26 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_getParameters(t) {
+ t.plan(4);
+
+ var c = new OpenLayers.Control.ArgParser(), p;
+
+ p = c.getParameters('http://example.com?fook=foov&bark=barv');
+ t.eq(p, {fook: 'foov', bark: 'barv'}, 'a) params are correct');
+
+ p = c.getParameters('http://example.com#fook=foov&bark=barv');
+ t.eq(p, {fook: 'foov', bark: 'barv'}, 'b) params are correct');
+
+ p = c.getParameters('http://example.com?a=b&b=c#fook=foov&bark=barv');
+ t.eq(p, {a: 'b', b: 'c', fook: 'foov', bark: 'barv'},
+ 'c) params are correct');
+
+ p = c.getParameters('http://example.com?a=b&b=c&fook=a&bark=b#fook=foov&bark=barv');
+ t.eq(p, {a: 'b', b: 'c', fook: 'foov', bark: 'barv'},
+ 'd) params are correct');
+ }
+ </script>
+</head>
+</html>
diff --git a/misc/openlayers/tests/Control/Attribution.html b/misc/openlayers/tests/Control/Attribution.html
new file mode 100644
index 0000000..04b85cf
--- /dev/null
+++ b/misc/openlayers/tests/Control/Attribution.html
@@ -0,0 +1,60 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var map;
+ function test_Control_Attribution_constructor (t) {
+ t.plan( 2 );
+
+ control = new OpenLayers.Control.Attribution();
+ t.ok( control instanceof OpenLayers.Control.Attribution, "new OpenLayers.Control returns object" );
+ t.eq( control.displayClass, "olControlAttribution", "displayClass is correct" );
+ }
+ function test_Control_Attribution_setBaseLayer (t) {
+ t.plan(1);
+ map = new OpenLayers.Map("map");
+ map.addControl(control);
+ map.addLayer(new OpenLayers.Layer("name",{'attribution':'My layer!', isBaseLayer: true}));
+ map.addLayer(new OpenLayers.Layer("name",{'attribution':'My layer 2!', isBaseLayer: true}));
+ map.setBaseLayer(map.layers[1]);
+ t.eq(control.div.innerHTML, 'My layer 2!', "Attribution correct with changed base layer");
+
+ }
+ function test_Control_Attribution_draw (t) {
+ t.plan(3);
+ control = new OpenLayers.Control.Attribution();
+ map = new OpenLayers.Map("map");
+ map.addControl(control);
+ map.addLayer(new OpenLayers.Layer("name",{'attribution':'My layer!'}));
+ t.eq(control.div.innerHTML, 'My layer!', "Attribution correct with one layer.");
+ map.addLayer(new OpenLayers.Layer("name", {'attribution':'My layer 2!'}));
+ t.eq(control.div.innerHTML, 'My layer!, My layer 2!', "Attribution correct with two layers.");
+ control.separator = '|';
+ control.template = "Map Copyright (c) 2012 by Foo Bar; ${layers}";
+ map.addLayer(new OpenLayers.Layer("name",{'attribution':'My layer 3!'}));
+ t.eq(control.div.innerHTML, 'Map Copyright (c) 2012 by Foo Bar; My layer!|My layer 2!|My layer 3!', "Attribution correct with three layers and diff seperator.");
+
+
+ }
+
+ function test_Control_Attribution_no_duplicates(t) {
+ t.plan(2);
+
+ map = new OpenLayers.Map("map");
+ map.addLayer(new OpenLayers.Layer("Company A: 1",{'attribution':'company A'}));
+ map.addLayer(new OpenLayers.Layer("Company A: 2",{'attribution':'company A'}));
+
+ control = new OpenLayers.Control.Attribution();
+ map.addControl(control);
+ t.eq(control.div.innerHTML, 'company A', "Attribution not duplicated.");
+
+ map.addLayer(new OpenLayers.Layer("Company B: 1",{'attribution':'company B'}));
+ map.addLayer(new OpenLayers.Layer("Company A: 3",{'attribution':'company A'}));
+ t.eq(control.div.innerHTML, 'company A, company B', "Attribution correct with four layers (3 with same attribution).");
+ }
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/Button.html b/misc/openlayers/tests/Control/Button.html
new file mode 100644
index 0000000..8d5c036
--- /dev/null
+++ b/misc/openlayers/tests/Control/Button.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Control_Button_constructor (t) {
+ t.plan( 2 );
+
+ control = new OpenLayers.Control.Button();
+ t.ok( control instanceof OpenLayers.Control.Button, "new OpenLayers.Control returns object" );
+ t.eq( control.displayClass, "olControlButton", "displayClass is correct" );
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/CacheRead.html b/misc/openlayers/tests/Control/CacheRead.html
new file mode 100644
index 0000000..ae0addf
--- /dev/null
+++ b/misc/openlayers/tests/Control/CacheRead.html
@@ -0,0 +1,108 @@
+<html>
+<head>
+ <script>
+ /**
+ * Because browsers that implement requestAnimationFrame may not execute
+ * animation functions while a window is not displayed (e.g. in a hidden
+ * iframe as in these tests), we mask the native implementations here. The
+ * native requestAnimationFrame functionality is tested in Util.html and
+ * in PanZoom.html (where a popup is opened before panning). The tests
+ * here will test the fallback setTimeout implementation for animation.
+ */
+ window.requestAnimationFrame =
+ window.webkitRequestAnimationFrame =
+ window.mozRequestAnimationFrame =
+ window.oRequestAnimationFrame =
+ window.msRequestAnimationFrame = null;
+ </script>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_addLayer_removeLayer(t) {
+ t.plan(6);
+ var control = new OpenLayers.Control.CacheRead();
+ var map = new OpenLayers.Map({
+ div: "map",
+ controls: [control],
+ layers: [
+ new OpenLayers.Layer.WMS("One"),
+ new OpenLayers.Layer.WMS("Two")
+ ]
+ });
+ t.ok(map.layers[0].events.listeners.tileloadstart, "tileloadstart listener registered on layer One");
+ t.ok(map.layers[1].events.listeners.tileloadstart, "tileloadstart listener registered on layer Two");
+ control.destroy();
+ t.ok(!map.layers[1].events.listeners.tileloadstart.length, "tileloadstart listener unregistered");
+
+ control = new OpenLayers.Control.CacheRead({
+ fetchEvent: "tileerror",
+ layers: [map.layers[0]]
+ });
+ map.addControl(control);
+ t.ok(map.layers[0].events.listeners.tileerror, "tileerror listener registered on layer One");
+ t.ok(!map.layers[1].events.listeners.tileerror, "tileerror listener not registered on layer Two");
+ control.destroy();
+ t.ok(!map.layers[0].events.listeners.tileerror.length, "tileerror listener unregistered");
+
+ map.destroy();
+ }
+
+ function test_fetch(t) {
+
+ if (!window.localStorage) {
+ t.plan(1);
+ var scope = {active: true};
+ t.eq(OpenLayers.Control.CacheRead.prototype.fetch.call(scope), undefined, "no tiles fetched when localStorage is not supported.");
+ return;
+ }
+
+ t.plan(5);
+
+ var data = "data:image/gif;base64,R0lGODlhAQABAIAAAP7//wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==";
+ window.localStorage.setItem("olCache_foo/1/1/1", data);
+ window.localStorage.setItem("olCache_bar/1/1/1", data);
+
+ var layer1 = new OpenLayers.Layer.XYZ("One", "foo/${x}/${y}/${z}");
+ var layer2 = new OpenLayers.Layer.XYZ("Two", "bar/${x}/${y}/${z}", {isBaseLayer: false});
+ var control1 = new OpenLayers.Control.CacheRead({
+ layers: [layer1]
+ });
+ var control2 = new OpenLayers.Control.CacheRead({
+ layers: [layer2],
+ fetchEvent: "tileerror"
+ });
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ projection: "EPSG:900913",
+ controls: [control1, control2],
+ layers: [layer1, layer2],
+ zoom: 1,
+ center: [0, 0]
+ });
+
+ OpenLayers.ProxyHost = "proxy?url=";
+ var tile = new OpenLayers.Tile.Image(layer1, new OpenLayers.LonLat(0, 0), new OpenLayers.Bounds(0, 0, 1, 1), "proxy?url=foo/1/1/1");
+ OpenLayers.Control.CacheWrite.urlMap[tile.url] = "foo/1/1/1";
+
+ control1.fetch({tile: tile});
+ t.eq(tile.url, data, "proxied url replaced with data uri for original url");
+ delete OpenLayers.Control.CacheWrite.urlMap[tile.url];
+
+ t.delay_call(1, function() {
+ t.eq(layer1.grid[1][1].imgDiv.src, data, "[tileloadstart] tile content from cache");
+ t.ok(layer1.grid[0][0].imgDiv.src !== data, "[tileloadstart] tile content from remote resource");
+ t.eq(layer2.grid[1][1].imgDiv.src, data, "[tileerror] tile content from cache");
+ t.ok(layer2.grid[0][0].imgDiv.src !== data, "[tileerror] tile content from remote resource");
+
+ window.localStorage.removeItem("olCache_foo/1/1/1");
+ window.localStorage.removeItem("olCache_bar/1/1/1");
+ map.destroy();
+ });
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 250px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/CacheWrite.html b/misc/openlayers/tests/Control/CacheWrite.html
new file mode 100644
index 0000000..9922569
--- /dev/null
+++ b/misc/openlayers/tests/Control/CacheWrite.html
@@ -0,0 +1,90 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_addLayer_removeLayer(t) {
+ t.plan(6);
+ var control = new OpenLayers.Control.CacheWrite();
+ var map = new OpenLayers.Map({
+ div: "map",
+ controls: [control],
+ layers: [
+ new OpenLayers.Layer.WMS("One"),
+ new OpenLayers.Layer.WMS("Two")
+ ]
+ });
+ t.ok(map.layers[0].events.listeners.tileloaded, "tileloaded listener registered on layer One");
+ t.ok(map.layers[1].events.listeners.tileloaded, "tileloaded listener registered on layer Two");
+ control.destroy();
+ t.ok(!map.layers[1].events.listeners.tileloaded.length, "tileloaded listener unregistered");
+
+ control = new OpenLayers.Control.CacheWrite({
+ layers: [map.layers[0]]
+ });
+ map.addControl(control);
+ t.ok(map.layers[0].events.listeners.tileloaded.length, "tileloaded listener registered on layer One");
+ t.ok(!map.layers[1].events.listeners.tileloaded.length, "tileloaded listener not registered on layer Two");
+ control.destroy();
+ t.ok(!map.layers[0].events.listeners.tileloaded.length, "tileloaded listener unregistered");
+
+ map.destroy();
+ }
+
+ function test_cache_clearCache(t) {
+
+ if (!window.localStorage) {
+ t.plan(2);
+ var scope = {active: true};
+ t.eq(OpenLayers.Control.CacheWrite.prototype.cache.call(scope), undefined, "no tiles cached when localStorage is not supported.");
+ t.ok(!OpenLayers.Control.CacheWrite.clearCache(), "clearCache does nothing when localStorage is not supported.");
+ return;
+ }
+
+ t.plan(4);
+ OpenLayers.Control.CacheWrite.clearCache();
+ var length = window.localStorage.length;
+
+ var tiles = 0;
+ var layer = new OpenLayers.Layer.XYZ("One", "../../img/blank.gif?${x},${y},${z}", {
+ eventListeners: {
+ tileloaded: function() {
+ tiles++;
+ }
+ }
+ });
+ var control = new OpenLayers.Control.CacheWrite({autoActivate: true});
+ var map = new OpenLayers.Map({
+ div: "map",
+ projection: "EPSG:900913",
+ controls: [control],
+ layers: [layer],
+ zoom: 1,
+ center: [0, 0]
+ });
+ t.delay_call(1, function() {
+ var canvasContext = layer.grid[1][1].getCanvasContext();
+ t.eq(window.localStorage.length, length + (canvasContext ? tiles : 0), "cache filled with tiles");
+ var url = layer.grid[1][1].url;
+ // content will be null for browsers that have localStorage but no canvas support
+ var content = canvasContext ? canvasContext.canvas.toDataURL("image/png") : null;
+ t.eq(window.localStorage.getItem("olCache_"+url), content, "localStorage contains correct image data");
+
+ layer.events.triggerEvent('tileloaded', {aborted: true, tile: layer.grid[1][1]});
+ t.eq(window.localStorage.length, length + (canvasContext ? tiles-1 : 0), "tile aborted during load not cached");
+
+ var key = Math.random();
+ window.localStorage.setItem(key, "bar");
+ OpenLayers.Control.CacheWrite.clearCache();
+ t.eq(window.localStorage.length, length + 1, "cache cleared, but foreign entries left in localStorage");
+ window.localStorage.removeItem(key);
+
+ map.destroy();
+ });
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 250px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/DragFeature.html b/misc/openlayers/tests/Control/DragFeature.html
new file mode 100644
index 0000000..cfc3a63
--- /dev/null
+++ b/misc/openlayers/tests/Control/DragFeature.html
@@ -0,0 +1,383 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Control_DragFeature_constructor(t) {
+ t.plan(3);
+
+ var options = {
+ geometryTypes: "foo"
+ };
+ var layer = "bar";
+ var control = new OpenLayers.Control.DragFeature(layer, options);
+ t.ok(control instanceof OpenLayers.Control.DragFeature,
+ "new OpenLayers.Control.DragFeature returns an instance");
+ t.eq(control.layer, "bar",
+ "constructor sets layer correctly");
+ t.eq(control.handlers.feature.geometryTypes, "foo",
+ "constructor sets options correctly on feature handler");
+ }
+
+ function test_Control_DragFeature_destroy(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.DragFeature(layer);
+ control.handlers.drag.destroy = function() {
+ t.ok(true,
+ "control.destroy calls destroy on drag handler");
+ }
+ control.handlers.feature.destroy = function() {
+ t.ok(true,
+ "control.destroy calls destroy on feature handler");
+ }
+
+ control.destroy();
+
+ }
+
+ function test_Control_DragFeature_activate(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.DragFeature(layer);
+ map.addControl(control);
+ t.ok(!control.handlers.feature.active,
+ "feature handler is not active prior to activating control");
+ control.activate();
+ t.ok(control.handlers.feature.active,
+ "feature handler is active after activating control");
+ }
+
+ function test_Control_DragFeature_deactivate(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.DragFeature(layer);
+ map.addControl(control);
+
+ control.handlers.drag.deactivate = function() {
+ t.ok(true,
+ "control.deactivate calls deactivate on drag handler");
+ }
+ control.handlers.feature.deactivate = function() {
+ t.ok(true,
+ "control.deactivate calls deactivate on feature handler");
+ }
+ control.deactivate();
+ }
+
+ function test_Control_DragFeature_over(t) {
+ t.plan(5);
+ var log = [];
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.DragFeature(layer, {
+ onEnter: function(f) { log.push({feature: f}); }
+ });
+ map.addControl(control);
+
+ control.activate();
+ t.ok(!control.handlers.drag.active,
+ "drag handler is not active before over a feature");
+
+ // simulate a mouseover on a feature
+ var feature = new OpenLayers.Feature.Vector();
+ feature.layer = layer;
+ layer.getFeatureFromEvent = function(evt) {
+ return feature;
+ }
+ map.events.triggerEvent("mousemove", {type: "mousemove"});
+
+ t.eq(control.feature.id, feature.id,
+ "control gets the proper feature from the feature handler");
+ t.ok(control.handlers.drag.active,
+ "drag handler activated when over a feature");
+ t.eq(log.length, 1,
+ "onEnter called exactly once");
+ t.eq(log[0].feature.id, feature.id,
+ "onEnter called with expected feature");
+ }
+
+ function test_Control_DragFeature_over_touch(t) {
+ t.plan(7);
+ var log = [];
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.DragFeature(layer, {
+ onEnter: function(f) { log.push({feature: f}); }
+ });
+ map.addControl(control);
+
+ control.activate();
+ t.ok(!control.handlers.drag.active,
+ "drag handler is not active before touch on a feature");
+
+ // simulate a touch on a feature
+ var feature = new OpenLayers.Feature.Vector();
+ feature.layer = layer;
+ layer.getFeatureFromEvent = function(evt) {
+ return feature;
+ }
+ map.events.triggerEvent("touchstart", {type: "touchstart", touches: ['foo']});
+
+ t.eq(control.feature.id, feature.id,
+ "control gets the proper feature from the feature handler");
+ t.ok(control.handlers.drag.active,
+ "drag handler activated when touch on a feature");
+ t.ok(control.handlers.drag.started, "drag handler has started");
+ t.ok(!control.handlers.drag.stopDown, "drag handler is not stopping down");
+ t.eq(log.length, 1,
+ "onEnter called exactly once");
+ t.eq(log[0].feature.id, feature.id,
+ "onEnter called with expected feature");
+ }
+
+ function test_Control_DragFeature_down(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.DragFeature(layer);
+ map.addControl(control);
+
+ control.activate();
+
+ // simulate a mouseover on a feature
+ var feature = new OpenLayers.Feature.Vector();
+ feature.layer = layer;
+ layer.getFeatureFromEvent = function(evt) {
+ return feature;
+ }
+ map.events.triggerEvent("mousemove", {type: "mousemove"});
+
+ // simulate a mousedown on a feature
+ control.onStart = function(feat, pixel) {
+ t.eq(feat.id, feature.id, "onStart called with the correct feature");
+ t.eq(pixel, "bar", "onStart called with the correct pixel");
+ }
+ map.events.triggerEvent("mousedown", {xy: "bar", which: 1, type: "mousemove"});
+
+ t.eq(control.lastPixel, "bar",
+ "mousedown sets the lastPixel correctly");
+ }
+
+ function test_Control_DragFeature_move(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.DragFeature(layer);
+ map.addControl(control);
+ map.getResolution = function() {
+ return 2;
+ }
+
+ control.activate();
+
+ // mock up a feature - for the sole purpose of testing mousemove
+ var uid = Math.random();
+ layer.getFeatureFromEvent = function() {
+ var geom = new OpenLayers.Geometry.Point(Math.random(),
+ Math.random());
+ geom.move = function(x, y) {
+ t.eq(x, 2, "move called with dx * res");
+ t.eq(y, -4, "move called with -dy * res");
+ };
+ var feature = new OpenLayers.Feature.Vector(geom);
+ feature.layer = layer;
+ feature.uid = uid;
+ return feature;
+ };
+ layer.drawFeature = function(feature) {
+ t.eq(feature.uid, uid,
+ "layer.drawFeature called with correct feature");
+ };
+
+ // simulate a mouseover on a feature
+ map.events.triggerEvent("mousemove", {type: "mousemove"});
+
+ // simulate a mousedown on a feature
+ var down = new OpenLayers.Pixel(0, 0);
+ map.events.triggerEvent("mousedown", {xy: down, which: 1, type: "mousemove"});
+
+ // simulate a mousemove on a feature
+ var move = new OpenLayers.Pixel(1, 2);
+ map.events.triggerEvent("mousemove", {xy: move, which: 1, type: "mousemove"});
+
+ }
+
+ function test_Control_DragFeature_up(t) {
+ t.plan(6);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.DragFeature(layer);
+ map.addControl(control);
+
+ control.activate();
+
+ // simulate a mouseover on a feature
+ var feature = new OpenLayers.Feature.Vector();
+ feature.layer = layer;
+ layer.getFeatureFromEvent = function(evt) {
+ return feature;
+ }
+ map.events.triggerEvent("mousemove", {type: "mousemove"});
+ t.eq(control.over, true,
+ "mouseover on a feature sets the over property to true");
+ t.ok(OpenLayers.Element.hasClass(control.map.viewPortDiv, "olControlDragFeatureOver"),
+ "mouseover on a feature adds class name to map container");
+ t.eq(control.handlers.drag.active, true,
+ "mouseover on a feature activates drag handler");
+
+ // simulate a mouse-up on the map, with the mouse still
+ // over the dragged feature
+ control.handlers.drag.started = true;
+ map.events.triggerEvent("mouseup", {type: "mouseup"});
+ t.eq(control.handlers.drag.active, true,
+ "mouseup while still over dragged feature does not deactivate drag handler");
+
+ // simulate a mouse-up on the map, with the mouse out of
+ // the dragged feature
+ control.handlers.drag.started = true;
+ control.over = false;
+ map.events.triggerEvent("mouseup", {type: "mouseup"});
+ t.eq(control.handlers.drag.active, false,
+ "mouseup deactivates drag handler");
+
+ control.deactivate();
+ t.ok(!OpenLayers.Element.hasClass(control.map.viewPortDiv, "olControlDragFeatureOver"),
+ "deactivate removes class name from map container");
+ }
+
+ function test_Control_DragFeature_done(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.DragFeature(layer);
+ map.addControl(control);
+
+ control.activate();
+
+
+ // simulate a mouseover on a feature
+ var feature = new OpenLayers.Feature.Vector();
+ feature.layer = layer;
+ layer.getFeatureFromEvent = function() {
+ return feature;
+ };
+ map.events.triggerEvent("mousemove", {type: "mousemove"});
+ t.eq(control.feature.id, feature.id,
+ "feature is set on mouse over");
+ control.doneDragging();
+ t.eq(control.feature.id, feature.id,
+ "feature sticks around after doneDragging is called.");
+
+ }
+
+ function test_Control_DragFeature_out(t) {
+ t.plan(4);
+ var log = [];
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.DragFeature(layer, {
+ onLeave: function(f) { log.push({feature: f}); }
+ });
+ map.addControl(control);
+
+ control.activate();
+
+
+ // simulate a mouseover on a feature
+ var feature = new OpenLayers.Feature.Vector();
+ feature.layer = layer;
+ layer.getFeatureFromEvent = function() {
+ return feature;
+ };
+ map.events.triggerEvent("mousemove", {type: "mousemove"});
+ t.eq(control.feature.id, feature.id,
+ "feature is set on mouse over");
+
+ // simulate a mouseout on a feature
+ layer.getFeatureFromEvent = function() {
+ return null;
+ };
+ map.events.triggerEvent("mousemove", {type: "mousemove"});
+ t.ok(control.feature == null,
+ "feature is set to null on mouse out");
+ t.eq(log.length, 1,
+ "onLeave called exactly once");
+ t.eq(log[0].feature.id, feature.id,
+ "onLeave called with expected feature");
+ }
+
+ function test_Control_DragFeature_out_touch(t) {
+ t.plan(5);
+ var log = [];
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.DragFeature(layer, {
+ onLeave: function(f) { log.push({feature: f}); }
+ });
+ map.addControl(control);
+
+ control.activate();
+
+ // simulate a touch on a feature
+ var feature = new OpenLayers.Feature.Vector();
+ feature.layer = layer;
+ layer.getFeatureFromEvent = function() {
+ return feature;
+ };
+ map.events.triggerEvent("touchstart", {type: "touchstart", touches: ['foo']});
+ t.eq(control.feature.id, feature.id,
+ "feature is set on mouse over");
+
+ // simulate a touch outside the feature
+ layer.getFeatureFromEvent = function() {
+ return null;
+ };
+ map.events.triggerEvent("touchstart", {type: "touchstart", touches: ['foo']});
+ t.ok(control.feature == null,
+ "feature is set to null on mouse out");
+ t.ok(control.handlers.drag.stopDown,
+ "drag handler is stopping down again");
+ t.eq(log.length, 1,
+ "onLeave called exactly once");
+ t.eq(log[0].feature.id, feature.id,
+ "onLeave called with expected feature");
+ }
+
+ function test_Control_DragFeature_click(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var feature = new OpenLayers.Feature.Vector();
+ feature.layer = layer;
+ var control = new OpenLayers.Control.DragFeature(layer);
+ map.addControl(control);
+
+ control.activate();
+
+ control.overFeature(feature);
+ control.handlers.feature.evt = {which: 1};
+ control.clickFeature(feature);
+ t.eq(control.handlers.drag.started, false, "click after over does not start drag handler");
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 250px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/DragPan.html b/misc/openlayers/tests/Control/DragPan.html
new file mode 100644
index 0000000..ba5224f
--- /dev/null
+++ b/misc/openlayers/tests/Control/DragPan.html
@@ -0,0 +1,104 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var map, control, layer;
+
+ function init_map() {
+ control = new OpenLayers.Control.DragPan();
+ map = new OpenLayers.Map("map", {controls:[control]});
+ layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'} );
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ map.zoomIn();
+ control.activate();
+ return [map, control];
+ }
+ function test_Control_DragPan_constructor (t) {
+ t.plan( 1 );
+
+ control = new OpenLayers.Control.DragPan();
+ t.ok( control instanceof OpenLayers.Control.DragPan, "new OpenLayers.Control returns object" );
+ }
+ function test_Control_DragPan_drag (t) {
+ t.plan(4);
+ var data = init_map();
+ map = data[0]; control = data[1];
+ res = map.baseLayer.resolutions[map.getZoom()];
+ t.eq(map.center.lat, 0, "Lat is 0 before drag");
+ t.eq(map.center.lon, 0, "Lon is 0 before drag");
+ map.events.triggerEvent('mousedown', {'type':'mousedown', 'xy':new OpenLayers.Pixel(0,0), 'which':1});
+ map.events.triggerEvent('mousemove', {'type':'mousemove', 'xy':new OpenLayers.Pixel(5,5), 'which':1});
+ map.events.triggerEvent('mouseup', {'type':'mouseup', 'xy':new OpenLayers.Pixel(5,5), 'which':1});
+
+ t.eq(map.getCenter().lat, res * 5, "Lat is " + (res * 5) + " after drag");
+ t.eq(map.getCenter().lon, res * -5, "Lon is " + (res * -5) + " after drag");
+ }
+ function test_Control_DragPan_drag_documentDrag (t) {
+ t.plan(4);
+ control = new OpenLayers.Control.DragPan({documentDrag: true});
+ map = new OpenLayers.Map("map", {controls:[control]});
+ layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'} );
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ map.zoomIn();
+ control.activate();
+
+ res = map.baseLayer.resolutions[map.getZoom()];
+ t.eq(map.center.lat, 0, "Lat is 0 before drag");
+ t.eq(map.center.lon, 0, "Lon is 0 before drag");
+ map.events.triggerEvent('mousedown', {'type':'mousedown', 'xy':new OpenLayers.Pixel(0,0), 'which':1});
+ map.events.triggerEvent('mousemove', {'type':'mousemove', 'xy':new OpenLayers.Pixel(5,5), 'which':1});
+ map.events.triggerEvent('mouseup', {'type':'mouseup', 'xy':new OpenLayers.Pixel(5,5), 'which':1});
+
+ t.eq(map.getCenter().lat, res * 5, "Lat is " + (res * 5) + " after drag");
+ t.eq(map.getCenter().lon, res * -5, "Lon is " + (res * -5) + " after drag");
+ }
+ function test_Control_DragPan_click(t) {
+ t.plan(1);
+ var control = new OpenLayers.Control.DragPan();
+ var map = new OpenLayers.Map("map", {controls:[control]});
+ var layer = new OpenLayers.Layer.WMS("OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'});
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ map.zoomIn();
+ control.activate();
+ map.setCenter = function() {
+ t.ok(false, "map.setCenter should not be called here");
+ };
+ var xy = new OpenLayers.Pixel(0, 0);
+ var down = {
+ 'type': 'mousedown',
+ 'xy': xy,
+ 'which': 1
+ };
+ var move = {
+ 'type': 'mousemove',
+ 'xy': xy,
+ 'which': 1
+ };
+ var up = {
+ 'type': 'mouseup',
+ 'xy': xy,
+ 'which': 1
+ };
+ map.events.triggerEvent('mousedown', down);
+ map.events.triggerEvent('mousemove', move);
+ map.events.triggerEvent('mouseup', up);
+ t.ok(true, "clicking without moving the mouse does not call setCenter");
+ }
+
+
+ </script>
+</head>
+<body>
+ <a id="scale" href="">DragPan</a> <br />
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/DrawFeature.html b/misc/openlayers/tests/Control/DrawFeature.html
new file mode 100644
index 0000000..ef0be5a
--- /dev/null
+++ b/misc/openlayers/tests/Control/DrawFeature.html
@@ -0,0 +1,160 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(1);
+ var control = new OpenLayers.Control.DrawFeature("foo", function() {});
+ t.ok(control instanceof OpenLayers.Control.DrawFeature,
+ "constructor returns an instance");
+ }
+
+ function test_multi(t) {
+ t.plan(4);
+
+ var layer = new OpenLayers.Layer.Vector();
+ var control;
+
+ // multi false by default
+ control = new OpenLayers.Control.DrawFeature(
+ layer, OpenLayers.Handler.Polygon
+ );
+ t.ok(!control.multi, "control.multi false by default");
+ t.ok(!control.handler.multi, "handler.multi false by default");
+
+ // set on handler
+ control = new OpenLayers.Control.DrawFeature(
+ layer, OpenLayers.Handler.Polygon, {multi: true}
+ );
+ t.ok(control.handler.multi, "handler.multi set from control options");
+
+ // respect handlerOptions
+ control = new OpenLayers.Control.DrawFeature(
+ layer, OpenLayers.Handler.Polygon,
+ {multi: true, handlerOptions: {multi: false}}
+ );
+ t.ok(!control.handler.multi, "handlerOptions.multi respected");
+
+ }
+
+ function test_rendererOptions(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map("map");
+ var renderers = ["Canvas", "VML"];
+
+ var layer = new OpenLayers.Layer.Vector(null, {
+ renderers: renderers,
+ rendererOptions: {zIndexing: true},
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+
+ var control = new OpenLayers.Control.DrawFeature(
+ layer, OpenLayers.Handler.Polygon, {autoActivate: true}
+ );
+ map.addControl(control);
+
+ var sketchLayer = control.handler.layer;
+
+ t.eq(sketchLayer.renderers, renderers, "Preferred renderers");
+ t.eq(sketchLayer.rendererOptions.zIndexing, true, "renderer options");
+
+ map.destroy();
+
+ }
+
+ function test_drawFeature(t) {
+ t.plan(3);
+ var layer = new OpenLayers.Layer.Vector();
+ var control = new OpenLayers.Control.DrawFeature(layer, function() {});
+ var geom = {};
+
+ layer.addFeatures = function(features) {
+ t.ok(features[0].geometry == geom, "layer.addFeatures called");
+ t.eq(features[0].state, OpenLayers.State.INSERT, "layer state set");
+ };
+ function handlefeatureadded(event) {
+ t.ok(event.feature.geometry == geom, "featureadded triggered");
+ }
+ control.events.on({"featureadded": handlefeatureadded});
+ control.drawFeature(geom);
+ control.events.un({"featureadded": handlefeatureadded});
+
+ }
+
+ function test_sketch_events(t) {
+ t.plan(11);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ var control = new OpenLayers.Control.DrawFeature(
+ layer, OpenLayers.Handler.Path, {
+ handlerOptions: {persist: true}
+ }
+ );
+ map.addLayer(layer);
+ map.addControl(control);
+ map.zoomToMaxExtent();
+
+ var log;
+ layer.events.on({
+ sketchstarted: function(event) {
+ log['sketchstarted'] = event;
+ },
+ sketchmodified: function(event) {
+ log['sketchmodified'] = event;
+ },
+ sketchcomplete: function(event) {
+ log['sketchcomplete'] = event;
+ }
+ });
+
+ // mock up draw/modify of a point
+ log = {};
+ control.activate();
+ t.eq(log, {}, "[activate] no event triggered");
+
+ log = {};
+ map.events.triggerEvent("mousemove", {xy: new OpenLayers.Pixel(0, 0)});
+ t.eq(log.sketchstarted.type, "sketchstarted", "[mousemove] sketchstarted triggered");
+ t.geom_eq(log.sketchstarted.vertex, new OpenLayers.Geometry.Point(-200, 125), "[mousemove] correct vertex");
+ t.eq(log.sketchmodified.type, "sketchmodified", "[mousemove] sketchmodified triggered");
+ t.geom_eq(log.sketchmodified.vertex, new OpenLayers.Geometry.Point(-200, 125), "[mousemove] correct vertex");
+
+ map.events.triggerEvent("mousedown", {xy: new OpenLayers.Pixel(0, 0)});
+
+ log = {};
+ map.events.triggerEvent("mouseup", {xy: new OpenLayers.Pixel(0, 0)});
+ t.eq(log.sketchmodified.type, "sketchmodified", "[mouseup] sketchmodified triggered");
+ t.geom_eq(log.sketchmodified.vertex, new OpenLayers.Geometry.Point(-200, 125), "[mouseup] correct vertex");
+
+ log = {};
+ map.events.triggerEvent("mousemove", {xy: new OpenLayers.Pixel(10, 10)});
+ t.eq(log.sketchmodified.type, "sketchmodified", "[mousemove] sketchmodified triggered");
+ t.geom_eq(log.sketchmodified.vertex, new OpenLayers.Geometry.Point(-190, 115), "[mousemove] correct vertex");
+
+ log = {};
+ map.events.triggerEvent("dblclick", {xy: new OpenLayers.Pixel(10, 10)});
+ t.eq(log.sketchcomplete.type, "sketchcomplete", "[dblclick] sketchcomplete triggered");
+ t.geom_eq(log.sketchcomplete.feature.geometry,
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(-200, 125),
+ new OpenLayers.Geometry.Point(-190, 115)
+ ]),
+ "[dblclick] correct geometry");
+
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 250px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/EditingToolbar.html b/misc/openlayers/tests/Control/EditingToolbar.html
new file mode 100644
index 0000000..570986d
--- /dev/null
+++ b/misc/openlayers/tests/Control/EditingToolbar.html
@@ -0,0 +1,33 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_ctor_draw(t) {
+ t.plan(5);
+ var map = new OpenLayers.Map('map');
+ var vLayer = new OpenLayers.Layer.Vector();
+ map.addLayer(vLayer);
+
+ var editingToolbar = new OpenLayers.Control.EditingToolbar(vLayer, {
+ citeCompliant: "foo"
+ });
+ map.addControl(editingToolbar);
+
+ t.ok(editingToolbar instanceof OpenLayers.Control.EditingToolbar,
+ "new OpenLayers.Control.EditingToolbar returns object" );
+ t.ok(editingToolbar.controls[0] instanceof OpenLayers.Control.Navigation,
+ "EditingToolbar contains Control.Navigation object" );
+ t.eq(editingToolbar.controls[0].active, true,
+ "First control is active" );
+ t.eq(editingToolbar.controls.length, 4,
+ "EditingToolbar contains 4 Controls" );
+ t.eq(editingToolbar.controls[1].handler.citeCompliant, "foo", "citeCompliant option passed to handler correctly")
+
+ map.destroy();
+ }
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/Geolocate.html b/misc/openlayers/tests/Control/Geolocate.html
new file mode 100644
index 0000000..4e43f39
--- /dev/null
+++ b/misc/openlayers/tests/Control/Geolocate.html
@@ -0,0 +1,129 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var map, control, centerLL
+ watch = null,
+ geolocation= {
+ getCurrentPosition: function(f) {
+ f({
+ coords: { latitude: 10, longitude: 10 }
+ });
+ },
+ watchPosition: function(f) {
+ watch = true;
+ },
+ clearWatch: function() {
+ watch = null;
+ }
+ };
+
+ function test_initialize(t) {
+ t.plan(3);
+ control = new OpenLayers.Control.Geolocate({geolocationOptions: {foo: 'bar'}});
+ t.ok(control instanceof OpenLayers.Control.Geolocate,
+ "new OpenLayers.Control returns object" );
+ t.eq(control.displayClass, "olControlGeolocate", "displayClass is correct" );
+ t.eq(control.geolocationOptions.foo, 'bar',
+ 'provided geolocation options are set in the geolocationOptions prop');
+ }
+ function test_bind(t) {
+ t.plan(3);
+ var control = new OpenLayers.Control.Geolocate({
+ geolocation: geolocation
+ });
+ control.events.register('locationupdated', null, function() {
+ t.ok(true, 'locationupdated event is fired when bound');
+ });
+ map.addControl(control);
+ control.activate();
+ var center = map.getCenter();
+ t.eq(center.lon, 10, 'bound control sets the map lon');
+ t.eq(center.lat, 10, 'bound control sets the map lat');
+ control.deactivate();
+ map.removeControl(control);
+ map.setCenter(centerLL);
+ }
+ function test_unbind(t) {
+ t.plan(3);
+ var control = new OpenLayers.Control.Geolocate({
+ geolocation: geolocation,
+ bind: false
+ });
+ control.events.register('locationupdated', null, function() {
+ t.ok(true, 'locationupdated event is fired when unbound');
+ });
+ map.addControl(control);
+ control.activate();
+ var center = map.getCenter();
+ t.eq(center.lon, 0, 'unbound control doesnt sets the map lon');
+ t.eq(center.lat, 0, 'unbound control doesnt sets the map lat');
+ control.deactivate();
+ map.removeControl(control);
+ map.setCenter(centerLL);
+ }
+ function test_getCurrentLocation(t) {
+ t.plan(5);
+ var control = new OpenLayers.Control.Geolocate({
+ geolocation: geolocation
+ });
+ map.addControl(control);
+ t.eq(control.getCurrentLocation(), false, 'getCurrentLocation return false if control hasnt been activated');
+ control.activate();
+ map.setCenter(centerLL);
+ t.eq(control.getCurrentLocation(), true, 'getCurrentLocation return true if control has been activated');
+ var center = map.getCenter();
+ t.eq(center.lon, 10, 'bound control sets the map lon when calling getCurrentLocation');
+ t.eq(center.lat, 10, 'bound control sets the map lat when calling getCurrentLocation');
+ control.deactivate();
+ map.removeControl(control);
+ map.setCenter(centerLL);
+ var control2 = new OpenLayers.Control.Geolocate({
+ geolocation: geolocation
+ });
+ map.addControl(control2);
+ t.eq(control2.getCurrentLocation(), false, 'getCurrentLocation return false if control is in watch mode');
+ control2.deactivate();
+ map.removeControl(control2);
+ map.setCenter(centerLL);
+ }
+ function test_watch(t) {
+ t.plan(2);
+ var control = new OpenLayers.Control.Geolocate({
+ geolocation: geolocation,
+ watch: true
+ });
+ map.addControl(control);
+ control.activate();
+ t.eq(watch, true, 'watch option makes calls to watchPosition');
+ control.deactivate();
+ t.eq(watch, null, 'deactivate control calls the clearwatch');
+ map.removeControl(control);
+ map.setCenter(centerLL);
+ }
+ function test_destroy(t) {
+ t.plan(1);
+ var control = new OpenLayers.Control.Geolocate({
+ geolocation: geolocation,
+ watch: true
+ });
+ control.activate();
+ control.destroy();
+ t.ok(control.active === false, "control deactivated before being destroyed");
+ }
+
+ function loader() {
+ map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://labs.metacarta.com/wms-c/Basic.py?",
+ {layers: "basic"});
+ map.addLayer(layer);
+ centerLL = new OpenLayers.LonLat(0,0);
+ map.setCenter(centerLL, 5);
+ }
+ </script>
+</head>
+<body onload="loader()">
+ <div id="map" style="width: 256px; height: 256px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/GetFeature.html b/misc/openlayers/tests/Control/GetFeature.html
new file mode 100644
index 0000000..bbdd0e4
--- /dev/null
+++ b/misc/openlayers/tests/Control/GetFeature.html
@@ -0,0 +1,177 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Control_GetFeature_constructor(t) {
+ t.plan(3);
+ var protocol = "foo";
+ var control = new OpenLayers.Control.GetFeature({
+ protocol: protocol
+ });
+ t.ok(control instanceof OpenLayers.Control.GetFeature,
+ "new OpenLayers.Control.SelectFeature returns an instance");
+ t.eq(control.protocol, "foo",
+ "constructor sets protocol correctly");
+
+ control = new OpenLayers.Control.GetFeature({
+ filterType: OpenLayers.Filter.Spatial.INTERSECTS
+ });
+ t.eq(control.filterType, OpenLayers.Filter.Spatial.INTERSECTS,
+ "constructor sets filterType correctly");
+
+ }
+
+ function test_Control_GetFeature_select(t) {
+ t.plan(10);
+ var cssAdded;
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.WMS("foo", "wms", {
+ layers: "foo"
+ });
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(1,2));
+ var feature1 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,2));
+ var feature2 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(2,3));
+ var feature3 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(3,1));
+ var control = new OpenLayers.Control.GetFeature({
+ protocol: new OpenLayers.Protocol({
+ read: function(obj) {
+ cssAdded = OpenLayers.Element.hasClass(map.viewPortDiv,
+ "olCursorWait");
+ obj.callback.call(obj.scope, {
+ features: [feature1, feature2, feature3],
+ success: function() {return true;}
+ });
+ }
+ }),
+ box: true
+ });
+ map.addControl(control);
+
+ var singleTest = function(evt) {
+ t.eq(evt.feature.id, feature1.id, "featureselected callback called with closest feature");
+ }
+ cssAdded = false;
+ control.events.register("featureselected", this, singleTest);
+ control.selectClick({xy: new OpenLayers.Pixel(200, 125)});
+ t.ok(cssAdded,
+ "select adds CSS class (click)");
+ t.ok(!OpenLayers.Element.hasClass(map.viewPortDiv, "olCursorWait"),
+ "callback removes CSS class (click)");
+ control.events.unregister("featureselected", this, singleTest);
+
+ var count = 0;
+ var beforeFeatureSelected = function(evt) {
+ count++;
+ return count < 3;
+ }
+ var features = [];
+ var boxTest = function(evt) {
+ features.push(evt.feature);
+ }
+ var beforeFeaturesSelected = function(evt) {
+ t.eq(evt.features.length, 3, "3 features passed to the beforefeaturesselected handler");
+ }
+ var featuresSelected = function(evt) {
+ t.eq(evt.features.length, 2, "2 features passed to the featuresselected handler");
+ }
+ control.events.register("beforefeatureselected", this, beforeFeatureSelected);
+ control.events.register("featureselected", this, boxTest);
+ control.events.register("beforefeaturesselected", this, beforeFeaturesSelected);
+ control.events.register("featuresselected", this, featuresSelected);
+ cssAdded = false;
+ control.selectBox(new OpenLayers.Bounds(0,0,4,4));
+ control.events.unregister("beforefeatureselected", this, beforeFeatureSelected);
+ control.events.unregister("featureselected", this, boxTest);
+ control.events.unregister("beforefeaturesselected", this, beforeFeaturesSelected);
+ control.events.unregister("featuresselected", this, featuresSelected);
+ t.eq(features.length, 2, "2 features inside box selected");
+ t.eq(features[1].id, feature2.id, "featureselected callback called with multiple features");
+ t.ok(cssAdded,
+ "select adds CSS class (box)");
+ t.ok(!OpenLayers.Element.hasClass(map.viewPortDiv, "olCursorWait"),
+ "callback removes CSS class (box)");
+
+ // allow several features even for single click
+ control.single = false;
+ var multiplePointTest = function(evt) {
+ t.eq(evt.features.length, 3, "3 features passed to the featuresselected handler");
+ }
+ control.events.register("featuresselected", this, multiplePointTest);
+ control.selectClick({xy: new OpenLayers.Pixel(200, 125)});
+ control.events.unregister("featuresselected", this, multiplePointTest);
+ }
+
+ function test_Control_GetFeature_hover(t) {
+ t.plan(9);
+ var cssAdded;
+ var abortedResponse = null;
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.WMS("foo", "wms", {
+ layers: "foo"
+ });
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(1,2));
+ var feature1 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,2));
+ var feature2 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(2,3));
+ var response = new OpenLayers.Protocol.Response();
+ var control = new OpenLayers.Control.GetFeature({
+ protocol: new OpenLayers.Protocol({
+ read: function(obj){
+ cssAdded = OpenLayers.Element.hasClass(map.viewPortDiv,
+ "olCursorWait");
+ obj.callback.call(obj.scope, {
+ features: [feature1, feature2],
+ success: function() {return true;}
+ });
+ return response;
+ },
+ abort: function(response) {
+ abortedResponse = response;
+ }
+ }),
+ hover: true
+ });
+ map.addControl(control);
+
+ var hoverFeature;
+ var hoverTest = function(evt) {
+ t.eq(evt.feature.id, hoverFeature.id, "hoverfeature callback called with closest feature");
+ }
+ var outTest = function(evt) {
+ t.eq(evt.feature.id, feature1.id, "outfeature callback called with previously hovered feature");
+ }
+ control.events.register("hoverfeature", this, hoverTest);
+ control.events.register("outfeature", this, outTest);
+ hoverFeature = feature1;
+ control.selectHover({xy: new OpenLayers.Pixel(200, 125)});
+ t.ok(control.hoverResponse == response,
+ "selectHover stores the protocol response in the hoverResponse property");
+
+ hoverFeature = feature2;
+ cssAdded = false;
+ control.selectHover({xy: new OpenLayers.Pixel(400, 0)});
+ t.ok(cssAdded,
+ "select adds CSS class (hover)");
+ t.ok(!OpenLayers.Element.hasClass(map.viewPortDiv, "olCursorWait"),
+ "callback removes CSS class (hover)");
+
+ OpenLayers.Element.addClass(map.viewPortDiv, "olCursorWait");
+ control.cancelHover();
+ t.ok(abortedResponse == response,
+ "cancelHover calls protocol.abort() with the expected response");
+ t.eq(control.hoverResponse, null,
+ "cancelHover sets this.hoverResponse to null");
+ t.ok(!OpenLayers.Element.hasClass(map.viewPortDiv, "olCursorWait"),
+ "cancelHover removes CSS class");
+
+ control.events.unregister("hoverfeature", this, hoverTest);
+ control.events.unregister("outfeature", this, outTest);
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 250px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/Graticule.html b/misc/openlayers/tests/Control/Graticule.html
new file mode 100644
index 0000000..4aa867f
--- /dev/null
+++ b/misc/openlayers/tests/Control/Graticule.html
@@ -0,0 +1,66 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script src="http://svn.osgeo.org/metacrs/proj4js/trunk/lib/proj4js-compressed.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(2);
+ var options = {};
+ var map = new OpenLayers.Map("map",{projection:"EPSG:4326"});
+ var layer = new OpenLayers.Layer.WMS();
+ map.addLayers([layer]);
+
+ var control = new OpenLayers.Control.Graticule(options);
+ map.addControl(control);
+ map.zoomToMaxExtent();
+
+ t.ok(control.gratLayer instanceof OpenLayers.Layer.Vector,
+ "constructor sets layer correctly");
+ t.ok(control.gratLayer.features.length > 0,
+ "graticule has features");
+ control.destroy();
+ }
+
+ function test_activate(t) {
+ t.plan(7);
+ var map = new OpenLayers.Map("map",{projection:"EPSG:4326"});
+ var layer = new OpenLayers.Layer.WMS();
+ map.addLayers([layer]);
+
+ var control = new OpenLayers.Control.Graticule({});
+ map.addControl(control);
+ map.zoomToMaxExtent();
+
+ t.ok(control.gratLayer.visibility, "Graticule layer is visible by default");
+ control.deactivate();
+ t.ok(control.gratLayer.map == null,
+ "Graticule layer is not in map when control is deactivated");
+ control.destroy();
+
+ var control2 = new OpenLayers.Control.Graticule({autoActivate:false});
+ map.addControl(control2);
+ t.ok(control2.gratLayer.map == null,
+ "Graticule layer is not in map when autoActivate:false");
+ t.ok(control2.gratLayer.features.length == 0,
+ "Graticule layer is empty when autoActivate:false");
+ control2.activate();
+ t.ok(control2.gratLayer.map != null,
+ "Graticule layer is on map when control is activated");
+ t.ok(control2.gratLayer.features.length > 0,
+ "Graticule features refreshed after control is activated");
+ control2.gratLayer.setVisibility(false);
+
+ control2.destroy();
+ t.ok(control2.gratLayer == null,
+ "Graticule layer is destroyed when control is destroyed");
+
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 250px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/KeyboardDefaults.html b/misc/openlayers/tests/Control/KeyboardDefaults.html
new file mode 100644
index 0000000..e190177
--- /dev/null
+++ b/misc/openlayers/tests/Control/KeyboardDefaults.html
@@ -0,0 +1,173 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var map;
+ function test_Control_KeyboardDefaults_constructor (t) {
+ t.plan( 2 );
+
+ control = new OpenLayers.Control.KeyboardDefaults();
+ t.ok( control instanceof OpenLayers.Control.KeyboardDefaults,
+ "new OpenLayers.Control.KeyboardDefaults returns object" );
+ t.eq( control.displayClass, "olControlKeyboardDefaults", "displayClass is correct" );
+ }
+
+ function test_Control_KeyboardDefaults_destroy (t) {
+ t.plan(2);
+
+ map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control.KeyboardDefaults();
+ map.addControl(control);
+ t.ok(control.handler != null, "control.handler is created");
+ control.destroy();
+ t.ok(control.handler == null, "control.handler is null after destroy");
+ map.destroy();
+ }
+
+ function test_Control_KeyboardDefaults_addControl (t) {
+ t.plan( 4 );
+
+ map = new OpenLayers.Map('map');
+ control = new OpenLayers.Control.KeyboardDefaults();
+ t.ok( control instanceof OpenLayers.Control.KeyboardDefaults,
+ "new OpenLayers.Control.KeyboardDefaults returns object" );
+ t.ok( map instanceof OpenLayers.Map,
+ "new OpenLayers.Map creates map" );
+ map.addControl(control);
+ t.ok( control.map === map, "Control.map is set to the map object" );
+ t.ok( OpenLayers.Util.indexOf(map.controls, control), "map.controls contains control" );
+ }
+
+ /* When interpretting
+ * the keycodes below (including the comments associated with them),
+ * consult the URL below. For instance, the Safari browser returns
+ * "IE keycodes", and so is supported by any keycode labeled "IE".
+ *
+ * Very informative URL:
+ * http://unixpapa.com/js/key.html
+ */
+ function test_Control_KeyboardDefaults_KeyDownEvent (t) {
+ t.plan( 25 );
+
+ var evt = {which: 1}, pans = [], zoomIns = 0, zoomOuts = 0;
+
+ map = new OpenLayers.Map('map');
+
+ // mock "pan", "zoomIn" and "zoomOut"
+ map.pan = function(dx, dy) {
+ pans.push({dx: dx, dy: dy});
+ };
+ map.zoomIn = function() {
+ zoomIns++;
+ };
+ map.zoomOut = function() {
+ zoomOuts++;
+ };
+
+ var layer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(layer);
+
+ var control = new OpenLayers.Control.KeyboardDefaults({
+ slideFactor: 100
+ });
+ map.addControl(control);
+
+ map.setCenter(new OpenLayers.LonLat(0, 0), 4);
+
+ // Start new test.
+ evt.keyCode = OpenLayers.Event.KEY_LEFT;
+ control.defaultKeyPress(evt);
+ t.eq(pans.length, 1, '[KEY_LEFT] pan called once');
+ t.eq(pans[0], {dx: -100, dy: 0},
+ '[KEY LEFT] pan called with expected args');
+
+ evt.keyCode = OpenLayers.Event.KEY_RIGHT;
+ control.defaultKeyPress(evt);
+ t.eq(pans.length, 2, '[KEY_RIGHT] pan called once');
+ t.eq(pans[1], {dx: 100, dy: 0},
+ '[KEY RIGHT] pan called with expected args');
+
+ evt.keyCode = OpenLayers.Event.KEY_UP;
+ control.defaultKeyPress(evt);
+ t.eq(pans.length, 3, '[KEY_UP] pan called once');
+ t.eq(pans[2], {dx: 0, dy: -100},
+ '[KEY UP] pan called with expected args');
+
+ evt.keyCode = OpenLayers.Event.KEY_DOWN;
+ control.defaultKeyPress(evt);
+ t.eq(pans.length, 4, '[KEY_DOWN] pan called once');
+ t.eq(pans[3], {dx: 0, dy: 100},
+ '[KEY DOWN] pan called with expected args');
+
+ evt.keyCode = 33;
+ control.defaultKeyPress(evt);
+ t.eq(pans.length, 5, '[33] pan called once');
+ t.eq(pans[4], {dx: 0, dy: -384},
+ '[33] pan called with expected args');
+
+ evt.keyCode = 34;
+ control.defaultKeyPress(evt);
+ t.eq(pans.length, 6, '[34] pan called once');
+ t.eq(pans[5], {dx: 0, dy: 384},
+ '[34] pan called with expected args');
+
+ evt.keyCode = 35;
+ control.defaultKeyPress(evt);
+ t.eq(pans.length, 7, '[35] pan called once');
+ t.eq(pans[6], {dx: 768, dy: 0},
+ '[35] pan called with expected args');
+
+ evt.keyCode = 36;
+ control.defaultKeyPress(evt);
+ t.eq(pans.length, 8, '[36] pan called once');
+ t.eq(pans[7], {dx: -768, dy: 0},
+ '[36] pan called with expected args');
+
+ evt.keyCode = 43;
+ control.defaultKeyPress(evt);
+ t.eq(zoomIns, 1, '[43] zoomIn called once');
+
+ evt.keyCode = 61;
+ control.defaultKeyPress(evt);
+ t.eq(zoomIns, 2, '[61] zoomIn called once');
+
+ evt.keyCode = 187;
+ control.defaultKeyPress(evt);
+ t.eq(zoomIns, 3, '[187] zoomIn called once');
+
+ evt.keyCode = 107;
+ control.defaultKeyPress(evt);
+ t.eq(zoomIns, 4, '[107] zoomIn called once');
+
+ evt.keyCode = 107;
+ control.defaultKeyPress(evt);
+ t.eq(zoomIns, 5, '[107] zoomIn called once');
+
+ evt.keyCode = 45;
+ control.defaultKeyPress(evt);
+ t.eq(zoomOuts, 1, '[45] zoomOut called once');
+
+ evt.keyCode = 109;
+ control.defaultKeyPress(evt);
+ t.eq(zoomOuts, 2, '[109] zoomOut called once');
+
+ evt.keyCode = 189;
+ control.defaultKeyPress(evt);
+ t.eq(zoomOuts, 3, '[189] zoomOut called once');
+
+ evt.keyCode = 95;
+ control.defaultKeyPress(evt);
+ t.eq(zoomOuts, 4, '[95] zoomOut called once');
+
+ map.destroy();
+ }
+
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/LayerSwitcher.html b/misc/openlayers/tests/Control/LayerSwitcher.html
new file mode 100644
index 0000000..c81a779
--- /dev/null
+++ b/misc/openlayers/tests/Control/LayerSwitcher.html
@@ -0,0 +1,249 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var map;
+ OpenLayers.Lang.setCode('en');
+
+ function test_Control_LayerSwitcher_constructor (t) {
+ t.plan( 2 );
+
+ control = new OpenLayers.Control.LayerSwitcher();
+ t.ok( control instanceof OpenLayers.Control.LayerSwitcher, "new OpenLayers.Control.LayerSwitcher returns object" );
+ t.eq( control.displayClass, "olControlLayerSwitcher", "displayClass is correct" );
+ }
+
+ function test_Control_LayerSwitcher_draw (t) {
+ t.plan( 2 );
+
+ map = new OpenLayers.Map('map');
+ control = new OpenLayers.Control.LayerSwitcher();
+ map.addControl(control);
+
+ var div = control.draw();
+ t.ok( control.div != null, "draw makes a div" );
+ t.ok( div != null, "draw returns its div" );
+ }
+ function test_Control_LayerSwitcher_outsideViewport (t) {
+ t.plan( 4 );
+
+ map = new OpenLayers.Map('map');
+ control = new OpenLayers.Control.LayerSwitcher({'div':OpenLayers.Util.getElement('layerswitcher')});
+ map.addControl(control);
+ t.eq(control.div.style.width, "250px", "Div is not minimized when added.");
+ t.ok(control.events.element && control.events.listeners.buttonclick, "[outside] Events instance attached to div and has buttonclick event");
+ control = new OpenLayers.Control.LayerSwitcher();
+ map.addControl(control);
+ t.eq(control.div.style.width, "0px", "Div is minimized when added.");
+ t.ok(!control.events.element && map.events.listeners.buttonclick, "[inside] Events instance not attached to div and buttonclick event registered on map");
+ }
+
+ function test_Control_LayerSwitcher_loadContents(t) {
+
+ t.plan( 10 );
+
+ map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS("WMS",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(layer);
+
+ markers = new OpenLayers.Layer.Markers("markers");
+ map.addLayer(markers);
+
+ control = new OpenLayers.Control.LayerSwitcher();
+ map.addControl(control);
+
+ t.ok(control.layersDiv != null, "correctly makes layers div");
+ t.ok(OpenLayers.Element.hasClass(control.layersDiv, "layersDiv"),
+ "layers div has class layersDiv");
+ t.ok(control.baseLayersDiv != null, "correctly makes layers div");
+ t.ok(OpenLayers.Element.hasClass(control.baseLayersDiv, "baseLayersDiv"),
+ "base layers div has class baseLayersDiv");
+ t.ok(control.dataLayersDiv != null, "correctly makes layers div");
+ t.ok(OpenLayers.Element.hasClass(control.dataLayersDiv, "dataLayersDiv"),
+ "data layers div has class dataLayersDiv");
+ t.ok(control.maximizeDiv != null, "correctly makes resize div");
+ t.ok(OpenLayers.Element.hasClass(control.maximizeDiv, "maximizeDiv"),
+ "maximize div has class maximizeDiv");
+ t.ok(control.minimizeDiv != null, "correctly makes resize div");
+ t.ok(OpenLayers.Element.hasClass(control.minimizeDiv, "minimizeDiv"),
+ "minimize div has class minmizeDiv");
+ }
+
+
+ function test_Control_LayerSwitcher_redraw (t) {
+
+ t.plan( (OpenLayers.BROWSER_NAME == "opera" ? 9 : 19 ) );
+
+ map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS("WMS",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(layer);
+
+ markers = new OpenLayers.Layer.Markers("markers");
+ map.addLayer(markers);
+
+ control = new OpenLayers.Control.LayerSwitcher();
+ map.addControl(control);
+
+ var wmsInput = control.div.childNodes[0].childNodes[1].childNodes[0];
+ t.ok(wmsInput != null, "correctly makes an input for wms layer");
+ t.eq(wmsInput.type, "radio", "wms correctly made a radio button");
+ t.eq(wmsInput.name, control.id + "_baseLayers", "wms correctly named");
+ t.eq(wmsInput.value, layer.name, "wms correctly valued");
+
+ var markersInput = control.div.childNodes[0].childNodes[3].childNodes[0];
+ t.ok(markersInput != null, "correctly makes an input for markers layer");
+ t.eq(markersInput.type, "checkbox", "wms correctly made a radio button");
+ t.eq(markersInput.name, markers.name, "wms correctly named");
+ t.eq(markersInput.value, markers.name, "wms correctly valued");
+
+ t.eq(false, control.checkRedraw(), "check redraw is false");
+ if (OpenLayers.BROWSER_NAME != "opera") {
+ control = new OpenLayers.Control.LayerSwitcher();
+ var myredraw = control.redraw;
+ control.redraw = function() {
+ t.ok(true, "redraw called when setting vis");
+ }
+ map.addControl(control);
+ var func = OpenLayers.Function.bind(myredraw, control);
+ func();
+ markers.setVisibility(false);
+ t.eq(control.checkRedraw(), true, "check redraw is true after changing layer and not letting redraw happen.");
+ map.removeControl(control);
+
+ control = new OpenLayers.Control.LayerSwitcher();
+ var myredraw = control.redraw;
+ control.redraw = function() {
+ t.ok(true, "redraw called when setting inRange");
+ }
+ map.addControl(control);
+ var func = OpenLayers.Function.bind(myredraw, control);
+ func();
+ markers.inRange = false;
+ t.eq(control.checkRedraw(), true, "check redraw is true after changing layer.inRange and not letting redraw happen.");
+ map.removeControl(control);
+
+ control = new OpenLayers.Control.LayerSwitcher();
+ var myredraw = control.redraw;
+ control.redraw = function() {
+ t.ok(true, "redraw called when raising base layer ");
+ }
+
+ map.addControl(control);
+ var func = OpenLayers.Function.bind(myredraw, control);
+ func();
+ map.raiseLayer(layer, 1);
+ t.eq(control.checkRedraw(), true, "check redraw is true after changing layer.inRange and not letting redraw happen.");
+ map.removeControl(control);
+ } else {
+ t.debug_print("FIXME: Some LayerSwitcher tests fail in Opera.");
+ }
+
+ }
+ function test_Control_LayerSwitcher_ascending (t) {
+
+ t.plan( 4 );
+
+ map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS("WMS",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(layer);
+
+ markers = new OpenLayers.Layer.Markers("markers");
+ map.addLayer(markers);
+
+ control = new OpenLayers.Control.LayerSwitcher();
+ map.addControl(control);
+ control2 = new OpenLayers.Control.LayerSwitcher({'ascending':false});
+ map.addControl(control2);
+ t.ok(control.div.childNodes[0].childNodes[0].innerHTML.match("Base Layer"), "Base Layers first in LayerSwitcher with ascending true");
+ t.ok(control.div.childNodes[0].childNodes[2].innerHTML.match("Overlays"), "Overlays in LayerSwitcher with ascending true");
+ t.ok(control2.div.childNodes[0].childNodes[2].innerHTML.match("Base Layer"), "Base Layers last in LayerSwitcher with ascending false");
+ t.ok(control2.div.childNodes[0].childNodes[0].innerHTML.match("Overlays"), "Base Layers last in LayerSwitcher with ascending false");
+ }
+
+ function test_Control_LayerSwitcher_displayInLayerSwitcher (t) {
+
+ t.plan( 2 );
+
+ map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS("WMS",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"}, {'displayInLayerSwitcher': false});
+ map.addLayer(layer);
+
+ control = new OpenLayers.Control.LayerSwitcher();
+ map.addControl(control);
+ t.eq(control.div.childNodes[0].childNodes[0].style.display, "none" , "Base layer display off when no visble base layer");
+
+ map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS("WMS",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(layer);
+
+ control = new OpenLayers.Control.LayerSwitcher();
+ map.addControl(control);
+ t.eq(control.div.childNodes[0].childNodes[0].style.display, "" , "Base layer display on when visble base layer");
+ }
+
+ // See e.g. https://github.com/openlayers/openlayers/issues/866
+ function test_Control_LayerSwitcher_validIds(t){
+ t.plan(2);
+
+ // setup
+ var layername = "Name with spaces & illegal characters * + ~ ` ' ? )",
+ map = new OpenLayers.Map("map", {
+ controls: [
+ new OpenLayers.Control.LayerSwitcher()
+ ],
+ layers: [
+ new OpenLayers.Layer.WMS(
+ layername,
+ "../../img/blank.gif"
+ ),
+ // add another layer with the same name, the generated id
+ // must be different
+ new OpenLayers.Layer.WMS(
+ layername,
+ "../../img/blank.gif"
+ )
+ ]
+ });
+
+ var baselayerDiv = map.controls[0].div.childNodes[0].childNodes[1],
+ firstGeneratedInputId = baselayerDiv.childNodes[0].id,
+ secondGeneratedInputId = baselayerDiv.childNodes[1].id,
+ // legal ids start with a letter and are followed only by word
+ // characters (letters, digits, and underscores) plus the dash (-)
+ // This is only a subset of all allowed charcters inside of ids.
+ allowedIdChars = (/^[a-zA-Z]{1}[\w-]*$/g);
+
+ // tests
+ // validity
+ t.ok(
+ allowedIdChars.test(firstGeneratedInputId),
+ "id only contains letters, digits, underscores and dashes. It " +
+ "starts with a letter."
+ );
+ // uniqueness
+ t.ok(
+ firstGeneratedInputId !== secondGeneratedInputId,
+ "generated ids are different even for equal layernames"
+ );
+
+ // teardown
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1024px; height: 512px;"/>
+ <div id="layerswitcher" style="width:250px; height:256px;" />
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/Measure.html b/misc/openlayers/tests/Control/Measure.html
new file mode 100644
index 0000000..ee6d192
--- /dev/null
+++ b/misc/openlayers/tests/Control/Measure.html
@@ -0,0 +1,386 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+
+ t.plan(1);
+
+ var map = new OpenLayers.Map("map");
+ var control = new OpenLayers.Control.Measure(
+ OpenLayers.Handler.Path, {persist: true}
+ );
+ map.addControl(control);
+
+ t.eq(control.persist, true, "passing persist to constructor sets persist on handler");
+
+ map.destroy();
+
+ }
+
+ function test_cancel(t) {
+
+ t.plan(4);
+
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer(null, {
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ var control = new OpenLayers.Control.Measure(
+ OpenLayers.Handler.Path, {persist: true}
+ );
+ map.addControl(control);
+
+ control.activate();
+
+ try {
+ control.cancel();
+ t.ok(true, "calling cancel before drawing works");
+ } catch(err) {
+ t.fail("calling cancel before drawing causes trouble: " + err);
+ }
+ t.eq(control.active, true, "control remains active after cancel");
+
+ // create a simple measurement
+ function trigger(type, x, y) {
+ map.events.triggerEvent(type, {
+ xy: new OpenLayers.Pixel(x, y)
+ })
+ };
+
+ trigger("mousemove", 0, 0);
+ // keep a reference to the line being drawn
+ var line = control.handler.line;
+ trigger("mousedown", 0, 0);
+ trigger("mouseup", 0, 0);
+ trigger("mousemove", 10, 10);
+ trigger("mousedown", 10, 10);
+ trigger("mouseup", 10, 10);
+ trigger("dblclick", 10, 10);
+
+ // the geometry is finalized, we first confirm that it is persisted
+ t.ok(line.layer === control.handler.layer, "feature persists");
+
+ // cancel and see that sketch is gone
+ control.cancel();
+ t.eq(line.layer, null, "feature is gone after cancel");
+
+ map.destroy();
+ }
+
+ // test for <http://trac.openlayers.org/ticket/2691>
+ function test_partial(t) {
+
+ t.plan(28);
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ units: "m",
+ resolutions: [1],
+ layers: [
+ new OpenLayers.Layer(null, {
+ isBaseLayer: true
+ })
+ ],
+ center: new OpenLayers.LonLat(0, 0)
+ });
+
+ var log = [];
+ var control = new OpenLayers.Control.Measure(
+ OpenLayers.Handler.Path, {persist: true,
+ eventListeners: {
+ measurepartial: function(evt) {
+ log.push(evt);
+ },
+ measure: function(evt){
+ log.push(evt);
+ }
+ },
+ handlerOptions: {
+ pixelTolerance: 0,
+ dblclickTolerance: 0
+ }
+ }
+ );
+ map.addControl(control);
+ control.activate();
+
+
+ // convenience function to trigger mouse events
+ function trigger(type, x, y) {
+ map.events.triggerEvent(type, {
+ xy: new OpenLayers.Pixel(x, y)
+ })
+ };
+
+ // delay in seconds
+ var delay = control.partialDelay / 1000;
+
+ // establish first point
+ trigger("mousemove", 0, 0);
+ trigger("mousedown", 0, 0);
+ trigger("mouseup", 0, 0);
+
+
+ // a) move 10 pixels and click
+ trigger("mousemove", 0, 10);
+ trigger("mousedown", 0, 10);
+ trigger("mouseup", 0, 10);
+
+ // confirm measurepartial is not fired before delay
+ t.eq(log.length, 0, "a) no event fired yet")
+
+ t.delay_call(
+ // wait for delay then confirm event was logged
+ delay, function() {
+ t.eq(log.length, 1, "a) event logged")
+ t.eq(log[0] && log[0].type, "measurepartial", "a) event logged");
+ t.eq(log[0] && log[0].measure, 10, "a) correct measure");
+
+ // b) move 10 pixels and click
+ trigger("mousemove", 0, 20);
+ trigger("mousedown", 0, 20);
+ trigger("mouseup", 0, 20);
+
+ // confirm measurepartial is not fired before delay
+ t.eq(log.length, 1, "b) no event fired yet")
+
+ },
+ delay, function() {
+ t.eq(log.length, 2, "b) event logged");
+ t.eq(log[1] && log[1].type, "measurepartial", "b) correct type");
+ t.eq(log[1] && log[1].measure, 20, "b) correct measure");
+
+ // c) move 10 pixels and click
+ trigger("mousemove", 0, 30);
+ trigger("mousedown", 0, 30);
+ trigger("mouseup", 0, 30);
+ },
+ // wait for half delay and confirm event not logged
+ delay / 2, function() {
+ // confirm measurepartial is not fired before delay
+ t.eq(log.length, 2, "c) no event fired yet")
+ },
+ // wait for rest of delay and confirm event logged
+ delay / 2, function() {
+ t.eq(log.length, 3, "c) event logged");
+ t.eq(log[2] && log[2].type, "measurepartial", "c) correct type");
+ t.eq(log[2] && log[2].measure, 30, "c) correct measure");
+
+ // d) move 10 pixels and click
+ trigger("mousemove", 0, 40);
+ trigger("mousedown", 0, 40);
+ trigger("mouseup", 0, 40);
+
+ // confirm measurepartial is not fired before delay
+ t.eq(log.length, 3, "d) no event fired yet")
+
+ // e) double click to finish
+ trigger("dblclick", 0, 40);
+
+ t.eq(log.length, 4, "e) event logged");
+ t.eq(log[3] && log[3].type, "measure", "e) correct type");
+ t.eq(log[3] && log[3].measure, 40, "e) correct measure");
+ },
+ // wait for rest of delay and confirm no measurepartial logged
+ delay, function() {
+ // confirm measurepartial is not fired after dblclick
+ t.eq(log.length, 4, "e) no additional event fired");
+
+ // change to freehand mode and confirm synchronous event dispatch
+ control.handler.freehand = true;
+ // clear log
+ log = [];
+
+ // f) establish first freehand point
+ trigger("mousemove", 0, 0);
+ trigger("mousedown", 0, 0);
+ t.eq(log.length, 0, "f) no event fired yet")
+
+ // g) move 10 pixels
+ trigger("mousemove", 10, 0);
+
+ t.eq(log.length, 1, "g) event logged");
+ t.eq(log[0] && log[0].type, "measurepartial", "g) correct type");
+ t.eq(log[0] && log[0].measure, 10, "g) correct measure");
+
+ // h) move 10 pixels
+ trigger("mousemove", 20, 0);
+
+ t.eq(log.length, 2, "h) event logged");
+ t.eq(log[1] && log[1].type, "measurepartial", "h) correct type");
+ t.eq(log[1] && log[1].measure, 20, "h) correct measure");
+
+ // i) mouse up to finish
+ trigger("mouseup", 20, 0);
+
+ t.eq(log.length, 3, "i) event logged");
+ t.eq(log[2] && log[2].type, "measure", "i) correct type");
+ t.eq(log[2] && log[2].measure, 20, "i) correct measure");
+
+ // j) clean up
+ log = [];
+ map.destroy();
+ },
+ // wait for delay and confirm event not logged
+ delay, function() {
+ t.eq(log.length, 0, "j) no event fired after destroy");
+ }
+ );
+
+ }
+
+ function test_immediate(t) {
+ t.plan(32);
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ units: "m",
+ resolutions: [1],
+ layers: [
+ new OpenLayers.Layer(null, {
+ isBaseLayer: true
+ })
+ ],
+ center: new OpenLayers.LonLat(0, 0)
+ });
+
+ var log = [];
+ var control = new OpenLayers.Control.Measure(
+ OpenLayers.Handler.Path, {
+ persist: true,
+ immediate: true,
+ eventListeners: {
+ measurepartial: function(evt) {
+ log.push(evt);
+ },
+ measure: function(evt){
+ log.push(evt);
+ }
+ },
+ handlerOptions: {
+ pixelTolerance: 0,
+ dblclickTolerance: 0
+ }
+ }
+ );
+ map.addControl(control);
+ control.activate();
+
+ // convenience function to trigger mouse events
+ function trigger(type, x, y) {
+ map.events.triggerEvent(type, {
+ xy: new OpenLayers.Pixel(x, y)
+ })
+ };
+
+ // delay in seconds
+ var delay = control.partialDelay / 1000;
+
+ // a) establish first point
+ trigger("mousemove", 0, 0);
+ trigger("mousedown", 0, 0);
+ trigger("mouseup", 0, 0);
+
+ // move 10 pixels
+ trigger("mousemove", 0, 10);
+
+ t.eq(log.length, 1, "a) has fired an event");
+
+ t.delay_call(
+ delay, function() {
+ // confirm measurepartial is fired
+ t.eq(log.length, 1, "a) one event logged");
+ t.ok(log[0] && log[0].type == "measurepartial", "a) correct type");
+ // mousemove within the partialDelay fires no event, so the
+ // measure below is the one of the initial point
+ t.eq(log[0]?log[0].measure:-1 , 10, "a) correct measure");
+
+ // b) move 10 pixels
+ trigger("mousemove", 0, 20);
+ // c) move 10 pixels again
+ trigger("mousemove", 0, 30);
+
+ // confirm measurepartial is fired 2 times
+ t.eq(log.length, 3, "b) event logged");
+ t.eq(log[1] && log[1].type, "measurepartial", "b) correct type");
+ t.eq(log[1] && log[1].measure, 20, "b) correct measure");
+ t.eq(log[2] && log[2].type, "measurepartial", "c) correct type");
+ t.eq(log[2] && log[2].measure, 30, "c) correct measure");
+
+ // d) switch immediate measurement off
+ control.setImmediate(false);
+ t.eq(control.immediate, false, "d) immediate is false");
+
+ // e) move 10 pixels and click
+ trigger("mousemove", 0, 40);
+ trigger("mousedown", 0, 40);
+ trigger("mouseup", 0, 40);
+ // confirm measurepartial is not fired before delay
+ t.eq(log.length, 3, "e) no event fired yet")
+ },
+ // wait for delay then confirm event was logged
+ delay, function() {
+ t.eq(log.length, 4, "e) event logged")
+ t.ok(log[3] && log[3].type == "measurepartial", "e) correct type");
+ t.ok(log[3] && log[3].measure == 40, "e) correct measure");
+
+ // f) switch immediate measurement on
+ control.setImmediate(true);
+ t.eq(control.immediate, true, "f) immediate is true");
+
+ // g) move 10 pixels
+ trigger("mousemove", 0, 50);
+ },
+ delay, function() {
+ t.eq(log.length, 5, "g) event logged");
+ t.ok(log[4] && log[4].type == "measurepartial", "g) correct type");
+ t.ok(log[4] && log[4].measure == 50, "g) correct measure");
+
+ // h) move 10 pixels
+ trigger("mousemove", 0, 60);
+
+ t.eq(log.length, 6, "h) event logged");
+ t.ok(log[5] && log[5].type == "measurepartial", "h) correct type");
+ t.ok(log[5] && log[5].measure == 60, "h) correct measure");
+
+ // i) double click to finish
+ trigger("mousedown", 0, 60);
+ t.eq(log.length, 7, "i) event logged");
+ t.eq(log[6] && log[6].type, "measurepartial", "i) correct type");
+ t.eq(log[6] && log[6].measure, 60, "i) correct measure");
+ trigger("mouseup", 0, 60);
+ t.eq(log.length, 7, "i) no event fired yet");
+ },
+ delay, function() {
+ t.eq(log.length, 8, "j) event logged");
+ t.eq(log[7] && log[7].type, "measurepartial", "j) correct type");
+ t.eq(log[7] && log[7].measure, 60, "j) correct measure");
+
+ trigger("dblclick", 0, 60);
+ t.eq(log.length, 9, "k) event logged");
+ t.eq(log[8] && log[8].type, "measure", "k) correct type");
+ t.eq(log[8] && log[8].measure, 60, "k) correct measure");
+ // clear log
+ log = [];
+
+ // l) clean up
+ map.destroy();
+ // wait for delay and confirm event not logged
+ },
+ delay, function() {
+ t.eq(log.length, 0, "l) no event fired after destroy");
+ }
+ );
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 512px; height: 256px;"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/ModifyFeature.html b/misc/openlayers/tests/Control/ModifyFeature.html
new file mode 100644
index 0000000..6226733
--- /dev/null
+++ b/misc/openlayers/tests/Control/ModifyFeature.html
@@ -0,0 +1,828 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(3);
+ var layer = {
+ styleMap: {createSymbolizer: function(){}},
+ events: {
+ on: function() {},
+ un: function() {}
+ }
+ };
+ var options = {
+ documentDrag: true
+ };
+ var control = new OpenLayers.Control.ModifyFeature(layer, options);
+
+ t.ok(control.layer == layer,
+ "constructor sets layer correctly");
+ t.eq(control.handlers.drag.documentDrag, true,
+ "constructor sets options correctly on drag handler");
+ t.eq(control.mode, OpenLayers.Control.ModifyFeature.RESHAPE,
+ "constructor initializes modification mode correctly");
+ control.destroy();
+ }
+
+ function test_destroy(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+ control.destroy();
+ t.eq(control.layer, null, "Layer reference removed on destroy.");
+ map.destroy();
+ }
+
+ function test_activate(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+ map.addControl(control);
+ t.ok(!control.handlers.drag.active,
+ "drag handler is not active prior to activating control");
+ control.activate();
+ t.ok(control.handlers.drag.active,
+ "drag handler is active after activating control");
+
+ map.destroy();
+ }
+
+ function test_initDeleteCodes(t) {
+ t.plan(3);
+ var layer = new OpenLayers.Layer.Vector();
+ var control = new OpenLayers.Control.ModifyFeature(layer, {'deleteCodes': 46});
+ t.eq(control.deleteCodes[0], 46, "Delete code properly turned into an array.");
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+ t.eq(control.deleteCodes[0], 46, "Default deleteCodes include delete");
+ t.eq(control.deleteCodes[1], 68, "Default deleteCodes include 'd'");
+
+ control.destroy();
+ layer.destroy();
+ }
+
+ function test_handleKeypress(t) {
+ t.plan(16);
+
+ /**
+ * There are two things that we want to test here
+ * 1) test that control.deleteCodes are respected
+ * 3) test that a vertex is properly deleted
+ *
+ * In the future, feature deletion may be added to the control.
+ */
+
+ var layer = new OpenLayers.Layer.Vector();
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+ var delKey = 46;
+ var dKey = 100;
+ control.deleteCodes = [delKey, dKey];
+
+ // test that a polygon vertex is deleted for all delete codes
+ var point = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point()
+ );
+ var poly = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Polygon()
+ );
+
+ // mock up vertex deletion
+ var origGetFeatureFromEvent = layer.getFeatureFromEvent;
+ layer.getFeatureFromEvent = function() { return point; };
+ control.feature = poly;
+ // we cannot use selectFeature since the control is not part of a map
+ control._originalGeometry = poly.geometry.clone();
+ control.vertices = [point];
+ point.geometry.parent = {
+ removeComponent: function(geometry) {
+ t.eq(geometry.id, point.geometry.id,
+ "vertex deletion: removeComponent called on parent with proper geometry");
+ }
+ };
+ layer.events.on({
+ "featuremodified": function(event) {
+ t.ok(event.feature.modified !== null, "modified property of feature should have been set");
+ t.eq(event.feature.id, poly.id, "vertex deletion: featuremodifed triggered");
+ },
+ "vertexremoved": function(evt) {
+ layer.events.unregister("vertexremoved", this, arguments.callee);
+ t.eq(evt.feature.id, poly.id, "vertexremoved triggered with correct feature");
+ t.eq(evt.vertex.id, point.geometry.id, "vertexremoved triggered with correct vertex");
+ t.eq(evt.pixel, "foo", "vertexremoved triggered with correct pixel");
+ }
+ });
+ layer.drawFeature = function(feature) {
+ t.eq(feature.id, poly.id,
+ "vertex deletion: drawFeature called with the proper feature");
+ };
+ control.resetVertices = function() {
+ t.ok(true, "vertex deletion: resetVertices called");
+ };
+ control.onModification = function(feature) {
+ t.eq(feature.id, poly.id,
+ "vertex deletion: onModification called with the proper feature");
+ };
+ // run the above four tests twice
+ control.handleKeypress({keyCode:delKey, xy: "foo"});
+ control.handleKeypress({keyCode:dKey});
+ t.eq(control.feature.state, OpenLayers.State.UPDATE, "feature state set to update");
+
+ // now make sure nothing happens if the vertex is mid-drag
+ control.handlers.drag.dragging = true;
+ control.handleKeypress({keyCode:delKey});
+
+ // clean up
+ layer.getFeatureFromEvent = origGetFeatureFromEvent;
+ control.destroy();
+ layer.destroy();
+ }
+
+
+ function test_onUnSelect(t) {
+ t.plan(5);
+ var layer = new OpenLayers.Layer.Vector();
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+ var fakeFeature = {'id':'myid'};
+ control.vertices = 'a';
+ control.virtualVertices = 'b';
+ control.features = true;
+ layer.events.on({"afterfeaturemodified": function(event) {
+ t.eq(event.feature, fakeFeature, "afterfeaturemodified triggered");
+ }});
+ control.onModificationEnd = function (feature) { t.eq(feature.id, fakeFeature.id, "onModificationEnd got feature.") }
+ layer.removeFeatures = function(verts) {
+ t.ok(verts == 'a', "Normal verts removed correctly");
+ }
+ layer.destroyFeatures = function(verts) {
+ t.ok(verts == 'b', "Virtual verts destroyed correctly");
+ }
+ control.unselectFeature(fakeFeature);
+ t.eq(control.feature, null, "feature is set to null");
+
+ layer.destroyFeatures = function() {};
+ control.destroy();
+ layer.destroy();
+ }
+
+ function test_stop_modification(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.Vector("Vectors!", {isBaseLayer: true});
+ var feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(0, 0)
+ );
+ layer.addFeatures([feature]);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0));
+
+
+ // If a feature is to be modified, control.selectFeature gets called.
+ // We want this test to fail if selectFeature gets called.
+ var modified = false;
+
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+ map.addControl(control);
+ control.activate();
+
+ // register a listener that will stop feature modification
+ layer.events.on({"beforefeaturemodified": function() {return false}});
+
+ // we can initiate feature modification by programmatically selecting
+ // a feature
+ control.selectFeature(feature);
+
+ if(modified) {
+ t.fail("selectFeature called, prepping feature for modification");
+ } else {
+ t.ok(true, "the beforefeaturemodified listener stopped feature modification");
+ }
+ }
+
+ function test_selectFeature(t) {
+ t.plan(12);
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.Vector("Vectors!", {isBaseLayer: true});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0));
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+ control.vertices = [];
+ control.virtualVertices = [];
+ var callback = function(obj) {
+ t.ok(obj.feature == fakeFeature, "beforefeaturemodified triggered");
+ };
+ layer.events.on({"beforefeaturemodified": callback});
+ control.onModificationStart = function(feature) { t.eq(feature.id, fakeFeature.id, "On Modification Start called with correct feature."); }
+
+ // Start of testing
+
+ control.collectVertices = function() { t.fail("Collect vertices called when geom is a point"); }
+ var fakeFeature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0, 0));
+
+ // Points don't call collectVertices
+ control.selectFeature(fakeFeature);
+ control.unselectFeature(fakeFeature);
+
+ control.collectVertices = function() {
+ t.ok(true, "collectVertices called");
+ this.vertices = 'a';
+ this.virtualVertices = 'd';
+ layer.addFeatures(this.vertices);
+ layer.addFeatures(this.virtualVertices);
+ }
+
+ layer.addFeatures = function(features) {
+ t.ok(features == 'a' || features == 'd', "features passed correctly");
+ }
+ layer.destroyFeatures = function() {};
+
+ fakeFeature.geometry = new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(0, 0),
+ new OpenLayers.Geometry.Point(1, 1)
+ ])
+ ]);
+
+ // OnSelect calls collectVertices and passes features to layer
+ control.selectFeature(fakeFeature);
+ control.unselectFeature(fakeFeature);
+ layer.destroyFeatures = OpenLayers.Layer.Vector.prototype.destroyFeatures;
+
+ control.vertices = ['a'];
+ control.virtualVertices = [{destroy: function() {}}];
+
+ layer.addFeatures = function(features) {}
+
+ layer.removeFeatures = function(features) {
+ t.eq(features.length, 1, "Correct feature length passed in");
+ }
+
+ // Features are removed whenever they exist
+ control.selectFeature(fakeFeature);
+
+ control.destroy();
+
+ // layer.destroy() will call removeFeatures with an empty array, make
+ // removeFeatures reference an empty function to prevent the above
+ // test to fail
+ layer.removeFeatures = function(features) {};
+ layer.destroy();
+ }
+
+ function test_resetVertices(t) {
+ t.plan(20);
+ var layer = new OpenLayers.Layer.Vector();
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+ var point = new OpenLayers.Geometry.Point(5,6);
+ var point2 = new OpenLayers.Geometry.Point(7,8);
+ var point3 = new OpenLayers.Geometry.Point(9,10);
+
+ control.feature = new OpenLayers.Feature.Vector(point);
+ control.resetVertices();
+ t.eq(control.vertices.length, 0, "Correct vertices length");
+ t.eq(control.virtualVertices.length, 0, "Correct virtual vertices length.");
+
+ var multiPoint = new OpenLayers.Geometry.MultiPoint([point, point2]);
+ control.feature = new OpenLayers.Feature.Vector(multiPoint);
+ control.resetVertices();
+ t.eq(control.vertices.length, 2, "Correct vertices length with multipoint");
+ t.eq(control.virtualVertices.length, 0, "Correct virtual vertices length (multipoint).");
+
+ var line = new OpenLayers.Geometry.LineString([point, point2]);
+ control.feature = new OpenLayers.Feature.Vector(line);
+ control.resetVertices();
+ t.eq(control.vertices.length, 2, "Correct vertices length with line");
+ t.eq(control.virtualVertices.length, 1, "Correct virtual vertices length (linestring).");
+
+ var polygon = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing([point, point2, point3])]);
+ control.feature = new OpenLayers.Feature.Vector(polygon);
+ control.resetVertices();
+ t.eq(control.vertices.length, 3, "Correct vertices length with polygon");
+ t.eq(control.virtualVertices.length, 3, "Correct virtual vertices length (polygon).");
+
+ control.mode = OpenLayers.Control.ModifyFeature.DRAG;
+ control.resetVertices();
+ t.ok(control.dragHandle != null, "Drag handle is set");
+ t.eq(control.vertices.length, 0, "Correct vertices length with polygon (DRAG)");
+
+ control.mode = OpenLayers.Control.ModifyFeature.ROTATE;
+ control.resetVertices();
+ t.ok(control.radiusHandle != null, "Radius handle is set");
+ t.eq(control.vertices.length, 0, "Correct vertices length with polygon (ROTATE)");
+
+ control.mode = OpenLayers.Control.ModifyFeature.RESIZE;
+ control.resetVertices();
+ t.ok(control.radiusHandle != null, "Radius handle is set");
+ t.eq(control.vertices.length, 0, "Correct vertices length with polygon (RESIZE)");
+
+ control.mode = OpenLayers.Control.ModifyFeature.RESHAPE;
+ control.resetVertices();
+ t.ok(control.radiusHandle == null, "Radius handle is not set (RESHAPE)");
+ t.eq(control.vertices.length, 3, "Correct vertices length with polygon (RESHAPE)");
+ t.eq(control.virtualVertices.length, 3, "Correct virtual vertices length (RESHAPE)");
+
+ control.mode = OpenLayers.Control.ModifyFeature.RESIZE | OpenLayers.Control.ModifyFeature.RESHAPE;
+ control.resetVertices();
+ t.ok(control.radiusHandle != null, "Radius handle is set (RESIZE|RESHAPE)");
+ t.eq(control.vertices.length, 0, "No vertices when both resizing and reshaping (RESIZE|RESHAPE)");
+ t.eq(control.virtualVertices.length, 0, "No virtual vertices when both resizing and reshaping (RESIZE|RESHAPE)");
+
+ control.destroy();
+ layer.destroy();
+ }
+
+ function test_dragVertex(t) {
+ t.plan(8);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+ map.addControl(control);
+ control.activate();
+
+ map.zoomToMaxExtent();
+
+ var log = {};
+ layer.events.on({
+ "vertexmodified": function(event) {
+ log.event = event;
+ }
+ });
+
+ // pretend to drag a point
+ var feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(0, 0)
+ );
+ control.feature = feature;
+ var pixel = new OpenLayers.Pixel(-100, 100);
+ control.dragVertex(feature, pixel);
+ t.eq(log.event.type, "vertexmodified", "[drag point] vertexmodified triggered");
+ t.geom_eq(log.event.vertex, feature.geometry, "[drag point] listeners receive correct vertex");
+ t.eq(log.event.feature.id, feature.id, "[drag point] listeners receive correct feature");
+ t.ok(log.event.pixel === pixel, "[drag point] listeners receive correct pixel");
+
+ // pretend to drag vertex of a linestring
+ var vert = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(0, 0)
+ );
+ var feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LineString([
+ vert.geometry, new OpenLayers.Geometry.Point(10, 0)
+ ])
+ );
+ control.feature = feature;
+ var pixel = new OpenLayers.Pixel(-100, 100);
+ control.dragVertex(vert, pixel);
+ t.eq(log.event.type, "vertexmodified", "[drag vertex] vertexmodified triggered");
+ t.geom_eq(log.event.vertex, vert.geometry, "[drag vertex] listeners receive correct vertex");
+ t.eq(log.event.feature.id, feature.id, "[drag vertex] listeners receive correct feature");
+ t.ok(log.event.pixel === pixel, "[drag vertex] listeners receive correct pixel");
+
+
+ map.destroy();
+ }
+ function test_collectDragHandle(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,1));
+ layer.addFeatures([feature]);
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+ map.addControl(control);
+ control.activate();
+ control.feature = feature;
+ control.collectDragHandle();
+ t.ok(control.dragHandle != null, "Drag handle created");
+ t.ok(control.dragHandle._sketch == true, "Handle has _sketch true");
+ t.ok(control.dragHandle.renderIntent == control.vertexRenderIntent,"Render intent for handle set");
+ t.ok(control.layer.getFeatureById(control.dragHandle.id) != null, "Drag handle added to layer");
+ }
+ function test_collectRadiusHandle(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,1));
+ layer.addFeatures([feature]);
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+ map.addControl(control);
+ control.activate();
+ control.feature = feature;
+ control.collectRadiusHandle();
+ t.ok(control.radiusHandle != null, "Radius handle created");
+ t.ok(control.radiusHandle._sketch == true, "Radius has _sketch true");
+ t.ok(control.radiusHandle.renderIntent == control.vertexRenderIntent,"Render intent for handle set");
+ t.ok(control.layer.getFeatureById(control.radiusHandle.id) != null, "Drag radius added to layer");
+ }
+ function test_onDrag(t) {
+ t.plan(1);
+ t.ok(true, "onDrag not tested yet.");
+ }
+
+ function test_dragComplete(t) {
+ t.plan(8);
+ var layer = new OpenLayers.Layer.Vector();
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+
+ var fakeFeature = {
+ 'geometry': { 'id':'myGeom'},
+ 'id': 'fakeFeature'
+ };
+ layer.addFeatures = function (verts) {
+ t.ok(verts == 'virtual' || verts == 'normal', verts + " verts correct");
+ }
+ layer.removeFeatures = function (verts) {
+ t.ok(verts == 'previous virtual' || verts == 'previous normal', verts + " verts correct");
+ }
+ layer.events.on({"featuremodified": function(event) {
+ t.eq(event.feature, fakeFeature, "featuremodified triggered");
+ }});
+ control.onModification = function(feat) {
+ t.eq(feat.id, fakeFeature.id, "onModification gets correct feat");
+ }
+ control.collectVertices = function() {
+ t.ok(true, "collectVertices called");
+ this.vertices = 'normal';
+ this.virtualVertices = 'virtual';
+ layer.addFeatures(this.vertices);
+ layer.addFeatures(this.virtualVertices);
+ }
+ control.feature = fakeFeature;
+ control.vertices = 'previous normal';
+ control.virtualVertices = 'previous virtual';
+ control.dragComplete();
+ t.eq(fakeFeature.state, OpenLayers.State.UPDATE, "feature state set to UPDATE");
+
+ control.destroy();
+
+ // layer.destroy() will call removeFeatures with an empty array, make
+ // removeFeatures reference an empty function to prevent the above
+ // test to fail
+ layer.removeFeatures = function(verts) {};
+ layer.destroy();
+ }
+
+ function test_deactivate(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+ map.addControl(control);
+
+ control.handlers.keyboard.deactivate = function() {
+ t.ok(true,
+ "control.deactivate calls deactivate on keyboard handler");
+ }
+ control.handlers.drag.deactivate = function() {
+ t.ok(true,
+ "control.deactivate calls deactivate on drag handler");
+ }
+ control.active = true;
+ control.deactivate();
+
+ control.handlers.keyboard.deactivate = OpenLayers.Handler.Keyboard.prototype.deactivate;
+ control.handlers.drag.deactivate = OpenLayers.Handler.Drag.prototype.deactivate;
+ map.destroy();
+ }
+
+ function test_onModificationStart(t) {
+ t.plan(5);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector(null, {
+ styleMap: new OpenLayers.StyleMap({
+ "vertex": new OpenLayers.Style({foo: "bar"})
+ }, {extendDefault: false})
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+ map.addControl(control);
+ control.activate();
+
+ // make sure onModificationStart is called on feature selection
+ var testFeature = new OpenLayers.Feature.Vector(
+ OpenLayers.Geometry.fromWKT("LINESTRING(3 4,10 50,20 25)")
+ );
+ layer.addFeatures([testFeature]);
+ control.onModificationStart = function(feature) {
+ t.eq(feature.id, testFeature.id,
+ "onModificationStart called with the right feature");
+ };
+ control.selectFeature(testFeature);
+
+ // make sure styles are set correctly from default style
+ t.eq(control.virtualStyle, OpenLayers.Util.applyDefaults({
+ strokeOpacity: 0.3,
+ fillOpacity: 0.3
+ }, OpenLayers.Feature.Vector.style["default"]), "virtual style set correctly");
+ var vertex = layer.features[layer.features.length-1];
+ t.eq(vertex.renderIntent, null, "vertex style set correctly - uses default style");
+ control.unselectFeature(testFeature);
+
+ // make sure styles are set correctly with vertexRenderIntent
+ control = new OpenLayers.Control.ModifyFeature(layer, {vertexRenderIntent: "vertex"});
+ map.addControl(control);
+ control.activate();
+ control.selectFeature(testFeature);
+ t.eq(control.virtualStyle, {
+ strokeOpacity: 0.3,
+ fillOpacity: 0.3,
+ foo: "bar"
+ }, "virtual style set correctly");
+ var vertex = layer.features[layer.features.length-1];
+ t.eq(vertex.renderIntent, "vertex", "vertex style set correctly - uses 'vertex' renderIntent");
+ control.unselectFeature(testFeature);
+
+ map.destroy();
+ }
+
+ function test_onModification(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+ map.addControl(control);
+ control.activate();
+
+ // make sure onModification is called on drag complete
+ var point = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(Math.random(), Math.random())
+ );
+ control.feature = point;
+ control.onModification = function(feature) {
+ t.eq(feature.id, point.id,
+ "onModification called with the right feature on drag complete");
+ };
+ control.dragComplete();
+
+ // make sure onModification is called on vertex deletion
+ var poly = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Polygon()
+ );
+ var oldDraw = layer.drawFeature;
+ layer.drawFeature = function() {
+ return;
+ };
+ control.feature = poly;
+ control.vertices = [point];
+ layer.events.on({"featuremodified": function(event) {
+ t.eq(event.feature.id, poly.id, "featuremodified triggered");
+ }});
+
+ control.onModification = function(feature) {
+ t.eq(feature.id, poly.id,
+ "onModification called with the right feature on vertex delete");
+ };
+ point.geometry.parent = poly.geometry;
+ origGetFeatureFromEvent = layer.getFeatureFromEvent;
+ layer.getFeatureFromEvent = function() { return point; };
+ control.handleKeypress({keyCode:46});
+ layer.drawFeature = oldDraw;
+ layer.getFeatureFromEvent = origGetFeatureFromEvent;
+
+ map.destroy();
+ }
+
+ function test_onModificationEnd(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+ map.addControl(control);
+ control.activate();
+
+ // make sure onModificationEnd is called on unselect feature
+ var testFeature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(Math.random(), Math.random())
+ );
+ layer.events.on({"afterfeaturemodified": function(event) {
+ t.eq(event.feature.id, testFeature.id, "afterfeaturemodified triggered");
+ t.eq(event.modified, false, "afterfeaturemodified event given proper modified property (false - feature was not modified in this case)");
+ }});
+ control.onModificationEnd = function(feature) {
+ t.eq(feature.id, testFeature.id,
+ "onModificationEnd called with the right feature");
+ };
+ control.unselectFeature(testFeature);
+
+ map.destroy();
+ }
+
+ function test_events(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.ModifyFeature(layer);
+ map.addControl(control);
+ control.activate();
+
+ // make sure onModificationStart is called on feature selection
+ var testFeature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(Math.random(), Math.random())
+ );
+
+ // test that beforefeatureselected is triggered
+ function handle_beforefeatureselected(event) {
+ t.ok(event.feature == testFeature, "beforefeatureselected called with the correct feature");
+ }
+ layer.events.on({
+ "beforefeatureselected": handle_beforefeatureselected
+ });
+ layer.events.triggerEvent("beforefeatureselected", {
+ feature: testFeature
+ });
+ layer.events.un({
+ "beforefeatureselected": handle_beforefeatureselected
+ });
+
+ // test that beforefeatureselected is triggered
+ function handle_featureselected(event) {
+ t.ok(event.feature == testFeature, "featureselected called with the correct feature");
+ }
+ layer.events.on({
+ "featureselected": handle_featureselected
+ });
+ layer.events.triggerEvent("featureselected", {
+ feature: testFeature
+ });
+ layer.events.un({
+ "featureselected": handle_featureselected
+ });
+
+ map.destroy();
+ }
+
+ function test_standalone(t) {
+
+ t.plan(17);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+
+ var f1 = new OpenLayers.Feature.Vector(
+ OpenLayers.Geometry.fromWKT("LINESTRING(3 4,10 50,20 25)")
+ );
+ var f2 = new OpenLayers.Feature.Vector(
+ OpenLayers.Geometry.fromWKT("POLYGON((1 1,5 1,5 5,1 5,1 1),(2 2, 3 2, 3 3, 2 3,2 2))")
+ );
+ var f3 = new OpenLayers.Feature.Vector(
+ OpenLayers.Geometry.fromWKT("POINT(10 15)")
+ );
+ var f4 = new OpenLayers.Feature.Vector(
+ OpenLayers.Geometry.fromWKT("POINT(15 10)")
+ );
+ layer.addFeatures([f1, f2, f3, f4]);
+
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.ModifyFeature(layer, {standalone: true});
+ map.addControl(control);
+
+ var log = [];
+ layer.events.on({
+ beforefeaturemodified: function(evt) {
+ layer.events.unregister("beforefeaturemodified", this, arguments.callee);
+ log.push(evt);
+ },
+ featuremodified: function(evt) {
+ log.push(evt);
+ },
+ afterfeaturemodified: function(evt) {
+ log.push(evt);
+ }
+ });
+
+ // activate control
+ control.activate();
+ t.eq(control.active, true, "[activate] control activated");
+
+ // manually select feature for editing
+ control.selectFeature(f1);
+ t.eq(log.length, 1, "[select f1] beforefeaturemodified triggered");
+ t.ok(control.feature === f1, "[select f1] control.feature set to f1");
+ log = []
+
+ // manually unselect feature for editing
+ control.unselectFeature(f1);
+ t.eq(control.feature, null, "[unselect f1] control.feature set to null");
+ t.eq(log.length, 1, "[unselect f1] event logged");
+ t.eq(log[0].type, "afterfeaturemodified", "[unselect f1] afterfeaturemodified triggered");
+ t.ok(log[0].feature === f1, "[unselect f1] correct feature");
+ t.eq(log[0].modified, false, "[unselect f1] feature not actually modified");
+
+ // clear log and select new feature for editing
+ log = [];
+ control.selectFeature(f2);
+ t.ok(control.feature === f2, "[select f2] control.feature set to f2");
+
+ // deactivate control and confirm feature is unselected
+ control.deactivate();
+ t.eq(log.length, 1, "[deactivate] event logged");
+ t.eq(log[0].type, "afterfeaturemodified", "[deactivate] afterfeaturemodified triggered");
+ t.ok(log[0].feature === f2, "[deactivate] correct feature");
+ t.eq(log[0].modified, false, "[deactivate] feature not actually modified");
+
+ // select the polygon feature to make sure that we can drag vertices and
+ // virtual vertices
+ control.selectFeature(f2);
+ var origGetFeatureFromEvent = layer.getFeatureFromEvent;
+ layer.getFeatureFromEvent = function() { return control.vertices[0]; };
+ control.handlers.drag.callbacks.down.call(control, new OpenLayers.Pixel(0,0));
+ t.ok(control.vertex === control.vertices[0], "can drag vertex of feature f2");
+ t.ok(control.feature === f2, "dragging a vertex does not change the selected feature");
+ layer.getFeatureFromEvent = function() { return control.virtualVertices[0]; };
+ control.handlers.drag.callbacks.down.call(control, new OpenLayers.Pixel(0,0));
+ t.ok(control.vertex === control.virtualVertices[0], "can drag virtual vertex of feature f2");
+ t.ok(control.feature === f2, "dragging a vertex does not change the selected feature");
+ layer.getFeatureFromEvent = origGetFeatureFromEvent;
+ control.deactivate();
+
+ map.destroy();
+
+ }
+
+ function test_setFeatureState(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector("vector", {isBaseLayer: true});
+ map.addLayer(layer);
+ var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,2));
+ layer.addFeatures([feature]);
+ var control = new OpenLayers.Control.ModifyFeature(layer, {standalone: true});
+ map.addControl(control);
+
+ control.selectFeature(feature);
+ var originalGeometry = feature.geometry;
+
+ t.ok(control._originalGeometry, "original geometry stored for later use in setFeatureState");
+
+ feature.geometry = new OpenLayers.Geometry.Point(2,3);
+ control.modified = true;
+ control.setFeatureState();
+
+ t.eq(feature.state, OpenLayers.State.UPDATE, "feature state set to UPDATE");
+ t.geom_eq(feature.modified.geometry, originalGeometry, "original geometry stored on the modified property");
+ t.eq(control._originalGeometry, undefined, "original geometry deleted once it is set on the modified property");
+ }
+
+ function test_createVertices(t) {
+ t.plan(2);
+ var layer = new OpenLayers.Layer.Vector();
+ var control = new OpenLayers.Control.ModifyFeature(layer, {
+ createVertices: false
+ });
+ var line = new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(5, 6),
+ new OpenLayers.Geometry.Point(7, 8),
+ new OpenLayers.Geometry.Point(9, 10)
+ ]);
+ control.feature = new OpenLayers.Feature.Vector(line);
+ control.resetVertices();
+
+ t.eq(control.vertices.length, 3, "Correct vertices length with createVertices is false");
+ t.eq(control.virtualVertices.length, 0, "Correct virtual vertices length with createVertices is false");
+ control.destroy();
+ }
+
+ function test_moveLayerToTop_moveLayerBack(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var layer1 = new OpenLayers.Layer.Vector();
+ var layer2 = new OpenLayers.Layer.Vector();
+ map.addLayers([layer1, layer2]);
+ var control = new OpenLayers.Control.ModifyFeature(layer1);
+ map.addControl(control);
+ control.activate();
+ t.ok(layer1.div.style.zIndex > layer2.div.style.zIndex, "layer raised so events don't get swallowed");
+ control.deactivate();
+ t.ok(layer1.div.style.zIndex < layer2.div.style.zIndex, 'layer order restored on deactivation');
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 250px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/MousePosition.html b/misc/openlayers/tests/Control/MousePosition.html
new file mode 100644
index 0000000..0695e16
--- /dev/null
+++ b/misc/openlayers/tests/Control/MousePosition.html
@@ -0,0 +1,109 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var map, control;
+ function test_initialize (t) {
+ t.plan( 2 );
+
+ control = new OpenLayers.Control.MousePosition();
+ t.ok( control instanceof OpenLayers.Control.MousePosition, "new OpenLayers.Control returns object" );
+ t.eq( control.displayClass, "olControlMousePosition", "displayClass is correct" );
+ }
+ function test_destroy(t) {
+ t.plan(1);
+
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control.MousePosition();
+ map.addControl(control);
+
+ var listeners = map.events.listeners.mousemove.length;
+ control.destroy();
+
+ t.eq(map.events.listeners.mousemove.length, listeners - 1, "mousemove event is unregistered");
+ map.destroy();
+ }
+ function test_addControl(t) {
+ t.plan(4);
+
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control.MousePosition();
+ map.addControl(control);
+
+ t.ok(control.map === map, "Control.map is set to the map object");
+ t.ok(map.controls[map.controls.length - 1] === control, "map.controls contains control");
+ t.eq(parseInt(control.div.style.zIndex), map.Z_INDEX_BASE['Control'] + 5, "Control div zIndexed properly" );
+ t.eq(parseInt(map.viewPortDiv.lastChild.style.zIndex), map.Z_INDEX_BASE['Control'] + 5, "Viewport div contains control div");
+ map.destroy();
+ }
+ function test_redraw_noLayer_displayProjection(t) {
+ t.plan(4);
+ var control = new OpenLayers.Control.MousePosition({'emptyString':''});
+ var map = new OpenLayers.Map('map');
+ map.addControl(control);
+ var control2 = new OpenLayers.Control.MousePosition();
+ map.addControl(control2);
+ t.eq(control2.emptyString, null, "Emptystring is null");
+ t.eq(control.div.innerHTML, "", "innerHTML set correctly");
+ control.redraw({'xy': new OpenLayers.Pixel(10,10)});
+ control.redraw({'xy': new OpenLayers.Pixel(12,12)});
+ t.eq(control.div.innerHTML, "", "innerHTML set correctly");
+ var l = new OpenLayers.Layer('name', {'isBaseLayer': true});
+ map.addLayer(l);
+ map.zoomToMaxExtent();
+ control.redraw({'xy': new OpenLayers.Pixel(10,10)});
+ control.redraw({'xy': new OpenLayers.Pixel(12,12)});
+ t.eq(control.div.innerHTML, "-175.78125, 85.78125", "innerHTML set correctly when triggered.");
+ map.destroy();
+ }
+ function test_formatOutput(t) {
+ t.plan(1);
+ var control = new OpenLayers.Control.MousePosition({
+ prefix: 'prefix',
+ suffix: 'suffix',
+ separator: 'separator',
+ numDigits: 3
+ });
+ var lonlat = new OpenLayers.LonLat(0.75699, 0.37365);
+ var val = control.formatOutput(lonlat);
+ t.eq(val, 'prefix0.757separator0.374suffix', 'formatOutput correctly formats the mouse position output');
+ }
+ function test_deactivate(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer(null, {isBaseLayer: true});
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ // Auxiliary function
+ function trigger(type, x, y) {
+ map.events.triggerEvent(type, {
+ xy: new OpenLayers.Pixel(x, y)
+ })
+ };
+
+ var control = new OpenLayers.Control.MousePosition();
+ map.addControl(control);
+ trigger("mousemove", 0, 0);
+
+ trigger("mousemove", 0, 1);
+ t.ok(control.div.innerHTML != "",
+ "Shows the position after add control (with autoActivate) and move");
+ control.deactivate();
+ t.ok(control.div.innerHTML == "",
+ "Position is not displayed after deactivate and move");
+ trigger("mousemove", 0, 2);
+ t.ok(control.div.innerHTML == "",
+ "Position is not displayed after move when deactivate");
+ control.activate();
+ trigger("mousemove", 0, 3);
+ t.ok(control.div.innerHTML != "",
+ "Shows the position after activate and move");
+
+ map.destroy();
+ }
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/NavToolbar.html b/misc/openlayers/tests/Control/NavToolbar.html
new file mode 100644
index 0000000..9b3bbec
--- /dev/null
+++ b/misc/openlayers/tests/Control/NavToolbar.html
@@ -0,0 +1,45 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var map;
+ function test_Control_NavToolbar_constructor (t) {
+ t.plan( 4 );
+ control = new OpenLayers.Control.NavToolbar();
+ t.ok( control instanceof OpenLayers.Control.NavToolbar, "new OpenLayers.Control.NavToolbar returns object" );
+ t.eq( control.displayClass, "olControlNavToolbar", "displayClass is correct" );
+ t.ok( control.controls[0] instanceof OpenLayers.Control.Navigation, "NavToolbar contains Control.Navigation object" );
+ t.ok( control.controls[1] instanceof OpenLayers.Control.ZoomBox, "NavToolbar contains Control.ZoomBox object" );
+ }
+ function test_Control_NavToolbar_addControl (t) {
+ t.plan( 6 );
+ map = new OpenLayers.Map('map');
+ control = new OpenLayers.Control.NavToolbar();
+ t.ok( control instanceof OpenLayers.Control.NavToolbar, "new OpenLayers.Control.NavToolbar returns object" );
+ t.ok( map instanceof OpenLayers.Map, "new OpenLayers.Map creates map" );
+ map.addControl(control);
+ t.ok( control.map === map, "Control.map is set to the map object" );
+ t.ok( map.controls[4] === control, "map.controls contains control" );
+ t.eq( parseInt(control.div.style.zIndex), map.Z_INDEX_BASE['Control'] + 7, "Control div zIndexed properly" );
+ t.eq( parseInt(map.viewPortDiv.lastChild.style.zIndex), map.Z_INDEX_BASE['Control'] + 7, "Viewport div contains control div" );
+ // t.eq( control.div.style.top, "6px", "Control div top located correctly by default");
+
+ }
+
+ function test_Control_NavToolbar_defaultControl (t) {
+ t.plan( 1 );
+ var map = new OpenLayers.Map('map');
+
+ var nav = new OpenLayers.Control.NavToolbar();
+ map.addControl(nav);
+
+ t.eq(nav.controls[0].active, true, "First control is active" );
+
+ map.destroy();
+ }
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/Navigation.html b/misc/openlayers/tests/Control/Navigation.html
new file mode 100644
index 0000000..e73ee42
--- /dev/null
+++ b/misc/openlayers/tests/Control/Navigation.html
@@ -0,0 +1,200 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_Control_Navigation_constructor (t) {
+ t.plan( 3 );
+ var temp = OpenLayers.Control.prototype.initialize;
+ OpenLayers.Control.prototype.initialize = function() {
+ t.ok(true, "OpenLayers.Control's constructor called");
+ };
+
+ var control = new OpenLayers.Control.Navigation();
+ t.ok( control instanceof OpenLayers.Control.Navigation, "new OpenLayers.Control returns object" );
+
+ t.ok( !control.handleRightClicks, "'handleRightClicks' property is disabled by default");
+
+ OpenLayers.Control.prototype.initialize = temp;
+ }
+
+ function test_draw(t) {
+ t.plan(5);
+ var map = new OpenLayers.Map({div: 'map', controls: []});
+ var control = new OpenLayers.Control.Navigation();
+ map.addControl(control);
+ t.ok(control.handlers.click instanceof OpenLayers.Handler.Click,
+ "click handler set in instance");
+ t.ok(control.dragPan instanceof OpenLayers.Control.DragPan,
+ "drag pan control set in instance");
+ t.ok(control.zoomBox instanceof OpenLayers.Control.ZoomBox,
+ "zoom box control set in instance");
+ t.ok(control.handlers.wheel instanceof OpenLayers.Handler.MouseWheel,
+ "mousewheel handler set in instance");
+ t.ok(control.pinchZoom instanceof OpenLayers.Control.PinchZoom,
+ "pinch zoom control set in instance");
+ map.destroy();
+ }
+
+ function test_Control_Navigation_destroy (t) {
+ t.plan(12);
+
+ var temp = OpenLayers.Control.prototype.destroy;
+ OpenLayers.Control.prototype.destroy = function() {
+ t.ok(true, "OpenLayers.Control's destroy called");
+ temp.call(this);
+ };
+
+ var control = {
+ events: {
+ destroy: function() {
+ t.ok(true, "events destroyed");
+ }
+ },
+ 'deactivate': function() {
+ t.ok(true, "navigation control deactivated before being destroyed");
+ },
+ 'dragPan': {
+ 'destroy': function() {
+ t.ok(true, "dragPan destroyed");
+ }
+ },
+ 'zoomBox': {
+ 'destroy': function() {
+ t.ok(true, "zoomBox destroyed");
+ }
+ },
+ 'pinchZoom': {
+ 'destroy': function() {
+ t.ok(true, "pinchZoom destroyed");
+ }
+ },
+ handlers: {
+ 'wheel': {
+ 'destroy': function() {
+ t.ok(true, "wheelHandler destroyed");
+ }
+ },
+ 'click': {
+ 'destroy': function() {
+ t.ok(true, "clickHandler destroyed");
+ }
+ }
+ }
+ };
+
+ //this will also trigger one test by calling OpenLayers.Control's destroy
+ // and three more for the destruction of dragPan, zoomBox, and wheelHandler
+ OpenLayers.Control.Navigation.prototype.destroy.apply(control, []);
+
+ t.eq(control.dragPan, null, "'dragPan' set to null");
+ t.eq(control.zoomBox, null, "'zoomBox' set to null");
+ t.eq(control.pinchZoom, null, "'pinchZoom' set to null");
+ t.eq(control.handlers, null, "handlers set to null");
+
+ OpenLayers.Control.prototype.destroy = temp;
+ }
+
+ function test_Control_Navigation_disableZoomBox(t) {
+ t.plan(2);
+ var nav = new OpenLayers.Control.Navigation();
+ var zb = new OpenLayers.Control.ZoomBox({});
+ nav.zoomBox = zb;
+ zb.activate();
+ nav.disableZoomBox();
+ t.eq(nav.zoomBoxEnabled, false, "zoom box deactivated");
+ t.eq(zb.active, false, "zoom box control deactivated");
+ }
+
+ function test_Control_Navigation_enableZoomBox(t) {
+ t.plan(2);
+ var nav = new OpenLayers.Control.Navigation();
+ var zb = new OpenLayers.Control.ZoomBox({});
+ nav.zoomBox = zb;
+ nav.active = true;
+ nav.enableZoomBox();
+ t.eq(nav.zoomBoxEnabled, true, "zoom box activated");
+ t.eq(zb.active, true, "zoom box control activated");
+ }
+
+ function test_Control_Navigation_disableZoomWheel(t) {
+ t.plan(2);
+ var nav = new OpenLayers.Control.Navigation();
+ var wheel = new OpenLayers.Handler.MouseWheel(nav, {});
+ nav.handlers.wheel = wheel;
+ wheel.register = function() {};
+ wheel.unregister = function() {};
+ wheel.activate();
+ nav.disableZoomWheel();
+ t.eq(nav.zoomWheelEnabled, false, "mouse wheel deactivated");
+ t.eq(wheel.active, false, "mouse wheel handler deactivated");
+ }
+
+ function test_Control_Navigation_enableZoomWheel(t) {
+ t.plan(2);
+ var nav = new OpenLayers.Control.Navigation({zoomWheelEnabled: false});
+ nav.active = true;
+ var wheel = new OpenLayers.Handler.MouseWheel(nav, {});
+ wheel.register = function() {};
+ wheel.unregister = function() {};
+ nav.handlers.wheel = wheel;
+ nav.enableZoomWheel();
+ t.eq(nav.zoomWheelEnabled, true, "mouse wheel activated");
+ t.eq(wheel.active, true, "mouse wheel handler activated");
+ }
+
+ function test_touches_zoom(t) {
+ t.plan(3);
+ var nav = new OpenLayers.Control.Navigation({zoomWheelEnabled: false});
+ var map = new OpenLayers.Map({
+ div: "map",
+ zoomMethod: null,
+ controls: [nav],
+ layers: [
+ new OpenLayers.Layer(null, {isBaseLayer: true})
+ ],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 3
+ });
+ t.eq(map.getZoom(), 3, "map zoom starts at 3");
+ nav.handlers.click.callback("click", [{lastTouches: ["foo", "bar"]}]);
+ t.eq(map.getZoom(), 2, "map zooms out with a two touch tap");
+ nav.handlers.click.callback("click", [{}]);
+ t.eq(map.getZoom(), 2, "map doesn't do anything with click");
+
+ map.destroy();
+ }
+
+ function test_documentDrag(t) {
+
+ t.plan(2);
+
+ /**
+ * These tests confirm that the documentDrag property is false by
+ * default and is passed on to the DragPan control. Tests of panning
+ * while dragging outside the viewport should go in the DragPan tests.
+ * Tests of the document events and appropriate callbacks from the
+ * handler should go in the Drag handler tests.
+ */
+
+ var nav = new OpenLayers.Control.Navigation();
+ t.eq(nav.documentDrag, false, "documentDrag false by default");
+ // nav.destroy(); // fails if called before draw
+
+ var map = new OpenLayers.Map({
+ div: document.body,
+ controls: [new OpenLayers.Control.Navigation({documentDrag: true})]
+ });
+ nav = map.controls[0];
+
+ t.eq(nav.dragPan.documentDrag, true, "documentDrag set on the DragPan control");
+ map.destroy();
+
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 256px; height: 256px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/NavigationHistory.html b/misc/openlayers/tests/Control/NavigationHistory.html
new file mode 100644
index 0000000..c992ff2
--- /dev/null
+++ b/misc/openlayers/tests/Control/NavigationHistory.html
@@ -0,0 +1,245 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(4);
+ control = new OpenLayers.Control.NavigationHistory();
+ t.ok(control instanceof OpenLayers.Control.NavigationHistory,
+ "constructor returns correct instance");
+ t.eq(control.displayClass, "olControlNavigationHistory",
+ "displayClass is correct");
+ t.ok(control.next instanceof OpenLayers.Control.Button,
+ "constructor creates next control");
+ t.ok(control.previous instanceof OpenLayers.Control.Button,
+ "constructor creates previous control");
+ }
+
+ function test_destroy(t) {
+ t.plan(2);
+ control = new OpenLayers.Control.NavigationHistory();
+ control.next.destroy = function() {
+ t.ok(true, "destroy calls next.destroy");
+ }
+ control.previous.destroy = function() {
+ t.ok(true, "destroy calls previous.destroy");
+ }
+ control.destroy();
+ }
+
+ function test_previous(t) {
+ var numStates = 10;
+
+ t.plan(
+ numStates * 3 // for lon, lat, zoom
+ + 3 // for confirming that previous with empty stack works
+ );
+
+ var history = new Array(numStates);
+ for(var i=0; i<numStates; ++i) {
+ history[i] = {
+ center: new OpenLayers.LonLat(
+ (i * 360 / numStates) - 180, (i * 180 / numStates) - 90
+ ),
+ zoom: i
+ };
+ }
+
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer(
+ "test", {isBaseLayer: true}
+ );
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.NavigationHistory();
+ map.addControl(control);
+
+ // set previous states
+ for(i=0; i<numStates; ++i) {
+ map.setCenter(history[i].center, history[i].zoom);
+ }
+ // test previous states
+ for(i=numStates-1; i>=0; --i) {
+ t.eq(map.getCenter().lon, history[i].center.lon, "(step " + i + ") lon correct");
+ t.eq(map.getCenter().lat, history[i].center.lat, "(step " + i + ") lat correct");
+ t.eq(map.getZoom(), history[i].zoom, "(step " + i + ") zoom correct");
+ control.previous.trigger();
+ }
+ // test previous with empty stack
+ t.eq(map.getCenter().lon, history[0].center.lon, "(step 0 again) lon correct");
+ t.eq(map.getCenter().lat, history[0].center.lat, "(step 0 again) lat correct");
+ t.eq(map.getZoom(), history[0].zoom, "(step 0 again) zoom correct");
+ }
+
+ function test_next(t) {
+ var numStates = 10;
+
+ t.plan(
+ numStates * 3 // for lon, lat, zoom
+ + 3 // for confirming that next with empty stack works
+ );
+
+ var history = new Array(numStates);
+ for(var i=0; i<numStates; ++i) {
+ history[i] = {
+ center: new OpenLayers.LonLat(
+ (i * 360 / numStates) - 180, (i * 180 / numStates) - 90
+ ),
+ zoom: i
+ };
+ }
+
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer(
+ "test", {isBaseLayer: true}
+ );
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.NavigationHistory();
+ map.addControl(control);
+
+ // set previous states
+ for(i=0; i<numStates; ++i) {
+ map.setCenter(history[i].center, history[i].zoom);
+ }
+ // set next states
+ for(i=numStates-1; i>=0; --i) {
+ control.previous.trigger();
+ }
+ // test next states
+ for(i=0; i<numStates; ++i) {
+ t.eq(map.getCenter().lon, history[i].center.lon, "(step " + i + ") lon correct");
+ t.eq(map.getCenter().lat, history[i].center.lat, "(step " + i + ") lat correct");
+ t.eq(map.getZoom(), history[i].zoom, "(step " + i + ") zoom correct");
+ control.next.trigger();
+ }
+ // test next with empty stack
+ t.eq(map.getCenter().lon, history[numStates-1].center.lon, "(step " + (numStates-1) + " again) lon correct");
+ t.eq(map.getCenter().lat, history[numStates-1].center.lat, "(step " + (numStates-1) + " again) lat correct");
+ t.eq(map.getZoom(), history[numStates-1].zoom, "(step " + (numStates-1) + " again) zoom correct");
+ }
+
+ function test_limit(t) {
+ var numStates = 10;
+ var limit = 3;
+
+ t.plan(
+ numStates * 6 // for previous & next lon, lat, zoom
+ );
+
+ var history = new Array(numStates);
+ for(var i=0; i<numStates; ++i) {
+ history[i] = {
+ center: new OpenLayers.LonLat(
+ (i * 360 / numStates) - 180, (i * 180 / numStates) - 90
+ ),
+ zoom: i
+ };
+ }
+
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer(
+ "test", {isBaseLayer: true}
+ );
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.NavigationHistory({limit: limit});
+ map.addControl(control);
+
+ // set previous states
+ for(i=0; i<numStates; ++i) {
+ map.setCenter(history[i].center, history[i].zoom);
+ }
+ // test previous states (only up to limit should work)
+ var state;
+ for(i=numStates-1; i>=0; --i) {
+ state = Math.max(i, numStates - limit - 1);
+ t.eq(map.getCenter().lon, history[state].center.lon, "(previous step " + i + ") lon correct: state " + state);
+ t.eq(map.getCenter().lat, history[state].center.lat, "(previous step " + i + ") lat correct: state " + state);
+ t.eq(map.getZoom(), history[state].zoom, "(previous step " + i + ") zoom correct: state " + state);
+ control.previous.trigger();
+ }
+ // test next states
+ for(i=0; i<numStates; ++i) {
+ state = Math.min(numStates - 1, numStates - limit - 1 + i);
+ t.eq(map.getCenter().lon, history[state].center.lon, "(next step " + i + ") lon correct: state " + state);
+ t.eq(map.getCenter().lat, history[state].center.lat, "(next step " + i + ") lat correct: state " + state);
+ t.eq(map.getZoom(), history[state].zoom, "(next step " + i + ") zoom correct: state " + state);
+ control.next.trigger();
+ }
+
+ }
+
+ function test_clear(t) {
+ t.plan(7);
+ var map = new OpenLayers.Map("map", {zoomMethod: null});
+ var layer = new OpenLayers.Layer(
+ "test", {isBaseLayer: true}
+ );
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ var control = new OpenLayers.Control.NavigationHistory();
+ map.addControl(control);
+
+ t.ok(!control.previous.active, "previous control not active");
+ t.ok(!control.next.active, "next control not active");
+
+ map.zoomTo(4);
+ t.ok(control.previous.active, "previous control is active after a move");
+ t.ok(!control.next.active, "next control is not active after a move");
+
+ control.clear();
+ t.eq(control.previousStack.length + control.nextStack.length, 0, "stacks are empty after a clear");
+ t.ok(!control.previous.active, "previous control not active after a clear");
+ t.ok(!control.next.active, "next control not active after a clear");
+
+ control.destroy();
+ }
+
+ function test_reprojection(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer(
+ "test", {isBaseLayer: true}
+ );
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ var control = new OpenLayers.Control.NavigationHistory();
+ map.addControl(control);
+
+ map.zoomTo(4);
+ var bounds = map.getExtent().clone();
+ var expected = bounds.transform(new OpenLayers.Projection('EPSG:4326'),
+ new OpenLayers.Projection('EPSG:900913'));
+ // change the projection to EPSG:900913
+ var projSettings = {
+ units: "m",
+ maxExtent: new OpenLayers.Bounds(-20037508, -20037508, 20037508, 20037508),
+ maxResolution: 156543.0339
+ };
+ map.setOptions(projSettings);
+ map.projection = 'EPSG:900913';
+ delete projSettings.maxResolution;
+ projSettings.projection = new OpenLayers.Projection('EPSG:900913');
+ layer.addOptions(projSettings);
+ layer.initResolutions();
+
+ map.zoomTo(7);
+
+ // go back one in the history
+ control.previous.trigger();
+
+ t.eq(map.getExtent().left.toFixed(3), expected.left.toFixed(3), "The extent [left] is reprojected correctly");
+ t.eq(map.getExtent().right.toFixed(3), expected.right.toFixed(3), "The extent [right] is reprojected correctly");
+ // top and bottom cannot be checked here since in EPSG:900913 the extent is not a rectangle so they are adjusted.
+
+ control.destroy();
+
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 100px; height: 100px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/OverviewMap.html b/misc/openlayers/tests/Control/OverviewMap.html
new file mode 100644
index 0000000..a5a598d
--- /dev/null
+++ b/misc/openlayers/tests/Control/OverviewMap.html
@@ -0,0 +1,266 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var map, control;
+
+ function test_initialize(t) {
+ t.plan( 2 );
+
+ control = new OpenLayers.Control.OverviewMap();
+ t.ok( control instanceof OpenLayers.Control.OverviewMap,
+ "new OpenLayers.Control.OverviewMap returns object" );
+ t.eq( control.displayClass,
+ "olControlOverviewMap", "displayClass is correct" );
+ }
+
+ function test_divs_title(t) {
+ t.plan(2);
+
+ control = new OpenLayers.Control.OverviewMap({
+ maximizeTitle: "maximize title",
+ minimizeTitle: "minimize title"
+ });
+ map = new OpenLayers.Map('map', {
+ layers: [new OpenLayers.Layer("layer", {isBaseLayer: true})],
+ controls: [control]
+ });
+ map.zoomToMaxExtent();
+ t.eq(control.maximizeDiv.title, "maximize title", "maximizeDiv.title is correct");
+ t.eq(control.minimizeDiv.title, "minimize title", "minimizeDiv.title is correct");
+ map.destroy();
+ }
+
+ function test_setMap(t) {
+ t.plan(4);
+
+ var setMapTest = function(map) {
+ t.ok(true,
+ "Handler.setMap called for " + this.CLASS_NAME);
+ this.map = map;
+ };
+ var drag_setMap = OpenLayers.Handler.Drag.prototype.setMap;
+ OpenLayers.Handler.Drag.prototype.setMap = setMapTest;
+ var click_setMap = OpenLayers.Handler.Click.prototype.setMap;
+ OpenLayers.Handler.Click.prototype.setMap = setMapTest;
+
+ map = new OpenLayers.Map('map', {
+ layers : [new OpenLayers.Layer("layer", {isBaseLayer: true})],
+ controls: []
+ });
+ control = new OpenLayers.Control.OverviewMap();
+
+ map.addControl(control);
+
+ map.zoomToMaxExtent();
+ t.eq(control.handlers.drag.map.id, control.ovmap.id,
+ "drag.map is correct");
+ t.eq(control.handlers.click.map.id, control.ovmap.id,
+ "click.map is correct");
+
+ map.destroy();
+ OpenLayers.Handler.Drag.prototype.setMap = drag_setMap;
+ OpenLayers.Handler.Click.prototype.setMap = click_setMap;
+ }
+
+ function test_destroy(t) {
+ t.plan(6);
+
+ // set up
+
+ var log_drag = [], log_click = [], control;
+
+ map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer("layer", {isBaseLayer: true}));
+
+ control = new OpenLayers.Control.OverviewMap();
+ map.addControl(control);
+
+ map.zoomToMaxExtent();
+
+ control.handlers.drag.destroy = function() {
+ log_drag.push({"map": !!this.map.events});
+ };
+ control.handlers.click.destroy = function() {
+ log_click.push({"map": !!this.map.events});
+ };
+
+ // test
+
+ control.destroy();
+ t.eq(log_drag.length, 2,
+ "destroy() destroys drag handler twice, expected");
+ if (log_drag.length == 2) {
+ t.eq(log_drag[0].map, true,
+ "destroy() destroys drag handler before ovmap is destroyed (0)");
+ t.eq(log_drag[1].map, false,
+ "destroy() destroys drag handler after ovmap is destroyed (1)");
+ }
+ t.eq(log_click.length, 2,
+ "destroy() destroys click handler twice, expected");
+ if (log_click.length == 2) {
+ t.eq(log_click[0].map, true,
+ "destroy() destroys click handler before ovmap is destroyed (0)");
+ t.eq(log_click[1].map, false,
+ "destroy() destroys click handler after ovmap is destroyed (1)");
+ }
+
+ // tear down
+ map.destroy();
+ }
+
+ function test_addControl (t) {
+ t.plan( 6 );
+ map = new OpenLayers.Map('map');
+ control = new OpenLayers.Control.OverviewMap();
+ t.ok( control instanceof OpenLayers.Control.OverviewMap,
+ "new OpenLayers.Control.OverviewMap returns object" );
+ t.ok( map instanceof OpenLayers.Map,
+ "new OpenLayers.Map creates map" );
+ map.addControl(control);
+ t.ok( control.map === map,
+ "Control.map is set to the map object" );
+ t.ok( map.controls[4] === control,
+ "map.controls contains control" );
+ t.eq( parseInt(control.div.style.zIndex), map.Z_INDEX_BASE['Control'] + 5,
+ "Control div zIndexed properly" );
+ t.eq( parseInt(map.viewPortDiv.lastChild.style.zIndex), map.Z_INDEX_BASE['Control'] + 5,
+ "Viewport div contains control div" );
+
+ map.destroy();
+ }
+
+ function test_control_events (t) {
+ t.plan( 10 );
+
+ map = new OpenLayers.Map('map', {
+ // when we recenter, don't waste time animating the panning
+ // without this, the test fails in Firefox 10.0.1 on Linux
+ panMethod: null,
+ layers: [ new OpenLayers.Layer('Test Layer', {isBaseLayer: true}) ]
+ });
+
+ control = new OpenLayers.Control.OverviewMap();
+ map.addControl(control, new OpenLayers.Pixel(20,20));
+
+ var centerLL = new OpenLayers.LonLat(-71,42);
+ map.setCenter(centerLL, 11);
+
+ t.delay_call(
+ 0.1,
+ function() {
+ var overviewCenter = control.ovmap.getCenter();
+ var overviewZoom = control.ovmap.getZoom();
+ t.eq(overviewCenter.lon, -71,
+ "OverviewMap center lon correct");
+ t.eq(overviewCenter.lat, 42,
+ "OverviewMap center lat correct");
+ t.eq(overviewZoom, 8,
+ "OverviewMap zoom correct");
+
+ control.mapDivClick({'xy':new OpenLayers.Pixel(5,5)});
+ },
+ 0.1,
+ function() {
+ var cent = map.getCenter();
+ t.eq(cent.lon, -71.3515625,
+ "Clicking on OverviewMap has correct effect on map lon");
+ t.eq(cent.lat, 42.17578125,
+ "Clicking on OverviewMap has correct effect on map lat");
+
+ control.handlers.drag = {
+ last: new OpenLayers.Pixel(5,5),
+ destroy: function() {}
+ };
+ control.rectDrag(new OpenLayers.Pixel(15, 15));
+ control.updateMapToRect();
+ },
+ 0.1,
+ function() {
+ var cent = map.getCenter();
+ t.eq(cent.lon, -71.2734375,
+ "Dragging on OverviewMap has correct effect on map lon");
+ t.eq(cent.lat, 42.09765625,
+ "Dragging on OverviewMap has correct effect on map lat");
+
+ map.setCenter(new OpenLayers.LonLat(0,0), 0);
+ var overviewCenter = control.ovmap.getCenter();
+ var overviewZoom = control.ovmap.getZoom();
+ t.eq(overviewCenter.lon, 0,
+ "OverviewMap center lon correct -- second zoom");
+ t.eq(overviewCenter.lat, 0,
+ "OverviewMap center lat correct -- second zoom");
+ t.eq(overviewZoom, 0,
+ "OverviewMap zoomcorrect -- second zoom");
+ map.destroy();
+ }
+ );
+ }
+
+ function test_initialize_maximized(t) {
+ t.plan(4);
+
+ control = new OpenLayers.Control.OverviewMap()
+ map = new OpenLayers.Map('map', {
+ layers : [new OpenLayers.Layer("layer", {isBaseLayer: true})],
+ controls: [control]
+ });
+
+ t.eq(control.maximized, false,
+ "OverviewMap is not maximized by default");
+ t.eq(control.element.style.display, 'none',
+ "OverviewMap.element is not visible");
+ map.destroy();
+
+ control = new OpenLayers.Control.OverviewMap({
+ maximized: true
+ })
+ map = new OpenLayers.Map('map', {
+ layers : [new OpenLayers.Layer("layer", {isBaseLayer: true})],
+ controls: [control]
+ });
+ t.eq(control.maximized, true,
+ "OverviewMap.maximized is set");
+ t.eq(control.element.style.display, '',
+ "OverviewMap.element is visible");
+
+ map.destroy();
+ }
+
+ function test_custom_div(t) {
+ t.plan(3);
+ var div = document.createElement('div');
+
+ control = new OpenLayers.Control.OverviewMap({
+ div: div
+ });
+
+ map = new OpenLayers.Map('map', {
+ layers : [new OpenLayers.Layer("layer", {isBaseLayer: true})],
+ controls: [control]
+ });
+
+ t.eq(control.maximizeDiv, null,
+ "OverviewMap does not create maximize div");
+ t.eq(control.minimizeDiv, null,
+ "OverviewMap does not create minimize div");
+
+ var exc;
+ try {
+ control.maximizeControl();
+ control.minimizeControl();
+ } catch(e) {
+ exc = e;
+ }
+
+ t.eq(exc, undefined, 'maximize and minimize do not trigger an exception');
+
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1024px; height: 512px;"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/Pan.html b/misc/openlayers/tests/Control/Pan.html
new file mode 100644
index 0000000..0c9dfaf
--- /dev/null
+++ b/misc/openlayers/tests/Control/Pan.html
@@ -0,0 +1,201 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+function test_Pan_constructor (t) {
+ t.plan( 2 );
+
+ // setup
+ var control = new OpenLayers.Control.Pan(
+ "Gargoyle" // the direction, here mocked up
+ );
+
+ // tests
+ //
+ t.ok(
+ control instanceof OpenLayers.Control.Pan,
+ "new OpenLayers.Control.Pan returns object"
+ );
+ t.eq(
+ control.displayClass, "olControlPanGargoyle",
+ "displayClass is correct"
+ );
+
+ // tear down
+ control.destroy();
+}
+
+function test_Pan_type (t) {
+ t.plan( 1 );
+
+ // setup
+ var control = new OpenLayers.Control.Pan();
+
+ // tests
+ //
+ t.eq(
+ control.type,
+ OpenLayers.Control.TYPE_BUTTON,
+ "Pan control is of type OpenLayers.Control.TYPE_BUTTON"
+ );
+
+ // tear down
+ control.destroy();
+}
+
+function test_Pan_constants (t) {
+ var dirs = [
+ 'North',
+ 'East',
+ 'South',
+ 'West'
+ ],
+ numDirs = dirs.length,
+ dir, uc_dir;
+
+ t.plan(numDirs);
+
+ for ( ; numDirs > 0; numDirs-- ) {
+ dir = dirs[numDirs - 1 ];
+ uc_dir = dir.toUpperCase();
+
+ t.eq(
+ OpenLayers.Control.Pan[ uc_dir ],
+ dir,
+ "A constant 'OpenLayers.Control.Pan." + uc_dir + "' is defined "+
+ "and has the correct value of '" + dir + "'."
+ );
+ }
+}
+
+function test_Pan_trigger (t) {
+ t.plan( 12 );
+
+ // set up
+ var controls = {
+ n: new OpenLayers.Control.Pan(OpenLayers.Control.Pan.NORTH),
+ e: new OpenLayers.Control.Pan(OpenLayers.Control.Pan.EAST),
+ s: new OpenLayers.Control.Pan(OpenLayers.Control.Pan.SOUTH),
+ w: new OpenLayers.Control.Pan(OpenLayers.Control.Pan.WEST)
+ },
+ controlKey, control,
+ zoomlevel = 5,
+ center = new OpenLayers.LonLat(25,25),
+ log = {
+ dx: null,
+ dy: null
+ },
+ map = new OpenLayers.Map("map", {
+ allOverlays: true,
+ layers: [
+ new OpenLayers.Layer.Vector()
+ ],
+ center: center,
+ zoom: zoomlevel
+ }),
+ oldZoom;
+
+ // overwrite native Map::pan
+ map.pan = function(dx, dy) {
+ log = {
+ dx: dx,
+ dy: dy
+ };
+ OpenLayers.Map.prototype.pan.apply(map, arguments);
+ };
+
+ oldCenter = map.getCenter().toString();
+
+ for (controlKey in controls) {
+ if (controls.hasOwnProperty(controlKey)) {
+ control = controls[controlKey];
+ // trigger the control; nothing should change, we aren't added yet.
+ control.trigger();
+
+ t.ok(
+ log.dx === null && log.dy === null,
+ 'Calling trigger on a non added control doesn\'t do anything.'
+ );
+
+ // reset log object
+ log = {
+ dx: null,
+ dy: null
+ };
+ }
+ }
+
+ // now lets add the controls, and trigger them again
+ for (controlKey in controls) {
+ if (controls.hasOwnProperty(controlKey)) {
+ control = controls[controlKey];
+ map.addControl(control);
+ // trigger again, now ...
+ control.trigger();
+
+ // ... the center should change ...
+ t.ok(
+ log.dx !== null && log.dy !== null,
+ 'Calling trigger on an added pan control calls map.pan()... '
+ );
+
+ // ... with sane arguments according to the passed direction.
+ switch (control.direction) {
+ case OpenLayers.Control.Pan.NORTH:
+ t.ok(
+ log.dx === 0 && log.dy < 0,
+ '... with sane arguments: pan north only results in ' +
+ 'negative delta y'
+ );
+ break;
+ case OpenLayers.Control.Pan.SOUTH:
+ t.ok(
+ log.dx === 0 && log.dy > 0,
+ '... with sane arguments: pan south only results in ' +
+ 'positive delta y'
+ );
+ break;
+ case OpenLayers.Control.Pan.WEST:
+ t.ok(
+ log.dx < 0 && log.dy === 0,
+ '... with sane arguments: pan west only results in ' +
+ 'negative delta x'
+ );
+ break;
+ case OpenLayers.Control.Pan.EAST:
+ t.ok(
+ log.dx > 0 && log.dy === 0,
+ '... with sane arguments: pan east only results in ' +
+ 'positive delta x'
+ );
+ break;
+ }
+
+ // reset log-object
+ log = {
+ dx: null,
+ dy: null
+ };
+ // always set to initial center and zoom:
+ map.setCenter(center, zoomlevel);
+ }
+ }
+
+ // tear down
+ for (controlKey in controls) {
+ if (controls.hasOwnProperty(controlKey)) {
+ control = controls[controlKey];
+ control.destroy();
+ }
+ }
+ map.destroy();
+}
+
+ </script>
+ </head>
+ <body>
+ <div id="map" style="width: 1000px; height: 1000px;"></div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/Control/PanPanel.html b/misc/openlayers/tests/Control/PanPanel.html
new file mode 100644
index 0000000..978a051
--- /dev/null
+++ b/misc/openlayers/tests/Control/PanPanel.html
@@ -0,0 +1,61 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_constructor (t) {
+ t.plan(2);
+
+ // set up
+ var control;
+
+ // tests
+ control = new OpenLayers.Control.PanPanel({slideFactor: 200});
+ t.ok(control.controls[0].slideFactor == 200 &&
+ control.controls[1].slideFactor == 200 &&
+ control.controls[2].slideFactor == 200 &&
+ control.controls[3].slideFactor == 200,
+ "ctor sets slideFactor in all Pan controls");
+
+ control.destroy();
+
+ control = new OpenLayers.Control.PanPanel({slideRatio: .5});
+ t.ok(control.controls[0].slideRatio == .5 &&
+ control.controls[1].slideRatio == .5 &&
+ control.controls[2].slideRatio == .5 &&
+ control.controls[3].slideRatio == .5,
+ "ctor sets slideRatio in all Pan controls");
+
+ control.destroy();
+ }
+
+ function test_slide(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map", {
+ panMethod: null,
+ controls: [
+ new OpenLayers.Control.PanPanel(),
+ new OpenLayers.Control.PanPanel({slideRatio: .5})
+ ],
+ layers: [new OpenLayers.Layer(null, {isBaseLayer: true})],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 1
+ });
+
+ map.controls[0].controls[0].trigger();
+ map.controls[0].controls[2].trigger();
+ map.pan(-50, 50);
+ t.eq(map.getCenter().toShortString(), "0, 0", "correct pan distance with slideFactor");
+
+ map.controls[1].controls[0].trigger();
+ map.controls[1].controls[2].trigger();
+ map.pan(-128, 64);
+ t.eq(map.getCenter().toShortString(), "0, 0", "correct pan distance with slideRatio");
+
+ map.destroy();
+ }
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 256px; height: 128px;"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/PanZoom.html b/misc/openlayers/tests/Control/PanZoom.html
new file mode 100644
index 0000000..4982fb0
--- /dev/null
+++ b/misc/openlayers/tests/Control/PanZoom.html
@@ -0,0 +1,244 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var map;
+ function test_Control_PanZoom_constructor (t) {
+ t.plan( 4 );
+
+ control = new OpenLayers.Control.PanZoom();
+ t.ok( control instanceof OpenLayers.Control.PanZoom, "new OpenLayers.Control.PanZoom returns object" );
+ t.eq( control.displayClass, "olControlPanZoom", "displayClass is correct" );
+ control = new OpenLayers.Control.PanZoom({position: new OpenLayers.Pixel(100,100)});
+ t.eq( control.position.x, 100, "PanZoom X Set correctly.");
+ t.eq( control.position.y, 100, "PanZoom y Set correctly.");
+ }
+ function test_Control_PanZoom_addControl (t) {
+ t.plan( 8 );
+ map = new OpenLayers.Map('map');
+ control = new OpenLayers.Control.PanZoom();
+ t.ok( control instanceof OpenLayers.Control.PanZoom, "new OpenLayers.Control.PanZoom returns object" );
+ t.ok( map instanceof OpenLayers.Map, "new OpenLayers.Map creates map" );
+ map.addControl(control);
+ t.ok( control.map === map, "Control.map is set to the map object" );
+ t.ok( map.controls[4] === control, "map.controls contains control" );
+ t.eq( parseInt(control.div.style.zIndex), map.Z_INDEX_BASE['Control'] + 5, "Control div zIndexed properly" );
+ t.eq( parseInt(map.viewPortDiv.lastChild.style.zIndex), map.Z_INDEX_BASE['Control'] + 5, "Viewport div contains control div" );
+ t.eq( control.div.style.top, "4px", "Control div top located correctly by default");
+
+ var control2 = new OpenLayers.Control.PanZoom();
+ map.addControl(control2, new OpenLayers.Pixel(100,100));
+ t.eq( control2.div.style.top, "100px", "2nd control div is located correctly");
+ }
+
+ function test_Control_PanZoom_removeButtons(t) {
+ t.plan(2);
+ map = new OpenLayers.Map("map");
+ control = new OpenLayers.Control.PanZoom();
+ map.addControl(control);
+ control.removeButtons();
+ t.eq(control.buttons.length, 0, "buttons array cleared correctly");
+ t.eq(control.div.childNodes.length, 0, "control div is empty");
+ }
+
+ function test_Control_PanZoom_control_events (t) {
+
+ // IE 9+ does support the standard document.createEvent,
+ // event.initMouseEvent, and elem.dispatchEvent calls, so it
+ // should be possible to simulate clicks in this browser.
+ // For example it looks like jQuery UI does simulate events
+ // using document.createElement in IE 9+. See
+ // https://github.com/jquery/jquery-ui/blob/master/tests/jquery.simulate.js.
+ // I haven't been able to make it work though.
+
+ if ( !window.document.createEvent ||
+ OpenLayers.BROWSER_NAME == "msie" ||
+ OpenLayers.BROWSER_NAME == "opera" ||
+ !t.open_window) {
+
+ t.plan(0);
+ t.debug_print("FIXME: This browser does not support the PanZoom test at this time.");
+ } else {
+ t.plan(35);
+ t.open_window( "Control/PanZoom.html", function( wnd ) {
+ t.delay_call( 3, function() {
+ var flag;
+ function setFlag(evt) {
+ flag[evt.type] = true;
+ }
+ function resetFlags() {
+ flag = {
+ mousedown: false,
+ mouseup: false,
+ click: false,
+ dblclick: false
+ };
+ }
+ resetFlags();
+
+ wnd.mapper.events.register("mousedown", mapper, setFlag);
+ wnd.mapper.events.register("mouseup", mapper, setFlag);
+ wnd.mapper.events.register("click", mapper, setFlag);
+ wnd.mapper.events.register("dblclick", mapper, setFlag);
+
+ simulateClick(wnd, wnd.control.buttons[0]);
+ t.delay_call(2, function() {
+ t.ok( wnd.mapper.getCenter().lat > wnd.centerLL.lat, "1) Pan up works correctly" );
+ t.ok(!flag.mousedown, "1) mousedown does not get to the map");
+ t.ok(!flag.mouseup, "1) mouseup does not get to the map");
+ t.ok(!flag.click, "1) click does not get to the map");
+ t.ok(!flag.dblclick, "1) dblclick does not get to the map");
+ resetFlags();
+
+ simulateClick(wnd, wnd.control.buttons[1]);
+ }, 2, function() {
+ t.ok( wnd.mapper.getCenter().lon < wnd.centerLL.lon, "2) Pan left works correctly" );
+ t.ok(!flag.mousedown, "2) mousedown does not get to the map");
+ t.ok(!flag.mouseup, "2) mouseup does not get to the map");
+ t.ok(!flag.click, "2) click does not get to the map");
+ t.ok(!flag.dblclick, "2) dblclick does not get to the map");
+ resetFlags();
+
+ simulateClick(wnd, wnd.control.buttons[2]);
+ }, 2, function() {
+ t.ok( wnd.mapper.getCenter().lon == wnd.centerLL.lon, "3) Pan right works correctly" );
+ t.ok(!flag.mousedown, "3) mousedown does not get to the map");
+ t.ok(!flag.mouseup, "3) mouseup does not get to the map");
+ t.ok(!flag.click, "3) click does not get to the map");
+ t.ok(!flag.dblclick, "3) dblclick does not get to the map");
+ resetFlags();
+
+ simulateClick(wnd, wnd.control.buttons[3]);
+ }, 2, function() {
+ t.ok( wnd.mapper.getCenter().lat == wnd.centerLL.lat, "4) Pan down works correctly" );
+ t.ok(!flag.mousedown, "4) mousedown does not get to the map");
+ t.ok(!flag.mouseup, "4) mouseup does not get to the map");
+ t.ok(!flag.click, "4) click does not get to the map");
+ t.ok(!flag.dblclick, "4) dblclick does not get to the map");
+ resetFlags();
+
+ simulateClick(wnd, wnd.control.buttons[4]);
+ }, 2, function() {
+ t.eq( wnd.mapper.getZoom(), 6, "5) zoomin works correctly" );
+ t.ok(!flag.mousedown, "5) mousedown does not get to the map");
+ t.ok(!flag.mouseup, "5) mouseup does not get to the map");
+ t.ok(!flag.click, "5) click does not get to the map");
+ t.ok(!flag.dblclick, "5) dblclick does not get to the map");
+ resetFlags();
+
+ simulateClick(wnd, wnd.control.buttons[6]);
+ }, 2, function() {
+ t.eq( wnd.mapper.getZoom(), 5, "6) zoomout works correctly" );
+ t.ok(!flag.mousedown, "6) mousedown does not get to the map");
+ t.ok(!flag.mouseup, "6) mouseup does not get to the map");
+ t.ok(!flag.click, "6) click does not get to the map");
+ t.ok(!flag.dblclick, "6) dblclick does not get to the map");
+ resetFlags();
+
+ simulateClick(wnd, wnd.control.buttons[5]);
+ }, 2, function() {
+ t.eq( wnd.mapper.getZoom(), 2, "7) zoomworld works correctly" );
+ t.ok(!flag.mousedown, "7) mousedown does not get to the map");
+ t.ok(!flag.mouseup, "7) mouseup does not get to the map");
+ t.ok(!flag.click, "7) click does not get to the map");
+ t.ok(!flag.dblclick, "7) dblclick does not get to the map");
+ resetFlags();
+ });
+ });
+ });
+ }
+ }
+
+ function test_slideRatio(t) {
+ t.plan(4);
+
+ var control = new OpenLayers.Control.PanZoom({
+ slideRatio: .5
+ });
+
+ var map = new OpenLayers.Map();
+
+ map.addControl(control);
+ control.draw();
+ control.activate();
+
+ map.getSize = function() {
+ return {
+ w: 250,
+ h: 100
+ }
+ };
+
+ var delta, dir;
+ var buttons = control.buttons;
+ map.pan = function(dx, dy){
+ t.eq([dx,dy],delta,"Panning " + dir + " sets right delta with slideRatio");
+ };
+
+ //up
+ var delta = [0, -50];
+ var dir = "up";
+ var evt = {buttonElement: buttons[0]};
+ control.onButtonClick.call(control, evt);
+
+ //left
+ var delta = [-125, 0];
+ var dir = "left";
+ evt.buttonElement = buttons[1];
+ control.onButtonClick.call(control, evt);
+
+ //right
+ var delta = [125, 0];
+ var dir = "right";
+ evt.buttonElement = buttons[2];
+ control.onButtonClick.call(control, evt);
+
+ //down
+ var delta = [0, 50];
+ var dir = "down";
+ evt.buttonElement = buttons[3];
+ control.onButtonClick.call(control, evt);
+
+ map.destroy();
+ }
+
+ function simulateClick(wnd, elem) {
+ var evt = wnd.document.createEvent("MouseEvents");
+ evt.initMouseEvent("mousedown", true, true, wnd, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ elem.dispatchEvent(evt);
+
+ evt = wnd.document.createEvent("MouseEvents");
+ evt.initMouseEvent("mouseup", true, true, wnd, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ elem.dispatchEvent(evt);
+
+ evt = wnd.document.createEvent("MouseEvents");
+ evt.initMouseEvent("click", true, true, wnd, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ elem.dispatchEvent(evt);
+
+ evt = wnd.document.createEvent("MouseEvents");
+ evt.initMouseEvent("dblclick", true, true, wnd, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ elem.dispatchEvent(evt);
+ }
+
+ function loader() {
+ control = new OpenLayers.Control.PanZoom();
+
+ mapper = new OpenLayers.Map('map', { controls: [control]});
+
+
+ var layer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://labs.metacarta.com/wms-c/Basic.py?",
+ {layers: "basic"});
+ mapper.addLayer(layer);
+
+ centerLL = new OpenLayers.LonLat(0,0);
+ mapper.setCenter(centerLL, 5);
+ }
+
+
+ </script>
+</head>
+<body onload="loader()">
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/PanZoomBar.html b/misc/openlayers/tests/Control/PanZoomBar.html
new file mode 100644
index 0000000..5ed2833
--- /dev/null
+++ b/misc/openlayers/tests/Control/PanZoomBar.html
@@ -0,0 +1,245 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var map;
+ function test_Control_PanZoomBar_constructor (t) {
+ t.plan( 4 );
+
+ control = new OpenLayers.Control.PanZoomBar({position: new OpenLayers.Pixel(100,100)});
+ t.ok( control instanceof OpenLayers.Control.PanZoomBar, "new OpenLayers.Control.PanZoomBar returns object" );
+ t.eq( control.displayClass, "olControlPanZoomBar", "displayClass is correct" );
+ t.eq( control.position.x, 100, "PanZoom X Set correctly.");
+ t.eq( control.position.y, 100, "PanZoom y Set correctly.");
+ }
+ function test_Control_PanZoomBar_addControl (t) {
+ t.plan( 8 );
+ map = new OpenLayers.Map('map', {controls:[]});
+ var layer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(layer);
+ control = new OpenLayers.Control.PanZoomBar();
+ t.ok( control instanceof OpenLayers.Control.PanZoomBar, "new OpenLayers.Control.PanZoomBar returns object" );
+ t.ok( map instanceof OpenLayers.Map, "new OpenLayers.Map creates map" );
+ map.addControl(control);
+ t.ok( control.map === map, "Control.map is set to the map object" );
+ t.ok( map.controls[0] === control, "map.controls contains control" );
+ t.eq( parseInt(control.div.style.zIndex), 1001, "Control div zIndexed properly" );
+ t.eq( parseInt(map.viewPortDiv.lastChild.style.zIndex), 1001, "Viewport div contains control div" );
+ t.eq( control.div.style.top, "4px", "Control div top located correctly by default");
+
+ var control2 = new OpenLayers.Control.PanZoomBar();
+ map.addControl(control2, new OpenLayers.Pixel(100,100));
+ t.eq( control2.div.style.top, "100px", "2nd control div is located correctly");
+ }
+
+ function test_draw(t) {
+ t.plan(3);
+ map = new OpenLayers.Map('map', {controls:[]});
+ var layer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ control = new OpenLayers.Control.PanZoomBar();
+ map.addControl(control);
+ t.eq(control.zoombarDiv.style.height, '176px', "Bar's height is correct.");
+
+ map.baseLayer.wrapDateLine = true;
+
+ control.redraw();
+ t.eq(control.zoombarDiv.style.height, '154px', "Bar's height is correct after minZoom restriction.");
+
+ map.div.style.width = "512px";
+ map.updateSize();
+ t.eq(control.zoombarDiv.style.height, '165px', "Bar's height is correct after resize and minZoom restriction.");
+
+ map.div.style.width = "1024px";
+ map.destroy();
+ }
+
+ function test_Control_PanZoomBar_clearDiv(t) {
+ t.plan(2);
+ map = new OpenLayers.Map('map', {controls:[]});
+ var layer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(layer);
+ control = new OpenLayers.Control.PanZoomBar();
+ map.addControl(control);
+ control.removeButtons();
+ var div = control.div;
+ map.destroy();
+ t.eq(div.childNodes.length, 0, "control's div cleared.");
+ t.eq(control.zoombarDiv, null, "zoombar div nullified.")
+ }
+
+ function test_Control_PanZoomBar_onButtonClick (t) {
+ t.plan(2);
+ map = new OpenLayers.Map('map', {controls:[], zoomMethod: null});
+ var layer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(layer);
+ control = new OpenLayers.Control.PanZoomBar();
+ map.addControl(control);
+ control.onButtonClick({'buttonXY': {'x': 0, 'y': 50}, buttonElement: control.zoombarDiv});
+ t.eq(map.zoom, 11, "zoom is correct on standard map");
+
+ map.fractionalZoom = true;
+ control.onButtonClick({'buttonXY': {'x': 0, 'y': 49}, buttonElement: control.zoombarDiv});
+ t.eq(map.zoom.toFixed(3), '10.545', "zoom is correct on fractional zoom map");
+
+ }
+
+ function test_Control_PanZoomBar_forceFixedZoomLevel_onButtonClick(t){
+ t.plan(1);
+ map = new OpenLayers.Map('map', {
+ controls: [],
+ fractionalZoom: true,
+ zoomMethod: null
+ });
+ var layer = new OpenLayers.Layer.WMS("Test Layer", "http://octo.metacarta.com/cgi-bin/mapserv?", {
+ map: "/mapdata/vmap_wms.map",
+ layers: "basic"
+ });
+ map.addLayer(layer);
+ control = new OpenLayers.Control.PanZoomBar({
+ forceFixedZoomLevel: true
+ });
+ map.addControl(control);
+
+ control.onButtonClick({
+ 'buttonXY': {
+ 'x': 0,
+ 'y': 49
+ },
+ buttonElement: control.zoombarDiv
+ });
+ t.eq(map.zoom, 11, "forceFixedZoomLevel makes sure that after a div click only fixed zoom levels are used even if the map has fractionalZoom");
+ }
+
+ function test_Control_PanZoomBar_forceFixedZoomLevel_zoomBarUp (t) {
+ var numRandomDrags = 25;
+ // plan one static recorded test and two for every random drag
+ t.plan(1 + (numRandomDrags * 2));
+
+
+ var map = new OpenLayers.Map('map', {
+ controls: [],
+ fractionalZoom: true,
+ zoomMethod: null
+ });
+ var layer = new OpenLayers.Layer.WMS("Test Layer", "http://octo.metacarta.com/cgi-bin/mapserv?", {
+ map: "/mapdata/vmap_wms.map",
+ layers: "basic"
+ });
+ map.addLayer(layer);
+
+ // zoom to a fractional ZoomLevel initially:
+ map.setCenter(new OpenLayers.LonLat(0, 0), 9.545);
+
+ control = new OpenLayers.Control.PanZoomBar({
+ forceFixedZoomLevel: true
+ });
+ map.addControl(control);
+
+ // The y values come from manually recording real values in an example
+ var evt = {
+ 'xy': {
+ 'x': 0,
+ 'y': -10.633
+ },
+ which: 1
+ };
+ control.zoomStart = {
+ 'x': 0,
+ 'y': 5.366
+ };
+ control.mouseDragStart = {
+ 'x': 0,
+ 'y': -10.633
+ };
+ control.deltaY = control.zoomStart.y - evt.xy.y
+ control.zoomBarUp(evt);
+ t.eq(map.zoom, 11, "forceFixedZoomLevel makes sure that after dragging of the handle only fixed zoom levels are used even if the map has fractionalZoom");
+
+ // randomly drag the handle around
+ // we should never get a zoom < 0 or a non-integer zoom, regardless of
+ // captured random values for start and end of the drag.
+ for (var i = 0; i < numRandomDrags; i++) {
+ var randStartY = Math.random() * 10 * ((i % 2 === 0) ? -1 : 1);
+ var randStopY = Math.random() * 160 * ((i % 2 === 1) ? -1 : 1);
+ var evt = {
+ 'xy': {
+ 'x': 0,
+ 'y': randStopY
+ },
+ which: 1
+ };
+ control.zoomStart = {
+ 'x': 0,
+ 'y': randStartY
+ };
+ control.mouseDragStart = {
+ 'x': 0,
+ 'y': randStopY
+ };
+ control.deltaY = control.zoomStart.y - evt.xy.y
+ control.zoomBarUp(evt);
+
+ t.eq(Math.floor(map.zoom), Math.ceil(map.zoom), 'Only integer zooms after random handle drag with forceFixedZoomLevel=true and fractionalZoom=true (current zoom was ' + map.zoom + ')');
+ t.ok(map.zoom >= 0, 'map.zoom is never < 0 after random handle drag with forceFixedZoomLevel=true and fractionalZoom=true');
+ }
+ }
+
+ function test_Control_PanZoomBar_shows (t) {
+ t.plan(22);
+
+ var control, map;
+
+ control = new OpenLayers.Control.PanZoomBar({panIcons: true, zoomWorldIcon: false});
+ map = new OpenLayers.Map('map', {controls: [control]});
+ t.eq(control.buttons.length, 6, "(a) pan, no world - expected number of buttons");
+ t.ok(control.buttons[0].id.match("_panup$"), "(a) pan, no world - pan up");
+ t.ok(control.buttons[1].id.match("_panleft$"), "(a) pan, no world - pan left");
+ t.ok(control.buttons[2].id.match("_panright$"), "(a) pan, no world - pan right");
+ t.ok(control.buttons[3].id.match("_pandown$"), "(a) pan, no world - pan down");
+ t.ok(control.buttons[4].id.match("_zoomin$"), "(a) pan, no world - zoom in");
+ t.ok(control.buttons[5].id.match("_zoomout$"), "(a) pan, no world - zoom out");
+ map.destroy();
+
+ control = new OpenLayers.Control.PanZoomBar({panIcons: true, zoomWorldIcon: true});
+ map = new OpenLayers.Map('map', {controls:[control]});
+ t.eq(control.buttons.length, 7, "(b) pan, world - expected number of buttons");
+ t.ok(control.buttons[0].id.match("_panup$"), "(b) pan, world - pan up");
+ t.ok(control.buttons[1].id.match("_panleft$"), "(b) pan, world - pan left");
+ t.ok(control.buttons[2].id.match("_zoomworld$"), "(b) pan, world - zoom world");
+ t.ok(control.buttons[3].id.match("_panright$"), "(b) pan, world - pan right");
+ t.ok(control.buttons[4].id.match("_pandown$"), "(b) pan, world - pan down");
+ t.ok(control.buttons[5].id.match("_zoomin$"), "(b) pan, world - zoom in");
+ t.ok(control.buttons[6].id.match("_zoomout$"), "(b) pan, world - zoom out");
+ map.destroy();
+
+ control = new OpenLayers.Control.PanZoomBar({panIcons: false, zoomWorldIcon: false});
+ map = new OpenLayers.Map('map', {controls:[control]});
+ t.eq(control.buttons.length, 2, "(c) no pan, no world - expected number of buttons");
+ t.ok(control.buttons[0].id.match("_zoomin$"), "(c) no pan, no world - zoom in");
+ t.ok(control.buttons[1].id.match("_zoomout$"), "(c) no pan, no world - zoom out");
+ map.destroy();
+
+ control = new OpenLayers.Control.PanZoomBar({panIcons: false, zoomWorldIcon: true});
+ map = new OpenLayers.Map('map', {controls:[control]});
+ t.eq(control.buttons.length, 3, "(d) no pan, world - expected number of buttons");
+ t.ok(control.buttons[0].id.match("_zoomin$"), "(d) no pan, world - zoom in");
+ t.ok(control.buttons[1].id.match("_zoomout$"), "(d) no pan, world - zoom out");
+ t.ok(control.buttons[2].id.match("_zoomworld$"), "(d) no pan, world - zoom world");
+ map.destroy();
+ }
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/Panel.html b/misc/openlayers/tests/Control/Panel.html
new file mode 100644
index 0000000..f02e643
--- /dev/null
+++ b/misc/openlayers/tests/Control/Panel.html
@@ -0,0 +1,382 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Control_Panel_constructor (t) {
+ t.plan( 2 );
+
+ control = new OpenLayers.Control.Panel();
+ t.ok( control instanceof OpenLayers.Control.Panel, "new OpenLayers.Control returns object" );
+ t.eq( control.displayClass, "olControlPanel", "displayClass is correct" );
+ }
+ function test_Control_Panel_constructor2 (t) {
+ t.plan(19);
+ var map = new OpenLayers.Map('map');
+ var toolControl = new OpenLayers.Control.ZoomBox();
+ var AnotherToolControl = OpenLayers.Class(OpenLayers.Control, {
+ CLASS_NAME: 'mbControl.TestTool',
+ type: OpenLayers.Control.TYPE_TOOL
+ });
+ var anotherToolControl = new AnotherToolControl();
+ var ToggleControl = OpenLayers.Class(OpenLayers.Control, {
+ CLASS_NAME: 'mbControl.TestToggle',
+ type: OpenLayers.Control.TYPE_TOGGLE
+ });
+
+ var toggleControl = new ToggleControl();
+ var buttonControl = new OpenLayers.Control.Button({
+ trigger: function () {
+ t.ok(true, "trigger function of button is called.");
+ }
+ });
+
+ var panel = new OpenLayers.Control.Panel(
+ {defaultControl: anotherToolControl});
+ t.ok(panel instanceof OpenLayers.Control.Panel,
+ "new OpenLayers.Control.Panel returns object");
+ panel.redraw = function(){
+ panel.redrawsCount++;
+ OpenLayers.Control.Panel.prototype.redraw.apply(this, arguments);
+ };
+
+ // To get length of events.listeners error-free
+ var getListenerLength= function(events,key){
+ if(!events) {
+ return -2; // events is destroyed
+ } else if(!events.listeners) {
+ return -1; // events is destroyed
+ } else if(!events.listeners[key]) {
+ return 0; // no listener in event
+ } else {
+ return events.listeners[key].length;
+ }
+ };
+ var toolEventListenerLength = getListenerLength(toolControl.events,"activate");
+ panel.addControls([toolControl, anotherToolControl, toggleControl]);
+ t.eq(panel.controls.length, 3,
+ "added three controls to the panel");
+ panel.addControls([buttonControl]);
+
+ panel.redrawsCount = 0;
+ map.addControl(panel);
+ t.eq(getListenerLength(toolControl.events,"activate"), toolEventListenerLength+1,
+ "toolControl additional listener for \"activate\" after adding Panel to the map.");
+ t.ok((panel.redrawsCount > 0), "Redraw called on add panel to map " +
+ panel.redrawsCount + " times.");
+ t.ok((panel.active),"Panel is active after add panel to map.");
+
+ panel.redrawsCount = 0;
+ panel.addControls(new AnotherToolControl());
+ t.ok((panel.redrawsCount > 0),
+ "Redraw called on add control to panel after add panel to map " +
+ panel.redrawsCount + " times.");
+
+ panel.deactivate();
+ panel.redrawsCount = 0;
+ panel.activate();
+ t.ok((panel.redrawsCount > 0),"Redraw called on activate panel " +
+ panel.redrawsCount + " times.");
+
+ panel.activateControl(toolControl);
+ t.ok(toolControl.active && !anotherToolControl.active && !toggleControl.active && !buttonControl.active,
+ "activated one tool control, the other one is inactive and the toggle & button controls also.");
+
+ panel.activateControl(toggleControl);
+ t.eq(toggleControl.panel_div.className,"mbControlTestToggleItemActive olButton",
+ "className of icon div for toggle control is active.");
+ t.ok(toolControl.active && !anotherToolControl.active && toggleControl.active,
+ "activated the toggle control, which has no influence on the tool & togggle controls.");
+ panel.activateControl(buttonControl);
+ t.ok(toolControl.active && !anotherToolControl.active && toggleControl.active,
+ "activateContol calling for button, which has no influence on the tool & togggle controls.");
+ t.ok(!buttonControl.active,
+ "activateContol calling for button, button remains inactive.");
+ buttonControl.activate();
+ t.ok(buttonControl.active && toolControl.active && !anotherToolControl.active && toggleControl.active,
+ "activated the button control, which has no influence on the tool & togggle controls.");
+
+ panel.activateControl(anotherToolControl);
+ t.eq(anotherToolControl.panel_div.className,"mbControlTestToolItemActive olButton",
+ "className of icon div for anotherToolControl is active.");
+ t.eq(toolControl.panel_div.className,"olControlZoomBoxItemInactive olButton",
+ "className of icon div for toolControl is inactive.");
+ t.ok(!toolControl.active && anotherToolControl.active && toggleControl.active,
+ "activated the other tool control, the first one is inactive and the toggle control still active.");
+ t.ok(buttonControl.active,
+ "activated the other tool control, the button control still active.");
+
+ panel.destroy();
+ t.eq(getListenerLength(toolControl.events,"activate"), toolEventListenerLength,
+ "toolControl additional listeners removed after destroy Panel.");
+ map.destroy();
+ }
+ function test_Control_Panel_titles (t) {
+ t.plan(2);
+ var panel = new OpenLayers.Control.Panel();
+ var toolControl = new OpenLayers.Control.ZoomBox({
+ title:"Zoom box: Selecting it you can zoom on an area by clicking and dragging."
+ });
+ panel.addControls([toolControl]);
+ t.eq(panel.controls.length, 1, "added a control to the panel");
+ t.eq(panel.controls[0].title, toolControl.panel_div.title, "the title is correctly set");
+ }
+
+ function test_Control_Panel_getBy(t) {
+
+ var panel = {
+ getBy: OpenLayers.Control.Panel.prototype.getBy,
+ getControlsBy: OpenLayers.Control.Panel.prototype.getControlsBy,
+ controls: [
+ {foo: "foo", id: Math.random()},
+ {foo: "bar", id: Math.random()},
+ {foo: "foobar", id: Math.random()},
+ {foo: "foo bar", id: Math.random()},
+ {foo: "foo", id: Math.random()}
+ ]
+ };
+
+ var cases = [
+ {
+ got: panel.getControlsBy("foo", "foo"),
+ expected: [panel.controls[0], panel.controls[4]],
+ message: "(string literal) got two controls matching foo"
+ }, {
+ got: panel.getControlsBy("foo", "bar"),
+ expected: [panel.controls[1]],
+ message: "(string literal) got one control matching foo"
+ }, {
+ got: panel.getControlsBy("foo", "barfoo"),
+ expected: [],
+ message: "(string literal) got empty array for no foo match"
+ }, {
+ got: panel.getControlsBy("foo", /foo/),
+ expected: [panel.controls[0], panel.controls[2], panel.controls[3], panel.controls[4]],
+ message: "(regexp literal) got three controls containing string"
+ }, {
+ got: panel.getControlsBy("foo", /foo$/),
+ expected: [panel.controls[0], panel.controls[4]],
+ message: "(regexp literal) got three controls ending with string"
+ }, {
+ got: panel.getControlsBy("foo", /\s/),
+ expected: [panel.controls[3]],
+ message: "(regexp literal) got control containing space"
+ }, {
+ got: panel.getControlsBy("foo", new RegExp("BAR", "i")),
+ expected: [panel.controls[1], panel.controls[2], panel.controls[3]],
+ message: "(regexp object) got layers ignoring case"
+ }, {
+ got: panel.getControlsBy("foo", {test: function(str) {return str.length > 3;}}),
+ expected: [panel.controls[2], panel.controls[3]],
+ message: "(custom object) got controls with foo length greater than 3"
+ }
+ ];
+ t.plan(cases.length);
+ for(var i=0; i<cases.length; ++i) {
+ t.eq(cases[i].got, cases[i].expected, cases[i].message);
+ }
+
+
+ }
+
+ function test_Control_Panel_saveState (t) {
+ t.plan(11);
+ var map = new OpenLayers.Map('map');
+
+ var defaultControl = new OpenLayers.Control();
+ var panel = new OpenLayers.Control.Panel({
+ defaultControl: defaultControl
+ });
+ panel.addControls([new OpenLayers.Control(), defaultControl]);
+ map.addControl(panel);
+ t.eq(defaultControl.active, true,
+ "After panel activation default control is active.");
+ t.ok(panel.defaultControl,
+ "defaultControl not nullified after initial panel activation");
+ // activate the 1st control
+ panel.activateControl(panel.controls[0]);
+ panel.deactivate();
+ t.ok(!panel.controls[0].active && !panel.controls[1].active,
+ "No controls are active after panel deactivation.");
+ panel.activate();
+ t.eq(panel.controls[0].active, false,
+ "After panel reactivation first control is inactive.");
+ t.eq(panel.controls[1].active, true,
+ "After panel reactivation default control is active again.");
+ panel.destroy();
+
+ defaultControl = new OpenLayers.Control();
+ panel = new OpenLayers.Control.Panel({
+ saveState: true,
+ defaultControl: defaultControl
+ });
+ panel.addControls([new OpenLayers.Control(), defaultControl]);
+ map.addControl(panel);
+ t.eq(defaultControl.active, true,
+ "After panel activation default control is active.");
+ t.eq(panel.defaultControl, null,
+ "defaultControl nullified after initial panel activation");
+ // activate the 1st control, which will deactivate the 2nd
+ panel.activateControl(panel.controls[0]);
+ t.eq(panel.controls[1].active, false,
+ "2nd control deactivated with activation of 1st");
+ panel.deactivate();
+ t.ok(!panel.controls[0].active && !panel.controls[1].active,
+ "No controls are active after panel deactivation.");
+ panel.activate();
+ t.eq(panel.controls[0].active, true,
+ "After panel reactivation first control is active.");
+ t.eq(panel.controls[1].active, false,
+ "After panel reactivation second control is inactive.");
+ panel.destroy();
+ map.destroy();
+ }
+
+ function test_Control_Panel_autoActivate (t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map');
+ var controlNoDeactive = new OpenLayers.Control({autoActivate:true});
+ var chkDeactivate = function () {
+ t.ok(false, "Tool control autoActivate:true was deactivated unnecessarily");
+ };
+ controlNoDeactive.events.on({deactivate: chkDeactivate});
+ var panel = new OpenLayers.Control.Panel();
+
+ map.addControl(panel);
+ panel.addControls([controlNoDeactive]);
+ controlNoDeactive.events.un({deactivate: chkDeactivate});
+ t.ok(!controlNoDeactive.active, "Tool control autoActivate:true is not active");
+
+ }
+
+ function test_Control_Panel_deactivate (t) {
+ t.plan(2);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ var panel = new OpenLayers.Control.Panel();
+ map.addControl(panel);
+ panel.addControls([control]);
+ t.ok(panel.div.innerHTML != "", "Panel displayed after activate");
+
+ panel.deactivate();
+ t.ok(panel.div.innerHTML == "",
+ "Panel is not displayed after deactivate without any active control");
+
+ map.destroy();
+ }
+
+ function test_allowDepress (t) {
+ t.plan(2);
+ var map = new OpenLayers.Map('map');
+
+ var panel = new OpenLayers.Control.Panel();
+ panel.addControls([new OpenLayers.Control(),new OpenLayers.Control()]);
+ map.addControl(panel);
+
+ var control1 = panel.controls[1]
+
+ panel.activateControl(control1);
+
+ panel.allowDepress = false;
+ panel.activateControl(control1);
+ t.eq(control1.active, true,
+ "control1 remains active after calling again activateControl when allowDepress = false");
+ panel.allowDepress = true;
+ panel.activateControl(control1);
+ t.eq(control1.active, false,
+ "control1 is inactive after calling again activateControl when allowDepress = true");
+
+ // panel.deactivate();
+ map.destroy();
+ }
+
+ function test_iconOn_iconOff(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map('map');
+
+ var panel = new OpenLayers.Control.Panel();
+ var ctrl = new OpenLayers.Control({displayClass: 'ctrl'});
+ panel.addControls([ctrl]);
+
+ map.addControl(panel);
+
+ // add arbitrary classes to the panel div - we want to test
+ // than iconOn and iconOff do their jobs even when the panel
+ // div has application-specific classes.
+
+ ctrl.panel_div.className =
+ 'ctrlItemInactive fooItemActive fooItemInactive';
+
+ panel.iconOn.call(ctrl);
+ t.eq(ctrl.panel_div.className,
+ 'ctrlItemActive fooItemActive fooItemInactive',
+ 'iconOn behaves as expected');
+
+ ctrl.panel_div.className =
+ 'ctrlItemActive fooItemActive fooItemInactive';
+
+ panel.iconOff.call(ctrl);
+ t.eq(ctrl.panel_div.className,
+ 'ctrlItemInactive fooItemActive fooItemInactive',
+ 'iconOff behaves as expected');
+
+ map.destroy();
+ }
+
+ function test_buttonclick(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map('map');
+ var panel1 = new OpenLayers.Control.Panel();
+ var div = document.createElement("div");
+ var panel2 = new OpenLayers.Control.Panel({div: div});
+ map.addControls([panel1, panel2]);
+
+ t.ok(map.events.listeners.buttonclick, "buttonclick event registered on map's Events instance for panel inside map");
+ t.ok(!panel1.events.element, "Panel inside map has no element on its Events instance");
+ t.ok(panel2.events.listeners.buttonclick, "buttonclick event registered on panel's Events instance if outside map")
+ t.ok(panel2.events.element === div, "Panel outside map has the panel's div as element on its Events instance");
+
+ }
+
+ function test_iconOniconOff (t) {
+ t.plan(6);
+ var map = new OpenLayers.Map("map"),
+ navControl = new OpenLayers.Control.Navigation({autoActivate: true}),
+ zbControl = new OpenLayers.Control.ZoomBox(),
+ panel = new OpenLayers.Control.Panel({defaultControl: navControl}),
+ navActiveClass, navInactiveClass, zbActiveClass, zbInactiveClass;
+
+ panel.addControls([navControl, zbControl]);
+ map.addControl(panel);
+
+ navControl.panel_div.className += " foo";
+ zbControl.panel_div.className = "bar " + zbControl.panel_div.className;
+
+ t.eq(navControl.panel_div.className, "olControlNavigationItemActive olButton foo",
+ "defaultControl className is set to [displayClass]Active on panel instantiation");
+ t.eq(zbControl.panel_div.className, "bar olControlZoomBoxItemInactive olButton",
+ "non-defaultControl className is set to [displayClass]Inactive on panel instantiation");
+
+ panel.activateControl(zbControl);
+
+ t.eq(zbControl.panel_div.className, "bar olControlZoomBoxItemActive olButton",
+ "active control class name with preceding secondary class name is set to [displayClass]Active");
+ t.eq(navControl.panel_div.className, "olControlNavigationItemInactive olButton foo",
+ "inactive control class name with trailing secondary class name is set to [displayClass]Inactive");
+
+ panel.activateControl(navControl);
+
+ t.eq(navControl.panel_div.className, "olControlNavigationItemActive olButton foo",
+ "active control class name with trailing secondary class name is set to [displayClass]Active");
+ t.eq(zbControl.panel_div.className, "bar olControlZoomBoxItemInactive olButton",
+ "inactive control class name with preceding secondary class name is set to [displayClass]Inactive");
+
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/Permalink.html b/misc/openlayers/tests/Control/Permalink.html
new file mode 100644
index 0000000..0b729da
--- /dev/null
+++ b/misc/openlayers/tests/Control/Permalink.html
@@ -0,0 +1,453 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var map;
+ function test_Control_Permalink_constructor (t) {
+ t.plan(42);
+
+ control = new OpenLayers.Control.Permalink();
+ t.ok(control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object");
+ t.eq(control.displayClass, "olControlPermalink", "displayClass is correct");
+ t.eq(control.base, document.location.href, "base is correct");
+ t.ok(!control.anchor, "anchor is correct");
+ control.destroy();
+
+ control = new OpenLayers.Control.Permalink('permalink', 'test.html');
+ t.ok(control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object");
+ t.eq(control.displayClass, "olControlPermalink", "displayClass is correct");
+ t.eq(control.base, 'test.html', "base is correct");
+ t.ok(OpenLayers.Util.isElement(control.element), "element is a dom object");
+ t.ok(!control.anchor, "anchor is correct");
+ control.destroy();
+
+ control = new OpenLayers.Control.Permalink('permalink');
+ t.ok(control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object");
+ t.eq(control.displayClass, "olControlPermalink", "displayClass is correct");
+ t.eq(control.base, document.location.href, "base is correct");
+ t.ok(OpenLayers.Util.isElement(control.element), "element is a dom object");
+ t.ok(!control.anchor, "anchor is correct");
+ control.destroy();
+
+ control = new OpenLayers.Control.Permalink(OpenLayers.Util.getElement('permalink'));
+ t.ok(control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object");
+ t.eq(control.displayClass, "olControlPermalink", "displayClass is correct");
+ t.eq(control.base, document.location.href, "base is correct");
+ t.ok(OpenLayers.Util.isElement(control.element), "element is a dom object");
+ t.ok(!control.anchor, "anchor is correct");
+ control.destroy();
+
+ control = new OpenLayers.Control.Permalink({anchor: true});
+ t.ok(control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object");
+ t.eq(control.displayClass, "olControlPermalink", "displayClass is correct");
+ t.eq(control.base, document.location.href, "base is correct");
+ t.ok(control.element == null, "element is null");
+ t.ok(control.anchor, "anchor is correct");
+ control.destroy();
+
+ control = new OpenLayers.Control.Permalink({anchor: false});
+ t.ok(control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object");
+ t.eq(control.displayClass, "olControlPermalink", "displayClass is correct");
+ t.eq(control.base, document.location.href, "base is correct");
+ t.ok(!control.anchor, "anchor is correct");
+ control.destroy();
+
+ control = new OpenLayers.Control.Permalink({});
+ t.ok(control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object");
+ t.eq(control.displayClass, "olControlPermalink", "displayClass is correct");
+ t.eq(control.base, document.location.href, "base is correct");
+ t.ok(!control.anchor, "anchor is correct");
+ control.destroy();
+
+ control = new OpenLayers.Control.Permalink({element: 'permalink', base: 'test.html'});
+ t.ok(control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object");
+ t.eq(control.displayClass, "olControlPermalink", "displayClass is correct");
+ t.eq(control.base, 'test.html', "base is correct");
+ t.ok(OpenLayers.Util.isElement(control.element), "element is a dom object");
+ t.ok(!control.anchor, "anchor is correct");
+ control.destroy();
+
+ control = new OpenLayers.Control.Permalink({element: 'permalink', base: 'test.html', anchor: true});
+ t.ok(control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object");
+ t.eq(control.displayClass, "olControlPermalink", "displayClass is correct");
+ t.eq(control.base, 'test.html', "base is correct");
+ t.ok(OpenLayers.Util.isElement(control.element), "element is a dom object");
+ t.ok(control.anchor, "anchor is correct");
+ control.destroy();
+ }
+ function test_Control_Permalink_uncentered (t) {
+ t.plan( 1 );
+
+ control = new OpenLayers.Control.Permalink('permalink');
+ map = new OpenLayers.Map('map');
+ map.addControl(control);
+ map.events.triggerEvent("changelayer", {});
+ t.ok(true, "permalink didn't bomb out.");
+ map.destroy();
+ }
+ function test_Control_Permalink_initwithelem (t) {
+ t.plan( 1 );
+
+ control = new OpenLayers.Control.Permalink(OpenLayers.Util.getElement('permalink'));
+ t.ok(true, "If the above line doesn't throw an error, we're safe.");
+ control.destroy();
+ }
+ function test_Control_Permalink_updateLinks (t) {
+ t.plan( 3 );
+
+ control = new OpenLayers.Control.Permalink('permalink');
+ t.ok( control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object" );
+ map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'});
+ map.addLayer(layer);
+ layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'}, {'isBaseLayer': false});
+ map.addLayer(layer);
+ layer.setVisibility(true);
+ if (!map.getCenter()) map.zoomToMaxExtent();
+ map.addControl(control);
+ map.pan(5, 0, {animate:false});
+ t.ok(OpenLayers.Util.isEquivalentUrl(OpenLayers.Util.getElement('permalink').href, location+"?zoom=2&lat=0&lon=1.75781&layers=BT"), 'pan sets permalink');
+
+ map.layers[1].setVisibility(false);
+
+ t.ok(OpenLayers.Util.isEquivalentUrl(OpenLayers.Util.getElement('permalink').href, location+"?zoom=2&lat=0&lon=1.75781&layers=BF"), 'setVisibility sets permalink');
+ map.destroy();
+ }
+ function test_Control_Permalink_updateLinksBase (t) {
+ t.plan( 2 );
+
+ control = new OpenLayers.Control.Permalink('permalink', "./edit.html" );
+ t.ok( control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object" );
+ map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'});
+ map.addLayer(layer);
+ if (!map.getCenter()) map.zoomToMaxExtent();
+ map.addControl(control);
+ map.pan(5, 0, {animate:false});
+ OpenLayers.Util.getElement('edit_permalink').href = './edit.html?zoom=2&lat=0&lon=1.75781&layers=B';
+ t.eq(OpenLayers.Util.getElement('permalink').href, OpenLayers.Util.getElement('edit_permalink').href, "Panning sets permalink with base");
+ map.destroy();
+ }
+ function test_Control_Permalink_noElement (t) {
+ t.plan( 2 );
+ control = new OpenLayers.Control.Permalink( );
+ t.ok( control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object" );
+ map = new OpenLayers.Map('map');
+ map.addControl(control);
+ t.eq(map.controls[4].div.firstChild.nodeName, "A", "Permalink control creates div with 'a' inside." );
+ map.destroy();
+ }
+ function test_Control_Permalink_base_with_query (t) {
+ t.plan( 3 );
+
+ control = new OpenLayers.Control.Permalink('permalink', "./edit.html?foo=bar" );
+ map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS('Test Layer', "http://example.com" );
+ map.addLayer(layer);
+ if (!map.getCenter()) map.zoomToMaxExtent();
+ map.addControl(control);
+ map.pan(5, 0, {animate:false});
+ OpenLayers.Util.getElement('edit_permalink').href = './edit.html?foo=bar&zoom=2&lat=0&lon=1.75781&layers=B';
+ t.eq(OpenLayers.Util.getElement('permalink').href, OpenLayers.Util.getElement('edit_permalink').href, "Panning sets permalink with base and querystring");
+
+ control = new OpenLayers.Control.Permalink('permalink', "./edit.html?foo=bar&" );
+ map.addControl(control);
+ map.pan(0, 0, {animate:false});
+ t.eq(OpenLayers.Util.getElement('permalink').href, OpenLayers.Util.getElement('edit_permalink').href, "Panning sets permalink with base and querystring ending with '&'");
+
+ control = new OpenLayers.Control.Permalink('permalink', "./edit.html?" );
+ OpenLayers.Util.getElement('edit_permalink').href = './edit.html?zoom=2&lat=0&lon=1.75781&layers=B';
+ map.addControl(control);
+ map.pan(5, 0, {animate:false});
+ map.pan(-5, 0, {animate:false});
+ t.eq(OpenLayers.Util.getElement('permalink').href, OpenLayers.Util.getElement('edit_permalink').href, "Panning sets permalink with base and querystring ending with '?'");
+ map.destroy();
+ }
+
+ function test_Control_Permalink_base_with_anchor (t) {
+ t.plan( 4 );
+ control = new OpenLayers.Control.Permalink('permalink', "./edit.html#foo" );
+ map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS('Test Layer', "http://example.com" );
+ map.addLayer(layer);
+ if (!map.getCenter()) map.zoomToMaxExtent();
+ map.addControl(control);
+ map.pan(5, 0, {animate:false});
+ OpenLayers.Util.getElement('edit_permalink').href = './edit.html?zoom=2&lat=0&lon=1.75781&layers=B#foo';
+ t.eq(OpenLayers.Util.getElement('permalink').href, OpenLayers.Util.getElement('edit_permalink').href, "Panning sets permalink with base and anchor");
+
+ control = new OpenLayers.Control.Permalink('permalink', "./edit.html#" );
+ map.addControl(control);
+ map.pan(0, 0, {animate:false});
+ OpenLayers.Util.getElement('edit_permalink').href = './edit.html?zoom=2&lat=0&lon=1.75781&layers=B#';
+ t.eq(OpenLayers.Util.getElement('permalink').href, OpenLayers.Util.getElement('edit_permalink').href, "Panning sets permalink with base and an empty anchor");
+
+ control = new OpenLayers.Control.Permalink('permalink', "./edit.html?foo=bar#test" );
+ OpenLayers.Util.getElement('edit_permalink').href = './edit.html?foo=bar&zoom=2&lat=0&lon=1.75781&layers=B#test';
+ map.addControl(control);
+ map.pan(5, 0, {animate:false});
+ map.pan(-5, 0, {animate:false});
+ t.eq(OpenLayers.Util.getElement('permalink').href, OpenLayers.Util.getElement('edit_permalink').href, "Panning sets permalink with base, querystring and an anchor");
+
+ control = new OpenLayers.Control.Permalink('permalink', "./edit.html#foo", {anchor : true} );
+ map.addControl(control);
+ map.pan(0, 0, {animate:false});
+ OpenLayers.Util.getElement('edit_permalink').href = './edit.html#zoom=2&lat=0&lon=1.75781&layers=B';
+ t.eq(OpenLayers.Util.getElement('permalink').href, OpenLayers.Util.getElement('edit_permalink').href, "Panning sets permalink with base and an empty anchor");
+ }
+
+ function test_Control_Permalink_nonRepeating (t) {
+ t.plan( 2 );
+
+ control = new OpenLayers.Control.Permalink('permalink', "./edit.html?zoom=3" );
+ t.ok( control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object" );
+ map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'});
+ map.addLayer(layer);
+ if (!map.getCenter()) map.zoomToMaxExtent();
+ map.addControl(control);
+ map.pan(5, 0, {animate:false});
+ OpenLayers.Util.getElement('edit_permalink').href = './edit.html?zoom=2&lat=0&lon=1.75781&layers=B';
+ t.eq(OpenLayers.Util.getElement('permalink').href, OpenLayers.Util.getElement('edit_permalink').href, "Panning sets permalink with existing zoom in base");
+ map.destroy();
+ }
+
+ function test_Control_Permalink_customized(t) {
+ t.plan(2);
+
+ var argParserClass = OpenLayers.Class(OpenLayers.Control.ArgParser, {
+ CLASS_NAME: "CustomArgParser"
+ });
+
+ control = new OpenLayers.Control.Permalink(null, "./edit.html", {
+ argParserClass: argParserClass,
+ createParams: function(center, zoom, layers) {
+ var params = OpenLayers.Control.Permalink.prototype.createParams.apply(control, arguments);
+ params.customParam = "foo";
+ return params;
+ }
+ });
+ map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'});
+ map.addLayer(layer);
+ if (!map.getCenter()) map.zoomToMaxExtent();
+ map.addControl(control);
+ map.pan(5, 0, {animate:false});
+
+ t.eq(this.map.controls[this.map.controls.length-1].CLASS_NAME, "CustomArgParser", "Custom ArgParser added correctly.");
+ t.eq(control.div.firstChild.getAttribute("href"), "./edit.html?zoom=2&lat=0&lon=1.75781&layers=B&customParam=foo", "Custom parameter encoded correctly.");
+ map.destroy();
+ }
+
+ function test_Control_Permalink_createParams(t) {
+ t.plan(18);
+
+ var baseLayer = { 'isBaseLayer': true };
+
+ var m = {
+ 'getCenter': function() { return null; }
+ };
+
+ var pl = {
+ 'map': m,
+ 'base': {}
+ };
+
+ old_getParameters = OpenLayers.Util.getParameters;
+ OpenLayers.Util.getParameters = function(base) {
+ t.ok(base == pl.base, "correct base sent in to Util.getParameters()");
+ return g_Params;
+ };
+
+ //null center, null map.getCenter()
+ g_Params = {};
+ m.baseLayer = baseLayer;
+ var returnParams = OpenLayers.Control.Permalink.prototype.createParams.apply(pl, []);
+ t.ok(returnParams == g_Params, "correct params returned on null center");
+
+ //valid center, zoom, layers
+ g_Params = { 'test': {} };
+ var center = { 'lon': 1.2345678901, 'lat': 9.8765432109 };
+ var zoom = {};
+ var layers = [
+ { 'isBaseLayer': true },
+ baseLayer,
+ { 'isBaseLayer': false, 'getVisibility': function() { return true; } },
+ { 'isBaseLayer': false, 'getVisibility': function() { return false; } }
+ ];
+ var returnParams = OpenLayers.Control.Permalink.prototype.createParams.apply(pl, [center, zoom, layers]);
+
+ t.ok(returnParams.test == g_Params.test, "correct params returned from Util.getParameters() when valid center, zoom, layers");
+ t.ok(returnParams.zoom == zoom, "params.zoom set correctly when valid center, zoom, layers");
+ t.eq(returnParams.lon, 1.23457, "lon set and rounded correctly when valid center, zoom, layers");
+ t.eq(returnParams.lat, 9.87654, "lat set and rounded correctly when valid center, zoom, layers");
+ t.eq(returnParams.layers, "0BTF", "layers processed correctly when valid center, zoom, layers")
+
+
+ //null center, zoom, layers, with displayProjection
+ g_Params = { 'test': {} };
+ g_Projection = {};
+ m = {
+ 'baseLayer': baseLayer,
+ 'getProjectionObject': function() { return g_Projection; },
+ 'center': { 'lon': {}, 'lat': {} },
+ 'getCenter': function() { return this.center; },
+ 'zoom': {},
+ 'getZoom': function() { return this.zoom; },
+ 'layers': [
+ { 'isBaseLayer': false, 'getVisibility': function() { return true; } },
+ baseLayer,
+ { 'isBaseLayer': false, 'getVisibility': function() { return false; } },
+ { 'isBaseLayer': true }
+ ],
+ 'getLayers': function() { return this.layers; }
+ };
+ pl = {
+ 'base': {},
+ 'map': m,
+ 'displayProjection': {}
+ };
+
+ old_transform = OpenLayers.Projection.transform;
+ OpenLayers.Projection.transform = function(point, projObj, dispProj) {
+ t.ok(point.x = m.center.lon, "correct x value passed into transform");
+ t.ok(point.y = m.center.lat, "correct x value passed into transform");
+ t.ok(projObj == g_Projection, "correct projection object from map passed into transform");
+ t.ok(dispProj == pl.displayProjection, "correct displayProjection from control passed into transform");
+
+ return { 'x': 9.8765432109, 'y': 1.2345678901 };
+ };
+
+ center = zoom = layers = null;
+
+ var returnParams = OpenLayers.Control.Permalink.prototype.createParams.apply(pl, [center, zoom, layers]);
+ t.ok(returnParams.test == g_Params.test, "correct params returned from Util.getParameters() when null center, zoom, layers, with displayProjection");
+ t.ok(returnParams.zoom == m.zoom, "params.zoom set correctly when null center, zoom, layers, with displayProjection");
+ t.eq(returnParams.lon, 9.87654, "lon set, transformed, and rounded correctly when null center, zoom, layers, with displayProjection");
+ t.eq(returnParams.lat, 1.23457, "lat set, transformed, and rounded correctly when null center, zoom, layers, with displayProjection");
+ t.eq(returnParams.layers, "TBF0", "layers processed correctly when null center, zoom, layers, with displayProjection");
+
+ OpenLayers.Util.getParameters = old_getParameters;
+ OpenLayers.Projection.transform = old_transform;
+ }
+ function test_Control_Permalink_Anchor (t) {
+ t.plan(3);
+
+ control = new OpenLayers.Control.Permalink({anchor: true});
+ t.ok( control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object" );
+ map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'});
+ map.addLayer(layer);
+ layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'}, {'isBaseLayer': false});
+ map.addLayer(layer);
+ layer.setVisibility(true);
+ if (!map.getCenter()) map.zoomToMaxExtent();
+ map.addControl(control);
+ map.pan(5, 0, {animate:false});
+ t.ok(OpenLayers.Util.isEquivalentUrl(OpenLayers.Util.getParameterString(control.createParams()), "zoom=2&lat=0&lon=1.75781&layers=BT"), 'pan sets permalink');
+
+ map.layers[1].setVisibility(false);
+ t.ok(OpenLayers.Util.isEquivalentUrl(OpenLayers.Util.getParameterString(control.createParams()), "zoom=2&lat=0&lon=1.75781&layers=BF"), 'setVisibility sets permalink');
+ map.destroy();
+ }
+
+ function test_Control_Permalink_AnchorBaseElement (t) {
+ t.plan(3);
+
+ control = new OpenLayers.Control.Permalink('permalink', document.location.href, {anchor: true});
+ t.ok( control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object" );
+ map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'});
+ map.addLayer(layer);
+ layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'}, {'isBaseLayer': false});
+ map.addLayer(layer);
+ layer.setVisibility(true);
+ if (!map.getCenter()) map.zoomToMaxExtent();
+ map.addControl(control);
+ map.pan(5, 0, {animate:false});
+ t.ok(OpenLayers.Util.isEquivalentUrl(OpenLayers.Util.getElement('permalink').href, location+"#zoom=2&lat=0&lon=1.75781&layers=BT"), 'pan sets permalink');
+
+ map.layers[1].setVisibility(false);
+ t.ok(OpenLayers.Util.isEquivalentUrl(OpenLayers.Util.getElement('permalink').href, location+"#zoom=2&lat=0&lon=1.75781&layers=BF"), 'setVisibility sets permalink');
+ map.destroy();
+ }
+
+ function test_center_from_map(t) {
+ t.plan(7);
+
+ var previous = window.location.hash;
+ window.location.hash = "";
+
+ var err;
+ try {
+ var map = new OpenLayers.Map({
+ layers: [new OpenLayers.Layer(null, {isBaseLayer: true})],
+ controls: [
+ new OpenLayers.Control.Permalink({anchor: true})
+ ],
+ center: [1, 2],
+ zoom: 3
+ });
+ } catch (e) {
+ err = e;
+ }
+ if (err) {
+ t.fail("Map construction failure: " + err.message);
+ } else {
+ t.ok(true, "Map construction works");
+ }
+
+ // confirm that map center is correctly set
+ var center = map.getCenter();
+ t.eq(center.lon, 1, "map x");
+ t.eq(center.lat, 2, "map y")
+ t.eq(map.getZoom(), 3, "map z");
+
+ // confirm that location from map options has been added to url
+ var params = OpenLayers.Util.getParameters(window.location.hash.replace("#", "?"));
+ t.eq(params.lon, "1", "url x");
+ t.eq(params.lat, "2", "url y");
+ t.eq(params.zoom, "3", "url z");
+
+ map.destroy();
+ window.location.hash = previous;
+ }
+
+ function test_center_from_url(t) {
+ t.plan(6);
+
+ // In cases where the location is specified in the URL and given in
+ // the map options, we respect the location in the URL.
+ var previous = window.location.hash;
+ window.location.hash = "#zoom=6&lat=5&lon=4&layers=B"
+
+ var map = new OpenLayers.Map({
+ layers: [new OpenLayers.Layer(null, {isBaseLayer: true})],
+ controls: [new OpenLayers.Control.Permalink({anchor: true})],
+ center: [0, 0],
+ zoom: 0
+ });
+
+ // confirm that map center is correctly set
+ var center = map.getCenter();
+ t.eq(center.lon, 4, "map x");
+ t.eq(center.lat, 5, "map y")
+ t.eq(map.getZoom(), 6, "map z");
+
+ var params = OpenLayers.Util.getParameters(window.location.hash.replace("#", "?"));
+ t.eq(params.lon, "4", "x set");
+ t.eq(params.lat, "5", "y set");
+ t.eq(params.zoom, "6", "z set");
+
+ map.destroy();
+ window.location.hash = previous;
+ }
+
+ </script>
+</head>
+<body>
+ <a id="permalink" href="">Permalink</a> <br />
+ <a id="edit_permalink" href="">Edit</a> <br />
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/PinchZoom.html b/misc/openlayers/tests/Control/PinchZoom.html
new file mode 100644
index 0000000..22db6a5
--- /dev/null
+++ b/misc/openlayers/tests/Control/PinchZoom.html
@@ -0,0 +1,134 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(2);
+ var control = new OpenLayers.Control.PinchZoom();
+ t.ok(control instanceof OpenLayers.Control.PinchZoom, "got an instance");
+ t.ok(control.handler instanceof OpenLayers.Handler.Pinch, "control has pinch handler");
+ control.destroy();
+ }
+
+ function test_destroy(t) {
+ t.plan(1);
+ var control = new OpenLayers.Control.PinchZoom();
+ control.destroy();
+ t.ok(!control.handler, "handler destroyed");
+ }
+
+ function test_activate(t) {
+ t.plan(3);
+ var control = new OpenLayers.Control.PinchZoom();
+ t.ok(!control.active, "control not activated after construction");
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ controls: [control]
+ });
+ t.ok(control.active, "control activated after being added to the map");
+
+ control.deactivate();
+ t.ok(!control.active, "control deactivated");
+
+ map.destroy();
+ }
+
+ function test_pinchMove(t) {
+
+ var control = new OpenLayers.Control.PinchZoom();
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ controls: [control]
+ });
+
+ var log = [];
+ map.applyTransform = function(x, y, scale) {
+ log.push([x, y, scale]);
+ }
+
+ map.layerContainerOriginPx = {
+ x: -50, y: -50
+ };
+
+ control.pinchOrigin = {
+ x: 100, y: 50
+ };
+
+ var cases = [
+ {x: 100, y: 60, scale: 1, transform: [-50, -40, 1]},
+ {x: 150, y: 60, scale: 1, transform: [0, -40, 1]},
+ {x: 150, y: 60, scale: 2, transform: [-150, -140, 2]},
+ {x: 50, y: 20, scale: 2.5, transform: [-325, -230, 2.5]},
+ {x: 150, y: 60, scale: 2, transform: [-150, -140, 2]},
+ {x: 50, y: 20, scale: 0.25, transform: [13, -5, 0.25]}
+ ];
+
+ var len = cases.length;
+ t.plan(len*2);
+
+ var c;
+ for (var i=0; i<len; ++i) {
+ c = cases[i];
+ control.pinchMove({xy: {x: c.x, y: c.y}}, {scale: c.scale});
+ t.eq(log.length, i+1, i + " called once");
+ t.eq(log[i], c.transform, i + " correct transform");
+ }
+
+ }
+
+ function test_pinchMove_preservecenter(t) {
+
+ var control = new OpenLayers.Control.PinchZoom({
+ preserveCenter: true
+ });
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ controls: [control],
+ layers: [new OpenLayers.Layer('fake', {isBaseLayer: true})]
+ });
+ map.zoomToMaxExtent();
+
+ var centerPx = map.getPixelFromLonLat(map.getCenter());
+
+ control.pinchStart = function(evt, pinchData) {
+ t.eq(map.layerContainerOriginPx, {x: 0, y: 0}, "center preserved");
+ t.eq(map.getPixelFromLonLat(map.getCenter()), centerPx, "center preserved");
+ }
+
+ control.pinchStart(null);
+
+ var log = [];
+ map.applyTransform = function(x, y, scale) {
+ log.push([x, y, scale]);
+ }
+ control.pinchOrigin = map.getPixelFromLonLat(map.getCenter());
+
+ var cases = [
+ {scale: 1, transform: [0, 0, 1]},
+ {scale: 2, transform: [-128, -128, 2]},
+ {scale: 2.5, transform: [-192, -192, 2.5]},
+ {scale: 0.25, transform: [96, 96, 0.25]}
+ ];
+
+ var len = cases.length;
+ t.plan(2 + len*2);
+
+ var c;
+ for (var i=0; i<len; ++i) {
+ c = cases[i];
+ control.pinchMove(null, {scale: c.scale});
+ t.eq(log.length, i+1, i + " called once");
+ t.eq(log[i], c.transform, i + " correct transform");
+ }
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 256px; height: 256px;"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/SLDSelect.html b/misc/openlayers/tests/Control/SLDSelect.html
new file mode 100644
index 0000000..03b871c
--- /dev/null
+++ b/misc/openlayers/tests/Control/SLDSelect.html
@@ -0,0 +1,239 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(11);
+ var control = new OpenLayers.Control.SLDSelect(OpenLayers.Handler.Click);
+ t.eq(control.handler instanceof OpenLayers.Handler.Click, true, "Click handler created");
+ t.ok(control.handler.callbacks["click"] === control.select, "Click callback correctly set");
+ control.destroy();
+ control = new OpenLayers.Control.SLDSelect(OpenLayers.Handler.RegularPolygon, {handlerOptions: {irregular: true}});
+ t.eq(control.handler instanceof OpenLayers.Handler.RegularPolygon, true, "RegularPolygon handler created");
+ t.eq(control.handler.irregular, true, "RegularPolygon handler is irregular");
+ t.eq(control.handler.persist, false, "RegularPolygon handler is not persistant");
+ t.ok(control.handler.callbacks["done"] === control.select, "Done callback correctly set");
+ control.destroy();
+ control = new OpenLayers.Control.SLDSelect(OpenLayers.Handler.Polygon);
+ t.eq(control.handler instanceof OpenLayers.Handler.Polygon, true, "Polygon handler created");
+ t.ok(control.handler.callbacks["done"] === control.select, "Done callback correctly set");
+ control.destroy();
+ control = new OpenLayers.Control.SLDSelect(OpenLayers.Handler.Path);
+ t.eq(control.handler instanceof OpenLayers.Handler.Path, true, "Path handler created");
+ t.ok(control.handler.callbacks["done"] === control.select, "Done callback correctly set");
+ control.destroy();
+ var layer = new OpenLayers.Layer.WMS('Foo', 'http://foo', {LAYERS: 'A'});
+ control = new OpenLayers.Control.SLDSelect(OpenLayers.Handler.RegularPolygon, {layers: [layer]});
+ t.eq(control.layers.length, 1, "Layers property correctly set");
+ control.destroy();
+ layer.destroy();
+ }
+
+ function test_select(t) {
+ t.plan(9);
+ var parser = new OpenLayers.Format.WFSDescribeFeatureType();
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS('Foo', 'http://foo', {LAYERS: 'AAA64'});
+ map.addLayer(layer);
+
+ var text =
+ '<?xml version="1.0" encoding="ISO-8859-1" ?>' +
+ '<schema' +
+ ' targetNamespace="http://mapserver.gis.umn.edu/mapserver" ' +
+ ' xmlns:rws="http://mapserver.gis.umn.edu/mapserver" ' +
+ ' xmlns:ogc="http://www.opengis.net/ogc"' +
+ ' xmlns:xsd="http://www.w3.org/2001/XMLSchema"' +
+ ' xmlns="http://www.w3.org/2001/XMLSchema"' +
+ ' xmlns:gml="http://www.opengis.net/gml"' +
+ ' elementFormDefault="qualified" version="0.1" >' +
+ ' <import namespace="http://www.opengis.net/gml"' +
+ ' schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd" />' +
+ ' <element name="AAA64" ' +
+ ' type="rws:AAA64Type" ' +
+ ' substitutionGroup="gml:_Feature" />' +
+ ' <complexType name="AAA64Type">' +
+ ' <complexContent>' +
+ ' <extension base="gml:AbstractFeatureType">' +
+ ' <sequence>' +
+ ' <element name="geometry" type="gml:MultiLineStringPropertyType" minOccurs="0" maxOccurs="1"/>' +
+ ' <element name="OBJECTID" type="string"/>' +
+ ' </sequence>' +
+ ' </extension>' +
+ ' </complexContent>' +
+ ' </complexType>' +
+ '</schema>';
+
+ OpenLayers.Control.SLDSelect.prototype.wfsCache[layer.id] = parser.read(text);
+ var control = new OpenLayers.Control.SLDSelect(OpenLayers.Handler.RegularPolygon,
+ {layers: [layer], clearOnDeactivate: true, handlerOptions: {irregular: true} });
+
+ var testEvent = function(evt) {
+ t.eq(evt.filters.length, 1, "Event has a filters array set");
+ t.eq(evt.filters[0] instanceof OpenLayers.Filter.Spatial, true, "Spatial filter has been created");
+ };
+
+ control.events.register("selected", this, testEvent);
+ map.addControl(control);
+ var geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(
+ new OpenLayers.Geometry.Point(0, 0), 5, 4);
+ control.select(geometry);
+ control.events.unregister("selected", this, testEvent);
+ t.eq(map.layers.length, 2, "Selection layer has been created and added to the map");
+ t.eq(map.layers[1] instanceof OpenLayers.Layer.WMS, true, "A WMS layer has been created as the selection layer");
+ t.eq(map.layers[1].tileOptions.maxGetUrlLength, 2048, "Selection layer will automatically switch to HTTP Post if content gets longer than 2048");
+ var expected_sld = '<sld:StyledLayerDescriptor xmlns:sld="http://www.opengis.net/sld" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml"><sld:NamedLayer><sld:Name>AAA64</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326"><gml:coordinates decimal="." cs="," ts=" ">-3.5355339059327,-3.5355339059327 3.5355339059327,3.5355339059327</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:LineSymbolizer><sld:Stroke><sld:CssParameter name="stroke">#FF0000</sld:CssParameter><sld:CssParameter name="stroke-width">2</sld:CssParameter></sld:Stroke></sld:LineSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer></sld:StyledLayerDescriptor>';
+
+ t.xml_eq(map.layers[1].params.SLD_BODY, expected_sld, "SLD generated correctly");
+
+ var geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(
+ new OpenLayers.Geometry.Point(0, 0), 7, 4);
+ control.select(geometry);
+ t.eq(map.layers.length, 2, "Selection layer is reused when new selection is performed");
+
+ map.layers[0].setVisibility(false);
+ t.eq(map.layers[1].getVisibility(), false, "Visibility of selection layer is synchronized with source layer");
+ // activate would issue a SLD WMS DescribeLayer request and we are bypassing this here
+ control.active = true;
+ control.deactivate();
+ t.eq(map.layers.length, 1, "Selection layer is removed on deactive if clearOnDeactivate is set to true");
+ map.destroy();
+ }
+
+ function test_filterModificationOnSelected(t) {
+ t.plan(1);
+ var parser = new OpenLayers.Format.WFSDescribeFeatureType();
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS('Foo', 'http://foo', {LAYERS: 'AAA64'});
+ map.addLayer(layer);
+
+ var text =
+ '<?xml version="1.0" encoding="ISO-8859-1" ?>' +
+ '<schema' +
+ ' targetNamespace="http://mapserver.gis.umn.edu/mapserver" ' +
+ ' xmlns:rws="http://mapserver.gis.umn.edu/mapserver" ' +
+ ' xmlns:ogc="http://www.opengis.net/ogc"' +
+ ' xmlns:xsd="http://www.w3.org/2001/XMLSchema"' +
+ ' xmlns="http://www.w3.org/2001/XMLSchema"' +
+ ' xmlns:gml="http://www.opengis.net/gml"' +
+ ' elementFormDefault="qualified" version="0.1" >' +
+ ' <import namespace="http://www.opengis.net/gml"' +
+ ' schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd" />' +
+ ' <element name="AAA64" ' +
+ ' type="rws:AAA64Type" ' +
+ ' substitutionGroup="gml:_Feature" />' +
+ ' <complexType name="AAA64Type">' +
+ ' <complexContent>' +
+ ' <extension base="gml:AbstractFeatureType">' +
+ ' <sequence>' +
+ ' <element name="geometry" type="gml:MultiLineStringPropertyType" minOccurs="0" maxOccurs="1"/>' +
+ ' <element name="OBJECTID" type="string"/>' +
+ ' </sequence>' +
+ ' </extension>' +
+ ' </complexContent>' +
+ ' </complexType>' +
+ '</schema>';
+
+ OpenLayers.Control.SLDSelect.prototype.wfsCache[layer.id] = parser.read(text);
+ var control = new OpenLayers.Control.SLDSelect(OpenLayers.Handler.RegularPolygon,
+ {layers: [layer], clearOnDeactivate: true, handlerOptions: {irregular: true} });
+
+ var testEvent = function(evt) {
+ // manipulate filter
+ var bbox = OpenLayers.Bounds.fromString('1,2,3,4');
+ evt.filters[0].value = bbox;
+ };
+ control.events.register("selected", this, testEvent);
+ map.addControl(control);
+ var geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(
+ new OpenLayers.Geometry.Point(0, 0), 5, 4);
+ control.select(geometry);
+ control.events.unregister("selected", this, testEvent);
+
+ var expected_sld = '<sld:StyledLayerDescriptor xmlns:sld="http://www.opengis.net/sld" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml"><sld:NamedLayer><sld:Name>AAA64</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326"><gml:coordinates decimal="." cs="," ts=" ">1,2 3,4</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:LineSymbolizer><sld:Stroke><sld:CssParameter name="stroke">#FF0000</sld:CssParameter><sld:CssParameter name="stroke-width">2</sld:CssParameter></sld:Stroke></sld:LineSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer></sld:StyledLayerDescriptor>';
+
+ t.xml_eq(map.layers[1].params.SLD_BODY, expected_sld, "Filter / SLD manipulated in select-callback correctly");
+
+ }
+
+ function test_multiselect(t) {
+ t.plan(2);
+
+ var parser = new OpenLayers.Format.WFSDescribeFeatureType();
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS('Multi', 'http://foo', {LAYERS: 'KGNAT.VKUNSTWERK,KGNAT.LKUNSTWERK,KGNAT.PKUNSTWERK'});
+ map.addLayer(layer);
+
+ var text =
+ '<?xml version="1.0" encoding="ISO-8859-1" ?>' +
+ '<schema' +
+ ' targetNamespace="http://mapserver.gis.umn.edu/mapserver" ' +
+ ' xmlns:rws="http://mapserver.gis.umn.edu/mapserver" ' +
+ ' xmlns:ogc="http://www.opengis.net/ogc"' +
+ ' xmlns:xsd="http://www.w3.org/2001/XMLSchema"' +
+ ' xmlns="http://www.w3.org/2001/XMLSchema"' +
+ ' xmlns:gml="http://www.opengis.net/gml"' +
+ ' elementFormDefault="qualified" version="0.1" >' +
+ ' <import namespace="http://www.opengis.net/gml"' +
+ ' schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd" />' +
+ ' <element name="KGNAT.VKUNSTWERK" ' +
+ ' type="rws:KGNAT.VKUNSTWERKType" ' +
+ ' substitutionGroup="gml:_Feature" />' +
+ ' <complexType name="KGNAT.VKUNSTWERKType">' +
+ ' <complexContent>' +
+ ' <extension base="gml:AbstractFeatureType">' +
+ ' <sequence>' +
+ ' <element name="geometry" type="gml:MultiPolygonPropertyType" minOccurs="0" maxOccurs="1"/>' +
+ ' </sequence>' +
+ ' </extension>' +
+ ' </complexContent>' +
+ ' </complexType>' +
+ ' <element name="KGNAT.LKUNSTWERK" ' +
+ ' type="rws:KGNAT.LKUNSTWERKType" ' +
+ ' substitutionGroup="gml:_Feature" />' +
+ ' <complexType name="KGNAT.LKUNSTWERKType">' +
+ ' <complexContent>' +
+ ' <extension base="gml:AbstractFeatureType">' +
+ ' <sequence>' +
+ ' <element name="geometry" type="gml:MultiLineStringPropertyType" minOccurs="0" maxOccurs="1"/>' +
+ ' </sequence>' +
+ ' </extension>' +
+ ' </complexContent>' +
+ ' </complexType>' +
+ ' <element name="KGNAT.PKUNSTWERK" ' +
+ ' type="rws:KGNAT.PKUNSTWERKType" ' +
+ ' substitutionGroup="gml:_Feature" />' +
+ ' <complexType name="KGNAT.PKUNSTWERKType">' +
+ ' <complexContent>' +
+ ' <extension base="gml:AbstractFeatureType">' +
+ ' <sequence>' +
+ ' <element name="geometry" type="gml:MultiPointPropertyType" minOccurs="0" maxOccurs="1"/>' +
+ ' </sequence>' +
+ ' </extension>' +
+ ' </complexContent>' +
+ ' </complexType>' +
+ '</schema>';
+
+ OpenLayers.Control.SLDSelect.prototype.wfsCache[layer.id] = parser.read(text);
+ var control = new OpenLayers.Control.SLDSelect(OpenLayers.Handler.RegularPolygon, {handlerOptions: {irregular: true}, layers: [layer]});
+ var testEvent = function(evt) {
+ t.eq(evt.filters.length, 3, "Event has a filters array set with length tree");
+ };
+ control.events.register("selected", this, testEvent);
+
+ map.addControl(control);
+ var geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(
+ new OpenLayers.Geometry.Point(0, 0), 5, 4);
+ control.select(geometry);
+ var expected_sld = '<sld:StyledLayerDescriptor xmlns:sld="http://www.opengis.net/sld" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml"><sld:NamedLayer><sld:Name>KGNAT.VKUNSTWERK</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326"><gml:coordinates decimal="." cs="," ts=" ">-3.5355339059327,-3.5355339059327 3.5355339059327,3.5355339059327</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:PolygonSymbolizer><sld:Fill><sld:CssParameter name="fill">#FF0000</sld:CssParameter></sld:Fill></sld:PolygonSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer><sld:NamedLayer><sld:Name>KGNAT.LKUNSTWERK</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326"><gml:coordinates decimal="." cs="," ts=" ">-3.5355339059327,-3.5355339059327 3.5355339059327,3.5355339059327</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:LineSymbolizer><sld:Stroke><sld:CssParameter name="stroke">#FF0000</sld:CssParameter><sld:CssParameter name="stroke-width">2</sld:CssParameter></sld:Stroke></sld:LineSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer><sld:NamedLayer><sld:Name>KGNAT.PKUNSTWERK</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326"><gml:coordinates decimal="." cs="," ts=" ">-3.5355339059327,-3.5355339059327 3.5355339059327,3.5355339059327</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:PointSymbolizer><sld:Graphic><sld:Mark><sld:WellKnownName>square</sld:WellKnownName><sld:Fill><sld:CssParameter name="fill">#FF0000</sld:CssParameter></sld:Fill><sld:Stroke/></sld:Mark><sld:Size>10</sld:Size></sld:Graphic></sld:PointSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer></sld:StyledLayerDescriptor>';
+
+ t.xml_eq(map.layers[1].params.SLD_BODY, expected_sld, "SLD generated correctly");
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 100px; height: 100px;"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/Scale.html b/misc/openlayers/tests/Control/Scale.html
new file mode 100644
index 0000000..1d43b25
--- /dev/null
+++ b/misc/openlayers/tests/Control/Scale.html
@@ -0,0 +1,54 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ OpenLayers.Lang.setCode('en');
+ var map;
+ function test_Control_Scale_constructor (t) {
+ t.plan( 2 );
+
+ control = new OpenLayers.Control.Scale();
+ t.ok( control instanceof OpenLayers.Control.Scale, "new OpenLayers.Control returns object" );
+ t.eq( control.displayClass, "olControlScale", "displayClass is correct" );
+ }
+ function test_Control_Scale_initwithelem (t) {
+ t.plan( 1 );
+
+ control = new OpenLayers.Control.Scale(OpenLayers.Util.getElement('scale'));
+ t.ok(true, "If this happens, then we passed. (FF throws an error above otherwise)");
+ }
+ function test_Control_Scale_updateScale (t) {
+ t.plan( 4 );
+
+ control = new OpenLayers.Control.Scale('scale');
+ t.ok( control instanceof OpenLayers.Control.Scale, "new OpenLayers.Control returns object" );
+ map = new OpenLayers.Map('map', {zoomMethod: null});
+ layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'});
+ map.addLayer(layer);
+ map.zoomTo(0);
+ map.addControl(control);
+ t.eq(OpenLayers.Util.getElement('scale').innerHTML, "Scale = 1 : 443M", "Scale set by default." );
+ map.zoomIn();
+ t.eq(OpenLayers.Util.getElement('scale').innerHTML, "Scale = 1 : 221M", "Zooming in changes scale" );
+ map.baseLayer.resolutions = [OpenLayers.Util.getResolutionFromScale(110)];
+ map.zoomTo(0);
+ t.eq(OpenLayers.Util.getElement('scale').innerHTML, "Scale = 1 : 110", "Scale of 100 isn't rounded" );
+ }
+ function test_Control_Scale_internalScale (t) {
+ t.plan(2);
+ control = new OpenLayers.Control.Scale();
+ t.ok( control instanceof OpenLayers.Control.Scale, "new OpenLayers.Control returns object" );
+ map = new OpenLayers.Map('map', {zoomMethod: null});
+ layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'});
+ map.addLayer(layer);
+ map.zoomTo(0);
+ map.addControl(control);
+ t.eq(control.div.firstChild.innerHTML, "Scale = 1 : 443M", "Internal scale displayed properly.");
+ }
+ </script>
+</head>
+<body>
+ <a id="scale" href="">Scale</a> <br />
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/ScaleLine.html b/misc/openlayers/tests/Control/ScaleLine.html
new file mode 100644
index 0000000..e863fd9
--- /dev/null
+++ b/misc/openlayers/tests/Control/ScaleLine.html
@@ -0,0 +1,187 @@
+<html>
+<head>
+ <script type="text/javascript">var oldAlert = window.alert, gMess; window.alert = function(message) {gMess = message; return true;};</script>
+ <!-- this gmaps key generated for http://openlayers.org/dev/ -->
+ <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAA9XNhd8q0UdwNC7YSO4YZghSPUCi5aRYVveCcVYxzezM4iaj_gxQ9t-UajFL70jfcpquH5l1IJ-Zyyw'></script>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var validkey = (window.location.protocol == "file:") ||
+ (window.location.host == "localhost") ||
+ (window.location.host == "openlayers.org");
+
+ function test_initialize(t) {
+ t.plan(2);
+ var control = new OpenLayers.Control.ScaleLine();
+ t.ok(control instanceof OpenLayers.Control.ScaleLine, "new OpenLayers.Control returns object" );
+ t.eq(control.displayClass, "olControlScaleLine", "displayClass is correct" );
+ control.destroy();
+ }
+
+ function test_initwithelem(t) {
+ t.plan(1);
+ var control = new OpenLayers.Control.ScaleLine({"div":OpenLayers.Util.getElement('ScaleLine')});
+ t.ok(true, "If this happens, then we passed. (FF throws an error above otherwise)");
+ control.destroy();
+ }
+
+ function test_calcDegrees(t) {
+ t.plan(5);
+ var control = new OpenLayers.Control.ScaleLine();
+ t.ok(control instanceof OpenLayers.Control.ScaleLine, "new OpenLayers.Control returns object" );
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'});
+ map.addLayer(layer);
+ map.zoomTo(0);
+ map.addControl(control);
+ t.eq(control.div.firstChild.style.visibility, "visible", "top scale is present.");
+ t.eq(control.div.lastChild.style.visibility, "visible", "bottom scale is present.");
+ t.eq(control.div.firstChild.innerHTML, "10000 km", "top scale has correct text.");
+ t.eq(control.div.lastChild.innerHTML, "5000 mi", "bottom scale has correct text.");
+ map.destroy();
+ }
+
+ function test_calcsOther (t) {
+ t.plan(5);
+ var control = new OpenLayers.Control.ScaleLine();
+ t.ok(control instanceof OpenLayers.Control.ScaleLine, "new OpenLayers.Control returns object" );
+ var map = new OpenLayers.Map('map');
+ map.units = "mi";
+ var layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'});
+ map.addLayer(layer);
+ map.zoomTo(0);
+ map.addControl(control);
+ t.eq(control.div.firstChild.style.visibility, "visible", "top scale is present.");
+ t.eq(control.div.lastChild.style.visibility, "visible", "bottom scale is present.");
+ t.eq(control.div.firstChild.innerHTML, "100 km", "top scale has correct text.");
+ t.eq(control.div.lastChild.innerHTML, "100 mi", "bottom scale has correct text.");
+ map.destroy();
+ }
+
+ function test_calcMeters (t) {
+ t.plan(5);
+ // this example is taken from the projected-map.html OpenLayers example
+ var lat = 900863;
+ var lon = 235829;
+ var zoom = 6;
+ var map = new OpenLayers.Map( 'map' );
+ var basemap = new OpenLayers.Layer.WMS( "Boston",
+ "http://boston.freemap.in/cgi-bin/mapserv?",
+ {
+ map: '/www/freemap.in/boston/map/gmaps.map',
+ layers: 'border,water,roads,rapid_transit,buildings',
+ format: 'png',
+ transparent: 'off'
+ },
+
+ {
+ maxExtent: new OpenLayers.Bounds(33861, 717605, 330846, 1019656),
+ maxResolution: 296985/1024,
+ projection:"EPSG:2805", // Used in WMS/WFS requests.
+ units: "m" // Only neccesary for working with scales.
+ } );
+
+ map.addLayer(basemap);
+ map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+ map.addControl(new OpenLayers.Control.LayerSwitcher());
+ var control = new OpenLayers.Control.ScaleLine();
+ t.ok( control instanceof OpenLayers.Control.ScaleLine, "new OpenLayers.Control returns object" );
+ map.addControl(control);
+ t.eq(control.div.firstChild.style.visibility, "visible", "top scale is present.");
+ t.eq(control.div.lastChild.style.visibility, "visible", "bottom scale is present.");
+ t.eq(control.div.firstChild.innerHTML, "200 m", "top scale has correct text.");
+ t.eq(control.div.lastChild.innerHTML, "1000 ft", "bottom scale has correct text.");
+ map.destroy();
+ }
+
+ function test_useArguments (t) {
+ t.plan(5);
+ var control = new OpenLayers.Control.ScaleLine({topOutUnits: 'dd'} );
+ t.ok( control instanceof OpenLayers.Control.ScaleLine, "new OpenLayers.Control returns object" );
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'});
+ map.addLayer(layer);
+ map.zoomTo(0);
+ map.addControl(control);
+ t.eq(control.div.firstChild.style.visibility, "visible", "top scale is present.");
+ t.eq(control.div.lastChild.style.visibility, "visible", "bottom scale is present.");
+ t.eq(control.div.firstChild.innerHTML, "100 dd", "top scale has correct text.");
+ t.eq(control.div.lastChild.innerHTML, "5000 mi", "bottom scale has correct text.");
+ map.destroy();
+ }
+
+ function test_respectZoom (t) {
+ if(validkey) {
+ t.plan( 4 );
+ } else {
+ t.plan( 3 );
+ }
+ // ok, switch the units we use for zoomed in values. This will test that we're both
+ // correctly respecting all specified parameters and that we're switching to the
+ // "in" units when zoomed in
+ var control = new OpenLayers.Control.ScaleLine({topOutUnits : "mi", bottomOutUnits: "km", topInUnits: 'ft', bottomInUnits: 'm'});
+ t.ok( control instanceof OpenLayers.Control.ScaleLine, "new OpenLayers.Control returns object" );
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'});
+ map.addLayer(layer);
+ map.zoomTo(0);
+ map.addControl(control);
+ var widthIsOk = true;
+ for (var i=0; i<map.numZoomLevels && widthIsOk; i++) {
+ map.zoomTo(i);
+ var w1 = parseInt(control.eTop.style.width);
+ var w2 = parseInt(control.eBottom.style.width);
+ widthIsOk = w1 <= control.maxWidth && w2 <= control.maxWidth;
+ }
+ t.ok(widthIsOk, "respects maxWidth at all zoom levels in dd");
+
+ widthIsOk = true;
+ control.maxWidth = 200;
+ for (var i=0; i<map.numZoomLevels && widthIsOk; i++) {
+ map.zoomTo(i);
+ var w1 = parseInt(control.eTop.style.width);
+ var w2 = parseInt(control.eBottom.style.width);
+ widthIsOk = w1 <= control.maxWidth && w2 <= control.maxWidth;
+ }
+ t.ok(widthIsOk, "respects modified maxWidth at all zoom levels in dd");
+
+ if (validkey) {
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.Google('Goog Layer');
+ var control = new OpenLayers.Control.ScaleLine({topOutUnits : "mi", bottomOutUnits: "km", topInUnits: 'ft', bottomInUnits: 'm'});
+ map.addLayer(layer);
+ map.zoomTo(0);
+ map.addControl(control);
+ var widthIsOk = true;
+ for (var i=0; i<map.numZoomLevels && widthIsOk; i++) {
+ map.zoomTo(i);
+ var w1 = parseInt(control.eTop.style.width);
+ var w2 = parseInt(control.eBottom.style.width);
+ widthIsOk = w1 <= control.maxWidth && w2 <= control.maxWidth;
+ }
+ t.ok(widthIsOk, "respects maxWidth at all zoom levels in m");
+ } else {
+ t.debug_print("Google tests can't be run from " +
+ window.location.host);
+ }
+
+ map.destroy();
+ }
+ function test_ie_oneunit(t) {
+ t.plan(2);
+ var control = new OpenLayers.Control.ScaleLine({bottomOutUnits:'',bottomInUnits:'',maxWidth:150});
+ t.ok(control instanceof OpenLayers.Control.ScaleLine, "new OpenLayers.Control returns object" );
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS('Test Layer', "bogus", {});
+ map.addLayer(layer);
+ map.zoomTo(0);
+ map.addControl(control);
+ t.ok(true, "invisible bottom scale doesn't cause scaleline failure (IE only)");
+ map.destroy();
+ }
+ </script>
+</head>
+<body>
+ <a id="ScaleLine" href="">ScaleLine</a> <br/>
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/SelectFeature.html b/misc/openlayers/tests/Control/SelectFeature.html
new file mode 100644
index 0000000..6e522e7
--- /dev/null
+++ b/misc/openlayers/tests/Control/SelectFeature.html
@@ -0,0 +1,632 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Control_SelectFeature_constructor(t) {
+ t.plan(5);
+ var options = {
+// geometryTypes: "foo"
+ };
+ var layer = "bar";
+ var control = new OpenLayers.Control.SelectFeature([layer], options);
+ t.ok(control instanceof OpenLayers.Control.SelectFeature,
+ "new OpenLayers.Control.SelectFeature returns an instance");
+ t.eq(control.layers[0], "bar",
+ "constructor with array of layers sets layer correctly");
+// t.eq(control.handlers.feature.geometryTypes, "foo",
+// "constructor sets options correctly on feature handler");
+ t.ok(control.layer instanceof OpenLayers.Layer.Vector.RootContainer, "control has a root container layer if constructor was called with an array of layers");
+
+ control = new OpenLayers.Control.SelectFeature(layer, options);
+ t.eq(control.layers, null, "this.layers is null if constructor called with a single layer");
+ t.eq(control.layer, layer, "this.layer holds the layer that was passed with the constructor if called with a single layer")
+ }
+
+ function test_Control_SelectFeature_destroy(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.SelectFeature(layer, {box: true});
+ control.handlers.feature.deactivate = function() {
+ t.ok(true,
+ "control.deactivate calls deactivate on feature handler");
+ }
+ control.handlers.box.deactivate = function() {
+ t.ok(true,
+ "control.deactivate calls deactivate on box handler");
+ }
+// should nullify the layer property here
+ control.destroy();
+
+ }
+
+ function test_Control_SelectFeature_select(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map("map");
+ var layer1 = new OpenLayers.Layer.Vector();
+ var layer2 = new OpenLayers.Layer.Vector();
+ map.addLayers([layer1, layer2]);
+ var control = new OpenLayers.Control.SelectFeature([layer1, layer2]);
+ var feature1 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,1));
+ var feature2 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,0));
+ layer1.addFeatures(feature1);
+ layer2.addFeatures(feature2);
+ var drawFeature = function(feature, style) {
+ feature.layer.styleMap.createSymbolizer(feature, style);
+ }
+ layer1.drawFeature = drawFeature;
+ layer2.drawFeature = drawFeature;
+ control.select(feature1);
+ t.eq(feature1.renderIntent, "select", "render intent is set to select");
+ control.select(feature2);
+ t.eq(feature2.renderIntent, "select", "render intent is set to select");
+ control.unselect(feature1);
+ t.eq(feature1.renderIntent, "default", "render intent is set back to default");
+ control.unselect(feature2);
+ t.eq(feature2.renderIntent, "default", "render intent is set back to default");
+ }
+
+ function test_Control_SelectFeature_clickFeature(t) {
+ t.plan(6);
+ // mock up layer
+ var layer = {
+ selectedFeatures: [],
+ drawFeature: function() {},
+ events: {
+ triggerEvent: function() {}
+ }
+ };
+ // mock up active control
+ var control = new OpenLayers.Control.SelectFeature(layer);
+ control.handlers.feature = {
+ evt: {}
+ };
+ // mock up features
+ var features = new Array(4);
+ for(var i=0; i<features.length; ++i) {
+ features[i] = {
+ id: Math.random(),
+ tested: 0,
+ style: "",
+ layer: layer
+ };
+ }
+
+ // test that onSelect gets called properly
+ control.onSelect = function(feature) {
+ feature.tested += 1;
+ t.eq(feature.id, features[feature.index].id,
+ "onSelect called with proper feature (" + feature.index + ")");
+ t.eq(feature.tested, feature.test,
+ "onSelect called only once for feature (" + feature.index + ")");
+ t.ok(this == control, "onSelect called in the scope of the control if control.scope is not provided");
+ }
+
+ // test that onUnselect gets called properly
+ control.onUnselect = function(feature) {
+ feature.tested += 1;
+ t.eq(feature.id, features[feature.index].id,
+ "onUnselect called with proper feature (" + feature.index + ")");
+ t.eq(feature.tested, feature.test,
+ "onUnselect called only once for feature (" + feature.index + ")");
+ t.ok(this == control, "onUnselect called in the scope of the control if control.scope is not provided");
+ }
+
+ // mock up first click on first feature (runs 3 tests from onSelect)
+ var feature = features[0];
+ feature.index = 0;
+ feature.test = 1;
+ control.clickFeature(feature);
+
+ // mock up second click on first feature (runs no tests - already selected)
+ control.toggle = false;
+ control.clickFeature(feature);
+
+ // mock up second click on first feature (runs 3 tests from onUnselect)
+ control.toggle = true;
+ feature.test = 2;
+ control.clickFeature(feature);
+
+
+ }
+
+ function test_box(t) {
+ t.plan(5);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ map.setBaseLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(1,1));
+ var control = new OpenLayers.Control.SelectFeature(layer, {'multiple': true, box: true });
+ var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,0));
+ var feature2 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,1));
+ var feature3 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(-2,-2));
+ var feature4 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(0, 0), new OpenLayers.Geometry.Point(1, 1)
+ ]));
+ layer.addFeatures([feature, feature2, feature3, feature4]);
+ control.setMap(map);
+ map.getLonLatFromPixel = function(arg) {
+ return new OpenLayers.LonLat(arg.x, arg.y);
+ }
+ control.selectBox(new OpenLayers.Bounds(-1, -1, 2, 2));
+ t.eq(layer.selectedFeatures.length, 3, "box around all features selects 3 features");
+
+ control.selectBox(new OpenLayers.Bounds(-3, -3, -1, -1));
+ t.eq(layer.selectedFeatures.length, 4, "box around other features doesn't turn off already selected features.");
+
+ control.multipleSelect = function() {
+ return false;
+ };
+ control.selectBox(new OpenLayers.Bounds(-3, -3, -1, -1));
+ t.eq(layer.selectedFeatures.length, 1, "box around other features correctly turns off already selected features.");
+
+ control.geometryTypes = null;
+ control.selectBox(new OpenLayers.Bounds(-100, -100, 100, 100));
+ t.eq(layer.selectedFeatures.length, layer.features.length, "all features selected with no geometryTypes filter");
+
+ control.geometryTypes = ["OpenLayers.Geometry.Point"];
+ control.selectBox(new OpenLayers.Bounds(-100, -100, 100, 100));
+ t.eq(layer.selectedFeatures.length, 3, "3 features selected with geometryTypes filter");
+
+
+ }
+
+
+ function test_selectBox_events(t){
+ t.plan(8);
+ var map = new OpenLayers.Map("map");
+ var layer1 = new OpenLayers.Layer.Vector();
+ map.addLayer(layer1);
+ map.setBaseLayer(layer1);
+ var layer2 = new OpenLayers.Layer.Vector();
+ map.addLayer(layer2);
+ map.setCenter(new OpenLayers.LonLat(1,1));
+ var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,1));
+ layer1.addFeatures([feature]);
+ var control = new OpenLayers.Control.SelectFeature(layer1);
+ control.setMap(map);
+ map.getLonLatFromPixel = function(arg) {
+ return new OpenLayers.LonLat(arg.x, arg.y);
+ }
+ control.activate();
+ var firesBoxselectionstart = false;
+ var beforeSelectingNumberOfFeatures = -1;
+ var firesBoxselectionend = false;
+ var afterSelectingNumberOfFeatures = -1;
+ control.events.register("boxselectionstart",null, function(e){
+ firesBoxselectionstart=true;
+ t.eq(e.layers.length,1,"right number of layers in event boxselectionstart");
+ t.eq(layer1.id, e.layers[0].id,"correct layer in event boxselectionstart");
+ beforeSelectingNumberOfFeatures = e.layers[0].selectedFeatures.length;
+ });
+ control.events.register("boxselectionend",null, function(e){
+ firesBoxselectionend=true;
+ t.eq(e.layers.length,1,"right number of layers in event boxselectionend");
+ t.eq(layer1.id, e.layers[0].id,"correct layer in event boxselectionend");
+ afterSelectingNumberOfFeatures = e.layers[0].selectedFeatures.length;
+ });
+ var bounds = new OpenLayers.Bounds(-1, -1, 2, 2);
+ control.selectBox(bounds);
+ t.ok(firesBoxselectionstart,"selectBox fires boxselectionstart event");
+ t.eq(beforeSelectingNumberOfFeatures,0,"boxselectionstart fires before selection of feature");
+ t.ok(firesBoxselectionend,"selectBox fires boxselectionend event");
+ t.eq(afterSelectingNumberOfFeatures,1,"boxselectionend fires after feature selected");
+ }
+ function test_Control_SelectFeature_activate(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.SelectFeature(layer, {box: true});
+ map.addControl(control);
+ t.ok(!control.handlers.feature.active,
+ "feature handler is not active prior to activating control");
+ t.ok(!control.handlers.box.active,
+ "box handler is not active prior to activating control");
+ control.activate();
+ t.ok(control.handlers.feature.active,
+ "feature handler is active after activating control");
+ t.ok(control.handlers.box.active,
+ "box handler is active after activating control");
+ }
+
+ function test_Control_SelectFeature_deactivate(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.SelectFeature(layer, {box: true});
+ map.addControl(control);
+
+ control.activate();
+ control.handlers.feature.deactivate = function() {
+ t.ok(true,
+ "control.deactivate calls deactivate on feature handler");
+ OpenLayers.Handler.Feature.prototype.deactivate.apply(this, arguments);
+ }
+ control.handlers.box.deactivate = function() {
+ t.ok(true,
+ "control.deactivate calls deactivate on box handler");
+ }
+ control.deactivate();
+ }
+
+ function test_highlighyOnly(t) {
+ t.plan(23);
+
+ /*
+ * setup
+ */
+
+ var map, layer, ctrl1, ctrl2, _feature, feature, evt, _style;
+
+ map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer.Vector("name", {isBaseLayer: true});
+ map.addLayer(layer);
+
+ ctrl1 = new OpenLayers.Control.SelectFeature(layer, {
+ highlightOnly: false,
+ hover: false
+ });
+ map.addControl(ctrl1);
+
+ ctrl2 = new OpenLayers.Control.SelectFeature(layer, {
+ highlightOnly: true,
+ hover: true
+ });
+ map.addControl(ctrl2);
+
+ ctrl2.activate();
+ ctrl1.activate();
+
+ feature = new OpenLayers.Feature.Vector();
+ feature.layer = layer;
+
+ // override the layer's getFeatureFromEvent func so that it always
+ // returns the feature referenced to by _feature
+ layer.getFeatureFromEvent = function(evt) { return _feature; };
+
+ evt = {xy: new OpenLayers.Pixel(Math.random(), Math.random())};
+
+ map.zoomToMaxExtent();
+
+ /*
+ * tests
+ */
+
+ // with renderIntent
+
+ ctrl1.renderIntent = "select";
+ ctrl2.renderIntent = "temporary";
+
+ // mouse over feature, feature is drawn with "temporary"
+ _feature = feature;
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+ t.eq(feature.renderIntent, "temporary",
+ "feature drawn with expected render intent after \"mouseover\"");
+ t.eq(feature._lastHighlighter, ctrl2.id,
+ "feature._lastHighlighter properly set after \"mouseover\"");
+ t.eq(feature._prevHighlighter, undefined,
+ "feature._prevHighlighter properly set after \"mouseover\"");
+
+ // click in feature, feature is drawn with "select"
+ _feature = feature;
+ evt.type = "click";
+ map.events.triggerEvent("click", evt);
+ t.eq(feature.renderIntent, "select",
+ "feature drawn with expected render intent after \"clickin\"");
+ t.eq(feature._lastHighlighter, ctrl1.id,
+ "feature._lastHighlighter properly set after \"clickin\"");
+ t.eq(feature._prevHighlighter, ctrl2.id,
+ "feature._prevHighlighter properly set after \"clickin\"");
+
+ // mouse out of feature, feature is still drawn with "select"
+ _feature = null;
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+ t.eq(feature.renderIntent, "select",
+ "feature drawn with expected render intent after \"mouseout\"");
+ t.eq(feature._lastHighlighter, ctrl1.id,
+ "feature._lastHighlighter properly set after \"nouseout\"");
+ t.ok(feature._prevHighlighter, ctrl2.id,
+ "feature._prevHighlighter properly set after \"mouseout\"");
+
+ // mouse over feature again, feature is drawn with "temporary"
+ _feature = feature;
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+ t.eq(feature.renderIntent, "temporary",
+ "feature drawn with expected render intent after \"mouseover\"");
+ t.eq(feature._lastHighlighter, ctrl2.id,
+ "feature._lastHighlighter properly set after \"mouseover\"");
+ t.eq(feature._prevHighlighter, ctrl1.id,
+ "feature._prevHighlighter properly set after \"mouseover\"");
+
+ // mouve out of feature again, feature is still drawn with "select"
+ _feature = null;
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+ t.eq(feature.renderIntent, "select",
+ "feature drawn with expected render intent after \"mouseout\"");
+ t.eq(feature._lastHighlighter, ctrl1.id,
+ "feature._lastHighlighter properly set after \"mouseout\"");
+ t.eq(feature._prevHighlighter, undefined,
+ "feature._prevHighlighter properly set after \"mouseout\"");
+
+ // click out of feature, feature is drawn with "default"
+ _feature = null;
+ evt.type = "click";
+ map.events.triggerEvent("click", evt);
+ t.eq(feature.renderIntent, "default",
+ "feature drawn with expected render intent after \"clickout\"");
+ t.eq(feature._lastHighlighter, undefined,
+ "feature._lastHighlighter properly set after \"clickout\"");
+ t.eq(feature._prevHighlighter, undefined,
+ "feature._prevHighlighter properly set after \"clickout\"");
+
+ // with selectStyle
+
+ ctrl1.selectStyle = OpenLayers.Feature.Vector.style["select"];
+ ctrl2.selectStyle = OpenLayers.Feature.Vector.style["temporary"];
+
+ layer.renderer.drawFeature = function(f, s) {
+ var style = OpenLayers.Feature.Vector.style[_style];
+ t.eq(s, style, "renderer drawFeature called with expected style obj (" + _style + ")");
+ };
+
+ // mouse over feature, feature is drawn with "temporary"
+ _feature = feature;
+ _style = "temporary";
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+
+ // click in feature, feature is drawn with "select"
+ _feature = feature;
+ _style = "select";
+ evt.type = "click";
+ map.events.triggerEvent("click", evt);
+
+ // mouse out of feature, feature is still drawn with "select" and
+ // the renderer drawFeature method should not be called
+ _feature = null;
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+
+ // mouse over feature again, feature is drawn with "temporary"
+ _feature = feature;
+ _style = "temporary";
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+
+ // mouve out of feature again, feature is still drawn with "select"
+ _feature = null;
+ _style = "select";
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+
+ // click out of feature, feature is drawn with "default"
+ _feature = null;
+ _style = "default";
+ evt.type = "click";
+ map.events.triggerEvent("click", evt);
+ }
+
+ // test for http://trac.openlayers.org/ticket/2812
+ function test_highlightOnly_toggle(t) {
+ t.plan(8);
+
+ /*
+ * setup
+ */
+
+ var map, layer, ctrl1, ctrl2, _feature, feature, evt, _style;
+
+ map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer.Vector("name", {isBaseLayer: true});
+ map.addLayer(layer);
+
+ ctrl1 = new OpenLayers.Control.SelectFeature(layer, {
+ highlightOnly: false,
+ hover: false,
+ clickout: false,
+ toggle: true
+ });
+ map.addControl(ctrl1);
+
+ ctrl2 = new OpenLayers.Control.SelectFeature(layer, {
+ highlightOnly: true,
+ hover: true
+ });
+ map.addControl(ctrl2);
+
+ ctrl2.activate();
+ ctrl1.activate();
+
+ feature = new OpenLayers.Feature.Vector();
+ feature.layer = layer;
+
+ // override the layer's getFeatureFromEvent func so that it always
+ // returns the feature referenced to by _feature
+ layer.getFeatureFromEvent = function(evt) { return _feature; };
+
+ evt = {xy: new OpenLayers.Pixel(Math.random(), Math.random())};
+
+ map.zoomToMaxExtent();
+
+ /*
+ * tests
+ */
+
+ // with renderIntent
+
+ ctrl1.renderIntent = "select";
+ ctrl2.renderIntent = "temporary";
+
+ // mouse over feature, feature is drawn with "temporary"
+ _feature = feature;
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+ t.eq(feature.renderIntent, "temporary",
+ "feature drawn with expected render intent after \"mouseover\"");
+
+ // click in feature, feature is drawn with "select"
+ _feature = feature;
+ evt.type = "click";
+ map.events.triggerEvent("click", evt);
+ t.eq(feature.renderIntent, "select",
+ "feature drawn with expected render intent after \"clickin\"");
+
+ // mouse out of feature, feature is still drawn with "select"
+ _feature = null;
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+ t.eq(feature.renderIntent, "select",
+ "feature drawn with expected render intent after \"mouseout\"");
+
+ // mouse over feature again, feature is drawn with "temporary"
+ _feature = feature;
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+ t.eq(feature.renderIntent, "temporary",
+ "feature drawn with expected render intent after \"mouseover\"");
+
+ // click in feature again, feature is drawn with "default"
+ _feature = feature;
+ evt.type = "click";
+ map.events.triggerEvent("click", evt);
+ t.eq(feature.renderIntent, "default",
+ "feature drawn with expected render intent after \"clickin\"");
+
+ // mouse out of feature again, feature is still drawn with "default"
+ _feature = null;
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+ t.eq(feature.renderIntent, "default",
+ "feature drawn with expected render intent after \"mouseout\"");
+
+ // mouse over feature again, feature is drawn with "temporary"
+ _feature = feature;
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+ t.eq(feature.renderIntent, "temporary",
+ "feature drawn with expected render intent after \"mouseover\"");
+
+ // mouse out of feature again, feature is still drawn with "default"
+ _feature = null;
+ evt.type = "mousemove";
+ map.events.triggerEvent("mousemove", evt);
+ t.eq(feature.renderIntent, "default",
+ "feature drawn with expected render intent after \"mouseout\"");
+ }
+
+ function test_setLayer(t) {
+ t.plan(5);
+ var map = new OpenLayers.Map("map");
+ var layer1 = new OpenLayers.Layer.Vector();
+ var layer2 = new OpenLayers.Layer.Vector();
+ var layer3 = new OpenLayers.Layer.Vector();
+ map.addLayer(layer1, layer2, layer3);
+ // initialize it with a single layer
+ var control = new OpenLayers.Control.SelectFeature(layer1);
+ map.addControl(control);
+ control.activate();
+ control.setLayer([layer2, layer3]);
+ t.eq(control.layer instanceof OpenLayers.Layer.Vector.RootContainer, true, "Root container created correctly on setLayer with multiple layers");
+ t.eq(control.active, true, "Control should be active still after using setLayer");
+ t.eq((control.handlers.feature.layer === control.layer), true, "Feature handler layer set correctly");
+ control.destroy();
+ // initialize with an array of layers
+ control = new OpenLayers.Control.SelectFeature([layer1, layer2]);
+ t.eq((control.layers !== null), true, "Control has a layers property");
+ control.setLayer(layer3);
+ t.eq((control.layers === null), true, "When using setLayer with a single layer, the layers property is removed if present before");
+ map.destroy();
+ }
+
+ function test_setLayerWithRemoving(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var layer1 = new OpenLayers.Layer.Vector();
+ var layer2 = new OpenLayers.Layer.Vector();
+ map.addLayer(layer1, layer2);
+ // initialize it with a single layer
+ var control = new OpenLayers.Control.SelectFeature(layer1);
+ map.addControl(control);
+ control.activate();
+ var noError = null;
+ map.events.register("preremovelayer", this, function(e) {
+ try {
+ control.setLayer(layer2);
+ } catch (e) {
+ noError = e;
+ }
+ });
+ layer1.destroy();
+ t.eq(layer2.id, control.layer.id, "Layer is set correctly without error");
+ t.eq(noError, null,"No error occured during setLayer. Error is: '"+noError+"'");
+ control.destroy();
+ map.destroy();
+ }
+
+ function test_destroy(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map("map");
+ var layer1 = new OpenLayers.Layer.Vector();
+ map.addLayer(layer1);
+ var control = new OpenLayers.Control.SelectFeature([layer1]);
+ map.addControl(control);
+ control.activate();
+ control.destroy();
+ t.eq(layer1.renderer.getRenderLayerId(), layer1.id,
+ "Root container moved correctly when control is destroyed and layers was an array parameter");
+ }
+
+ function test_unselectAll(t) {
+ t.plan(2);
+
+ var layer = new OpenLayers.Layer.Vector();
+
+ var control = new OpenLayers.Control.SelectFeature(layer);
+
+ var feature1 = new OpenLayers.Feature.Vector();
+ feature1.id = 1;
+ var feature2 = new OpenLayers.Feature.Vector();
+ feature2.id = 2;
+ var feature3 = new OpenLayers.Feature.Vector();
+ feature3.id = 3;
+ var feature4 = new OpenLayers.Feature.Vector();
+ feature4.id = 4;
+
+ layer.addFeatures([feature1, feature2, feature3, feature4]);
+
+ control.select(feature1);
+ control.select(feature2);
+ control.select(feature3);
+ control.select(feature4);
+
+ layer.events.on({
+ featureunselected: function(e) {
+ // we change the selectedFeatures array while
+ // unselectAll is iterating over that array.
+ if(feature2.layer) {
+ layer.removeFeatures([feature2]);
+ }
+ }
+ });
+
+ control.unselectAll({except: feature3});
+ t.eq(layer.selectedFeatures.length, 1,
+ 'unselectAll unselected all but one');
+ t.eq(layer.selectedFeatures[0].id, 3,
+ 'the remaining selected features is the one expected');
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 250px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/Snapping.html b/misc/openlayers/tests/Control/Snapping.html
new file mode 100644
index 0000000..f065f66
--- /dev/null
+++ b/misc/openlayers/tests/Control/Snapping.html
@@ -0,0 +1,448 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+
+ t.plan(5);
+
+ // construct with a single layer
+ var layer = new OpenLayers.Layer.Vector();
+ var control = new OpenLayers.Control.Snapping({
+ layer: layer
+ });
+
+ t.ok(control.layer === layer, "[a] source layer properly set");
+ t.eq(control.targets.length, 1, "[a] one target layer");
+ t.ok(control.targets[0].layer === layer, "[a] target set");
+ control.destroy();
+
+ // construct with a different target, default target config
+ var layer2 = new OpenLayers.Layer.Vector();
+ control = new OpenLayers.Control.Snapping({
+ layer: layer,
+ targets: [layer2]
+ });
+
+ t.eq(control.targets.length, 1, "[b] one target layer");
+ t.ok(control.targets[0].layer == layer2, "[b] target set");
+ control.destroy();
+ }
+
+ function test_setLayer(t) {
+
+ t.plan(4);
+
+ var layer = new OpenLayers.Layer.Vector();
+ var control = new OpenLayers.Control.Snapping();
+ control.setLayer(layer);
+
+ t.ok(control.layer === layer, "layer properly set");
+
+ // confirm that the control is deactivated and reactivated when setting new layer
+ var log = {
+ activated: 0,
+ deactivated: 0
+ };
+ control.activate = function() {
+ log.activated++;
+ }
+ control.deactivate = function() {
+ log.deactivated++;
+ }
+ control.active = true;
+
+ var layer2 = new OpenLayers.Layer.Vector();
+ control.setLayer(layer2);
+
+ t.eq(log.deactivated, 1, "control deactivated");
+ t.ok(control.layer === layer2, "layer properly reset");
+ t.eq(log.activated, 1, "control reactivated");
+
+ control.destroy();
+ }
+
+ function test_setTargets(t) {
+
+ t.plan(4);
+
+ var layer1 = new OpenLayers.Layer.Vector();
+ var layer2 = new OpenLayers.Layer.Vector();
+ var control = new OpenLayers.Control.Snapping();
+
+ var log = {
+ addTarget: [],
+ addTargetLayer: []
+ };
+ control.addTarget = function(target) {
+ log.addTarget.push(target);
+ };
+ control.addTargetLayer = function(target) {
+ log.addTargetLayer.push(target);
+ };
+
+ control.setTargets([layer1, {layer: layer2}]);
+
+ t.eq(log.addTargetLayer.length, 1, "setTargetLayer called once");
+ t.ok(log.addTargetLayer[0] === layer1, "setTargetLayer called with layer1");
+ t.eq(log.addTarget.length, 1, "setTarget called once");
+ t.ok(log.addTarget[0].layer === layer2, "setTarget called with layer2");
+
+ control.destroy();
+ }
+
+ function test_addTarget(t) {
+ t.plan(5);
+
+ var layer = new OpenLayers.Layer.Vector();
+
+ var control = new OpenLayers.Control.Snapping({
+ defaults: {
+ nodeTolerance: 30,
+ tolerance: 40
+ }
+ });
+
+ var log = {};
+ control.addTarget({layer: layer});
+
+ t.eq(control.targets.length, 1, "single target");
+ var target = control.targets[0];
+ t.ok(target.layer === layer, "correct target layer");
+ t.eq(target.nodeTolerance, 30, "correct nodeTolerance");
+ t.eq(target.edgeTolerance, 40, "correct edgeTolerance");
+ t.eq(target.vertexTolerance, 40, "correct vertexTolerance");
+
+ control.destroy();
+ }
+
+ function test_removeTargetLayer(t) {
+
+ t.plan(3);
+
+ var layer1 = new OpenLayers.Layer.Vector();
+ var layer2 = new OpenLayers.Layer.Vector();
+ var layer3 = new OpenLayers.Layer.Vector();
+ var control = new OpenLayers.Control.Snapping({
+ targets: [layer1, layer2, layer3]
+ });
+
+ control.removeTargetLayer(layer2);
+
+ t.eq(control.targets.length, 2, "correct targets length");
+ t.ok(control.targets[0].layer === layer1, "layer1 remains");
+ t.ok(control.targets[1].layer === layer3, "layer3 remains");
+
+ control.destroy();
+
+ }
+
+ function test_activate(t) {
+
+ t.plan(4);
+ var layer = new OpenLayers.Layer.Vector();
+ var control = new OpenLayers.Control.Snapping({
+ layer: layer
+ });
+
+ control.activate();
+
+ t.eq(layer.events.listeners.sketchmodified.length, 1, "one sketchmodified listener");
+ t.ok(layer.events.listeners.sketchmodified[0].func === control.onSketchModified, "correct sketchmodified listener");
+ t.eq(layer.events.listeners.vertexmodified.length, 1, "one vertexmodified listener");
+ t.ok(layer.events.listeners.vertexmodified[0].func === control.onVertexModified, "correct vertexmodified listener");
+
+ control.destroy();
+ }
+
+ function test_deactivate(t) {
+
+ t.plan(2);
+ var layer = new OpenLayers.Layer.Vector();
+ var control = new OpenLayers.Control.Snapping({
+ layer: layer
+ });
+
+ control.activate();
+ control.deactivate();
+
+ t.eq(layer.events.listeners.sketchmodified.length, 0, "no sketchmodified listeners");
+ t.eq(layer.events.listeners.vertexmodified.length, 0, "no vertexmodified listeners");
+
+ control.destroy();
+ }
+
+ function test_resolution_limits(t) {
+ t.plan(7);
+
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1],
+ maxExtent: new OpenLayers.Bounds(0, 0, 100, 100)
+ });
+
+ var layer = new OpenLayers.Layer.Vector(null, {
+ isBaseLayer: true
+ });
+ layer.addFeatures([
+ new OpenLayers.Feature.Vector(OpenLayers.Geometry.fromWKT(
+ "POINT(50 50)"
+ ))
+ ]);
+
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ var control = new OpenLayers.Control.Snapping({layer: layer});
+
+ var result;
+ var loc = new OpenLayers.Geometry.Point(49, 49);
+
+ // 1) test a target with no constraints
+ control.setTargets([{layer: layer}]);
+ result = control.testTarget(control.targets[0], loc);
+ t.ok(result !== null, "1) target is eligible");
+
+ // 2) test a target with minResolution < map.resolution
+ control.setTargets([{layer: layer, minResolution: 0.5}]);
+ result = control.testTarget(control.targets[0], loc);
+ t.ok(result !== null, "2) target is eligible");
+
+ // 3) test a target with minResolution === map.resolution
+ control.setTargets([{layer: layer, minResolution: 1}]);
+ result = control.testTarget(control.targets[0], loc);
+ t.ok(result !== null, "3) target is eligible");
+
+ // 4) test a target with minResolution > map.resolution
+ control.setTargets([{layer: layer, minResolution: 1.5}]);
+ result = control.testTarget(control.targets[0], loc);
+ t.ok(result === null, "4) target is not eligible");
+
+ // 5) test a target with maxResolution < map.resolution
+ control.setTargets([{layer: layer, maxResolution: 0.5}]);
+ result = control.testTarget(control.targets[0], loc);
+ t.ok(result === null, "5) target is not eligible");
+
+ // 6) test a target with maxResolution === map.resolution
+ control.setTargets([{layer: layer, maxResolution: 1}]);
+ result = control.testTarget(control.targets[0], loc);
+ t.ok(result === null, "6) target is not eligible");
+
+ // 7) test a target with maxResolution > map.resolution
+ control.setTargets([{layer: layer, maxResolution: 1.5}]);
+ result = control.testTarget(control.targets[0], loc);
+ t.ok(result !== null, "7) target is eligible");
+
+ map.destroy();
+
+ }
+
+ function test_filter(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1],
+ maxExtent: new OpenLayers.Bounds(0, 0, 100, 100)
+ });
+
+ var layer1 = new OpenLayers.Layer.Vector(null, {
+ isBaseLayer: true
+ });
+ var f1 = new OpenLayers.Feature.Vector(OpenLayers.Geometry.fromWKT(
+ "LINESTRING(0 0, 10 10, 20 20, 30 30)"
+ ), {foo: 'bar'});
+ f1.fid = "FID1";
+ var f2 = new OpenLayers.Feature.Vector(OpenLayers.Geometry.fromWKT(
+ "LINESTRING(11 10, 20 10, 30 10)"
+ ), {foo: 'bar'});
+ f2.fid = "FID2";
+ layer1.addFeatures([f1, f2]);
+ map.addLayers([layer1]);
+ map.zoomToMaxExtent();
+
+ var control = new OpenLayers.Control.Snapping({
+ layer: layer1,
+ targets: [layer1],
+ defaults: {tolerance: 4}
+ });
+ control.activate();
+
+ var result;
+ var loc = new OpenLayers.Geometry.Point(1, 1);
+
+ control.setTargets([{layer: layer1}]);
+ result = control.testTarget(control.targets[0], loc);
+ t.ok(result !== null, "target is eligible without a filter set");
+ var filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.NOT,
+ filters: [
+ new OpenLayers.Filter.FeatureId({fids: ["FID1", "FID2"]})
+ ]
+ });
+ control.setTargets([{layer: layer1, filter: filter}]);
+ result = control.testTarget(control.targets[0], loc);
+ t.ok(result === null, "target is not eligible with a filter set which excludes the target's features");
+ filter = new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO, value: 'bar', property: 'foo'});
+ control.setTargets([{layer: layer1, filter: filter}]);
+ result = control.testTarget(control.targets[0], loc);
+ t.ok(result === null, "target is not eligible with a filter set which excludes the target's features using a comparison filter");
+ }
+
+ function test_snapping(t) {
+
+ t.plan(46);
+
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1],
+ maxExtent: new OpenLayers.Bounds(0, 0, 100, 100)
+ });
+
+ var layer1 = new OpenLayers.Layer.Vector(null, {
+ isBaseLayer: true
+ });
+ layer1.addFeatures([
+ new OpenLayers.Feature.Vector(OpenLayers.Geometry.fromWKT(
+ "LINESTRING(0 0, 10 10, 20 20, 30 30)"
+ )),
+ new OpenLayers.Feature.Vector(OpenLayers.Geometry.fromWKT(
+ "LINESTRING(11 10, 20 10, 30 10)"
+ ))
+ ]);
+
+ var layer2 = new OpenLayers.Layer.Vector();
+ layer2.addFeatures([
+ new OpenLayers.Feature.Vector(OpenLayers.Geometry.fromWKT(
+ "LINESTRING(10 10, 20 20, 30 30)"
+ )),
+ new OpenLayers.Feature.Vector(OpenLayers.Geometry.fromWKT(
+ "LINESTRING(21 10, 20 20, 20 30)"
+ ))
+ ]);
+
+ map.addLayers([layer1, layer2]);
+ map.zoomToMaxExtent();
+
+ var control = new OpenLayers.Control.Snapping({
+ layer: layer1,
+ targets: [layer1, layer2],
+ defaults: {tolerance: 4}
+ });
+ control.activate();
+ map.addControl(control);
+
+ // log beforesnap, snap, and unsnap events
+ var events = [];
+ function listener(event) {
+ events.push(event);
+ }
+ control.events.on({
+ beforesnap: listener,
+ snap: listener,
+ unsnap: listener
+ });
+
+ // create a vertex and a convenience method for mocking the drag
+ var vertex = new OpenLayers.Geometry.Point(-100, -100);
+ function drag(x, y) {
+ var px = map.getPixelFromLonLat(new OpenLayers.LonLat(x, y));
+ layer1.events.triggerEvent("vertexmodified", {
+ vertex: vertex, pixel: px
+ });
+ }
+
+ // mock up drag far from features
+ drag(-100, -100);
+ t.eq(events.length, 0, "no snapping");
+
+ // mock up drag near first node of first feature
+ drag(0, 1);
+ t.eq(events.length, 2, "[a] 2 events triggered");
+ t.eq(events[0].type, "beforesnap", "[a] beforesnap triggered");
+ t.eq(events[0].snapType, "node", "[a] beforesnap triggered for node");
+ t.ok(events[0].point === vertex, "[a] beforesnap triggered with vertex");
+ t.eq(events[0].x, 0, "[a] beforesnap triggered correct x");
+ t.eq(events[0].y, 0, "[a] beforesnap triggered with correct y");
+ t.eq(events[1].type, "snap", "[a] snap triggered");
+ t.eq(events[1].snapType, "node", "[a] snap triggered for node");
+ t.ok(events[1].point === vertex, "[a] snap triggered with point");
+ t.eq(events[1].distance, 1, "[a] snap triggered correct distance");
+ t.ok(events[1].layer === layer1, "[a] snap triggered with correct target layer");
+ t.eq(vertex.x, 0, "[a] vertex x modified");
+ t.eq(vertex.y, 0, "[a] vertex y modified");
+ events = [];
+
+ // mock up drag that unsnaps
+ drag(-100, -50);
+ t.eq(events.length, 1, "[b] 1 event triggered");
+ t.eq(events[0].type, "unsnap", "[b] unsnap triggered");
+ t.ok(events[0].point === vertex, "[b] unsnap triggered with vertex");
+ t.eq(vertex.x, -100, "[b] vertex x unsnapped");
+ t.eq(vertex.y, -50, "[b] vertex y unsnapped");
+ events = [];
+
+ // drag near node of second feature in first layer to demonstrate precedence of node snapping
+ drag(9, 10);
+ t.eq(events.length, 2, "[c] 2 events triggered");
+ t.eq(events[0].type, "beforesnap", "[c] beforesnap triggered first");
+ t.eq(events[1].type, "snap", "[c] snap triggered second");
+ t.eq(events[1].snapType, "node", "[c] snap to node");
+ // unsnap & reset
+ drag(-100, -50);
+ events = [];
+
+ // drag near node of second feature in second layer to demonstrate greedy property
+ // with greedy true (default) the best target from the first layer with eligible targets is used
+ drag(22, 10);
+ t.eq(events.length, 2, "[d] 2 events triggered");
+ t.eq(events[1].type, "snap", "[d] snap triggered second");
+ t.eq(events[1].snapType, "vertex", "[d] snap to vertex");
+ t.ok(events[1].layer === layer1, "[d] snap to vertex in first layer");
+ t.eq(vertex.x, 20, "[d] vertex x modified");
+ t.eq(vertex.y, 10, "[d] vertex y modified");
+ // unsnap & reset
+ drag(-100, -50);
+ events = [];
+
+ // do the same drag but with greedy false - this will look for best target in all layers
+ control.greedy = false;
+ drag(22, 10);
+ t.eq(events.length, 2, "[d] 2 events triggered");
+ t.eq(events[1].type, "snap", "[d] snap triggered second");
+ t.eq(events[1].snapType, "node", "[d] snap to node");
+ t.ok(events[1].layer === layer2, "[d] snap to node in second layer");
+ // unsnap & reset
+ drag(-100, -50);
+ control.greedy = true;
+ events = [];
+
+ // demonstrate snapping on sketchstarted
+ var p = new OpenLayers.Geometry.Point(0, 1);
+ layer1.events.triggerEvent("sketchstarted", {
+ vertex: p,
+ feature: new OpenLayers.Feature.Vector(p)
+ });
+ t.eq(events.length, 2, "[sketchstarted] 2 events triggered");
+ t.eq(events[0].type, "beforesnap", "[sketchstarted] beforesnap triggered");
+ t.eq(events[0].snapType, "node", "[sketchstarted] beforesnap triggered for node");
+ t.ok(events[0].point === p, "[sketchstarted] beforesnap triggered with vertex");
+ t.eq(events[0].x, 0, "[sketchstarted] beforesnap triggered correct x");
+ t.eq(events[0].y, 0, "[sketchstarted] beforesnap triggered with correct y");
+ t.eq(events[1].type, "snap", "[sketchstarted] snap triggered");
+ t.eq(events[1].snapType, "node", "[sketchstarted] snap triggered for node");
+ t.ok(events[1].point === p, "[sketchstarted] snap triggered with point");
+ t.eq(events[1].distance, 1, "[sketchstarted] snap triggered correct distance");
+ t.ok(events[1].layer === layer1, "[sketchstarted] snap triggered with correct target layer");
+ t.eq(p.x, 0, "[sketchstarted] vertex x modified");
+ t.eq(p.y, 0, "[sketchstarted] vertex y modified");
+ // reset
+ events = [];
+
+ map.destroy();
+
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 100px; height: 100px;"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/Split.html b/misc/openlayers/tests/Control/Split.html
new file mode 100644
index 0000000..e3a6eac
--- /dev/null
+++ b/misc/openlayers/tests/Control/Split.html
@@ -0,0 +1,319 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+
+ t.plan(4);
+
+ var layer = new OpenLayers.Layer.Vector();
+ var control;
+
+ // construct with nothing
+ control = new OpenLayers.Control.Split();
+ t.ok(control instanceof OpenLayers.Control, "instanceof OpenLayers.Control");
+ t.ok(control instanceof OpenLayers.Control, "instanceof OpenLayers.Control.Split")
+ control.destroy();
+
+ // construct with a single target layer
+ control = new OpenLayers.Control.Split({
+ layer: layer
+ });
+ t.ok(control.layer === layer, "target layer properly set");
+ control.destroy();
+
+ // construct with same target and source
+ control = new OpenLayers.Control.Split({
+ layer: layer,
+ source: layer
+ });
+ t.ok(control.source === layer, "source layer properly set");
+ control.destroy();
+ }
+
+ function test_setSource(t) {
+ t.plan(5);
+
+ var layer1 = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ var layer2 = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+
+ var control = new OpenLayers.Control.Split({layer: layer1});
+
+ var map = new OpenLayers.Map("map");
+ map.addLayers([layer1, layer2]);
+ map.zoomToMaxExtent();
+ map.addControl(control);
+ control.activate();
+
+ // confirm sketch hander created
+ t.ok(control.handler, "sketch handler created");
+ t.eq(control.handler.active, true, "sketch handler active");
+
+ control.setSource(layer1);
+ t.ok(control.source === layer1, "layer1 properly set");
+ t.ok(!control.handler, "no more sketch handler");
+
+ // activate and switch to new source layer
+ control.setSource(layer2);
+ t.ok(control.source === layer2, "layer2 properly set");
+
+ map.destroy();
+
+ }
+
+ function test_activate(t) {
+ t.plan(8);
+
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ var control = new OpenLayers.Control.Split({layer: layer});
+ var map = new OpenLayers.Map("map");
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ map.addControl(control);
+
+ // test activation with no source layer
+ control.activate();
+ t.eq(control.active, true, "control is active");
+ t.ok(control.handler instanceof OpenLayers.Handler.Path, "line sketch handler created");
+ t.ok(control.handler.callbacks.done, "done callback set on sketch handler");
+ t.eq(control.handler.active, true, "sketch handler is active");
+
+ // change the source layer - this should call activate again
+ control.setSource(layer);
+
+ t.eq(control.active, true, "control is still active");
+ t.ok(control.source === layer, "source layer set");
+ t.ok(layer.events.listeners.sketchcomplete, "sketchcomplete listener registered");
+ t.ok(layer.events.listeners.afterfeaturemodified, "afterfeaturemodified listener registered");
+
+ map.destroy();
+
+ }
+
+ function test_deactivate(t) {
+
+ t.plan(7);
+
+ var layer1 = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ var layer2 = new OpenLayers.Layer.Vector("bar", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: false
+ });
+ var control = new OpenLayers.Control.Split({layer: layer1});
+ var map = new OpenLayers.Map("map");
+ map.addLayer(layer1);
+ map.addLayer(layer2);
+ map.zoomToMaxExtent();
+ map.addControl(control);
+
+ // activate and check sketch handler
+ control.activate();
+ t.ok(control.handler, "sketch handler present");
+ t.eq(control.handler.active, true, "sketch handler active");
+
+ // deactivate and check sketch handler
+ control.deactivate();
+ t.eq(control.handler.active, false, "sketch handler deactivated");
+
+ // set a source layer
+ control.setSource(layer2);
+
+ // activate and check that listeners are registered
+ control.activate();
+ t.ok(layer2.events.listeners.sketchcomplete, "sketchcomplete listener registered");
+ t.ok(layer2.events.listeners.afterfeaturemodified, "afterfeaturemodified listener registered");
+
+ // deactivate and confirm no draw related events
+ control.deactivate();
+ t.eq(layer2.events.listeners.sketchcomplete.length, 0, "no sketchcomplete listeners");
+ t.eq(layer2.events.listeners.afterfeaturemodified.length, 0, "no afterfeaturemodified listeners");
+
+ map.destroy();
+ }
+
+ function test_isEligible(t) {
+
+ t.plan(10);
+
+ var control = new OpenLayers.Control.Split();
+ var geometry = OpenLayers.Geometry.fromWKT("LINESTRING(0 1, 1 2)");
+ var feature = new OpenLayers.Feature.Vector(
+ geometry,
+ {foo: "bar"}
+ );
+
+ t.eq(control.isEligible(feature), true, "plain old feature is eligible");
+
+ feature.state = OpenLayers.State.DELETE;
+ t.eq(control.isEligible(feature), false, "feature slated for deletion is not eligible");
+ delete feature.state;
+ t.eq(control.isEligible(feature), true, "feature with no state is eligible");
+
+ feature.geometry = new OpenLayers.Geometry.Point(1, 1);
+ t.eq(control.isEligible(feature), false, "feature with point geometry is not eligible");
+ feature.geometry = new OpenLayers.Geometry.MultiLineString([geometry]);
+ t.eq(control.isEligible(feature), true, "feature with multilinestring geometry is eligible");
+
+ control.feature = feature;
+ t.eq(control.isEligible(feature), false, "source feature is not eligible as target");
+ control.feature = new OpenLayers.Feature.Vector();
+ t.eq(control.isEligible(feature), true, "feature is eligible if different than source feature");
+
+ control.targetFilter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+ property: "foo",
+ value: "bar"
+ });
+ t.eq(control.isEligible(feature), false, "feature is not eligible unless it matches filter");
+ control.targetFilter.value = "baz";
+ t.eq(control.isEligible(feature), true, "feature is eligible if it matches filter");
+
+ delete feature.geometry;
+ t.eq(control.isEligible(feature), false, "feature with no geometry is not eligible");
+
+ control.destroy();
+
+ }
+
+ function test_considerSplit(t) {
+
+ var layer = new OpenLayers.Layer.Vector();
+
+ var wkt = OpenLayers.Geometry.fromWKT;
+ var geoms = {
+ abc: wkt("LINESTRING(0 0, 2 2)"),
+ ab: wkt("LINESTRING(0 0, 1 1)"),
+ bc: wkt("LINESTRING(1 1, 2 2)"),
+ dbe: wkt("LINESTRING(2 0, 0 2)"),
+ db: wkt("LINESTRING(2 0, 1 1)"),
+ be: wkt("LINESTRING(1 1, 0 2)")
+ };
+
+ var Feature = OpenLayers.Feature.Vector;
+ var feats = {
+ abc: new Feature(geoms.abc),
+ ab: new Feature(geoms.ab),
+ bc: new Feature(geoms.bc),
+ dbe: new Feature(geoms.dbe),
+ db: new Feature(geoms.db),
+ be: new Feature(geoms.be)
+ };
+
+ function feature(id, options) {
+ var f = OpenLayers.Util.extend(feats[id].clone(), options);
+ // for testing, we want to check when features are destroyed
+ f.destroy = function() {
+ f.state = "destroyed";
+ }
+ return f;
+ }
+ var DELETE = OpenLayers.State.DELETE;
+ var INSERT = OpenLayers.State.INSERT;
+ var UPDATE = OpenLayers.State.UPDATE;
+
+ var cases = [{
+ targets: [
+ feature("abc")
+ ],
+ source: feature("dbe"),
+ splits: [{
+ original: feature("abc", {state: "destroyed"}),
+ features: [feature("ab", {state: INSERT}), feature("bc", {state: INSERT})]
+ }, {
+ original: feature("dbe", {state: "destroyed"}),
+ features: [feature("db", {state: INSERT}), feature("be", {state: INSERT})]
+ }]
+ }, {
+ options: {deferDelete: true},
+ targets: [
+ feature("abc", {state: INSERT})
+ ],
+ source: feature("dbe"),
+ splits: [{
+ original: feature("abc", {state: "destroyed"}),
+ features: [feature("ab", {state: INSERT}), feature("bc", {state: INSERT})]
+ }, {
+ original: feature("dbe", {state: DELETE}),
+ features: [feature("db", {state: INSERT}), feature("be", {state: INSERT})]
+ }]
+ }, {
+ options: {deferDelete: true},
+ targets: [
+ feature("abc", {state: UPDATE})
+ ],
+ source: feature("dbe", {state: INSERT}),
+ splits: [{
+ original: feature("abc", {state: DELETE}),
+ features: [feature("ab", {state: INSERT}), feature("bc", {state: INSERT})]
+ }, {
+ original: feature("dbe", {state: "destroyed"}),
+ features: [feature("db", {state: INSERT}), feature("be", {state: INSERT})]
+ }]
+ }];
+
+ var count = 0;
+ var c, control, options, log, event, split;
+ for(var i=0; i<cases.length; ++i) {
+ c = cases[i];
+ ++count; // test number of splits
+ for(var j=0; j<c.splits.length; ++j) {
+ split = c.splits[j];
+ ++count; // test original state
+ ++count; // test original geometry
+ ++count; // test number of parts
+ for(var k=0; k<split.features.length; ++k) {
+ ++count; // test part state
+ ++count; // test part geometry
+ }
+ }
+ }
+ t.plan(count);
+
+ for(var i=0; i<cases.length; ++i) {
+ c = cases[i];
+ log = {events: []};
+ options = OpenLayers.Util.extend({layer: layer, source: layer}, c.options);
+ control = new OpenLayers.Control.Split(options);
+ control.events.on({
+ split: function(e) {
+ log.events.push(e);
+ }
+ });
+ layer.features = c.targets;
+ control.considerSplit(c.source);
+ t.eq(log.events.length, c.splits.length, "case " + i + ": correct number of split events");
+ for(var j=0; j<log.events.length; ++j) {
+ event = log.events[j];
+ split = c.splits[j];
+ t.eq(event.original.state, split.original.state, "case " + i + " split " + j + ": correct original state");
+ t.geom_eq(event.original.geometry, split.original.geometry, "case " + i + " split " + j + ": correct original geometry");
+ t.eq(event.features.length, split.features.length, "case " + i + " split " + j + ": correct number of parts");
+ for(var k=0; k<split.features.length; ++k) {
+ t.eq(event.features[k].state, split.features[k].state, "case " + i + " split " + j + " feature " + k + ": correct state");
+ t.geom_eq(event.features[k].geometry, split.features[k].geometry, "case " + i + " split " + j + " feature " + k + ": correct geometry");
+ }
+ }
+ control.destroy();
+ }
+
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 100px; height: 100px;"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/TouchNavigation.html b/misc/openlayers/tests/Control/TouchNavigation.html
new file mode 100644
index 0000000..bffc225
--- /dev/null
+++ b/misc/openlayers/tests/Control/TouchNavigation.html
@@ -0,0 +1,155 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_Control_TouchNavigation_constructor (t) {
+ t.plan( 2 );
+ var options = {bar: "foo"};
+ var temp = OpenLayers.Control.prototype.initialize;
+ OpenLayers.Control.prototype.initialize = function(opt) {
+ t.eq(opt, options,
+ "constructor calls parent with the correct options");
+ };
+
+ var control = new OpenLayers.Control.TouchNavigation(options);
+ t.ok(control instanceof OpenLayers.Control.TouchNavigation,
+ "new OpenLayers.Control returns object");
+
+ OpenLayers.Control.prototype.initialize = temp;
+ }
+
+ function test_Control_TouchNavigation_destroy(t) {
+ t.plan(6);
+
+ var control = {
+ events: {
+ destroy: function() {
+ t.ok(true, "events destroyed");
+ }
+ },
+ deactivate: function() {
+ t.ok(true, "navigation control deactivated before being destroyed");
+ },
+ dragPan: {
+ destroy: function() {
+ t.ok(true, "dragPan destroyed");
+ }
+ },
+ handlers: {
+ click: {
+ destroy: function() {
+ t.ok(true, "clickHandler destroyed");
+ }
+ }
+ }
+ };
+
+ //this will also trigger one test by calling OpenLayers.Control's destroy
+ // and three more for the destruction of dragPan, zoomBox, and wheelHandler
+ OpenLayers.Control.TouchNavigation.prototype.destroy.apply(control, []);
+
+ t.eq(control.dragPan, null, "'dragPan' set to null");
+ t.eq(control.handlers, null, "handlers set to null");
+ }
+
+ function test_documentDrag(t) {
+
+ t.plan(2);
+
+ /**
+ * These tests confirm that the documentDrag property is false by
+ * default and is passed on to the DragPan control. Tests of panning
+ * while dragging outside the viewport should go in the DragPan tests.
+ * Tests of the document events and appropriate callbacks from the
+ * handler should go in the Drag handler tests.
+ */
+
+ var nav = new OpenLayers.Control.TouchNavigation();
+ t.eq(nav.documentDrag, false, "documentDrag false by default");
+
+ var map = new OpenLayers.Map({
+ div: document.body,
+ controls: [new OpenLayers.Control.TouchNavigation({documentDrag: true})]
+ });
+ nav = map.controls[0];
+
+ t.eq(nav.dragPan.documentDrag, true, "documentDrag set on the DragPan control");
+ map.destroy();
+
+ }
+
+ function test_dragPanOptions(t) {
+
+ t.plan(2);
+
+ var nav = new OpenLayers.Control.TouchNavigation();
+ t.eq(nav.dragPanOptions, null, "dragPanOptions null by default");
+
+ var map = new OpenLayers.Map({
+ div: document.body,
+ controls: [
+ new OpenLayers.Control.TouchNavigation({
+ dragPanOptions: {foo: 'bar'}
+ })
+ ]
+ });
+ nav = map.controls[0];
+
+ t.eq(nav.dragPan.foo, 'bar',
+ "foo property is set on the DragPan control");
+ map.destroy();
+
+ }
+
+ function test_clickHandlerOptions(t) {
+
+ t.plan(3);
+
+ var nav = new OpenLayers.Control.TouchNavigation();
+ t.eq(nav.clickHandlerOptions, null, "clickHandlerOptions null by default");
+
+ var map = new OpenLayers.Map({
+ div: document.body,
+ controls: [
+ new OpenLayers.Control.TouchNavigation({
+ clickHandlerOptions: {foo: "bar"}
+ })
+ ]
+ });
+ nav = map.controls[0];
+
+ t.eq(nav.handlers.click.foo, "bar", "foo property is set on the click handler");
+ t.eq(nav.handlers.click.pixelTolerance, 2, "pixelTolerance is 2 by default");
+ map.destroy();
+
+ }
+
+ function test_zoomOut(t) {
+ t.plan(1);
+
+ var map = new OpenLayers.Map('map', {zoomMethod: null});
+ var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'} );
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 5);
+ var origSetTimeout = window.setTimeout;
+ window.setTimeout = function(fn) { fn(); return 'id'; };
+ var control = new OpenLayers.Control.TouchNavigation();
+ map.addControl(control);
+ var handler = control.handlers.click;
+ handler.touchstart({xy: new OpenLayers.Pixel(1 ,1), touches: ["foo", "bar"]});
+ handler.touchend({});
+ t.eq(map.getZoom(), 4, "Did we zoom out?");
+ // tear down
+ map.destroy();
+ window.setTimeout = origSetTimeout;
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width:512px;height:256px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/TransformFeature.html b/misc/openlayers/tests/Control/TransformFeature.html
new file mode 100644
index 0000000..3279867
--- /dev/null
+++ b/misc/openlayers/tests/Control/TransformFeature.html
@@ -0,0 +1,129 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(6);
+ var layer = "foo";
+ var control = new OpenLayers.Control.TransformFeature(layer);
+
+ t.ok(control.layer == layer,
+ "constructor sets layer correctly");
+ t.ok(control.dragControl instanceof OpenLayers.Control.DragFeature,
+ "constructor sets the drag control correctly");
+ t.ok(control.box instanceof OpenLayers.Feature.Vector,
+ "box feature created");
+ t.eq(control.handles.length, 8, "8 handles created");
+ t.eq(control.rotationHandles.length, 4, "4 rotation handles created")
+ t.eq(typeof control.rotationHandleSymbolizer, "object",
+ "rotationHandleSymbolizer initialized");
+ control.destroy();
+ }
+
+ function test_destroy(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.TransformFeature(layer);
+ control.dragControl.destroy = function() {
+ t.ok(true,
+ "control.destroy calls destroy on drag control");
+ };
+ control.destroy();
+ map.destroy();
+ }
+
+ function test_activate(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.TransformFeature(layer);
+ map.addControl(control);
+
+ t.ok(!control.dragControl.active,
+ "drag control is not active prior to activating control");
+ control.activate();
+ t.ok(control.dragControl.active,
+ "drag control is active after activating control");
+ t.ok(control.box.layer === layer, "box added to layer");
+
+ map.destroy();
+ }
+
+ function test_setFeature(t) {
+ t.plan(6);
+ var map = new OpenLayers.Map("map", {allOverlays: true});
+ var layer = new OpenLayers.Layer.Vector();
+ var feature = new OpenLayers.Feature.Vector(
+ OpenLayers.Geometry.fromWKT("POLYGON((-1 -1, 1 -1, 1 1, -1 1))"));
+ layer.addFeatures([feature]);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 18);
+ var control = new OpenLayers.Control.TransformFeature(layer);
+ map.addControl(control);
+ var log = [];
+ control.events.on({
+ "beforesetfeature": function(e) { log.push(e); },
+ "setfeature": function(e) { log.push(e); }
+ });
+ control.setFeature(feature);
+
+ t.eq(log[0].type, "beforesetfeature", "beforesetfeature event fired with correct event type");
+ t.eq(log[1].type, "setfeature", "setfeature event fired with correct event type");
+
+ t.ok(control.active, "control activated on setFeature");
+ t.ok(feature.geometry.getBounds().equals(control.box.geometry.getBounds()), "box positioned correctly");
+ t.geom_eq(control.handles[0].geometry, control.box.geometry.components[0], "handle positioned with box");
+
+ var center = new OpenLayers.LonLat(1, 1);
+ control.box.move(center);
+ t.geom_eq(control.handles[0].geometry, control.box.geometry.components[0], "handle moved with box");
+ }
+
+ function test_handleMove(t) {
+ t.plan(16);
+ var map = new OpenLayers.Map("map", {allOverlays: true});
+ var layer = new OpenLayers.Layer.Vector();
+ var feature = new OpenLayers.Feature.Vector(
+ OpenLayers.Geometry.fromWKT("POLYGON((-1 -1, 1 -1, 1 1, -1 1))"));
+ layer.addFeatures([feature]);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 18);
+ var control = new OpenLayers.Control.TransformFeature(layer);
+ map.addControl(control);
+ control.setFeature(feature);
+
+ var bottomLeft = new OpenLayers.LonLat(-2, -2);
+ control.handles[0].move(bottomLeft);
+ t.geom_eq(control.handles[0].geometry, new OpenLayers.Geometry.Point(-2, -2), "bottom left handle at -2,-2");
+ t.geom_eq(control.handles[1].geometry, new OpenLayers.Geometry.Point(0, -2), "bottom handle at 0,-2");
+ t.geom_eq(control.handles[2].geometry, new OpenLayers.Geometry.Point(2, -2), "bottom right handle at 2,-2");
+ t.geom_eq(control.handles[3].geometry, new OpenLayers.Geometry.Point(2, 0), "right handle at 2,0");
+ t.geom_eq(control.handles[4].geometry, new OpenLayers.Geometry.Point(2, 2), "top right handle at 2,2");
+ t.geom_eq(control.handles[5].geometry, new OpenLayers.Geometry.Point(0, 2), "top handle at 0,2");
+ t.geom_eq(control.handles[6].geometry, new OpenLayers.Geometry.Point(-2, 2), "top left handle at -2,2");
+ t.geom_eq(control.handles[7].geometry, new OpenLayers.Geometry.Point(-2, 0), "left handle at -2,0");
+
+ control.irregular = true;
+
+ var bottomLeft = new OpenLayers.LonLat(-3, -3);
+ control.handles[0].move(bottomLeft);
+ t.geom_eq(control.handles[0].geometry, new OpenLayers.Geometry.Point(-3, -3), "bottom left handle at -3,-3");
+ t.geom_eq(control.handles[1].geometry, new OpenLayers.Geometry.Point(-0.5, -3), "bottom handle at 0,-3");
+ t.geom_eq(control.handles[2].geometry, new OpenLayers.Geometry.Point(2, -3), "bottom right handle at 2,-3");
+ t.geom_eq(control.handles[3].geometry, new OpenLayers.Geometry.Point(2, -0.5), "right handle at 2,0");
+ t.geom_eq(control.handles[4].geometry, new OpenLayers.Geometry.Point(2, 2), "top right handle at 2,2");
+ t.geom_eq(control.handles[5].geometry, new OpenLayers.Geometry.Point(-0.5, 2), "top handle at 0,2");
+ t.geom_eq(control.handles[6].geometry, new OpenLayers.Geometry.Point(-3, 2), "top left handle at -3,2");
+ t.geom_eq(control.handles[7].geometry, new OpenLayers.Geometry.Point(-3, -0.5), "left handle at -3,0");
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 250px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/UTFGrid.html b/misc/openlayers/tests/Control/UTFGrid.html
new file mode 100644
index 0000000..74b4b99
--- /dev/null
+++ b/misc/openlayers/tests/Control/UTFGrid.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <script>
+ /**
+ * Because browsers that implement requestAnimationFrame may not execute
+ * animation functions while a window is not displayed (e.g. in a hidden
+ * iframe as in these tests), we mask the native implementations here. The
+ * native requestAnimationFrame functionality is tested in Util.html and
+ * in PanZoom.html (where a popup is opened before panning). The panTo tests
+ * here will test the fallback setTimeout implementation for animation.
+ */
+ window.requestAnimationFrame =
+ window.webkitRequestAnimationFrame =
+ window.mozRequestAnimationFrame =
+ window.oRequestAnimationFrame =
+ window.msRequestAnimationFrame = null;
+ </script>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var map, layer, control;
+ var log;
+ function setUp() {
+ layer = new OpenLayers.Layer.UTFGrid({
+ url: "../data/utfgrid/world_utfgrid/${z}/${x}/${y}.json",
+ isBaseLayer: true,
+ utfgridResolution: 4
+ });
+ map = new OpenLayers.Map({
+ div: "map",
+ tileManager: null,
+ projection: "EPSG:900913",
+ layers: [layer],
+ center: [0, 0],
+ zoom: 1
+ });
+ log = [];
+ control = new OpenLayers.Control.UTFGrid({
+ callback: function(infoLookup, loc, pixel) {
+ log.push([infoLookup, loc, pixel]);
+ }
+ });
+ map.addControl(control);
+ }
+
+ function tearDown() {
+ map.destroy();
+ map = null;
+ layer = null;
+ control = null;
+ log = [];
+ }
+
+ function test_constructor(t) {
+ t.plan(2);
+
+ var control = new OpenLayers.Control.UTFGrid();
+ t.ok(control instanceof OpenLayers.Control.UTFGrid, "utfgrid instance");
+ t.eq(control.handlerMode, "click", "control mode");
+
+ control.destroy();
+
+ }
+
+ function test_handleEvent(t) {
+ setUp();
+
+ var cases = [{
+ evt: {xy: {x: 100, y: 70}},
+ lookup: {
+ "0": {
+ id: "207",
+ data: {
+ NAME: "United States",
+ POP2005: 299846449
+ }
+ }
+ }
+ }, {
+ evt: {xy: {x: 350, y: 20}},
+ lookup: {
+ "0": {
+ id: "245",
+ data: {
+ NAME: "Russia",
+ POP2005: 143953092
+ }
+ }
+ }
+ }];
+
+ var len = cases.length;
+ t.plan(4*len);
+
+ // wait for tile loading to finish
+ t.delay_call(0.5, function() {
+ var c, arg;
+ for (var i=0; i<len; ++i) {
+ c = cases[i];
+ t.eq(log.length, i, i + ": log length before");
+ control.handleEvent(c.evt);
+ t.eq(log.length, i+1, i + ": log length after");
+ t.eq(log[i][0], c.lookup, i + ": callback infoLookup");
+ t.eq(log[i][2], c.evt.xy, i + ": callback pixel");
+ }
+
+ tearDown();
+ });
+
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="height: 256px; width: 512px"></div>
+</body>
+</html>
+
diff --git a/misc/openlayers/tests/Control/WMSGetFeatureInfo.html b/misc/openlayers/tests/Control/WMSGetFeatureInfo.html
new file mode 100644
index 0000000..b5e6d5d
--- /dev/null
+++ b/misc/openlayers/tests/Control/WMSGetFeatureInfo.html
@@ -0,0 +1,644 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(5);
+
+ var options = {
+ url: 'http://localhost/wms',
+ layers: ["foo"],
+ formatOptions: {
+ foo: "bar"
+ }
+ };
+ var control = new OpenLayers.Control.WMSGetFeatureInfo(options);
+ t.ok(control instanceof OpenLayers.Control.WMSGetFeatureInfo,
+ "new OpenLayers.Control.WMSGetFeatureInfo returns an instance");
+ t.eq(control.url, 'http://localhost/wms',
+ "constructor sets url correctly");
+ t.eq(control.layers, ["foo"],
+ "constructor layers"
+ );
+ t.ok(control.format instanceof OpenLayers.Format.WMSGetFeatureInfo, "format created");
+ t.eq(control.format.foo, "bar", "format options used")
+ }
+
+ function test_clickCallBack_option(t) {
+ t.plan(9);
+
+ var control;
+
+ control = new OpenLayers.Control.WMSGetFeatureInfo({
+ hover: true
+ });
+ t.ok(control.handler instanceof OpenLayers.Handler.Hover,
+ 'constructor creates hover handler');
+ t.ok(control.handler.callbacks["move"] === control.cancelHover,
+ 'constructor registers proper "move" callback in handler');
+ t.ok(control.handler.callbacks["pause"] === control.getInfoForHover,
+ 'constructor registers proper "pause" callback in handler');
+
+ control = new OpenLayers.Control.WMSGetFeatureInfo();
+ t.ok(control.handler instanceof OpenLayers.Handler.Click,
+ 'constructor creates click handler');
+ t.ok(control.handler.callbacks["click"] === control.getInfoForClick,
+ 'constructor registers proper "click" callback in handler');
+
+ control = new OpenLayers.Control.WMSGetFeatureInfo({
+ clickCallback: "rightclick"
+ });
+ t.ok(control.handler.callbacks["rightclick"] === control.getInfoForClick,
+ 'constructor registers proper "rightclick" callback in handler');
+
+ control = new OpenLayers.Control.WMSGetFeatureInfo({
+ clickCallback: "dblclick",
+ handlerOptions: {
+ click: {
+ "single": false,
+ "double": true
+ }
+ }
+ });
+ t.ok(control.handler.callbacks["dblclick"] === control.getInfoForClick,
+ 'constructor registers proper "dblclick" callback in handler');
+ t.eq(control.handler["single"], false,
+ 'constructor sets "single" to false in handler');
+ t.eq(control.handler["double"], true,
+ 'constructor sets "double" to true in handler');
+ }
+
+ function test_destroy(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var click = new OpenLayers.Control.WMSGetFeatureInfo({
+ url: 'http://localhost/wms',
+ layers: ["foo"]
+ });
+
+ var hover = new OpenLayers.Control.WMSGetFeatureInfo({
+ url: 'http://localhost/wms',
+ layers: ["foo"],
+ hover: true
+ });
+
+ click.handler.deactivate = function() {
+ t.ok(true,
+ "control.deactivate calls deactivate on click handler");
+ };
+ hover.handler.deactivate = function() {
+ t.ok(true,
+ "control.deactivate calls deactivate on hover handler");
+ };
+ click.destroy();
+ hover.destroy();
+ }
+
+ function test_click(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map('map');
+
+ // mock up active control
+ var control = new OpenLayers.Control.WMSGetFeatureInfo();
+ map.addControl(control);
+ control.activate();
+
+ control.request = function(position) {
+ t.eq(position.x, 50,
+ "x position is as expected");
+ t.eq(position.y, 50,
+ "y position is as expected");
+ };
+
+ control.getInfoForClick({xy: {x: 50, y: 50}});
+ control.getInfoForHover({xy: {x: 50, y: 50}});
+ }
+
+ function test_getfeatureinfo_event(t) {
+
+ t.plan(5);
+
+ var text =
+ '<?xml version="1.0" encoding="UTF-8" ?>' +
+ '<FeatureInfoResponse>' +
+ ' <FIELDS OBJECTID="1188" HECTARES="1819.734" ZONENR="5854" NULZONES=" " AREA="18197340.1426" PERIMETER="19177.4073627" SHAPE="NULL" SE_ANNO_CAD_DATA="NULL" SHAPE.AREA="0" SHAPE.LEN="0"/>' +
+ '</FeatureInfoResponse>';
+
+ var map = new OpenLayers.Map('map');
+
+ var xy;
+ var url = "http://foo";
+
+ // mock up a control with output "object" and drillDown true
+ var control = new OpenLayers.Control.WMSGetFeatureInfo({
+ output: "object",
+ drillDown: true,
+ request: function(position) {},
+ eventListeners: {
+ getfeatureinfo: function(evt) {
+ t.ok(evt.features[0].url === url, "features is an object with a property url when output is object");
+ var features = evt.features[0].features;
+ t.ok(features.length === 1, "features properties has a length of 1");
+ t.ok(features[0] instanceof OpenLayers.Feature.Vector, "Feature array contains 1 feature");
+ }
+ }
+ });
+
+ // mock up a control with output "features" and drillDown true
+ var control2 = new OpenLayers.Control.WMSGetFeatureInfo({
+ autoActivate: true,
+ drillDown: true,
+ request: function(position) {},
+ eventListeners: {
+ getfeatureinfo: function(evt) {
+ var features = evt.features;
+ t.ok(features.length === 1, "features properties has a length of 1");
+ t.ok(features[0] instanceof OpenLayers.Feature.Vector, "Feature array contains 1 feature");
+ }
+ }
+ });
+
+ map.addControls([control, control2]);
+ control.activate();
+
+ xy = {x: 50, y: 50};
+ control._requestCount = control2._requestCount = 0;
+ control._numRequests = control2._numRequests = 1;
+ control.handleResponse({xy: xy}, {responseText: text}, url);
+ control2.handleResponse({xy: xy}, {responseText: text}, url);
+ map.destroy();
+ }
+
+ function test_beforegetfeatureinfo_event(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map('map');
+
+ var xy, mode;
+
+ // mock up active control
+ var control = new OpenLayers.Control.WMSGetFeatureInfo({
+ request: function(position) {},
+ eventListeners: {
+ beforegetfeatureinfo: function(evt) {
+ t.eq(evt.xy, xy,
+ "beforegetfeatureinfo listener gets " +
+ "expected xy (" + mode + ")");
+ }
+ }
+ });
+ map.addControl(control);
+ control.activate();
+
+ // 1 test
+ mode = "click";
+ xy = {x: 50, y: 50};
+ control.getInfoForClick({xy: xy});
+
+ // 1 test
+ mode = "hover";
+ xy = {x: 70, y: 70};
+ control.getInfoForHover({xy: xy});
+ }
+
+ function test_nogetfeatureinfo_event(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map');
+ // mock up active control
+ var control = new OpenLayers.Control.WMSGetFeatureInfo({
+ eventListeners: {
+ nogetfeatureinfo: function(evt) {
+ t.ok((evt.type == "nogetfeatureinfo"), "nogetfeatureinfo listener gets called when there are no queryable layers");
+ }
+ }
+ });
+ map.addControl(control);
+ control.activate();
+
+ // 1 test
+ mode = "click";
+ xy = {x: 50, y: 50};
+ control.getInfoForClick({xy: xy});
+ }
+
+ function test_activate(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map("map");
+ var click = new OpenLayers.Control.WMSGetFeatureInfo({
+ url: 'http://localhost/wms',
+ featureType: 'type',
+ featureNS: 'http://localhost/ns',
+ layers: 'ns:type'
+ });
+ var hover = new OpenLayers.Control.WMSGetFeatureInfo({
+ url: 'http://localhost/wms',
+ featureType: 'type',
+ featureNS: 'http://localhost/ns',
+ layers: 'ns:type',
+ hover: true
+ });
+ map.addControl(click);
+ map.addControl(hover);
+ t.ok(!click.handler.active,
+ "click handler is not active prior to activating control");
+ t.ok(!hover.handler.active,
+ "hover handler is not active prior to activating control");
+ click.activate();
+ hover.activate();
+ t.ok(click.handler.active,
+ "click handler is active after activating control");
+ t.ok(hover.handler.active,
+ "hover handler is active after activating control");
+ }
+
+ function test_deactivate(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var click = new OpenLayers.Control.WMSGetFeatureInfo({
+ url: 'http://localhost/wms',
+ featureType: 'type',
+ featureNS: 'http://localhost/ns',
+ layers: 'ns:type'
+ });
+ var hover = new OpenLayers.Control.WMSGetFeatureInfo({
+ url: 'http://localhost/wms',
+ featureType: 'type',
+ featureNS: 'http://localhost/ns',
+ layers: 'ns:type'
+ });
+ map.addControl(click);
+ map.addControl(hover);
+ click.activate();
+ hover.activate();
+
+ click.handler.deactivate = function() {
+ t.ok(true,
+ "control.deactivate calls deactivate on click handler");
+ OpenLayers.Handler.Click.prototype.deactivate.apply(this, arguments);
+ };
+ hover.handler.deactivate = function() {
+ t.ok(true,
+ "control.deactivate calls deactivate on hover handler");
+ OpenLayers.Handler.Hover.prototype.deactivate.apply(this, arguments);
+ };
+ click.deactivate();
+ hover.deactivate();
+ }
+
+ // Verify that things work all right when we combine different types for the STYLES and LAYERS
+ // params in the WMS Layers involved
+ function test_mixedParams(t) {
+ t.plan(5);
+ var map = new OpenLayers.Map("map", {
+ getExtent: function() {return(new OpenLayers.Bounds(-180,-90,180,90));}
+ });
+ var geographic = new OpenLayers.Projection("EPSG:4326");
+
+ var a = new OpenLayers.Layer.WMS("dummy","http://localhost/wms", {
+ layers: "a,b,c,d",
+ styles: "a,b,c,d"
+ }, {projection: geographic});
+
+ var b = new OpenLayers.Layer.WMS("dummy","http://localhost/wms", {
+ layers: ["a","b","c","d"],
+ styles: ["a","b","c","d"]
+ }, {projection: geographic});
+
+ var c = new OpenLayers.Layer.WMS("dummy","http://localhost/wms", {
+ layers: ["a","b","c","d"]
+ });
+
+ var d = new OpenLayers.Layer.WMS("dummy","http://localhost/wms", {
+ layers: "a,b,c,d"
+ }, {projection: geographic});
+
+ var click = new OpenLayers.Control.WMSGetFeatureInfo({
+ featureType: 'type',
+ featureNS: 'ns',
+ layers: [a, b, c, d]
+ }, {projection: geographic});
+
+ map.addControl(click);
+
+ var log = {};
+ var _request = OpenLayers.Request.GET;
+ OpenLayers.Request.GET = function(options) {
+ log.options = options;
+ };
+ click.activate();
+ click.getInfoForClick({xy: {x: 50.2, y: 50.1}});
+ OpenLayers.Request.GET = _request;
+
+ t.eq(
+ log.options && log.options.params.X,
+ 50,
+ "X should be an integer"
+ );
+
+ t.eq(
+ log.options && log.options.params.Y,
+ 50,
+ "Y should be an integer"
+ );
+
+ t.eq(
+ log.options && log.options.url,
+ "http://localhost/wms",
+ "url from first layer used"
+ );
+ t.eq(
+ log.options && log.options.params.STYLES.join(","),
+ ",,,,,,,,a,b,c,d,a,b,c,d",
+ "Styles merged correctly"
+ );
+
+ t.eq(
+ log.options && log.options.params.FORMAT,
+ "image/jpeg",
+ "Required 'format' parameter included"
+ );
+
+ }
+
+ function test_urlMatches(t) {
+
+ t.plan(5);
+
+ var control = new OpenLayers.Control.WMSGetFeatureInfo({
+ url: "http://host/wms?one=1&two=2"
+ });
+
+ t.ok(!control.urlMatches("foo"), "doesn't match garbage");
+ t.ok(control.urlMatches("http://host:80/wms?two=2&one=1"), "matches equivalent url");
+
+ // give the control more urls to match from
+ control.layerUrls = ["http://a.host/wms", "http://b.host/wms"];
+
+ t.ok(control.urlMatches("http://host:80/wms?two=2&one=1"), "still matches equivalent url");
+ t.ok(control.urlMatches("http://a.host:80/wms"), "matches equivalent of first of layerUrls");
+ t.ok(control.urlMatches("http://b.host:80/wms"), "matches equivalent of second of layerUrls");
+
+ }
+
+ function test_layerUrls(t) {
+
+ t.plan(4);
+ var map = new OpenLayers.Map({
+ div: "map",
+ getExtent: function() {
+ return new OpenLayers.Bounds(-180,-90,180,90);
+ }
+ });
+
+ var a = new OpenLayers.Layer.WMS(
+ null, "http://a.mirror/wms", {layers: "a"}
+ );
+ var b = new OpenLayers.Layer.WMS(
+ null, "http://b.mirror/wms", {layers: "b"}
+ );
+ var c = new OpenLayers.Layer.WMS(
+ null, ["http://c.mirror/wms", "http://d.mirror/wms"], {layers: "c"}
+ );
+ map.addLayers([a, b, c]);
+
+ var control = new OpenLayers.Control.WMSGetFeatureInfo({
+ url: "http://host/wms",
+ layers: [a, b, c]
+ });
+ map.addControl(control);
+ control.activate();
+
+ // log calls to GET
+ var log;
+ var _request = OpenLayers.Request.GET;
+ OpenLayers.Request.GET = function(options) {
+ log.options = options;
+ };
+
+ // control url doesn't match layer urls, no request issued
+ log = {};
+ control.getInfoForClick({xy: {x: 50, y: 50}});
+ t.ok(!log.options, "no url match, no request issued");
+
+ // give control a list of urls to match
+ log = {};
+ control.layerUrls = ["http://a.mirror/wms", "http://b.mirror/wms"];
+ control.getInfoForClick({xy: {x: 50, y: 50}});
+ t.eq(log.options && log.options.url, "http://host/wms", "some match, request issued");
+ t.eq(log.options && log.options.params["QUERY_LAYERS"].join(","), "b,a", "selected layers queried");
+
+ // show that a layer can be matched if it has a urls array itself (first needs to be matched)
+ log = {};
+ control.layerUrls = ["http://c.mirror/wms"];
+ control.getInfoForClick({xy: {x: 50, y: 50}});
+ t.eq(log.options && log.options.params["QUERY_LAYERS"].join(","), "c", "layer with urls array can be queried");
+
+ // clean up
+ OpenLayers.Request.GET = _request;
+ map.destroy();
+
+ }
+
+ function test_hover(t) {
+
+ t.plan(2);
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ layers: [
+ new OpenLayers.Layer.WMS(null, "/dummywms", {layers: "one"})
+ ],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 1
+ });
+
+ var control = new OpenLayers.Control.WMSGetFeatureInfo({
+ hover: true
+ });
+ map.addControl(control);
+ control.activate();
+
+ // mock up a mousemove
+ control.getInfoForHover({xy: new OpenLayers.Pixel(10, 10)});
+ t.ok(!!control.hoverRequest, "hoverRequest set");
+
+ // confirm that request is canceled on next move
+ var called = 0;
+ control.hoverRequest.abort = function() {
+ ++called;
+ };
+ control.handler.px = null;
+ control.handler.mousemove({xy: new OpenLayers.Pixel(20, 20)});
+ t.eq(called, 1, "hover request aborted");
+
+ map.destroy();
+
+ }
+
+ function test_exceptions(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map("map", {
+ getExtent: function() {return(new OpenLayers.Bounds(-180,-90,180,90));}
+ }
+ );
+
+ var a = new OpenLayers.Layer.WMS("dummy","http://myhost/wms", {
+ layers: "x",
+ exceptions: "text/xml"
+ });
+
+ map.addLayer(a);
+
+ var click = new OpenLayers.Control.WMSGetFeatureInfo({
+ });
+
+ map.addControl(click);
+
+ var _request = OpenLayers.Request.GET;
+ OpenLayers.Request.GET = function(options) {
+ t.eq(options.params["EXCEPTIONS"], "text/xml", "Exceptions parameter taken from the WMS layer if provided");
+ };
+ click.activate();
+ click.getInfoForClick({xy: {x: 50, y: 50}});
+ OpenLayers.Request.GET = _request;
+ map.destroy();
+ }
+
+ function test_drillDown(t) {
+ t.plan(6);
+ var map = new OpenLayers.Map("map", {
+ getExtent: function() {return(new OpenLayers.Bounds(-180,-90,180,90));}
+ }
+ );
+
+ var a = new OpenLayers.Layer.WMS("dummy","http://localhost/wms", {
+ layers: "a"
+ });
+
+ var b = new OpenLayers.Layer.WMS("dummy","http://localhost/wms", {
+ layers: "c"
+ });
+
+ // this service does not support application/vnd.ogc.gml for GetFeatureInfo, only text/xml
+ var c = new OpenLayers.Layer.WMS("dummy","http://myhost/wms", {
+ layers: "x",
+ info_format: "text/xml"
+ });
+
+ map.addLayers([a, b, c]);
+
+ var click = new OpenLayers.Control.WMSGetFeatureInfo({
+ drillDown: true,
+ infoFormat: "application/vnd.ogc.gml"
+ });
+
+ map.addControl(click);
+
+ var count = 0;
+ var _request = OpenLayers.Request.GET;
+ OpenLayers.Request.GET = function(options) {
+ count++;
+ if (count == 2) {
+ t.eq(options.params["INFO_FORMAT"], "application/vnd.ogc.gml", "Default info format of the control is used");
+ t.eq(options.params["QUERY_LAYERS"].join(","), "c,a", "Layers should be grouped by service url");
+ t.eq(options.url, "http://localhost/wms", "Correct url used for second request");
+ } else if (count == 1) {
+ t.eq(options.params["INFO_FORMAT"], "text/xml", "Overridden info format is used instead of the control's infoFormat");
+ t.eq(options.url, "http://myhost/wms", "Correct url used for first request");
+ }
+ };
+ click.activate();
+ click.getInfoForClick({xy: {x: 50, y: 50}});
+ OpenLayers.Request.GET = _request;
+ t.eq(count, 2, "We expect 2 requests to go off");
+ map.destroy();
+ }
+
+ function test_GetFeatureInfo_buildWMSOptions(t) {
+ t.plan(3);
+
+ var map = new OpenLayers.Map("map", {
+ getExtent: function() {return(new OpenLayers.Bounds(-180,-90,180,90));},
+ projection: "EPSG:900913"
+ });
+ var a = new OpenLayers.Layer.WMS("dummy", "http://localhost/wms", {
+ layers: "a"
+ }, {projection: "EPSG:3857"});
+ var b = new OpenLayers.Layer.WMS("dummy", "http://localhost/wms", {
+ layers: "b"
+ });
+ var c = new OpenLayers.Layer.WMS("dummy", "http://localhost/wms", {
+ layers: "c"
+ }, {projection: "EPSG:4326"});
+ map.addLayers([a, b, c]);
+ var gfi = new OpenLayers.Control.WMSGetFeatureInfo();
+ map.addControl(gfi);
+ gfi.activate();
+
+ var options = gfi.buildWMSOptions("http://localhost/wms", [a], {xy: {x: 50, y: 50}}, "text/html");
+ t.eq(options.params.SRS, "EPSG:3857", "layer projection used if provided and equal map projection");
+
+ options = gfi.buildWMSOptions("http://localhost/wms", [b], {xy: {x: 50, y: 50}}, "text/html");
+ t.eq(options.params.SRS, "EPSG:900913", "map projection used if layer has no projection configured");
+
+ options = gfi.buildWMSOptions("http://localhost/wms", [b], {xy: {x: 50, y: 50}}, "text/html");
+ t.eq(options.params.SRS, "EPSG:900913", "map projection used if layer configured with an incompatible projection");
+ }
+
+ function test_GetFeatureInfo_WMS13(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map("map", {
+ getExtent: function() {return(new OpenLayers.Bounds(-180,-90,180,90));}
+ }
+ );
+
+ var a = new OpenLayers.Layer.WMS(null, "http://localhost/wms", {
+ layers: "a",
+ version: "1.3.0"
+ });
+ map.addLayer(a);
+
+ var click = new OpenLayers.Control.WMSGetFeatureInfo({
+ });
+
+ map.addControl(click);
+ var log = {};
+ var _request = OpenLayers.Request.GET;
+ OpenLayers.Request.GET = function(options) {
+ log.options = options;
+ };
+ click.activate();
+ click.getInfoForClick({xy: {x: 50.1, y: 60.2}});
+ OpenLayers.Request.GET = _request;
+ t.eq(
+ log.options && log.options.params.CRS,
+ "EPSG:4326",
+ "Since it is WMS 1.3 use CRS parameter instead of SRS in the GetFeatureInfo request"
+ );
+
+ t.eq(
+ log.options && log.options.params.I,
+ 50,
+ "Since it is WMS 1.3 use I parameter instead of X in the GetFeatureInfo request"
+ );
+
+ t.eq(
+ log.options && log.options.params.J,
+ 60,
+ "Since it is WMS 1.3 use J parameter instead of Y in the GetFeatureInfo request"
+ );
+
+ t.eq(
+ log.options && log.options.params.BBOX,
+ "-90,-180,90,180",
+ "Since it is WMS 1.3 the BBOX should respect axis order"
+ );
+
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 250px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/WMTSGetFeatureInfo.html b/misc/openlayers/tests/Control/WMTSGetFeatureInfo.html
new file mode 100644
index 0000000..c7be78c
--- /dev/null
+++ b/misc/openlayers/tests/Control/WMTSGetFeatureInfo.html
@@ -0,0 +1,334 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(4);
+
+ var options = {
+ url: "http://localhost/wmts",
+ layers: ["foo"],
+ formatOptions: {
+ foo: "bar"
+ }
+ };
+ var control = new OpenLayers.Control.WMTSGetFeatureInfo(options);
+ t.ok(control instanceof OpenLayers.Control.WMTSGetFeatureInfo,
+ "new OpenLayers.Control.WMTSGetFeatureInfo returns an instance");
+ t.eq(control.layers, ["foo"],
+ "constructor layers"
+ );
+ t.ok(control.format instanceof OpenLayers.Format.WMSGetFeatureInfo, "format created");
+ t.eq(control.format.foo, "bar", "format options used")
+ }
+
+ function test_clickCallBack_option(t) {
+ t.plan(9);
+
+ var control;
+
+ control = new OpenLayers.Control.WMTSGetFeatureInfo({
+ hover: true
+ });
+ t.ok(control.handler instanceof OpenLayers.Handler.Hover,
+ 'constructor creates hover handler');
+ t.ok(control.handler.callbacks["move"] === control.cancelHover,
+ 'constructor registers proper "move" callback in handler');
+ t.ok(control.handler.callbacks["pause"] === control.getInfoForHover,
+ 'constructor registers proper "pause" callback in handler');
+
+ control = new OpenLayers.Control.WMTSGetFeatureInfo();
+ t.ok(control.handler instanceof OpenLayers.Handler.Click,
+ 'constructor creates click handler');
+ t.ok(control.handler.callbacks["click"] === control.getInfoForClick,
+ 'constructor registers proper "click" callback in handler');
+
+ control = new OpenLayers.Control.WMTSGetFeatureInfo({
+ clickCallback: "rightclick"
+ });
+ t.ok(control.handler.callbacks["rightclick"] === control.getInfoForClick,
+ 'constructor registers proper "rightclick" callback in handler');
+
+ control = new OpenLayers.Control.WMTSGetFeatureInfo({
+ clickCallback: "dblclick",
+ handlerOptions: {
+ click: {
+ "single": false,
+ "double": true
+ }
+ }
+ });
+ t.ok(control.handler.callbacks["dblclick"] === control.getInfoForClick,
+ 'constructor registers proper "dblclick" callback in handler');
+ t.eq(control.handler["single"], false,
+ 'constructor sets "single" to false in handler');
+ t.eq(control.handler["double"], true,
+ 'constructor sets "double" to true in handler');
+ }
+
+ function test_destroy(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var click = new OpenLayers.Control.WMTSGetFeatureInfo({
+ url: 'http://localhost/wms',
+ layers: ["foo"]
+ });
+
+ var hover = new OpenLayers.Control.WMTSGetFeatureInfo({
+ url: 'http://localhost/wms',
+ layers: ["foo"],
+ hover: true
+ });
+
+ click.handler.deactivate = function() {
+ t.ok(true,
+ "control.deactivate calls deactivate on click handler");
+ };
+ hover.handler.deactivate = function() {
+ t.ok(true,
+ "control.deactivate calls deactivate on hover handler");
+ };
+ click.destroy();
+ hover.destroy();
+ }
+
+ function test_click(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map('map');
+
+ // mock up active control
+ var control = new OpenLayers.Control.WMTSGetFeatureInfo();
+ map.addControl(control);
+ control.activate();
+
+ control.request = function(position) {
+ t.eq(position.x, 200,
+ "x position is as expected");
+ t.eq(position.y, 125,
+ "y position is as expected");
+ };
+
+ control.getInfoForClick({xy: {x: 200, y: 125}});
+ control.getInfoForHover({xy: {x: 200, y: 125}});
+ }
+
+ function test_beforegetfeatureinfo_event(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map({
+ div: "map",
+ allOverlays: true,
+ layers: [
+ new OpenLayers.Layer.WMTS({
+ name: "Test WMTS 1",
+ url: "/testwmts/",
+ layer: "test1",
+ style: "",
+ matrixSet: "set-id",
+ isBaseLayer: false
+ }),
+ new OpenLayers.Layer.WMTS({
+ name: "Test WMTS 2",
+ url: "/testwmts/",
+ layer: "test2",
+ style: "",
+ matrixSet: "set-id",
+ isBaseLayer: false
+ })
+ ],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 0
+ });
+
+ var log = [];
+
+ // test click
+ var click = new OpenLayers.Control.WMTSGetFeatureInfo({
+ drillDown: true,
+ eventListeners: {
+ beforegetfeatureinfo: function(evt) {
+ log.push({xy: evt.xy});
+ }
+ }
+ });
+ map.addControl(click);
+ click.activate();
+ click.getInfoForClick({xy: {x: 200, y: 125}});
+ t.eq(log.length, 2, "click: beforegetfeatureinfo triggered twice");
+ log = [];
+ click.deactivate();
+
+ // test hover
+ var hover = new OpenLayers.Control.WMTSGetFeatureInfo({
+ hover: true,
+ eventListeners: {
+ beforegetfeatureinfo: function(evt) {
+ log.push({xy: evt.xy});
+ }
+ }
+ });
+ map.addControl(hover);
+ hover.activate();
+ xy = {x: 70, y: 70};
+ hover.getInfoForHover({xy: {x: 70, y: 70}});
+ t.eq(log.length, 1, "hover: beforegetfeatureinfo triggered once");
+ log = [];
+ hover.deactivate();
+
+ map.destroy();
+ }
+
+ function test_activate(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map("map");
+ var click = new OpenLayers.Control.WMTSGetFeatureInfo({
+ url: 'http://localhost/wms',
+ layers: ['ns:type']
+ });
+ var hover = new OpenLayers.Control.WMTSGetFeatureInfo({
+ url: 'http://localhost/wms',
+ featureType: 'type',
+ featureNS: 'http://localhost/ns',
+ layers: 'ns:type',
+ hover: true
+ });
+ map.addControl(click);
+ map.addControl(hover);
+ t.ok(!click.handler.active,
+ "click handler is not active prior to activating control");
+ t.ok(!hover.handler.active,
+ "hover handler is not active prior to activating control");
+ click.activate();
+ hover.activate();
+ t.ok(click.handler.active,
+ "click handler is active after activating control");
+ t.ok(hover.handler.active,
+ "hover handler is active after activating control");
+ }
+
+ function test_deactivate(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var click = new OpenLayers.Control.WMTSGetFeatureInfo({
+ url: 'http://localhost/wms',
+ featureType: 'type',
+ featureNS: 'http://localhost/ns',
+ layers: 'ns:type'
+ });
+ var hover = new OpenLayers.Control.WMTSGetFeatureInfo({
+ url: 'http://localhost/wms',
+ featureType: 'type',
+ featureNS: 'http://localhost/ns',
+ layers: 'ns:type'
+ });
+ map.addControl(click);
+ map.addControl(hover);
+ click.activate();
+ hover.activate();
+
+ click.handler.deactivate = function() {
+ t.ok(true,
+ "control.deactivate calls deactivate on click handler");
+ OpenLayers.Handler.Click.prototype.deactivate.apply(this, arguments);
+ };
+ hover.handler.deactivate = function() {
+ t.ok(true,
+ "control.deactivate calls deactivate on hover handler");
+ OpenLayers.Handler.Hover.prototype.deactivate.apply(this, arguments);
+ };
+ click.deactivate();
+ hover.deactivate();
+ }
+
+ function test_getInfoForClick(t) {
+
+ t.plan(13);
+ var map = new OpenLayers.Map({
+ div: "map",
+ getExtent: function() {
+ return new OpenLayers.Bounds(-180,-90,180,90);
+ }
+ });
+
+ var a = new OpenLayers.Layer.WMTS({
+ url: "http://a.example.com/wmts",
+ layer: "a",
+ matrixSet: "bar",
+ style: "default"
+ });
+
+ var b = new OpenLayers.Layer.WMTS({
+ url: "http://b.example.com/wmts",
+ layer: "b",
+ matrixSet: "bar",
+ style: "default",
+ isBaseLayer: false
+ });
+
+ var c = new OpenLayers.Layer.WMTS({
+ url: ["http://c1.example.com/wmts", "http://c2.example.com"],
+ layer: "c",
+ matrixSet: "bar",
+ style: "default",
+ isBaseLayer: false
+ });
+ map.addLayers([a, b, c]);
+ map.zoomToMaxExtent();
+
+ var control = new OpenLayers.Control.WMTSGetFeatureInfo({
+ layers: [a, b, c]
+ });
+ map.addControl(control);
+ control.activate();
+
+ // log calls to GET
+ var log;
+ var _request = OpenLayers.Request.GET;
+ OpenLayers.Request.GET = function(options) {
+ log.push(options);
+ };
+
+ // query first layer (drillDown false)
+ log = [];
+ control.drillDown = false;
+ control.queryVisible = false;
+ control.getInfoForClick({xy: {x: 200, y: 125}});
+ t.eq(log.length, 1, "one requests issued");
+ t.eq(log[0].url, "http://c1.example.com/wmts", "{drillDown: false} correct url");
+ t.eq(log[0].params["LAYER"], "c", "{drillDown: false} correct layer parameter");
+
+ // query all layers
+ log = [];
+ control.drillDown = true;
+ control.queryVisible = false;
+ control.getInfoForClick({xy: {x: 200, y: 125}});
+ t.eq(log.length, 3, "three requests issued");
+ t.eq(log[0].url, "http://c1.example.com/wmts", "[c] correct url");
+ t.eq(log[0].params["LAYER"], "c", "[c] correct layer parameter");
+ t.eq(log[1].url, "http://b.example.com/wmts", "[b] correct url");
+ t.eq(log[1].params["LAYER"], "b", "[b] correct layer parameter");
+ t.eq(log[2].url, "http://a.example.com/wmts", "[a] correct url");
+ t.eq(log[2].params["LAYER"], "a", "[a] correct layer parameter");
+
+ // query only visible layers
+ log = [];
+ control.drillDown = true;
+ control.queryVisible = true;
+ b.setVisibility(false);
+ control.getInfoForClick({xy: {x: 200, y: 125}});
+ t.eq(log.length, 2, "two requests issued");
+ t.eq(log[0].url, "http://c1.example.com/wmts", "correct url for second visible layer");
+ t.eq(log[1].url, "http://a.example.com/wmts", "correct url for first visible layer");
+
+ // clean up
+ OpenLayers.Request.GET = _request;
+ map.destroy();
+
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 250px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/Zoom.html b/misc/openlayers/tests/Control/Zoom.html
new file mode 100644
index 0000000..cfeb082
--- /dev/null
+++ b/misc/openlayers/tests/Control/Zoom.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(5);
+
+ var control = new OpenLayers.Control.Zoom();
+ t.ok(control instanceof OpenLayers.Control, "instance of Control");
+ t.ok(control instanceof OpenLayers.Control.Zoom, "instance of Zoom");
+ t.eq(control.displayClass, "olControlZoom", "displayClass");
+ control.destroy();
+
+ control = new OpenLayers.Control.Zoom({
+ zoomInText: "zoom in!",
+ zoomOutText: "zoom out!"
+ });
+ t.eq(control.zoomInText, "zoom in!", "zoomInText");
+ t.eq(control.zoomOutText, "zoom out!", "zoomOutText");
+ control.destroy();
+ }
+
+ function test_addControl(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map("map");
+ var control = new OpenLayers.Control.Zoom();
+ map.addControl(control);
+ t.ok(control.map === map, "Control.map set");
+ t.ok(!!~OpenLayers.Util.indexOf(map.controls, control), "map.controls contains control");
+
+ control = new OpenLayers.Control.Zoom({zoomInId: "in", zoomOutId: "out"});
+ map.addControl(control);
+ var eventsEl = document.getElementById("out").parentNode;
+ t.ok(control.events.element === eventsEl, "Events instance listens to custom div's parentNode");
+
+ map.destroy();
+ }
+
+ function test_zoomIn(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ layers: [new OpenLayers.Layer(null, {isBaseLayer: true})],
+ zoomMethod: null
+ });
+ var control = new OpenLayers.Control.Zoom();
+ map.addControl(control);
+ map.setCenter([0, 0], 0);
+
+ t.eq(map.getZoom(), 0, "initial center");
+ map.events.triggerEvent("buttonclick", {buttonElement: control.zoomInLink});
+ t.eq(map.getZoom(), 1, "after zoom in");
+ map.destroy();
+ }
+
+ function test_zoomOut(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ layers: [new OpenLayers.Layer(null, {isBaseLayer: true})],
+ zoomMethod: null
+ });
+ var control = new OpenLayers.Control.Zoom();
+ map.addControl(control);
+ map.setCenter([0, 0], 1);
+
+ t.eq(map.getZoom(), 1, "initial center");
+ map.events.triggerEvent("buttonclick", {buttonElement: control.zoomOutLink});
+ t.eq(map.getZoom(), 0, "after zoom out");
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 512px; height: 256px;"/>
+ <div id="in">in</div><div id="out">out</out>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/ZoomBox.html b/misc/openlayers/tests/Control/ZoomBox.html
new file mode 100644
index 0000000..7763bcf
--- /dev/null
+++ b/misc/openlayers/tests/Control/ZoomBox.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(4);
+
+ var control = new OpenLayers.Control.ZoomBox();
+ t.ok(control instanceof OpenLayers.Control, "instance of Control");
+ t.ok(control instanceof OpenLayers.Control.ZoomBox, "instance of ZoomBox");
+ t.eq(control.displayClass, "olControlZoomBox", "displayClass");
+ control.destroy();
+
+ control = new OpenLayers.Control.ZoomBox({
+ zoomOnClick: false
+ });
+ t.eq(control.zoomOnClick, false, "zoomOnClick");
+ control.destroy();
+ }
+
+ function test_zoomBox(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map("map", {
+ zoomMethod: null,
+ layers: [new OpenLayers.Layer("", {isBaseLayer: true})],
+ center: [0, 0],
+ zoom: 1
+ });
+ var control = new OpenLayers.Control.ZoomBox();
+ map.addControl(control);
+ control.zoomBox(new OpenLayers.Pixel(50, 60));
+ t.eq(map.getZoom(), 2, "zoomed on click");
+
+ control.zoomOnClick = false;
+ control.zoomBox(new OpenLayers.Pixel(-50, -60));
+ t.eq(map.getZoom(), 2, "not zoomed with zoomOnClick set to false");
+
+ map.zoomToMaxExtent();
+ // pixel bounds bottom > top
+ control.zoomBox(new OpenLayers.Bounds(128, 128, 256, 64));
+ t.eq(map.getCenter().toShortString(), "-45, 22.5", "centered to box center");
+ t.eq(map.getZoom(), 3, "zoomed to box extent");
+
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 512px; height: 256px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Control/ZoomIn.html b/misc/openlayers/tests/Control/ZoomIn.html
new file mode 100644
index 0000000..844ded5
--- /dev/null
+++ b/misc/openlayers/tests/Control/ZoomIn.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+function test_ZoomIn_constructor (t) {
+ t.plan( 2 );
+
+ // setup
+ var control = new OpenLayers.Control.ZoomIn();
+
+ // tests
+ //
+ t.ok(
+ control instanceof OpenLayers.Control.ZoomIn,
+ "new OpenLayers.Control.ZoomIn returns object"
+ );
+ t.eq(
+ control.displayClass, "olControlZoomIn",
+ "displayClass is correct"
+ );
+
+ // tear down
+ control.destroy();
+}
+
+function test_ZoomIn_type (t) {
+ t.plan( 1 );
+
+ // setup
+ var control = new OpenLayers.Control.ZoomIn();
+
+ // tests
+ //
+ t.eq(
+ control.type,
+ OpenLayers.Control.TYPE_BUTTON,
+ "ZoomIn control is of type OpenLayers.Control.TYPE_BUTTON"
+ );
+
+ // tear down
+ control.destroy();
+}
+
+function test_ZoomIn_trigger (t) {
+ t.plan( 2 );
+
+ // set up
+ var control = new OpenLayers.Control.ZoomIn(),
+ zoomlevel = 5,
+ map = new OpenLayers.Map("map", {
+ allOverlays: true,
+ layers: [
+ new OpenLayers.Layer.Vector()
+ ],
+ center: new OpenLayers.LonLat(1,1),
+ zoom: zoomlevel
+ }),
+ oldZoom;
+
+ oldZoom = map.getZoom();
+
+ // tests
+ //
+ // trigger the control before it is being added,
+ // nothing should change
+ control.trigger();
+
+ t.eq(
+ oldZoom,
+ zoomlevel,
+ 'Calling trigger on a non added control doesn\'t do anything ' +
+ '(map zoom is ' + oldZoom + ').'
+ );
+
+ // now lets add the control
+ map.addControl(control);
+
+ // trigger it again, now the map should have a different extent
+ control.trigger();
+
+ t.eq(
+ map.getZoom(),
+ zoomlevel + 1,
+ 'Calling trigger on a added control changes the map zoom ' +
+ '(map zoom was ' + zoomlevel +
+ ' and is now ' + map.getZoom() + ').'
+ );
+
+ // tear down
+ control.destroy();
+ map.destroy();
+}
+
+ </script>
+ </head>
+ <body>
+ <div id="map" style="width: 1000px; height: 1000px;"></div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/Control/ZoomOut.html b/misc/openlayers/tests/Control/ZoomOut.html
new file mode 100644
index 0000000..5345c55
--- /dev/null
+++ b/misc/openlayers/tests/Control/ZoomOut.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+function test_ZoomOut_constructor (t) {
+ t.plan( 2 );
+
+ // setup
+ var control = new OpenLayers.Control.ZoomOut();
+
+ // tests
+ //
+ t.ok(
+ control instanceof OpenLayers.Control.ZoomOut,
+ "new OpenLayers.Control.ZoomOut returns object"
+ );
+ t.eq(
+ control.displayClass, "olControlZoomOut",
+ "displayClass is correct"
+ );
+
+ // tear down
+ control.destroy();
+}
+
+function test_ZoomOut_type(t){
+ t.plan( 1 );
+
+ // setup
+ var control = new OpenLayers.Control.ZoomOut();
+
+ // check that the type of the control equals OpenLayers.Control.TYPE_BUTTON
+ t.eq(
+ control.type,
+ OpenLayers.Control.TYPE_BUTTON,
+ 'ZoomOut-control is of type "OpenLayers.Control.TYPE_BUTTON".'
+ );
+
+ // tear down
+ control.destroy();
+}
+
+function test_ZoomOut_trigger (t) {
+ t.plan( 2 );
+
+ // set up
+ var control = new OpenLayers.Control.ZoomOut(),
+ zoomlevel = 5,
+ map = new OpenLayers.Map("map", {
+ allOverlays: true,
+ layers: [
+ new OpenLayers.Layer.Vector()
+ ],
+ center: new OpenLayers.LonLat(1,1),
+ zoom: zoomlevel
+ }),
+ oldZoom;
+
+ oldZoom = map.getZoom();
+
+ // tests
+ //
+ // trigger the control before it is being added,
+ // nothing should change
+ control.trigger();
+
+ t.eq(
+ oldZoom,
+ zoomlevel,
+ 'Calling trigger on a non added control doesn\'t do anything ' +
+ '(map zoom is ' + oldZoom + ').'
+ );
+
+ // now lets add the control
+ map.addControl(control);
+
+ // trigger it again, now the map should have a different extent
+ control.trigger();
+
+ t.eq(
+ map.getZoom(),
+ zoomlevel - 1,
+ 'Calling trigger on a added control changes the map zoom ' +
+ '(map zoom was ' + zoomlevel +
+ ' and is now ' + map.getZoom() + ').'
+ );
+
+ // tear down
+ control.destroy();
+ map.destroy();
+}
+
+ </script>
+ </head>
+ <body>
+ <div id="map" style="width: 1000px; height: 1000px;"></div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/Control/ZoomToMaxExtent.html b/misc/openlayers/tests/Control/ZoomToMaxExtent.html
new file mode 100644
index 0000000..8ed5512
--- /dev/null
+++ b/misc/openlayers/tests/Control/ZoomToMaxExtent.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+function test_ZoomToMaxExtent_constructor (t) {
+ t.plan( 2 );
+
+ // setup
+ var control = new OpenLayers.Control.ZoomToMaxExtent();
+
+ // tests
+ //
+ t.ok(
+ control instanceof OpenLayers.Control.ZoomToMaxExtent,
+ "new OpenLayers.Control.ZoomToMaxExtent returns object"
+ );
+ t.eq(
+ control.displayClass, "olControlZoomToMaxExtent",
+ "displayClass is correct"
+ );
+
+ // tear down
+ control.destroy();
+}
+
+function test_ZoomToMaxExtent_type (t) {
+ t.plan( 1 );
+
+ // setup
+ var control = new OpenLayers.Control.ZoomToMaxExtent();
+
+ // check that the type of the control equals OpenLayers.Control.TYPE_BUTTON
+ t.eq(
+ control.type,
+ OpenLayers.Control.TYPE_BUTTON,
+ 'ZoomToMaxExtent-control is of type "OpenLayers.Control.TYPE_BUTTON".'
+ );
+
+ // tear down
+ control.destroy();
+}
+
+function test_ZoomToMaxExtent_trigger (t) {
+ t.plan( 2 );
+
+ // set up
+ var mapsMaxExtent = new OpenLayers.Bounds(0, 0, 45, 45),
+ mapsInitialExtent = new OpenLayers.Bounds(5, 5, 7, 7),
+ control = new OpenLayers.Control.ZoomToMaxExtent(),
+ map = new OpenLayers.Map("map", {
+ maxExtent: mapsMaxExtent,
+ allOverlays: true,
+ fractionalZoom: true,
+ layers: [
+ new OpenLayers.Layer.Vector()
+ ]
+ }),
+ oldExtent;
+
+ map.zoomToExtent(mapsInitialExtent);
+
+ oldExtent = map.getExtent().toString();
+
+ // tests
+ //
+ // trigger the control before it is being added,
+ // nothing should change
+ control.trigger();
+ t.eq(
+ oldExtent,
+ map.getExtent().toString(),
+ 'Calling trigger on a non added control doesn\'t do anything ' +
+ '(map extent is "' + oldExtent + '").'
+ );
+
+ // now lets add the control
+ map. addControl(control);
+
+ // trigger it again, now the map should have a different extent
+ control.trigger();
+
+ t.eq(
+ map.getExtent().toString(),
+ mapsMaxExtent.toString(),
+ 'Calling trigger on a added control changes the map extent ' +
+ '(map extent was "' + oldExtent + '"' +
+ ' and is now "' + mapsMaxExtent.toString() + '").'
+ );
+
+ // tear down
+ control.destroy();
+ map.destroy();
+}
+
+ </script>
+ </head>
+ <body>
+ <div id="map" style="width: 1000px; height: 1000px;"></div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/Events.html b/misc/openlayers/tests/Events.html
new file mode 100644
index 0000000..03c540c
--- /dev/null
+++ b/misc/openlayers/tests/Events.html
@@ -0,0 +1,487 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var map;
+ var a;
+
+ function test_Events_constructor (t) {
+ t.plan(4);
+
+ var mapDiv = OpenLayers.Util.getElement('map');
+ var obj = {result: 0};
+
+ events = new OpenLayers.Events(obj, mapDiv);
+ t.ok( events instanceof OpenLayers.Events, "new OpenLayers.Control returns object" );
+ t.ok(events.object ==obj, " 'object' property correctly set");
+ t.ok(events.element == mapDiv, " 'element' property correctly set");
+ events.destroy();
+
+ // default/nulls
+ events = new OpenLayers.Events(null, null, null);
+ t.ok( events.listeners != null,
+ "init of Events with null object/element/eventTypes still creates listeners array" );
+ events.destroy();
+ }
+
+ function test_Events_register(t){
+ t.plan(4);
+
+ var ev = {
+ 'object': {},
+ 'extensionCount': {
+ 'listenerA': 0,
+ 'listenerB': 0
+ },
+ 'listeners': {
+ 'listenerA': {
+ 'push': function(options){
+ gObjA = options.obj;
+ gFuncA = options.func;
+ }
+ },
+ 'listenerB': {
+ 'push': function(options){
+ gObjB = options.obj;
+ gFuncB = options.func;
+ }
+ }
+ }
+ };
+
+ var type = null;
+ var object = null;
+ var func = null;
+
+ //func null
+ gObjA = null; gFuncA = null; gObjB = null; gFuncB = null;
+ OpenLayers.Events.prototype.register.apply(ev, [type, object, func]);
+ t.ok((gObjA == null) && (gFuncA == null) &&
+ (gObjB == null) && (gFuncB == null), "no push called func null");
+
+ //valid func, type not in ev.eventTypes
+ func = function() {};
+ gObjA = null; gFuncA = null; gObjB = null; gFuncB = null;
+ OpenLayers.Events.prototype.register.apply(ev, [type, object, func]);
+ t.ok((gObjA == null) && (gFuncA == null) &&
+ (gObjB == null) && (gFuncB == null), "no push called func null");
+
+ //valid func, type in ev.eventTypes, null obj
+ type = 'listenerA';
+ gObjA = null; gFuncA = null; gObjB = null; gFuncB = null;
+ OpenLayers.Events.prototype.register.apply(ev, [type, object, func]);
+ t.ok((gObjA == ev.object) && (gFuncA == func) &&
+ (gObjB == null) && (gFuncB == null), "push called on listenerA's mock array when type passed in 'listenerA'. events.object taken since obj is null.");
+
+ //valid func, type in ev.eventTypes, valid obj
+ type = 'listenerB';
+ object = {};
+ gObjA = null; gFuncA = null; gObjB = null; gFuncB = null;
+ OpenLayers.Events.prototype.register.apply(ev, [type, object, func]);
+ t.ok((gObjA == null) && (gFuncA == null) &&
+ (gObjB == object) && (gFuncB == func), "push called on listenerB's mock array when type passed in 'listenerB'.");
+
+ }
+
+ function test_Events_register_unregister(t) {
+
+ t.plan(20);
+
+ var mapDiv = OpenLayers.Util.getElement('map');
+ var obj = {result: 0};
+
+ events = new OpenLayers.Events(obj, mapDiv);
+
+ var func = function () { this.result++ }
+ events.register( "doThingA", obj, func );
+
+ var listenerList = events.listeners["doThingA"];
+ t.eq( listenerList.length, 1, "register correctly adds to event.listeners" );
+ t.ok( listenerList[0].obj == obj, "obj property correctly registered");
+ t.ok( listenerList[0].func == func, "func property correctly registered");
+
+ var func2 = function () { this.result-- }
+ events.register( "doThingA", obj, func2 );
+
+ var listenerList = events.listeners["doThingA"];
+ t.eq( listenerList.length, 2, "register correctly appends new callback to event.listeners[doThingA]" );
+ t.ok( listenerList[1].obj == obj, "obj property correctly registered");
+ t.ok( listenerList[1].func == func2, "func property correctly registered");
+
+ var func3 = function () { this.result = this.result + 3; }
+ events.register( "doThingA", null, func3 );
+
+ var listenerList = events.listeners["doThingA"];
+ t.eq( listenerList.length, 3, "register correctly appends new callback to event.listeners[doThingA] even when obj passed in is null" );
+ t.ok( listenerList[2].obj == obj, "obj is correctly set to Events.object default when null is passed in.");
+ t.ok( listenerList[2].func == func3, "func property correctly registered");
+
+ events.register( "doThingA", obj, null);
+
+ var listenerList = events.listeners["doThingA"];
+ t.eq( listenerList.length, 3, "register correctly does not append null callback to event.listeners[doThingA] even when obj passed in is null" );
+
+ events.register("chicken", obj, func);
+ t.eq(events.listeners["chicken"].length, 1, "register() allows listeners for any named event");
+
+ events.unregister("chicken", obj, func);
+ t.eq(events.listeners["chicken"].length, 0, "unregistering an event that is not in eventTypes list works")
+
+ events.unregister("doThingA", obj, null);
+ var listenerList = events.listeners["doThingA"];
+ t.eq( listenerList.length, 3, "trying to unregister a null callback does nothing -- but doesnt crash :-)" );
+
+ events.unregister("doThingA", obj, func);
+ var found = false;
+ for (var i = 0; i < listenerList.length; i++) {
+ var listener = listenerList[i];
+ if (listener.obj == obj && listener.func == func) {
+ found = true;
+ }
+ }
+ t.ok( (listenerList.length == 2) && !found, "unregister correctly removes callback" );
+
+ events.unregister("doThingA", null, func3);
+ var found = false;
+ for (var i = 0; i < listenerList.length; i++) {
+ var listener = listenerList[i];
+ if (listener.obj == obj && listener.func == func) {
+ found = true;
+ }
+ }
+ t.ok( (listenerList.length == 1) && !found, "unregister correctly removes callback when no obj specified" );
+
+ var func4 = function () { this.result = "chicken"; }
+ events.unregister("doThingA", obj, func4);
+ t.ok( (listenerList.length == 1), "unregister does not bomb if you try to remove an unregistered callback" );
+
+ var obj2 = { chicken: 151 };
+ events.unregister("doThingA", obj2, func2);
+ t.ok( (listenerList.length == 1), "unregister does not bomb or accidntally remove if you try to remove a valid callback on a valid event type, but with the wrong context object" );
+
+ events.unregister("doThingA", obj, null);
+ t.ok( (listenerList.length == 1), "unregister does not bomb if you try to remove a null callback" );
+
+ try {
+ events.unregister("asdf", obj, func);
+ t.ok("unregistering for an event with no registered listeners works");
+ } catch (err) {
+ t.fail("unregistering for an event with no registered listeners causes trouble: " + err);
+ }
+
+ events.register("buttonclick", obj, func);
+ t.ok(events.extensions.buttonclick, "buttonclick extension registered");
+
+ }
+
+ function test_Events_remove(t) {
+
+ t.plan( 2 );
+
+ var mapDiv = OpenLayers.Util.getElement('map');
+ var obj = {result: 0};
+
+ events = new OpenLayers.Events(obj, mapDiv);
+
+ var func = function () { this.result++ }
+ var func2 = function () { this.result-- }
+ var func3 = function () { this.result = this.result + 3; }
+
+ events.register( "doThingA", obj, func );
+ events.register( "doThingA", obj, func2 );
+ events.register( "doThingA", null, func3 );
+
+ events.remove("doThingA");
+
+ t.eq( events.listeners["doThingA"].length, 0, "remove() correctly clears the event listeners" );
+
+ events.remove("chicken");
+ t.ok( events.listeners["chicken"] == null, "remove on non-enabled event does not break or accidentally enable the event");
+
+ }
+
+ function test_Events_triggerEvent(t) {
+
+ t.plan(13);
+
+ var mapDiv = OpenLayers.Util.getElement('map');
+ var obj = {result: 0};
+
+ events = new OpenLayers.Events(obj, mapDiv);
+
+
+ var func = function () { this.result++ }
+ events.register( "doThingA", obj, func );
+
+ events.triggerEvent("doThingA", {});
+ t.eq( obj.result, 1, "result is 1 after we call triggerEvent" );
+ events.triggerEvent("doThingA");
+ t.eq( obj.result, 2, "result is 2 after we call triggerEvent with no event" );
+
+ var funcB = function() { this.FUNK = "B"; return false; }
+ events.register( "doThingA", obj, funcB);
+
+ events.triggerEvent("doThingA");
+ t.ok ((obj.result == 3) && (obj.FUNK == "B"), "executing multiple callbacks works")
+
+ var funcZ = function() { this.FUNK = "Z"; }
+ events.register( "doThingA", obj, funcZ);
+
+ events.triggerEvent("doThingA");
+ t.ok ((obj.result == 4) && (obj.FUNK == "B"), "executing multiple callbacks works, and when one returns false, it stops chain correctly")
+
+
+ var func2 = function() { this.result = this.result + 10; }
+ events.register( "doThingB", null, func2);
+
+ events.triggerEvent("doThingB");
+ t.eq( obj.result, 14, "passing a null object default gets set correctly");
+
+ //no specific t.ok for this one, but if it breaks, you will know it.
+ events.triggerEvent("chicken");
+
+ events = new OpenLayers.Events(null, mapDiv);
+
+ // a is global variable (context-irrelevant)
+ a = 0;
+ var func = function () { a = 5; }
+ events.register( "doThingC", null, func );
+ events.triggerEvent("doThingC");
+
+ t.eq(a, 5, "if Events has no object set and an event is registered also with no object, triggerEvent() calls it without trying to set the context to null");
+
+ // trigger events with additional arguments
+ events = new OpenLayers.Events();
+ var instance = {id: Math.random()};
+ var listener = function(obj) {
+ t.eq(this.id, instance.id,
+ "listener called with proper scope");
+ t.eq(arguments.length, 1,
+ "listener called with a single argument");
+ t.eq(typeof arguments, "object",
+ "listener called with an object");
+ t.eq(obj.foo, evt.foo,
+ "foo property set on the layer");
+ };
+ events.register("something", instance, listener);
+ var evt = {
+ id: Math.random(),
+ "foo": "bar"
+ };
+ events.triggerEvent("something", evt);
+ events.unregister("something", instance, listener);
+
+ // test return from triggerEvent
+ var listener1 = function() {
+ return "foo";
+ }
+ var listener2 = function() {
+ return false;
+ }
+ var listener3 = function() {
+ t.fail("never call me again!");
+ }
+ events.register("something", instance, listener1);
+ var ret = events.triggerEvent("something", evt);
+ t.eq(ret, "foo", "correct return from single listener");
+
+ events.register("something", instance, listener2);
+ ret = events.triggerEvent("something", evt);
+ t.eq(ret, false, "correct return for two listeners");
+
+ events.register("something", instance, listener3);
+ ret = events.triggerEvent("something", evt);
+ t.eq(ret, false, "correct return for three listeners where second cancels");
+
+ events.unregister("something", instance, listener1);
+ events.unregister("something", instance, listener2);
+ events.unregister("something", instance, listener3);
+ }
+
+ function test_Events_handleBrowserEvent(t) {
+ t.plan(8);
+ var events = new OpenLayers.Events({}, null);
+ events.on({'sometouchevent': function() {}});
+
+ // this test verifies that when handling a touch event we correctly
+ // set clientX and clientY in the event object
+ var evt = {type: 'sometouchevent',
+ touches: [{clientX: 1, clientY: 1}, {clientX: 2, clientY: 2}]
+ };
+ events.handleBrowserEvent(evt);
+ t.eq(evt.clientX, 1.5, "evt.clientX value is correct");
+ t.eq(evt.clientY, 1.5, "evt.clientY value is correct");
+
+ // test bug where clientX/clientY includes scroll offset
+ window.olMockWin = {
+ pageXOffset: 10,
+ pageYOffset: 20
+ };
+ evt = {type: 'sometouchevent',
+ touches: [{
+ clientX: 11,
+ clientY: 21,
+ pageX: 0,
+ pageY: 0
+ }]
+ };
+ events.handleBrowserEvent(evt);
+ t.eq(evt.clientX, 1, "evt.clientX value is correct");
+ t.eq(evt.clientY, 1, "evt.clientY value is correct");
+
+
+ // test bug where clientX/clientY have negative values
+ evt = {
+ type: 'sometouchevent',
+ touches: [{
+ clientX: -412,
+ clientY: -1005,
+ pageX: 11,
+ pageY: 21
+ }]
+ };
+ events.handleBrowserEvent(evt);
+ t.eq(evt.clientX, 1, "evt.clientX value is correct");
+ t.eq(evt.clientY, 1, "evt.clientY value is correct");
+
+ window.olMockWin = {
+ pageXOffset: 11,
+ pageYOffset: 299
+ };
+ evt = {
+ type: 'sometouchevent',
+ touches: [{
+ clientX: 223,
+ clientY: 119,
+ pageX: 242,
+ pageY: 623
+ }]
+ };
+ events.handleBrowserEvent(evt);
+ t.eq(evt.clientX, 231, "evt.clientX value is correct");
+ t.eq(evt.clientY, 324, "evt.clientY value is correct");
+
+ window.olMockWin = undefined;
+ }
+
+ function test_Events_attachToElement(t) {
+ t.plan(3);
+ var events = new OpenLayers.Events({}, null);
+ var element = document.createElement("div");
+ events.attachToElement(element);
+ t.ok(events.eventHandler, "eventHandler method bound");
+ t.ok(events.clearMouseListener, "clearMouseListener method bound");
+ t.ok(events.element === element, "element set");
+ }
+
+ function test_Events_destroy (t) {
+ t.plan(2);
+
+ var div = OpenLayers.Util.getElement('test');
+ var obj = {};
+ var events = new OpenLayers.Events(obj, div);
+
+ // +1 because of blocking dragstart in attachToElement()
+ t.eq(OpenLayers.Event.observers[div._eventCacheID].length,
+ OpenLayers.Events.prototype.BROWSER_EVENTS.length + 1,
+ "construction creates new arrayin hash, registers appropriate events");
+
+ events.destroy();
+ events = null;
+ t.eq(OpenLayers.Event.observers["test"], null,
+ "destruction removes the event observer from hash");
+ }
+
+ function test_Event(t) {
+ t.plan(24);
+
+ var div = OpenLayers.Util.getElement('test');
+ var name = "mouseover";
+ var func = function() {};
+
+ //1st elem 1st listener
+ OpenLayers.Event.observe(div, name, func);
+
+ var cacheID = div._eventCacheID;
+ t.ok(cacheID, "element given new cache id");
+
+ var elementObservers = OpenLayers.Event.observers[cacheID];
+
+ t.ok(elementObservers, "new cache bucket made for event");
+ t.eq(elementObservers.length, 1, "one listener registered");
+
+ var listener = elementObservers[0];
+
+ t.ok(listener.element == div, "element registered");
+ t.eq(listener.name, name, "name registered");
+ t.ok(listener.observer == func, "function registered");
+ t.eq(listener.useCapture, false, "useCapture defaults to false");
+
+ //1st elem 2nd listener
+ name = "mouseout";
+ var newFunc = function() {};
+
+ OpenLayers.Event.observe(div, name, newFunc, true);
+ var newCacheID = div._eventCacheID;
+ t.eq(newCacheID, cacheID, "element's cache id not overridden");
+
+ t.eq(elementObservers.length, 2, "listener added to existing bucket");
+
+ var listener = elementObservers[1];
+
+ t.ok(listener.element == div, "element registered");
+ t.eq(listener.name, name, "name registered");
+ t.ok(listener.observer == newFunc, "function registered");
+ t.eq(listener.useCapture, true, "useCapture correctly registered");
+
+ //2st elem 1st listener
+ div = OpenLayers.Util.getElement('test2');
+ OpenLayers.Event.observe(div, name, func);
+
+ var cacheID = div._eventCacheID;
+ t.ok(cacheID, "new element given new cache id");
+ t.ok(cacheID != newCacheID, "new cache id is unique");
+
+ elementObservers = OpenLayers.Event.observers[cacheID];
+
+ t.ok(elementObservers, "new cache bucket made for event");
+ t.eq(elementObservers.length, 1, "one listener registered");
+
+ var listener = elementObservers[0];
+
+ t.ok(listener.element == div, "element registered");
+ t.eq(listener.name, name, "name registered");
+ t.ok(listener.observer == func, "function registered");
+ t.eq(listener.useCapture, false, "useCapture defaults to false");
+
+ //stopObservingElement by element
+ OpenLayers.Event.stopObservingElement(div);
+ elementObservers = OpenLayers.Event.observers[cacheID];
+ t.ok(elementObservers == null, "stopObservingElement by elem works");
+
+ //stopObservingElement by id
+ OpenLayers.Event.stopObservingElement("test");
+ elementObservers = OpenLayers.Event.observers[newCacheID];
+ t.ok(elementObservers == null, "stopObservingElement by id works");
+
+
+ //unloadCache by element
+ OpenLayers.Event.observe(div, name, func);
+
+ OpenLayers.Event.unloadCache();
+
+ elementObservers = OpenLayers.Event.observers[cacheID];
+ t.ok(elementObservers == null, "stopObservingElement by elem works");
+
+
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1024px; height: 512px;"/>
+ <div id="test"></div>
+ <div id="test2"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Events/buttonclick.html b/misc/openlayers/tests/Events/buttonclick.html
new file mode 100644
index 0000000..dadbd3a
--- /dev/null
+++ b/misc/openlayers/tests/Events/buttonclick.html
@@ -0,0 +1,214 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var log, buttonClick, events, element, button;
+ function init() {
+ element = document.getElementById("map");
+ button = document.getElementById("button");
+ }
+ function trigger(evt) {
+ OpenLayers.Util.applyDefaults(evt, {
+ button: 1,
+ target: button
+ });
+ events.handleBrowserEvent(evt);
+ }
+ function logEvent(evt) {
+ log.push(evt);
+ }
+
+ function test_ButtonClick(t) {
+ t.plan(1);
+ events = new OpenLayers.Events({}, element);
+ buttonClick = new OpenLayers.Events.buttonclick(events);
+ t.ok(buttonClick.target === events, "target set from constructor arg");
+ buttonClick.destroy();
+ events.destroy();
+ }
+
+ function test_getPressedButton(t) {
+ t.plan(4);
+
+ // set up
+
+ events = new OpenLayers.Events({}, element);
+ buttonClick = new OpenLayers.Events.buttonclick(events);
+
+ var button = document.createElement('button'),
+ span1 = document.createElement('span'),
+ span2 = document.createElement('span'),
+ span3 = document.createElement('span');
+ button.className = 'olButton';
+ button.appendChild(span1);
+ span1.appendChild(span2);
+ span2.appendChild(span3);
+
+ t.ok(buttonClick.getPressedButton(button) === button,
+ 'getPressedButton returns button when element is button');
+ t.ok(buttonClick.getPressedButton(span1) === button,
+ 'getPressedButton returns button when element is button descendant level 1');
+ t.ok(buttonClick.getPressedButton(span2) === button,
+ 'getPressedButton returns button when element is button descendant level 2');
+ t.eq(buttonClick.getPressedButton(span3), undefined,
+ 'getPressedButton returns undefined when element is button descendant level 3');
+
+ // test
+
+
+ // tear down
+
+ buttonClick.destroy();
+ events.destroy();
+ }
+
+ function test_ignore(t) {
+ t.plan(5);
+
+ // set up
+
+ events = new OpenLayers.Events({}, element);
+ buttonClick = new OpenLayers.Events.buttonclick(events);
+
+ var link = document.createElement('a'),
+ span1 = document.createElement('span'),
+ span2 = document.createElement('span'),
+ span3 = document.createElement('span');
+ link.appendChild(span1);
+ span1.appendChild(span2);
+ span2.appendChild(span3);
+
+ t.eq(buttonClick.ignore(link), true,
+ 'ignore returns true when element is a link');
+ t.eq(buttonClick.ignore(span1), true,
+ 'ignore returns true when element is link descendant level 1');
+ t.eq(buttonClick.ignore(span2), true,
+ 'ignore returns true when element is link descendant level 2');
+ t.eq(buttonClick.ignore(span3), false,
+ 'ignore returns false when element is link descendant level 3');
+ t.eq(buttonClick.ignore(element), false,
+ 'ignore returns false when element is not a link');
+
+
+ // tear down
+
+ buttonClick.destroy();
+ events.destroy();
+ }
+
+ function test_ButtonClick_buttonClick(t) {
+ t.plan(27);
+ events = new OpenLayers.Events({}, element);
+ events.on({
+ "buttonclick": logEvent,
+ "mousedown": logEvent,
+ "mouseup": logEvent,
+ "click": logEvent,
+ "dblclick": logEvent,
+ "touchstart": logEvent,
+ "touchend": logEvent,
+ "keydown": logEvent
+ });
+ buttonClick = events.extensions["buttonclick"];
+
+ // a complete click
+ log = [];
+ trigger({type: "mousedown"});
+ trigger({type: "mouseup"});
+ t.eq(log.length, 1, "one event fired for mousedown-mouseup");
+ t.eq(log[0].type, "buttonclick", "buttonclick event fired");
+
+ // a complete tap
+ log = [];
+ trigger({type: "touchstart"});
+ trigger({type: "touchend"});
+ t.eq(log.length, 1, "one event fired for touchstart-touchend");
+ t.eq(log[0].type, "buttonclick", "buttonclick event fired");
+
+ // mouse sequence started on button
+ log = [];
+ trigger({type: "mousedown"});
+ trigger({type: "mouseup", target: element});
+ t.eq(log.length, 1, "one event fired for mousedown-(leave)-mouseup");
+ t.eq(log[0].type, "mouseup", "mouseup event goes through when sequence not finished on button");
+
+ // touch sequence started on button
+ log = [];
+ trigger({type: "touchstart"});
+ trigger({type: "touchmove"});
+ trigger({type: "touchend"});
+ t.eq(log.length, 1, "one event fired for touchstart-(leave)-touchend");
+ t.eq(log[0].type, "touchend", "touchend event goes through when sequence not finished on button");
+
+ // mouse sequence finished on button
+ log = [];
+ trigger({type: "mousedown", target: element});
+ trigger({type: "mouseup"});
+ t.eq(log.length, 2, "two event fired for mousedown-(enter)-mouseup");
+ t.eq(log[0].type, "mousedown", "mousedown unrelated to button goes through");
+ t.eq(log[1].type, "mouseup", "mouseup goes through when sequence started outside button");
+
+ // touch sequence finished on button
+ log = [];
+ trigger({type: "touchstart", target: element});
+ trigger({type: "touchend"});
+ t.eq(log.length, 2, "two event fired for touchstart-(enter)-touchend");
+ t.eq(log[0].type, "touchstart", "touchstart unrelated to button goes through");
+ t.eq(log[1].type, "touchend", "touchend goes through when sequence started outside button");
+
+ // dblclick
+ log = [];
+ trigger({type: "mousedown"});
+ trigger({type: "mouseup"});
+ trigger({type: "click"});
+ trigger({type: "mousedown"});
+ trigger({type: "mouseup"});
+ trigger({type: "click"});
+ trigger({type: "dblclick"});
+ t.eq(log.length, 2, "two events fired for doubleclick");
+ t.eq(log[0].type, "buttonclick", "buttonclick for 1st click");
+ t.eq(log[1].type, "buttonclick", "buttonclick for 2nd click");
+
+ // dblclick - IE
+ log = [];
+ trigger({type: "mousedown"});
+ trigger({type: "mouseup"});
+ trigger({type: "mouseup"});
+ trigger({type: "dblclick"});
+ t.eq(log.length, 2, "two events fired for dblclick in IE");
+ t.eq(log[0].type, "buttonclick", "buttonclick for 1st click in IE");
+ t.eq(log[1].type, "buttonclick", "buttonclick for 2nd click IE");
+
+ // rightclick
+ log = [];
+ trigger({type: "mousedown", button: 2});
+ trigger({type: "mouseup", button: 2});
+ t.eq(log.length, 2, "two events fired for rightclick");
+ t.eq(log[0].type, "mousedown", "mousedown from rightclick goes through");
+ t.eq(log[1].type, "mouseup", "mouseup from rightclick goes through");
+
+ // keydown RETURN
+ log = [];
+ trigger({type: "keydown", keyCode: OpenLayers.Event.KEY_RETURN});
+ trigger({type: "click"});
+ t.eq(log.length, 1, "one event fired for RETURN keydown");
+ t.eq(log[0].type, "buttonclick", "buttonclick for RETURN keydown");
+
+ // keydown SPACE
+ log = [];
+ trigger({type: "keydown", keyCode: OpenLayers.Event.KEY_SPACE});
+ trigger({type: "click"});
+ t.eq(log.length, 1, "one event fired for SPACE keydown");
+ t.eq(log[0].type, "buttonclick", "buttonclick for SPACE keydown");
+ }
+ </script>
+</head>
+<body onload="init()">
+ <div id="map" style="width: 600px; height: 300px;">
+ <div id="button" class="olButton">
+ <img class="olAlphaImg">
+ </div>
+ </div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Events/featureclick.html b/misc/openlayers/tests/Events/featureclick.html
new file mode 100644
index 0000000..ae111b7
--- /dev/null
+++ b/misc/openlayers/tests/Events/featureclick.html
@@ -0,0 +1,91 @@
+<html>
+<head>
+<script src="../OLLoader.js"></script>
+<script type="text/javascript">
+
+var layer1, style, logevt, lognoevt, map, lonlat, pixel, element;
+
+function init() {
+
+ element = document.getElementById("map");
+
+ style = new OpenLayers.StyleMap({
+ 'default': OpenLayers.Util.applyDefaults(
+ {label: "${l}", pointRadius: 30},
+ OpenLayers.Feature.Vector.style["default"]
+ ),
+ 'select': OpenLayers.Util.applyDefaults(
+ {pointRadius: 30},
+ OpenLayers.Feature.Vector.style.select
+ )
+ });
+
+ layer1 = new OpenLayers.Layer.Vector("Layer 1", {
+ styleMap: style
+ });
+
+ layer1.addFeatures([
+ new OpenLayers.Feature.Vector(OpenLayers.Geometry.fromWKT("POINT(0 0)"), {l:1}),
+ new OpenLayers.Feature.Vector(OpenLayers.Geometry.fromWKT("POINT(0 0)"), {l:1}),
+ new OpenLayers.Feature.Vector(OpenLayers.Geometry.fromWKT("POINT(0 0)"), {l:1}),
+ new OpenLayers.Feature.Vector(OpenLayers.Geometry.fromWKT("POINT(0 0)"), {l:1})
+ ]);
+
+ map = new OpenLayers.Map({
+ div: "map",
+ allOverlays: true,
+ layers: [layer1],
+ zoom: 6,
+ center: [0, 0],
+ eventListeners: {
+ featureclick: logEvent,
+ nofeatureclick: logNoEvent
+ }
+ });
+}
+
+function logNoEvent(e) {
+ lognoevt.push(e);
+}
+
+function logEvent(e) {
+ logevt.push(e);
+}
+
+function trigger(type, pxl) {
+ var map_position = OpenLayers.Util.pagePosition(element);
+ map.events.triggerEvent(type, {
+ xy: pxl,
+ clientX: pxl.x + map_position[0],
+ clientY: pxl.y + map_position[1],
+ which: 1 // which == 1 means left-click
+ });
+}
+
+// TESTS
+
+function test_onClick(t) {
+ t.plan(2);
+ logevt = [];
+ lognoevt = [];
+ lonlat = new OpenLayers.LonLat(0,0);
+ pixel = map.getPixelFromLonLat(lonlat);
+
+ trigger('mousedown', pixel);
+ trigger('mouseup', pixel);
+
+ t.eq(logevt.length, 4, "4 features hit");
+
+ trigger('mousedown', {x: 40, y: 40});
+ trigger('mouseup', {x: 40, y: 40});
+ t.eq(lognoevt.length, 1, "nofeatureclick fired for click outside features.");
+}
+
+// END TESTS
+
+</script>
+</head>
+<body onload="init()">
+<div id="map" style="width: 300px; height: 150px; border: 1px solid black"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Extras.html b/misc/openlayers/tests/Extras.html
new file mode 100644
index 0000000..264eb0f
--- /dev/null
+++ b/misc/openlayers/tests/Extras.html
@@ -0,0 +1,21 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var map;
+
+ // Ensure that we continue to work if silly Javascript frameworks
+ // extend object.
+ Object.prototype.foo = function() { }
+ function test_Events_Object_Extension(t) {
+ t.plan(1)
+ map = new OpenLayers.Map("map");
+ t.ok(true, "Map created if object prototype is extended.");
+ }
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 600px; height: 300px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Feature.html b/misc/openlayers/tests/Feature.html
new file mode 100644
index 0000000..aa3db24
--- /dev/null
+++ b/misc/openlayers/tests/Feature.html
@@ -0,0 +1,205 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var map;
+ var feature, layer;
+
+ function test_Feature_constructor (t) {
+ t.plan( 6 );
+
+ var layer = {};
+ var lonlat = new OpenLayers.LonLat(2,1);
+ var iconURL = 'http://boston.openguides.org/features/ORANGE.png';
+ var iconSize = new OpenLayers.Size(12, 17);
+ var data = { iconURL: iconURL,
+ iconSize: iconSize
+ };
+
+ feature = new OpenLayers.Feature(layer, lonlat, data);
+
+ t.ok( feature instanceof OpenLayers.Feature, "new OpenLayers.Feature returns Feature object" );
+ t.eq( feature.layer, layer, "feature.layer set correctly" );
+ t.ok(OpenLayers.String.startsWith(feature.id, "OpenLayers_Feature_"),
+ "feature.id set correctly");
+ t.ok( feature.lonlat.equals(lonlat), "feature.lonlat set correctly" );
+ t.eq( feature.data.iconURL, iconURL, "feature.data.iconURL set correctly" );
+ t.ok( feature.data.iconSize.equals(iconSize), "feature.data.iconSize set correctly" );
+ }
+
+ function test_Feature_createPopup (t) {
+ t.plan(3);
+ var layer = {};
+ var lonlat = new OpenLayers.LonLat(2,1);
+ var iconURL = 'http://boston.openguides.org/features/ORANGE.png';
+ var iconSize = new OpenLayers.Size(12, 17);
+ var data = { iconURL: iconURL,
+ iconSize: iconSize,
+ 'overflow':'auto'
+ };
+
+ feature = new OpenLayers.Feature(layer, lonlat, data);
+ popup = feature.createPopup();
+ //Safari 3 separates style overflow into overflow-x and overflow-y
+ var prop = (OpenLayers.BROWSER_NAME == 'safari') ? 'overflowX' : 'overflow';
+ t.eq(popup.contentDiv.style[prop], "auto", 'overflow on popup is correct');
+ t.ok( popup instanceof OpenLayers.Popup.Anchored, "popup is a Popup.Anchored by default");
+ feature.destroyPopup();
+
+ feature.popupClass = OpenLayers.Popup.FramedCloud;
+ popup = feature.createPopup();
+ t.ok( popup instanceof OpenLayers.Popup.FramedCloud, "setting feature.popupClass works");
+ }
+ function test_Feature_createMarker (t) {
+ t.plan(1);
+ t.ok(true);
+/*
+
+ t.plan( 11 );
+ feature = new OpenLayers.Feature("myfeature", new OpenLayers.LonLat(2,1),
+ {
+ iconURL:'http://boston.openguides.org/features/ORANGE.png',
+ iconW: 12,
+ iconH: 17
+ });
+ layer = new OpenLayers.Layer.Markers('Marker Layer');
+ t.ok( feature instanceof OpenLayers.Feature, "new OpenLayers.Feature returns Feature object" );
+ t.ok( layer instanceof OpenLayers.Layer.Markers, "Layer is a marker layer" );
+ feature.createMarker(layer);
+
+ t.ok( feature.marker instanceof OpenLayers.Marker,
+ "createMarker sets a marker property to a marker" );
+ t.ok( layer.markers[0] === feature.marker,
+ "First marker in layer is the feature marker" );
+
+ t.ok( feature.marker.lonlat instanceof OpenLayers.LonLat,
+ "createMarker sets a marker lontlat property to a lonlat" );
+ t.ok( layer.markers[0].lonlat === feature.lonlat,
+ "First marker in the layer matches feature lonlat" );
+
+ t.ok( feature.marker.icon instanceof OpenLayers.Icon,
+ "createMarker sets a marker icon property to an icon" );
+
+ t.eq( feature.marker.icon.url,
+ "http://boston.openguides.org/features/ORANGE.png",
+ "createMarker sets marker url correctly" );
+
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0),0);
+ t.ok( map.layers[0] == layer,
+ "Marker layer added to map okay." );
+ if (!isMozilla)
+ t.ok( true, "skipping element test outside of Mozilla");
+ else
+ t.ok( map.layers[0].div.firstChild instanceof HTMLImageElement,
+ "layer div firstChild is an image" );
+ t.eq( map.layers[0].div.firstChild.src,
+ "http://boston.openguides.org/features/ORANGE.png",
+ "Layer div img contains correct url" );
+*/
+ }
+
+ function test_Feature_onScreen(t) {
+ t.plan( 2 );
+
+ var map = new OpenLayers.Map("map");
+
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ var wms = new OpenLayers.Layer.WMS(name, url);
+
+ map.addLayer(wms);
+
+ var layer = new OpenLayers.Layer("foo");
+ map.addLayer(layer);
+
+ map.zoomToExtent(new OpenLayers.Bounds(-50,-50,50,50));
+
+ //onscreen feature
+ var feature1 = new OpenLayers.Feature(layer,
+ new OpenLayers.LonLat(0,0));
+ t.ok( feature1.onScreen(), "feature knows it's onscreen" );
+
+ //onscreen feature
+ var feature2 = new OpenLayers.Feature(layer,
+ new OpenLayers.LonLat(100,100));
+ t.ok( !feature2.onScreen(), "feature knows it's offscreen" );
+ }
+
+ function test_Feature_createPopup_2(t) {
+ t.plan(11);
+
+ //no lonlat
+ var f = {
+ 'popup': null
+ };
+
+ var ret = OpenLayers.Feature.prototype.createPopup.apply(f, []);
+ t.ok((ret == f.popup) && (f.popup == null), "if no 'lonlat' set on feature, still returns reference to this.popup (though it is null)");
+
+
+
+ f.popupClass = OpenLayers.Class({
+ initialize: function(id, lonlat, size, contentHTML, anchor, closeBox) {
+ t.eq(id, "Campion_popup", "correctly generates new popup id from feature's id");
+ t.eq(lonlat, f.lonlat, "correctly passes feature's lonlat to popup constructor");
+ t.eq(size, f.data.popupSize, "correctly passes feature's data.popupSize to popup constructor");
+ t.eq(contentHTML, f.data.popupContentHTML, "correctly passes feature's data.popupContentHTML to popup constructor");
+ t.eq(anchor, g_ExpectedAnchor, "passes correct anchor to popup constructor");
+ t.eq(closeBox, g_CloseBox, "correctly relays closeBox argument to popup constructor");
+ }
+ });
+
+
+ //valid lonlat but no anchor
+ f.popup = null;
+
+ f.id = "Campion";
+ f.lonlat = {};
+ f.data = {
+ 'popupSize': {},
+ 'popupContentHTML': {}
+ };
+ g_ExpectedAnchor = null;
+ g_CloseBox = {};
+
+ ret = OpenLayers.Feature.prototype.createPopup.apply(f, [g_CloseBox]);
+
+ t.ok((ret == f.popup) && (f.popup != null), "a valid popup has been set and returned")
+ t.ok( f.popup.feature == f, "popup's 'feature' property correctly set");
+
+
+ //valid lonlat with anchor
+
+ f.marker = {
+ 'icon': {}
+ };
+ g_ExpectedAnchor = f.marker.icon;
+ ret = OpenLayers.Feature.prototype.createPopup.apply(f, [g_CloseBox]);
+ t.ok((ret == f.popup) && (f.popup != null), "a valid popup has been set and returned")
+ t.ok( f.popup.feature == f, "popup's 'feature' property correctly set");
+ }
+
+ function test_Feature_destroyPopup(t) {
+ t.plan(2);
+
+ var f = {
+ 'popup': {
+ 'feature': {},
+ 'destroy': function() {
+ t.ok(true, "default destroyPopup() calls popup.destroy");
+ }
+ }
+ };
+
+ OpenLayers.Feature.prototype.destroyPopup.apply(f, []);
+ t.ok(f.popup == null, "popup property nullified on destroy");
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 500px; height: 300px;"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Feature/Vector.html b/misc/openlayers/tests/Feature/Vector.html
new file mode 100644
index 0000000..59b0abb
--- /dev/null
+++ b/misc/openlayers/tests/Feature/Vector.html
@@ -0,0 +1,170 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var map;
+ var feature;
+
+ function test_Feature_Vector_constructor(t) {
+ t.plan(3);
+
+ var geometry = new OpenLayers.Geometry();
+ geometry.id = Math.random();
+ var style = {foo: "bar"};
+ var attributes = {bar: "foo"};
+
+ feature = new OpenLayers.Feature.Vector(geometry, attributes, style);
+
+ t.ok(feature instanceof OpenLayers.Feature.Vector,
+ "new OpenLayers.Feature.Vector returns Feature.Vector object" );
+ t.eq(feature.attributes, attributes,
+ "attributes property set properly" );
+ t.eq(feature.geometry.id, geometry.id,
+ "geometry.property set properly" );
+ }
+
+ function test_Feature_onScreen(t) {
+ t.plan(6);
+ var line = new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(0, 0),
+ new OpenLayers.Geometry.Point(10, 20)
+ ]);
+ var feature = new OpenLayers.Feature.Vector(line);
+ feature.layer = {
+ map: {
+ getExtent: function() {
+ return new OpenLayers.Bounds(5, 5, 10, 10);
+ }
+ }
+ };
+ t.eq(feature.onScreen(), true,
+ "intersecting feature returns true for intersection");
+ t.eq(feature.onScreen(true), true,
+ "intersecting feature returns true for bounds only");
+
+ // move the line so only the bounds intersects
+ line.move(0, 5);
+ t.eq(feature.onScreen(), false,
+ "bounds-only feature returns false for intersection");
+ t.eq(feature.onScreen(true), true,
+ "bounds-only feature returns true for bounds only");
+
+ // move the line so bounds does not intersect
+ line.move(0, 10);
+ t.eq(feature.onScreen(), false,
+ "off-screen feature returns false for intersection");
+ t.eq(feature.onScreen(true), false,
+ "off-screen feature returns false for bounds only");
+
+ }
+
+ function test_Feature_getVisibility(t) {
+ t.plan(5);
+ var feature = new OpenLayers.Feature.Vector();
+ feature.layer = {
+ getVisibility: function() {return true}
+ };
+
+ t.ok(feature.getVisibility(),
+ "returns true in a not specific case");
+
+ feature.style = {display: 'none'};
+ t.eq(feature.getVisibility(), false,
+ "returns false when feature style display property is set to 'none'");
+
+ feature.style = null;
+ feature.layer.styleMap = {
+ createSymbolizer: function() {
+ return {display: 'none'}
+ }
+ }
+ t.eq(feature.getVisibility(), false,
+ "returns false when layer styleMap is configured so that the feature" +
+ "should not be displayed");
+
+ delete feature.layer.styleMap;
+ feature.layer.getVisibility = function() {return false}
+ t.eq(feature.getVisibility(), false,
+ "returns false when layer it belongs to is not visible");
+
+ feature.layer = null;
+ t.eq(feature.getVisibility(), false,
+ "returns false when it doesn't belong to any layer");
+ }
+
+ function test_Feature_Vector_clone(t) {
+ t.plan(6);
+
+ var geometry = new OpenLayers.Geometry.Point(Math.random(),
+ Math.random());
+ var style = {foo: "bar"};
+ var attributes = {bar: "foo"};
+
+ feature = new OpenLayers.Feature.Vector(geometry, attributes, style);
+ var clone = feature.clone();
+
+ t.ok(clone instanceof OpenLayers.Feature.Vector,
+ "new OpenLayers.Feature.Vector returns Feature.Vector object");
+ t.eq(clone.attributes, attributes,
+ "attributes property set properly");
+ t.eq(clone.style, style,
+ "style property set properly");
+ t.eq(clone.geometry.x, geometry.x,
+ "geometry.x property set properly");
+ t.eq(clone.geometry.y, geometry.y,
+ "geometry.y property set properly");
+
+ feature = new OpenLayers.Feature.Vector();
+ clone = feature.clone();
+ t.ok(clone instanceof OpenLayers.Feature.Vector,
+ "clone can clone geometry-less features");
+ }
+
+ function test_Feature_Vector_move(t) {
+ t.plan(3);
+
+ var oldx = 26;
+ var oldy = 14;
+ var newx = 6;
+ var newy = 4;
+ var res = 10;
+
+ var geometry = new OpenLayers.Geometry.Point(oldx,
+ oldy);
+
+ var drawn = false;
+
+ feature = new OpenLayers.Feature.Vector(geometry);
+
+ feature.layer = {
+ getViewPortPxFromLonLat : function(lonlat){
+ return new OpenLayers.Pixel(lonlat.lon,lonlat.lat);
+ },
+ map: {
+ getResolution: function(){
+ return res;
+ }
+ },
+ drawFeature: function(){
+ drawn = true;
+ }
+ }
+
+ var pixel = new OpenLayers.Pixel(newx,newy)
+
+ feature.move(pixel);
+
+ geometry = feature.geometry;
+
+ t.ok(drawn, "The feature is redrawn after the move");
+ t.eq(geometry.x, res * (newx - oldx) + oldx, "New geometry has proper x coordinate");
+ t.eq(geometry.y, res * (oldy - newy) + oldy, "New geometry has proper y coordinate");
+ }
+
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 500px; height: 300px;"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Filter.html b/misc/openlayers/tests/Filter.html
new file mode 100644
index 0000000..01b8f47
--- /dev/null
+++ b/misc/openlayers/tests/Filter.html
@@ -0,0 +1,31 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(3);
+
+ var options = {'foo': 'bar'};
+ var filter = new OpenLayers.Filter(options);
+ t.ok(filter instanceof OpenLayers.Filter,
+ "new OpenLayers.Filter returns object" );
+ t.eq(filter.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof filter.evaluate, "function", "filter has an evaluate function");
+ }
+
+ function test_toString(t) {
+ t.plan(1);
+ var filter = new OpenLayers.Filter.Comparison({
+ property: "PERSONS",
+ value: 2000000,
+ type: OpenLayers.Filter.Comparison.LESS_THAN
+ });
+ t.eq(filter.toString(), "PERSONS < 2000000", "toString returns CQL representation");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Filter/Comparison.html b/misc/openlayers/tests/Filter/Comparison.html
new file mode 100644
index 0000000..04e192a
--- /dev/null
+++ b/misc/openlayers/tests/Filter/Comparison.html
@@ -0,0 +1,373 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(3);
+
+ var options = {'foo': 'bar'};
+ var filter = new OpenLayers.Filter.Comparison(options);
+ t.ok(filter instanceof OpenLayers.Filter.Comparison,
+ "new OpenLayers.Filter.Comparison returns object" );
+ t.eq(filter.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof filter.evaluate, "function", "filter has an evaluate function");
+ }
+
+ function test_destroy(t) {
+ t.plan(1);
+
+ var filter = new OpenLayers.Filter.Comparison();
+ filter.destroy();
+ t.eq(filter.symbolizer, null, "symbolizer hash nulled properly");
+ }
+
+ function test_value2regex(t) {
+ t.plan(4);
+
+ var filter = new OpenLayers.Filter.Comparison({
+ property: "foo",
+ value: "*b?r\\*\\?*",
+ type: OpenLayers.Filter.Comparison.LIKE});
+ filter.value2regex("*", "?", "\\");
+ t.eq(filter.value, ".*b.r\\*\\?.*", "Regular expression generated correctly.");
+
+ filter.value = "%b.r!%!.%";
+ filter.value2regex("%", ".", "!");
+ t.eq(filter.value, ".*b.r\\%\\..*", "Regular expression with different wildcard and escape chars generated correctly.");
+
+ filter.value = "!!";
+ filter.value2regex();
+ t.eq(filter.value, "\\!", "!! successfully unescaped to \\!");
+
+ // Big one.
+ filter.value = "!!c!!!d!e";
+ filter.value2regex();
+ t.eq(filter.value, "\\!c\\!\\d\\e", "!!c!!!d!e successfully unescaped to \\!c\\!\\d\\e");
+ }
+
+ function test_regex2value(t) {
+ t.plan(8);
+
+ function r2v(regex) {
+ return OpenLayers.Filter.Comparison.prototype.regex2value.call(
+ {value: regex}
+ );
+ }
+
+ t.eq(r2v("foo"), "foo", "doesn't change string without special chars");
+ t.eq(r2v("foo.*foo"), "foo*foo", "wildCard replaced");
+ t.eq(r2v("foo.foo"), "foo.foo", "singleChar replaced");
+ t.eq(r2v("foo\\\\foo"), "foo\\foo", "escape removed");
+ t.eq(r2v("foo!foo"), "foo!!foo", "escapes !");
+ t.eq(r2v("foo\\*foo"), "foo!*foo", "replaces escape on *");
+ t.eq(r2v("foo\\.foo"), "foo!.foo", "replaces escape on .");
+ t.eq(r2v("foo\\\\.foo"), "foo\\.foo", "unescapes only \\ before .");
+
+ }
+
+ function test_evaluate(t) {
+
+ var cases = [{
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.BETWEEN,
+ property: "area",
+ lowerBoundary: 1000,
+ upperBoundary: 4999
+ }),
+ context: {area: 999},
+ expect: false
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.BETWEEN,
+ property: "area",
+ lowerBoundary: 1000,
+ upperBoundary: 4999
+ }),
+ context: {area: 1000},
+ expect: true
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.BETWEEN,
+ property: "area",
+ lowerBoundary: 1000,
+ upperBoundary: 4999
+ }),
+ context: {area: 4999},
+ expect: true
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.BETWEEN,
+ property: "area",
+ lowerBoundary: 1000,
+ upperBoundary: 4999
+ }),
+ context: {area: 5000},
+ expect: false
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.BETWEEN,
+ property: "area",
+ lowerBoundary: 1000,
+ upperBoundary: 4999
+ }),
+ context: {area: 999},
+ expect: false
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "prop",
+ value: "Foo"
+ }),
+ context: {prop: "Foo"},
+ expect: true
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "prop",
+ value: "Foo"
+ }),
+ context: {prop: "foo"},
+ expect: false
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ matchCase: true,
+ property: "prop",
+ value: "Foo"
+ }),
+ context: {prop: "foo"},
+ expect: false
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+ property: "prop",
+ value: "foo"
+ }),
+ context: {prop: "FOO"},
+ expect: true
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+ matchCase: true,
+ property: "prop",
+ value: "foo"
+ }),
+ context: {prop: "FOO"},
+ expect: true
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+ matchCase: false,
+ property: "prop",
+ value: "foo"
+ }),
+ context: {prop: "FOO"},
+ expect: false
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.IS_NULL,
+ property: "prop"
+ }),
+ context: {prop: null},
+ expect: true
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.IS_NULL,
+ property: "prop"
+ }),
+ context: {},
+ expect: false
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.IS_NULL,
+ property: "prop"
+ }),
+ context: {prop: "foo"},
+ expect: false
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.IS_NULL,
+ property: "prop"
+ }),
+ context: {prop: 0},
+ expect: false
+ }];
+
+ t.plan(cases.length);
+
+ var c;
+ for(var i=0; i<cases.length; ++i) {
+ c = cases[i];
+ t.eq(c.filter.evaluate(c.context), c.expect, "case " + i + ": " + c.filter.type);
+ }
+
+ }
+
+ function test_evaluate_feature(t) {
+
+ var cases = [{
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.BETWEEN,
+ property: "area",
+ lowerBoundary: 1000,
+ upperBoundary: 4999
+ }),
+ context: new OpenLayers.Feature.Vector(null, {area: 999}),
+ expect: false
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.BETWEEN,
+ property: "area",
+ lowerBoundary: 1000,
+ upperBoundary: 4999
+ }),
+ context: new OpenLayers.Feature.Vector(null, {area: 1000}),
+ expect: true
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.BETWEEN,
+ property: "area",
+ lowerBoundary: 1000,
+ upperBoundary: 4999
+ }),
+ context: new OpenLayers.Feature.Vector(null, {area: 4999}),
+ expect: true
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.BETWEEN,
+ property: "area",
+ lowerBoundary: 1000,
+ upperBoundary: 4999
+ }),
+ context: new OpenLayers.Feature.Vector(null, {area: 5000}),
+ expect: false
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.BETWEEN,
+ property: "area",
+ lowerBoundary: 1000,
+ upperBoundary: 4999
+ }),
+ context: new OpenLayers.Feature.Vector(null, {area: 999}),
+ expect: false
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "prop",
+ value: "Foo"
+ }),
+ context: new OpenLayers.Feature.Vector(null, {prop: "Foo"}),
+ expect: true
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "prop",
+ value: "Foo"
+ }),
+ context: new OpenLayers.Feature.Vector(null, {prop: "foo"}),
+ expect: false
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ matchCase: true,
+ property: "prop",
+ value: "Foo"
+ }),
+ context: new OpenLayers.Feature.Vector(null, {prop: "foo"}),
+ expect: false
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+ property: "prop",
+ value: "foo"
+ }),
+ context: {prop: "FOO"},
+ expect: true
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+ matchCase: true,
+ property: "prop",
+ value: "foo"
+ }),
+ context: new OpenLayers.Feature.Vector(null, {prop: "FOO"}),
+ expect: true
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+ matchCase: false,
+ property: "prop",
+ value: "foo"
+ }),
+ context: new OpenLayers.Feature.Vector(null, {prop: "FOO"}),
+ expect: false
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.IS_NULL,
+ property: "prop"
+ }),
+ context: new OpenLayers.Feature.Vector(null, {prop: null}),
+ expect: true
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.IS_NULL,
+ property: "prop"
+ }),
+ context: new OpenLayers.Feature.Vector(null, {}),
+ expect: false
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.IS_NULL,
+ property: "prop"
+ }),
+ context: new OpenLayers.Feature.Vector(null, {prop: "foo"}),
+ expect: false
+ }, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.IS_NULL,
+ property: "prop"
+ }),
+ context: new OpenLayers.Feature.Vector(null, {prop: 0}),
+ expect: false
+ }];
+
+ t.plan(cases.length);
+
+ var c;
+ for(var i=0; i<cases.length; ++i) {
+ c = cases[i];
+ t.eq(c.filter.evaluate(c.context), c.expect, "case " + i + ": " + c.filter.type);
+ }
+
+ }
+
+ function test_clone(t) {
+
+ t.plan(3);
+
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "prop",
+ value: "val"
+ });
+
+ var clone = filter.clone();
+
+ // modify the original
+ filter.type = OpenLayers.Filter.Comparison.NOT_EQUAL_TO;
+
+ t.eq(clone.type, OpenLayers.Filter.Comparison.EQUAL_TO, "clone has proper type");
+ t.eq(clone.property, "prop", "clone has proper property");
+ t.eq(clone.value, "val", "clone has proper value");
+
+ filter.destroy();
+
+ }
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Filter/FeatureId.html b/misc/openlayers/tests/Filter/FeatureId.html
new file mode 100644
index 0000000..8c20192
--- /dev/null
+++ b/misc/openlayers/tests/Filter/FeatureId.html
@@ -0,0 +1,67 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(3);
+
+ var options = {'foo': 'bar'};
+ var filter = new OpenLayers.Filter.FeatureId(options);
+ t.ok(filter instanceof OpenLayers.Filter.FeatureId,
+ "new OpenLayers.Filter.FeatureId returns object" );
+ t.eq(filter.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof filter.evaluate, "function", "filter has an evaluate function");
+ }
+
+ function test_destroy(t) {
+ t.plan(1);
+
+ var filter = new OpenLayers.Filter.FeatureId();
+ filter.destroy();
+ t.eq(filter.symbolizer, null, "symbolizer hash nulled properly");
+ }
+
+ function test_evaluate(t) {
+ t.plan(3);
+
+ var filter = new OpenLayers.Filter.FeatureId(
+ {fids: ["fid_1", "fid_3"]});
+
+ var filterResults = {
+ "fid_1" : true,
+ "fid_2" : false,
+ "fid_3" : true};
+ for (var i in filterResults) {
+ var feature = new OpenLayers.Feature.Vector();
+ feature.fid = i;
+ var result = filter.evaluate(feature);
+ t.eq(result, filterResults[i], "feature "+i+" evaluates to "+result.toString()+" correctly.");
+ feature.destroy();
+ }
+ }
+
+ function test_clone(t) {
+
+ t.plan(1);
+
+ var filter = new OpenLayers.Filter.FeatureId({
+ fids: [1, 2, 3]
+ });
+
+ var clone = filter.clone();
+
+ // modify the original
+ filter.fids.push(4);
+
+ t.eq(clone.fids.length, 3, "clone has proper fids length");
+
+ filter.destroy();
+
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Filter/Logical.html b/misc/openlayers/tests/Filter/Logical.html
new file mode 100644
index 0000000..187cf5b
--- /dev/null
+++ b/misc/openlayers/tests/Filter/Logical.html
@@ -0,0 +1,144 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(3);
+
+ var options = {'foo': 'bar'};
+ var filter = new OpenLayers.Filter.Logical(options);
+ t.ok(filter instanceof OpenLayers.Filter.Logical,
+ "new OpenLayers.Filter.Logical returns object" );
+ t.eq(filter.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof filter.evaluate, "function", "filter has an evaluate function");
+ }
+
+ function test_destroy(t) {
+ t.plan(1);
+
+ var filter = new OpenLayers.Filter.Logical();
+ filter.destroy();
+ t.eq(filter.filters, null, "filters array nulled properly");
+ }
+
+ function test_evaluate(t) {
+ t.plan(1);
+
+ var filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.NOT});
+ filter.filters.push(new OpenLayers.Filter());
+
+ var feature = new OpenLayers.Feature.Vector();
+
+ t.eq(filter.evaluate(feature.attributes), false,
+ "feature evaluates to false correctly.");
+ }
+
+ function test_evaluate_feature(t) {
+ t.plan(6);
+
+ var feature = new OpenLayers.Feature.Vector(null, {
+ pop: 200,
+ name: "foo"
+ });
+
+ var smallPop = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LESS_THAN,
+ property: "pop",
+ value: 120
+ });
+
+ var bigPop = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.GREATER_THAN,
+ property: "pop",
+ value: 120
+ });
+
+ var namedFoo = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "name",
+ value: "foo"
+ });
+
+ var filter;
+
+ // test simple not
+ filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.NOT,
+ filters: [smallPop]
+ });
+ t.eq(filter.evaluate(feature), true, "not smallPop");
+
+ filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.NOT,
+ filters: [bigPop]
+ });
+ t.eq(filter.evaluate(feature), false, "not bigPop");
+
+ // test or
+ filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.OR,
+ filters: [smallPop, namedFoo]
+ });
+ t.eq(filter.evaluate(feature), true, "smallPop or namedFoo");
+
+ filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.OR,
+ filters: [bigPop, namedFoo]
+ });
+ t.eq(filter.evaluate(feature), true, "bigPop or namedFoo");
+
+ // test and
+ filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.AND,
+ filters: [smallPop, namedFoo]
+ });
+ t.eq(filter.evaluate(feature), false, "smallPop and namedFoo");
+
+ filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.AND,
+ filters: [bigPop, namedFoo]
+ });
+ t.eq(filter.evaluate(feature), true, "bigPop and namedFoo");
+
+ }
+
+ function test_clone(t) {
+
+ t.plan(2);
+
+ var filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.AND,
+ filters: [
+ new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "prop1",
+ value: "val1"
+ }),
+ new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+ property: "prop2",
+ value: "val2"
+ })
+ ]
+ });
+
+ var clone = filter.clone();
+
+ // modify the original
+ filter.type = OpenLayers.Filter.Logical.OR;
+ filter.filters[0].value = "nada";
+
+ t.eq(clone.type, OpenLayers.Filter.Logical.AND, "clone has proper type");
+ t.eq(clone.filters[0].value, "val1", "clone has cloned child filters");
+
+ filter.destroy();
+
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Filter/Spatial.html b/misc/openlayers/tests/Filter/Spatial.html
new file mode 100644
index 0000000..558f924
--- /dev/null
+++ b/misc/openlayers/tests/Filter/Spatial.html
@@ -0,0 +1,112 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(3);
+
+ var options = {'foo': 'bar'};
+ var filter = new OpenLayers.Filter.Spatial(options);
+ t.ok(filter instanceof OpenLayers.Filter.Spatial,
+ "new OpenLayers.Filter.Spatial returns object" );
+ t.eq(filter.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof filter.evaluate, "function", "filter has an evaluate function");
+ }
+
+ function test_destroy(t) {
+ t.plan(1);
+
+ var filter = new OpenLayers.Filter.Spatial();
+ filter.destroy();
+ t.eq(filter.symbolizer, null, "symbolizer hash nulled properly");
+ }
+
+ function test_evaluate(t) {
+ t.plan(8);
+
+ var filer, feature, res, geom, bounds;
+
+ bounds = new OpenLayers.Bounds(0, 0, 10, 10);
+ filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ value: bounds
+ });
+
+ var not = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.NOT,
+ filters: [filter]
+ });
+
+ feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(2, 2));
+ res = filter.evaluate(feature);
+ t.eq(res, true,
+ "evaluates returns correct value when feature intersects bounds");
+ t.eq(not.evaluate(feature), !res, "not bbox");
+
+ feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(20, 20));
+ res = filter.evaluate(feature);
+ t.eq(res, false,
+ "evaluates returns correct value when feature does not intersect bounds");
+ t.eq(not.evaluate(feature), !res, "not outside bbox");
+
+ geom = bounds.toGeometry();
+ feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(2, 2));
+ filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.INTERSECTS,
+ value: geom
+ });
+ res = filter.evaluate(feature);
+ t.eq(res, true,
+ "evaluates returns correct value when feature intersects bounds");
+ not.filters = [filter];
+ t.eq(not.evaluate(feature), !res, "not intersection");
+
+ geom = bounds.toGeometry();
+ feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(20, 20));
+ filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.INTERSECTS,
+ value: geom
+ });
+ not.filters = [filter];
+ res = filter.evaluate(feature);
+ t.eq(res, false,
+ "evaluates returns correct value when feature does not intersect bounds");
+ t.eq(not.evaluate(feature), !res, "not non-intersection");
+
+
+ }
+
+ function test_clone(t) {
+
+ t.plan(2);
+
+ var bounds = new OpenLayers.Bounds(0, 0, 10, 10);
+ var filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ value: bounds
+ });
+
+ var clone = filter.clone();
+
+ // modify the original
+ filter.value.bottom = -100;
+
+ t.eq(clone.type, OpenLayers.Filter.Spatial.BBOX, "clone has proper type");
+ t.eq(clone.value.toBBOX(), "0,0,10,10", "clone has proper value");
+
+ filter.destroy();
+ clone.destroy();
+
+ }
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format.html b/misc/openlayers/tests/Format.html
new file mode 100644
index 0000000..66d2696
--- /dev/null
+++ b/misc/openlayers/tests/Format.html
@@ -0,0 +1,23 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_Format_constructor(t) {
+ t.plan(5);
+
+ var options = {'foo': 'bar'};
+ var format = new OpenLayers.Format(options);
+ t.ok(format instanceof OpenLayers.Format,
+ "new OpenLayers.Format returns object" );
+ t.eq(format.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a write function");
+ t.eq(format.options, options, "format.options correctly set");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/ArcXML.html b/misc/openlayers/tests/Format/ArcXML.html
new file mode 100644
index 0000000..ea7d273
--- /dev/null
+++ b/misc/openlayers/tests/Format/ArcXML.html
@@ -0,0 +1,277 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var axl_image_response = '<?xml version="1.0" encoding="UTF-8"?><ARCXML version="1.1"><RESPONSE><IMAGE><ENVELOPE minx="-2471.42857142857" miny="0" maxx="105671.428571429" maxy="75700" /><OUTPUT url="http://localhost/output/364826560.png" /></IMAGE></RESPONSE></ARCXML>';
+ var axl_feature_response = '<?xml version="1.0" encoding="Cp1252"?><ARCXML version="1.1"><RESPONSE><FEATURES><FEATURE><FIELDS><FIELD name="UNIQUE_ID" value="514504b5-0458-461d-b540-8e18a454f619" /><FIELD name="LABEL" value="LIBRARY" /><FIELD name="Y_COORD" value="39.57" /><FIELD name="X_COORD" value="-104.24" /><FIELD name="#SHAPE#" value="[Geometry]" /><FIELD name="OBJECTID" value="1" /><FIELD name="shape.area" value="0" /><FIELD name="shape.len" value="0" /></FIELDS></FEATURE><FEATURE><FIELDS><FIELD name="UNIQUE_ID" value="514504b5-0458-461d-b540-8e81a454f619" /><FIELD name="LABEL" value="LIBRARY2" /><FIELD name="Y_COORD" value="39.75" /><FIELD name="X_COORD" value="-104.42" /><FIELD name="#SHAPE#" value="[Geometry]" /><FIELD name="OBJECTID" value="2" /><FIELD name="shape.area" value="0" /><FIELD name="shape.len" value="0" /></FIELDS></FEATURE><FEATURECOUNT count="2" hasmore="false" /><ENVELOPE minx="-678853.220047791" miny="1810.22081371862" maxx="-678853.220047791" maxy="1810.22081371862"/></FEATURES></RESPONSE></ARCXML>';
+
+ //
+ // creating a new arcxml format creates an object that has a read and write function
+ //
+ function test_Format_ArcXML_constructor1(t) {
+ t.plan(4);
+
+ var format = new OpenLayers.Format.ArcXML();
+ t.ok(format instanceof OpenLayers.Format.ArcXML,
+ "new OpenLayers.Format.ArcXML returns object" );
+
+ t.ok(format.request, null, "no options creates a null request");
+
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a write function");
+ }
+
+ //
+ // creating a new arcxml format with a set of options for an image request
+ // creates a request child object, and a get_image grandchild.
+ //
+ function test_Format_ArcXML_constructor2(t) {
+ t.plan(6);
+
+ var options = {
+ requesttype:'image',
+ envelope: new OpenLayers.Bounds( -180, -90, 180, 90 ).toArray(),
+ layers: [],
+ tileSize: new OpenLayers.Size( 256,256 ),
+ featureCoordSys: '4326',
+ filterCoordSys: '4326'
+ };
+
+ var format = new OpenLayers.Format.ArcXML( options );
+ t.ok(format instanceof OpenLayers.Format.ArcXML,
+ "new OpenLayers.Format.ArcXML returns object" );
+
+ t.ok(format.request instanceof OpenLayers.Format.ArcXML.Request,
+ "constructor with 'image' requesttype generates a request");
+ t.ok( format.request.get_image !== null, "get_image property exists" );
+ t.ok( format.request.get_feature === null, "get_feature property does not exists" );
+
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a write function");
+ }
+
+ //
+ // creating a new arcxml format with a set of options for a feature request
+ // creates a request child object, and a get_feature grandchild
+ //
+ function test_Format_ArcXML_constructor3(t) {
+ t.plan(6);
+
+ var options = {
+ requesttype:'feature'
+ };
+
+ var format = new OpenLayers.Format.ArcXML( options );
+ t.ok(format instanceof OpenLayers.Format.ArcXML,
+ "new OpenLayers.Format.ArcXML returns object" );
+
+ t.ok(format.request instanceof OpenLayers.Format.ArcXML.Request,
+ "constructor with 'feature' requesttype generates a request");
+ t.ok( format.request.get_feature !== null, "get_feature property exists" );
+ t.ok( format.request.get_image === null, "get_image property does not exists" );
+
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a write function");
+ }
+
+ //
+ // read in a known good axl image response
+ //
+ function test_Format_ArcXML_read1(t) {
+ t.plan(4);
+ var f = new OpenLayers.Format.ArcXML();
+ var response = f.read(axl_image_response);
+
+ t.ok(response !== null, "get_image response object is not null" );
+ t.ok(response.image !== null, "get_image image tag is not null");
+ t.ok(response.image.envelope !== null, "get_image image envelope tag is not null");
+ t.ok(response.image.output !== null, "get_image image output tag is not null");
+ }
+
+ //
+ // read in a known good axl feature response
+ //
+ function test_Format_ArcXML_read2(t) {
+ t.plan(10);
+ var f = new OpenLayers.Format.ArcXML();
+ var response = f.read(axl_feature_response);
+
+ t.ok(response !== null, "get_feature response object is not null" );
+ t.ok(response.features !== null, "get_feature features tag is not null");
+ t.ok(response.features.envelope !== null, "get_feature envelope tag is not null");
+ t.eq(response.features.featurecount, "2", "feature count is 2" );
+
+ // test the second feature parsed
+ // <FIELD name="UNIQUE_ID" value="514504b5-0458-461d-b540-8e81a454f619" />
+ // <FIELD name="LABEL" value="LIBRARY2" />
+ // <FIELD name="Y_COORD" value="39.75" />
+ // <FIELD name="X_COORD" value="-104.42" />
+ // <FIELD name="#SHAPE#" value="[Geometry]" />
+ // <FIELD name="OBJECTID" value="2" />
+ // <FIELD name="shape.area" value="0" />
+ // <FIELD name="shape.len" value="0" />
+ t.eq( response.features.feature[1].attributes['UNIQUE_ID'], "514504b5-0458-461d-b540-8e81a454f619", "field 1 for feature 2 is correct" );
+ t.eq( response.features.feature[1].attributes['LABEL'], "LIBRARY2", "field 2 for feature 2 is correct" );
+ t.eq( response.features.feature[1].attributes['Y_COORD'], "39.75", "field 3 for feature 2 is correct" );
+ t.eq( response.features.feature[1].attributes['X_COORD'], "-104.42", "field 4 for feature 2 is correct" );
+ t.eq( response.features.feature[1].attributes['#SHAPE#'], "[Geometry]", "field 5 for feature 2 is correct" );
+ t.eq( response.features.feature[1].attributes['OBJECTID'], "2", "field 6 for feature 2 is correct" );
+ }
+
+ //
+ // cause an error by parsing bad axl
+ //
+ function test_Format_ArcXML_parseerror(t) {
+ t.plan(1);
+ var f = new OpenLayers.Format.ArcXML();
+
+ try {
+ f.read( '<?xml version="1.0" encoding="Cp1252"?><ARCXML version="1.1"><NO END TAG>' );
+ t.fail("parsing failed to fail")
+ } catch (ex) {
+ t.ok( true, "Exception message indicates parsing error." );
+ }
+ }
+
+ //
+ // create an arcxml image request, and verify that it matches a known image request
+ //
+ function test_format_ArcXML_write1(t) {
+ var options = {
+ requesttype:'image',
+ envelope: new OpenLayers.Bounds( -180, -90, 180, 90 ).toArray(),
+ layers: [],
+ tileSize: new OpenLayers.Size( 256,256 ),
+ featureCoordSys: '4326',
+ filterCoordSys: '4326'
+ };
+ var truth = '<ARCXML version="1.1"><REQUEST><GET_IMAGE><PROPERTIES><FEATURECOORDSYS id="4326"/><FILTERCOORDSYS id="4326"/><ENVELOPE minx="-180" miny="-90" maxx="180" maxy="90"/><IMAGESIZE height="256" width="256"/></PROPERTIES></GET_IMAGE></REQUEST></ARCXML>';
+ axl_write(t,options,truth);
+ }
+
+ //
+ // create an arcxml image request that specifies layer visibilities, and
+ // verify that it matches a known image request
+ //
+ function test_format_ArcXML_write2(t) {
+ var options = {
+ requesttype:'image',
+ envelope: new OpenLayers.Bounds( -180, -90, 180, 90 ).toArray(),
+ layers: [{
+ id: "0",
+ visible: "true"
+ }],
+ tileSize: new OpenLayers.Size( 256,256 ),
+ featureCoordSys: '4326',
+ filterCoordSys: '4326'
+ };
+ var truth = '<ARCXML version="1.1"><REQUEST><GET_IMAGE><PROPERTIES><FEATURECOORDSYS id="4326"/><FILTERCOORDSYS id="4326"/><ENVELOPE minx="-180" miny="-90" maxx="180" maxy="90"/><IMAGESIZE height="256" width="256"/><LAYERLIST><LAYERDEF id="0" visible="true"/></LAYERLIST></PROPERTIES></GET_IMAGE></REQUEST></ARCXML>';
+ axl_write(t, options, truth );
+ }
+
+ //
+ // create an arcxml image request that performs a query for thematic mapping,
+ // and verify that it matches a known image request
+ //
+ function test_format_ArcXML_write3(t) {
+ var options = {
+ requesttype:'image',
+ envelope: new OpenLayers.Bounds( -180, -90, 180, 90 ).toArray(),
+ layers: [{
+ id: "0",
+ visible: "true",
+ query: {
+ where: "COMPANY='AVENCIA'"
+ }
+ }],
+ tileSize: new OpenLayers.Size( 256,256 ),
+ featureCoordSys: '4326',
+ filterCoordSys: '4326'
+ };
+ var truth = '<ARCXML version="1.1"><REQUEST><GET_IMAGE><PROPERTIES><FEATURECOORDSYS id="4326"/><FILTERCOORDSYS id="4326"/><ENVELOPE minx="-180" miny="-90" maxx="180" maxy="90"/><IMAGESIZE height="256" width="256"/><LAYERLIST><LAYERDEF id="0" visible="true"><QUERY where="COMPANY=\'AVENCIA\'"/></LAYERDEF></LAYERLIST></PROPERTIES></GET_IMAGE></REQUEST></ARCXML>';
+ axl_write(t, options, truth );
+ }
+
+ //
+ // create an arcxml image request that performs a spatial query for thematic mapping,
+ // and verify that it matches a known image request
+ //
+ function test_format_ArcXML_write4(t) {
+ var options = {
+ requesttype:'image',
+ envelope: new OpenLayers.Bounds( -180, -90, 180, 90 ).toArray(),
+ layers: [{
+ id: "0",
+ visible: "true",
+ query: {
+ spatialfilter: true,
+ where: "COMPANY='AVENCIA'"
+ }
+ }],
+ tileSize: new OpenLayers.Size( 256,256 ),
+ featureCoordSys: '4326',
+ filterCoordSys: '4326'
+ };
+ var truth = '<ARCXML version="1.1"><REQUEST><GET_IMAGE><PROPERTIES><FEATURECOORDSYS id="4326"/><FILTERCOORDSYS id="4326"/><ENVELOPE minx="-180" miny="-90" maxx="180" maxy="90"/><IMAGESIZE height="256" width="256"/><LAYERLIST><LAYERDEF id="0" visible="true"><SPATIALQUERY where="COMPANY=\'AVENCIA\'"/></LAYERDEF></LAYERLIST></PROPERTIES></GET_IMAGE></REQUEST></ARCXML>';
+ axl_write(t, options, truth );
+ }
+
+ //
+ // create an arcxml image request that performs a thematic map request, and
+ // verify that it matches a known image request.
+ //
+ function test_format_ArcXML_write5(t) {
+ var options = {
+ requesttype:'image',
+ envelope: new OpenLayers.Bounds( -180, -90, 180, 90 ).toArray(),
+ layers: [{
+ id: "0",
+ visible: "true",
+ query: {
+ spatialfilter: true,
+ where: "COMPANY='AVENCIA'"
+ },
+ renderer: {
+ type: 'valuemap',
+ lookupfield: 'lookup',
+ ranges: [{
+ lower: 0,
+ upper: 10,
+ symbol: {
+ type: 'simplepolygon',
+ fillcolor: '0,0,0'
+ }
+ },{
+ lower: 10,
+ upper: 20,
+ symbol: {
+ type: 'simplepolygon',
+ fillcolor: '255,255,255'
+ }
+ }]
+ }
+ }],
+ tileSize: new OpenLayers.Size( 256,256 ),
+ featureCoordSys: '4326',
+ filterCoordSys: '4326'
+ };
+ var truth = '<ARCXML version="1.1"><REQUEST><GET_IMAGE><PROPERTIES><FEATURECOORDSYS id="4326"/><FILTERCOORDSYS id="4326"/><ENVELOPE minx="-180" miny="-90" maxx="180" maxy="90"/><IMAGESIZE height="256" width="256"/><LAYERLIST><LAYERDEF id="0" visible="true"><SPATIALQUERY where="COMPANY=\'AVENCIA\'"/><VALUEMAPRENDERER lookupfield="lookup"><RANGE lower="0" upper="10"><SIMPLEPOLYGONSYMBOL fillcolor="0,0,0"/></RANGE><RANGE lower="10" upper="20"><SIMPLEPOLYGONSYMBOL fillcolor="255,255,255"/></RANGE></VALUEMAPRENDERER></LAYERDEF></LAYERLIST></PROPERTIES></GET_IMAGE></REQUEST></ARCXML>';
+ axl_write(t, options, truth );
+ }
+
+ //
+ // helper function to write some axl, and compare it against a truth axl string
+ //
+ function axl_write(t, options, truth) {
+ t.plan(1);
+
+ var f = new OpenLayers.Format.ArcXML( options );
+ var arcxml = f.write();
+ t.eq( arcxml, truth, "ArcXML request is correct.");
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/ArcXML/Features.html b/misc/openlayers/tests/Format/ArcXML/Features.html
new file mode 100644
index 0000000..bd2f680
--- /dev/null
+++ b/misc/openlayers/tests/Format/ArcXML/Features.html
@@ -0,0 +1,69 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var axl_feature_response = '<?xml version="1.0" encoding="Cp1252"?><ARCXML version="1.1"><RESPONSE><FEATURES><FEATURE><FIELDS><FIELD name="UNIQUE_ID" value="514504b5-0458-461d-b540-8e18a454f619" /><FIELD name="LABEL" value="LIBRARY" /><FIELD name="Y_COORD" value="39.57" /><FIELD name="X_COORD" value="-104.24" /><FIELD name="#SHAPE#" value="[Geometry]" /><FIELD name="OBJECTID" value="1" /><FIELD name="shape.area" value="0" /><FIELD name="shape.len" value="0" /></FIELDS></FEATURE><FEATURE><FIELDS><FIELD name="UNIQUE_ID" value="514504b5-0458-461d-b540-8e81a454f619" /><FIELD name="LABEL" value="LIBRARY2" /><FIELD name="Y_COORD" value="39.75" /><FIELD name="X_COORD" value="-104.42" /><FIELD name="#SHAPE#" value="[Geometry]" /><FIELD name="OBJECTID" value="2" /><FIELD name="shape.area" value="0" /><FIELD name="shape.len" value="0" /></FIELDS></FEATURE><FEATURECOUNT count="2" hasmore="false" /><ENVELOPE minx="-678853.220047791" miny="1810.22081371862" maxx="-678853.220047791" maxy="1810.22081371862"/></FEATURES></RESPONSE></ARCXML>';
+
+ //
+ // creating a new arcxml features format creates an object that has a read and write function
+ //
+ function test_initialize(t) {
+ t.plan(3);
+
+ var format = new OpenLayers.Format.ArcXML.Features();
+ t.ok(format instanceof OpenLayers.Format.ArcXML.Features,
+ "new OpenLayers.Format.ArcXML.Features returns object" );
+
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a write function");
+ }
+
+ //
+ // read in a known good axl feature response
+ //
+ function test_read1(t) {
+ t.plan(8);
+ var f = new OpenLayers.Format.ArcXML.Features();
+ var features = f.read(axl_feature_response);
+
+ t.ok(features !== null, "features are not null" );
+ t.eq(features.length, 2, "feature count is 2" );
+
+ // test the second feature parsed
+ // <FIELD name="UNIQUE_ID" value="514504b5-0458-461d-b540-8e81a454f619" />
+ // <FIELD name="LABEL" value="LIBRARY2" />
+ // <FIELD name="Y_COORD" value="39.75" />
+ // <FIELD name="X_COORD" value="-104.42" />
+ // <FIELD name="#SHAPE#" value="[Geometry]" />
+ // <FIELD name="OBJECTID" value="2" />
+ // <FIELD name="shape.area" value="0" />
+ // <FIELD name="shape.len" value="0" />
+ t.eq( features[1].attributes['UNIQUE_ID'], "514504b5-0458-461d-b540-8e81a454f619", "field 1 for feature 2 is correct" );
+ t.eq( features[1].attributes['LABEL'], "LIBRARY2", "field 2 for feature 2 is correct" );
+ t.eq( features[1].attributes['Y_COORD'], "39.75", "field 3 for feature 2 is correct" );
+ t.eq( features[1].attributes['X_COORD'], "-104.42", "field 4 for feature 2 is correct" );
+ t.eq( features[1].attributes['#SHAPE#'], "[Geometry]", "field 5 for feature 2 is correct" );
+ t.eq( features[1].attributes['OBJECTID'], "2", "field 6 for feature 2 is correct" );
+ }
+
+ //
+ // cause an error by parsing bad axl
+ //
+ function test_parseerror(t) {
+ t.plan(1);
+ var f = new OpenLayers.Format.ArcXML.Features();
+
+ try {
+ f.read( '<?xml version="1.0" encoding="Cp1252"?><ARCXML version="1.1"><NO END TAG>' );
+ t.fail("reading didn't fail");
+ } catch (ex) {
+ t.eq( ex.message, "Error parsing the ArcXML request", "Exception message indicates parsing error." );
+ }
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/Atom.html b/misc/openlayers/tests/Format/Atom.html
new file mode 100644
index 0000000..71bccc5
--- /dev/null
+++ b/misc/openlayers/tests/Format/Atom.html
@@ -0,0 +1,450 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(4);
+ var options = {'foo': 'bar'};
+ var format = new OpenLayers.Format.Atom(options);
+ t.ok(format instanceof OpenLayers.Format.Atom,
+ "new OpenLayers.Format.GeoRSS returns object" );
+ t.eq(format.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a write function");
+ }
+
+ /* Reading tests */
+
+ function test_reproject_null(t) {
+ t.plan(1);
+ var parser = new OpenLayers.Format.Atom({'internalProjection':new OpenLayers.Projection("EPSG:4326"), 'externalProjection': new OpenLayers.Projection("EPSG:4326")});
+ var data = parser.read(
+ // begin document
+ '<feed xmlns="http://www.w3.org/2005/Atom">' +
+ '<entry></entry>' +
+ '</feed>'
+ // end document
+ );
+ t.eq(
+ data.length, 1,
+ "Parsing items with null geometry and reprojection doesn't fail"
+ );
+ }
+
+ // read entry 1: basic entry, no categories or persons
+ function test_readentry1(t) {
+ t.plan(10);
+ var parser = new OpenLayers.Format.Atom();
+ var data = parser.read(
+ // begin document
+ '<entry xmlns="http://www.w3.org/2005/Atom">' +
+ ' <id>urn:uuid:82ede847-b31a-4e3d-b773-7471bad154ed</id>' +
+ ' <link href="http://example.com/blog/1" rel="alternate"/>' +
+ ' <summary>An Atom testing entry</summary>' +
+ ' <title>Atom test</title>' +
+ ' <updated>2009-06-02T10:00:00Z</updated>' +
+ '</entry>'
+ // end document
+ );
+ t.ok(data instanceof Array, "Read features");
+ var fx = data[0];
+ t.ok(fx instanceof OpenLayers.Feature.Vector, "Read feature");
+ t.eq(fx.geometry, null, "Geometry is null");
+ t.eq(
+ fx.fid,
+ "urn:uuid:82ede847-b31a-4e3d-b773-7471bad154ed",
+ "Read fid"
+ );
+ var attrib = fx.attributes;
+ t.eq(attrib.title, "Atom test", "Correct title attribute");
+ t.eq(
+ attrib.description,
+ "An Atom testing entry",
+ "Correct description attribute"
+ );
+ var atomAttrib = attrib.atom;
+ t.eq(
+ atomAttrib.links,
+ [{href: "http://example.com/blog/1", rel: "alternate"}],
+ "Correct links in atom namespace"
+ );
+ t.eq(
+ atomAttrib.summary,
+ "An Atom testing entry",
+ "Correct summary in atom namespace"
+ );
+ t.eq(
+ atomAttrib.title,
+ "Atom test",
+ "Correct title in atom namespace"
+ );
+ t.eq(
+ atomAttrib.updated,
+ "2009-06-02T10:00:00Z",
+ "Correct timestamp in atom namespace"
+ );
+ }
+
+ // read entry 2: with georss:where
+ function test_readentry2(t) {
+ t.plan(5);
+ var parser = new OpenLayers.Format.Atom();
+ var data = parser.read(
+ // begin document
+ '<entry xmlns="http://www.w3.org/2005/Atom">' +
+ ' <id>urn:uuid:82ede847-b31a-4e3d-b773-7471bad154ed</id>' +
+ ' <georss:where xmlns:georss="http://www.georss.org/georss">' +
+ ' <gml:Point xmlns:gml="http://www.opengis.net/gml">' +
+ ' <gml:pos>45.68 -111.04</gml:pos>' +
+ ' </gml:Point>' +
+ ' </georss:where>' +
+ '</entry>'
+ // end document
+ );
+ t.ok(data instanceof Array, "Read features");
+ var fx = data[0];
+ t.ok(fx instanceof OpenLayers.Feature.Vector, "Read feature");
+ t.ok(fx.geometry instanceof OpenLayers.Geometry.Point, "Read geometry");
+ t.eq(fx.geometry.x, -111.04, "Read x");
+ t.eq(fx.geometry.y, 45.68, "Read y");
+ }
+
+ // read entry 3: with georss:point
+ function test_readentry3(t) {
+ t.plan(5);
+ var parser = new OpenLayers.Format.Atom();
+ var data = parser.read(
+ // begin document
+ '<entry xmlns="http://www.w3.org/2005/Atom">' +
+ ' <id>urn:uuid:82ede847-b31a-4e3d-b773-7471bad154ed</id>' +
+ ' <georss:point xmlns:georss="http://www.georss.org/georss">45.68 -111.04</georss:point>' +
+ '</entry>'
+ // end document
+ );
+ t.ok(data instanceof Array, "Read features");
+ var fx = data[0];
+ t.ok(fx instanceof OpenLayers.Feature.Vector, "Read feature");
+ t.ok(fx.geometry instanceof OpenLayers.Geometry.Point, "Read geometry");
+ t.eq(fx.geometry.x, -111.04, "Read x");
+ t.eq(fx.geometry.y, 45.68, "Read y");
+ }
+
+ // read entry 4: basic entry, text content
+ function test_readentry4(t) {
+ t.plan(3);
+ var parser = new OpenLayers.Format.Atom();
+ var data = parser.read(
+ // begin document
+ '<entry xmlns="http://www.w3.org/2005/Atom">' +
+ ' <id>urn:uuid:82ede847-b31a-4e3d-b773-7471bad154ed</id>' +
+ ' <link href="http://example.com/blog/1" rel="alternate"/>' +
+ ' <summary>An Atom testing entry</summary>' +
+ ' <title>Atom test</title>' +
+ ' <updated>2009-06-02T10:00:00Z</updated>' +
+ ' <content type="text">Blah, blah, blah</content>' +
+ '</entry>'
+ // end document
+ );
+ t.ok(data instanceof Array, "Read features");
+ var fx = data[0];
+ var attrib = fx.attributes;
+ var atomAttrib = attrib.atom;
+ t.eq(
+ atomAttrib.content.type,
+ "text",
+ "Correct content.type in atom namespace"
+ );
+ t.eq(
+ atomAttrib.content.value,
+ "Blah, blah, blah",
+ "Correct content.value in atom namespace"
+ );
+ }
+
+ // read entry 5: basic entry, KML content
+ function test_readentry5(t) {
+ t.plan(3);
+ var parser = new OpenLayers.Format.Atom();
+ var data = parser.read(
+ // begin document
+ '<entry xmlns="http://www.w3.org/2005/Atom">' +
+ ' <id>urn:uuid:82ede847-b31a-4e3d-b773-7471bad154ed</id>' +
+ ' <link href="http://example.com/blog/1" rel="alternate"/>' +
+ ' <summary>An Atom testing entry</summary>' +
+ ' <title>Atom test</title>' +
+ ' <updated>2009-06-02T10:00:00Z</updated>' +
+ ' <content type="application/vnd.google-earth.kml+xml"><kml xmlns="http://earth.google.com/kml/2.0"><Folder><name>A folder</name><description>It\'s a folder</description></Folder></kml></content>' +
+ '</entry>'
+ // end document
+ );
+ t.ok(data instanceof Array, "Read features");
+ var fx = data[0];
+ var attrib = fx.attributes;
+ var atomAttrib = attrib.atom;
+ t.eq(
+ atomAttrib.content.type,
+ "application/vnd.google-earth.kml+xml",
+ "Correct content.type in atom namespace"
+ );
+ var node = atomAttrib.content.value;
+ var name = node.localName || node.nodeName.split(":").pop();
+ t.eq(
+ name,
+ "kml",
+ "Correct content.value in atom namespace"
+ );
+ }
+
+ // read feed 1
+ function test_readfeed1(t) {
+ t.plan(2);
+ var parser = new OpenLayers.Format.Atom();
+ var data = parser.read(
+ // begin document
+ '<feed xmlns="http://www.w3.org/2005/Atom">' +
+ ' <entry>' +
+ ' <id>urn:uuid:82ede847-b31a-4e3d-b773-7471bad154ed</id>' +
+ ' </entry>' +
+ '</feed>'
+ // end document
+ );
+ t.ok(data instanceof Array, "Read features");
+ var fx = data[0];
+ t.ok(fx instanceof OpenLayers.Feature.Vector, "Read feature");
+ }
+
+ /* Writing tests */
+
+ // write entry 1: null geometry, no attributes
+ function test_writeentry1(t) {
+ t.plan(1);
+ var writer = new OpenLayers.Format.Atom();
+ var feature = new OpenLayers.Feature.Vector(null, {});
+ feature.fid = '1';
+ var data = writer.write(feature);
+ t.xml_eq(
+ data,
+ // begin document
+ '<entry xmlns="http://www.w3.org/2005/Atom">' +
+ '<id>1</id>' +
+ '<title>untitled</title>' +
+ '</entry>',
+ // end document
+ 'Writes an entry doc with id, no attributes'
+ );
+ }
+
+ // write entry 2: null geometry, well-known attributes
+ function test_writeentry2(t) {
+ t.plan(1);
+ var writer = new OpenLayers.Format.Atom();
+ var feature = new OpenLayers.Feature.Vector(null, {title: "Test", description: "A testing feature"});
+ feature.fid = '1';
+ var data = writer.write(feature);
+ t.xml_eq(
+ data,
+ // begin document
+ '<entry xmlns="http://www.w3.org/2005/Atom">' +
+ '<id>1</id>' +
+ '<summary>A testing feature</summary>' +
+ '<title>Test</title>' +
+ '</entry>',
+ // end document
+ 'Writes an entry doc with id, well-known attributes'
+ );
+ }
+
+ // write entry 3: null geometry, Atom constructs to override
+ // well-known attributes
+ function test_writeentry3(t) {
+ t.plan(1);
+ var writer = new OpenLayers.Format.Atom();
+ var feature = new OpenLayers.Feature.Vector(null, {title: "Test", description: "A testing feature", atom: {title: "Atom test", summary: "An Atom testing feature", updated: "2009-06-02T10:00:00Z"}});
+ feature.fid = '1';
+ var data = writer.write(feature);
+ t.xml_eq(
+ data,
+ // begin document
+ '<entry xmlns="http://www.w3.org/2005/Atom">' +
+ '<id>1</id>' +
+ '<summary>An Atom testing feature</summary>' +
+ '<title>Atom test</title>' +
+ '<updated>2009-06-02T10:00:00Z</updated>' +
+ '</entry>',
+ // end document
+ 'Writes an entry doc with Atom constructs overriding well-known atts'
+ );
+ }
+
+ // write entry 4: Atom categories
+ function test_writeentry4(t) {
+ t.plan(1);
+ var writer = new OpenLayers.Format.Atom();
+ var feature = new OpenLayers.Feature.Vector(null, {title: "Test", description: "A testing feature", atom: {title: "Atom test", summary: "An Atom testing feature", updated: "2009-06-02T10:00:00Z", categories: [{term: "blog", scheme: "http://example.com/terms", label: "A blog post"}]}});
+ feature.fid = '1';
+ var data = writer.write(feature);
+ t.xml_eq(
+ data,
+ // begin document
+ '<entry xmlns="http://www.w3.org/2005/Atom">' +
+ '<category term="blog" scheme="http://example.com/terms" label="A blog post"/>' +
+ '<id>1</id>' +
+ '<summary>An Atom testing feature</summary>' +
+ '<title>Atom test</title>' +
+ '<updated>2009-06-02T10:00:00Z</updated>' +
+ '</entry>',
+ // end document
+ 'Writes an entry doc with Atom constructs and categories'
+ );
+ }
+
+ // write entry 5: Atom authors, contributors
+ function test_writeentry5(t) {
+ t.plan(1);
+ var writer = new OpenLayers.Format.Atom();
+ var feature = new OpenLayers.Feature.Vector(null, {title: "Test", description: "A testing feature", atom: {title: "Atom test", summary: "An Atom testing feature", updated: "2009-06-02T10:00:00Z", authors: [{name: "John Doe", uri: "http://example.com/people/jdoe", email: "jdoe@example.com"}], contributors: [{name: "Pikov Andropov", uri: "http://example.com/people/pandropov", email: "pandropov@example.com"}]}});
+ feature.fid = '1';
+ var data = writer.write(feature);
+ t.xml_eq(
+ data,
+ // begin document
+ '<entry xmlns="http://www.w3.org/2005/Atom">' +
+ '<author>' +
+ ' <name>John Doe</name>' +
+ ' <uri>http://example.com/people/jdoe</uri>' +
+ ' <email>jdoe@example.com</email>' +
+ '</author>' +
+ '<contributor>' +
+ ' <name>Pikov Andropov</name>' +
+ ' <uri>http://example.com/people/pandropov</uri>' +
+ ' <email>pandropov@example.com</email>' +
+ '</contributor>' +
+ '<id>1</id>' +
+ '<summary>An Atom testing feature</summary>' +
+ '<title>Atom test</title>' +
+ '<updated>2009-06-02T10:00:00Z</updated>' +
+ '</entry>',
+ // end document
+ 'Writes an entry doc with Atom constructs and persons'
+ );
+ }
+
+ // write entry 6: Atom links
+ function test_writeentry6(t) {
+ t.plan(1);
+
+ // Feature attributes in Atom namespace
+ var atomAttrib = {
+ title: "Atom test",
+ summary: "An Atom testing feature",
+ updated: "2009-06-02T10:00:00Z",
+ links: [
+ { href: "http://example.com/blog/1", rel: "alternate" }
+ ]
+ };
+ var fx = new OpenLayers.Feature.Vector(null, {atom: atomAttrib});
+ fx.fid = 'urn:uuid:82ede847-b31a-4e3d-b773-7471bad154ed';
+
+ var writer = new OpenLayers.Format.Atom();
+ var data = writer.write(fx);
+
+ t.xml_eq(
+ data,
+ // begin document
+ '<entry xmlns="http://www.w3.org/2005/Atom">' +
+ '<id>urn:uuid:82ede847-b31a-4e3d-b773-7471bad154ed</id>' +
+ '<link href="http://example.com/blog/1" rel="alternate"/>' +
+ '<summary>An Atom testing feature</summary>' +
+ '<title>Atom test</title>' +
+ '<updated>2009-06-02T10:00:00Z</updated>' +
+ '</entry>',
+ // end document
+ 'Writes an entry doc with Atom constructs and links'
+ );
+ }
+
+ // write out point -- just enough to see that we're getting the
+ // georss:where element with a Point. We'll trust GML.v3 to get the
+ // details right.
+ function test_writepoint(t) {
+ t.plan(1);
+
+ var point = new OpenLayers.Geometry.Point(-111.04, 45.68);
+ var fx = new OpenLayers.Feature.Vector(point, {});
+ fx.fid = 'urn:uuid:82ede847-b31a-4e3d-b773-7471bad154ed';
+
+ var writer = new OpenLayers.Format.Atom();
+ var data = writer.write(fx);
+
+ t.xml_eq(
+ data,
+ // begin document
+ '<entry xmlns="http://www.w3.org/2005/Atom">' +
+ '<id>urn:uuid:82ede847-b31a-4e3d-b773-7471bad154ed</id>' +
+ '<title>untitled</title>' +
+ '<georss:where xmlns:georss="http://www.georss.org/georss">' +
+ ' <gml:Point xmlns:gml="http://www.opengis.net/gml">' +
+ ' <gml:pos>45.68 -111.04</gml:pos>' +
+ ' </gml:Point>' +
+ '</georss:where>' +
+ '</entry>',
+ // end document
+ 'Writes an entry doc with a point location'
+ );
+ }
+
+ // write entry 7: text type content
+ function test_writeentry7(t) {
+ t.plan(1);
+ var writer = new OpenLayers.Format.Atom();
+ var feature = new OpenLayers.Feature.Vector(null, {title: "Test", description: "A testing feature", atom: {title: "Atom test", summary: "An Atom testing feature", updated: "2009-06-02T10:00:00Z", content: {type: "text", value: "Blah, blah, blah"}}});
+ feature.fid = '1';
+ var data = writer.write(feature);
+ t.xml_eq(
+ data,
+ // begin document
+ '<entry xmlns="http://www.w3.org/2005/Atom">' +
+ '<content type="text">Blah, blah, blah</content>' +
+ '<id>1</id>' +
+ '<summary>An Atom testing feature</summary>' +
+ '<title>Atom test</title>' +
+ '<updated>2009-06-02T10:00:00Z</updated>' +
+ '</entry>',
+ // end document
+ 'Writes an entry doc with Atom constructs overriding well-known atts'
+ );
+ }
+
+ // write entry 8: +xml type content
+ function test_writeentry8(t) {
+ t.plan(1);
+ var kml = new OpenLayers.Format.KML();
+ kml.foldersName = "A folder";
+ kml.foldersDesc = "It's a folder";
+ var kmlDoc = kml.createElementNS(kml.kmlns, "kml");
+ var kmlFolder = kml.createFolderXML();
+ kmlDoc.appendChild(kmlFolder);
+ var writer = new OpenLayers.Format.Atom();
+ var feature = new OpenLayers.Feature.Vector(null, {title: "Test", description: "A testing feature", atom: {title: "Atom test", summary: "An Atom testing feature", updated: "2009-06-02T10:00:00Z", content: {type: "application/vnd.google-earth.kml+xml", value: kmlDoc}}});
+ feature.fid = '1';
+ var data = writer.write(feature);
+ t.xml_eq(
+ data,
+ // begin document
+ '<entry xmlns="http://www.w3.org/2005/Atom">' +
+ '<content type="application/vnd.google-earth.kml+xml"><kml xmlns="http://earth.google.com/kml/2.0"><Folder><name>A folder</name><description>It\'s a folder</description></Folder></kml></content>' +
+ '<id>1</id>' +
+ '<summary>An Atom testing feature</summary>' +
+ '<title>Atom test</title>' +
+ '<updated>2009-06-02T10:00:00Z</updated>' +
+ '</entry>',
+ // end document
+ 'Writes an entry doc with Atom constructs overriding well-known atts'
+ );
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/CQL.html b/misc/openlayers/tests/Format/CQL.html
new file mode 100644
index 0000000..7b31eab
--- /dev/null
+++ b/misc/openlayers/tests/Format/CQL.html
@@ -0,0 +1,364 @@
+<html>
+ <head>
+ <script src="../OLLoader.js"></script>
+
+ <script type="text/javascript">
+
+function test_CQL_Constructor(t) {
+ t.plan(5);
+ var options = {'foo': 'bar'};
+ var format = new OpenLayers.Format.CQL(options);
+ t.ok(format instanceof OpenLayers.Format.CQL,
+ "new OpenLayers.Format.CQL object");
+ t.eq(format.foo, "bar", "constructor sets options correctly")
+ t.eq(typeof format.read, 'function', 'format has a read function');
+ t.eq(typeof format.write, 'function', 'format has a write function');
+ t.eq(format.options, options, "format.options correctly set");
+}
+
+function test_Comparison_string(t) {
+ t.plan(5);
+ var test_cql, format, filter;
+ test_cql = "X >= 'B'";
+ format = new OpenLayers.Format.CQL();
+ filter = format.read(test_cql);
+ t.ok(filter instanceof OpenLayers.Filter.Comparison,
+ "Parsing a simple >= filter produces a Filter.Comparison");
+ t.eq(filter.type, OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,
+ ">= parsed as Filter.Comparison.GREATER_THAN_OR_EQUAL_TO");
+ t.eq(filter.property, 'X',
+ "Property extracted from CQL text");
+ t.eq(filter.value, 'B',
+ "Value extracted from CQL text");
+
+
+ t.eq(format.write(filter), test_cql, "write returned test cql");
+}
+
+function test_read_whitespace(t) {
+ t.plan(4);
+ var cql = "TYPEDESC = 'BOE Numbered Plans'";
+ var format = new OpenLayers.Format.CQL();
+ var filter = format.read(cql);
+ t.ok(filter instanceof OpenLayers.Filter.Comparison, "filter parsed correctly with whitespace in string");
+ t.eq(filter.property, 'TYPEDESC', "filter property parsed correctly");
+ t.eq(filter.value, 'BOE Numbered Plans', "value parsed correctly");
+ t.eq(filter.type, '==', 'filter type parsed correctly');
+}
+
+function test_read_escaped_quotes(t) {
+ t.plan(14);
+ var cql = "PROP = 'don''t worry' or PROP = 'value''s value' or PROP = 'foo'";
+ var format = new OpenLayers.Format.CQL();
+
+ var filter = format.read(cql);
+ t.ok(filter instanceof OpenLayers.Filter.Logical, "filter type");
+ t.eq(filter.filters.length, 2, "filter children");
+
+ var f0 = filter.filters[0];
+ t.ok(f0 instanceof OpenLayers.Filter.Logical, "f0 type");
+ t.eq(f0.filters.length, 2, "f0 children");
+
+ var f00 = f0.filters[0];
+ t.eq(f00.property, "PROP", "f000 property");
+ t.eq(f00.type, "==", "f000 type");
+ t.eq(f00.value, "don't worry", "f000 value");
+
+ var f01 = f0.filters[1];
+ t.eq(f01.property, "PROP", "f001 property");
+ t.eq(f01.type, "==", "f001 type");
+ t.eq(f01.value, "value's value", "f001 value");
+
+ var f1 = filter.filters[1];
+ t.ok(f1 instanceof OpenLayers.Filter.Comparison, "f1 type");
+ t.eq(f1.property, "PROP", "f1 property");
+ t.eq(f1.type, "==", "f1 type");
+ t.eq(f1.value, "foo", "f1 value");
+}
+
+function test_write_escaped_quotes(t) {
+ t.plan(1);
+ var filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.OR,
+ filters: [
+ new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "PROP",
+ value: "quot'd string"
+ }),
+ new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "PROP",
+ value: "don't quote's"
+ })
+ ]
+ });
+ var format = new OpenLayers.Format.CQL();
+ var cql = format.write(filter);
+ t.eq(cql, "(PROP = 'quot''d string') OR (PROP = 'don''t quote''s')", "escaped");
+}
+
+function test_Comparison_number(t) {
+ t.plan(5);
+ var test_cql, format, filter;
+ test_cql = "X >= 10";
+ format = new OpenLayers.Format.CQL();
+ filter = format.read(test_cql);
+ t.ok(filter instanceof OpenLayers.Filter.Comparison,
+ "Parsing a simple >= filter produces a Filter.Comparison");
+ t.eq(filter.type, OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,
+ ">= parsed as Filter.Comparison.GREATER_THAN_OR_EQUAL_TO");
+ t.eq(filter.property, 'X',
+ "Property extracted from CQL text");
+ t.eq(filter.value, 10,
+ "Value extracted from CQL text");
+
+
+ t.eq(format.write(filter), test_cql, "write returned test cql");
+}
+
+function test_Logical(t) {
+ t.plan(7);
+ var test_cql, format, filter;
+ test_cql = "X >= 'B' AND X < 'M'";
+ format = new OpenLayers.Format.CQL();
+ filter = format.read(test_cql);
+ t.ok(filter instanceof OpenLayers.Filter.Logical,
+ "Parsing ANDed filters produces a Filter.Logical");
+ t.eq(filter.type, OpenLayers.Filter.Logical.AND,
+ "AND parsed as Filter.Logical.AND");
+ t.eq(filter.filters.length, 2,
+ "AND Filter contains two subfilters");
+ t.ok(filter.filters[0] instanceof OpenLayers.Filter.Comparison,
+ "First sub-filter is a Filter.Comparison");
+ t.eq(filter.filters[0].type, OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,
+ "First sub-filter is the first filter in the CQL text");
+ t.ok(filter.filters[1] instanceof OpenLayers.Filter.Comparison,
+ "Second sub-filter is a Filter.Comparison");
+ t.eq(filter.filters[1].type, OpenLayers.Filter.Comparison.LESS_THAN,
+ "Second sub-filter is the second filter in the CQL text");
+
+}
+
+function test_Logical_write(t) {
+ t.plan(1);
+ var cql = "(X >= 'B') AND (X < 'M')";
+ var format = new OpenLayers.Format.CQL();
+ var filter = format.read(cql);
+ t.eq(format.write(filter), cql, "write returned test cql");
+}
+
+function test_Logical_spatial(t) {
+ t.plan(9);
+ var test_cql, format, filter;
+ test_cql = "INTERSECTS(the_geom, POLYGON((-111 41,-115 41,-115 45,-110 45,-111 41))) AND CONTAINS(the_geom, POINT(-111 41))";
+ format = new OpenLayers.Format.CQL();
+ filter = format.read(test_cql);
+ t.ok(filter instanceof OpenLayers.Filter.Logical,
+ "Parsing ANDed filters produces a Filter.Logical");
+ t.eq(filter.type, OpenLayers.Filter.Logical.AND,
+ "AND parsed as Filter.Logical.AND");
+ t.eq(filter.filters.length, 2,
+ "AND Filter contains two subfilters");
+ t.ok(filter.filters[0] instanceof OpenLayers.Filter.Spatial,
+ "First sub-filter is a Filter.Spatial");
+ t.eq(filter.filters[0].type, OpenLayers.Filter.Spatial.INTERSECTS,
+ "First sub-filter is the first filter in the CQL text");
+ t.geom_eq(filter.filters[0].value, OpenLayers.Geometry.fromWKT("POLYGON((-111 41,-115 41,-115 45,-110 45,-111 41))"),
+ "First sub-filter is has correct geometry");
+ t.ok(filter.filters[1] instanceof OpenLayers.Filter.Spatial,
+ "Second sub-filter is a Filter.Comparison");
+ t.eq(filter.filters[1].type, OpenLayers.Filter.Spatial.CONTAINS,
+ "Second sub-filter is the second filter in the CQL text");
+ t.geom_eq(filter.filters[1].value, OpenLayers.Geometry.fromWKT("POINT(-111 41)"),
+ "Second sub-filter is has correct geometry");
+}
+
+function test_Logical_spatial_write(t) {
+ // TODO: remove this if extra parentheses are avoided by checking logical operator precedence
+ t.plan(1);
+ var cql = "(INTERSECTS(the_geom, POLYGON((-111 41,-115 41,-115 45,-110 45,-111 41)))) AND (CONTAINS(the_geom, POINT(-111 41)))";
+ var format = new OpenLayers.Format.CQL();
+ var filter = format.read(cql);
+ t.eq(format.write(filter), cql, "write returned test cql");
+}
+
+function test_Parentheticals(t) {
+ t.plan(2);
+ var format, cqlA, filterA, cqlB, filterB;
+ format = new OpenLayers.Format.CQL();
+ cqlA = "A = '1' AND B = '2' OR C = '3'";
+ cqlB = "A = '1' AND (B = '2' OR C = '3')";
+ filterA = format.read(cqlA);
+ filterB = format.read(cqlB);
+
+ t.ok(filterA instanceof OpenLayers.Filter.Logical &&
+ filterA.filters[0] instanceof OpenLayers.Filter.Logical &&
+ filterA.filters[1] instanceof OpenLayers.Filter.Comparison,
+ "Unparenthesized expression groups left to right");
+ t.ok(filterB instanceof OpenLayers.Filter.Logical &&
+ filterB.filters[0] instanceof OpenLayers.Filter.Comparison &&
+ filterB.filters[1] instanceof OpenLayers.Filter.Logical,
+ "Parenthesized expression groups as specified by parentheses");
+}
+
+function test_Parentheticals_write(t) {
+ // TODO: remove this if extra parentheses are avoided by checking logical operator precedence
+ t.plan(1);
+ var format = new OpenLayers.Format.CQL();
+ var cql = "(A = '1') AND ((B = '2') OR (C = '3'))";
+ var filter = format.read(cql);
+ t.eq(format.write(filter), cql, "write returned test cql");
+}
+
+function test_BBOX(t) {
+ t.plan(5);
+ var format = new OpenLayers.Format.CQL(),
+ cql = "BBOX(the_geom,1,2,3,4)",
+ filter = format.read(cql);
+ t.ok(filter instanceof OpenLayers.Filter.Spatial,
+ "Parsing BBOX expression produces Filter.Spatial");
+ t.eq(filter.type, OpenLayers.Filter.Spatial.BBOX,
+ "Spatial filter is a bbox filter");
+ t.eq(filter.property, "the_geom",
+ "Property name is as specified in CQL");
+ t.eq(filter.value.toBBOX(), "1,2,3,4",
+ "Value is as specified in CQL");
+
+ t.eq(format.write(filter), cql, "write returned test cql");
+
+}
+
+function test_INTERSECTS(t) {
+ t.plan(5);
+ var format = new OpenLayers.Format.CQL(),
+ cql = "INTERSECTS(the_geom, POINT(1 2))",
+ filter = format.read(cql);
+ t.ok(filter instanceof OpenLayers.Filter.Spatial,
+ "Parsing BBOX expression produces Filter.Spatial");
+ t.eq(filter.type, OpenLayers.Filter.Spatial.INTERSECTS,
+ "Spatial filter is an intersects filter");
+ t.eq(filter.property, "the_geom",
+ "Property name is as specified in CQL");
+ t.ok(filter.value instanceof OpenLayers.Geometry,
+ "Value is a geometry");
+
+ t.eq(format.write(filter), cql, "write returned test cql");
+
+}
+
+function test_WITHIN(t) {
+ t.plan(5);
+ var format = new OpenLayers.Format.CQL(),
+ cql = "WITHIN(the_geom, POLYGON((1 2,3 4,5 6,3 8,1 6,1 2)))",
+ filter = format.read(cql);
+ t.ok(filter instanceof OpenLayers.Filter.Spatial,
+ "Parsing BBOX expression produces Filter.Spatial");
+ t.eq(filter.type, OpenLayers.Filter.Spatial.WITHIN,
+ "Spatial filter is a within filter");
+ t.eq(filter.property, "the_geom",
+ "Property name is as specified in CQL");
+ t.ok(filter.value instanceof OpenLayers.Geometry,
+ "Value is a geometry");
+
+ t.eq(format.write(filter), cql, "write returned test cql");
+
+}
+
+function test_DWITHIN(t) {
+ t.plan(6);
+ var format = new OpenLayers.Format.CQL(),
+ cql = "DWITHIN(the_geom, POINT(1 2), 6)",
+ filter = format.read(cql);
+ t.ok(filter instanceof OpenLayers.Filter.Spatial,
+ "Parsing DWITHIN expression produces Filter.Spatial");
+ t.eq(filter.type, OpenLayers.Filter.Spatial.DWITHIN,
+ "Spatial filter is a DWITHIN filter");
+ t.eq(filter.property, "the_geom",
+ "Property name is as specified in CQL");
+ t.ok(filter.value instanceof OpenLayers.Geometry,
+ "Value is a geometry");
+ t.eq(filter.distance, 6,
+ "Distance is as specified in CQL");
+
+ t.eq(format.write(filter), cql, "write returned test cql");
+
+}
+
+function test_CONTAINS(t) {
+ t.plan(5);
+ var format = new OpenLayers.Format.CQL(),
+ cql = "CONTAINS(the_geom, POINT(1 2))",
+ filter = format.read(cql);
+ t.ok(filter instanceof OpenLayers.Filter.Spatial,
+ "Parsing BBOX expression produces Filter.Spatial");
+ t.eq(filter.type, OpenLayers.Filter.Spatial.CONTAINS,
+ "Spatial filter is a within filter");
+ t.eq(filter.property, "the_geom",
+ "Property name is as specified in CQL");
+ t.ok(filter.value instanceof OpenLayers.Geometry,
+ "Value is a geometry");
+
+ t.eq(format.write(filter), cql, "write returned test cql");
+
+}
+
+function test_NOT(t) {
+ t.plan(4);
+ var format = new OpenLayers.Format.CQL(),
+ cql = "NOT X < 12",
+ filter = format.read(cql);
+ t.ok(filter instanceof OpenLayers.Filter.Logical,
+ "Parsing NOT expression produces Logical.Not");
+ t.eq(filter.type, OpenLayers.Filter.Logical.NOT,
+ "Logical filter is a NOT filter");
+ t.eq(filter.filters[0].property, "X",
+ "Property name is as specified in CQL");
+ t.eq(filter.filters[0].value, 12, "Value is as specified in CQL");
+}
+
+function test_NOT_write(t) {
+ // TODO: remove this if extra parentheses are avoided by checking logical operator precedence
+ t.plan(1);
+ var format = new OpenLayers.Format.CQL(),
+ cql = "NOT (X < 12)",
+ filter = format.read(cql);
+ t.eq(format.write(filter), cql, "write returned test cql");
+}
+
+function test_BETWEEN(t) {
+ t.plan(6);
+ var format = new OpenLayers.Format.CQL(),
+ cql = "A BETWEEN 0 AND 5",
+ filter = format.read(cql);
+ t.ok(filter instanceof OpenLayers.Filter.Comparison,
+ "Parsing BETWEEN expression produces Filter.Comparison");
+ t.eq(filter.type, OpenLayers.Filter.Comparison.BETWEEN,
+ "Comparison filter is a between filter");
+ t.eq(filter.property, "A",
+ "Property name is as specified in CQL");
+ t.eq(filter.lowerBoundary, 0, 'Lower boundary is as specified in CQL');
+ t.eq(filter.upperBoundary, 5, 'Upper bondary is as specified in CQL');
+
+ t.eq(format.write(filter), cql, "write returned test cql");
+
+}
+
+function test_NULL(t) {
+ t.plan(3);
+ var filter = new OpenLayers.Filter.Comparison({
+ property: "GEOM",
+ type: "NULL"
+ });
+ var format = new OpenLayers.Format.CQL();
+ var str = 'GEOM IS NULL';
+ t.eq(format.write(filter), str, "NULL filter written correctly");
+ filter = format.read(str);
+ t.eq(filter.type, OpenLayers.Filter.Comparison.IS_NULL, "filter type is correctly parsed");
+ t.eq(filter.property, "GEOM", "filter property is correctly parsed");
+}
+
+ </script>
+ </head>
+ <body></body>
+</html>
diff --git a/misc/openlayers/tests/Format/CSWGetDomain.html b/misc/openlayers/tests/Format/CSWGetDomain.html
new file mode 100644
index 0000000..1e37826
--- /dev/null
+++ b/misc/openlayers/tests/Format/CSWGetDomain.html
@@ -0,0 +1,23 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(2);
+
+ var format = new OpenLayers.Format.CSWGetDomain();
+ t.ok(format instanceof OpenLayers.Format.CSWGetDomain.v2_0_2, "constructor returns instance with default versioned format");
+
+ format = new OpenLayers.Format.CSWGetDomain({
+ version: "2.0.2"
+ });
+ t.ok(format instanceof OpenLayers.Format.CSWGetDomain.v2_0_2, "constructor returns instance with custom versioned format");
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:512px; height:256px"> </div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/CSWGetDomain/v2_0_2.html b/misc/openlayers/tests/Format/CSWGetDomain/v2_0_2.html
new file mode 100644
index 0000000..ea0be83
--- /dev/null
+++ b/misc/openlayers/tests/Format/CSWGetDomain/v2_0_2.html
@@ -0,0 +1,56 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script src="v2_0_2.js"></script>
+ <script type="text/javascript">
+
+ var format = new OpenLayers.Format.CSWGetDomain();
+
+ function test_write(t) {
+
+ t.plan(1);
+
+ var options = {
+ PropertyName: "type"
+ };
+
+ var result = format.write(options);
+
+ t.eq(result, csw_request, "check value returned by format " +
+ "CSWGetDomain: write method");
+
+ }
+
+
+ function test_read(t) {
+
+ t.plan(9);
+
+ var obj = format.read(csw_response);
+
+ var domainValues = obj.DomainValues;
+ // test getRecordsResponse object
+ t.ok(domainValues, "object contains DomainValues property");
+
+ // test DomainValues
+ t.eq(domainValues.length, 1, "object contains 1 object in DomainValues");
+ var domainValue = domainValues[0];
+ t.eq(domainValue.type, "csw:Record", "check value for attribute type");
+ t.eq(domainValue.PropertyName, "type", "check value for element PropertyName");
+ t.ok(domainValue.ListOfValues, "object contains ListOfValues property");
+
+ // test ListOfValues
+ t.eq(domainValue.ListOfValues.length, 2, "object contains 2 objects " +
+ "in ListOfValues");
+ var val = domainValue.ListOfValues[0];
+ t.ok(val.Value, "object contains Value property");
+ t.eq(val.Value.my_attr, "my_value", "check value for attribute my_attr");
+ t.eq(val.Value.value, "dataset", "check value for element Value");
+
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/CSWGetDomain/v2_0_2.js b/misc/openlayers/tests/Format/CSWGetDomain/v2_0_2.js
new file mode 100644
index 0000000..19e1ad0
--- /dev/null
+++ b/misc/openlayers/tests/Format/CSWGetDomain/v2_0_2.js
@@ -0,0 +1,18 @@
+var csw_request =
+'<csw:GetDomain xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" service="CSW" version="2.0.2">' +
+ '<csw:PropertyName>type</csw:PropertyName>' +
+'</csw:GetDomain>';
+
+var csw_response =
+'<?xml version="1.0" encoding="UTF-8"?>' +
+'<csw:GetDomainResponse xmlns:csw="http://www.opengis.net/cat/csw/2.0.2">' +
+ '<csw:DomainValues type="csw:Record">' +
+ '<csw:PropertyName>type</csw:PropertyName>' +
+ '<csw:ListOfValues>' +
+ '<csw:Value my_attr="my_value">dataset</csw:Value>' +
+ '<csw:Value>service</csw:Value>' +
+ '</csw:ListOfValues>' +
+ '</csw:DomainValues>' +
+'</csw:GetDomainResponse>'
+;
+
diff --git a/misc/openlayers/tests/Format/CSWGetRecords.html b/misc/openlayers/tests/Format/CSWGetRecords.html
new file mode 100644
index 0000000..2b8bc6b
--- /dev/null
+++ b/misc/openlayers/tests/Format/CSWGetRecords.html
@@ -0,0 +1,23 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(2);
+
+ var format = new OpenLayers.Format.CSWGetRecords();
+ t.ok(format instanceof OpenLayers.Format.CSWGetRecords.v2_0_2, "constructor returns instance with default versioned format");
+
+ format = new OpenLayers.Format.CSWGetRecords({
+ version: "2.0.2"
+ });
+ t.ok(format instanceof OpenLayers.Format.CSWGetRecords.v2_0_2, "constructor returns instance with custom versioned format");
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:512px; height:256px"> </div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/CSWGetRecords/v2_0_2.html b/misc/openlayers/tests/Format/CSWGetRecords/v2_0_2.html
new file mode 100644
index 0000000..07e1b96
--- /dev/null
+++ b/misc/openlayers/tests/Format/CSWGetRecords/v2_0_2.html
@@ -0,0 +1,88 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script src="v2_0_2.js"></script>
+ <script type="text/javascript">
+
+ var format = new OpenLayers.Format.CSWGetRecords();
+
+ function test_write(t) {
+
+ t.plan(1);
+
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LIKE,
+ property: "my_prop",
+ value: "my_prop_value"
+ });
+
+ var options = {
+ "resultType": "results",
+ "startPosition": "10",
+ "maxRecords": "20",
+ "Query": {
+ "ElementSetName": {
+ "value": "brief"
+ },
+ "Constraint": {
+ "version": "1.1.0",
+ "Filter": filter
+ }
+ }
+ };
+
+ var result = format.write(options);
+
+ t.eq(result, csw_request, "check value returned by format " +
+ "CSWGetRecords: write method");
+
+ }
+
+
+ function test_read(t) {
+
+ t.plan(17);
+
+ var obj = format.read(csw_response);
+
+ var searchStatus = obj.SearchStatus;
+ var searchResults = obj.SearchResults;
+ var records = obj.records;
+ // test getRecordsResponse object
+ t.ok(searchStatus, "object contains SearchStatus property");
+ t.ok(searchResults, "object contains SearchResults property");
+ t.ok(records, "object contains records property");
+
+ // test SearchResults attributes
+ t.eq(searchResults.numberOfRecordsMatched, 10, "check value for SearchResults.numberOfRecordsMatched");
+ t.eq(searchResults.numberOfRecordsReturned, 2, "check value for SearchResults.numberOfRecordsReturned");
+ t.eq(searchResults.elementSet, "brief", "check value for SearchResults.elementSet");
+ t.eq(searchResults.nextRecord, 3, "check value for SearchResults.nextRecord");
+
+ // test records
+ t.eq(records.length, 2, "object contains 10 records");
+ var testRecord = records[0];
+ t.eq(testRecord.type, "BriefRecord", "check value for record.type");
+ t.eq(testRecord.title, [{value:"Sample title"}], "check value for record.title");
+
+ // test empty subject
+ t.eq(testRecord.subject, [], "Empty subject tags are ignored");
+
+ //test bbox
+ t.eq(testRecord.BoundingBox.length, 2, "object contains 2 BoundingBoxes");
+ var bbox = testRecord.BoundingBox[0];
+ t.ok(bbox, "object contains BoundingBox properties");
+ t.eq(bbox.crs, "::Lambert Azimuthal Projection", "check value for BoundingBox.crs");
+ t.eq(bbox.value, [156, -3, 37, 83], "check value for record.BoundingBox");
+
+ // test gninfo
+ testRecord = records[1];
+ t.ok(testRecord.gninfo, "object contains gninfo properties");
+ t.eq(testRecord.gninfo.schema, "iso19139", "check value for schema property in record.gninfo");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/CSWGetRecords/v2_0_2.js b/misc/openlayers/tests/Format/CSWGetRecords/v2_0_2.js
new file mode 100644
index 0000000..4763aef
--- /dev/null
+++ b/misc/openlayers/tests/Format/CSWGetRecords/v2_0_2.js
@@ -0,0 +1,50 @@
+var csw_request =
+'<csw:GetRecords xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" service="CSW" version="2.0.2" resultType="results" startPosition="10" maxRecords="20" xmlns:gmd="http://www.isotc211.org/2005/gmd">' +
+ '<csw:Query typeNames="csw:Record">' +
+ '<csw:ElementSetName>brief</csw:ElementSetName>' +
+ '<csw:Constraint version="1.1.0">' +
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:PropertyIsLike wildCard="*" singleChar="." escapeChar="!">' +
+ '<ogc:PropertyName>my_prop</ogc:PropertyName>' +
+ '<ogc:Literal>my_prop_value</ogc:Literal>' +
+ '</ogc:PropertyIsLike>' +
+ '</ogc:Filter>' +
+ '</csw:Constraint>' +
+ '</csw:Query>' +
+'</csw:GetRecords>';
+
+var csw_response =
+'<?xml version="1.0" encoding="UTF-8"?>' +
+'<csw:GetRecordsResponse xmlns:csw="http://www.opengis.net/cat/csw/2.0.2">' +
+ '<csw:SearchStatus timestamp="2009-06-08T12:03:34" />' +
+ '<csw:SearchResults numberOfRecordsMatched="10" numberOfRecordsReturned="2" elementSet="brief" nextRecord="3">' +
+ '<csw:BriefRecord xmlns:geonet="http://www.fao.org/geonetwork" xmlns:ows="http://www.opengis.net/ows" xmlns:dc="http://purl.org/dc/elements/1.1/">' +
+ '<dc:identifier>895ac38b-7aef-4a21-b593-b35a6fc7bba9</dc:identifier>' +
+ '<dc:title>Sample title</dc:title>' +
+ '<dc:subject />' +
+ '<dc:subject />' +
+ '<ows:BoundingBox crs="::Lambert Azimuthal Projection">' +
+ '<ows:LowerCorner>156 -3</ows:LowerCorner>' +
+ '<ows:UpperCorner>37 83</ows:UpperCorner>' +
+ '</ows:BoundingBox>' +
+ '<ows:BoundingBox crs="::WGS 1984">' +
+ '<ows:LowerCorner>51.1 -34.6</ows:LowerCorner>' +
+ '<ows:UpperCorner>-17.3 38.2</ows:UpperCorner>' +
+ '</ows:BoundingBox>' +
+ '</csw:BriefRecord>' +
+ '<csw:BriefRecord xmlns:geonet="http://www.fao.org/geonetwork" xmlns:ows="http://www.opengis.net/ows" xmlns:dc="http://purl.org/dc/elements/1.1/">' +
+ '<dc:identifier>8a7245c3-8546-42de-8e6f-8fb8b5fd1bc3</dc:identifier>' +
+ '<dc:title>Second record : sample title</dc:title>' +
+ '<ows:BoundingBox crs="::WGS 1984">' +
+ '<ows:LowerCorner>51.1 -34.6</ows:LowerCorner>' +
+ '<ows:UpperCorner>-17.3 38.2</ows:UpperCorner>' +
+ '</ows:BoundingBox>' +
+ '<geonet:info xmlns:gmd="http://www.isotc211.org/2005/gmd" xmlns:gco="http://www.isotc211.org/2005/gco" xmlns:gts="http://www.isotc211.org/2005/gts" xmlns:gml="http://www.opengis.net/gml">' +
+ '<id>859</id>' +
+ '<schema>iso19139</schema>' +
+ '</geonet:info>' +
+ '</csw:BriefRecord>' +
+ '</csw:SearchResults>' +
+'</csw:GetRecordsResponse>'
+;
+
diff --git a/misc/openlayers/tests/Format/EncodedPolyline.html b/misc/openlayers/tests/Format/EncodedPolyline.html
new file mode 100644
index 0000000..1466347
--- /dev/null
+++ b/misc/openlayers/tests/Format/EncodedPolyline.html
@@ -0,0 +1,372 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var flatPoints;
+ var floats, smallFloats, encodedFloats;
+ var signedIntegers, encodedSignedIntegers;
+ var unsignedIntegers, encodedUnsignedIntegers;
+
+ function resetTestingData() {
+ flatPoints = [38.50000, -120.20000,
+ 40.70000, -120.95000,
+ 43.25200, -126.45300];
+
+ floats = [0.00, 0.15, -0.01, -0.16, 0.16, 0.01];
+ smallFloats = [0.00000, 0.00015, -0.00001, -0.00016, 0.00016, 0.00001];
+ encodedFloats = '?]@^_@A';
+
+ signedIntegers = [0, 15, -1, -16, 16, 1];
+ encodedSignedIntegers = '?]@^_@A';
+
+ unsignedIntegers = [0, 30, 1, 31, 32, 2, 174];
+ encodedUnsignedIntegers = '?]@^_@AmD';
+ }
+
+ var basePoints = new Array(
+ new Array(3850000, -12020000),
+ new Array(4070000, -12095000),
+ new Array(4325200, -12645300)
+ );
+
+ var points = [
+ new OpenLayers.Geometry.Point(-120.200, 38.500),
+ new OpenLayers.Geometry.Point(-120.950, 40.700),
+ new OpenLayers.Geometry.Point(-126.453, 43.252)
+ ];
+
+ var singlePoint = new OpenLayers.Feature.Vector(points[0]);
+
+ var linestring = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LineString(points)
+ );
+
+ var multipoint = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.MultiPoint(points)
+ );
+
+ var linearring = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LinearRing(points)
+ );
+
+ var polygon = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing(points)
+ ])
+ );
+
+ var encoded = "_p~iF~ps|U_ulLnnqC_mqNvxq`@";
+
+ function test_Format_EncodedPolyline_constructor(t) {
+ t.plan(4);
+
+ var options = {'foo': 'bar'};
+ var format = new OpenLayers.Format.EncodedPolyline(options);
+ t.ok(format instanceof OpenLayers.Format.EncodedPolyline,
+ "new OpenLayers.Format.EncodedPolyline returns object" );
+ t.eq(format.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a write function");
+ }
+
+ function test_Format_EncodedPolyline_read(t) {
+ t.plan(5);
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.ok(linestring.geometry.equals(format.read(encoded).geometry),
+ "format correctly reads encoded polyline");
+
+ format = new OpenLayers.Format.EncodedPolyline({
+ geometryType: "multipoint"
+ });
+ t.ok(multipoint.geometry.equals(format.read(encoded).geometry),
+ "format correctly reads encoded multipoint");
+
+ format.geometryType = "linearring";
+ t.ok(linearring.geometry.equals(format.read(encoded).geometry),
+ "format correctly reads encoded linearring");
+
+ format.geometryType = "polygon";
+ t.ok(polygon.geometry.equals(format.read(encoded).geometry),
+ "format correctly reads encoded polygon");
+
+ format.geometryType = "point";
+ t.ok(points[0].equals(format.read(encoded).geometry),
+ "format correctly reads encoded point");
+ }
+
+ function test_Format_EncodedPolyline_decode(t) {
+ t.plan(6);
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ var decodedPoints = format.decode(encoded, 2);
+ for (i in decodedPoints) {
+ var point = basePoints[i];
+ var decodedPoint = decodedPoints[i];
+ t.eq(parseInt(decodedPoint[0] * 1e5), point[0]);
+ t.eq(parseInt(decodedPoint[1] * 1e5), point[1]);
+ }
+ }
+
+ function test_Format_EncodedPolyline_write(t) {
+ t.plan(5);
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.eq(format.write(linestring), encoded,
+ "format correctly writes encoded polyline");
+
+ t.eq(format.write(multipoint), encoded,
+ "format correctly writes encoded multipoint");
+
+ // Different output than encoded,
+ // because polygon closing point is included
+ t.eq(format.write(linearring),
+ "_p~iF~ps|U_ulLnnqC_mqNvxq`@~b_\\ghde@",
+ "format correctly writes encoded linearring");
+
+ t.eq(format.write(polygon),
+ "_p~iF~ps|U_ulLnnqC_mqNvxq`@~b_\\ghde@",
+ "format correctly writes encoded polygon");
+
+ t.eq(format.write(singlePoint), "_p~iF~ps|U",
+ "format correctly writes encoded point");
+ }
+
+ function test_Format_EncodedPolyline_encode(t) {
+ t.plan(1);
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.eq(format.encode(basePoints, 2, 1), encoded);
+ }
+
+ function test_encodeDeltas_returns_expected_value(t) {
+ t.plan(1);
+ resetTestingData();
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.eq(format.encodeDeltas(flatPoints, 2), encoded);
+ }
+
+ function test_decodeDeltas_returns_expected_value(t) {
+ t.plan(1);
+ resetTestingData();
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.eq(format.decodeDeltas(encoded, 2), flatPoints);
+ }
+
+
+
+ function test_encodeFloats_returns_expected_value(t) {
+ t.plan(3);
+ resetTestingData();
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.eq(format.encodeFloats(smallFloats), encodedFloats);
+
+ resetTestingData();
+ t.eq(format.encodeFloats(smallFloats, 1e5), encodedFloats);
+
+ t.eq(format.encodeFloats(floats, 1e2), encodedFloats);
+ }
+
+ function test_decodeFloats_returns_expected_value(t) {
+ t.plan(3);
+ resetTestingData();
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.eq(format.decodeFloats(encodedFloats), smallFloats);
+ t.eq(format.decodeFloats(encodedFloats, 1e5), smallFloats);
+ t.eq(format.decodeFloats(encodedFloats, 1e2), floats);
+ }
+
+
+
+ function test_encodeSignedIntegers_returns_expected_value(t) {
+ t.plan(1);
+ resetTestingData();
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.eq(format.encodeSignedIntegers(
+ signedIntegers), encodedSignedIntegers);
+ }
+
+ function test_decodeSignedIntegers_returns_expected_value(t) {
+ t.plan(1);
+ resetTestingData();
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.eq(format.decodeSignedIntegers(
+ encodedSignedIntegers), signedIntegers);
+ }
+
+
+
+ function test_encodeUnsignedIntegers_returns_expected_value(t) {
+ t.plan(1);
+ resetTestingData();
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.eq(format.encodeUnsignedIntegers(
+ unsignedIntegers), encodedUnsignedIntegers);
+ }
+
+ function test_decodeUnsignedIntegers_returns_expected_value(t) {
+ t.plan(1);
+ resetTestingData();
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.eq(format.decodeUnsignedIntegers(
+ encodedUnsignedIntegers), unsignedIntegers);
+ }
+
+
+
+ function test_encodeFloat_returns_expected_value(t) {
+ t.plan(12);
+ resetTestingData();
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.eq(format.encodeFloat(0.00000), '?');
+ t.eq(format.encodeFloat(-0.00001), '@');
+ t.eq(format.encodeFloat(0.00001), 'A');
+ t.eq(format.encodeFloat(-0.00002), 'B');
+ t.eq(format.encodeFloat(0.00002), 'C');
+ t.eq(format.encodeFloat(0.00015), ']');
+ t.eq(format.encodeFloat(-0.00016), '^');
+
+ t.eq(format.encodeFloat(-0.1, 10), '@');
+ t.eq(format.encodeFloat(0.1, 10), 'A');
+
+ t.eq(format.encodeFloat(16 * 32 / 1e5), '__@');
+ t.eq(format.encodeFloat(16 * 32 * 32 / 1e5), '___@');
+
+ // from the "Encoded Polyline Algorithm Format" page at Google
+ t.eq(format.encodeFloat(-179.9832104), '`~oia@');
+ }
+
+ function test_decodeFloat_returns_expected_value(t) {
+ t.plan(12);
+ resetTestingData();
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.eq(format.decodeFloat('?'), 0.00000);
+ t.eq(format.decodeFloat('@'), -0.00001);
+ t.eq(format.decodeFloat('A'), 0.00001);
+ t.eq(format.decodeFloat('B'), -0.00002);
+ t.eq(format.decodeFloat('C'), 0.00002);
+ t.eq(format.decodeFloat(']'), 0.00015);
+ t.eq(format.decodeFloat('^'), -0.00016);
+
+ t.eq(format.decodeFloat('@', 10), -0.1);
+ t.eq(format.decodeFloat('A', 10), 0.1);
+
+ t.eq(format.decodeFloat('__@'), 16 * 32 / 1e5);
+ t.eq(format.decodeFloat('___@'), 16 * 32 * 32 / 1e5);
+
+ // from the "Encoded Polyline Algorithm Format" page at Google
+ t.eq(format.decodeFloat('`~oia@'), -179.98321);
+ }
+
+
+
+ function test_encodeSignedInteger_returns_expected_value(t) {
+ t.plan(10);
+ resetTestingData();
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.eq(format.encodeSignedInteger(0), '?');
+ t.eq(format.encodeSignedInteger(-1), '@');
+ t.eq(format.encodeSignedInteger(1), 'A');
+ t.eq(format.encodeSignedInteger(-2), 'B');
+ t.eq(format.encodeSignedInteger(2), 'C');
+ t.eq(format.encodeSignedInteger(15), ']');
+ t.eq(format.encodeSignedInteger(-16), '^');
+
+ t.eq(format.encodeSignedInteger(16), '_@');
+ t.eq(format.encodeSignedInteger(16 * 32), '__@');
+ t.eq(format.encodeSignedInteger(16 * 32 * 32), '___@');
+ }
+
+ function test_decodeSignedInteger_returns_expected_value(t) {
+ t.plan(10);
+ resetTestingData();
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.eq(format.decodeSignedInteger('?'), 0);
+ t.eq(format.decodeSignedInteger('@'), -1);
+ t.eq(format.decodeSignedInteger('A'), 1);
+ t.eq(format.decodeSignedInteger('B'), -2);
+ t.eq(format.decodeSignedInteger('C'), 2);
+ t.eq(format.decodeSignedInteger(']'), 15);
+ t.eq(format.decodeSignedInteger('^'), -16);
+
+ t.eq(format.decodeSignedInteger('__@'), 16 * 32);
+ t.eq(format.decodeSignedInteger('___@'), 16 * 32 * 32);
+ t.eq(format.decodeSignedInteger('_@'), 16);
+ }
+
+
+
+ function test_encodeUnsignedInteger_returns_expected_value(t) {
+ t.plan(10);
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.eq(format.encodeUnsignedInteger(0), '?');
+ t.eq(format.encodeUnsignedInteger(1), '@');
+ t.eq(format.encodeUnsignedInteger(2), 'A');
+ t.eq(format.encodeUnsignedInteger(30), ']');
+ t.eq(format.encodeUnsignedInteger(31), '^');
+ t.eq(format.encodeUnsignedInteger(32), '_@');
+
+ t.eq(format.encodeUnsignedInteger(32 * 32), '__@');
+ t.eq(format.encodeUnsignedInteger(5 * 32 * 32), '__D');
+ t.eq(format.encodeUnsignedInteger(32 * 32 * 32), '___@');
+
+ // from the "Encoded Polyline Algorithm Format" page at Google
+ t.eq(format.encodeUnsignedInteger(174), 'mD');
+ }
+
+ function test_decodeUnsignedInteger_returns_expected_value(t) {
+ t.plan(10);
+
+ var format = new OpenLayers.Format.EncodedPolyline();
+
+ t.eq(format.decodeUnsignedInteger('?'), 0);
+ t.eq(format.decodeUnsignedInteger('@'), 1);
+ t.eq(format.decodeUnsignedInteger('A'), 2);
+ t.eq(format.decodeUnsignedInteger(']'), 30);
+ t.eq(format.decodeUnsignedInteger('^'), 31);
+ t.eq(format.decodeUnsignedInteger('_@'), 32);
+
+ t.eq(format.decodeUnsignedInteger('__@'), 32 * 32);
+ t.eq(format.decodeUnsignedInteger('__D'), 5 * 32 * 32);
+ t.eq(format.decodeUnsignedInteger('___@'), 32 * 32 * 32);
+
+ // from the "Encoded Polyline Algorithm Format" page at Google
+ t.eq(format.decodeUnsignedInteger('mD'), 174);
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/Filter.html b/misc/openlayers/tests/Format/Filter.html
new file mode 100644
index 0000000..69a0564
--- /dev/null
+++ b/misc/openlayers/tests/Format/Filter.html
@@ -0,0 +1,21 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(3);
+
+ var options = {'foo': 'bar'};
+ var format = new OpenLayers.Format.Filter(options);
+ t.ok(format instanceof OpenLayers.Format.Filter,
+ "new OpenLayers.Format.Filter returns object" );
+ t.eq(format.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof format.read, "function", "format has a read function");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/Filter/v1.html b/misc/openlayers/tests/Format/Filter/v1.html
new file mode 100644
index 0000000..a862955
--- /dev/null
+++ b/misc/openlayers/tests/Format/Filter/v1.html
@@ -0,0 +1,404 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_PropertyIsBetween(t) {
+
+ t.plan(6);
+
+ var test_xml, parser, xml;
+
+ parser = new OpenLayers.Format.Filter.v1();
+ xml = new OpenLayers.Format.XML();
+
+ test_xml =
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:PropertyIsBetween>' +
+ '<ogc:PropertyName>number</ogc:PropertyName>' +
+ '<ogc:LowerBoundary>' +
+ '<ogc:Literal>0</ogc:Literal>' +
+ '</ogc:LowerBoundary>' +
+ '<ogc:UpperBoundary>' +
+ '<ogc:Literal>100</ogc:Literal>' +
+ '</ogc:UpperBoundary>' +
+ '</ogc:PropertyIsBetween>' +
+ '</ogc:Filter>';
+
+ var filter = parser.read(xml.read(test_xml).documentElement);
+ t.eq(filter.type, OpenLayers.Filter.Comparison.BETWEEN,
+ "[0] read correct type");
+ t.eq(filter.lowerBoundary, 0,
+ "[0] record correct lower boundary value");
+ t.eq(filter.upperBoundary, 100,
+ "[0] record correct upper boundary value");
+
+ test_xml =
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:PropertyIsBetween>' +
+ '<ogc:PropertyName>number</ogc:PropertyName>' +
+ '<ogc:LowerBoundary>0</ogc:LowerBoundary>' +
+ '<ogc:UpperBoundary>100</ogc:UpperBoundary>' +
+ '</ogc:PropertyIsBetween>' +
+ '</ogc:Filter>';
+
+ var filter = parser.read(xml.read(test_xml).documentElement);
+ t.eq(filter.type, OpenLayers.Filter.Comparison.BETWEEN,
+ "[1] read correct type");
+ t.eq(filter.lowerBoundary, 0,
+ "[1] record correct lower boundary value");
+ t.eq(filter.upperBoundary, 100,
+ "[1] record correct upper boundary value");
+ }
+
+ function test_PropertyIsNull(t) {
+
+ t.plan(3);
+
+ var format, test_xml, xml, filter, node;
+
+ format = new OpenLayers.Format.Filter.v1();
+
+ test_xml =
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:PropertyIsNull>' +
+ '<ogc:PropertyName>prop</ogc:PropertyName>' +
+ '</ogc:PropertyIsNull>' +
+ '</ogc:Filter>';
+
+ // Test reading a PropertyIsNull filter from an XML doc
+ xml = new OpenLayers.Format.XML();
+ filter = format.read(xml.read(test_xml).documentElement);
+ t.eq(filter.type, OpenLayers.Filter.Comparison.IS_NULL,
+ "[0] read correct type");
+ t.eq(filter.property, 'prop',
+ "[0] record correct property name");
+
+ // Test writing a PropertyIsNull filter out to XML
+ filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.IS_NULL,
+ property: "prop"
+ });
+ node = format.write(filter);
+ t.xml_eq(node, test_xml, "filter correctly written");
+
+ }
+
+ function test_Intersects(t) {
+
+ t.plan(4);
+
+ var str =
+ '<Filter xmlns="http://www.opengis.net/ogc">' +
+ '<Intersects>' +
+ '<PropertyName>Geometry</PropertyName>' +
+ '<gml:Polygon xmlns:gml="http://www.opengis.net/gml">' +
+ '<gml:outerBoundaryIs>' +
+ '<gml:LinearRing>' +
+ '<gml:coordinates decimal="." cs="," ts=" ">2488789,289552 2588789,289552 2588789,389552 2488789,389552 2488789,289552</gml:coordinates>' +
+ '</gml:LinearRing>' +
+ '</gml:outerBoundaryIs>' +
+ '</gml:Polygon>' +
+ '</Intersects>' +
+ '</Filter>';
+
+ var format = new OpenLayers.Format.Filter.v1_0_0();
+ var filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.INTERSECTS,
+ property: "Geometry",
+ value: OpenLayers.Geometry.fromWKT("POLYGON((2488789 289552, 2588789 289552, 2588789 389552, 2488789 389552, 2488789 289552))")
+ });
+
+ // test writing
+ var node = format.write(filter);
+ t.xml_eq(node, str, "filter correctly written");
+
+ // test reading
+ var doc = (new OpenLayers.Format.XML).read(str);
+ var got = format.read(doc.firstChild);
+ t.eq(got.type, filter.type, "read correct type");
+ t.eq(got.property, filter.property, "read correct property");
+ t.geom_eq(got.value, filter.value, "read correct value");
+
+ }
+
+ function test_Within(t) {
+
+ t.plan(4);
+
+ var str =
+ '<Filter xmlns="http://www.opengis.net/ogc">' +
+ '<Within>' +
+ '<PropertyName>Geometry</PropertyName>' +
+ '<gml:Polygon xmlns:gml="http://www.opengis.net/gml">' +
+ '<gml:outerBoundaryIs>' +
+ '<gml:LinearRing>' +
+ '<gml:coordinates decimal="." cs="," ts=" ">2488789,289552 2588789,289552 2588789,389552 2488789,389552 2488789,289552</gml:coordinates>' +
+ '</gml:LinearRing>' +
+ '</gml:outerBoundaryIs>' +
+ '</gml:Polygon>' +
+ '</Within>' +
+ '</Filter>';
+
+ var format = new OpenLayers.Format.Filter.v1_0_0();
+ var filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.WITHIN,
+ property: "Geometry",
+ value: OpenLayers.Geometry.fromWKT("POLYGON((2488789 289552, 2588789 289552, 2588789 389552, 2488789 389552, 2488789 289552))")
+ });
+
+ // test writing
+ var node = format.write(filter);
+ t.xml_eq(node, str, "filter correctly written");
+
+ // test reading
+ var doc = (new OpenLayers.Format.XML).read(str);
+ var got = format.read(doc.firstChild);
+ t.eq(got.type, filter.type, "read correct type");
+ t.eq(got.property, filter.property, "read correct property");
+ t.geom_eq(got.value, filter.value, "read correct value");
+
+ }
+
+ function test_Contains(t) {
+
+ t.plan(4);
+
+ var str =
+ '<Filter xmlns="http://www.opengis.net/ogc">' +
+ '<Contains>' +
+ '<PropertyName>Geometry</PropertyName>' +
+ '<gml:Polygon xmlns:gml="http://www.opengis.net/gml">' +
+ '<gml:outerBoundaryIs>' +
+ '<gml:LinearRing>' +
+ '<gml:coordinates decimal="." cs="," ts=" ">2488789,289552 2588789,289552 2588789,389552 2488789,389552 2488789,289552</gml:coordinates>' +
+ '</gml:LinearRing>' +
+ '</gml:outerBoundaryIs>' +
+ '</gml:Polygon>' +
+ '</Contains>' +
+ '</Filter>';
+
+ var format = new OpenLayers.Format.Filter.v1_0_0();
+ var filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.CONTAINS,
+ property: "Geometry",
+ value: OpenLayers.Geometry.fromWKT("POLYGON((2488789 289552, 2588789 289552, 2588789 389552, 2488789 389552, 2488789 289552))")
+ });
+
+ // test writing
+ var node = format.write(filter);
+ t.xml_eq(node, str, "filter correctly written");
+
+ // test reading
+ var doc = (new OpenLayers.Format.XML).read(str);
+ var got = format.read(doc.firstChild);
+ t.eq(got.type, filter.type, "read correct type");
+ t.eq(got.property, filter.property, "read correct property");
+ t.geom_eq(got.value, filter.value, "read correct value");
+
+ }
+
+ function test_logical_fid(t) {
+ // the Filter Encoding spec doesn't allow for FID filters inside logical filters
+ // however, to be liberal, we will write them without complaining
+ t.plan(3);
+
+ var filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.OR,
+ filters: [
+ new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LIKE,
+ property: "person",
+ value: "me"
+ }),
+ new OpenLayers.Filter.FeatureId({fids: ["foo.1", "foo.2"]})
+ ]
+ });
+ var format = new OpenLayers.Format.Filter.v1_0_0();
+
+ var got = format.write(filter);
+ var exp = readXML("LogicalFeatureId");
+ t.xml_eq(got, exp, "wrote FID filter in logical OR without complaint");
+
+ filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.AND,
+ filters: [
+ new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LIKE,
+ property: "person",
+ value: "me"
+ }),
+ new OpenLayers.Filter.FeatureId({fids: ["foo.1", "foo.2"]})
+ ]
+ });
+ got = format.write(filter);
+ exp = readXML("LogicalFeatureIdAnd");
+ t.xml_eq(got, exp, "wrote FID filter in logical AND without complaint");
+
+ filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.NOT,
+ filters: [
+ new OpenLayers.Filter.FeatureId({fids: ["foo.2"]})
+ ]
+ });
+ got = format.write(filter);
+ exp = readXML("LogicalFeatureIdNot");
+ t.xml_eq(got, exp, "wrote FID filter in logical NOT without complaint");
+ }
+
+ function test_between_literal(t) {
+ t.plan(3);
+
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.BETWEEN,
+ property: "foo",
+ lowerBoundary: 1.0,
+ upperBoundary: 2.0
+ });
+ var format = new OpenLayers.Format.Filter.v1_0_0();
+
+ var exp = format.read(readXML("BetweenLiteral"));
+
+ // confirm that reading works as expected
+ t.eq(exp.property, "foo", "property");
+ t.eq(exp.lowerBoundary, 1.0, "lowerBoundary");
+ t.eq(exp.upperBoundary, 2.0, "upperBoundary");
+ }
+
+
+ function test_date_writing(t) {
+ t.plan(1);
+
+ // ISO 8601: 2010-11-27T18:19:15.123Z
+ var start = new Date(Date.UTC(2010, 10, 27, 18, 19, 15, 123));
+
+ // ISO 8601: 2011-12-27T18:19:15.123Z
+ var end = new Date(Date.UTC(2011, 11, 27, 18, 19, 15, 123));
+
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.BETWEEN,
+ property: "when",
+ lowerBoundary: start,
+ upperBoundary: end
+ });
+
+ var format = new OpenLayers.Format.Filter.v1_0_0();
+
+ var got = format.write(filter);
+ var exp = readXML("BetweenDates");
+ t.xml_eq(got, exp, "comparison filter with dates");
+ }
+
+
+ function test_custom_date_writing(t) {
+ t.plan(1);
+
+ // ISO 8601: 2010-11-27T18:19:15.123Z
+ var start = new Date(Date.UTC(2010, 10, 27, 18, 19, 15, 123));
+
+ // ISO 8601: 2011-12-27T18:19:15.123Z
+ var end = new Date(Date.UTC(2011, 11, 27, 18, 19, 15, 123));
+
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.BETWEEN,
+ property: "when",
+ lowerBoundary: start,
+ upperBoundary: end
+ });
+
+ var format = new OpenLayers.Format.Filter({
+ encodeLiteral: function(value) {
+ // return just the date and not the time portion
+ return OpenLayers.Date.toISOString(value).split("T").shift();
+ }
+ });
+
+ var got = format.write(filter);
+ var exp = readXML("CustomBetweenDates");
+ t.xml_eq(got, exp, "comparison filter with dates");
+ }
+
+
+ function readXML(id) {
+ var xml = document.getElementById(id).firstChild.nodeValue;
+ return new OpenLayers.Format.XML().read(xml).documentElement;
+ }
+
+
+ </script>
+</head>
+<body>
+
+<div id="LogicalFeatureId"><!--
+<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:Or>
+ <ogc:PropertyIsLike wildCard="*" singleChar="." escape="!">
+ <ogc:PropertyName>person</ogc:PropertyName>
+ <ogc:Literal>me</ogc:Literal>
+ </ogc:PropertyIsLike>
+ <ogc:FeatureId fid="foo.1"/>
+ <ogc:FeatureId fid="foo.2"/>
+ </ogc:Or>
+</ogc:Filter>
+--></div>
+<div id="LogicalFeatureIdAnd"><!--
+<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:And>
+ <ogc:PropertyIsLike wildCard="*" singleChar="." escape="!">
+ <ogc:PropertyName>person</ogc:PropertyName>
+ <ogc:Literal>me</ogc:Literal>
+ </ogc:PropertyIsLike>
+ <ogc:FeatureId fid="foo.1"/>
+ <ogc:FeatureId fid="foo.2"/>
+ </ogc:And>
+</ogc:Filter>
+--></div>
+<div id="LogicalFeatureIdNot"><!--
+<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:Not>
+ <ogc:FeatureId fid="foo.2"/>
+ </ogc:Not>
+</ogc:Filter>
+--></div>
+<div id="BetweenLiteral"><!--
+<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:PropertyIsBetween>
+ <ogc:PropertyName>foo</ogc:PropertyName>
+ <ogc:LowerBoundary>
+ <ogc:Literal>1.0</ogc:Literal>
+ </ogc:LowerBoundary>
+ <ogc:UpperBoundary>
+ <ogc:Literal>2.0</ogc:Literal>
+ </ogc:UpperBoundary>
+ </ogc:PropertyIsBetween>
+</ogc:Filter>
+--></div>
+<div id="BetweenDates"><!--
+<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:PropertyIsBetween>
+ <ogc:PropertyName>when</ogc:PropertyName>
+ <ogc:LowerBoundary>
+ <ogc:Literal>2010-11-27T18:19:15.123Z</ogc:Literal>
+ </ogc:LowerBoundary>
+ <ogc:UpperBoundary>
+ <ogc:Literal>2011-12-27T18:19:15.123Z</ogc:Literal>
+ </ogc:UpperBoundary>
+ </ogc:PropertyIsBetween>
+</ogc:Filter>
+--></div>
+<div id="CustomBetweenDates"><!--
+<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:PropertyIsBetween>
+ <ogc:PropertyName>when</ogc:PropertyName>
+ <ogc:LowerBoundary>
+ <ogc:Literal>2010-11-27</ogc:Literal>
+ </ogc:LowerBoundary>
+ <ogc:UpperBoundary>
+ <ogc:Literal>2011-12-27</ogc:Literal>
+ </ogc:UpperBoundary>
+ </ogc:PropertyIsBetween>
+</ogc:Filter>
+--></div>
+
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/Filter/v1_0_0.html b/misc/openlayers/tests/Format/Filter/v1_0_0.html
new file mode 100644
index 0000000..876723f
--- /dev/null
+++ b/misc/openlayers/tests/Format/Filter/v1_0_0.html
@@ -0,0 +1,295 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var test_xml =
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:Or>' +
+ '<ogc:PropertyIsBetween>' +
+ '<ogc:PropertyName>number</ogc:PropertyName>' +
+ '<ogc:LowerBoundary>' +
+ '<ogc:Literal>1064866676</ogc:Literal>' +
+ '</ogc:LowerBoundary>' +
+ '<ogc:UpperBoundary>' +
+ '<ogc:Literal>1065512599</ogc:Literal>' +
+ '</ogc:UpperBoundary>' +
+ '</ogc:PropertyIsBetween>' +
+ '<ogc:PropertyIsLike wildCard="*" singleChar="." escape="!">' +
+ '<ogc:PropertyName>cat</ogc:PropertyName>' +
+ '<ogc:Literal>*dog.food!*good</ogc:Literal>' +
+ '</ogc:PropertyIsLike>' +
+ '<ogc:Not>' +
+ '<ogc:PropertyIsLessThanOrEqualTo>' +
+ '<ogc:PropertyName>FOO</ogc:PropertyName>' +
+ '<ogc:Literal>5000</ogc:Literal>' +
+ '</ogc:PropertyIsLessThanOrEqualTo>' +
+ '</ogc:Not>' +
+ '</ogc:Or>' +
+ '</ogc:Filter>';
+
+ function test_read(t) {
+ t.plan(3);
+
+ var parser = new OpenLayers.Format.Filter.v1_0_0();
+ var xml = new OpenLayers.Format.XML();
+ var filter = parser.read(xml.read(test_xml).documentElement);
+
+ t.ok(filter instanceof OpenLayers.Filter.Logical, "instance of correct class");
+ t.eq(filter.type, OpenLayers.Filter.Logical.OR, "correct type");
+ t.eq(filter.filters.length, 3, "correct number of child filters");
+ }
+
+ function test_write(t) {
+ t.plan(1);
+
+ // read first - testing that write produces the ogc:Filter element above
+ var parser = new OpenLayers.Format.Filter.v1_0_0();
+ var xml = new OpenLayers.Format.XML();
+ var filter = parser.read(xml.read(test_xml).documentElement);
+
+ var node = parser.write(filter);
+ t.xml_eq(node, test_xml, "filter correctly written");
+
+ }
+
+ function test_BBOX(t) {
+ t.plan(1);
+ var filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ property: "the_geom",
+ value: new OpenLayers.Bounds(-180, -90, 180, 90),
+ projection: "EPSG:4326"
+ });
+
+ var out =
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:BBOX>' +
+ '<ogc:PropertyName>the_geom</ogc:PropertyName>' +
+ '<gml:Box xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326">' +
+ '<gml:coordinates decimal="." cs="," ts=" ">-180,-90 180,90</gml:coordinates>' +
+ '</gml:Box>' +
+ '</ogc:BBOX>' +
+ '</ogc:Filter>';
+
+ var parser = new OpenLayers.Format.Filter.v1_0_0();
+ var node = parser.write(filter);
+
+ t.xml_eq(node, out, "bbox correctly written");
+ }
+
+ function test_BBOX_noGeometryName(t) {
+ t.plan(1);
+ // WFS 1.0.0 does not allow BBOX filters without property, but
+ // GeoServer accepts them.
+ var filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ value: new OpenLayers.Bounds(-180, -90, 180, 90),
+ projection: "EPSG:4326"
+ });
+
+ var out =
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:BBOX>' +
+ '<gml:Box xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326">' +
+ '<gml:coordinates decimal="." cs="," ts=" ">-180,-90 180,90</gml:coordinates>' +
+ '</gml:Box>' +
+ '</ogc:BBOX>' +
+ '</ogc:Filter>';
+
+ var parser = new OpenLayers.Format.Filter.v1_0_0();
+ var node = parser.write(filter);
+
+ t.xml_eq(node, out, "bbox correctly written");
+ }
+
+ function test_DWithin(t) {
+
+ t.plan(6);
+
+ var str =
+ '<Filter xmlns="http://www.opengis.net/ogc">' +
+ '<DWithin>' +
+ '<PropertyName>Geometry</PropertyName>' +
+ '<gml:Point xmlns:gml="http://www.opengis.net/gml">' +
+ '<gml:coordinates decimal="." cs="," ts=" ">2488789,289552</gml:coordinates>' +
+ '</gml:Point>' +
+ '<Distance units="m">1000</Distance>' +
+ '</DWithin>' +
+ '</Filter>';
+
+ var format = new OpenLayers.Format.Filter.v1_0_0();
+ var filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.DWITHIN,
+ property: "Geometry",
+ value: new OpenLayers.Geometry.Point(2488789,289552),
+ distance: 1000,
+ distanceUnits: "m"
+ });
+
+ // test writing
+ var node = format.write(filter);
+ t.xml_eq(node, str, "filter correctly written");
+
+ // test reading
+ var doc = (new OpenLayers.Format.XML).read(str);
+ var got = format.read(doc.firstChild);
+ t.eq(got.type, filter.type, "read correct type");
+ t.eq(got.property, filter.property, "read correct property");
+ t.geom_eq(got.value, filter.value, "read correct value");
+ t.eq(got.distance, filter.distance, "read correct distance");
+ t.eq(got.distanceUnits, filter.distanceUnits, "read correct distance units");
+
+ }
+
+ function test_Intersects(t) {
+
+ t.plan(4);
+
+ var str =
+ '<Filter xmlns="http://www.opengis.net/ogc">' +
+ '<Intersects>' +
+ '<PropertyName>Geometry</PropertyName>' +
+ '<gml:Box xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326">' +
+ '<gml:coordinates decimal="." cs="," ts=" ">-180,-90 180,90</gml:coordinates>' +
+ '</gml:Box>' +
+ '</Intersects>' +
+ '</Filter>';
+
+ var format = new OpenLayers.Format.Filter.v1_0_0();
+ var filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.INTERSECTS,
+ property: "Geometry",
+ value: new OpenLayers.Bounds(-180, -90, 180, 90),
+ projection: "EPSG:4326"
+ });
+
+ // test writing
+ var node = format.write(filter);
+ t.xml_eq(node, str, "filter correctly written");
+
+ // test reading
+ var doc = (new OpenLayers.Format.XML).read(str);
+ var got = format.read(doc.firstChild);
+ t.eq(got.type, filter.type, "read correct type");
+ t.eq(got.property, filter.property, "read correct property");
+ t.eq(got.value.toArray(), filter.value.toArray(), "read correct value");
+
+ }
+
+ function test_FilterFunctions(t) {
+ t.plan(2);
+
+ var parser = new OpenLayers.Format.Filter.v1_0_0();
+
+ //test spatial intersects with filter function
+ var filter = new OpenLayers.Filter.Spatial({
+ property: 'the_geom',
+ type: OpenLayers.Filter.Spatial.INTERSECTS,
+ value: new OpenLayers.Filter.Function({
+ name : 'querySingle',
+ params: ['sf:restricted', 'the_geom', 'cat=3']
+ })
+ });
+
+ var out =
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:Intersects>' +
+ '<ogc:PropertyName>the_geom</ogc:PropertyName>' +
+ '<ogc:Function name="querySingle">' +
+ '<ogc:Literal>sf:restricted</ogc:Literal>' +
+ '<ogc:Literal>the_geom</ogc:Literal>' +
+ '<ogc:Literal>cat=3</ogc:Literal>' +
+ '</ogc:Function>' +
+ '</ogc:Intersects>' +
+ '</ogc:Filter>';
+
+
+ var node = parser.write(filter);
+
+ //test writer
+ t.xml_eq(node, out, "spatial intersect filter with functions correctly written");
+
+ //test logical filter with custom function
+ filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.AND,
+ filters: [
+ new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+ property: "FOO",
+ value: new OpenLayers.Filter.Function({
+ name : 'customFunction',
+ params : ['param1', 'param2']
+ })
+ })
+ ]
+ });
+
+ out =
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:And>' +
+ '<ogc:PropertyIsNotEqualTo>' +
+ '<ogc:PropertyName>FOO</ogc:PropertyName>' +
+ '<ogc:Function name="customFunction">' +
+ '<ogc:Literal>param1</ogc:Literal>' +
+ '<ogc:Literal>param2</ogc:Literal>' +
+ '</ogc:Function>' +
+ '</ogc:PropertyIsNotEqualTo>' +
+ '</ogc:And>' +
+ '</ogc:Filter>';
+
+ node = parser.write(filter);
+
+ //test writer
+ t.xml_eq(node, out, "comparison filter with filter functions correctly written");
+
+ }
+
+ function test_NestedFilterFunctions(t) {
+ t.plan(1);
+
+ //test spatial dwithin with nested filter function
+ var filter = new OpenLayers.Filter.Spatial({
+ property: 'the_geom',
+ type: OpenLayers.Filter.Spatial.DWITHIN,
+ value: new OpenLayers.Filter.Function({
+ name : 'collectGeometries',
+ params: [
+ new OpenLayers.Filter.Function({
+ name : 'queryCollection',
+ params: ['sf:roads', 'the_geom', 'INCLUDE']
+ })
+ ]
+ }),
+ distanceUnits: "meters",
+ distance: 200
+ });
+
+ var out =
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:DWithin>' +
+ '<ogc:PropertyName>the_geom</ogc:PropertyName>' +
+ '<ogc:Function name="collectGeometries">' +
+ '<ogc:Function name="queryCollection">' +
+ '<ogc:Literal>sf:roads</ogc:Literal>' +
+ '<ogc:Literal>the_geom</ogc:Literal>' +
+ '<ogc:Literal>INCLUDE</ogc:Literal>' +
+ '</ogc:Function>' +
+ '</ogc:Function>' +
+ '<ogc:Distance units="meters">200</ogc:Distance>' +
+ '</ogc:DWithin>' +
+ '</ogc:Filter>';
+
+ var parser = new OpenLayers.Format.Filter.v1_0_0();
+ var node = parser.write(filter);
+
+ //test writer
+ t.xml_eq(node, out, "spatial dwithin filter with nested functions correctly written");
+ }
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/Filter/v1_1_0.html b/misc/openlayers/tests/Format/Filter/v1_1_0.html
new file mode 100644
index 0000000..68d1ec1
--- /dev/null
+++ b/misc/openlayers/tests/Format/Filter/v1_1_0.html
@@ -0,0 +1,402 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var test_xml =
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:Or>' +
+ '<ogc:PropertyIsBetween>' +
+ '<ogc:PropertyName>number</ogc:PropertyName>' +
+ '<ogc:LowerBoundary>' +
+ '<ogc:Literal>1064866676</ogc:Literal>' +
+ '</ogc:LowerBoundary>' +
+ '<ogc:UpperBoundary>' +
+ '<ogc:Literal>1065512599</ogc:Literal>' +
+ '</ogc:UpperBoundary>' +
+ '</ogc:PropertyIsBetween>' +
+ '<ogc:PropertyIsLike wildCard="*" singleChar="." escapeChar="!">' +
+ '<ogc:PropertyName>cat</ogc:PropertyName>' +
+ '<ogc:Literal>*dog.food!*good</ogc:Literal>' +
+ '</ogc:PropertyIsLike>' +
+ '<ogc:Not>' +
+ '<ogc:PropertyIsLessThanOrEqualTo>' +
+ '<ogc:PropertyName>FOO</ogc:PropertyName>' +
+ '<ogc:Literal>5000</ogc:Literal>' +
+ '</ogc:PropertyIsLessThanOrEqualTo>' +
+ '</ogc:Not>' +
+ '<ogc:PropertyIsEqualTo matchCase="true">' +
+ '<ogc:PropertyName>cat</ogc:PropertyName>' +
+ '<ogc:Literal>dog</ogc:Literal>' +
+ '</ogc:PropertyIsEqualTo>' +
+ '<ogc:PropertyIsEqualTo matchCase="false">' +
+ '<ogc:PropertyName>cat</ogc:PropertyName>' +
+ '<ogc:Literal>dog</ogc:Literal>' +
+ '</ogc:PropertyIsEqualTo>' +
+ '</ogc:Or>' +
+ '</ogc:Filter>';
+
+ function test_read(t) {
+ t.plan(3);
+
+ var parser = new OpenLayers.Format.Filter.v1_1_0();
+ var xml = new OpenLayers.Format.XML();
+ var filter = parser.read(xml.read(test_xml).documentElement);
+
+ t.ok(filter instanceof OpenLayers.Filter.Logical, "instance of correct class");
+ t.eq(filter.type, OpenLayers.Filter.Logical.OR, "correct type");
+ t.eq(filter.filters.length, 5, "correct number of child filters");
+ }
+
+ function test_write(t) {
+ t.plan(1);
+
+ // read first - testing that write produces the ogc:Filter element above
+ var parser = new OpenLayers.Format.Filter.v1_1_0();
+ var xml = new OpenLayers.Format.XML();
+ var filter = parser.read(xml.read(test_xml).documentElement);
+
+ var node = parser.write(filter);
+ t.xml_eq(node, test_xml, "filter correctly written");
+
+ }
+
+ function test_matchCase(t) {
+ var parser = new OpenLayers.Format.Filter.v1_1_0();
+ var xml = new OpenLayers.Format.XML();
+
+ var cases = [{
+ str:
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:PropertyIsEqualTo>' +
+ '<ogc:PropertyName>cat</ogc:PropertyName>' +
+ '<ogc:Literal>dog</ogc:Literal>' +
+ '</ogc:PropertyIsEqualTo>' +
+ '</ogc:Filter>',
+ exp: true
+ }, {
+ str:
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:PropertyIsEqualTo matchCase="1">' +
+ '<ogc:PropertyName>cat</ogc:PropertyName>' +
+ '<ogc:Literal>dog</ogc:Literal>' +
+ '</ogc:PropertyIsEqualTo>' +
+ '</ogc:Filter>',
+ exp: true
+ }, {
+ str:
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:PropertyIsEqualTo matchCase="true">' +
+ '<ogc:PropertyName>cat</ogc:PropertyName>' +
+ '<ogc:Literal>dog</ogc:Literal>' +
+ '</ogc:PropertyIsEqualTo>' +
+ '</ogc:Filter>',
+ exp: true
+ }, {
+ str:
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:PropertyIsEqualTo matchCase="0">' +
+ '<ogc:PropertyName>cat</ogc:PropertyName>' +
+ '<ogc:Literal>dog</ogc:Literal>' +
+ '</ogc:PropertyIsEqualTo>' +
+ '</ogc:Filter>',
+ exp: false
+ }, {
+ str:
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:PropertyIsEqualTo matchCase="0">' +
+ '<ogc:PropertyName>cat</ogc:PropertyName>' +
+ '<ogc:Literal>dog</ogc:Literal>' +
+ '</ogc:PropertyIsEqualTo>' +
+ '</ogc:Filter>',
+ exp: false
+ }, {
+ str:
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:PropertyIsNotEqualTo matchCase="true">' +
+ '<ogc:PropertyName>cat</ogc:PropertyName>' +
+ '<ogc:Literal>dog</ogc:Literal>' +
+ '</ogc:PropertyIsNotEqualTo>' +
+ '</ogc:Filter>',
+ exp: true
+ }, {
+ str:
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:PropertyIsNotEqualTo matchCase="false">' +
+ '<ogc:PropertyName>cat</ogc:PropertyName>' +
+ '<ogc:Literal>dog</ogc:Literal>' +
+ '</ogc:PropertyIsNotEqualTo>' +
+ '</ogc:Filter>',
+ exp: false
+ }];
+
+ t.plan(cases.length);
+
+ var filter, c;
+ for(var i=0; i<cases.length; ++i) {
+ c = cases[i];
+ filter = parser.read(xml.read(c.str).documentElement);
+ t.eq(filter.matchCase, c.exp, "case " + i);
+ }
+
+ }
+
+ function test_BBOX(t) {
+ t.plan(1);
+ var filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ property: "the_geom",
+ value: new OpenLayers.Bounds(-180, -90, 180, 90),
+ projection: "EPSG:4326"
+ });
+
+ var out =
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:BBOX>' +
+ '<ogc:PropertyName>the_geom</ogc:PropertyName>' +
+ '<gml:Envelope xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326">' +
+ '<gml:lowerCorner>-180 -90</gml:lowerCorner>' +
+ '<gml:upperCorner>180 90</gml:upperCorner>' +
+ '</gml:Envelope>' +
+ '</ogc:BBOX>' +
+ '</ogc:Filter>';
+
+ var parser = new OpenLayers.Format.Filter.v1_1_0();
+ var node = parser.write(filter);
+
+ t.xml_eq(node, out, "bbox correctly written");
+ }
+
+ function test_BBOX_noGeometryName(t) {
+ t.plan(1);
+ var filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ value: new OpenLayers.Bounds(-180, -90, 180, 90),
+ projection: "EPSG:4326"
+ });
+
+ var out =
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:BBOX>' +
+ '<gml:Envelope xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326">' +
+ '<gml:lowerCorner>-180 -90</gml:lowerCorner>' +
+ '<gml:upperCorner>180 90</gml:upperCorner>' +
+ '</gml:Envelope>' +
+ '</ogc:BBOX>' +
+ '</ogc:Filter>';
+
+ var parser = new OpenLayers.Format.Filter.v1_1_0();
+ var node = parser.write(filter);
+
+ t.xml_eq(node, out, "bbox correctly written");
+ }
+
+
+ function test_Intersects(t) {
+
+ t.plan(4);
+
+ var str =
+ '<Filter xmlns="http://www.opengis.net/ogc">' +
+ '<Intersects>' +
+ '<PropertyName>Geometry</PropertyName>' +
+ '<gml:Envelope xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326">' +
+ '<gml:lowerCorner>-180 -90</gml:lowerCorner>' +
+ '<gml:upperCorner>180 90</gml:upperCorner>' +
+ '</gml:Envelope>' +
+ '</Intersects>' +
+ '</Filter>';
+
+ var format = new OpenLayers.Format.Filter.v1_1_0();
+ var filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.INTERSECTS,
+ property: "Geometry",
+ value: new OpenLayers.Bounds(-180, -90, 180, 90),
+ projection: "EPSG:4326"
+ });
+
+ // test writing
+ var node = format.write(filter);
+ t.xml_eq(node, str, "filter correctly written");
+
+ // test reading
+ var doc = (new OpenLayers.Format.XML).read(str);
+ var got = format.read(doc.firstChild);
+ t.eq(got.type, filter.type, "read correct type");
+ t.eq(got.property, filter.property, "read correct property");
+ t.eq(got.value.toArray(), filter.value.toArray(), "read correct value");
+
+ }
+
+ function test_FilterFunctions(t) {
+ t.plan(2);
+
+ var parser = new OpenLayers.Format.Filter.v1_1_0();
+
+ //test spatial intersects with filter function
+ var filter = new OpenLayers.Filter.Spatial({
+ property: 'the_geom',
+ type: OpenLayers.Filter.Spatial.INTERSECTS,
+ value: new OpenLayers.Filter.Function({
+ name : 'querySingle',
+ params: ['sf:restricted', 'the_geom', 'cat=3']
+ })
+ });
+
+ var out =
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:Intersects>' +
+ '<ogc:PropertyName>the_geom</ogc:PropertyName>' +
+ '<ogc:Function name="querySingle">' +
+ '<ogc:Literal>sf:restricted</ogc:Literal>' +
+ '<ogc:Literal>the_geom</ogc:Literal>' +
+ '<ogc:Literal>cat=3</ogc:Literal>' +
+ '</ogc:Function>' +
+ '</ogc:Intersects>' +
+ '</ogc:Filter>';
+
+
+ var node = parser.write(filter);
+
+ //test writer
+ t.xml_eq(node, out, "spatial intersect filter with functions correctly written");
+
+ //test logical filter with custom function
+ filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.AND,
+ filters: [
+ new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+ matchCase: false,
+ property: "FOO",
+ value: new OpenLayers.Filter.Function({
+ name : 'customFunction',
+ params : ['param1', 'param2']
+ })
+ })
+ ]
+ });
+
+ out =
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:And>' +
+ '<ogc:PropertyIsNotEqualTo matchCase="false">' +
+ '<ogc:PropertyName>FOO</ogc:PropertyName>' +
+ '<ogc:Function name="customFunction">' +
+ '<ogc:Literal>param1</ogc:Literal>' +
+ '<ogc:Literal>param2</ogc:Literal>' +
+ '</ogc:Function>' +
+ '</ogc:PropertyIsNotEqualTo>' +
+ '</ogc:And>' +
+ '</ogc:Filter>';
+
+ node = parser.write(filter);
+
+ //test writer
+ t.xml_eq(node, out, "comparison filter with filter functions correctly written");
+
+ }
+
+ function test_NestedFilterFunctions(t) {
+ t.plan(1);
+
+ //test spatial dwithin with nested filter function
+ var filter = new OpenLayers.Filter.Spatial({
+ property: 'the_geom',
+ type: OpenLayers.Filter.Spatial.DWITHIN,
+ value: new OpenLayers.Filter.Function({
+ name : 'collectGeometries',
+ params: [
+ new OpenLayers.Filter.Function({
+ name : 'queryCollection',
+ params: ['sf:roads', 'the_geom', 'INCLUDE']
+ })
+ ]
+ }),
+ distanceUnits: "meters",
+ distance: 200
+ });
+
+ var out =
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:DWithin>' +
+ '<ogc:PropertyName>the_geom</ogc:PropertyName>' +
+ '<ogc:Function name="collectGeometries">' +
+ '<ogc:Function name="queryCollection">' +
+ '<ogc:Literal>sf:roads</ogc:Literal>' +
+ '<ogc:Literal>the_geom</ogc:Literal>' +
+ '<ogc:Literal>INCLUDE</ogc:Literal>' +
+ '</ogc:Function>' +
+ '</ogc:Function>' +
+ '<ogc:Distance units="meters">200</ogc:Distance>' +
+ '</ogc:DWithin>' +
+ '</ogc:Filter>';
+
+ var parser = new OpenLayers.Format.Filter.v1_1_0();
+ var node = parser.write(filter);
+
+ //test writer
+ t.xml_eq(node, out, "spatial dwithin filter with nested functions correctly written");
+ }
+
+ function test_write_like_matchcase(t) {
+ t.plan(1);
+
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LIKE,
+ property: "person",
+ value: "*me*",
+ matchCase: false
+ });
+
+ var format = new OpenLayers.Format.Filter.v1_1_0();
+
+ var got = format.write(filter);
+ var exp = readXML("LikeMatchCase");
+ t.xml_eq(got, exp, "wrote matchCase attribute on PropertyIsLike");
+ }
+
+ function readXML(id) {
+ var xml = document.getElementById(id).firstChild.nodeValue;
+ return new OpenLayers.Format.XML().read(xml).documentElement;
+ }
+
+ function test_SortBy(t) {
+ t.plan(1);
+
+ var out =
+ '<ogc:SortBy xmlns:ogc="http://www.opengis.net/ogc">'+
+ '<ogc:SortProperty>'+
+ '<ogc:PropertyName>Title</ogc:PropertyName>'+
+ '<ogc:SortOrder>ASC</ogc:SortOrder>'+
+ '</ogc:SortProperty>'+
+ '<ogc:SortProperty>'+
+ '<ogc:PropertyName>Relevance</ogc:PropertyName>'+
+ '<ogc:SortOrder>DESC</ogc:SortOrder>'+
+ '</ogc:SortProperty>'+
+ '</ogc:SortBy>';
+
+ var parser = new OpenLayers.Format.Filter.v1_1_0();
+ var node = parser.writers['ogc'].SortBy.call(parser, [{
+ "property": 'Title',
+ "order": "ASC"
+ },{
+ "property": 'Relevance',
+ "order": "DESC"
+ }]);
+
+ t.xml_eq(node, out, "Check SortBy");
+ }
+ </script>
+</head>
+<body>
+<div id="LikeMatchCase"><!--
+<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:PropertyIsLike wildCard="*" singleChar="." escapeChar="!" matchCase="false">
+ <ogc:PropertyName>person</ogc:PropertyName>
+ <ogc:Literal>*me*</ogc:Literal>
+ </ogc:PropertyIsLike>
+</ogc:Filter>
+--></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/GML.html b/misc/openlayers/tests/Format/GML.html
new file mode 100644
index 0000000..43b8fbd
--- /dev/null
+++ b/misc/openlayers/tests/Format/GML.html
@@ -0,0 +1,462 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_Format_GML_constructor(t) {
+ t.plan(4);
+
+ var options = {'foo': 'bar'};
+ var format = new OpenLayers.Format.GML(options);
+ t.ok(format instanceof OpenLayers.Format.GML,
+ "new OpenLayers.Format.GML returns object" );
+ t.eq(format.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a write function");
+ }
+ function test_Format_GML_getFid(t) {
+ t.plan(2);
+ var parser = new OpenLayers.Format.GML();
+ data = parser.read(test_content[1]);
+ t.eq(data[0].fid, '221', 'fid on polygons set correctly (with whitespace)');
+ t.eq(data[1].fid, '8', 'fid on linestrings set correctly with whitespace');
+ }
+ function test_Format_GML_no_clobber(t) {
+ t.plan(1);
+ var parser = new OpenLayers.Format.GML();
+ data = parser.read(test_content[1]);
+ t.eq(window.i, undefined,
+ "i is undefined in window scope after reading.");
+ }
+ function test_Format_GML_read_3d(t) {
+ t.plan(2);
+ var parser = new OpenLayers.Format.GML();
+ data = parser.read(test_content[0]);
+ t.eq(data[0].geometry.getBounds().toBBOX(), "-1254041.389712,250906.951598,-634517.119991,762236.29408", "Reading 3d content returns geometry with correct bounds (no 0,0)");
+ t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.Polygon", "Reading 3d content returns polygon geometry");
+ }
+ function test_Format_GML_write_geoms(t) {
+ t.plan(5);
+ var parser = new OpenLayers.Format.GML();
+
+ var point = shell_start + serialize_geoms['point'] + shell_end;
+ data = parser.read(point);
+ var output = parser.write(data);
+ var output = output.replace(/<\?[^>]*\?>/, ''); // Remove XML Prolog
+ t.eq(output, point, "Point geometry round trips correctly.");
+
+ var linestring = shell_start + serialize_geoms['linestring'] + shell_end;
+ data = parser.read(linestring);
+ var output = parser.write(data);
+ var output = output.replace(/<\?[^>]*\?>/, ''); // Remove XML Prolog
+ t.eq(output, linestring, "Line geometry round trips correctly.");
+
+ var polygon = shell_start + serialize_geoms['polygon'] + shell_end;
+ data = parser.read(polygon);
+ var output = parser.write(data);
+ output = output.replace(/<\?[^>]*\?>/, ''); // Remove XML Prolog
+ t.eq(output, polygon, "Poly geometry round trips correctly.");
+
+ var multipoint = shell_start + serialize_geoms['multipoint'] + shell_end;
+ data = parser.read(multipoint);
+ var output = parser.write(data);
+ var output = output.replace(/<\?[^>]*\?>/, ''); // Remove XML Prolog
+ t.eq(output, multipoint, "MultiPoint geometry round trips correctly.");
+
+ var multilinestring = shell_start + serialize_geoms['multilinestring'] + shell_end;
+ data = parser.read(multilinestring);
+ var output = parser.write(data);
+ var output = output.replace(/<\?[^>]*\?>/, ''); // Remove XML Prolog
+ t.eq(output, multilinestring, "MultiLine geometry round trips correctly.");
+
+ }
+ function test_Format_GML_read_point_geom(t) {
+ t.plan(3);
+
+ var point = shell_start + geoms['point'] + shell_end;
+ var parser = new OpenLayers.Format.GML();
+ data = parser.read(point);
+ t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.Point", "Point GML returns correct classname");
+ t.eq(data[0].geometry.x, 1, "x coord correct");
+ t.eq(data[0].geometry.y, 2, "y coord correct");
+ }
+ function test_Format_GML_read_linestring_geom(t) {
+ t.plan(5);
+
+ var line = shell_start + geoms['linestring'] + shell_end;
+ var parser = new OpenLayers.Format.GML();
+ data = parser.read(line);
+ t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.LineString", "LineString GML returns correct classname");
+ t.eq(data[0].geometry.components[0].x, 1, "first x coord correct");
+ t.eq(data[0].geometry.components[0].y, 2, "first y coord correct");
+ t.eq(data[0].geometry.components[1].x, 4, "second x coord correct");
+ t.eq(data[0].geometry.components[1].y, 5, "second y coord correct");
+ }
+ function test_Format_GML_read_polygon_geom(t) {
+ t.plan(7);
+
+ var polygon = shell_start + geoms['polygon'] + shell_end;
+ var parser = new OpenLayers.Format.GML();
+ data = parser.read(polygon);
+ t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.Polygon", "Polygon GML returns correct classname");
+ t.eq(data[0].geometry.components[0].components[0].x, 1, "first x coord correct");
+ t.eq(data[0].geometry.components[0].components[0].y, 2, "first y coord correct");
+ t.eq(data[0].geometry.components[0].components[1].x, 4, "second x coord correct");
+ t.eq(data[0].geometry.components[0].components[1].y, 5, "second y coord correct");
+ t.eq(data[0].geometry.components[0].components.length, 4, "coords length correct");
+ t.eq(data[0].geometry.components.length, 1, "rings length correct");
+ }
+ function test_Format_GML_read_multipoint_geom(t) {
+ t.plan(6);
+
+ var multipoint = shell_start + geoms['multipoint'] + shell_end;
+ var parser = new OpenLayers.Format.GML();
+ data = parser.read(multipoint);
+ t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.MultiPoint", "MultiPoint GML returns correct classname");
+ t.eq(data[0].geometry.components[0].x, 1, "x coord correct");
+ t.eq(data[0].geometry.components[0].y, 2, "y coord correct");
+ t.eq(data[0].geometry.components.length, 2, "length correct");
+ t.eq(data[0].geometry.components[1].x, 4, "x coord correct");
+ t.eq(data[0].geometry.components[1].y, 5, "y coord correct");
+ }
+ function test_Format_GML_read_multilinestring_geom(t) {
+ t.plan(6);
+
+ var multilinestring = shell_start + geoms['multilinestring'] + shell_end;
+ var parser = new OpenLayers.Format.GML();
+ data = parser.read(multilinestring);
+ t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.MultiLineString", "MultiLineString GML returns correct classname");
+ t.eq(data[0].geometry.components[0].components[0].x, 1, "x coord correct");
+ t.eq(data[0].geometry.components[0].components[0].y, 2, "y coord correct");
+ t.eq(data[0].geometry.components[0].components.length, 2, "length correct");
+ t.eq(data[0].geometry.components[0].components[1].x, 4, "x coord correct");
+ t.eq(data[0].geometry.components[0].components[1].y, 5, "y coord correct");
+
+ }
+ function test_Format_GML_read_polygon_with_holes_geom(t) {
+ t.plan(12);
+
+ var polygon_with_holes = shell_start + geoms['polygon_with_holes'] + shell_end;
+ var parser = new OpenLayers.Format.GML();
+ data = parser.read(polygon_with_holes);
+ t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.Polygon", "Polygon GML returns correct classname");
+ t.eq(data[0].geometry.components[0].components[0].x, 1, "first x coord correct");
+ t.eq(data[0].geometry.components[0].components[0].y, 2, "first y coord correct");
+ t.eq(data[0].geometry.components[0].components[1].x, 4, "second x coord correct");
+ t.eq(data[0].geometry.components[0].components[1].y, 5, "second y coord correct");
+ t.eq(data[0].geometry.components[0].components.length, 4, "coords length correct");
+ t.eq(data[0].geometry.components[1].components[0].x, 11, "first x coord correct");
+ t.eq(data[0].geometry.components[1].components[0].y, 12, "first y coord correct");
+ t.eq(data[0].geometry.components[1].components[1].x, 14, "second x coord correct");
+ t.eq(data[0].geometry.components[1].components[1].y, 15, "second y coord correct");
+ t.eq(data[0].geometry.components[1].components.length, 4, "coords length correct");
+ t.eq(data[0].geometry.components.length, 2, "rings length correct");
+ }
+ function test_Format_GML_read_attributes(t) {
+ t.plan(3);
+ var parser = new OpenLayers.Format.GML();
+ data = parser.read(test_content[0]);
+ t.eq(data[0].attributes['NAME'], "WY", "Simple Attribute data is read correctly.");
+ t.eq(data[0].attributes['LONGNAME'], "Wyoming", "Attribute data is read from CDATA node correctly.");
+ t.ok(data[0].attributes['EMPTYATTR'] === null, "Attribute set to null for empty element.");
+ }
+ function test_Format_GML_read_envelope_geom(t) {
+ t.plan(13);
+
+ var envelope = shell_start + geoms['envelope'] + shell_end;
+ var parser = new OpenLayers.Format.GML();
+ data = parser.read(envelope);
+ t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.Polygon", "Envelope GML returns correct classname");
+ t.eq(data[0].geometry.components[0].components[0].x, 0, "first x coord correct");
+ t.eq(data[0].geometry.components[0].components[0].y, 1, "first y coord correct");
+ t.eq(data[0].geometry.components[0].components[1].x, 20, "second x coord correct");
+ t.eq(data[0].geometry.components[0].components[1].y, 1, "second y coord correct");
+ t.eq(data[0].geometry.components[0].components[2].x, 20, "third x coord correct");
+ t.eq(data[0].geometry.components[0].components[2].y, 21, "third y coord correct");
+ t.eq(data[0].geometry.components[0].components[3].x, 0, "fouth x coord correct");
+ t.eq(data[0].geometry.components[0].components[3].y, 21, "fourth y coord correct");
+ t.eq(data[0].geometry.components[0].components[4].x, 0, "fifth x coord correct");
+ t.eq(data[0].geometry.components[0].components[4].y, 1, "fifth y coord correct");
+ t.eq(data[0].geometry.components[0].components.length, 5, "coords length correct");
+ t.eq(data[0].geometry.components.length, 1, "rings length correct");
+ }
+ ///////////////////////////////////////////////////////////////
+ // tests the y x order of gml point
+ /////////////////////////////////////////////////////////////
+ function test_Format_GML_read_point_geom_yx(t) {
+ t.plan(3);
+
+ var point = shell_start + geoms_yx['point'] + shell_end;
+ var parser = new OpenLayers.Format.GML({'xy':false});
+ data = parser.read(point);
+ t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.Point", "Point GML returns correct classname");
+ t.eq(data[0].geometry.x, 1, "x coord correct");
+ t.eq(data[0].geometry.y, 2, "y coord correct");
+ }
+ function test_Format_GML_read_linestring_geom_yx(t) {
+ t.plan(5);
+
+ var line = shell_start + geoms_yx['linestring'] + shell_end;
+ var parser = new OpenLayers.Format.GML({'xy':false});
+ data = parser.read(line);
+ t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.LineString", "LineString GML returns correct classname");
+ t.eq(data[0].geometry.components[0].x, 1, "first x coord correct");
+ t.eq(data[0].geometry.components[0].y, 2, "first y coord correct");
+ t.eq(data[0].geometry.components[1].x, 4, "second x coord correct");
+ t.eq(data[0].geometry.components[1].y, 5, "second y coord correct");
+ }
+ function test_Format_GML_read_polygon_geom_yx(t) {
+ t.plan(7);
+
+ var polygon = shell_start + geoms_yx['polygon'] + shell_end;
+ var parser = new OpenLayers.Format.GML({'xy':false});
+ data = parser.read(polygon);
+ t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.Polygon", "Polygon GML returns correct classname");
+ t.eq(data[0].geometry.components[0].components[0].x, 1, "first x coord correct");
+ t.eq(data[0].geometry.components[0].components[0].y, 2, "first y coord correct");
+ t.eq(data[0].geometry.components[0].components[1].x, 4, "second x coord correct");
+ t.eq(data[0].geometry.components[0].components[1].y, 5, "second y coord correct");
+ t.eq(data[0].geometry.components[0].components.length, 4, "coords length correct");
+ t.eq(data[0].geometry.components.length, 1, "rings length correct");
+ }
+ function test_Format_GML_read_multipoint_geom_yx(t) {
+ t.plan(6);
+
+ var multipoint = shell_start + geoms_yx['multipoint'] + shell_end;
+ var parser = new OpenLayers.Format.GML({'xy':false});
+ data = parser.read(multipoint);
+ t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.MultiPoint", "MultiPoint GML returns correct classname");
+ t.eq(data[0].geometry.components[0].x, 1, "x coord correct");
+ t.eq(data[0].geometry.components[0].y, 2, "y coord correct");
+ t.eq(data[0].geometry.components.length, 2, "length correct");
+ t.eq(data[0].geometry.components[1].x, 4, "x coord correct");
+ t.eq(data[0].geometry.components[1].y, 5, "y coord correct");
+ }
+ function test_Format_GML_read_multilinestring_geom_yx(t) {
+ t.plan(6);
+
+ var multilinestring = shell_start + geoms_yx['multilinestring'] + shell_end;
+ var parser = new OpenLayers.Format.GML({'xy':false});
+ data = parser.read(multilinestring);
+ t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.MultiLineString", "MultiLineString GML returns correct classname");
+ t.eq(data[0].geometry.components[0].components[0].x, 1, "x coord correct");
+ t.eq(data[0].geometry.components[0].components[0].y, 2, "y coord correct");
+ t.eq(data[0].geometry.components[0].components.length, 2, "length correct");
+ t.eq(data[0].geometry.components[0].components[1].x, 4, "x coord correct");
+ t.eq(data[0].geometry.components[0].components[1].y, 5, "y coord correct");
+
+ }
+ function test_Format_GML_read_polygon_with_holes_geom_yx(t) {
+ t.plan(12);
+
+ var polygon_with_holes = shell_start + geoms_yx['polygon_with_holes'] + shell_end;
+ var parser = new OpenLayers.Format.GML({'xy':false});
+ data = parser.read(polygon_with_holes);
+ t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.Polygon", "Polygon GML returns correct classname");
+ t.eq(data[0].geometry.components[0].components[0].x, 1, "first x coord correct");
+ t.eq(data[0].geometry.components[0].components[0].y, 2, "first y coord correct");
+ t.eq(data[0].geometry.components[0].components[1].x, 4, "second x coord correct");
+ t.eq(data[0].geometry.components[0].components[1].y, 5, "second y coord correct");
+ t.eq(data[0].geometry.components[0].components.length, 4, "coords length correct");
+ t.eq(data[0].geometry.components[1].components[0].x, 11, "first x coord correct");
+ t.eq(data[0].geometry.components[1].components[0].y, 12, "first y coord correct");
+ t.eq(data[0].geometry.components[1].components[1].x, 14, "second x coord correct");
+ t.eq(data[0].geometry.components[1].components[1].y, 15, "second y coord correct");
+ t.eq(data[0].geometry.components[1].components.length, 4, "coords length correct");
+ t.eq(data[0].geometry.components.length, 2, "rings length correct");
+ }
+
+ function test_Format_GML_read_envelope_geom_yx(t) {
+ t.plan(13);
+
+ var envelope = shell_start + geoms_yx['envelope'] + shell_end;
+ var parser = new OpenLayers.Format.GML({'xy':false});
+ data = parser.read(envelope);
+ t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.Polygon", "Envelope GML returns correct classname");
+ t.eq(data[0].geometry.components[0].components[0].x, 0, "first x coord correct");
+ t.eq(data[0].geometry.components[0].components[0].y, 1, "first y coord correct");
+ t.eq(data[0].geometry.components[0].components[1].x, 20, "second x coord correct");
+ t.eq(data[0].geometry.components[0].components[1].y, 1, "second y coord correct");
+ t.eq(data[0].geometry.components[0].components[2].x, 20, "third x coord correct");
+ t.eq(data[0].geometry.components[0].components[2].y, 21, "third y coord correct");
+ t.eq(data[0].geometry.components[0].components[3].x, 0, "fouth x coord correct");
+ t.eq(data[0].geometry.components[0].components[3].y, 21, "fourth y coord correct");
+ t.eq(data[0].geometry.components[0].components[4].x, 0, "fifth x coord correct");
+ t.eq(data[0].geometry.components[0].components[4].y, 1, "fifth y coord correct");
+ t.eq(data[0].geometry.components[0].components.length, 5, "coords length correct");
+ t.eq(data[0].geometry.components.length, 1, "rings length correct");
+ }
+
+ function test_Format_GML_write_geoms_yx(t) {
+ t.plan(5);
+ var parser = new OpenLayers.Format.GML({'xy':false});
+
+ var point_xy = shell_start + serialize_geoms['point'] + shell_end;
+ var point = shell_start + serialize_geoms_yx['point'] + shell_end;
+ data = parser.read(point);
+ var output = parser.write(data);
+ var output = output.replace(/<\?[^>]*\?>/, ''); // Remove XML Prolog
+ t.eq(output, point_xy, "Point geometry round trips correctly.");
+
+ var linestring_xy = shell_start + serialize_geoms['linestring'] + shell_end;
+ var linestring = shell_start + serialize_geoms_yx['linestring'] + shell_end;
+ data = parser.read(linestring);
+ var output = parser.write(data);
+ var output = output.replace(/<\?[^>]*\?>/, ''); // Remove XML Prolog
+ t.eq(output, linestring_xy, "Line geometry round trips correctly.");
+
+ var polygon_xy = shell_start + serialize_geoms['polygon'] + shell_end;
+ var polygon = shell_start + serialize_geoms_yx['polygon'] + shell_end;
+ data = parser.read(polygon);
+ var output = parser.write(data);
+ output = output.replace(/<\?[^>]*\?>/, ''); // Remove XML Prolog
+ t.eq(output, polygon_xy, "Poly geometry round trips correctly.");
+
+ var multipoint_xy = shell_start + serialize_geoms['multipoint'] + shell_end;
+ var multipoint = shell_start + serialize_geoms_yx['multipoint'] + shell_end;
+ data = parser.read(multipoint);
+ var output = parser.write(data);
+ var output = output.replace(/<\?[^>]*\?>/, ''); // Remove XML Prolog
+ t.eq(output, multipoint_xy, "MultiPoint geometry round trips correctly.");
+
+ var multilinestring_xy = shell_start + serialize_geoms['multilinestring'] + shell_end;
+ var multilinestring = shell_start + serialize_geoms_yx['multilinestring'] + shell_end;
+ data = parser.read(multilinestring);
+ var output = parser.write(data);
+ var output = output.replace(/<\?[^>]*\?>/, ''); // Remove XML Prolog
+ t.eq(output, multilinestring_xy, "MultiLine geometry round trips correctly.");
+ }
+
+ function test_buildGeometryNode_bounds(t) {
+ t.plan(1);
+ var parser = new OpenLayers.Format.GML();
+ var bounds = new OpenLayers.Bounds(-180, -90, 180, 90);
+ var output, expect;
+
+ // test that bounds are written as gml:Box
+ var output = parser.buildGeometryNode(bounds);
+ var expect = '<gml:Box xmlns:gml="http://www.opengis.net/gml"><gml:coordinates decimal="." cs="," ts=" ">-180,-90 180,90</gml:coordinates></gml:Box>';
+ t.xml_eq(output, expect, "[xy true] Bounds correctly written as gml:Box");
+ }
+
+ function test_parseFeatureBox(t) {
+ t.plan(8);
+ var parser = new OpenLayers.Format.GML();
+ var xmlparser = new OpenLayers.Format.XML();
+
+ var data = xmlparser.read(test_box[0]);
+ var feature = parser.parseFeature(data);
+ t.ok(feature.bounds instanceof OpenLayers.Bounds,
+ "got bounds object for feature.bounds when with boundedBy Box, without geometry");
+ t.eq(feature.geometry, null, 'geometry is null for a feature with boundedBy Box, but no geometry');
+
+ var data = xmlparser.read(test_box[1]);
+ feature = parser.parseFeature(data);
+ t.eq(feature.bounds, null,
+ "feature is null when without boundedBy Box, with Box geometry");
+ t.ok(feature.geometry instanceof OpenLayers.Geometry.Polygon,
+ "got polygon object for feature.geometry when without boundedBy Box, with Box geometry");
+
+ data = xmlparser.read(test_box[2]);
+ feature = parser.parseFeature(data);
+ t.eq(feature.bounds, null,
+ "feature.bounds is null when without boundedBy Box, without geometry");
+ t.eq(feature.geometry, null, 'geometry is null when without boundedBy Box, without geometry');
+
+ data = xmlparser.read(test_box[3]);
+ feature = parser.parseFeature(data);
+ t.ok(feature.bounds instanceof OpenLayers.Bounds,
+ "got bounds object for feature.bounds when with boundedBy Box, with Box geometry");
+ t.ok(feature.geometry instanceof OpenLayers.Geometry.Polygon,
+ "got polygon object for feature.geometry when with boundedBy Box, with Box geometry");
+ }
+
+ var test_box = [
+ // with boundedBy Box, without geometry
+ '<Sentiers_littoraux_feature><gml:boundedBy xmlns:gml="http://www.opengis.net/gml"><gml:Box srsName="EPSG:2154"><gml:coordinates>143564.081753,6817901.121957 144209.641321,6819104.781451</gml:coordinates></gml:Box></gml:boundedBy><DEPARTEMENT>Finistère</DEPARTEMENT></Sentiers_littoraux_feature>',
+ // without boundedBy Box, with Box geometry
+ '<Sentiers_littoraux_feature><msGeometry><gml:Box srsName="EPSG:2154" xmlns:gml="http://www.opengis.net/gml"><gml:coordinates>143564.081753,6817901.121957 144209.641321,6819104.781451</gml:coordinates></gml:Box></msGeometry><DEPARTEMENT>Finistère</DEPARTEMENT></Sentiers_littoraux_feature>',
+ // without boundedBy, without geometry
+ '<Sentiers_littoraux_feature><DEPARTEMENT>Finistère</DEPARTEMENT></Sentiers_littoraux_feature>',
+ // with boundedBy Box, with Box geometry
+ '<Sentiers_littoraux_feature><msGeometry><gml:Box srsName="EPSG:2154" xmlns:gml="http://www.opengis.net/gml"><gml:coordinates>143564.081753,6817901.121957 144209.641321,6819104.781451</gml:coordinates></gml:Box></msGeometry><gml:boundedBy xmlns:gml="http://www.opengis.net/gml"><gml:Box srsName="EPSG:2154"><gml:coordinates>143564.081753,6817901.121957 144209.641321,6819104.781451</gml:coordinates></gml:Box></gml:boundedBy><DEPARTEMENT>Finistère</DEPARTEMENT></Sentiers_littoraux_feature>'];
+
+ var test_content = ['<?xml version="1.0" encoding="utf-8" ?>\n<ogr:FeatureCollection\n xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n xsi:schemaLocation="http://ogr.maptools.org/ testoutput.xsd"\n xmlns:ogr="http://ogr.maptools.org/"\n xmlns:gml="http://www.opengis.net/gml">\n <gml:boundedBy>\n <gml:Box>\n <gml:coord><gml:X>-1254041.389711702</gml:X><gml:Y>250906.9515983529</gml:Y></gml:coord>\n <gml:coord><gml:X>-634517.1199908922</gml:X><gml:Y>762236.2940800377</gml:Y></gml:coord>\n </gml:Box>\n </gml:boundedBy> \n <gml:featureMember>\n <ogr:states fid="F0">\n <ogr:geometryProperty><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-634517.11999089224,691849.77929356066,0 -653761.64509297756,471181.53429472551,0 -673343.60852865304,250906.9515983529,0 -1088825.734430399,299284.85108220269,0 -1254041.3897117018,324729.27754874947,0 -1235750.4212498858,434167.33911316615,0 -1190777.7803201093,704392.96327195223,0 -1181607.835811228,762236.29408003774,0 -634517.11999089224,691849.77929356066,0</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>\n <ogr:NAME>WY</ogr:NAME>\n <ogr:EMPTYATTR/><ogr:LONGNAME><![CDATA[Wyoming]]></ogr:LONGNAME>\n </ogr:states>\n </gml:featureMember>\n</ogr:FeatureCollection>\n',
+ '<wfs:FeatureCollection' +
+ ' xmlns:fs="http://example.com/featureserver"' +
+ ' xmlns:wfs="http://www.opengis.net/wfs"' +
+ ' xmlns:gml="http://www.opengis.net/gml"' +
+ ' xmlns:ogc="http://www.opengis.net/ogc"' +
+ ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
+ ' xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengeospatial.net//wfs/1.0.0/WFS-basic.xsd">' +
+ ' ' +
+ '' +
+ ' <gml:featureMember>' +
+ ' \n<fs:scribble fid="221">' +
+ ' <fs:geometry>' +
+ ' <gml:Polygon>' +
+ ' ' +
+ ' <gml:outerBoundaryIs><gml:LinearRing>' +
+ ' <gml:coordinates>149.105072021,-35.1816558838 149.100608826,-35.1844024658 149.098892212,-35.1898956299 149.105072021,-35.1816558838</gml:coordinates>' +
+ ' </gml:LinearRing></gml:outerBoundaryIs>' +
+ ' ' +
+ ' </gml:Polygon>' +
+ ' </fs:geometry>' +
+ ' <fs:title>random test features</fs:title>' +
+ ' </fs:scribble>' +
+ '</gml:featureMember> ' +
+ ' <gml:featureMember><fs:scribble fid="8"> <fs:geometry> <gml:Point><gml:coordinates>-81.38671875,27.0703125</gml:coordinates></gml:Point> </fs:geometry> ' +
+ ' <fs:down>south</fs:down><fs:title>Florida</fs:title> </fs:scribble></gml:featureMember>' +
+ '</wfs:FeatureCollection>'
+ ];
+
+ var shell_start = '<wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs"><gml:featureMember xmlns:gml="http://www.opengis.net/gml"><feature:features xmlns:feature="http://mapserver.gis.umn.edu/mapserver" fid="221"><feature:geometry>';
+ if (OpenLayers.BROWSER_NAME == "opera") {
+ shell_start = '<wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs"><gml:featureMember xmlns:gml="http://www.opengis.net/gml"><feature:features fid="221" xmlns:feature="http://mapserver.gis.umn.edu/mapserver"><feature:geometry>';
+ }
+ var shell_end = '</feature:geometry></feature:features></gml:featureMember></wfs:FeatureCollection>';
+ var serialize_geoms = {
+ 'point': '<gml:Point><gml:coordinates decimal="." cs="," ts=" ">1,2</gml:coordinates></gml:Point>',
+ 'linestring': '<gml:LineString><gml:coordinates decimal="." cs="," ts=" ">1,2 4,5</gml:coordinates></gml:LineString>',
+ 'polygon': '<gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates decimal="." cs="," ts=" ">1,2 4,5 3,6 1,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon>',
+ 'multipoint': '<gml:MultiPoint><gml:pointMember><gml:Point><gml:coordinates decimal="." cs="," ts=" ">1,2</gml:coordinates></gml:Point></gml:pointMember><gml:pointMember><gml:Point><gml:coordinates decimal="." cs="," ts=" ">4,5</gml:coordinates></gml:Point></gml:pointMember></gml:MultiPoint>',
+ 'multilinestring': '<gml:MultiLineString><gml:lineStringMember><gml:LineString><gml:coordinates decimal="." cs="," ts=" ">1,2 4,5</gml:coordinates></gml:LineString></gml:lineStringMember><gml:lineStringMember><gml:LineString><gml:coordinates decimal="." cs="," ts=" ">11,12 14,15</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString>'
+ };
+ var geoms = {
+ 'point': '<gml:Point><gml:coordinates>1,2,3</gml:coordinates></gml:Point>',
+ 'linestring': '<gml:LineString><gml:coordinates>1,2,3 4,5,6</gml:coordinates></gml:LineString>',
+ 'polygon': '<gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>1,2 4,5 3,6 1,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon>',
+ 'polygon_with_holes': '<gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>1,2 4,5 3,6 1,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>11,12 14,15 13,16 11,12</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs></gml:Polygon>',
+ 'multipoint': '<gml:MultiPoint><gml:Point><gml:coordinates>1,2,3</gml:coordinates></gml:Point><gml:Point><gml:coordinates>4,5,6</gml:coordinates></gml:Point></gml:MultiPoint>',
+ 'multilinestring': '<gml:MultiLineString><gml:LineString><gml:coordinates>1,2,3 4,5,6</gml:coordinates></gml:LineString><gml:LineString><gml:coordinates>11,12,13 14,15,16</gml:coordinates></gml:LineString></gml:MultiLineString>',
+ 'envelope': '<gml:Envelope><gml:lowerCorner>0 1</gml:lowerCorner><gml:upperCorner>20 21</gml:upperCorner></gml:Envelope>' // ,
+ // 'multipolygon_with_holes': '<gml:MultiPolygon><gml:Polygon><gml:outerBoundaryIs>1,2 4,5 3,6 1,2</gml:outerBoundaryIs><gml:innerBoundaryIs>11,12 14,15 13,16 11,12</gml:innerBoundaryIs></gml:Polygon><gml:Polygon><gml:outerBoundaryIs>101,102 104,105 103,106 101,102</gml:outerBoundaryIs><gml:innerBoundaryIs>111,112 114,115 113,116 111,112</gml:innerBoundaryIs></gml:Polygon></gml:MultiPolygon>'
+ };
+
+
+//
+// The following data has the (x y) reordered to (y x), in 3 dimensions it goes from (x y z) to (y x z)
+//
+
+ var serialize_geoms_yx = {
+ 'point': '<gml:Point><gml:coordinates decimal="." cs="," ts=" ">2,1</gml:coordinates></gml:Point>',
+ 'linestring': '<gml:LineString><gml:coordinates decimal="." cs="," ts=" ">2,1 5,4</gml:coordinates></gml:LineString>',
+ 'polygon': '<gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates decimal="." cs="," ts=" ">2,1 5,4 6,3 2,1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon>',
+ 'multipoint': '<gml:MultiPoint><gml:pointMember><gml:Point><gml:coordinates decimal="." cs="," ts=" ">2,1</gml:coordinates></gml:Point></gml:pointMember><gml:pointMember><gml:Point><gml:coordinates decimal="." cs="," ts=" ">5,4</gml:coordinates></gml:Point></gml:pointMember></gml:MultiPoint>',
+ 'multilinestring': '<gml:MultiLineString><gml:lineStringMember><gml:LineString><gml:coordinates decimal="." cs="," ts=" ">2,1 5,4</gml:coordinates></gml:LineString></gml:lineStringMember><gml:lineStringMember><gml:LineString><gml:coordinates decimal="." cs="," ts=" ">12,11 15,14</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString>'
+ };
+ var geoms_yx = {
+ 'point': '<gml:Point><gml:coordinates>2,1,3</gml:coordinates></gml:Point>',
+ 'linestring': '<gml:LineString><gml:coordinates>2,1,3 5,4,6</gml:coordinates></gml:LineString>',
+ 'polygon': '<gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2,1 5,4 6,3 2,1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon>',
+ 'polygon_with_holes': '<gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2,1 5,4 6,3 2,1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>12,11 15,14 16,13 12,11</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs></gml:Polygon>',
+ 'multipoint': '<gml:MultiPoint><gml:Point><gml:coordinates>2,1,3</gml:coordinates></gml:Point><gml:Point><gml:coordinates>5,4,6</gml:coordinates></gml:Point></gml:MultiPoint>',
+ 'multilinestring': '<gml:MultiLineString><gml:LineString><gml:coordinates>2,1,3 5,4,6</gml:coordinates></gml:LineString><gml:LineString><gml:coordinates>12,11,13 15,14,16</gml:coordinates></gml:LineString></gml:MultiLineString>',
+ 'envelope': '<gml:Envelope><gml:lowerCorner>1 0</gml:lowerCorner><gml:upperCorner>21 20</gml:upperCorner></gml:Envelope>'
+ };
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/GML/cases.js b/misc/openlayers/tests/Format/GML/cases.js
new file mode 100644
index 0000000..88499cd
--- /dev/null
+++ b/misc/openlayers/tests/Format/GML/cases.js
@@ -0,0 +1,232 @@
+var xml = new OpenLayers.Format.XML();
+function readXML(file) {
+ return xml.read(document.getElementById(file).firstChild.nodeValue);
+}
+
+var cases = {
+
+ "v2/point-coord.xml": new OpenLayers.Geometry.Point(1, 2),
+
+ "v2/point-coordinates.xml": new OpenLayers.Geometry.Point(1, 2),
+
+ "v2/linestring-coord.xml": new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(3, 4)
+ ]),
+
+ "v2/linestring-coordinates.xml": new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(3, 4)
+ ]),
+
+ "v2/linearring-coord.xml": new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(3, 4),
+ new OpenLayers.Geometry.Point(5, 6),
+ new OpenLayers.Geometry.Point(1, 2)
+ ]),
+
+ "v2/linearring-coordinates.xml": new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(3, 4),
+ new OpenLayers.Geometry.Point(5, 6),
+ new OpenLayers.Geometry.Point(1, 2)
+ ]),
+
+ "v2/polygon-coord.xml": new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(3, 4),
+ new OpenLayers.Geometry.Point(5, 6),
+ new OpenLayers.Geometry.Point(1, 2)
+ ]),
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(2, 3),
+ new OpenLayers.Geometry.Point(4, 5),
+ new OpenLayers.Geometry.Point(6, 7),
+ new OpenLayers.Geometry.Point(2, 3)
+ ]),
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(3, 4),
+ new OpenLayers.Geometry.Point(5, 6),
+ new OpenLayers.Geometry.Point(7, 8),
+ new OpenLayers.Geometry.Point(3, 4)
+ ])
+ ]),
+
+ "v2/polygon-coordinates.xml": new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(3, 4),
+ new OpenLayers.Geometry.Point(5, 6),
+ new OpenLayers.Geometry.Point(1, 2)
+ ]),
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(2, 3),
+ new OpenLayers.Geometry.Point(4, 5),
+ new OpenLayers.Geometry.Point(6, 7),
+ new OpenLayers.Geometry.Point(2, 3)
+ ]),
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(3, 4),
+ new OpenLayers.Geometry.Point(5, 6),
+ new OpenLayers.Geometry.Point(7, 8),
+ new OpenLayers.Geometry.Point(3, 4)
+ ])
+ ]),
+
+ "v2/multipoint-coord.xml": new OpenLayers.Geometry.MultiPoint([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(2, 3),
+ new OpenLayers.Geometry.Point(3, 4)
+ ]),
+
+ "v2/multipoint-coordinates.xml": new OpenLayers.Geometry.MultiPoint([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(2, 3),
+ new OpenLayers.Geometry.Point(3, 4)
+ ]),
+
+ "v2/multilinestring-coord.xml": new OpenLayers.Geometry.MultiLineString([
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(2, 3)
+ ]),
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(3, 4),
+ new OpenLayers.Geometry.Point(4, 5)
+ ])
+ ]),
+
+ "v2/multilinestring-coordinates.xml": new OpenLayers.Geometry.MultiLineString([
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(2, 3)
+ ]),
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(3, 4),
+ new OpenLayers.Geometry.Point(4, 5)
+ ])
+ ]),
+
+ "v2/multipolygon-coord.xml": new OpenLayers.Geometry.MultiPolygon([
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(3, 4),
+ new OpenLayers.Geometry.Point(5, 6),
+ new OpenLayers.Geometry.Point(1, 2)
+ ]),
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(2, 3),
+ new OpenLayers.Geometry.Point(4, 5),
+ new OpenLayers.Geometry.Point(6, 7),
+ new OpenLayers.Geometry.Point(2, 3)
+ ]),
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(3, 4),
+ new OpenLayers.Geometry.Point(5, 6),
+ new OpenLayers.Geometry.Point(7, 8),
+ new OpenLayers.Geometry.Point(3, 4)
+ ])
+ ]),
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(3, 4),
+ new OpenLayers.Geometry.Point(5, 6),
+ new OpenLayers.Geometry.Point(1, 2)
+ ])
+ ])
+ ]),
+
+ "v2/multipolygon-coordinates.xml": new OpenLayers.Geometry.MultiPolygon([
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(3, 4),
+ new OpenLayers.Geometry.Point(5, 6),
+ new OpenLayers.Geometry.Point(1, 2)
+ ]),
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(2, 3),
+ new OpenLayers.Geometry.Point(4, 5),
+ new OpenLayers.Geometry.Point(6, 7),
+ new OpenLayers.Geometry.Point(2, 3)
+ ]),
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(3, 4),
+ new OpenLayers.Geometry.Point(5, 6),
+ new OpenLayers.Geometry.Point(7, 8),
+ new OpenLayers.Geometry.Point(3, 4)
+ ])
+ ]),
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(3, 4),
+ new OpenLayers.Geometry.Point(5, 6),
+ new OpenLayers.Geometry.Point(1, 2)
+ ])
+ ])
+ ]),
+
+ "v2/geometrycollection-coordinates.xml": new OpenLayers.Geometry.Collection([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(3, 4)
+ ]),
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(3, 4),
+ new OpenLayers.Geometry.Point(5, 6),
+ new OpenLayers.Geometry.Point(1, 2)
+ ]),
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(2, 3),
+ new OpenLayers.Geometry.Point(4, 5),
+ new OpenLayers.Geometry.Point(6, 7),
+ new OpenLayers.Geometry.Point(2, 3)
+ ]),
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(3, 4),
+ new OpenLayers.Geometry.Point(5, 6),
+ new OpenLayers.Geometry.Point(7, 8),
+ new OpenLayers.Geometry.Point(3, 4)
+ ])
+ ])
+ ]),
+
+ "v2/box-coord.xml": new OpenLayers.Bounds(1, 2, 3, 4),
+
+ "v2/box-coordinates.xml": new OpenLayers.Bounds(1, 2, 3, 4),
+
+ "v3/linestring3d.xml": new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(1, 2, 3),
+ new OpenLayers.Geometry.Point(4, 5, 6)
+ ])
+
+};
+
+// some cases for v3 use the same geometries
+OpenLayers.Util.extend(cases, {
+ "v3/point.xml": cases["v2/point-coordinates.xml"],
+ "v3/linestring.xml": cases["v2/linestring-coordinates.xml"],
+ "v3/curve.xml": cases["v2/linestring-coordinates.xml"],
+ "v3/polygon.xml": cases["v2/polygon-coordinates.xml"],
+ "v3/surface.xml": cases["v2/polygon-coordinates.xml"],
+ "v3/multipoint-singular.xml": cases["v2/multipoint-coordinates.xml"],
+ "v3/multipoint-plural.xml": cases["v2/multipoint-coordinates.xml"],
+ "v3/multilinestring-singular.xml": cases["v2/multilinestring-coordinates.xml"],
+ "v3/multilinestring-plural.xml": cases["v2/multilinestring-coordinates.xml"],
+ "v3/multicurve-singular.xml": cases["v2/multilinestring-coordinates.xml"],
+ "v3/multicurve-curve.xml": cases["v2/multilinestring-coordinates.xml"],
+ "v3/multipolygon-singular.xml": cases["v2/multipolygon-coordinates.xml"],
+ "v3/multipolygon-plural.xml": cases["v2/multipolygon-coordinates.xml"],
+ "v3/multisurface-singular.xml": cases["v2/multipolygon-coordinates.xml"],
+ "v3/multisurface-plural.xml": cases["v2/multipolygon-coordinates.xml"],
+ "v3/multisurface-surface.xml": cases["v2/multipolygon-coordinates.xml"],
+ "v3/envelope.xml": cases["v2/box-coordinates.xml"]
+}); \ No newline at end of file
diff --git a/misc/openlayers/tests/Format/GML/v2.html b/misc/openlayers/tests/Format/GML/v2.html
new file mode 100644
index 0000000..8857d05
--- /dev/null
+++ b/misc/openlayers/tests/Format/GML/v2.html
@@ -0,0 +1,684 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script src="cases.js"></script>
+ <script type="text/javascript">
+
+ function test_readNode_geometry(t) {
+ var files = [
+ "v2/point-coord.xml", "v2/point-coordinates.xml",
+ "v2/linestring-coord.xml", "v2/linestring-coordinates.xml",
+ "v2/polygon-coord.xml", "v2/polygon-coordinates.xml",
+ "v2/multipoint-coord.xml", "v2/multipoint-coordinates.xml",
+ "v2/multilinestring-coord.xml", "v2/multilinestring-coordinates.xml",
+ "v2/multipolygon-coord.xml", "v2/multipolygon-coordinates.xml",
+ "v2/geometrycollection-coordinates.xml"
+ ];
+
+ var len = files.length;
+ t.plan(len);
+
+ var format = new OpenLayers.Format.GML.v2({
+ featureType: "feature",
+ featureNS: "http://example.com/feature"
+ });
+ var file, doc, expect, out;
+ for(var i=0; i<len; ++i) {
+ file = files[i];
+ expect = cases[file];
+ if(expect) {
+ doc = readXML(file);
+ if(doc && doc.documentElement) {
+ out = format.readNode(doc.documentElement);
+ if(out.components && out.components.length == 1) {
+ t.geom_eq(out.components[0], expect, "[" + file + "] geometry read");
+ } else {
+ t.fail("[" + file + "] gml parsing");
+ }
+ } else {
+ t.fail("[" + file + "] xml parsing");
+ }
+ } else {
+ t.fail("[" + file + "] case not found");
+ }
+ }
+
+ }
+
+ function test_readNode_bounds(t) {
+ var files = ["v2/box-coord.xml", "v2/box-coordinates.xml"];
+
+ var len = files.length;
+ t.plan(len);
+
+ var file, doc, expect, got;
+ var format = new OpenLayers.Format.GML.v2({
+ featureType: "feature",
+ featureNS: "http://example.com/feature"
+ });
+ for(var i=0; i<len; ++i) {
+ file = files[i];
+ expect = cases[file];
+ if(expect) {
+ doc = readXML(file);
+ if(doc && doc.documentElement) {
+ out = format.readNode(doc.documentElement);
+ if(out.components && out.components.length == 1) {
+ got = out.components[0];
+ if(got instanceof OpenLayers.Bounds) {
+ t.ok(out.components[0].equals(expect), "[" + file + "] bounds read")
+ } else {
+ t.fail("[" + file + "] expected a bounds, got " + got);
+ }
+ } else {
+ t.fail("[" + file + "] gml parsing");
+ }
+ } else {
+ t.fail("[" + file + "] xml parsing");
+ }
+ } else {
+ t.fail("[" + file + "] case not found");
+ }
+ }
+
+ }
+
+ function test_writeNode_geometry(t) {
+ // we only care to write the 'coordinates' variant of GML 2
+ var files = [
+ "v2/point-coordinates.xml",
+ "v2/linestring-coordinates.xml",
+ "v2/polygon-coordinates.xml",
+ "v2/multipoint-coordinates.xml",
+ "v2/multilinestring-coordinates.xml",
+ "v2/multipolygon-coordinates.xml",
+ "v2/geometrycollection-coordinates.xml"
+ ];
+
+ var len = files.length;
+ t.plan(len);
+
+ var format = new OpenLayers.Format.GML.v2({
+ featureType: "feature",
+ featureNS: "http://example.com/feature",
+ srsName: "foo" // GML geometry collections require srsName, we only write if provided
+ });
+ var file, geom, doc, node;
+ for(var i=0; i<len; ++i) {
+ file = files[i];
+ geom = cases[file];
+ if(geom) {
+ doc = readXML(file);
+ if(doc && doc.documentElement) {
+ node = format.writeNode("feature:_geometry", geom);
+ t.xml_eq(node.firstChild, doc.documentElement, "[" + file + "] geometry written");
+ } else {
+ t.fail("[" + file + "] xml parsing");
+ }
+ } else {
+ t.fail("[" + file + "] case not found");
+ }
+ }
+ }
+
+ function test_writeNode_bounds(t) {
+ // we only care to write the 'coordinates' variant of GML 2
+ var files = [
+ "v2/box-coordinates.xml"
+ ];
+
+ var len = files.length;
+ t.plan(len);
+
+ var format = new OpenLayers.Format.GML.v2({
+ featureType: "feature",
+ featureNS: "http://example.com/feature",
+ srsName: "foo" // GML box does not require srsName, we only write if provided
+ });
+ var file, bounds, doc, node;
+ for(var i=0; i<len; ++i) {
+ file = files[i];
+ bounds = cases[file];
+ if(bounds) {
+ doc = readXML(file);
+ if(doc && doc.documentElement) {
+ node = format.writeNode("gml:Box", bounds);
+ t.xml_eq(node, doc.documentElement, "[" + file + "] bounds written");
+ } else {
+ t.fail("[" + file + "] xml parsing");
+ }
+ } else {
+ t.fail("[" + file + "] case not found");
+ }
+ }
+ }
+
+ function test_read(t) {
+ t.plan(8);
+ var doc = readXML("v2/topp-states.xml");
+ var format = new OpenLayers.Format.GML.v2({
+ featureType: "states",
+ featureNS: "http://www.openplans.org/topp",
+ geometryName: "the_geom"
+ });
+ var features = format.read(doc.documentElement);
+
+ t.eq(features.length, 3, "read 3 features");
+ var feature = features[0];
+ t.eq(feature.fid, "states.1", "read fid");
+ t.eq(feature.geometry.CLASS_NAME, "OpenLayers.Geometry.MultiPolygon",
+ "read multipolygon geometry");
+ var attributes = feature.attributes;
+ t.eq(attributes["STATE_NAME"], "Illinois", "read STATE_NAME");
+ t.eq(attributes["STATE_FIPS"], "17", "read STATE_FIPS");
+ t.eq(attributes["SUB_REGION"], "E N Cen", "read SUB_REGION");
+ t.eq(attributes["STATE_ABBR"], "IL", "read STATE_ABBR");
+ t.eq(attributes["LAND_KM"], "143986.61", "read LAND_KM");
+ }
+
+ function test_read_autoconfig(t) {
+ t.plan(5);
+ var doc = readXML("v2/topp-states.xml");
+ var format = new OpenLayers.Format.GML.v2();
+ var features = format.read(doc.documentElement);
+
+ t.eq(features.length, 3, "read 3 features");
+ var feature = features[0];
+ t.eq(feature.fid, "states.1", "read fid");
+ t.eq(feature.geometry.CLASS_NAME, "OpenLayers.Geometry.MultiPolygon",
+ "read multipolygon geometry");
+ t.eq(format.featureType, "states", "featureType correctly auto-configured");
+ t.eq(format.featureNS, "http://www.openplans.org/topp", "featureNS correctly auto-configured");
+ }
+
+ function test_boundedBy(t) {
+ t.plan(5);
+
+ var doc = readXML("v2/boundedBy.xml");
+ var format = new OpenLayers.Format.GML.v2({
+ featureType: "states",
+ featureNS: "http://www.openplans.org/topp",
+ geometryName: "the_geom",
+ xy: false
+ });
+ var features = format.read(doc.documentElement);
+ var bounds = features[0].bounds;
+
+ t.ok(bounds instanceof OpenLayers.Bounds, "feature given a bounds");
+ t.eq(bounds.left.toFixed(2), "-91.52", "bounds left correct");
+ t.eq(bounds.bottom.toFixed(2), "36.99", "bounds bottom correct");
+ t.eq(bounds.right.toFixed(2), "-87.51", "bounds right correct");
+ t.eq(bounds.top.toFixed(2), "42.51", "bounds top correct");
+ }
+
+ function test_write(t) {
+ t.plan(1);
+ var doc = readXML("v2/topp-states.xml");
+ var format = new OpenLayers.Format.GML.v2({
+ featureType: "states",
+ featureNS: "http://www.openplans.org/topp",
+ geometryName: "the_geom",
+ schemaLocation: "http://www.openplans.org/topp http://sigma.openplans.org:80/geoserver/wfs?service=WFS&version=1.0.0&request=DescribeFeatureType&typeName=topp:states http://www.opengis.net/wfs http://sigma.openplans.org:80/geoserver/schemas/wfs/1.0.0/WFS-basic.xsd",
+ srsName: "http://www.opengis.net/gml/srs/epsg.xml#4326"
+ });
+ var features = format.read(doc.documentElement);
+
+ var got = format.write(features);
+ t.xml_eq(got, doc.documentElement, "wfs:FeatureCollection round trip");
+
+ }
+
+ function test_multipleTypenames(t) {
+ t.plan(5);
+ var doc = readXML("v2/multipletypenames.xml");
+ var format = new OpenLayers.Format.GML.v2({
+ featureType: ["LKUNSTWERK", "PKUNSTWERK", "VKUNSTWERK"],
+ featureNS: "http://mapserver.gis.umn.edu/mapserver",
+ geometry_name: "geometry"
+ });
+ var features = format.read(doc.documentElement);
+ t.eq(features.length, 3, "Expected 3 features from GML containing multiple typenames");
+ t.eq(features[0].type, "VKUNSTWERK", "First feature type is from the VKUNSTWERK typename");
+ t.eq(features[1].type, "LKUNSTWERK", "Second feature type is from the LKUNSTWERK typename");
+ t.eq(features[2].type, "PKUNSTWERK", "Third feature type is from the PKUNSTWERK typename");
+ t.eq(features[0].namespace, "http://mapserver.gis.umn.edu/mapserver", "Namespace is set correctly on feature");
+ }
+
+ function test_noGeom(t) {
+ t.plan(7);
+ var doc = readXML("v2/nogeom.xml");
+ var format = new OpenLayers.Format.GML.v2({
+ featureType: "DEPARTEMENT",
+ featureNS: "http://server.fr/geoserver/loc"
+ });
+ var features = format.read(doc.documentElement);
+ t.eq(features.length, 2, "Expected 2 features from GML with no geom");
+ var feature = features[0];
+ t.ok(feature.geometry == null, "feature 0 has no geometry");
+ var bounds = feature.bounds;
+ t.ok(bounds && (bounds instanceof OpenLayers.Bounds), "feature 0 has been assigned bounds");
+ t.eq(bounds.left, 209565, "bounds left correct");
+ t.eq(bounds.bottom, 6785323, "bounds bottom correct");
+ t.eq(bounds.right, 337568, "bounds right correct");
+ t.eq(bounds.top, 6885985, "bounds top correct");
+ }
+
+ </script>
+</head>
+<body>
+<div id="v2/point-coord.xml"><!--
+<gml:Point xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+</gml:Point>
+--></div>
+<div id="v2/point-coordinates.xml"><!--
+<gml:Point xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:coordinates decimal="." cs="," ts=" ">1,2</gml:coordinates>
+</gml:Point>
+--></div>
+<div id="v2/linestring-coord.xml"><!--
+<gml:LineString xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>3</gml:X>
+ <gml:Y>4</gml:Y>
+ </gml:coord>
+</gml:LineString>
+--></div>
+<div id="v2/linestring-coordinates.xml"><!--
+<gml:LineString xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:coordinates decimal="." cs="," ts=" ">1,2 3,4</gml:coordinates>
+</gml:LineString>
+--></div>
+<div id="v2/polygon-coord.xml"><!--
+<gml:Polygon xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:outerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>3</gml:X>
+ <gml:Y>4</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>5</gml:X>
+ <gml:Y>6</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+ </gml:LinearRing>
+ </gml:outerBoundaryIs>
+ <gml:innerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coord>
+ <gml:X>2</gml:X>
+ <gml:Y>3</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>4</gml:X>
+ <gml:Y>5</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>6</gml:X>
+ <gml:Y>7</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>2</gml:X>
+ <gml:Y>3</gml:Y>
+ </gml:coord>
+ </gml:LinearRing>
+ </gml:innerBoundaryIs>
+ <gml:innerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coord>
+ <gml:X>3</gml:X>
+ <gml:Y>4</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>5</gml:X>
+ <gml:Y>6</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>7</gml:X>
+ <gml:Y>8</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>3</gml:X>
+ <gml:Y>4</gml:Y>
+ </gml:coord>
+ </gml:LinearRing>
+ </gml:innerBoundaryIs>
+</gml:Polygon>
+--></div>
+<div id="v2/polygon-coordinates.xml"><!--
+<gml:Polygon xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:outerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coordinates decimal="." cs="," ts=" ">1,2 3,4 5,6 1,2</gml:coordinates>
+ </gml:LinearRing>
+ </gml:outerBoundaryIs>
+ <gml:innerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coordinates decimal="." cs="," ts=" ">2,3 4,5 6,7 2,3</gml:coordinates>
+ </gml:LinearRing>
+ </gml:innerBoundaryIs>
+ <gml:innerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coordinates decimal="." cs="," ts=" ">3,4 5,6 7,8 3,4</gml:coordinates>
+ </gml:LinearRing>
+ </gml:innerBoundaryIs>
+</gml:Polygon>
+--></div>
+<div id="v2/multipoint-coord.xml"><!--
+<gml:MultiPoint xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:pointMember>
+ <gml:Point>
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+ </gml:Point>
+ </gml:pointMember>
+ <gml:pointMember>
+ <gml:Point>
+ <gml:coord>
+ <gml:X>2</gml:X>
+ <gml:Y>3</gml:Y>
+ </gml:coord>
+ </gml:Point>
+ </gml:pointMember>
+ <gml:pointMember>
+ <gml:Point>
+ <gml:coord>
+ <gml:X>3</gml:X>
+ <gml:Y>4</gml:Y>
+ </gml:coord>
+ </gml:Point>
+ </gml:pointMember>
+</gml:MultiPoint>
+--></div>
+<div id="v2/multipoint-coordinates.xml"><!--
+<gml:MultiPoint xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:pointMember>
+ <gml:Point>
+ <gml:coordinates decimal="." cs="," ts=" ">1,2</gml:coordinates>
+ </gml:Point>
+ </gml:pointMember>
+ <gml:pointMember>
+ <gml:Point>
+ <gml:coordinates decimal="." cs="," ts=" ">2,3</gml:coordinates>
+ </gml:Point>
+ </gml:pointMember>
+ <gml:pointMember>
+ <gml:Point>
+ <gml:coordinates decimal="." cs="," ts=" ">3,4</gml:coordinates>
+ </gml:Point>
+ </gml:pointMember>
+</gml:MultiPoint>
+--></div>
+<div id="v2/multilinestring-coord.xml"><!--
+<gml:MultiLineString xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:lineStringMember>
+ <gml:LineString>
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>2</gml:X>
+ <gml:Y>3</gml:Y>
+ </gml:coord>
+ </gml:LineString>
+ </gml:lineStringMember>
+ <gml:lineStringMember>
+ <gml:LineString>
+ <gml:coord>
+ <gml:X>3</gml:X>
+ <gml:Y>4</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>4</gml:X>
+ <gml:Y>5</gml:Y>
+ </gml:coord>
+ </gml:LineString>
+ </gml:lineStringMember>
+</gml:MultiLineString>
+--></div>
+<div id="v2/multilinestring-coordinates.xml"><!--
+<gml:MultiLineString xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:lineStringMember>
+ <gml:LineString>
+ <gml:coordinates decimal="." cs="," ts=" ">1,2 2,3</gml:coordinates>
+ </gml:LineString>
+ </gml:lineStringMember>
+ <gml:lineStringMember>
+ <gml:LineString>
+ <gml:coordinates decimal="." cs="," ts=" ">3,4 4,5</gml:coordinates>
+ </gml:LineString>
+ </gml:lineStringMember>
+</gml:MultiLineString>
+--></div>
+<div id="v2/multipolygon-coord.xml"><!--
+<gml:MultiPolygon xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:polygonMember>
+ <gml:Polygon>
+ <gml:outerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>3</gml:X>
+ <gml:Y>4</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>5</gml:X>
+ <gml:Y>6</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+ </gml:LinearRing>
+ </gml:outerBoundaryIs>
+ <gml:innerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coord>
+ <gml:X>2</gml:X>
+ <gml:Y>3</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>4</gml:X>
+ <gml:Y>5</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>6</gml:X>
+ <gml:Y>7</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>2</gml:X>
+ <gml:Y>3</gml:Y>
+ </gml:coord>
+ </gml:LinearRing>
+ </gml:innerBoundaryIs>
+ <gml:innerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coord>
+ <gml:X>3</gml:X>
+ <gml:Y>4</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>5</gml:X>
+ <gml:Y>6</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>7</gml:X>
+ <gml:Y>8</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>3</gml:X>
+ <gml:Y>4</gml:Y>
+ </gml:coord>
+ </gml:LinearRing>
+ </gml:innerBoundaryIs>
+ </gml:Polygon>
+ </gml:polygonMember>
+ <gml:polygonMember>
+ <gml:Polygon>
+ <gml:outerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>3</gml:X>
+ <gml:Y>4</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>5</gml:X>
+ <gml:Y>6</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+ </gml:LinearRing>
+ </gml:outerBoundaryIs>
+ </gml:Polygon>
+ </gml:polygonMember>
+</gml:MultiPolygon>
+--></div>
+<div id="v2/multipolygon-coordinates.xml"><!--
+<gml:MultiPolygon xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:polygonMember>
+ <gml:Polygon>
+ <gml:outerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coordinates decimal="." cs="," ts=" ">1,2 3,4 5,6 1,2</gml:coordinates>
+ </gml:LinearRing>
+ </gml:outerBoundaryIs>
+ <gml:innerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coordinates decimal="." cs="," ts=" ">2,3 4,5 6,7 2,3</gml:coordinates>
+ </gml:LinearRing>
+ </gml:innerBoundaryIs>
+ <gml:innerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coordinates decimal="." cs="," ts=" ">3,4 5,6 7,8 3,4</gml:coordinates>
+ </gml:LinearRing>
+ </gml:innerBoundaryIs>
+ </gml:Polygon>
+ </gml:polygonMember>
+ <gml:polygonMember>
+ <gml:Polygon>
+ <gml:outerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coordinates decimal="." cs="," ts=" ">1,2 3,4 5,6 1,2</gml:coordinates>
+ </gml:LinearRing>
+ </gml:outerBoundaryIs>
+ </gml:Polygon>
+ </gml:polygonMember>
+</gml:MultiPolygon>
+--></div>
+<div id="v2/geometrycollection-coordinates.xml"><!--
+<gml:GeometryCollection xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:geometryMember>
+ <gml:Point srsName="foo">
+ <gml:coordinates decimal="." cs="," ts=" ">1,2</gml:coordinates>
+ </gml:Point>
+ </gml:geometryMember>
+ <gml:geometryMember>
+ <gml:LineString srsName="foo">
+ <gml:coordinates decimal="." cs="," ts=" ">1,2 3,4</gml:coordinates>
+ </gml:LineString>
+ </gml:geometryMember>
+ <gml:geometryMember>
+ <gml:Polygon srsName="foo">
+ <gml:outerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coordinates decimal="." cs="," ts=" ">1,2 3,4 5,6 1,2</gml:coordinates>
+ </gml:LinearRing>
+ </gml:outerBoundaryIs>
+ <gml:innerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coordinates decimal="." cs="," ts=" ">2,3 4,5 6,7 2,3</gml:coordinates>
+ </gml:LinearRing>
+ </gml:innerBoundaryIs>
+ <gml:innerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coordinates decimal="." cs="," ts=" ">3,4 5,6 7,8 3,4</gml:coordinates>
+ </gml:LinearRing>
+ </gml:innerBoundaryIs>
+ </gml:Polygon>
+ </gml:geometryMember>
+</gml:GeometryCollection>
+--></div>
+<div id="v2/box-coord.xml"><!--
+<gml:Box xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>3</gml:X>
+ <gml:Y>4</gml:Y>
+ </gml:coord>
+</gml:Box>
+--></div>
+<div id="v2/box-coordinates.xml"><!--
+<gml:Box xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:coordinates decimal="." cs="," ts=" ">1,2 3,4</gml:coordinates>
+</gml:Box>
+--></div>
+<div id="v2/linearring-coord.xml"><!--
+<gml:LinearRing xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>3</gml:X>
+ <gml:Y>4</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>5</gml:X>
+ <gml:Y>6</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+</gml:LinearRing>
+--></div>
+<div id="v2/linearring-coordinates.xml"><!--
+<gml:LinearRing xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:coordinates decimal="." cs="," ts=" ">1,2 3,4 5,6 1,2</gml:coordinates>
+</gml:LinearRing>
+--></div>
+<div id="v2/topp-states.xml"><!--
+<?xml version="1.0" encoding="UTF-8"?><wfs:FeatureCollection xmlns="http://www.opengis.net/wfs" xmlns:wfs="http://www.opengis.net/wfs" xmlns:topp="http://www.openplans.org/topp" xmlns:gml="http://www.opengis.net/gml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openplans.org/topp http://sigma.openplans.org:80/geoserver/wfs?service=WFS&amp;version=1.0.0&amp;request=DescribeFeatureType&amp;typeName=topp:states http://www.opengis.net/wfs http://sigma.openplans.org:80/geoserver/schemas/wfs/1.0.0/WFS-basic.xsd"><gml:featureMember><topp:states fid="states.1"><topp:the_geom><gml:MultiPolygon srsName="http://www.opengis.net/gml/srs/epsg.xml#4326"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates xmlns:gml="http://www.opengis.net/gml" decimal="." cs="," ts=" ">37.5101,-88.0711 37.4761,-88.0871 37.4421,-88.3111 37.4091,-88.3591 37.4201,-88.4191 37.4001,-88.4671 37.2961,-88.5111 37.2571,-88.5011 37.2051,-88.4501 37.1561,-88.4221 37.0981,-88.4501 37.0721,-88.4761 37.0681,-88.4901 37.0641,-88.5171 37.0721,-88.5591 37.1091,-88.6141 37.1351,-88.6881 37.1411,-88.7391 37.1521,-88.7461 37.2021,-88.8631 37.2181,-88.9321 37.2201,-88.9931 37.1851,-89.0651 37.1121,-89.1161 37.0931,-89.1461 37.0641,-89.1691 37.0251,-89.1741 36.9981,-89.1501 36.9881,-89.1291 36.9861,-89.1931 37.0281,-89.2101 37.0411,-89.2371 37.0871,-89.2641 37.0911,-89.2841 37.0851,-89.3031 37.0601,-89.3091 37.0271,-89.2641 37.0081,-89.2621 36.9991,-89.2821 37.0091,-89.3101 37.0491,-89.3821 37.0991,-89.3791 37.1371,-89.4231 37.1651,-89.4401 37.2241,-89.4681 37.2531,-89.4651 37.2561,-89.4891 37.2761,-89.5131 37.3041,-89.5131 37.3291,-89.5001 37.3391,-89.4681 37.3551,-89.4351 37.4111,-89.4271 37.4531,-89.4531 37.4911,-89.4941 37.5711,-89.5241 37.6151,-89.5131 37.6501,-89.5191 37.6791,-89.5131 37.6941,-89.5211 37.7061,-89.5811 37.7451,-89.6661 37.7831,-89.6751 37.8041,-89.6911 37.8401,-89.7281 37.9051,-89.8511 37.9051,-89.8611 37.8911,-89.8661 37.8751,-89.9001 37.8781,-89.9371 37.9111,-89.9781 37.9631,-89.9581 37.9691,-90.0101 37.9931,-90.0411 38.0321,-90.1191 38.0531,-90.1341 38.0881,-90.2071 38.1221,-90.2541 38.1661,-90.2891 38.1881,-90.3361 38.2341,-90.3641 38.3231,-90.3691 38.3651,-90.3581 38.3901,-90.3391 38.4271,-90.3011 38.5181,-90.2651 38.5321,-90.2611 38.5621,-90.2401 38.6101,-90.1831 38.6581,-90.1831 38.7001,-90.2021 38.7231,-90.1961 38.7731,-90.1631 38.7851,-90.1351 38.8001,-90.1211 38.8301,-90.1131 38.8531,-90.1321 38.9141,-90.2431 38.9241,-90.2781 38.9241,-90.3191 38.9621,-90.4131 38.9591,-90.4691 38.8911,-90.5301 38.8711,-90.5701 38.8801,-90.6271 38.9351,-90.6681 39.0371,-90.7061 39.0581,-90.7071 39.0931,-90.6901 39.1441,-90.7161 39.1951,-90.7181 39.2241,-90.7321 39.2471,-90.7381 39.2961,-90.7791 39.3501,-90.8501 39.4001,-90.9471 39.4441,-91.0361 39.4731,-91.0641 39.5281,-91.0931 39.5521,-91.1561 39.6001,-91.2031 39.6851,-91.3171 39.7241,-91.3671 39.7611,-91.3731 39.8031,-91.3811 39.8631,-91.4491 39.8851,-91.4501 39.9011,-91.4341 39.9211,-91.4301 39.9461,-91.4471 40.0051,-91.4871 40.0661,-91.5041 40.1341,-91.5161 40.2001,-91.5061 40.2511,-91.4981 40.3091,-91.4861 40.3711,-91.4481 40.3861,-91.4181 40.3921,-91.3851 40.4021,-91.3721 40.4471,-91.3851 40.5031,-91.3741 40.5281,-91.3821 40.5471,-91.4121 40.5721,-91.4111 40.6031,-91.3751 40.6391,-91.2621 40.6431,-91.2141 40.6561,-91.1621 40.6821,-91.1291 40.7051,-91.1191 40.7611,-91.0921 40.8331,-91.0881 40.8791,-91.0491 40.9231,-90.9831 40.9501,-90.9601 41.0701,-90.9541 41.1041,-90.9571 41.1441,-90.9901 41.1651,-91.0181 41.1761,-91.0561 41.2311,-91.1011 41.2671,-91.1021 41.3341,-91.0731 41.4011,-91.0551 41.4231,-91.0271 41.4311,-91.0001 41.4211,-90.9491 41.4441,-90.8441 41.4491,-90.7791 41.4501,-90.7081 41.4621,-90.6581 41.5091,-90.6001 41.5251,-90.5401 41.5271,-90.4541 41.5431,-90.4341 41.5671,-90.4231 41.5861,-90.3481 41.6021,-90.3391 41.6491,-90.3411 41.7221,-90.3261 41.7561,-90.3041 41.7811,-90.2551 41.8061,-90.1951 41.9301,-90.1541 41.9831,-90.1421 42.0331,-90.1501 42.0611,-90.1681 42.1031,-90.1661 42.1201,-90.1761 42.1221,-90.1911 42.1591,-90.2301 42.1971,-90.3231 42.2101,-90.3671 42.2421,-90.4071 42.2631,-90.4171 42.3401,-90.4271 42.3601,-90.4411 42.3881,-90.4911 42.4211,-90.5631 42.4601,-90.6051 42.4751,-90.6481 42.4941,-90.6511 42.5091,-90.6381 42.5081,-90.4191 42.5041,-89.9231 42.5031,-89.8341 42.4971,-89.4001 42.4971,-89.3591 42.4901,-88.9391 42.4901,-88.7641 42.4891,-88.7061 42.4911,-88.2971 42.4891,-88.1941 42.4891,-87.7971 42.3141,-87.8361 42.1561,-87.7601 42.0591,-87.6701 41.8471,-87.6121 41.7231,-87.5291 41.4691,-87.5321 41.3011,-87.5321 41.1731,-87.5311 41.0091,-87.5321 40.7451,-87.5321 40.4941,-87.5371 40.4831,-87.5351 40.1661,-87.5351 39.8871,-87.5351 39.6091,-87.5351 39.4771,-87.5381 39.3501,-87.5401 39.3381,-87.5971 39.3071,-87.6251 39.2971,-87.6101 39.2811,-87.6151 39.2581,-87.6061 39.2481,-87.5841 39.2081,-87.5881 39.1981,-87.5941 39.1961,-87.6071 39.1681,-87.6441 39.1461,-87.6701 39.1301,-87.6591 39.1131,-87.6621 39.1031,-87.6311 39.0881,-87.6301 39.0841,-87.6121 39.0621,-87.5851 38.9951,-87.5811 38.9941,-87.5911 38.9771,-87.5471 38.9631,-87.5331 38.9311,-87.5301 38.9041,-87.5391 38.8691,-87.5591 38.8571,-87.5501 38.7951,-87.5071 38.7761,-87.5191 38.7691,-87.5081 38.7361,-87.5081 38.6851,-87.5431 38.6721,-87.5881 38.6421,-87.6251 38.6221,-87.6281 38.5991,-87.6191 38.5931,-87.6401 38.5731,-87.6521 38.5471,-87.6721 38.5151,-87.6511 38.5001,-87.6531 38.5041,-87.6791 38.4811,-87.6921 38.4661,-87.7561 38.4571,-87.7581 38.4451,-87.7381 38.4171,-87.7481 38.3781,-87.7841 38.3521,-87.8341 38.2861,-87.8501 38.2851,-87.8631 38.3161,-87.8741 38.3151,-87.8831 38.3001,-87.8881 38.2811,-87.9141 38.3021,-87.9131 38.3041,-87.9251 38.2411,-87.9801 38.2341,-87.9861 38.2001,-87.9771 38.1711,-87.9321 38.1571,-87.9311 38.1361,-87.9501 38.1311,-87.9731 38.1031,-88.0181 38.0921,-88.0121 38.0961,-87.9641 38.0731,-87.9751 38.0541,-88.0341 38.0451,-88.0431 38.0381,-88.0411 38.0331,-88.0211 38.0081,-88.0291 37.9751,-88.0211 37.9561,-88.0421 37.9341,-88.0411 37.9291,-88.0641 37.944,-88.0781 37.9231,-88.084 37.9171,-88.0301 37.9051,-88.0261 37.8961,-88.0441 37.9061,-88.1001 37.8951,-88.1011 37.8671,-88.0751 37.8431,-88.0341 37.8271,-88.0421 37.8311,-88.0891 37.8171,-88.0861 37.8051,-88.0351 37.7351,-88.0721 37.7001,-88.1331 37.6601,-88.1591 37.6281,-88.1571 37.5831,-88.1341 37.5101,-88.0711</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></topp:the_geom><topp:STATE_NAME>Illinois</topp:STATE_NAME><topp:STATE_FIPS>17</topp:STATE_FIPS><topp:SUB_REGION>E N Cen</topp:SUB_REGION><topp:STATE_ABBR>IL</topp:STATE_ABBR><topp:LAND_KM>143986.61</topp:LAND_KM><topp:WATER_KM>1993.335</topp:WATER_KM><topp:PERSONS>1.1431E7</topp:PERSONS><topp:FAMILIES>2924880.0</topp:FAMILIES><topp:HOUSHOLD>4202240.0</topp:HOUSHOLD><topp:MALE>5552233.0</topp:MALE><topp:FEMALE>5878369.0</topp:FEMALE><topp:WORKERS>4199206.0</topp:WORKERS><topp:DRVALONE>3741715.0</topp:DRVALONE><topp:CARPOOL>652603.0</topp:CARPOOL><topp:PUBTRANS>538071.0</topp:PUBTRANS><topp:EMPLOYED>5417967.0</topp:EMPLOYED><topp:UNEMPLOY>385040.0</topp:UNEMPLOY><topp:SERVICE>1360159.0</topp:SERVICE><topp:MANUAL>828906.0</topp:MANUAL><topp:P_MALE>0.486</topp:P_MALE><topp:P_FEMALE>0.514</topp:P_FEMALE><topp:SAMP_POP>1747776.0</topp:SAMP_POP></topp:states></gml:featureMember><gml:featureMember><topp:states fid="states.2"><topp:the_geom><gml:MultiPolygon srsName="http://www.opengis.net/gml/srs/epsg.xml#4326"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates xmlns:gml="http://www.opengis.net/gml" decimal="." cs="," ts=" ">38.9661,-77.0081 38.8891,-76.9111 38.7881,-77.0451 38.8131,-77.0351 38.8291,-77.0451 38.8381,-77.0401 38.8621,-77.0391 38.8861,-77.0671 38.9151,-77.0781 38.9321,-77.1221 38.9931,-77.0421 38.9661,-77.0081</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></topp:the_geom><topp:STATE_NAME>District of Columbia</topp:STATE_NAME><topp:STATE_FIPS>11</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>DC</topp:STATE_ABBR><topp:LAND_KM>159.055</topp:LAND_KM><topp:WATER_KM>17.991</topp:WATER_KM><topp:PERSONS>606900.0</topp:PERSONS><topp:FAMILIES>122087.0</topp:FAMILIES><topp:HOUSHOLD>249634.0</topp:HOUSHOLD><topp:MALE>282970.0</topp:MALE><topp:FEMALE>323930.0</topp:FEMALE><topp:WORKERS>229975.0</topp:WORKERS><topp:DRVALONE>106694.0</topp:DRVALONE><topp:CARPOOL>36621.0</topp:CARPOOL><topp:PUBTRANS>111422.0</topp:PUBTRANS><topp:EMPLOYED>303994.0</topp:EMPLOYED><topp:UNEMPLOY>23442.0</topp:UNEMPLOY><topp:SERVICE>65498.0</topp:SERVICE><topp:MANUAL>22407.0</topp:MANUAL><topp:P_MALE>0.466</topp:P_MALE><topp:P_FEMALE>0.534</topp:P_FEMALE><topp:SAMP_POP>72696.0</topp:SAMP_POP></topp:states></gml:featureMember><gml:featureMember><topp:states fid="states.3"><topp:the_geom><gml:MultiPolygon srsName="http://www.opengis.net/gml/srs/epsg.xml#4326"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates xmlns:gml="http://www.opengis.net/gml" decimal="." cs="," ts=" ">38.5571,-75.7071 38.6491,-75.7111 38.8301,-75.7241 39.1411,-75.7521 39.2471,-75.7611 39.2951,-75.7641 39.3831,-75.7721 39.7231,-75.7911 39.7241,-75.7751 39.7741,-75.7451 39.8201,-75.6951 39.8381,-75.6441 39.8401,-75.5831 39.8261,-75.4701 39.7981,-75.4201 39.7891,-75.4121 39.7781,-75.4281 39.7631,-75.4601 39.7411,-75.4751 39.7191,-75.4761 39.7141,-75.4891 39.6121,-75.6101 39.5661,-75.5621 39.4631,-75.5901 39.3661,-75.5151 39.2571,-75.4021 39.0731,-75.3971 39.0121,-75.3241 38.9451,-75.3071 38.8081,-75.1901 38.7991,-75.0831 38.4491,-75.0451 38.4491,-75.0681 38.4501,-75.0931 38.4551,-75.3501 38.4631,-75.6991 38.5571,-75.7071</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></topp:the_geom><topp:STATE_NAME>Delaware</topp:STATE_NAME><topp:STATE_FIPS>10</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>DE</topp:STATE_ABBR><topp:LAND_KM>5062.456</topp:LAND_KM><topp:WATER_KM>1385.022</topp:WATER_KM><topp:PERSONS>666168.0</topp:PERSONS><topp:FAMILIES>175867.0</topp:FAMILIES><topp:HOUSHOLD>247497.0</topp:HOUSHOLD><topp:MALE>322968.0</topp:MALE><topp:FEMALE>343200.0</topp:FEMALE><topp:WORKERS>247566.0</topp:WORKERS><topp:DRVALONE>258087.0</topp:DRVALONE><topp:CARPOOL>42968.0</topp:CARPOOL><topp:PUBTRANS>8069.0</topp:PUBTRANS><topp:EMPLOYED>335147.0</topp:EMPLOYED><topp:UNEMPLOY>13945.0</topp:UNEMPLOY><topp:SERVICE>87973.0</topp:SERVICE><topp:MANUAL>44140.0</topp:MANUAL><topp:P_MALE>0.485</topp:P_MALE><topp:P_FEMALE>0.515</topp:P_FEMALE><topp:SAMP_POP>102776.0</topp:SAMP_POP></topp:states></gml:featureMember></wfs:FeatureCollection>
+--></div>
+<div id="v2/boundedBy.xml"><!--
+<?xml version="1.0" encoding="UTF-8"?><wfs:FeatureCollection xmlns="http://www.opengis.net/wfs" xmlns:wfs="http://www.opengis.net/wfs" xmlns:topp="http://www.openplans.org/topp" xmlns:gml="http://www.opengis.net/gml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openplans.org/topp http://publicus.opengeo.org:80/geoserver/wfs?service=WFS&amp;version=1.0.0&amp;request=DescribeFeatureType&amp;typeName=topp:states http://www.opengis.net/wfs http://publicus.opengeo.org:80/geoserver/schemas/wfs/1.0.0/WFS-basic.xsd"><gml:featureMember><topp:states fid="states.1"><gml:boundedBy><gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#4326"><gml:coordinates xmlns:gml="http://www.opengis.net/gml" decimal="." cs="," ts=" ">36.9861,-91.5161 42.5091,-87.5071</gml:coordinates></gml:Box></gml:boundedBy><topp:the_geom><gml:MultiPolygon srsName="http://www.opengis.net/gml/srs/epsg.xml#4326"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates xmlns:gml="http://www.opengis.net/gml" decimal="." cs="," ts=" ">37.5101,-88.0711 37.4761,-88.0871 37.4421,-88.3111 37.4091,-88.3591 37.4201,-88.4191 37.4001,-88.4671 37.2961,-88.5111 37.2571,-88.5011 37.2051,-88.4501 37.1561,-88.4221 37.0981,-88.4501 37.0721,-88.4761 37.0681,-88.4901 37.0641,-88.5171 37.0721,-88.5591 37.1091,-88.6141 37.1351,-88.6881 37.1411,-88.7391 37.1521,-88.7461 37.2021,-88.8631 37.2181,-88.9321 37.2201,-88.9931 37.1851,-89.0651 37.1121,-89.1161 37.0931,-89.1461 37.0641,-89.1691 37.0251,-89.1741 36.9981,-89.1501 36.9881,-89.1291 36.9861,-89.1931 37.0281,-89.2101 37.0411,-89.2371 37.0871,-89.2641 37.0911,-89.2841 37.0851,-89.3031 37.0601,-89.3091 37.0271,-89.2641 37.0081,-89.2621 36.9991,-89.2821 37.0091,-89.3101 37.0491,-89.3821 37.0991,-89.3791 37.1371,-89.4231 37.1651,-89.4401 37.2241,-89.4681 37.2531,-89.4651 37.2561,-89.4891 37.2761,-89.5131 37.3041,-89.5131 37.3291,-89.5001 37.3391,-89.4681 37.3551,-89.4351 37.4111,-89.4271 37.4531,-89.4531 37.4911,-89.4941 37.5711,-89.5241 37.6151,-89.5131 37.6501,-89.5191 37.6791,-89.5131 37.6941,-89.5211 37.7061,-89.5811 37.7451,-89.6661 37.7831,-89.6751 37.8041,-89.6911 37.8401,-89.7281 37.9051,-89.8511 37.9051,-89.8611 37.8911,-89.8661 37.8751,-89.9001 37.8781,-89.9371 37.9111,-89.9781 37.9631,-89.9581 37.9691,-90.0101 37.9931,-90.0411 38.0321,-90.1191 38.0531,-90.1341 38.0881,-90.2071 38.1221,-90.2541 38.1661,-90.2891 38.1881,-90.3361 38.2341,-90.3641 38.3231,-90.3691 38.3651,-90.3581 38.3901,-90.3391 38.4271,-90.3011 38.5181,-90.2651 38.5321,-90.2611 38.5621,-90.2401 38.6101,-90.1831 38.6581,-90.1831 38.7001,-90.2021 38.7231,-90.1961 38.7731,-90.1631 38.7851,-90.1351 38.8001,-90.1211 38.8301,-90.1131 38.8531,-90.1321 38.9141,-90.2431 38.9241,-90.2781 38.9241,-90.3191 38.9621,-90.4131 38.9591,-90.4691 38.8911,-90.5301 38.8711,-90.5701 38.8801,-90.6271 38.9351,-90.6681 39.0371,-90.7061 39.0581,-90.7071 39.0931,-90.6901 39.1441,-90.7161 39.1951,-90.7181 39.2241,-90.7321 39.2471,-90.7381 39.2961,-90.7791 39.3501,-90.8501 39.4001,-90.9471 39.4441,-91.0361 39.4731,-91.0641 39.5281,-91.0931 39.5521,-91.1561 39.6001,-91.2031 39.6851,-91.3171 39.7241,-91.3671 39.7611,-91.3731 39.8031,-91.3811 39.8631,-91.4491 39.8851,-91.4501 39.9011,-91.4341 39.9211,-91.4301 39.9461,-91.4471 40.0051,-91.4871 40.0661,-91.5041 40.1341,-91.5161 40.2001,-91.5061 40.2511,-91.4981 40.3091,-91.4861 40.3711,-91.4481 40.3861,-91.4181 40.3921,-91.3851 40.4021,-91.3721 40.4471,-91.3851 40.5031,-91.3741 40.5281,-91.3821 40.5471,-91.4121 40.5721,-91.4111 40.6031,-91.3751 40.6391,-91.2621 40.6431,-91.2141 40.6561,-91.1621 40.6821,-91.1291 40.7051,-91.1191 40.7611,-91.0921 40.8331,-91.0881 40.8791,-91.0491 40.9231,-90.9831 40.9501,-90.9601 41.0701,-90.9541 41.1041,-90.9571 41.1441,-90.9901 41.1651,-91.0181 41.1761,-91.0561 41.2311,-91.1011 41.2671,-91.1021 41.3341,-91.0731 41.4011,-91.0551 41.4231,-91.0271 41.4311,-91.0001 41.4211,-90.9491 41.4441,-90.8441 41.4491,-90.7791 41.4501,-90.7081 41.4621,-90.6581 41.5091,-90.6001 41.5251,-90.5401 41.5271,-90.4541 41.5431,-90.4341 41.5671,-90.4231 41.5861,-90.3481 41.6021,-90.3391 41.6491,-90.3411 41.7221,-90.3261 41.7561,-90.3041 41.7811,-90.2551 41.8061,-90.1951 41.9301,-90.1541 41.9831,-90.1421 42.0331,-90.1501 42.0611,-90.1681 42.1031,-90.1661 42.1201,-90.1761 42.1221,-90.1911 42.1591,-90.2301 42.1971,-90.3231 42.2101,-90.3671 42.2421,-90.4071 42.2631,-90.4171 42.3401,-90.4271 42.3601,-90.4411 42.3881,-90.4911 42.4211,-90.5631 42.4601,-90.6051 42.4751,-90.6481 42.4941,-90.6511 42.5091,-90.6381 42.5081,-90.4191 42.5041,-89.9231 42.5031,-89.8341 42.4971,-89.4001 42.4971,-89.3591 42.4901,-88.9391 42.4901,-88.7641 42.4891,-88.7061 42.4911,-88.2971 42.4891,-88.1941 42.4891,-87.7971 42.3141,-87.8361 42.1561,-87.7601 42.0591,-87.6701 41.8471,-87.6121 41.7231,-87.5291 41.4691,-87.5321 41.3011,-87.5321 41.1731,-87.5311 41.0091,-87.5321 40.7451,-87.5321 40.4941,-87.5371 40.4831,-87.5351 40.1661,-87.5351 39.8871,-87.5351 39.6091,-87.5351 39.4771,-87.5381 39.3501,-87.5401 39.3381,-87.5971 39.3071,-87.6251 39.2971,-87.6101 39.2811,-87.6151 39.2581,-87.6061 39.2481,-87.5841 39.2081,-87.5881 39.1981,-87.5941 39.1961,-87.6071 39.1681,-87.6441 39.1461,-87.6701 39.1301,-87.6591 39.1131,-87.6621 39.1031,-87.6311 39.0881,-87.6301 39.0841,-87.6121 39.0621,-87.5851 38.9951,-87.5811 38.9941,-87.5911 38.9771,-87.5471 38.9631,-87.5331 38.9311,-87.5301 38.9041,-87.5391 38.8691,-87.5591 38.8571,-87.5501 38.7951,-87.5071 38.7761,-87.5191 38.7691,-87.5081 38.7361,-87.5081 38.6851,-87.5431 38.6721,-87.5881 38.6421,-87.6251 38.6221,-87.6281 38.5991,-87.6191 38.5931,-87.6401 38.5731,-87.6521 38.5471,-87.6721 38.5151,-87.6511 38.5001,-87.6531 38.5041,-87.6791 38.4811,-87.6921 38.4661,-87.7561 38.4571,-87.7581 38.4451,-87.7381 38.4171,-87.7481 38.3781,-87.7841 38.3521,-87.8341 38.2861,-87.8501 38.2851,-87.8631 38.3161,-87.8741 38.3151,-87.8831 38.3001,-87.8881 38.2811,-87.9141 38.3021,-87.9131 38.3041,-87.9251 38.2411,-87.9801 38.2341,-87.9861 38.2001,-87.9771 38.1711,-87.9321 38.1571,-87.9311 38.1361,-87.9501 38.1311,-87.9731 38.1031,-88.0181 38.0921,-88.0121 38.0961,-87.9641 38.0731,-87.9751 38.0541,-88.0341 38.0451,-88.0431 38.0381,-88.0411 38.0331,-88.0211 38.0081,-88.0291 37.9751,-88.0211 37.9561,-88.0421 37.9341,-88.0411 37.9291,-88.0641 37.944,-88.0781 37.9231,-88.084 37.9171,-88.0301 37.9051,-88.0261 37.8961,-88.0441 37.9061,-88.1001 37.8951,-88.1011 37.8671,-88.0751 37.8431,-88.0341 37.8271,-88.0421 37.8311,-88.0891 37.8171,-88.0861 37.8051,-88.0351 37.7351,-88.0721 37.7001,-88.1331 37.6601,-88.1591 37.6281,-88.1571 37.5831,-88.1341 37.5101,-88.0711</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></topp:the_geom><topp:STATE_NAME>Illinois</topp:STATE_NAME><topp:STATE_FIPS>17</topp:STATE_FIPS><topp:SUB_REGION>E N Cen</topp:SUB_REGION><topp:STATE_ABBR>IL</topp:STATE_ABBR><topp:LAND_KM>143986.61</topp:LAND_KM><topp:WATER_KM>1993.335</topp:WATER_KM><topp:PERSONS>1.1431E7</topp:PERSONS><topp:FAMILIES>2924880.0</topp:FAMILIES><topp:HOUSHOLD>4202240.0</topp:HOUSHOLD><topp:MALE>5552233.0</topp:MALE><topp:FEMALE>5878369.0</topp:FEMALE><topp:WORKERS>4199206.0</topp:WORKERS><topp:DRVALONE>3741715.0</topp:DRVALONE><topp:CARPOOL>652603.0</topp:CARPOOL><topp:PUBTRANS>538071.0</topp:PUBTRANS><topp:EMPLOYED>5417967.0</topp:EMPLOYED><topp:UNEMPLOY>385040.0</topp:UNEMPLOY><topp:SERVICE>1360159.0</topp:SERVICE><topp:MANUAL>828906.0</topp:MANUAL><topp:P_MALE>0.486</topp:P_MALE><topp:P_FEMALE>0.514</topp:P_FEMALE><topp:SAMP_POP>1747776.0</topp:SAMP_POP></topp:states></gml:featureMember></wfs:FeatureCollection>
+--></div>
+<div id="v2/multipletypenames.xml"><!--
+<?xml version='1.0' encoding="ISO-8859-1" ?><wfs:FeatureCollection xmlns:rws="http://mapserver.gis.umn.edu/mapserver" xmlns:wfs="http://www.opengis.net/wfs" xmlns:gml="http://www.opengis.net/gml" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/WFS-basic.xsd http://mapserver.gis.umn.edu/mapserver http://intranet.rijkswaterstaat.nl/services/geoservices/kerngisnat_utre?SERVICE=WFS&amp;VERSION=1.0.0&amp;REQUEST=DescribeFeatureType&amp;TYPENAME=VKUNSTWERK,LKUNSTWERK,PKUNSTWERK&amp;OUTPUTFORMAT=XMLSCHEMA"> <gml:boundedBy> <gml:Box srsName="EPSG:28992"> <gml:coordinates>134503.789000,455332.337000 135149.909000,455893.926000</gml:coordinates> </gml:Box> </gml:boundedBy> <gml:featureMember> <rws:VKUNSTWERK fid="VKUNSTWERK.16"> <gml:boundedBy> <gml:Box srsName="EPSG:28992"> <gml:coordinates>134949.571000,455438.845000 134978.799000,455471.762000</gml:coordinates> </gml:Box> </gml:boundedBy> <rws:geometry> <gml:MultiPolygon srsName="EPSG:28992"> <gml:polygonMember> <gml:Polygon> <gml:outerBoundaryIs> <gml:LinearRing> <gml:coordinates>134974.191000,455471.587000 134973.974000,455471.762000 134973.558000,455471.248000 134973.579000,455471.230000 134963.143000,455458.768000 134962.787000,455458.653000 134960.514000,455456.003000 134960.440000,455455.539000 134950.207000,455443.320000 134950.158000,455443.360000 134949.571000,455442.638000 134949.810000,455442.462000 134951.417000,455441.223000 134951.435000,455441.209000 134954.158000,455439.108000 134954.507000,455438.845000 134955.000000,455439.420000 134954.954000,455439.458000 134965.046000,455451.520000 134965.568000,455451.606000 134968.159000,455454.642000 134968.120000,455455.195000 134978.294000,455467.355000 134978.330000,455467.326000 134978.799000,455467.881000 134978.598000,455468.042000 134975.885000,455470.224000 134974.191000,455471.587000 </gml:coordinates> </gml:LinearRing> </gml:outerBoundaryIs> <gml:innerBoundaryIs> <gml:LinearRing> <gml:coordinates>134960.590000,455455.163000 134963.589000,455458.755000 134973.756000,455470.929000 134973.836000,455471.019000 134974.216000,455471.445000 134975.807000,455470.163000 134978.485000,455468.005000 134978.077000,455467.534000 134978.015000,455467.462000 134967.969000,455455.479000 134964.782000,455451.678000 134954.705000,455439.660000 134954.622000,455439.561000 134954.271000,455439.152000 134951.498000,455441.284000 134949.973000,455442.456000 134950.452000,455443.023000 134950.501000,455443.081000 134960.590000,455455.163000 </gml:coordinates> </gml:LinearRing> </gml:innerBoundaryIs> </gml:Polygon> </gml:polygonMember> </gml:MultiPolygon> </rws:geometry> <rws:OBJECTID>16</rws:OBJECTID> <rws:OBJECTSUBCATEGORIE>31</rws:OBJECTSUBCATEGORIE> </rws:VKUNSTWERK> </gml:featureMember> <gml:featureMember> <rws:LKUNSTWERK fid="LKUNSTWERK.14"> <gml:boundedBy> <gml:Box srsName="EPSG:28992"> <gml:coordinates>135080.966000,455332.337000 135149.909000,455390.384000</gml:coordinates> </gml:Box> </gml:boundedBy> <rws:geometry> <gml:MultiLineString srsName="EPSG:28992"> <gml:lineStringMember> <gml:LineString> <gml:coordinates>135080.966000,455390.384000 135096.654000,455377.009000 135109.082000,455366.755000 135122.769000,455355.276000 135141.565000,455339.633000 135149.909000,455332.337000 </gml:coordinates> </gml:LineString> </gml:lineStringMember> </gml:MultiLineString> </rws:geometry> <rws:OBJECTID>14</rws:OBJECTID> <rws:OBJECTSUBCATEGORIE>30</rws:OBJECTSUBCATEGORIE> </rws:LKUNSTWERK> </gml:featureMember> <gml:featureMember> <rws:PKUNSTWERK fid="PKUNSTWERK.29"> <gml:boundedBy> <gml:Box srsName="EPSG:28992"> <gml:coordinates>134832.017000,455596.187000 134832.017000,455596.187000</gml:coordinates> </gml:Box> </gml:boundedBy> <rws:geometry> <gml:MultiPoint srsName="EPSG:28992"> <gml:pointMember> <gml:Point> <gml:coordinates>134832.017000,455596.187000</gml:coordinates> </gml:Point> </gml:pointMember> </gml:MultiPoint> </rws:geometry> <rws:OBJECTID>29</rws:OBJECTID> <rws:OBJECTSUBCATEGORIE>30</rws:OBJECTSUBCATEGORIE> </rws:PKUNSTWERK> </gml:featureMember></wfs:FeatureCollection>
+--></div>
+<div id="v2/nogeom.xml"><!--
+<?xml version="1.0" encoding="UTF-8"?><wfs:FeatureCollection xmlns="http://www.opengis.net/wfs" xmlns:wfs="http://www.opengis.net/wfs" xmlns:gml="http://www.opengis.net/gml" xmlns:loc="http://server.fr/geoserver/loc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs http://server.fr:80/geoserver/schemas/wfs/1.0.0/WFS-basic.xsd http://server.fr/geoserver/loc http://server.fr:80/geoserver/wfs?service=WFS&amp;version=1.0.0&amp;request=DescribeFeatureType&amp;typeName=loc:DEPARTEMENT"><gml:boundedBy><gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#2154"><gml:coordinates xmlns:gml="http://www.opengis.net/gml" decimal="." cs="," ts=" ">199373,6704170 337568,6885985</gml:coordinates></gml:Box></gml:boundedBy><gml:featureMember><loc:DEPARTEMENT fid="DEPARTEMENT.1"><gml:boundedBy><gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#2154"><gml:coordinates xmlns:gml="http://www.opengis.net/gml" decimal="." cs="," ts=" ">209565,6785323 337568,6885985</gml:coordinates></gml:Box></gml:boundedBy><loc:NOM_DEPT>COTES-D'ARMOR</loc:NOM_DEPT></loc:DEPARTEMENT></gml:featureMember><gml:featureMember><loc:DEPARTEMENT fid="DEPARTEMENT.3"><gml:boundedBy><gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#2154"><gml:coordinates xmlns:gml="http://www.opengis.net/gml" decimal="." cs="," ts=" ">199373,6704170 323518,6807542</gml:coordinates></gml:Box></gml:boundedBy><loc:NOM_DEPT>MORBIHAN</loc:NOM_DEPT></loc:DEPARTEMENT></gml:featureMember></wfs:FeatureCollection>
+--></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/GML/v3.html b/misc/openlayers/tests/Format/GML/v3.html
new file mode 100644
index 0000000..92f2154
--- /dev/null
+++ b/misc/openlayers/tests/Format/GML/v3.html
@@ -0,0 +1,828 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script src="cases.js"></script>
+ <script type="text/javascript">
+
+ function test_readNode_geometry(t) {
+ var files = [
+ "v2/point-coord.xml", "v2/point-coordinates.xml",
+ "v2/linestring-coord.xml", "v2/linestring-coordinates.xml",
+ "v2/multipoint-coord.xml", "v2/multipoint-coordinates.xml",
+ "v2/multilinestring-coord.xml", "v2/multilinestring-coordinates.xml",
+ "v3/point.xml", "v3/linestring.xml", "v3/linestring3d.xml",
+ "v3/curve.xml", "v3/polygon.xml", "v3/surface.xml",
+ "v3/multipoint-singular.xml", "v3/multipoint-plural.xml",
+ "v3/multilinestring-singular.xml", "v3/multilinestring-plural.xml",
+ "v3/multicurve-singular.xml", "v3/multicurve-curve.xml",
+ "v3/multipolygon-singular.xml", "v3/multipolygon-plural.xml",
+ "v3/multisurface-singular.xml", "v3/multisurface-plural.xml",
+ "v3/multisurface-surface.xml"
+ ];
+
+ var len = files.length;
+ t.plan(len);
+
+ var format = new OpenLayers.Format.GML.v3({
+ featureType: "feature",
+ featureNS: "http://example.com/feature"
+ });
+ var file, doc, expect, out;
+ for(var i=0; i<len; ++i) {
+ file = files[i];
+ expect = cases[file];
+ if(expect) {
+ doc = readXML(file);
+ if(doc && doc.documentElement) {
+ out = format.readNode(doc.documentElement);
+ if(out.components && out.components.length == 1) {
+ t.geom_eq(out.components[0], expect, "[" + file + "] geometry read");
+ } else {
+ t.fail("[" + file + "] gml parsing");
+ }
+ } else {
+ t.fail("[" + file + "] xml parsing");
+ }
+ } else {
+ t.fail("[" + file + "] case not found");
+ }
+ }
+
+ }
+
+ function test_read_setGeometryName(t) {
+ t.plan(1);
+ var doc = readXML("v3/topp-states-gml.xml");
+ var format = new OpenLayers.Format.GML.v3({
+ featureType: "states",
+ featureNS: "http://www.openplans.org/topp",
+ geometryName: null
+ });
+ var features = format.read(doc.documentElement);
+
+ t.eq(format.geometryName, "the_geom", "geometryName set when parsing features");
+ }
+
+ function test_readNode_bounds(t) {
+ var files = ["v3/envelope.xml"];
+
+ var len = files.length;
+ t.plan(len);
+
+ var file, doc, expect, got;
+ var format = new OpenLayers.Format.GML.v3({
+ featureType: "feature",
+ featureNS: "http://example.com/feature"
+ });
+ for(var i=0; i<len; ++i) {
+ file = files[i];
+ expect = cases[file];
+ if(expect) {
+ doc = readXML(file);
+ if(doc && doc.documentElement) {
+ out = format.readNode(doc.documentElement);
+ if(out.components && out.components.length == 1) {
+ got = out.components[0];
+ if(got instanceof OpenLayers.Bounds) {
+ t.ok(out.components[0].equals(expect), "[" + file + "] bounds read")
+ } else {
+ t.fail("[" + file + "] expected a bounds, got " + got);
+ }
+ } else {
+ t.fail("[" + file + "] gml parsing");
+ }
+ } else {
+ t.fail("[" + file + "] xml parsing");
+ }
+ } else {
+ t.fail("[" + file + "] case not found");
+ }
+ }
+
+ }
+
+ function test_writeNode_geometry(t) {
+ // we only care to write the 'pos' and 'posList' variants of GML 3 - conforming with simple features profile
+ var files = [
+ {path: "v3/point.xml"},
+ {path: "v3/linestring.xml"},
+ {path: "v3/curve.xml", options: {curve: true}},
+ {path: "v3/polygon.xml"},
+ {path: "v3/surface.xml", options: {surface: true}},
+ {path: "v3/multipoint-singular.xml"},
+ {path: "v3/multilinestring-singular.xml", options: {multiCurve: false}},
+ {path: "v3/multicurve-singular.xml"},
+ {path: "v3/multicurve-curve.xml", options: {curve: true}},
+ {path: "v3/multipolygon-singular.xml", options: {multiSurface: false}},
+ {path: "v3/multisurface-singular.xml"},
+ {path: "v3/multisurface-surface.xml", options: {surface: true}}
+ ];
+
+ var len = files.length;
+ t.plan(len);
+
+ var defaults = {
+ featureType: "feature",
+ featureNS: "http://example.com/feature",
+ srsName: "foo" // GML geometry collections require srsName, we only write if provided
+ };
+
+ var format, options, file, geom, doc, node;
+ for(var i=0; i<len; ++i) {
+ file = files[i].path;
+ geom = cases[file];
+ if(geom) {
+ doc = readXML(file);
+ if(doc && doc.documentElement) {
+ options = OpenLayers.Util.extend({}, defaults);
+ if(files[i].options) {
+ OpenLayers.Util.extend(options, files[i].options);
+ }
+ format = new OpenLayers.Format.GML.v3(options);
+ node = format.writeNode("feature:_geometry", geom);
+ t.xml_eq(node.firstChild, doc.documentElement, "[" + file + "] geometry written");
+ } else {
+ t.fail("[" + file + "] xml parsing");
+ }
+ } else {
+ t.fail("[" + file + "] case not found");
+ }
+ }
+ }
+
+ function test_writeNode_bounds(t) {
+ var files = [
+ "v3/envelope.xml"
+ ];
+
+ var len = files.length;
+ t.plan(len);
+
+ var format = new OpenLayers.Format.GML.v3({
+ featureType: "feature",
+ featureNS: "http://example.com/feature",
+ srsName: "foo" // GML envelopes require srsName, we only write if provided
+ });
+ var file, bounds, doc, node;
+ for(var i=0; i<len; ++i) {
+ file = files[i];
+ bounds = cases[file];
+ if(bounds) {
+ doc = readXML(file);
+ if(doc && doc.documentElement) {
+ node = format.writeNode("gml:Envelope", bounds);
+ t.xml_eq(node, doc.documentElement, "[" + file + "] bounds written");
+ } else {
+ t.fail("[" + file + "] xml parsing");
+ }
+ } else {
+ t.fail("[" + file + "] case not found");
+ }
+ }
+ }
+
+ function test_boundedBy(t) {
+ t.plan(5);
+
+ var doc = readXML("v3/topp-states-wfs.xml");
+ var format = new OpenLayers.Format.GML.v3({
+ featureType: "states",
+ featureNS: "http://www.openplans.org/topp",
+ geometryName: "the_geom",
+ xy: false
+ });
+ var features = format.read(doc.documentElement);
+ var bounds = features[0].bounds;
+
+ t.ok(bounds instanceof OpenLayers.Bounds, "feature given a bounds");
+ t.eq(bounds.left.toFixed(2), "-91.52", "bounds left correct");
+ t.eq(bounds.bottom.toFixed(2), "36.99", "bounds bottom correct");
+ t.eq(bounds.right.toFixed(2), "-87.51", "bounds right correct");
+ t.eq(bounds.top.toFixed(2), "42.51", "bounds top correct");
+ }
+
+ function test_read(t) {
+ t.plan(8);
+ var doc = readXML("v3/topp-states-wfs.xml");
+ var format = new OpenLayers.Format.GML.v3({
+ featureType: "states",
+ featureNS: "http://www.openplans.org/topp",
+ geometryName: "the_geom",
+ xy: false
+ });
+ var features = format.read(doc.documentElement);
+
+ t.eq(features.length, 3, "read 3 features");
+ var feature = features[0];
+ t.eq(feature.fid, "states.1", "read fid");
+ t.eq(feature.geometry.CLASS_NAME, "OpenLayers.Geometry.MultiPolygon",
+ "read multipolygon geometry");
+ var attributes = feature.attributes;
+ t.eq(attributes["STATE_NAME"], "Illinois", "read STATE_NAME");
+ t.eq(attributes["STATE_FIPS"], "17", "read STATE_FIPS");
+ t.eq(attributes["SUB_REGION"], "E N Cen", "read SUB_REGION");
+ t.eq(attributes["STATE_ABBR"], "IL", "read STATE_ABBR");
+ t.eq(attributes["LAND_KM"], "143986.61", "read LAND_KM");
+ }
+
+ function test_read_autoconfig(t) {
+ t.plan(7);
+ var doc = readXML("v3/topp-states-wfs.xml");
+ var format = new OpenLayers.Format.GML.v3();
+ var features = format.read(doc.documentElement);
+
+ t.eq(features.length, 3, "read 3 features");
+ var feature = features[0];
+ t.eq(feature.fid, "states.1", "read fid");
+ t.eq(feature.geometry.CLASS_NAME, "OpenLayers.Geometry.MultiPolygon",
+ "read multipolygon geometry");
+ t.eq(format.featureType, "states", "featureType correctly auto-configured");
+ t.eq(format.featureNS, "http://www.openplans.org/topp", "featureNS correctly auto-configured");
+
+ t.eq(format.autoConfig, true, "autoConfig set to true");
+ format.autoConfig = false;
+ format.read(doc.documentElement);
+ t.eq(format.autoConfig, false, "now that featureNS is set, the format does not auto-configure again");
+ }
+
+ function test_emptyAttribute(t) {
+ t.plan(4);
+ var str =
+ '<gml:featureMembers xmlns:gml="http://www.opengis.net/gml">' +
+ '<topp:gnis_pop gml:id="gnis_pop.148604" xmlns:topp="http://www.openplans.org/topp">' +
+ '<gml:name>Aflu</gml:name>' +
+ '<topp:the_geom>' +
+ '<gml:Point srsName="urn:x-ogc:def:crs:EPSG:4326">' +
+ '<gml:pos>34.12 2.09</gml:pos>' +
+ '</gml:Point>' +
+ '</topp:the_geom>' +
+ '<topp:population>84683</topp:population>' +
+ '<topp:country>Algeria</topp:country>' +
+ '<topp:type>place</topp:type>' +
+ '<topp:name>Aflu</topp:name>' +
+ '<topp:empty></topp:empty>' +
+ '</topp:gnis_pop>' +
+ '</gml:featureMembers>';
+
+ var format = new OpenLayers.Format.GML.v3({
+ featureType: "gnis_pop",
+ featureNS: "http://www.openplans.org/topp",
+ geometryName: "the_geom"
+ });
+
+ var features = format.read(str);
+ t.eq(features.length, 1, "read one feature");
+ var attr = features[0].attributes;
+ t.eq(attr.name, "Aflu", "correctly read attribute value");
+ t.eq(attr.foo, undefined, "bogus attribute is undefined");
+ t.eq(attr.empty, "", "empty attribute value is empty string");
+ }
+
+ function test_repeatedName(t) {
+ // test that if an attribute name matches the featureType, all goes well
+ t.plan(2);
+ var doc = readXML("v3/repeated-name.xml");
+ var format = new OpenLayers.Format.GML.v3({
+ featureType: "zoning",
+ featureNS: "http://opengeo.org/#medford",
+ geometryName: "the_geom",
+ xy: false
+ });
+ var features = format.read(doc.documentElement);
+
+ t.eq(features.length, 1, "read one feature");
+ var atts = features[0].attributes;
+ t.eq(atts.zoning, "I-L", "correct zoning attribute on zoning feature type");
+
+ }
+
+ function test_write(t) {
+ t.plan(1);
+ var doc = readXML("v3/topp-states-gml.xml");
+ var format = new OpenLayers.Format.GML.v3({
+ featureType: "states",
+ featureNS: "http://www.openplans.org/topp",
+ geometryName: "the_geom",
+ srsName: "urn:x-ogc:def:crs:EPSG:4326",
+ xy: false,
+ schemaLocation: "http://www.openplans.org/topp http://sigma.openplans.org:80/geoserver/wfs?service=WFS&version=1.1.0&request=DescribeFeatureType&typeName=topp:states http://www.opengis.net/gml http://schemas.opengis.net/gml/3.2.1/gml.xsd"
+ });
+ var features = format.read(doc.documentElement);
+
+ var got = format.write(features);
+ t.xml_eq(got, doc.documentElement, "gml:featureMembers round trip");
+ }
+
+ </script>
+</head>
+<body>
+<div id="v3/envelope.xml"><!--
+<gml:Envelope xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:lowerCorner>1 2</gml:lowerCorner>
+ <gml:upperCorner>3 4</gml:upperCorner>
+</gml:Envelope>
+--></div>
+<div id="v3/linearring.xml"><!--
+<gml:LinearRing xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:posList>1 2 3 4 5 6 1 2</gml:posList>
+</gml:LinearRing>
+--></div>
+<div id="v3/linestring.xml"><!--
+<gml:LineString xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:posList>1 2 3 4</gml:posList>
+</gml:LineString>
+--></div>
+<div id="v3/linestring3d.xml"><!--
+<gml:LineString xmlns:gml="http://www.opengis.net/gml" srsName="foo" srsDimension="3">
+ <gml:posList>1 2 3 4 5 6</gml:posList>
+</gml:LineString>
+--></div>
+<div id="v3/curve.xml"><!--
+<gml:Curve xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:segments>
+ <gml:LineStringSegment>
+ <gml:posList>1 2 3 4</gml:posList>
+ </gml:LineStringSegment>
+ </gml:segments>
+</gml:Curve>
+--></div>
+<div id="v3/multilinestring-plural.xml"><!--
+<gml:MultiLineString xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:lineStringMembers>
+ <gml:LineString>
+ <gml:posList>1 2 2 3</gml:posList>
+ </gml:LineString>
+ <gml:LineString>
+ <gml:posList>3 4 4 5</gml:posList>
+ </gml:LineString>
+ </gml:lineStringMembers>
+</gml:MultiLineString>
+--></div>
+<div id="v3/multilinestring-singular.xml"><!--
+<gml:MultiLineString xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:lineStringMember>
+ <gml:LineString>
+ <gml:posList>1 2 2 3</gml:posList>
+ </gml:LineString>
+ </gml:lineStringMember>
+ <gml:lineStringMember>
+ <gml:LineString>
+ <gml:posList>3 4 4 5</gml:posList>
+ </gml:LineString>
+ </gml:lineStringMember>
+</gml:MultiLineString>
+--></div>
+<div id="v3/multicurve-singular.xml"><!--
+<gml:MultiCurve xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:curveMember>
+ <gml:LineString>
+ <gml:posList>1 2 2 3</gml:posList>
+ </gml:LineString>
+ </gml:curveMember>
+ <gml:curveMember>
+ <gml:LineString>
+ <gml:posList>3 4 4 5</gml:posList>
+ </gml:LineString>
+ </gml:curveMember>
+</gml:MultiCurve>
+--></div>
+<div id="v3/multicurve-curve.xml"><!--
+<gml:MultiCurve xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:curveMember>
+ <gml:Curve>
+ <gml:segments>
+ <gml:LineStringSegment>
+ <gml:posList>1 2 2 3</gml:posList>
+ </gml:LineStringSegment>
+ </gml:segments>
+ </gml:Curve>
+ </gml:curveMember>
+ <gml:curveMember>
+ <gml:Curve>
+ <gml:segments>
+ <gml:LineStringSegment>
+ <gml:posList>3 4 4 5</gml:posList>
+ </gml:LineStringSegment>
+ </gml:segments>
+ </gml:Curve>
+ </gml:curveMember>
+</gml:MultiCurve>
+--></div>
+<div id="v3/multipoint-plural.xml"><!--
+<gml:MultiPoint xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:pointMembers>
+ <gml:Point>
+ <gml:pos>1 2</gml:pos>
+ </gml:Point>
+ <gml:Point>
+ <gml:pos>2 3</gml:pos>
+ </gml:Point>
+ <gml:Point>
+ <gml:pos>3 4</gml:pos>
+ </gml:Point>
+ </gml:pointMembers>
+</gml:MultiPoint>
+--></div>
+<div id="v3/multipoint-singular.xml"><!--
+<gml:MultiPoint xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:pointMember>
+ <gml:Point>
+ <gml:pos>1 2</gml:pos>
+ </gml:Point>
+ </gml:pointMember>
+ <gml:pointMember>
+ <gml:Point>
+ <gml:pos>2 3</gml:pos>
+ </gml:Point>
+ </gml:pointMember>
+ <gml:pointMember>
+ <gml:Point>
+ <gml:pos>3 4</gml:pos>
+ </gml:Point>
+ </gml:pointMember>
+</gml:MultiPoint>
+--></div>
+<div id="v3/multipolygon-plural.xml"><!--
+<gml:MultiPolygon xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:polygonMembers>
+ <gml:Polygon>
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList>1 2 3 4 5 6 1 2</gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+ <gml:interior>
+ <gml:LinearRing>
+ <gml:posList>2 3 4 5 6 7 2 3</gml:posList>
+ </gml:LinearRing>
+ </gml:interior>
+ <gml:interior>
+ <gml:LinearRing>
+ <gml:posList>3 4 5 6 7 8 3 4</gml:posList>
+ </gml:LinearRing>
+ </gml:interior>
+ </gml:Polygon>
+ <gml:Polygon>
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList>1 2 3 4 5 6 1 2</gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+ </gml:Polygon>
+ </gml:polygonMembers>
+</gml:MultiPolygon>
+--></div>
+<div id="v3/multipolygon-singular.xml"><!--
+<gml:MultiPolygon xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:polygonMember>
+ <gml:Polygon>
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList>1 2 3 4 5 6 1 2</gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+ <gml:interior>
+ <gml:LinearRing>
+ <gml:posList>2 3 4 5 6 7 2 3</gml:posList>
+ </gml:LinearRing>
+ </gml:interior>
+ <gml:interior>
+ <gml:LinearRing>
+ <gml:posList>3 4 5 6 7 8 3 4</gml:posList>
+ </gml:LinearRing>
+ </gml:interior>
+ </gml:Polygon>
+ </gml:polygonMember>
+ <gml:polygonMember>
+ <gml:Polygon>
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList>1 2 3 4 5 6 1 2</gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+ </gml:Polygon>
+ </gml:polygonMember>
+</gml:MultiPolygon>
+--></div>
+<div id="v3/multisurface-plural.xml"><!--
+<gml:MultiSurface xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:surfaceMembers>
+ <gml:Polygon>
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList>1 2 3 4 5 6 1 2</gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+ <gml:interior>
+ <gml:LinearRing>
+ <gml:posList>2 3 4 5 6 7 2 3</gml:posList>
+ </gml:LinearRing>
+ </gml:interior>
+ <gml:interior>
+ <gml:LinearRing>
+ <gml:posList>3 4 5 6 7 8 3 4</gml:posList>
+ </gml:LinearRing>
+ </gml:interior>
+ </gml:Polygon>
+ <gml:Polygon>
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList>1 2 3 4 5 6 1 2</gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+ </gml:Polygon>
+ </gml:surfaceMembers>
+</gml:MultiSurface>
+--></div>
+<div id="v3/multisurface-singular.xml"><!--
+<gml:MultiSurface xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:surfaceMember>
+ <gml:Polygon>
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList>1 2 3 4 5 6 1 2</gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+ <gml:interior>
+ <gml:LinearRing>
+ <gml:posList>2 3 4 5 6 7 2 3</gml:posList>
+ </gml:LinearRing>
+ </gml:interior>
+ <gml:interior>
+ <gml:LinearRing>
+ <gml:posList>3 4 5 6 7 8 3 4</gml:posList>
+ </gml:LinearRing>
+ </gml:interior>
+ </gml:Polygon>
+ </gml:surfaceMember>
+ <gml:surfaceMember>
+ <gml:Polygon>
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList>1 2 3 4 5 6 1 2</gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+ </gml:Polygon>
+ </gml:surfaceMember>
+</gml:MultiSurface>
+--></div>
+<div id="v3/multisurface-surface.xml"><!--
+<gml:MultiSurface xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:surfaceMember>
+ <gml:Surface>
+ <gml:patches>
+ <gml:PolygonPatch interpolation="planar">
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList>1 2 3 4 5 6 1 2</gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+ <gml:interior>
+ <gml:LinearRing>
+ <gml:posList>2 3 4 5 6 7 2 3</gml:posList>
+ </gml:LinearRing>
+ </gml:interior>
+ <gml:interior>
+ <gml:LinearRing>
+ <gml:posList>3 4 5 6 7 8 3 4</gml:posList>
+ </gml:LinearRing>
+ </gml:interior>
+ </gml:PolygonPatch>
+ </gml:patches>
+ </gml:Surface>
+ </gml:surfaceMember>
+ <gml:surfaceMember>
+ <gml:Surface>
+ <gml:patches>
+ <gml:PolygonPatch interpolation="planar">
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList>1 2 3 4 5 6 1 2</gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+ </gml:PolygonPatch>
+ </gml:patches>
+ </gml:Surface>
+ </gml:surfaceMember>
+</gml:MultiSurface>
+--></div>
+<div id="v3/point.xml"><!--
+<gml:Point xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:pos>1 2</gml:pos>
+</gml:Point>
+--></div>
+<div id="v3/polygon.xml"><!--
+<gml:Polygon xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList>1 2 3 4 5 6 1 2</gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+ <gml:interior>
+ <gml:LinearRing>
+ <gml:posList>2 3 4 5 6 7 2 3</gml:posList>
+ </gml:LinearRing>
+ </gml:interior>
+ <gml:interior>
+ <gml:LinearRing>
+ <gml:posList>3 4 5 6 7 8 3 4</gml:posList>
+ </gml:LinearRing>
+ </gml:interior>
+</gml:Polygon>
+--></div>
+<div id="v3/surface.xml"><!--
+<gml:Surface xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:patches>
+ <gml:PolygonPatch interpolation="planar">
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList>1 2 3 4 5 6 1 2</gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+ <gml:interior>
+ <gml:LinearRing>
+ <gml:posList>2 3 4 5 6 7 2 3</gml:posList>
+ </gml:LinearRing>
+ </gml:interior>
+ <gml:interior>
+ <gml:LinearRing>
+ <gml:posList>3 4 5 6 7 8 3 4</gml:posList>
+ </gml:LinearRing>
+ </gml:interior>
+ </gml:PolygonPatch>
+ </gml:patches>
+</gml:Surface>
+--></div>
+<div id="v3/topp-states-gml.xml"><!--
+<?xml version="1.0" encoding="UTF-8"?>
+<gml:featureMembers xsi:schemaLocation="http://www.openplans.org/topp http://sigma.openplans.org:80/geoserver/wfs?service=WFS&amp;version=1.1.0&amp;request=DescribeFeatureType&amp;typeName=topp:states http://www.opengis.net/gml http://schemas.opengis.net/gml/3.2.1/gml.xsd" xmlns:topp="http://www.openplans.org/topp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml"><topp:states fid="states.1"><topp:the_geom><gml:MultiSurface srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>37.5101 -88.0711 37.4761 -88.0871 37.4421 -88.3111 37.4091 -88.3591 37.4201 -88.4191 37.4001 -88.4671 37.2961 -88.5111 37.2571 -88.5011 37.2051 -88.4501 37.1561 -88.4221 37.0981 -88.4501 37.0721 -88.4761 37.0681 -88.4901 37.0641 -88.5171 37.0721 -88.5591 37.1091 -88.6141 37.1351 -88.6881 37.1411 -88.7391 37.1521 -88.7461 37.2021 -88.8631 37.2181 -88.9321 37.2201 -88.9931 37.1851 -89.0651 37.1121 -89.1161 37.0931 -89.1461 37.0641 -89.1691 37.0251 -89.1741 36.9981 -89.1501 36.9881 -89.1291 36.9861 -89.1931 37.0281 -89.2101 37.0411 -89.2371 37.0871 -89.2641 37.0911 -89.2841 37.0851 -89.3031 37.0601 -89.3091 37.0271 -89.2641 37.0081 -89.2621 36.9991 -89.2821 37.0091 -89.3101 37.0491 -89.3821 37.0991 -89.3791 37.1371 -89.4231 37.1651 -89.4401 37.2241 -89.4681 37.2531 -89.4651 37.2561 -89.4891 37.2761 -89.5131 37.3041 -89.5131 37.3291 -89.5001 37.3391 -89.4681 37.3551 -89.4351 37.4111 -89.4271 37.4531 -89.4531 37.4911 -89.4941 37.5711 -89.5241 37.6151 -89.5131 37.6501 -89.5191 37.6791 -89.5131 37.6941 -89.5211 37.7061 -89.5811 37.7451 -89.6661 37.7831 -89.6751 37.8041 -89.6911 37.8401 -89.7281 37.9051 -89.8511 37.9051 -89.8611 37.8911 -89.8661 37.8751 -89.9001 37.8781 -89.9371 37.9111 -89.9781 37.9631 -89.9581 37.9691 -90.0101 37.9931 -90.0411 38.0321 -90.1191 38.0531 -90.1341 38.0881 -90.2071 38.1221 -90.2541 38.1661 -90.2891 38.1881 -90.3361 38.2341 -90.3641 38.3231 -90.3691 38.3651 -90.3581 38.3901 -90.3391 38.4271 -90.3011 38.5181 -90.2651 38.5321 -90.2611 38.5621 -90.2401 38.6101 -90.1831 38.6581 -90.1831 38.7001 -90.2021 38.7231 -90.1961 38.7731 -90.1631 38.7851 -90.1351 38.8001 -90.1211 38.8301 -90.1131 38.8531 -90.1321 38.9141 -90.2431 38.9241 -90.2781 38.9241 -90.3191 38.9621 -90.4131 38.9591 -90.4691 38.8911 -90.5301 38.8711 -90.5701 38.8801 -90.6271 38.9351 -90.6681 39.0371 -90.7061 39.0581 -90.7071 39.0931 -90.6901 39.1441 -90.7161 39.1951 -90.7181 39.2241 -90.7321 39.2471 -90.7381 39.2961 -90.7791 39.3501 -90.8501 39.4001 -90.9471 39.4441 -91.0361 39.4731 -91.0641 39.5281 -91.0931 39.5521 -91.1561 39.6001 -91.2031 39.6851 -91.3171 39.7241 -91.3671 39.7611 -91.3731 39.8031 -91.3811 39.8631 -91.4491 39.8851 -91.4501 39.9011 -91.4341 39.9211 -91.4301 39.9461 -91.4471 40.0051 -91.4871 40.0661 -91.5041 40.1341 -91.5161 40.2001 -91.5061 40.2511 -91.4981 40.3091 -91.4861 40.3711 -91.4481 40.3861 -91.4181 40.3921 -91.3851 40.4021 -91.3721 40.4471 -91.3851 40.5031 -91.3741 40.5281 -91.3821 40.5471 -91.4121 40.5721 -91.4111 40.6031 -91.3751 40.6391 -91.2621 40.6431 -91.2141 40.6561 -91.1621 40.6821 -91.1291 40.7051 -91.1191 40.7611 -91.0921 40.8331 -91.0881 40.8791 -91.0491 40.9231 -90.9831 40.9501 -90.9601 41.0701 -90.9541 41.1041 -90.9571 41.1441 -90.9901 41.1651 -91.0181 41.1761 -91.0561 41.2311 -91.1011 41.2671 -91.1021 41.3341 -91.0731 41.4011 -91.0551 41.4231 -91.0271 41.4311 -91.0001 41.4211 -90.9491 41.4441 -90.8441 41.4491 -90.7791 41.4501 -90.7081 41.4621 -90.6581 41.5091 -90.6001 41.5251 -90.5401 41.5271 -90.4541 41.5431 -90.4341 41.5671 -90.4231 41.5861 -90.3481 41.6021 -90.3391 41.6491 -90.3411 41.7221 -90.3261 41.7561 -90.3041 41.7811 -90.2551 41.8061 -90.1951 41.9301 -90.1541 41.9831 -90.1421 42.0331 -90.1501 42.0611 -90.1681 42.1031 -90.1661 42.1201 -90.1761 42.1221 -90.1911 42.1591 -90.2301 42.1971 -90.3231 42.2101 -90.3671 42.2421 -90.4071 42.2631 -90.4171 42.3401 -90.4271 42.3601 -90.4411 42.3881 -90.4911 42.4211 -90.5631 42.4601 -90.6051 42.4751 -90.6481 42.4941 -90.6511 42.5091 -90.6381 42.5081 -90.4191 42.5041 -89.9231 42.5031 -89.8341 42.4971 -89.4001 42.4971 -89.3591 42.4901 -88.9391 42.4901 -88.7641 42.4891 -88.7061 42.4911 -88.2971 42.4891 -88.1941 42.4891 -87.7971 42.3141 -87.8361 42.1561 -87.7601 42.0591 -87.6701 41.8471 -87.6121 41.7231 -87.5291 41.4691 -87.5321 41.3011 -87.5321 41.1731 -87.5311 41.0091 -87.5321 40.7451 -87.5321 40.4941 -87.5371 40.4831 -87.5351 40.1661 -87.5351 39.8871 -87.5351 39.6091 -87.5351 39.4771 -87.5381 39.3501 -87.5401 39.3381 -87.5971 39.3071 -87.6251 39.2971 -87.6101 39.2811 -87.6151 39.2581 -87.6061 39.2481 -87.5841 39.2081 -87.5881 39.1981 -87.5941 39.1961 -87.6071 39.1681 -87.6441 39.1461 -87.6701 39.1301 -87.6591 39.1131 -87.6621 39.1031 -87.6311 39.0881 -87.6301 39.0841 -87.6121 39.0621 -87.5851 38.9951 -87.5811 38.9941 -87.5911 38.9771 -87.5471 38.9631 -87.5331 38.9311 -87.5301 38.9041 -87.5391 38.8691 -87.5591 38.8571 -87.5501 38.7951 -87.5071 38.7761 -87.5191 38.7691 -87.5081 38.7361 -87.5081 38.6851 -87.5431 38.6721 -87.5881 38.6421 -87.6251 38.6221 -87.6281 38.5991 -87.6191 38.5931 -87.6401 38.5731 -87.6521 38.5471 -87.6721 38.5151 -87.6511 38.5001 -87.6531 38.5041 -87.6791 38.4811 -87.6921 38.4661 -87.7561 38.4571 -87.7581 38.4451 -87.7381 38.4171 -87.7481 38.3781 -87.7841 38.3521 -87.8341 38.2861 -87.8501 38.2851 -87.8631 38.3161 -87.8741 38.3151 -87.8831 38.3001 -87.8881 38.2811 -87.9141 38.3021 -87.9131 38.3041 -87.9251 38.2411 -87.9801 38.2341 -87.9861 38.2001 -87.9771 38.1711 -87.9321 38.1571 -87.9311 38.1361 -87.9501 38.1311 -87.9731 38.1031 -88.0181 38.0921 -88.0121 38.0961 -87.9641 38.0731 -87.9751 38.0541 -88.0341 38.0451 -88.0431 38.0381 -88.0411 38.0331 -88.0211 38.0081 -88.0291 37.9751 -88.0211 37.9561 -88.0421 37.9341 -88.0411 37.9291 -88.0641 37.944 -88.0781 37.9231 -88.084 37.9171 -88.0301 37.9051 -88.0261 37.8961 -88.0441 37.9061 -88.1001 37.8951 -88.1011 37.8671 -88.0751 37.8431 -88.0341 37.8271 -88.0421 37.8311 -88.0891 37.8171 -88.0861 37.8051 -88.0351 37.7351 -88.0721 37.7001 -88.1331 37.6601 -88.1591 37.6281 -88.1571 37.5831 -88.1341 37.5101 -88.0711</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Illinois</topp:STATE_NAME><topp:STATE_FIPS>17</topp:STATE_FIPS><topp:SUB_REGION>E N Cen</topp:SUB_REGION><topp:STATE_ABBR>IL</topp:STATE_ABBR><topp:LAND_KM>143986.61</topp:LAND_KM><topp:WATER_KM>1993.335</topp:WATER_KM><topp:PERSONS>1.1431E7</topp:PERSONS><topp:FAMILIES>2924880.0</topp:FAMILIES><topp:HOUSHOLD>4202240.0</topp:HOUSHOLD><topp:MALE>5552233.0</topp:MALE><topp:FEMALE>5878369.0</topp:FEMALE><topp:WORKERS>4199206.0</topp:WORKERS><topp:DRVALONE>3741715.0</topp:DRVALONE><topp:CARPOOL>652603.0</topp:CARPOOL><topp:PUBTRANS>538071.0</topp:PUBTRANS><topp:EMPLOYED>5417967.0</topp:EMPLOYED><topp:UNEMPLOY>385040.0</topp:UNEMPLOY><topp:SERVICE>1360159.0</topp:SERVICE><topp:MANUAL>828906.0</topp:MANUAL><topp:P_MALE>0.486</topp:P_MALE><topp:P_FEMALE>0.514</topp:P_FEMALE><topp:SAMP_POP>1747776.0</topp:SAMP_POP></topp:states><topp:states fid="states.2"><topp:the_geom><gml:MultiSurface srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>38.9661 -77.0081 38.8891 -76.9111 38.7881 -77.0451 38.8131 -77.0351 38.8291 -77.0451 38.8381 -77.0401 38.8621 -77.0391 38.8861 -77.0671 38.9151 -77.0781 38.9321 -77.1221 38.9931 -77.0421 38.9661 -77.0081</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>District of Columbia</topp:STATE_NAME><topp:STATE_FIPS>11</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>DC</topp:STATE_ABBR><topp:LAND_KM>159.055</topp:LAND_KM><topp:WATER_KM>17.991</topp:WATER_KM><topp:PERSONS>606900.0</topp:PERSONS><topp:FAMILIES>122087.0</topp:FAMILIES><topp:HOUSHOLD>249634.0</topp:HOUSHOLD><topp:MALE>282970.0</topp:MALE><topp:FEMALE>323930.0</topp:FEMALE><topp:WORKERS>229975.0</topp:WORKERS><topp:DRVALONE>106694.0</topp:DRVALONE><topp:CARPOOL>36621.0</topp:CARPOOL><topp:PUBTRANS>111422.0</topp:PUBTRANS><topp:EMPLOYED>303994.0</topp:EMPLOYED><topp:UNEMPLOY>23442.0</topp:UNEMPLOY><topp:SERVICE>65498.0</topp:SERVICE><topp:MANUAL>22407.0</topp:MANUAL><topp:P_MALE>0.466</topp:P_MALE><topp:P_FEMALE>0.534</topp:P_FEMALE><topp:SAMP_POP>72696.0</topp:SAMP_POP></topp:states><topp:states fid="states.3"><topp:the_geom><gml:MultiSurface srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>38.5571 -75.7071 38.6491 -75.7111 38.8301 -75.7241 39.1411 -75.7521 39.2471 -75.7611 39.2951 -75.7641 39.3831 -75.7721 39.7231 -75.7911 39.7241 -75.7751 39.7741 -75.7451 39.8201 -75.6951 39.8381 -75.6441 39.8401 -75.5831 39.8261 -75.4701 39.7981 -75.4201 39.7891 -75.4121 39.7781 -75.4281 39.7631 -75.4601 39.7411 -75.4751 39.7191 -75.4761 39.7141 -75.4891 39.6121 -75.6101 39.5661 -75.5621 39.4631 -75.5901 39.3661 -75.5151 39.2571 -75.4021 39.0731 -75.3971 39.0121 -75.3241 38.9451 -75.3071 38.8081 -75.1901 38.7991 -75.0831 38.4491 -75.0451 38.4491 -75.0681 38.4501 -75.0931 38.4551 -75.3501 38.4631 -75.6991 38.5571 -75.7071</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Delaware</topp:STATE_NAME><topp:STATE_FIPS>10</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>DE</topp:STATE_ABBR><topp:LAND_KM>5062.456</topp:LAND_KM><topp:WATER_KM>1385.022</topp:WATER_KM><topp:PERSONS>666168.0</topp:PERSONS><topp:FAMILIES>175867.0</topp:FAMILIES><topp:HOUSHOLD>247497.0</topp:HOUSHOLD><topp:MALE>322968.0</topp:MALE><topp:FEMALE>343200.0</topp:FEMALE><topp:WORKERS>247566.0</topp:WORKERS><topp:DRVALONE>258087.0</topp:DRVALONE><topp:CARPOOL>42968.0</topp:CARPOOL><topp:PUBTRANS>8069.0</topp:PUBTRANS><topp:EMPLOYED>335147.0</topp:EMPLOYED><topp:UNEMPLOY>13945.0</topp:UNEMPLOY><topp:SERVICE>87973.0</topp:SERVICE><topp:MANUAL>44140.0</topp:MANUAL><topp:P_MALE>0.485</topp:P_MALE><topp:P_FEMALE>0.515</topp:P_FEMALE><topp:SAMP_POP>102776.0</topp:SAMP_POP></topp:states><topp:states fid="states.4"><topp:the_geom><gml:MultiSurface srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>38.4801 -79.2311 38.4371 -79.2721 38.4121 -79.3171 38.4621 -79.4861 38.5531 -79.5361 38.5921 -79.6421 38.5501 -79.6691 38.5201 -79.6651 38.5001 -79.6921 38.4301 -79.6841 38.3941 -79.7201 38.3511 -79.7331 38.3531 -79.7641 38.3141 -79.8001 38.2981 -79.8031 38.2841 -79.7861 38.2681 -79.7931 38.2501 -79.8311 38.1791 -79.9161 38.1621 -79.9101 38.1211 -79.9351 38.1031 -79.9281 38.0671 -79.9571 38.0381 -79.9661 37.9891 -80.0001 37.9551 -80.0551 37.9141 -80.1061 37.8911 -80.1181 37.8771 -80.1601 37.8601 -80.1721 37.8421 -80.1711 37.8021 -80.2231 37.7781 -80.2201 37.7571 -80.2541 37.7251 -80.2501 37.6821 -80.3031 37.6711 -80.2951 37.6521 -80.3051 37.6401 -80.3011 37.6401 -80.2541 37.6241 -80.2191 37.5961 -80.2461 37.5661 -80.3161 37.5331 -80.3261 37.5281 -80.3081 37.5361 -80.2801 37.5111 -80.2881 37.4911 -80.3471 37.4751 -80.3521 37.4651 -80.3881 37.4341 -80.4251 37.4221 -80.4741 37.4331 -80.4871 37.4601 -80.4881 37.4741 -80.5081 37.4691 -80.5421 37.4451 -80.5971 37.3881 -80.7051 37.3921 -80.7291 37.3871 -80.7461 37.3781 -80.7471 37.3711 -80.7631 37.3861 -80.7701 37.3911 -80.7991 37.4121 -80.7991 37.4231 -80.8501 37.3881 -80.8771 37.3501 -80.8481 37.3391 -80.8551 37.3011 -80.9341 37.2911 -80.9681 37.2961 -80.9781 37.3061 -80.9861 37.2851 -81.0251 37.2741 -81.1401 37.2401 -81.2231 37.2931 -81.3121 37.3381 -81.3581 37.3111 -81.3911 37.2821 -81.4031 37.2541 -81.4751 37.2521 -81.4951 37.2341 -81.5051 37.2061 -81.5561 37.2041 -81.6661 37.2351 -81.7011 37.2501 -81.7381 37.2721 -81.7521 37.2871 -81.7921 37.2791 -81.8151 37.2851 -81.8391 37.3061 -81.8581 37.3251 -81.8631 37.3401 -81.8971 37.3711 -81.9261 37.4151 -81.9201 37.4661 -81.9881 37.4821 -81.9761 37.4921 -81.9481 37.5061 -81.9351 37.5311 -81.9591 37.5431 -81.9761 37.5301 -82.0261 37.5511 -82.0491 37.5251 -82.0551 37.5481 -82.0841 37.5571 -82.1421 37.5651 -82.1461 37.5691 -82.1371 37.5901 -82.1311 37.5931 -82.1591 37.6401 -82.1851 37.6231 -82.2051 37.6561 -82.2381 37.6681 -82.2951 37.7441 -82.3291 37.7581 -82.3191 37.7841 -82.3391 37.8111 -82.4051 37.8721 -82.4211 37.8941 -82.4371 37.9221 -82.5001 37.9421 -82.4931 37.9541 -82.4801 37.9751 -82.4751 38.0151 -82.5241 38.1091 -82.5931 38.1461 -82.6461 38.1691 -82.6471 38.1781 -82.6131 38.1931 -82.6061 38.2381 -82.6161 38.2451 -82.5891 38.2551 -82.5741 38.2921 -82.5801 38.3071 -82.5721 38.3681 -82.5981 38.4121 -82.5861 38.4031 -82.5751 38.4001 -82.5471 38.4051 -82.4951 38.4301 -82.4151 38.4281 -82.3941 38.4411 -82.3291 38.4651 -82.3141 38.5791 -82.2901 38.5941 -82.2711 38.5841 -82.2131 38.5941 -82.1841 38.6321 -82.1731 38.6771 -82.1891 38.7101 -82.1841 38.7781 -82.2161 38.8041 -82.1971 38.8381 -82.1461 38.8991 -82.1391 38.9521 -82.1011 38.9771 -82.0851 38.9881 -82.0581 39.0141 -82.0431 39.0151 -81.9991 38.9921 -81.9751 38.9911 -81.9371 38.9841 -81.9271 38.9321 -81.8981 38.8941 -81.9311 38.8841 -81.9151 38.8731 -81.8921 38.8851 -81.8661 38.9371 -81.8411 38.9481 -81.8231 38.9231 -81.7831 38.9301 -81.7621 38.9681 -81.7811 39.0161 -81.7751 39.0441 -81.8131 39.0661 -81.8241 39.0761 -81.8191 39.0771 -81.7861 39.0941 -81.7531 39.1251 -81.7441 39.1751 -81.7591 39.2131 -81.7231 39.2191 -81.6981 39.2601 -81.6891 39.2701 -81.6671 39.2651 -81.5721 39.3321 -81.5571 39.3521 -81.5401 39.4061 -81.4651 39.4101 -81.4481 39.4051 -81.4341 39.3451 -81.3761 39.3531 -81.3381 39.3861 -81.2841 39.3881 -81.2371 39.4081 -81.2251 39.4151 -81.2001 39.4371 -81.1801 39.4671 -81.1171 39.4961 -81.0981 39.5321 -81.0371 39.5441 -81.0321 39.5811 -80.9831 39.6061 -80.9321 39.6071 -80.9121 39.6241 -80.8811 39.6621 -80.8721 39.6801 -80.8631 39.7031 -80.8321 39.7181 -80.8321 39.7361 -80.8561 39.7591 -80.8701 39.8081 -80.8191 39.8391 -80.8261 39.8561 -80.7981 39.8721 -80.7911 39.9041 -80.8121 39.9151 -80.8081 39.9191 -80.7961 39.9131 -80.7681 39.9211 -80.7591 39.9461 -80.7631 39.9831 -80.7391 40.0351 -80.7381 40.1541 -80.7021 40.1681 -80.7011 40.1941 -80.6781 40.2451 -80.6501 40.2761 -80.6141 40.3061 -80.6041 40.3731 -80.6091 40.3881 -80.6291 40.3981 -80.6281 40.4801 -80.6021 40.5041 -80.6251 40.5391 -80.6331 40.5681 -80.6681 40.5821 -80.6671 40.6131 -80.6371 40.6191 -80.6111 40.6151 -80.5741 40.6371 -80.5221 40.4781 -80.5241 40.4021 -80.5231 40.1621 -80.5261 40.0221 -80.5251 39.9581 -80.5241 39.7211 -80.5241 39.7191 -80.4291 39.7211 -79.9181 39.7211 -79.7651 39.7201 -79.4811 39.1971 -79.4901 39.2131 -79.4611 39.2111 -79.4491 39.2691 -79.3851 39.2911 -79.3461 39.3001 -79.2951 39.3251 -79.2801 39.3481 -79.2601 39.3931 -79.1631 39.4131 -79.1581 39.4161 -79.1311 39.4471 -79.1041 39.4641 -79.0961 39.4701 -79.1041 39.4701 -79.0701 39.4851 -79.0641 39.4831 -79.0491 39.4381 -78.9701 39.4601 -78.9551 39.5251 -78.8711 39.5631 -78.8381 39.5661 -78.8061 39.5851 -78.8221 39.6151 -78.7981 39.6301 -78.7981 39.6441 -78.7721 39.6261 -78.7671 39.6261 -78.7321 39.6211 -78.7301 39.6081 -78.7361 39.6011 -78.7741 39.5811 -78.7611 39.5761 -78.7321 39.5591 -78.7161 39.5361 -78.6661 39.5371 -78.6491 39.5291 -78.6371 39.5351 -78.6041 39.5201 -78.5641 39.5251 -78.5091 39.5191 -78.4811 39.5331 -78.4561 39.5481 -78.4461 39.5491 -78.4211 39.5801 -78.4621 39.5921 -78.4501 39.5871 -78.4041 39.6201 -78.4321 39.6141 -78.3841 39.6311 -78.3771 39.6321 -78.3571 39.6401 -78.3481 39.6181 -78.2731 39.6411 -78.2571 39.6581 -78.2291 39.6731 -78.2271 39.6751 -78.2041 39.6941 -78.1831 39.6751 -78.0941 39.6221 -78.0261 39.5981 -77.9951 39.6111 -77.9641 39.5851 -77.9451 39.5911 -77.9351 39.6141 -77.9471 39.6181 -77.9381 39.5961 -77.9031 39.6001 -77.8911 39.6161 -77.8881 39.6021 -77.8551 39.6051 -77.8421 39.5721 -77.8401 39.5651 -77.8531 39.5641 -77.8851 39.5571 -77.8901 39.5451 -77.8691 39.5141 -77.8641 39.5311 -77.8441 39.5251 -77.8351 39.5291 -77.8291 39.5111 -77.8251 39.5011 -77.8481 39.4931 -77.8251 39.4981 -77.7711 39.4801 -77.7991 39.4591 -77.7851 39.4631 -77.8041 39.4501 -77.7961 39.4391 -77.8041 39.4321 -77.8021 39.4251 -77.7571 39.4031 -77.7411 39.3961 -77.7371 39.3781 -77.7561 39.3601 -77.7451 39.3381 -77.7541 39.3261 -77.7501 39.3171 -77.7271 39.2841 -77.7591 39.2461 -77.7681 39.1961 -77.8051 39.1411 -77.8201 39.1321 -77.8301 39.2651 -78.0331 39.3911 -78.2291 39.4231 -78.2771 39.4561 -78.3471 39.3801 -78.3501 39.3611 -78.3651 39.3501 -78.3441 39.3411 -78.3411 39.2571 -78.4131 39.2441 -78.3991 39.2121 -78.4231 39.1971 -78.4241 39.1701 -78.4021 39.1481 -78.4301 39.1181 -78.4481 39.1111 -78.4851 39.0931 -78.5011 39.0571 -78.5361 39.0351 -78.5641 39.0231 -78.5491 39.0131 -78.5531 38.9671 -78.5981 38.9791 -78.6311 38.9501 -78.6471 38.9211 -78.6801 38.9041 -78.7191 38.9301 -78.7241 38.9291 -78.7371 38.9111 -78.7491 38.8801 -78.7931 38.8331 -78.8161 38.7631 -78.8661 38.8461 -78.9871 38.7991 -79.0331 38.7901 -79.0551 38.7611 -79.0561 38.7071 -79.0871 38.6591 -79.0881 38.6631 -79.1211 38.6581 -79.1271 38.4801 -79.2311</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>West Virginia</topp:STATE_NAME><topp:STATE_FIPS>54</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>WV</topp:STATE_ABBR><topp:LAND_KM>62384.2</topp:LAND_KM><topp:WATER_KM>375.199</topp:WATER_KM><topp:PERSONS>1793477.0</topp:PERSONS><topp:FAMILIES>500259.0</topp:FAMILIES><topp:HOUSHOLD>688557.0</topp:HOUSHOLD><topp:MALE>861536.0</topp:MALE><topp:FEMALE>931941.0</topp:FEMALE><topp:WORKERS>661702.0</topp:WORKERS><topp:DRVALONE>493164.0</topp:DRVALONE><topp:CARPOOL>106918.0</topp:CARPOOL><topp:PUBTRANS>7237.0</topp:PUBTRANS><topp:EMPLOYED>671085.0</topp:EMPLOYED><topp:UNEMPLOY>71142.0</topp:UNEMPLOY><topp:SERVICE>205950.0</topp:SERVICE><topp:MANUAL>124172.0</topp:MANUAL><topp:P_MALE>0.48</topp:P_MALE><topp:P_FEMALE>0.52</topp:P_FEMALE><topp:SAMP_POP>317564.0</topp:SAMP_POP></topp:states><topp:states fid="states.5"><topp:the_geom><gml:MultiSurface srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>38.6491 -75.7111 38.5571 -75.7071 38.4631 -75.6991 38.4551 -75.3501 38.4501 -75.0931 38.3691 -75.1551 38.2731 -75.1501 38.2011 -75.2621 38.0681 -75.3731 38.0161 -75.3721 37.9961 -75.6261 37.9701 -75.6481 37.9791 -75.8651 38.0971 -75.7691 38.1741 -75.8971 38.2311 -75.8381 38.2401 -75.8611 38.2631 -75.7941 38.2581 -75.8941 38.3571 -75.8721 38.3751 -75.8861 38.2821 -75.9491 38.2821 -75.9951 38.3211 -76.0201 38.2581 -76.0651 38.4361 -76.2941 38.4781 -76.2911 38.5431 -76.1921 38.5951 -76.2511 38.5711 -76.0311 38.6221 -76.0281 38.5911 -76.0461 38.6101 -76.0751 38.7071 -76.1241 38.7091 -76.1741 38.7621 -76.2231 38.7691 -76.2671 38.6791 -76.3371 38.6991 -76.3501 38.834 -76.2721 38.7651 -76.1951 38.7881 -76.1651 38.8851 -76.1141 38.8891 -76.0751 38.8981 -76.1021 38.9481 -76.0951 38.9201 -76.1131 38.9731 -76.1991 39.1181 -76.1111 39.0921 -76.2211 39.1301 -76.2381 39.2041 -76.2181 39.3211 -76.1121 39.3581 -76.0371 39.3791 -75.8491 39.3941 -75.9781 39.4711 -75.9521 39.5241 -75.9741 39.5691 -76.0311 39.5421 -76.0781 39.4011 -76.1541 39.3741 -76.2261 39.3931 -76.3641 39.2311 -76.3991 39.2421 -76.5311 39.2591 -76.6041 39.2311 -76.5651 39.1981 -76.5761 39.1801 -76.6071 39.1581 -76.5951 39.1961 -76.5631 39.1181 -76.4231 38.9081 -76.4721 38.7581 -76.5491 38.7091 -76.5251 38.5221 -76.5081 38.3911 -76.3851 38.3461 -76.4051 38.3201 -76.4211 38.3351 -76.4711 38.4101 -76.5201 38.4501 -76.6471 38.2131 -76.3431 38.0451 -76.3301 38.2221 -76.5771 38.2341 -76.7601 38.3911 -76.8641 38.2991 -76.9081 38.3311 -76.9731 38.4261 -77.0021 38.3901 -77.2201 38.4131 -77.2551 38.4871 -77.2771 38.6481 -77.1291 38.6771 -77.1251 38.7031 -77.0931 38.7151 -77.0811 38.7121 -77.0571 38.7181 -77.0461 38.7881 -77.0451 38.8891 -76.9111 38.9661 -77.0081 38.9931 -77.0421 38.9321 -77.1221 38.9641 -77.1521 38.9751 -77.2431 39.0271 -77.2551 39.0621 -77.3241 39.0681 -77.3461 39.0661 -77.4331 39.0801 -77.4591 39.1031 -77.4791 39.1161 -77.5131 39.1571 -77.5161 39.1761 -77.4781 39.2181 -77.4611 39.2291 -77.4641 39.2491 -77.4941 39.2681 -77.5421 39.2981 -77.5681 39.2991 -77.6161 39.3181 -77.6791 39.3171 -77.7271 39.3261 -77.7501 39.3381 -77.7541 39.3601 -77.7451 39.3781 -77.7561 39.3961 -77.7371 39.4031 -77.7411 39.4251 -77.7571 39.4321 -77.8021 39.4391 -77.8041 39.4501 -77.7961 39.4631 -77.8041 39.4591 -77.7851 39.4801 -77.7991 39.4981 -77.7711 39.4931 -77.8251 39.5011 -77.8481 39.5111 -77.8251 39.5291 -77.8291 39.5251 -77.8351 39.5311 -77.8441 39.5141 -77.8641 39.5451 -77.8691 39.5571 -77.8901 39.5641 -77.8851 39.5651 -77.8531 39.5721 -77.8401 39.6051 -77.8421 39.6021 -77.8551 39.6161 -77.8881 39.6001 -77.8911 39.5961 -77.9031 39.6181 -77.9381 39.6141 -77.9471 39.5911 -77.9351 39.5851 -77.9451 39.6111 -77.9641 39.5981 -77.9951 39.6221 -78.0261 39.6751 -78.0941 39.6941 -78.1831 39.6751 -78.2041 39.6731 -78.2271 39.6581 -78.2291 39.6411 -78.2571 39.6181 -78.2731 39.6401 -78.3481 39.6321 -78.3571 39.6311 -78.3771 39.6141 -78.3841 39.6201 -78.4321 39.5871 -78.4041 39.5921 -78.4501 39.5801 -78.4621 39.5491 -78.4211 39.5481 -78.4461 39.5331 -78.4561 39.5191 -78.4811 39.5251 -78.5091 39.5201 -78.5641 39.5351 -78.6041 39.5291 -78.6371 39.5371 -78.6491 39.5361 -78.6661 39.5591 -78.7161 39.5761 -78.7321 39.5811 -78.7611 39.6011 -78.7741 39.6081 -78.7361 39.6211 -78.7301 39.6261 -78.7321 39.6261 -78.7671 39.6441 -78.7721 39.6301 -78.7981 39.6151 -78.7981 39.5851 -78.8221 39.5661 -78.8061 39.5631 -78.8381 39.5251 -78.8711 39.4601 -78.9551 39.4381 -78.9701 39.4831 -79.0491 39.4851 -79.0641 39.4701 -79.0701 39.4701 -79.1041 39.4641 -79.0961 39.4471 -79.1041 39.4161 -79.1311 39.4131 -79.1581 39.3931 -79.1631 39.3481 -79.2601 39.3251 -79.2801 39.3001 -79.2951 39.2911 -79.3461 39.2691 -79.3851 39.2111 -79.4491 39.2131 -79.4611 39.1971 -79.4901 39.7201 -79.4811 39.7191 -79.3961 39.7221 -78.9301 39.7231 -78.8181 39.7231 -78.3851 39.7241 -78.3341 39.7251 -78.0961 39.7191 -77.4761 39.7191 -77.4641 39.7201 -77.2211 39.7201 -76.9971 39.7211 -76.7901 39.7201 -76.5701 39.7211 -76.2331 39.7221 -76.1391 39.7231 -75.7911 39.3831 -75.7721 39.2951 -75.7641 39.2471 -75.7611 39.1411 -75.7521 38.8301 -75.7241 38.6491 -75.7111</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>38.9071 -76.2931 38.9671 -76.2941 38.9561 -76.3391 38.9411 -76.3141 38.9121 -76.3221 38.9241 -76.3421 38.8751 -76.3291 38.8541 -76.3751 38.9581 -76.3561 39.0401 -76.2991 38.9781 -76.2481 38.9231 -76.2461 38.9491 -76.2731 38.9071 -76.2931</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>38.4491 -75.0681 38.4491 -75.0451 38.3221 -75.0871 38.4491 -75.0681</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>38.0271 -75.2701 38.0371 -75.2441 38.0941 -75.2091 38.2041 -75.1641 38.3201 -75.0941 38.1241 -75.1731 38.0281 -75.2421 38.0271 -75.2701</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Maryland</topp:STATE_NAME><topp:STATE_FIPS>24</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>MD</topp:STATE_ABBR><topp:LAND_KM>25316.345</topp:LAND_KM><topp:WATER_KM>6188.794</topp:WATER_KM><topp:PERSONS>4781468.0</topp:PERSONS><topp:FAMILIES>1245814.0</topp:FAMILIES><topp:HOUSHOLD>1748991.0</topp:HOUSHOLD><topp:MALE>2318671.0</topp:MALE><topp:FEMALE>2462797.0</topp:FEMALE><topp:WORKERS>1783061.0</topp:WORKERS><topp:DRVALONE>1732837.0</topp:DRVALONE><topp:CARPOOL>376449.0</topp:CARPOOL><topp:PUBTRANS>202169.0</topp:PUBTRANS><topp:EMPLOYED>2481342.0</topp:EMPLOYED><topp:UNEMPLOY>111536.0</topp:UNEMPLOY><topp:SERVICE>586994.0</topp:SERVICE><topp:MANUAL>260308.0</topp:MANUAL><topp:P_MALE>0.485</topp:P_MALE><topp:P_FEMALE>0.515</topp:P_FEMALE><topp:SAMP_POP>684773.0</topp:SAMP_POP></topp:states><topp:states fid="states.6"><topp:the_geom><gml:MultiSurface srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>37.6411 -102.0431 37.3861 -102.0411 36.9881 -102.0361 36.9981 -102.9971 36.9991 -103.0771 36.9941 -103.9931 36.9931 -105.1451 36.9921 -105.2121 36.9941 -105.7121 36.9921 -105.9911 36.9911 -106.4711 36.9891 -106.8601 36.9991 -106.8891 36.9971 -107.4101 36.9981 -107.4711 36.9991 -108.3711 36.9961 -109.0471 37.6301 -109.0441 37.8871 -109.0421 38.1521 -109.0421 38.2441 -109.0551 38.4941 -109.0531 39.3601 -109.0501 39.5181 -109.0521 39.6571 -109.0511 40.2101 -109.0501 40.6651 -109.0451 40.9981 -109.0471 41.0031 -107.9181 41.0001 -107.3031 40.9981 -106.8641 41.0011 -106.3281 41.0001 -106.2021 40.9961 -105.2781 40.9941 -104.9331 41.0031 -104.0511 40.9991 -103.5711 41.0001 -103.3821 40.9981 -102.6511 41.0001 -102.6201 40.9981 -102.0471 40.7431 -102.0461 40.6971 -102.0451 40.4311 -102.0471 40.3421 -102.0471 39.9981 -102.0511 39.5681 -102.0481 39.5621 -102.0481 39.1261 -102.0471 39.0361 -102.0481 38.6921 -102.0471 38.6151 -102.0471 38.2631 -102.0451 38.2531 -102.0451 37.7341 -102.0431 37.6411 -102.0431</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Colorado</topp:STATE_NAME><topp:STATE_FIPS>08</topp:STATE_FIPS><topp:SUB_REGION>Mtn</topp:SUB_REGION><topp:STATE_ABBR>CO</topp:STATE_ABBR><topp:LAND_KM>268659.501</topp:LAND_KM><topp:WATER_KM>960.364</topp:WATER_KM><topp:PERSONS>3294394.0</topp:PERSONS><topp:FAMILIES>854214.0</topp:FAMILIES><topp:HOUSHOLD>1282489.0</topp:HOUSHOLD><topp:MALE>1631295.0</topp:MALE><topp:FEMALE>1663099.0</topp:FEMALE><topp:WORKERS>1233023.0</topp:WORKERS><topp:DRVALONE>1216639.0</topp:DRVALONE><topp:CARPOOL>210274.0</topp:CARPOOL><topp:PUBTRANS>46983.0</topp:PUBTRANS><topp:EMPLOYED>1633281.0</topp:EMPLOYED><topp:UNEMPLOY>99438.0</topp:UNEMPLOY><topp:SERVICE>421079.0</topp:SERVICE><topp:MANUAL>181760.0</topp:MANUAL><topp:P_MALE>0.495</topp:P_MALE><topp:P_FEMALE>0.505</topp:P_FEMALE><topp:SAMP_POP>512677.0</topp:SAMP_POP></topp:states><topp:states fid="states.7"><topp:the_geom><gml:MultiSurface srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>36.6551 -86.5101 36.6521 -86.7701 36.6501 -87.0681 36.6511 -87.1121 36.6491 -87.3461 36.6451 -87.6401 36.6441 -87.6931 36.6411 -87.8531 36.6691 -87.8701 36.6791 -88.0711 36.5821 -88.0411 36.5381 -88.0351 36.4961 -88.0421 36.4981 -88.4951 36.4991 -88.5121 36.4981 -88.8101 36.4991 -88.8261 36.4991 -88.8301 36.5021 -89.3461 36.5021 -89.4141 36.5101 -89.4181 36.6161 -89.3731 36.6251 -89.3631 36.6281 -89.3421 36.6221 -89.3221 36.5751 -89.2831 36.5691 -89.2411 36.5811 -89.2101 36.6311 -89.2001 36.6531 -89.1771 36.6711 -89.1671 36.7131 -89.1971 36.7271 -89.1961 36.7601 -89.1771 36.7591 -89.1511 36.7681 -89.1251 36.7921 -89.1251 36.8041 -89.1641 36.8291 -89.1731 36.8431 -89.1661 36.8661 -89.1291 36.9531 -89.1041 36.9771 -89.1071 36.9881 -89.1291 36.9981 -89.1501 37.0251 -89.1741 37.0641 -89.1691 37.0931 -89.1461 37.1121 -89.1161 37.1851 -89.0651 37.2201 -88.9931 37.2181 -88.9321 37.2021 -88.8631 37.1521 -88.7461 37.1411 -88.7391 37.1351 -88.6881 37.1091 -88.6141 37.0721 -88.5591 37.0641 -88.5171 37.0681 -88.4901 37.0721 -88.4761 37.0981 -88.4501 37.1561 -88.4221 37.2051 -88.4501 37.2571 -88.5011 37.2961 -88.5111 37.4001 -88.4671 37.4201 -88.4191 37.4091 -88.3591 37.4421 -88.3111 37.4761 -88.0871 37.5101 -88.0711 37.5831 -88.1341 37.6281 -88.1571 37.6601 -88.1591 37.7001 -88.1331 37.7351 -88.0721 37.8051 -88.0351 37.8011 -88.0111 37.7761 -87.9581 37.7991 -87.9391 37.8091 -87.9201 37.8381 -87.9101 37.8751 -87.9361 37.9041 -87.9341 37.9191 -87.9211 37.9241 -87.8991 37.8901 -87.8571 37.8781 -87.8231 37.8981 -87.7531 37.8941 -87.7281 37.8991 -87.7091 37.8971 -87.6791 37.8361 -87.6841 37.8281 -87.6511 37.8431 -87.6071 37.8641 -87.5931 37.8901 -87.5941 37.9231 -87.6271 37.9711 -87.6041 37.9151 -87.5041 37.9361 -87.4521 37.9341 -87.3871 37.8931 -87.3101 37.8701 -87.2721 37.8491 -87.2261 37.8381 -87.1751 37.8261 -87.1581 37.7891 -87.1311 37.7841 -87.1061 37.8071 -87.0711 37.9071 -87.0361 37.9241 -87.0131 37.9301 -86.9891 37.9371 -86.9311 37.9531 -86.9001 37.9861 -86.8631 37.9911 -86.8261 37.9781 -86.8021 37.8981 -86.7531 37.8941 -86.7281 37.9111 -86.6891 37.9131 -86.6681 37.9021 -86.6601 37.8601 -86.6701 37.8471 -86.6651 37.8451 -86.6451 37.8571 -86.6141 37.9211 -86.5981 37.9251 -86.5811 37.9211 -86.5411 37.9271 -86.5221 37.9421 -86.5161 37.9871 -86.5301 38.0181 -86.5271 38.0461 -86.5191 38.0511 -86.5031 38.0591 -86.4581 38.0751 -86.4421 38.0881 -86.4421 38.1111 -86.4741 38.1291 -86.4641 38.1291 -86.4521 38.1081 -86.4071 38.1231 -86.3931 38.1341 -86.3441 38.1431 -86.3351 38.1551 -86.3431 38.1671 -86.3871 38.1941 -86.3881 38.1931 -86.3641 38.1771 -86.3411 38.1501 -86.2971 38.0781 -86.2911 38.0581 -86.2771 38.0401 -86.2521 38.0171 -86.1901 38.0111 -86.1051 37.9661 -86.0521 37.9921 -86.0311 38.0011 -86.0061 38.0111 -85.9581 38.0331 -85.9301 38.0641 -85.9141 38.1791 -85.9121 38.2381 -85.8521 38.2761 -85.8391 38.2861 -85.8061 38.2821 -85.7861 38.2701 -85.7461 38.3001 -85.6811 38.3371 -85.6541 38.3831 -85.6431 38.4461 -85.6121 38.4711 -85.5071 38.5181 -85.4661 38.5361 -85.4321 38.5611 -85.4171 38.5841 -85.4241 38.6941 -85.4531 38.7241 -85.4461 38.7381 -85.4181 38.7361 -85.3351 38.7441 -85.2711 38.6951 -85.2051 38.6951 -85.1601 38.7141 -85.1191 38.7501 -85.0681 38.7641 -85.0251 38.7801 -84.9751 38.7931 -84.8181 38.8341 -84.8241 38.8661 -84.7871 38.8841 -84.7881 38.8971 -84.8031 38.9011 -84.8591 38.9091 -84.8751 38.9271 -84.8751 38.9541 -84.8461 38.9821 -84.8341 39.0051 -84.8441 39.0321 -84.8761 39.0501 -84.8901 39.0641 -84.8861 39.1031 -84.8271 39.1021 -84.8111 39.1061 -84.7891 39.1421 -84.7421 39.0891 -84.6671 39.0741 -84.6221 39.0701 -84.5931 39.0941 -84.5151 39.1071 -84.4921 39.1111 -84.4441 39.0841 -84.4251 39.0471 -84.4191 39.0351 -84.3911 39.0371 -84.3451 39.0141 -84.3131 38.9441 -84.2901 38.9171 -84.2611 38.8741 -84.2351 38.8121 -84.2281 38.7881 -84.1761 38.7651 -84.0881 38.7631 -84.0531 38.7771 -83.9621 38.7571 -83.9121 38.7441 -83.8571 38.7111 -83.8371 38.6931 -83.7901 38.6501 -83.7701 38.6351 -83.7121 38.6201 -83.6781 38.6231 -83.6551 38.6351 -83.6431 38.6641 -83.6331 38.6771 -83.6181 38.6961 -83.5261 38.6901 -83.5001 38.6631 -83.4531 38.6541 -83.3711 38.6311 -83.3301 38.6061 -83.3201 38.5961 -83.3061 38.5961 -83.2901 38.6091 -83.2721 38.6241 -83.2451 38.6091 -83.1821 38.6191 -83.1431 38.6641 -83.1111 38.6851 -83.0601 38.7141 -83.0271 38.7191 -82.9721 38.7461 -82.9211 38.7421 -82.8901 38.7181 -82.8731 38.6831 -82.8801 38.6521 -82.8601 38.6001 -82.8531 38.5711 -82.8271 38.5571 -82.8021 38.5521 -82.7421 38.5391 -82.6951 38.5021 -82.6691 38.4721 -82.6131 38.4121 -82.5861 38.3681 -82.5981 38.3071 -82.5721 38.2921 -82.5801 38.2551 -82.5741 38.2451 -82.5891 38.2381 -82.6161 38.1931 -82.6061 38.1781 -82.6131 38.1691 -82.6471 38.1461 -82.6461 38.1091 -82.5931 38.0151 -82.5241 37.9751 -82.4751 37.9541 -82.4801 37.9421 -82.4931 37.9221 -82.5001 37.8941 -82.4371 37.8721 -82.4211 37.8111 -82.4051 37.7841 -82.3391 37.7581 -82.3191 37.7441 -82.3291 37.6681 -82.2951 37.6561 -82.2381 37.6231 -82.2051 37.6401 -82.1851 37.5931 -82.1591 37.5901 -82.1311 37.5691 -82.1371 37.5651 -82.1461 37.5571 -82.1421 37.5481 -82.0841 37.5251 -82.0551 37.5511 -82.0491 37.5301 -82.0261 37.5431 -81.9761 37.5311 -81.9591 37.3041 -82.2891 37.2601 -82.3531 37.2501 -82.4061 37.1991 -82.5501 37.1931 -82.5681 37.1091 -82.7191 37.0931 -82.7211 37.0751 -82.7091 37.0651 -82.7201 37.0331 -82.7231 37.0051 -82.8121 36.9741 -82.8661 36.9321 -82.8601 36.8931 -82.8781 36.8631 -82.9501 36.8581 -83.0461 36.8501 -83.0681 36.7791 -83.1281 36.7511 -83.1241 36.7391 -83.1381 36.7341 -83.2031 36.7091 -83.3211 36.6881 -83.3851 36.6721 -83.4041 36.6611 -83.4601 36.6611 -83.5301 36.6161 -83.6461 36.5981 -83.6751 36.5841 -83.6951 36.5911 -83.9351 36.5921 -84.0061 36.5951 -84.2541 36.5951 -84.2561 36.6051 -84.7811 36.6051 -84.7911 36.6201 -84.9981 36.6251 -85.2721 36.6261 -85.3001 36.6181 -85.4371 36.6261 -85.7851 36.6331 -85.9801 36.6431 -86.1991 36.6501 -86.4151 36.6551 -86.5101</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>36.4981 -89.5331 36.5181 -89.5661 36.5411 -89.5681 36.5571 -89.5561 36.5641 -89.5301 36.5591 -89.4931 36.5471 -89.4811 36.5251 -89.4711 36.5041 -89.4811 36.4981 -89.4751 36.4981 -89.5331</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Kentucky</topp:STATE_NAME><topp:STATE_FIPS>21</topp:STATE_FIPS><topp:SUB_REGION>E S Cen</topp:SUB_REGION><topp:STATE_ABBR>KY</topp:STATE_ABBR><topp:LAND_KM>103961.904</topp:LAND_KM><topp:WATER_KM>1772.542</topp:WATER_KM><topp:PERSONS>4551524.0</topp:PERSONS><topp:FAMILIES>1237346.0</topp:FAMILIES><topp:HOUSHOLD>1718663.0</topp:HOUSHOLD><topp:MALE>2195130.0</topp:MALE><topp:FEMALE>2356394.0</topp:FEMALE><topp:WORKERS>1656590.0</topp:WORKERS><topp:DRVALONE>1502949.0</topp:DRVALONE><topp:CARPOOL>273091.0</topp:CARPOOL><topp:PUBTRANS>48158.0</topp:PUBTRANS><topp:EMPLOYED>1970934.0</topp:EMPLOYED><topp:UNEMPLOY>148125.0</topp:UNEMPLOY><topp:SERVICE>556744.0</topp:SERVICE><topp:MANUAL>361621.0</topp:MANUAL><topp:P_MALE>0.482</topp:P_MALE><topp:P_FEMALE>0.518</topp:P_FEMALE><topp:SAMP_POP>646517.0</topp:SAMP_POP></topp:states><topp:states fid="states.8"><topp:the_geom><gml:MultiSurface srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>37.0011 -95.0711 37.0001 -95.4061 37.0001 -95.5251 36.9981 -95.7851 37.0001 -95.9571 36.9981 -96.0051 37.0001 -96.5181 37.0001 -96.7481 36.9991 -97.1371 36.9961 -97.4651 36.9981 -97.8031 36.9981 -98.1041 36.9991 -98.3461 36.9981 -98.5391 36.9981 -98.9991 36.9941 -99.4371 36.9951 -99.5441 36.9951 -99.9981 36.9971 -100.0881 36.9971 -100.6331 36.9961 -100.9501 36.9971 -101.0711 36.9961 -101.5531 36.9881 -102.0241 36.9881 -102.0361 37.3861 -102.0411 37.6411 -102.0431 37.7341 -102.0431 38.2531 -102.0451 38.2631 -102.0451 38.6151 -102.0471 38.6921 -102.0471 39.0361 -102.0481 39.1261 -102.0471 39.5621 -102.0481 39.5681 -102.0481 39.9981 -102.0511 40.0011 -101.4061 40.0011 -101.3211 40.0001 -100.7541 39.9991 -100.7341 40.0001 -100.1901 40.0001 -100.1801 40.0021 -99.6271 39.9991 -99.1771 39.9981 -99.0641 39.9981 -98.7201 39.9971 -98.5041 39.9981 -98.2631 39.9981 -97.9291 39.9991 -97.8161 39.9971 -97.3611 39.9961 -96.9071 39.9941 -96.8011 39.9941 -96.4531 39.9941 -96.2401 39.9951 -96.0001 39.9931 -95.7801 39.9921 -95.3291 39.9991 -95.3081 39.9421 -95.2401 39.9381 -95.2071 39.9101 -95.1931 39.9081 -95.1501 39.8691 -95.1001 39.8661 -95.0621 39.8771 -95.0331 39.8961 -95.0211 39.9001 -94.9641 39.8961 -94.9371 39.8491 -94.9361 39.8331 -94.9231 39.8281 -94.8981 39.8171 -94.8881 39.7931 -94.8991 39.7821 -94.9331 39.7751 -94.9341 39.7571 -94.9211 39.7601 -94.8761 39.7541 -94.8701 39.7391 -94.8771 39.7261 -94.9051 39.7271 -94.9301 39.7361 -94.9521 39.7321 -94.9611 39.6841 -94.9781 39.6611 -95.0281 39.6251 -95.0551 39.5861 -95.0531 39.5601 -95.1081 39.5321 -95.1011 39.4851 -95.0471 39.4621 -95.0401 39.4391 -94.9851 39.4111 -94.9581 39.3811 -94.9251 39.3801 -94.8981 39.3401 -94.9111 39.3231 -94.9071 39.2861 -94.8801 39.2611 -94.8331 39.2111 -94.8201 39.1961 -94.7891 39.1711 -94.7301 39.1741 -94.6751 39.1581 -94.6461 39.1511 -94.6121 39.1411 -94.6001 39.1121 -94.6071 39.0441 -94.6091 38.8371 -94.6121 38.7371 -94.6121 38.4711 -94.6181 38.3921 -94.6181 38.0551 -94.6171 38.0301 -94.6161 37.6791 -94.6191 37.6501 -94.6181 37.3601 -94.6181 37.3271 -94.6181 37.0601 -94.6201 36.9961 -94.6201 37.0001 -95.0321 37.0011 -95.0711</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Kansas</topp:STATE_NAME><topp:STATE_FIPS>20</topp:STATE_FIPS><topp:SUB_REGION>W N Cen</topp:SUB_REGION><topp:STATE_ABBR>KS</topp:STATE_ABBR><topp:LAND_KM>211921.641</topp:LAND_KM><topp:WATER_KM>1188.865</topp:WATER_KM><topp:PERSONS>2477574.0</topp:PERSONS><topp:FAMILIES>658600.0</topp:FAMILIES><topp:HOUSHOLD>944726.0</topp:HOUSHOLD><topp:MALE>1214645.0</topp:MALE><topp:FEMALE>1262929.0</topp:FEMALE><topp:WORKERS>907383.0</topp:WORKERS><topp:DRVALONE>928575.0</topp:DRVALONE><topp:CARPOOL>135598.0</topp:CARPOOL><topp:PUBTRANS>7585.0</topp:PUBTRANS><topp:EMPLOYED>1172214.0</topp:EMPLOYED><topp:UNEMPLOY>57772.0</topp:UNEMPLOY><topp:SERVICE>346339.0</topp:SERVICE><topp:MANUAL>166429.0</topp:MANUAL><topp:P_MALE>0.49</topp:P_MALE><topp:P_FEMALE>0.51</topp:P_FEMALE><topp:SAMP_POP>453411.0</topp:SAMP_POP></topp:states><topp:states fid="states.9"><topp:the_geom><gml:MultiSurface srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>36.5461 -79.1441 36.5491 -79.2171 36.5471 -79.5101 36.5471 -79.7171 36.5451 -80.0241 36.5471 -80.0481 36.5511 -80.4351 36.5571 -80.6111 36.5631 -80.8381 36.5651 -80.9031 36.5721 -81.3451 36.5891 -81.6691 36.6071 -81.6521 36.6111 -81.8291 36.6131 -81.9181 36.5951 -81.9291 36.5951 -82.1541 36.5931 -82.2161 36.5911 -82.2961 36.5911 -82.6101 36.5901 -82.8491 36.5911 -82.9861 36.5881 -83.2111 36.5891 -83.2481 36.6001 -83.2751 36.5981 -83.4641 36.5981 -83.6751 36.6161 -83.6461 36.6611 -83.5301 36.6611 -83.4601 36.6721 -83.4041 36.6881 -83.3851 36.7091 -83.3211 36.7341 -83.2031 36.7391 -83.1381 36.7511 -83.1241 36.7791 -83.1281 36.8501 -83.0681 36.8581 -83.0461 36.8631 -82.9501 36.8931 -82.8781 36.9321 -82.8601 36.9741 -82.8661 37.0051 -82.8121 37.0331 -82.7231 37.0651 -82.7201 37.0751 -82.7091 37.0931 -82.7211 37.1091 -82.7191 37.1931 -82.5681 37.1991 -82.5501 37.2501 -82.4061 37.2601 -82.3531 37.3041 -82.2891 37.5311 -81.9591 37.5061 -81.9351 37.4921 -81.9481 37.4821 -81.9761 37.4661 -81.9881 37.4151 -81.9201 37.3711 -81.9261 37.3401 -81.8971 37.3251 -81.8631 37.3061 -81.8581 37.2851 -81.8391 37.2791 -81.8151 37.2871 -81.7921 37.2721 -81.7521 37.2501 -81.7381 37.2351 -81.7011 37.2041 -81.6661 37.2061 -81.5561 37.2341 -81.5051 37.2521 -81.4951 37.2541 -81.4751 37.2821 -81.4031 37.3111 -81.3911 37.3381 -81.3581 37.2931 -81.3121 37.2401 -81.2231 37.2741 -81.1401 37.2851 -81.0251 37.3061 -80.9861 37.2961 -80.9781 37.2911 -80.9681 37.3011 -80.9341 37.3391 -80.8551 37.3501 -80.8481 37.3881 -80.8771 37.4231 -80.8501 37.4121 -80.7991 37.3911 -80.7991 37.3861 -80.7701 37.3711 -80.7631 37.3781 -80.7471 37.3871 -80.7461 37.3921 -80.7291 37.3881 -80.7051 37.4451 -80.5971 37.4691 -80.5421 37.4741 -80.5081 37.4601 -80.4881 37.4331 -80.4871 37.4221 -80.4741 37.4341 -80.4251 37.4651 -80.3881 37.4751 -80.3521 37.4911 -80.3471 37.5111 -80.2881 37.5361 -80.2801 37.5281 -80.3081 37.5331 -80.3261 37.5661 -80.3161 37.5961 -80.2461 37.6241 -80.2191 37.6401 -80.2541 37.6401 -80.3011 37.6521 -80.3051 37.6711 -80.2951 37.6821 -80.3031 37.7251 -80.2501 37.7571 -80.2541 37.7781 -80.2201 37.8021 -80.2231 37.8421 -80.1711 37.8601 -80.1721 37.8771 -80.1601 37.8911 -80.1181 37.9141 -80.1061 37.9551 -80.0551 37.9891 -80.0001 38.0381 -79.9661 38.0671 -79.9571 38.1031 -79.9281 38.1211 -79.9351 38.1621 -79.9101 38.1791 -79.9161 38.2501 -79.8311 38.2681 -79.7931 38.2841 -79.7861 38.2981 -79.8031 38.3141 -79.8001 38.3531 -79.7641 38.3511 -79.7331 38.3941 -79.7201 38.4301 -79.6841 38.5001 -79.6921 38.5201 -79.6651 38.5501 -79.6691 38.5921 -79.6421 38.5531 -79.5361 38.4621 -79.4861 38.4121 -79.3171 38.4371 -79.2721 38.4801 -79.2311 38.6581 -79.1271 38.6631 -79.1211 38.6591 -79.0881 38.7071 -79.0871 38.7611 -79.0561 38.7901 -79.0551 38.7991 -79.0331 38.8461 -78.9871 38.7631 -78.8661 38.8331 -78.8161 38.8801 -78.7931 38.9111 -78.7491 38.9291 -78.7371 38.9301 -78.7241 38.9041 -78.7191 38.9211 -78.6801 38.9501 -78.6471 38.9791 -78.6311 38.9671 -78.5981 39.0131 -78.5531 39.0231 -78.5491 39.0351 -78.5641 39.0571 -78.5361 39.0931 -78.5011 39.1111 -78.4851 39.1181 -78.4481 39.1481 -78.4301 39.1701 -78.4021 39.1971 -78.4241 39.2121 -78.4231 39.2441 -78.3991 39.2571 -78.4131 39.3411 -78.3411 39.3501 -78.3441 39.3611 -78.3651 39.3801 -78.3501 39.4561 -78.3471 39.4231 -78.2771 39.3911 -78.2291 39.2651 -78.0331 39.1321 -77.8301 39.1411 -77.8201 39.1961 -77.8051 39.2461 -77.7681 39.2841 -77.7591 39.3171 -77.7271 39.3181 -77.6791 39.2991 -77.6161 39.2981 -77.5681 39.2681 -77.5421 39.2491 -77.4941 39.2291 -77.4641 39.2181 -77.4611 39.1761 -77.4781 39.1571 -77.5161 39.1161 -77.5131 39.1031 -77.4791 39.0801 -77.4591 39.0661 -77.4331 39.0681 -77.3461 39.0621 -77.3241 39.0271 -77.2551 38.9751 -77.2431 38.9641 -77.1521 38.9321 -77.1221 38.9151 -77.0781 38.8861 -77.0671 38.8621 -77.0391 38.8381 -77.0401 38.8291 -77.0451 38.8131 -77.0351 38.7881 -77.0451 38.7181 -77.0461 38.7121 -77.0571 38.7151 -77.0811 38.7031 -77.0931 38.6771 -77.1251 38.6481 -77.1291 38.6221 -77.1971 38.6601 -77.1941 38.6501 -77.2271 38.5011 -77.3031 38.4361 -77.3381 38.3621 -77.2891 38.3431 -77.3211 38.3311 -77.2401 38.3751 -77.0541 38.2801 -76.9991 38.2021 -76.9361 38.1201 -76.5951 38.0741 -76.5491 38.0251 -76.5581 38.0031 -76.5731 38.0121 -76.5241 37.9561 -76.3671 37.8901 -76.2591 37.8501 -76.2511 37.7981 -76.3241 37.7191 -76.3091 37.7001 -76.3571 37.6771 -76.3231 37.6221 -76.3441 37.6561 -76.5071 37.7701 -76.5801 37.7961 -76.6311 37.9161 -76.7711 37.9191 -76.8181 37.7981 -76.7321 37.7741 -76.6811 37.6411 -76.5691 37.5511 -76.3141 37.5251 -76.3481 37.5521 -76.5121 37.5151 -76.4341 37.5151 -76.3551 37.3901 -76.2541 37.3301 -76.2751 37.3341 -76.3001 37.3931 -76.3391 37.4571 -76.4461 37.4181 -76.4631 37.4121 -76.4171 37.3731 -76.4031 37.3771 -76.4551 37.2931 -76.3921 37.2551 -76.4611 37.4121 -76.6531 37.4181 -76.7041 37.3711 -76.6691 37.2911 -76.5951 37.2071 -76.4241 37.1521 -76.4121 37.1731 -76.3961 37.1461 -76.3631 37.1771 -76.3371 37.1221 -76.2851 37.1071 -76.3951 37.0741 -76.2781 37.0201 -76.2931 36.9901 -76.3841 36.9651 -76.4261 37.0671 -76.5311 37.0881 -76.5151 37.1171 -76.5641 37.0801 -76.5681 37.1321 -76.6241 37.1781 -76.6101 37.2251 -76.6481 37.2321 -76.6971 37.1931 -76.7461 37.2401 -76.7951 37.2431 -76.8571 37.3221 -76.8751 37.2591 -76.8781 37.2361 -76.9411 37.2011 -76.9001 37.2071 -76.7971 37.1501 -76.7291 37.1971 -76.6851 37.1471 -76.6711 37.0541 -76.6651 37.0241 -76.5771 36.9941 -76.6131 37.0061 -76.5551 36.9611 -76.4891 36.9121 -76.5171 36.9191 -76.4821 36.8951 -76.4861 36.8411 -76.5601 36.7951 -76.5611 36.8691 -76.5071 36.9011 -76.4101 36.9131 -76.3481 36.8601 -76.3411 36.8351 -76.3941 36.8261 -76.4011 36.8451 -76.3171 36.8281 -76.2921 36.9421 -76.3071 36.9621 -76.2841 36.9351 -76.2021 36.9041 -76.1911 36.9311 -76.1181 36.9231 -75.9951 36.5551 -75.8781 36.5561 -75.9011 36.5991 -75.8921 36.7211 -75.9501 36.5561 -75.9981 36.5561 -76.0271 36.6031 -76.0611 36.5561 -76.0451 36.5571 -76.1271 36.5561 -76.3301 36.5551 -76.4971 36.5551 -76.5631 36.5541 -76.9211 36.5541 -76.9241 36.5561 -77.1771 36.5531 -77.3201 36.5531 -77.7631 36.5521 -77.8981 36.5521 -78.0511 36.5451 -78.3211 36.5411 -78.4581 36.5461 -78.7371 36.5431 -78.7961 36.5461 -79.1441</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>38.0271 -75.2701 38.0281 -75.2421 37.9621 -75.2981 37.8881 -75.3391 37.8751 -75.3861 37.9011 -75.3441 37.9001 -75.3781 37.9181 -75.3461 38.0271 -75.2701</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>37.5521 -75.8671 37.5611 -75.9411 37.5851 -75.9291 37.5801 -75.8871 37.5921 -75.9051 37.7111 -75.7991 37.7891 -75.7821 37.8241 -75.6961 37.8581 -75.6861 37.9301 -75.7331 37.9411 -75.6581 37.9701 -75.6481 37.9961 -75.6261 38.0161 -75.3721 37.6971 -75.6171 37.6771 -75.5891 37.5891 -75.6991 37.5591 -75.6501 37.5581 -75.7271 37.5101 -75.7561 37.4931 -75.7051 37.4691 -75.8131 37.4261 -75.8201 37.4081 -75.7901 37.4181 -75.8261 37.3671 -75.8971 37.1421 -75.9311 37.1261 -75.9701 37.3081 -76.0181 37.4841 -75.9341 37.4791 -75.9651 37.5211 -75.9541 37.5561 -75.9301 37.5521 -75.8671</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Virginia</topp:STATE_NAME><topp:STATE_FIPS>51</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>VA</topp:STATE_ABBR><topp:LAND_KM>102537.328</topp:LAND_KM><topp:WATER_KM>4263.82</topp:WATER_KM><topp:PERSONS>6180651.0</topp:PERSONS><topp:FAMILIES>1627615.0</topp:FAMILIES><topp:HOUSHOLD>2289067.0</topp:HOUSHOLD><topp:MALE>3030948.0</topp:MALE><topp:FEMALE>3149703.0</topp:FEMALE><topp:WORKERS>2343200.0</topp:WORKERS><topp:DRVALONE>2278600.0</topp:DRVALONE><topp:CARPOOL>499251.0</topp:CARPOOL><topp:PUBTRANS>125792.0</topp:PUBTRANS><topp:EMPLOYED>3025109.0</topp:EMPLOYED><topp:UNEMPLOY>141926.0</topp:UNEMPLOY><topp:SERVICE>777181.0</topp:SERVICE><topp:MANUAL>420070.0</topp:MANUAL><topp:P_MALE>0.49</topp:P_MALE><topp:P_FEMALE>0.51</topp:P_FEMALE><topp:SAMP_POP>898089.0</topp:SAMP_POP></topp:states><topp:states fid="states.10"><topp:the_geom><gml:MultiSurface srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>36.9531 -89.1041 36.8661 -89.1291 36.8431 -89.1661 36.8291 -89.1731 36.8041 -89.1641 36.7921 -89.1251 36.7681 -89.1251 36.7591 -89.1511 36.7601 -89.1771 36.7271 -89.1961 36.7131 -89.1971 36.6711 -89.1671 36.6531 -89.1771 36.6311 -89.2001 36.5811 -89.2101 36.5691 -89.2411 36.5751 -89.2831 36.6221 -89.3221 36.6281 -89.3421 36.6251 -89.3631 36.6161 -89.3731 36.5101 -89.4181 36.5021 -89.4141 36.4561 -89.4481 36.4451 -89.4701 36.4651 -89.4911 36.4981 -89.4751 36.5041 -89.4811 36.5251 -89.4711 36.5471 -89.4811 36.5591 -89.4931 36.5641 -89.5301 36.5571 -89.5561 36.5411 -89.5681 36.5181 -89.5661 36.4981 -89.5331 36.4711 -89.5161 36.4401 -89.5451 36.4011 -89.5201 36.3551 -89.5191 36.3451 -89.5441 36.3541 -89.6051 36.3341 -89.6221 36.3081 -89.6061 36.2801 -89.5421 36.2641 -89.5351 36.2571 -89.5411 36.2401 -89.6181 36.2541 -89.6701 36.2521 -89.6941 36.2401 -89.6951 36.2201 -89.6761 36.1831 -89.6181 36.1521 -89.5891 36.1291 -89.5891 36.0991 -89.6671 36.0821 -89.6781 36.0251 -89.6881 35.9991 -89.7211 35.9961 -89.9631 35.9911 -90.2831 35.9891 -90.3781 36.0911 -90.3151 36.1151 -90.2841 36.1181 -90.2631 36.1371 -90.2341 36.1611 -90.2321 36.1721 -90.2191 36.1961 -90.1611 36.2121 -90.1311 36.2571 -90.1091 36.2721 -90.0661 36.3001 -90.0491 36.3251 -90.0671 36.3621 -90.0501 36.3821 -90.0521 36.3971 -90.0801 36.4041 -90.1161 36.4221 -90.1231 36.4531 -90.1171 36.4571 -90.1371 36.4911 -90.1501 36.4921 -90.2241 36.4901 -90.5811 36.4891 -90.8041 36.4871 -91.1331 36.4911 -91.4111 36.4901 -91.4521 36.4901 -91.6881 36.4911 -92.1271 36.4911 -92.1461 36.4901 -92.5221 36.4891 -92.7771 36.4891 -92.8521 36.4901 -93.2971 36.4901 -93.3281 36.4891 -93.5961 36.4891 -93.8571 36.4901 -94.0801 36.4891 -94.6171 36.6701 -94.6201 36.7631 -94.6211 36.9961 -94.6201 37.0601 -94.6201 37.3271 -94.6181 37.3601 -94.6181 37.6501 -94.6181 37.6791 -94.6191 38.0301 -94.6161 38.0551 -94.6171 38.3921 -94.6181 38.4711 -94.6181 38.7371 -94.6121 38.8371 -94.6121 39.0441 -94.6091 39.1121 -94.6071 39.1411 -94.6001 39.1511 -94.6121 39.1581 -94.6461 39.1741 -94.6751 39.1711 -94.7301 39.1961 -94.7891 39.2111 -94.8201 39.2611 -94.8331 39.2861 -94.8801 39.3231 -94.9071 39.3401 -94.9111 39.3801 -94.8981 39.3811 -94.9251 39.4111 -94.9581 39.4391 -94.9851 39.4621 -95.0401 39.4851 -95.0471 39.5321 -95.1011 39.5601 -95.1081 39.5861 -95.0531 39.6251 -95.0551 39.6611 -95.0281 39.6841 -94.9781 39.7321 -94.9611 39.7361 -94.9521 39.7271 -94.9301 39.7261 -94.9051 39.7391 -94.8771 39.7541 -94.8701 39.7601 -94.8761 39.7571 -94.9211 39.7751 -94.9341 39.7821 -94.9331 39.7931 -94.8991 39.8171 -94.8881 39.8281 -94.8981 39.8331 -94.9231 39.8491 -94.9361 39.8961 -94.9371 39.9001 -94.9641 39.8961 -95.0211 39.8771 -95.0331 39.8661 -95.0621 39.8691 -95.1001 39.9081 -95.1501 39.9101 -95.1931 39.9381 -95.2071 39.9421 -95.2401 39.9991 -95.3081 40.0241 -95.3441 40.0281 -95.3701 40.0431 -95.3901 40.0481 -95.4131 40.0801 -95.4031 40.0951 -95.3841 40.1151 -95.3921 40.1311 -95.4221 40.1731 -95.4601 40.2131 -95.4661 40.2261 -95.4761 40.2661 -95.5461 40.3091 -95.5951 40.3091 -95.6461 40.3221 -95.6451 40.3311 -95.6171 40.3461 -95.6151 40.3581 -95.6331 40.3961 -95.6361 40.4851 -95.6951 40.5121 -95.6841 40.5301 -95.6571 40.5581 -95.6621 40.5651 -95.6751 40.5611 -95.6871 40.5241 -95.6911 40.5321 -95.7361 40.5491 -95.7631 40.5891 -95.7671 40.5841 -95.3821 40.5811 -95.2171 40.5771 -94.9201 40.5751 -94.6391 40.5741 -94.4841 40.5701 -94.2381 40.5741 -94.0171 40.5781 -93.7861 40.5801 -93.5621 40.5801 -93.3701 40.5841 -93.1001 40.5891 -92.7171 40.5911 -92.6461 40.5991 -92.3611 40.6001 -92.1921 40.6081 -91.9461 40.6091 -91.7411 40.5931 -91.7161 40.5811 -91.6891 40.5511 -91.6911 40.5321 -91.6221 40.5041 -91.6161 40.4841 -91.5851 40.4631 -91.5791 40.4551 -91.5331 40.4411 -91.5381 40.4351 -91.5291 40.4101 -91.5271 40.4051 -91.5001 40.3901 -91.4901 40.3901 -91.4761 40.3711 -91.4481 40.3091 -91.4861 40.2511 -91.4981 40.2001 -91.5061 40.1341 -91.5161 40.0661 -91.5041 40.0051 -91.4871 39.9461 -91.4471 39.9211 -91.4301 39.9011 -91.4341 39.8851 -91.4501 39.8631 -91.4491 39.8031 -91.3811 39.7611 -91.3731 39.7241 -91.3671 39.6851 -91.3171 39.6001 -91.2031 39.5521 -91.1561 39.5281 -91.0931 39.4731 -91.0641 39.4441 -91.0361 39.4001 -90.9471 39.3501 -90.8501 39.2961 -90.7791 39.2471 -90.7381 39.2241 -90.7321 39.1951 -90.7181 39.1441 -90.7161 39.0931 -90.6901 39.0581 -90.7071 39.0371 -90.7061 38.9351 -90.6681 38.8801 -90.6271 38.8711 -90.5701 38.8911 -90.5301 38.9591 -90.4691 38.9621 -90.4131 38.9241 -90.3191 38.9241 -90.2781 38.9141 -90.2431 38.8531 -90.1321 38.8301 -90.1131 38.8001 -90.1211 38.7851 -90.1351 38.7731 -90.1631 38.7231 -90.1961 38.7001 -90.2021 38.6581 -90.1831 38.6101 -90.1831 38.5621 -90.2401 38.5321 -90.2611 38.5181 -90.2651 38.4271 -90.3011 38.3901 -90.3391 38.3651 -90.3581 38.3231 -90.3691 38.2341 -90.3641 38.1881 -90.3361 38.1661 -90.2891 38.1221 -90.2541 38.0881 -90.2071 38.0531 -90.1341 38.0321 -90.1191 37.9931 -90.0411 37.9691 -90.0101 37.9631 -89.9581 37.9111 -89.9781 37.8781 -89.9371 37.8751 -89.9001 37.8911 -89.8661 37.9051 -89.8611 37.9051 -89.8511 37.8401 -89.7281 37.8041 -89.6911 37.7831 -89.6751 37.7451 -89.6661 37.7061 -89.5811 37.6941 -89.5211 37.6791 -89.5131 37.6501 -89.5191 37.6151 -89.5131 37.5711 -89.5241 37.4911 -89.4941 37.4531 -89.4531 37.4111 -89.4271 37.3551 -89.4351 37.3391 -89.4681 37.3291 -89.5001 37.3041 -89.5131 37.2761 -89.5131 37.2561 -89.4891 37.2531 -89.4651 37.2241 -89.4681 37.1651 -89.4401 37.1371 -89.4231 37.0991 -89.3791 37.0491 -89.3821 37.0091 -89.3101 36.9991 -89.2821 37.0081 -89.2621 37.0271 -89.2641 37.0601 -89.3091 37.0851 -89.3031 37.0911 -89.2841 37.0871 -89.2641 37.0411 -89.2371 37.0281 -89.2101 36.9861 -89.1931 36.9881 -89.1291 36.9771 -89.1071 36.9531 -89.1041</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Missouri</topp:STATE_NAME><topp:STATE_FIPS>29</topp:STATE_FIPS><topp:SUB_REGION>W N Cen</topp:SUB_REGION><topp:STATE_ABBR>MO</topp:STATE_ABBR><topp:LAND_KM>178445.951</topp:LAND_KM><topp:WATER_KM>2100.115</topp:WATER_KM><topp:PERSONS>5117073.0</topp:PERSONS><topp:FAMILIES>1368334.0</topp:FAMILIES><topp:HOUSHOLD>1961206.0</topp:HOUSHOLD><topp:MALE>2464315.0</topp:MALE><topp:FEMALE>2652758.0</topp:FEMALE><topp:WORKERS>1861192.0</topp:WORKERS><topp:DRVALONE>1816079.0</topp:DRVALONE><topp:CARPOOL>312042.0</topp:CARPOOL><topp:PUBTRANS>47129.0</topp:PUBTRANS><topp:EMPLOYED>2367395.0</topp:EMPLOYED><topp:UNEMPLOY>155388.0</topp:UNEMPLOY><topp:SERVICE>659782.0</topp:SERVICE><topp:MANUAL>386746.0</topp:MANUAL><topp:P_MALE>0.482</topp:P_MALE><topp:P_FEMALE>0.518</topp:P_FEMALE><topp:SAMP_POP>864999.0</topp:SAMP_POP></topp:states></gml:featureMembers>
+--></div>
+<div id="v3/topp-states-wfs.xml"><!--
+<?xml version="1.0" encoding="UTF-8"?>
+<wfs:FeatureCollection numberOfFeatures="3" timeStamp="2008-09-12T00:24:21.013-04:00" xsi:schemaLocation="http://www.openplans.org/topp http://sigma.openplans.org:80/geoserver/wfs?service=WFS&amp;version=1.1.0&amp;request=DescribeFeatureType&amp;typeName=topp:states http://www.opengis.net/wfs http://sigma.openplans.org:80/geoserver/schemas/wfs/1.1.0/wfs.xsd" xmlns:ogc="http://www.opengis.net/ogc" xmlns:opengeo="http://open-geo.com" xmlns:tiger="http://www.census.gov" xmlns:wfs="http://www.opengis.net/wfs" xmlns:topp="http://www.openplans.org/topp" xmlns:seb="http://seb.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ows="http://www.opengis.net/ows" xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink"><gml:featureMembers><topp:states gml:id="states.1"><gml:boundedBy><gml:Envelope srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>36.986 -91.516</gml:lowerCorner><gml:upperCorner>42.509 -87.507</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>37.511 -88.071 37.476 -88.087 37.442 -88.311 37.409 -88.359 37.421 -88.419 37.401 -88.467 37.296 -88.511 37.257 -88.501 37.205 -88.451 37.156 -88.422 37.098 -88.451 37.072 -88.476 37.068 -88.491 37.064 -88.517 37.072 -88.559 37.109 -88.614 37.135 -88.688 37.141 -88.739 37.152 -88.746 37.202 -88.863 37.218 -88.932 37.221 -88.993 37.185 -89.065 37.112 -89.116 37.093 -89.146 37.064 -89.169 37.025 -89.174 36.998 -89.151 36.988 -89.129 36.986 -89.193 37.028 -89.211 37.041 -89.237 37.087 -89.264 37.091 -89.284 37.085 -89.303 37.061 -89.309 37.027 -89.264 37.008 -89.262 36.999 -89.282 37.009 -89.311 37.049 -89.382 37.099 -89.379 37.137 -89.423 37.165 -89.441 37.224 -89.468 37.253 -89.465 37.256 -89.489 37.276 -89.513 37.304 -89.513 37.329 -89.501 37.339 -89.468 37.355 -89.435 37.411 -89.427 37.453 -89.453 37.491 -89.494 37.571 -89.524 37.615 -89.513 37.651 -89.519 37.679 -89.513 37.694 -89.521 37.706 -89.581 37.745 -89.666 37.783 -89.675 37.804 -89.691 37.841 -89.728 37.905 -89.851 37.905 -89.861 37.891 -89.866 37.875 -89.901 37.878 -89.937 37.911 -89.978 37.963 -89.958 37.969 -90.011 37.993 -90.041 38.032 -90.119 38.053 -90.134 38.088 -90.207 38.122 -90.254 38.166 -90.289 38.188 -90.336 38.234 -90.364 38.323 -90.369 38.365 -90.358 38.391 -90.339 38.427 -90.301 38.518 -90.265 38.532 -90.261 38.562 -90.241 38.611 -90.183 38.658 -90.183 38.701 -90.202 38.723 -90.196 38.773 -90.163 38.785 -90.135 38.801 -90.121 38.831 -90.113 38.853 -90.132 38.914 -90.243 38.924 -90.278 38.924 -90.319 38.962 -90.413 38.959 -90.469 38.891 -90.531 38.871 -90.571 38.881 -90.627 38.935 -90.668 39.037 -90.706 39.058 -90.707 39.093 -90.691 39.144 -90.716 39.195 -90.718 39.224 -90.732 39.247 -90.738 39.296 -90.779 39.351 -90.851 39.401 -90.947 39.444 -91.036 39.473 -91.064 39.528 -91.093 39.552 -91.156 39.601 -91.203 39.685 -91.317 39.724 -91.367 39.761 -91.373 39.803 -91.381 39.863 -91.449 39.885 -91.451 39.901 -91.434 39.921 -91.431 39.946 -91.447 40.005 -91.487 40.066 -91.504 40.134 -91.516 40.201 -91.506 40.251 -91.498 40.309 -91.486 40.371 -91.448 40.386 -91.418 40.392 -91.385 40.402 -91.372 40.447 -91.385 40.503 -91.374 40.528 -91.382 40.547 -91.412 40.572 -91.411 40.603 -91.375 40.639 -91.262 40.643 -91.214 40.656 -91.162 40.682 -91.129 40.705 -91.119 40.761 -91.092 40.833 -91.088 40.879 -91.049 40.923 -90.983 40.951 -90.961 41.071 -90.954 41.104 -90.957 41.144 -90.991 41.165 -91.018 41.176 -91.056 41.231 -91.101 41.267 -91.102 41.334 -91.073 41.401 -91.055 41.423 -91.027 41.431 -91.001 41.421 -90.949 41.444 -90.844 41.449 -90.779 41.451 -90.708 41.462 -90.658 41.509 -90.601 41.525 -90.541 41.527 -90.454 41.543 -90.434 41.567 -90.423 41.586 -90.348 41.602 -90.339 41.649 -90.341 41.722 -90.326 41.756 -90.304 41.781 -90.255 41.806 -90.195 41.931 -90.154 41.983 -90.142 42.033 -90.151 42.061 -90.168 42.103 -90.166 42.121 -90.176 42.122 -90.191 42.159 -90.231 42.197 -90.323 42.211 -90.367 42.242 -90.407 42.263 -90.417 42.341 -90.427 42.361 -90.441 42.388 -90.491 42.421 -90.563 42.461 -90.605 42.475 -90.648 42.494 -90.651 42.509 -90.638 42.508 -90.419 42.504 -89.923 42.503 -89.834 42.497 -89.401 42.497 -89.359 42.491 -88.939 42.491 -88.764 42.489 -88.706 42.491 -88.297 42.489 -88.194 42.489 -87.797 42.314 -87.836 42.156 -87.761 42.059 -87.671 41.847 -87.612 41.723 -87.529 41.469 -87.532 41.301 -87.532 41.173 -87.531 41.009 -87.532 40.745 -87.532 40.494 -87.537 40.483 -87.535 40.166 -87.535 39.887 -87.535 39.609 -87.535 39.477 -87.538 39.351 -87.541 39.338 -87.597 39.307 -87.625 39.297 -87.611 39.281 -87.615 39.258 -87.606 39.248 -87.584 39.208 -87.588 39.198 -87.594 39.196 -87.607 39.168 -87.644 39.146 -87.671 39.131 -87.659 39.113 -87.662 39.103 -87.631 39.088 -87.631 39.084 -87.612 39.062 -87.585 38.995 -87.581 38.994 -87.591 38.977 -87.547 38.963 -87.533 38.931 -87.531 38.904 -87.539 38.869 -87.559 38.857 -87.551 38.795 -87.507 38.776 -87.519 38.769 -87.508 38.736 -87.508 38.685 -87.543 38.672 -87.588 38.642 -87.625 38.622 -87.628 38.599 -87.619 38.593 -87.641 38.573 -87.652 38.547 -87.672 38.515 -87.651 38.501 -87.653 38.504 -87.679 38.481 -87.692 38.466 -87.756 38.457 -87.758 38.445 -87.738 38.417 -87.748 38.378 -87.784 38.352 -87.834 38.286 -87.851 38.285 -87.863 38.316 -87.874 38.315 -87.883 38.301 -87.888 38.281 -87.914 38.302 -87.913 38.304 -87.925 38.241 -87.981 38.234 -87.986 38.201 -87.977 38.171 -87.932 38.157 -87.931 38.136 -87.951 38.131 -87.973 38.103 -88.018 38.092 -88.012 38.096 -87.964 38.073 -87.975 38.054 -88.034 38.045 -88.043 38.038 -88.041 38.033 -88.021 38.008 -88.029 37.975 -88.021 37.956 -88.042 37.934 -88.041 37.929 -88.064 37.944 -88.078 37.923 -88.084 37.917 -88.031 37.905 -88.026 37.896 -88.044 37.906 -88.101 37.895 -88.101 37.867 -88.075 37.843 -88.034 37.827 -88.042 37.831 -88.089 37.817 -88.086 37.805 -88.035 37.735 -88.072 37.701 -88.133 37.661 -88.159 37.628 -88.157 37.583 -88.134 37.511 -88.071</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Illinois</topp:STATE_NAME><topp:STATE_FIPS>17</topp:STATE_FIPS><topp:SUB_REGION>E N Cen</topp:SUB_REGION><topp:STATE_ABBR>IL</topp:STATE_ABBR><topp:LAND_KM>143986.61</topp:LAND_KM><topp:WATER_KM>1993.335</topp:WATER_KM><topp:PERSONS>1.143E7</topp:PERSONS><topp:FAMILIES>2924880.0</topp:FAMILIES><topp:HOUSHOLD>4202240.0</topp:HOUSHOLD><topp:MALE>5552233.0</topp:MALE><topp:FEMALE>5878369.0</topp:FEMALE><topp:WORKERS>4199206.0</topp:WORKERS><topp:DRVALONE>3741715.0</topp:DRVALONE><topp:CARPOOL>652603.0</topp:CARPOOL><topp:PUBTRANS>538071.0</topp:PUBTRANS><topp:EMPLOYED>5417967.0</topp:EMPLOYED><topp:UNEMPLOY>385040.0</topp:UNEMPLOY><topp:SERVICE>1360159.0</topp:SERVICE><topp:MANUAL>828906.0</topp:MANUAL><topp:P_MALE>0.486</topp:P_MALE><topp:P_FEMALE>0.514</topp:P_FEMALE><topp:SAMP_POP>1747776.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.2"><gml:boundedBy><gml:Envelope srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>38.788 -77.122</gml:lowerCorner><gml:upperCorner>38.993 -76.911</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>38.966 -77.008 38.889 -76.911 38.788 -77.045 38.813 -77.035 38.829 -77.045 38.838 -77.041 38.862 -77.039 38.886 -77.067 38.915 -77.078 38.932 -77.122 38.993 -77.042 38.966 -77.008</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>District of Columbia</topp:STATE_NAME><topp:STATE_FIPS>11</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>DC</topp:STATE_ABBR><topp:LAND_KM>159.055</topp:LAND_KM><topp:WATER_KM>17.991</topp:WATER_KM><topp:PERSONS>606900.0</topp:PERSONS><topp:FAMILIES>122087.0</topp:FAMILIES><topp:HOUSHOLD>249634.0</topp:HOUSHOLD><topp:MALE>282970.0</topp:MALE><topp:FEMALE>323930.0</topp:FEMALE><topp:WORKERS>229975.0</topp:WORKERS><topp:DRVALONE>106694.0</topp:DRVALONE><topp:CARPOOL>36621.0</topp:CARPOOL><topp:PUBTRANS>111422.0</topp:PUBTRANS><topp:EMPLOYED>303994.0</topp:EMPLOYED><topp:UNEMPLOY>23442.0</topp:UNEMPLOY><topp:SERVICE>65498.0</topp:SERVICE><topp:MANUAL>22407.0</topp:MANUAL><topp:P_MALE>0.466</topp:P_MALE><topp:P_FEMALE>0.534</topp:P_FEMALE><topp:SAMP_POP>72696.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.3"><gml:boundedBy><gml:Envelope srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>38.449 -75.791</gml:lowerCorner><gml:upperCorner>39.841 -75.045</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>38.557 -75.707 38.649 -75.711 38.831 -75.724 39.141 -75.752 39.247 -75.761 39.295 -75.764 39.383 -75.772 39.723 -75.791 39.724 -75.775 39.774 -75.745 39.821 -75.695 39.838 -75.644 39.841 -75.583 39.826 -75.471 39.798 -75.421 39.789 -75.412 39.778 -75.428 39.763 -75.461 39.741 -75.475 39.719 -75.476 39.714 -75.489 39.612 -75.611 39.566 -75.562 39.463 -75.591 39.366 -75.515 39.257 -75.402 39.073 -75.397 39.012 -75.324 38.945 -75.307 38.808 -75.191 38.799 -75.083 38.449 -75.045 38.449 -75.068 38.451 -75.093 38.455 -75.351 38.463 -75.699 38.557 -75.707</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Delaware</topp:STATE_NAME><topp:STATE_FIPS>10</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>DE</topp:STATE_ABBR><topp:LAND_KM>5062.456</topp:LAND_KM><topp:WATER_KM>1385.022</topp:WATER_KM><topp:PERSONS>666168.0</topp:PERSONS><topp:FAMILIES>175867.0</topp:FAMILIES><topp:HOUSHOLD>247497.0</topp:HOUSHOLD><topp:MALE>322968.0</topp:MALE><topp:FEMALE>343200.0</topp:FEMALE><topp:WORKERS>247566.0</topp:WORKERS><topp:DRVALONE>258087.0</topp:DRVALONE><topp:CARPOOL>42968.0</topp:CARPOOL><topp:PUBTRANS>8069.0</topp:PUBTRANS><topp:EMPLOYED>335147.0</topp:EMPLOYED><topp:UNEMPLOY>13945.0</topp:UNEMPLOY><topp:SERVICE>87973.0</topp:SERVICE><topp:MANUAL>44140.0</topp:MANUAL><topp:P_MALE>0.485</topp:P_MALE><topp:P_FEMALE>0.515</topp:P_FEMALE><topp:SAMP_POP>102776.0</topp:SAMP_POP></topp:states></gml:featureMembers></wfs:FeatureCollection>
+--></div>
+<div id="v2/point-coord.xml"><!--
+<gml:Point xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+</gml:Point>
+--></div>
+<div id="v2/point-coordinates.xml"><!--
+<gml:Point xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:coordinates decimal="." cs="," ts=" ">1,2</gml:coordinates>
+</gml:Point>
+--></div>
+<div id="v2/linestring-coord.xml"><!--
+<gml:LineString xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>3</gml:X>
+ <gml:Y>4</gml:Y>
+ </gml:coord>
+</gml:LineString>
+--></div>
+<div id="v2/linestring-coordinates.xml"><!--
+<gml:LineString xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:coordinates decimal="." cs="," ts=" ">1,2 3,4</gml:coordinates>
+</gml:LineString>
+--></div>
+<div id="v2/multipoint-coord.xml"><!--
+<gml:MultiPoint xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:pointMember>
+ <gml:Point>
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+ </gml:Point>
+ </gml:pointMember>
+ <gml:pointMember>
+ <gml:Point>
+ <gml:coord>
+ <gml:X>2</gml:X>
+ <gml:Y>3</gml:Y>
+ </gml:coord>
+ </gml:Point>
+ </gml:pointMember>
+ <gml:pointMember>
+ <gml:Point>
+ <gml:coord>
+ <gml:X>3</gml:X>
+ <gml:Y>4</gml:Y>
+ </gml:coord>
+ </gml:Point>
+ </gml:pointMember>
+</gml:MultiPoint>
+--></div>
+<div id="v2/multipoint-coordinates.xml"><!--
+<gml:MultiPoint xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:pointMember>
+ <gml:Point>
+ <gml:coordinates decimal="." cs="," ts=" ">1,2</gml:coordinates>
+ </gml:Point>
+ </gml:pointMember>
+ <gml:pointMember>
+ <gml:Point>
+ <gml:coordinates decimal="." cs="," ts=" ">2,3</gml:coordinates>
+ </gml:Point>
+ </gml:pointMember>
+ <gml:pointMember>
+ <gml:Point>
+ <gml:coordinates decimal="." cs="," ts=" ">3,4</gml:coordinates>
+ </gml:Point>
+ </gml:pointMember>
+</gml:MultiPoint>
+--></div>
+<div id="v2/multilinestring-coord.xml"><!--
+<gml:MultiLineString xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:lineStringMember>
+ <gml:LineString>
+ <gml:coord>
+ <gml:X>1</gml:X>
+ <gml:Y>2</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>2</gml:X>
+ <gml:Y>3</gml:Y>
+ </gml:coord>
+ </gml:LineString>
+ </gml:lineStringMember>
+ <gml:lineStringMember>
+ <gml:LineString>
+ <gml:coord>
+ <gml:X>3</gml:X>
+ <gml:Y>4</gml:Y>
+ </gml:coord>
+ <gml:coord>
+ <gml:X>4</gml:X>
+ <gml:Y>5</gml:Y>
+ </gml:coord>
+ </gml:LineString>
+ </gml:lineStringMember>
+</gml:MultiLineString>
+--></div>
+<div id="v2/multilinestring-coordinates.xml"><!--
+<gml:MultiLineString xmlns:gml="http://www.opengis.net/gml" srsName="foo">
+ <gml:lineStringMember>
+ <gml:LineString>
+ <gml:coordinates decimal="." cs="," ts=" ">1,2 2,3</gml:coordinates>
+ </gml:LineString>
+ </gml:lineStringMember>
+ <gml:lineStringMember>
+ <gml:LineString>
+ <gml:coordinates decimal="." cs="," ts=" ">3,4 4,5</gml:coordinates>
+ </gml:LineString>
+ </gml:lineStringMember>
+</gml:MultiLineString>
+--></div>
+<div id="v3/repeated-name.xml"><!--
+<?xml version="1.0" encoding="UTF-8"?>
+<wfs:FeatureCollection numberOfFeatures="1" timeStamp="2010-01-29T15:10:38.921-07:00"
+ xsi:schemaLocation="http://medford.opengeo.org http://localhost:8080/geoserver/wfs?service=WFS&amp;version=1.1.0&amp;request=DescribeFeatureType&amp;typeName=medford%3Azoning http://www.opengis.net/wfs http://localhost:8080/geoserver/schemas/wfs/1.1.0/wfs.xsd"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:wfs="http://www.opengis.net/wfs"
+ xmlns:medford="http://opengeo.org/#medford"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:ows="http://www.opengis.net/ows"
+ xmlns:gml="http://www.opengis.net/gml"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <gml:featureMembers>
+ <medford:zoning gml:id="zoning.1">
+ <medford:the_geom>
+ <gml:MultiSurface srsName="urn:x-ogc:def:crs:EPSG:4326">
+ <gml:surfaceMember>
+ <gml:Polygon>
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList>42.397027571297585 -122.88465674265922 42.39702893980587 -122.88509730796012 42.397029086785146 -122.88511582432085 42.39702379767053 -122.88528111596624 42.39748517484964 -122.88529300380065 42.39748473847452 -122.88509914138723 42.39748482219041 -122.8849959517568 42.397485082635576 -122.8846741899541 42.3974853307826 -122.88436529392652 42.39702663751206 -122.88435664014142 42.397027571297585 -122.88465674265922</gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+ </gml:Polygon>
+ </gml:surfaceMember>
+ </gml:MultiSurface>
+ </medford:the_geom>
+ <medford:objectid>1</medford:objectid>
+ <medford:cityzone>YES</medford:cityzone>
+ <medford:zoning>I-L</medford:zoning>
+ <medford:revdate>2004-04-12T00:00:00-06:00</medford:revdate>
+ <medford:finord></medford:finord>
+ <medford:filenum></medford:filenum>
+ <medford:acres>0.95741118624</medford:acres>
+ <medford:misc></medford:misc>
+ <medford:shape_leng>835.705330224</medford:shape_leng>
+ <medford:perimeter>835.705330224</medford:perimeter>
+ <medford:area>41704.8312728</medford:area>
+ <medford:shape_le_1>835.705330224</medford:shape_le_1>
+ <medford:shape_area>41704.8312728</medford:shape_area>
+ <medford:hectares>0.38745056079</medford:hectares>
+ </medford:zoning>
+ </gml:featureMembers>
+</wfs:FeatureCollection>
+-->
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/GPX.html b/misc/openlayers/tests/Format/GPX.html
new file mode 100644
index 0000000..6286cfe
--- /dev/null
+++ b/misc/openlayers/tests/Format/GPX.html
@@ -0,0 +1,179 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var gpx_data = '<?xml version="1.0" encoding="ISO-8859-1"?><gpx version="1.1" creator="Memory-Map 5.1.3.715 http://www.memory-map.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"><wpt lat="51.3697845627" lon="-0.1853562259"><name>Mark</name><sym><![CDATA[Flag]]></sym><type><![CDATA[Marks]]></type></wpt><rte><name><![CDATA[Route8]]></name><type><![CDATA[Route]]></type><rtept lat="51.3761803674" lon="-0.1829991904"><name><![CDATA[WP0801]]></name><sym><![CDATA[Dot]]></sym><type><![CDATA[Waypoints]]></type></rtept><rtept lat="51.3697894659" lon="-0.1758887005"><name><![CDATA[WP0802]]></name><sym><![CDATA[Dot]]></sym><type><![CDATA[Waypoints]]></type></rtept><rtept lat="51.3639790884" lon="-0.1833202965"><name><![CDATA[WP0803]]></name><sym><![CDATA[Dot]]></sym><type><![CDATA[Waypoints]]></type></rtept><rtept lat="51.3567607069" lon="-0.1751119509"><name><![CDATA[WP0804]]></name><sym><![CDATA[Dot]]></sym><type><![CDATA[Waypoints]]></type></rtept></rte><trk><name><![CDATA[Track]]></name><type><![CDATA[Track]]></type><trkseg><trkpt lat="51.3768216433" lon="-0.1721292044"></trkpt><trkpt lat="51.3708337670" lon="-0.1649230916"></trkpt><trkpt lat="51.3644368725" lon="-0.1736741378"></trkpt><trkpt lat="51.3576354272" lon="-0.1662595250"></trkpt></trkseg></trk></gpx>';
+
+ function test_Format_GPX_constructor(t) {
+ t.plan(5);
+
+ var options = {'foo': 'bar'};
+ var format = new OpenLayers.Format.GPX(options);
+ t.ok(format instanceof OpenLayers.Format.GPX,
+ "new OpenLayers.Format.GPX returns object" );
+ t.eq(format.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a write function");
+ t.eq(format.externalProjection.getCode(), "EPSG:4326",
+ "default external projection is EPSG:4326");
+ }
+ function test_Format_GPX_read(t) {
+ t.plan(7);
+
+ var origDefaultPrecision = OpenLayers.Util.DEFAULT_PRECISION;
+ OpenLayers.Util.DEFAULT_PRECISION = 9;
+
+ var expected,
+ P = OpenLayers.Geometry.Point,
+ LS = OpenLayers.Geometry.LineString;
+ var f = new OpenLayers.Format.GPX();
+ var features = f.read(gpx_data);
+ t.eq(features.length, 3, "Number of features read is correct");
+ expected = new P(-0.1853562259, 51.3697845627);
+ t.geom_eq(features[2].geometry, expected, "waypoint feature correctly created");
+ expected = new LS([
+ new P(-0.1721292044, 51.3768216433),
+ new P(-0.1649230916, 51.370833767),
+ new P(-0.1736741378, 51.3644368725),
+ new P(-0.166259525, 51.3576354272)
+ ]);
+ t.geom_eq(features[0].geometry, expected, "track feature correctly created");
+ expected = new LS([
+ new P(-0.1829991904, 51.3761803674),
+ new P(-0.1758887005, 51.3697894659),
+ new P(-0.1833202965, 51.3639790884),
+ new P(-0.1751119509, 51.3567607069)
+ ]);
+ t.geom_eq(features[1].geometry, expected, "route feature correctly created");
+
+ f.internalProjection = new OpenLayers.Projection("EPSG:3857");
+ features = f.read(gpx_data);
+ expected = new P(-20633.760679678744, 6686966.841929403);
+ t.geom_eq(features[2].geometry, expected, "transformed waypoint feature correctly created");
+ expected = new LS([
+ new P(-19161.33538179203, 6688221.743275255),
+ new P(-18359.1545744088, 6687153.931130851),
+ new P(-19333.316581165607, 6686013.33343931),
+ new P(-18507.925659955214, 6684800.777090962)
+ ]);
+ t.geom_eq(features[0].geometry, expected, "transformed track feature correctly created");
+ expected = new LS([
+ new P(-20371.3766880736, 6688107.378491073),
+ new P(-19579.84057322507, 6686967.716235109),
+ new P(-20407.12205561124, 6685931.714395953),
+ new P(-19493.373203291227, 6684644.845706556)
+ ]);
+ t.geom_eq(features[1].geometry, expected, "transformed route feature correctly created");
+
+ OpenLayers.Util.DEFAULT_PRECISION = origDefaultPrecision;
+ }
+ function test_format_GPX_read_attributes(t) {
+ t.plan(2);
+ var f = new OpenLayers.Format.GPX();
+ var features = f.read(gpx_data);
+ t.eq(features[2].attributes['name'], "Mark", "Text attribute node read correctly.");
+ t.eq(features[2].attributes['sym'], "Flag", "CDATA attribute node read correctly.");
+ }
+ function test_Format_GPX_serialize_points(t) {
+ t.plan(2);
+
+ var parser = new OpenLayers.Format.GPX();
+
+ var point = new OpenLayers.Geometry.Point(-111.04, 45.68);
+ var point2 = new OpenLayers.Geometry.Point(-112.04, 45.68);
+ var features = [
+ new OpenLayers.Feature.Vector(point, {name: 'foo', description: 'bar'}),
+ new OpenLayers.Feature.Vector(point2, {name: 'foo', description: 'bar'})
+ ];
+ var data = parser.write(features);
+ t.xml_eq(data, '<gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="OpenLayers" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><wpt lon="-111.04" lat="45.68"><name>foo</name><desc>bar</desc></wpt><wpt lon="-112.04" lat="45.68"><name>foo</name><desc>bar</desc></wpt></gpx>', 'GPX serializes points correctly');
+
+ parser.internalProjection = new OpenLayers.Projection("EPSG:3857");
+ point = new OpenLayers.Geometry.Point(-12367595.42541111, 5621521.485409545);
+ point2 = new OpenLayers.Geometry.Point(-12472235.746742222, 5621521.485409545);
+ features = [
+ new OpenLayers.Feature.Vector(point, {name: 'foo', description: 'bar'}),
+ new OpenLayers.Feature.Vector(point2, {name: 'foo', description: 'bar'})
+ ];
+ data = parser.write(features);
+ t.xml_eq(data, '<gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="OpenLayers" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><wpt lon="-111.1" lat="45"><name>foo</name><desc>bar</desc></wpt><wpt lon="-112.04" lat="45"><name>foo</name><desc>bar</desc></wpt></gpx>', 'GPX serializes transformed points correctly');
+ }
+ function test_Format_GPX_serialize_line(t) {
+ t.plan(2);
+
+ var parser = new OpenLayers.Format.GPX();
+
+ var point = new OpenLayers.Geometry.Point(-111.04, 45.68);
+ var point2 = new OpenLayers.Geometry.Point(-112.04, 45.68);
+ var line = new OpenLayers.Geometry.LineString([point, point2]);
+ var f = new OpenLayers.Feature.Vector(line, {name: 'foo', description: 'bar'});
+ var data = parser.write(f);
+ t.xml_eq(data, '<gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="OpenLayers" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><trk><name>foo</name><desc>bar</desc><trkseg><trkpt lon="-111.04" lat="45.68"/><trkpt lon="-112.04" lat="45.68"/></trkseg></trk></gpx>', 'GPX serializes line correctly');
+
+ parser.internalProjection = new OpenLayers.Projection("EPSG:3857");
+ point = new OpenLayers.Geometry.Point(-12367595.42541111, 5621521.485409545);
+ point2 = new OpenLayers.Geometry.Point(-12472235.746742222, 5621521.485409545);
+ line = new OpenLayers.Geometry.LineString([point, point2]);
+ f = new OpenLayers.Feature.Vector(line, {name: 'foo', description: 'bar'});
+ data = parser.write(f);
+ t.xml_eq(data, '<gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="OpenLayers" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><trk><name>foo</name><desc>bar</desc><trkseg><trkpt lon="-111.1" lat="45"/><trkpt lon="-112.04" lat="45"/></trkseg></trk></gpx>', 'GPX serializes transformed line correctly');
+ }
+ function test_Format_GPX_serialize_lines(t) {
+ t.plan(1);
+
+ var parser = new OpenLayers.Format.GPX();
+
+ var point = new OpenLayers.Geometry.Point(-111.04, 45.68);
+ var point2 = new OpenLayers.Geometry.Point(-112.04, 45.68);
+ var line = new OpenLayers.Geometry.LineString([point, point2]);
+ var point3 = new OpenLayers.Geometry.Point(1, 2);
+ var point4 = new OpenLayers.Geometry.Point(3, 4);
+ var line2 = new OpenLayers.Geometry.LineString([point3, point4]);
+ var f = new OpenLayers.Feature.Vector(line, {name: 'foo', description: 'bar'});
+ var f2 = new OpenLayers.Feature.Vector(line2, {name: 'dude', description: 'truite'});
+ var data = parser.write([f, f2]);
+ t.xml_eq(data, '<gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="OpenLayers" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><trk><name>foo</name><desc>bar</desc><trkseg><trkpt lon="-111.04" lat="45.68"/><trkpt lon="-112.04" lat="45.68"/></trkseg></trk><trk><name>dude</name><desc>truite</desc><trkseg><trkpt lon="1" lat="2"/><trkpt lon="3" lat="4"/></trkseg></trk></gpx>', 'GPX serializes lines correctly');
+ }
+ function test_Format_GPX_serialize_multiline(t) {
+ t.plan(1);
+
+ var parser = new OpenLayers.Format.GPX();
+
+ var point = new OpenLayers.Geometry.Point(-111.04, 45.68);
+ var point2 = new OpenLayers.Geometry.Point(-112.04, 45.68);
+ var line = new OpenLayers.Geometry.LineString([point, point2]);
+ var point3 = new OpenLayers.Geometry.Point(1, 2);
+ var point4 = new OpenLayers.Geometry.Point(3, 4);
+ var line2 = new OpenLayers.Geometry.LineString([point3, point4]);
+ var multiline = new OpenLayers.Geometry.MultiLineString([line, line2]);
+ var f = new OpenLayers.Feature.Vector(multiline, {name: 'foo', description: 'bar'});
+ var data = parser.write([f]);
+ t.xml_eq(data, '<gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="OpenLayers" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><trk><name>foo</name><desc>bar</desc><trkseg><trkpt lon="-111.04" lat="45.68"/><trkpt lon="-112.04" lat="45.68"/></trkseg><trkseg><trkpt lon="1" lat="2"/><trkpt lon="3" lat="4"/></trkseg></trk></gpx>', 'GPX serializes multiline correctly');
+ }
+ function test_Format_GPX_serialize_polygon(t) {
+ t.plan(1);
+
+ var parser = new OpenLayers.Format.GPX();
+
+ var point = new OpenLayers.Geometry.Point(-111.04, 45.68);
+ var point2 = new OpenLayers.Geometry.Point(-112.04, 45.68);
+ var linearRing = new OpenLayers.Geometry.LinearRing([point, point2, point.clone()]);
+ var polygon = new OpenLayers.Geometry.Polygon([linearRing]);
+ var f = new OpenLayers.Feature.Vector(polygon, {name: 'foo', description: 'bar'});
+ var data = parser.write([f]);
+ t.xml_eq(data, '<gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="OpenLayers" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><trk><name>foo</name><desc>bar</desc><trkseg><trkpt lon="-111.04" lat="45.68"/><trkpt lon="-112.04" lat="45.68"/><trkpt lon="-111.04" lat="45.68"/></trkseg></trk></gpx>', 'GPX serializes polygon correctly');
+ }
+ function test_Format_GPX_serialize_metadata(t) {
+ t.plan(1);
+
+ var parser = new OpenLayers.Format.GPX();
+
+ var data = parser.write([], {name: 'foo', desc: 'bar'});
+ t.xml_eq(data, '<gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="OpenLayers" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><metadata><name>foo</name><desc>bar</desc></metadata></gpx>', 'GPX serializes metadata correctly');
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/GeoJSON.html b/misc/openlayers/tests/Format/GeoJSON.html
new file mode 100644
index 0000000..98e950f
--- /dev/null
+++ b/misc/openlayers/tests/Format/GeoJSON.html
@@ -0,0 +1,468 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var poly_content = '{"type": "FeatureCollection", "features": [{"geometry": {"type": "Polygon", "coordinates": [[[-131.484375, -5.9765625], [-112.5, -58.0078125], [-32.34375, -50.2734375], [-114.609375, 52.3828125], [-167.34375, -35.5078125], [-146.953125, -57.3046875], [-139.921875, -34.1015625], [-131.484375, -5.9765625]]]}, "type": "Feature", "id": 562, "properties": {"strokeColor": "red", "title": "Feature 2", "author": "Your Name Here"}}]}';
+ var null_geom_feature = '{"type":"Feature","properties":{"strokeColor":"blue","title":"Feature 5","author":"Your Name Here"},"geometry":null,"id":573}';
+ var point_feature = '{"geometry": {"type": "Point", "coordinates": [94.21875, 72.94921875]}, "type": "Feature", "id": 573, "properties": {"strokeColor": "blue", "title": "Feature 5", "author": "Your Name Here"}}';
+ var line_feature = '{"type": "FeatureCollection", "features": [{"geometry": {"type": "LineString", "coordinates": [[-27.0703125, 59.4140625], [-77.6953125, 20.7421875], [30.5859375, -36.2109375], [67.1484375, 34.8046875]]}, "type": "Feature", "id": 559, "properties": {"strokeColor": "red", "title": "Feature 1", "author": "Your Name Here"}}]}';
+ var multiple_features = '{"type": "FeatureCollection", "features": [{"geometry": {"type": "Point", "coordinates": [-91.0546875, 43.9453125]}, "type": "Feature", "id": 577, "properties": {"strokeColor": "red", "title": "Feature 2", "image": "foo.gif", "author": "Your Name Here"}}, {"geometry": {"type": "LineString", "coordinates": [[91.40625, -1.40625], [116.015625, -42.890625], [153.28125, -28.125], [108.984375, 11.25], [75.234375, 8.4375], [76.640625, 9.140625], [67.5, -36.5625], [67.5, -35.859375]]}, "type": "Feature", "id": 576, "properties": {"strokeColor": "red", "title": "Feature 1", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [139.5703125, 57.48046875]}, "type": "Feature", "id": 575, "properties": {"strokeColor": "blue", "title": "Feature 7", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [107.2265625, 82.44140625]}, "type": "Feature", "id": 574, "properties": {"strokeColor": "blue", "title": "Feature 6", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [94.21875, 72.94921875]}, "type": "Feature", "id": 573, "properties": {"strokeColor": "blue", "title": "Feature 5", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [116.3671875, 61.69921875]}, "type": "Feature", "id": 572, "properties": {"strokeColor": "blue", "title": "Feature 4", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [145.8984375, 73.65234375]}, "type": "Feature", "id": 571, "properties": {"strokeColor": "blue", "title": "Feature 3", "author": "Your Name Here"}}, {"geometry": {"type": "Polygon", "coordinates": [[[32.34375, 52.20703125], [87.1875, 70.13671875], [122.6953125, 37.44140625], [75.234375, 42.36328125], [40.078125, 42.36328125], [28.828125, 48.33984375], [18.6328125, 56.77734375], [23.203125, 65.56640625], [32.34375, 52.20703125]]]}, "type": "Feature", "id": 570, "properties": {"strokeColor": "blue", "title": "Feature 2", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [62.578125, -53.4375]}, "type": "Feature", "id": 569, "properties": {"strokeColor": "red", "title": "Feature 3", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [121.640625, 16.875]}, "type": "Feature", "id": 568, "properties": {"strokeColor": "red", "title": "Feature 6", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [135.703125, 8.4375]}, "type": "Feature", "id": 567, "properties": {"strokeColor": "red", "title": "Feature 4", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [137.109375, 48.515625]}, "type": "Feature", "id": 566, "properties": {"strokeColor": "red", "title": "Feature 274", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [0, 5]}, "type": "Feature", "id": 565, "properties": {}}, {"geometry": {"type": "Point", "coordinates": [0, 5]}, "type": "Feature", "id": 564, "properties": {}}, {"geometry": {"type": "Point", "coordinates": [0, 5]}, "type": "Feature", "id": 563, "properties": {}}, {"geometry": {"type": "Polygon", "coordinates": [[[-131.484375, -5.9765625], [-112.5, -58.0078125], [-32.34375, -50.2734375], [-114.609375, 52.3828125], [-167.34375, -35.5078125], [-146.953125, -57.3046875], [-139.921875, -34.1015625], [-131.484375, -5.9765625]]]}, "type": "Feature", "id": 562, "properties": {"strokeColor": "red", "title": "Feature 2", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [48.8671875, -15.8203125]}, "type": "Feature", "id": 560, "properties": {"strokeColor": "red", "title": "Feature 2", "author": "Your Name Here"}}, {"geometry": {"type": "LineString", "coordinates": [[-27.0703125, 59.4140625], [-77.6953125, 20.7421875], [30.5859375, -36.2109375], [67.1484375, 34.8046875]]}, "type": "Feature", "id": 559, "properties": {"strokeColor": "red", "title": "Feature 1", "author": "Your Name Here"}}, {"geometry": {"type": "Point", "coordinates": [12.65625, 16.5234375]}, "type": "Feature", "id": 558, "properties": {"styleUrl": "#allstyle", "title": "Feature 1", "strokeColor": "red", "author": "Your Name Here"}}]}';
+ var parser = new OpenLayers.Format.GeoJSON();
+ var data;
+
+ function test_Format_GeoJSON_constructor(t) {
+ t.plan(4);
+
+ var options = {'foo': 'bar'};
+ var format = new OpenLayers.Format.GeoJSON(options);
+ t.ok(format instanceof OpenLayers.Format.GeoJSON,
+ "new OpenLayers.Format.GeoJSON returns object" );
+ t.eq(format.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a write function");
+ }
+
+ function test_Format_GeoJSON_null_geom(t) {
+ t.plan(2);
+ var f = new OpenLayers.Format.GeoJSON();
+ var fs = f.read(null_geom_feature);
+ t.ok(fs[0].geometry == null, "Reading feature with null geom works okay");
+ t.eq(f.write(fs[0]), null_geom_feature, "round trip null okay");
+
+ }
+ function test_Format_GeoJSON_valid_type(t) {
+ t.plan(14);
+
+ OpenLayers.Console.error = function(error) { window.global_error = error;};
+ var types = ["Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon", "Box", "GeometryCollection"];
+ for (var i = 0; i < types.length; i++) {
+ t.ok(parser.isValidType({'type':types[i]}, "Geometry"), "Geometry with type " + types[i] + " is valid");
+ }
+ t.ok(!parser.isValidType({'type':"foo"}, "Geometry"), "Geometry with type foo is not valid");
+ t.eq(global_error, "Unsupported geometry type: foo", "error message set correctly for 'foo' geom.");
+ t.ok(parser.isValidType({}, "FeatureCollection"), "Feature collection type is always valid");
+ t.ok(parser.isValidType({'type':"GeometryCollection"}, "GeometryCollection"), "Geometry Collection type is valid");
+ t.ok(!parser.isValidType({'type':"GeometryCollection2"}, "GeometryCollection"), "Geometry Collection 2 type is invalid");
+ t.eq(global_error, "Cannot convert types from GeometryCollection2 to GeometryCollection", "error message set correctly for bad geometrycollection type");
+ }
+
+ function test_Format_GeoJSON_point(t) {
+ t.plan(3);
+
+ data = parser.read(point_feature);
+ t.eq(data[0].fid, 573, "Fid is correct on point feature");
+ t.eq(data[0].geometry.x, 94.21875, 'Reading point feature gives correct x');
+ data = parser.read(point_feature, "Feature");
+ t.eq(data.fid, 573, 'Reading point feature with type gives feature instead of array of features ');
+ }
+
+ function test_Format_GeoJSON_line(t) {
+ t.plan(5);
+
+ data = parser.read(line_feature);
+ t.eq(data[0].fid, 559, "Fid is correct on line feature");
+ t.eq(data[0].geometry.components.length, 4, 'Reading line feature gives correct length');
+ t.eq(data[0].geometry.CLASS_NAME, 'OpenLayers.Geometry.LineString', 'Reading line feature gives correct class');
+ t.eq(data[0].geometry.components[0].x, -27.0703125, 'Reading line feature gives correct x');
+ t.eq(data[0].geometry.components[0].y, 59.4140625, 'Reading line feature gives correct y');
+ }
+
+ function test_Format_GeoJSON_poly(t) {
+ t.plan(2);
+
+ data = parser.read(poly_content);
+ t.eq(data[0].fid, 562, "poly id is correct");
+ t.eq(data[0].geometry.components[0].components.length, 8,
+ 'Reading polygon first ring on feature from featurecollection gives correct length');
+ }
+
+ function test_Format_GeoJSON_multipoint(t) {
+ t.plan(5);
+
+ var multipoint = {
+ "type": "MultiPoint",
+ "coordinates": [
+ [100.0, 0.0], [101.0, 1.0]
+ ]
+ };
+ data = parser.read(multipoint, "Geometry");
+ t.eq(data.components.length, 2,
+ "Right number of components");
+ t.eq(data.components[0].CLASS_NAME, "OpenLayers.Geometry.Point", "First component is point");
+ t.eq(data.components[1].CLASS_NAME, "OpenLayers.Geometry.Point", "Second component is point");
+ t.eq(data.components[1].x, 101, "x of second component is right");
+ t.eq(data.components[1].y, 1, "y of second component is right");
+ }
+
+ function test_Format_GeoJSON_multipoint_projected(t) {
+ t.plan(1);
+ var f = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.MultiPoint([
+ new OpenLayers.Geometry.Point(15555162, 4247484)]));
+ var format = new OpenLayers.Format.GeoJSON({
+ internalProjection: new OpenLayers.Projection("EPSG:900913"),
+ externalProjection: new OpenLayers.Projection("EPSG:4326")
+ });
+ var data = format.write(f);
+ var found = (data.search('139.734') != -1);
+ t.ok(found, "Found 139.734 (correct reprojection) in data output.");
+ }
+
+ function test_Format_GeoJSON_multiline(t) {
+ t.plan(3);
+
+ var multiline = {
+ "type": "MultiLineString",
+ "coordinates": [
+ [ [100.0, 0.0], [101.0, 1.0] ],
+ [ [102.0, 2.0], [103.0, 3.0] ]
+ ]
+ };
+ data = parser.read(multiline, "Geometry");
+ t.eq(data.CLASS_NAME, "OpenLayers.Geometry.MultiLineString", "Correct class retrieved");
+ t.eq(data.components[0].components[0].CLASS_NAME, "OpenLayers.Geometry.Point", "correct type of components");
+ t.eq(data.components[0].CLASS_NAME, "OpenLayers.Geometry.LineString", "correct type of components");
+ }
+
+ function test_Format_GeoJSON_multipol(t) {
+ t.plan(2);
+
+ var multipol = {
+ "type": "MultiPolygon",
+ "coordinates": [
+ [
+ [ [102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0] ]
+ ],
+ [
+ [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ],
+ [ [100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2] ]
+ ]
+ ]
+ };
+ OpenLayers.Console.error = function(error) { window.global_error = error; };
+ data = parser.read(multipol, "Geometry");
+ t.eq(data.CLASS_NAME, "OpenLayers.Geometry.MultiPolygon", "Correct class retrieved");
+ t.eq(data.components[1].components[0].components[0].CLASS_NAME, "OpenLayers.Geometry.Point", "correct type of components");
+ }
+
+ function test_Format_GeoJSON_box(t) {
+ t.plan(6);
+
+ var box = {
+ "type": "Box",
+ "coordinates": [[100.0, 0.0], [101.0, 1.0]]
+ };
+ var poly = parser.read(box, "Geometry");
+ t.eq(poly.CLASS_NAME, "OpenLayers.Geometry.Polygon", "Box creates polygon");
+ t.eq(poly.components[0].components[1].x, 101, "x of lower right is correct");
+ t.eq(poly.components[0].components[1].y, 0, "y of lower right is correct");
+ t.eq(poly.components[0].components[3].x, 100, "x of upper left is correct");
+ t.eq(poly.components[0].components[3].y, 1, "y of upper left is correct");
+ box = parser.write(poly );
+ t.ok(box.search("Polygon") != -1 , "Serializes back to polygon");
+ }
+
+ // This test is from the geom_collection example on geojson spec.
+ function test_Format_GeoJSON_collection(t) {
+ t.plan(12);
+
+ var geomcol = {
+ "type": "GeometryCollection",
+ "geometries": [
+ {
+ "type": "Point",
+ "coordinates": [100.0, 0.0]
+ },
+ {
+ "type": "LineString",
+ "coordinates": [
+ [101.0, 0.0], [102.0, 1.0]
+ ]
+ }
+ ]
+ };
+ data = parser.read(geomcol, "Geometry");
+ t.eq(data.CLASS_NAME, "OpenLayers.Geometry.Collection",
+ "GeometryCollection deserialized into geometry.collection");
+ t.eq(data.components[0].CLASS_NAME, "OpenLayers.Geometry.Point",
+ "First geom is correct type");
+ t.eq(data.components[0].x, 100,
+ "First geom in geom collection has correct x");
+ t.eq(data.components[0].y, 0,
+ "First geom in geom collection has correct x");
+
+ t.eq(data.components[1].CLASS_NAME, "OpenLayers.Geometry.LineString",
+ "Second geom in geom collection is point linestring");
+ t.eq(data.components[1].components.length, 2,
+ "linestring is correct length");
+ t.eq(data.components[1].components[1].x, 102,
+ "linestring is correct x end");
+ t.eq(data.components[1].components[1].y, 1,
+ "linestring is correct y end");
+
+ data = parser.read(geomcol, "FeatureCollection");
+ t.eq(data[0].CLASS_NAME, "OpenLayers.Feature.Vector",
+ "GeometryCollection can be read in as a feature collection");
+ t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.Collection",
+ "feature contains the correct geometry type");
+ var feature = {
+ "type": "Feature",
+ "geometry": {
+ "type": "GeometryCollection",
+ "geometries": [
+ {
+ "type": "Point",
+ "coordinates": [100.0, 0.0]
+ },
+ {
+ "type": "LineString",
+ "coordinates": [
+ [101.0, 0.0], [102.0, 1.0]
+ ]
+ }
+ ]
+ },
+ "properties": {
+ "prop0": "value0",
+ "prop1": "value1"
+ }
+ };
+ data = parser.read(feature, "Feature");
+ t.eq(data.geometry.CLASS_NAME, "OpenLayers.Geometry.Collection", "Geometry of feature is a collection");
+ var l = new OpenLayers.Layer.Vector();
+ l.addFeatures(data);
+ t.ok(true, "adding a feature with geomcollection to layer doesn't cause error.");
+ }
+
+ function test_Format_GeoJSON_multipleFeatures(t) {
+ t.plan(2);
+
+ var feats = parser.read(multiple_features);
+ t.eq(feats.length, 19, "parsing a feature collection returns the correct number of features.");
+ var types = {'Point':0, 'LineString':0, 'Polygon':0};
+ for(var i = 0; i < feats.length; i++) {
+ var type = feats[i].geometry.CLASS_NAME.replace("OpenLayers.Geometry.", "");
+ types[type]++;
+ }
+ t.eq(types, {'Point':15, 'Polygon': 2, 'LineString':2}, "Correct number of each type");
+ }
+
+ function test_Format_GeoJSON_writeWithCRS(t) {
+ t.plan(2);
+ var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,2));
+ feature.fid = 0;
+ var output = '{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[1,2]},"id":0,"crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:OGC:1.3:CRS84"}}}';
+ var layer = new OpenLayers.Layer.Vector();
+ layer.projection = "EPSG:4326";
+ feature.layer = layer;
+ var parser = new OpenLayers.Format.GeoJSON();
+ var test_out = parser.write(feature);
+ t.eq(test_out, output, "Output is equal for vector with layer in EPSG:4326 ");
+ feature.layer.projection = "EPSG:2805";
+ output = '{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[1,2]},"id":0,"crs":{"type":"name","properties":{"name":"EPSG:2805"}}}';
+ test_out = parser.write(feature);
+ t.eq(test_out, output, "Output is equal for vector with point");
+ }
+
+ function test_Format_GeoJSON_write(t) {
+ t.plan(10);
+
+ var line_object = {
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "geometry": {
+ "type": "LineString",
+ "coordinates": [
+ [-27.0703125, 59.4140625],
+ [-77.6953125, 20.7421875],
+ [30.5859375, -36.2109375],
+ [67.1484375, 34.8046875]
+ ]
+ },
+ "type": "Feature",
+ "id": 559,
+ "properties": {
+ "strokeColor": "red",
+ "title": "Feature 1",
+ "author": "Your Name Here"
+ }
+ }
+ ]
+ };
+ data = parser.read(line_object);
+ out = parser.write(data);
+ serialized = '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"strokeColor":"red","title":"Feature 1","author":"Your Name Here"},"geometry":{"type":"LineString","coordinates":[[-27.0703125,59.4140625],[-77.6953125,20.7421875],[30.5859375,-36.2109375],[67.1484375,34.8046875]]},"id":559}]}';
+ t.eq(out, serialized, "input and output on line collections are the same");
+
+ var serialize_tests = [
+ [
+ new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,2)),
+ '{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[1,2]},"id":0}'
+ ],
+ [
+ new OpenLayers.Geometry.Point(1,2),
+ '{"type":"Point","coordinates":[1,2]}'
+ ],
+ [
+ new OpenLayers.Geometry.MultiPoint([
+ new OpenLayers.Geometry.Point(1,2)
+ ]),
+ '{"type":"MultiPoint","coordinates":[[1,2]]}'
+ ],
+ [
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(1,2),
+ new OpenLayers.Geometry.Point(3,4)
+ ]),
+ '{"type":"LineString","coordinates":[[1,2],[3,4]]}'
+ ],
+ [
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(1,2),
+ new OpenLayers.Geometry.Point(3,4),
+ new OpenLayers.Geometry.Point(5,6)
+ ])
+ ]),
+ '{"type":"Polygon","coordinates":[[[1,2],[3,4],[5,6],[1,2]]]}'
+ ],
+ [
+ new OpenLayers.Geometry.Collection([
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(1,2),
+ new OpenLayers.Geometry.Point(3,4),
+ new OpenLayers.Geometry.Point(5,6)
+ ])
+ ]),
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(1,2),
+ new OpenLayers.Geometry.Point(3,4)
+ ]),
+ new OpenLayers.Geometry.Point(1,2)
+ ]),
+ '{"type":"GeometryCollection","geometries":[{"type":"Polygon","coordinates":[[[1,2],[3,4],[5,6],[1,2]]]},{"type":"LineString","coordinates":[[1,2],[3,4]]},{"type":"Point","coordinates":[1,2]}]}'
+ ]
+ ];
+ serialize_tests[0][0].fid = 0;
+ multiline = new OpenLayers.Geometry.MultiLineString([serialize_tests[3][0], serialize_tests[3][0]]);
+ serialize_tests.push([multiline, '{"type":"MultiLineString","coordinates":[[[1,2],[3,4]],[[1,2],[3,4]]]}']);
+ multipolygon = new OpenLayers.Geometry.MultiPolygon([serialize_tests[4][0], serialize_tests[4][0]]);
+ serialize_tests.push([multipolygon, '{"type":"MultiPolygon","coordinates":[[[[1,2],[3,4],[5,6],[1,2]]],[[[1,2],[3,4],[5,6],[1,2]]]]}']);
+ serialize_tests.push([ [ serialize_tests[0][0] ], '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[1,2]},"id":0}]}' ]);
+ for (var i = 0; i < serialize_tests.length; i++) {
+ var input = serialize_tests[i][0];
+ var output = serialize_tests[i][1];
+ test_out = parser.write(input);
+ t.eq(test_out, output, "Serializing " + input.toString() + " saved correctly.");
+ }
+ }
+
+ function test_write_no_fid(t) {
+ t.plan(4);
+
+ var geojson;
+ var feature = new OpenLayers.Feature.Vector();
+
+ feature.fid = null;
+ geojson = parser.write(feature);
+ t.eq(geojson, '{"type":"Feature","properties":{},"geometry":null}',
+ "no id set in the GeoJSON string when fid is null");
+
+ feature.fid = undefined;
+ geojson = parser.write(feature);
+ t.eq(geojson, '{"type":"Feature","properties":{},"geometry":null}',
+ "no id set in the GeoJSON string when fid is undefined");
+
+ feature.fid = 0;
+ geojson = parser.write(feature);
+ t.eq(geojson, '{"type":"Feature","properties":{},"geometry":null,"id":0}',
+ "id set in the GeoJSON string when fid is 0");
+
+ delete feature.fid;
+ geojson = parser.write(feature);
+ t.eq(geojson, '{"type":"Feature","properties":{},"geometry":null}',
+ "id not set in the GeoJSON string when fid is delected");
+ }
+
+ function test_Format_GeoJSON_read_object(t) {
+ t.plan(1);
+
+ var line_object = {
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "geometry": {
+ "type": "LineString",
+ "coordinates": [
+ [-27.0703125, 59.4140625],
+ [-77.6953125, 20.7421875],
+ [30.5859375, -36.2109375],
+ [67.1484375, 34.8046875]
+ ]
+ },
+ "type": "Feature",
+ "id": 559,
+ "properties": {
+ "strokeColor": "red",
+ "title": "Feature 1",
+ "author": "Your Name Here"
+ }
+ }
+ ]
+ };
+ data = parser.read(line_object);
+ t.eq(data[0].fid, 559, "Can read data from an object correctly.");
+ }
+
+ function test_Format_GeoJSON_read_attributes(t) {
+ t.plan(3);
+
+ var parser = new OpenLayers.Format.GeoJSON();
+ data = parser.read(line_feature);
+ t.eq(data[0].attributes['strokeColor'], 'red', 'read strokeColor attribute properly');
+ t.eq(data[0].attributes['title'], 'Feature 1', 'read title attribute properly');
+ t.eq(data[0].attributes['author'], 'Your Name Here', 'read author attribute properly');
+ }
+
+ function test_read_bbox(t) {
+ t.plan(8);
+
+ var f;
+ parser = new OpenLayers.Format.GeoJSON();
+
+ // 4 tests
+ f = '{"geometry": {"type": "Point", "coordinates": [94.21875, 72.94921875], "bbox": [94.21875, 72.94921875, 94.21875, 72.94921875]}, "type": "Feature", "id": 573, "properties": {}, "bbox": [95.0, 73.0]}';
+ data = parser.read(f);
+ t.eq(data[0].bounds.left, 94.21875, "read left bound is correct");
+ t.eq(data[0].bounds.bottom, 72.94921875, "read bottom left bound is correct");
+ t.eq(data[0].bounds.right, 94.21875, "read right bound is correct");
+ t.eq(data[0].bounds.top, 72.94921875, "read top left bound is correct");
+
+ // 4 tests
+ f = '{"geometry": {"type": "Point", "coordinates": [94.21875, 72.94921875]}, "type": "Feature", "id": 573, "properties": {}, "bbox": [95.0, 73.0, 96.0, 74.0]}';
+ data = parser.read(f);
+ t.eq(data[0].bounds.left, 95.0, "read left bound is correct");
+ t.eq(data[0].bounds.bottom, 73.0, "read bottom left bound is correct");
+ t.eq(data[0].bounds.right, 96.0, "read right bound is correct");
+ t.eq(data[0].bounds.top, 74.0, "read top left bound is correct");
+ }
+
+ function test_Format_GeoJSON_point_extradims(t) {
+ t.plan(3);
+ var point_feature_3d = '{"geometry": {"type": "Point", "coordinates": [94.21875, 72.94921875, 0.0]}, "type": "Feature", "id": 573, "properties": {"strokeColor": "blue", "title": "Feature 5", "author": "Your Name Here"}}';
+ p = new OpenLayers.Format.GeoJSON({"ignoreExtraDims": true});
+ data = p.read(point_feature_3d);
+ t.eq(data[0].fid, 573, "Fid is correct on point feature");
+ t.eq(data[0].geometry.x, 94.21875, 'Reading point feature gives correct x');
+ data = p.read(point_feature_3d, "Feature");
+ t.eq(data.fid, 573, 'Reading point feature with type gives feature instead of array of features ');
+ }
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/GeoRSS.html b/misc/openlayers/tests/Format/GeoRSS.html
new file mode 100644
index 0000000..6e82f05
--- /dev/null
+++ b/misc/openlayers/tests/Format/GeoRSS.html
@@ -0,0 +1,122 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_Format_GeoRSS_constructor(t) {
+ t.plan(4);
+
+ var options = {'foo': 'bar'};
+ var format = new OpenLayers.Format.GeoRSS(options);
+ t.ok(format instanceof OpenLayers.Format.GeoRSS,
+ "new OpenLayers.Format.GeoRSS returns object" );
+ t.eq(format.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a write function");
+ }
+
+ function test_Format_GeoRSS_serializeline(t) {
+ t.plan(1);
+
+ var parser = new OpenLayers.Format.GeoRSS();
+ var point = new OpenLayers.Geometry.Point(-111.04, 45.68);
+ var point2 = new OpenLayers.Geometry.Point(-112.04, 45.68);
+ var l = new OpenLayers.Geometry.LineString([point, point2]);
+ var f = new OpenLayers.Feature.Vector(l);
+ var data = parser.write([f]);
+ t.xml_eq(data, '<rss xmlns="http://backend.userland.com/rss2"><item><title></title><description></description><georss:line xmlns:georss="http://www.georss.org/georss">45.68 -111.04 45.68 -112.04</georss:line></item></rss>', 'GeoRSS serializes a line correctly');
+ }
+ function test_Format_GeoRSS_box(t) {
+ t.plan(4);
+ var xml = '<rss xmlns="http://backend.userland.com/rss2"><item><title></title><description></description><georss:box xmlns:georss="http://www.georss.org/georss">45.68 -112.04 47.68 -111.04</georss:box></item></rss>';
+ var format = new OpenLayers.Format.GeoRSS();
+ var features = format.read(xml);
+ t.eq(features.length, 1, "one feature returned");
+ t.eq(features[0].geometry.components[0].components.length, 5, "polygon returned");
+ t.eq(features[0].geometry.components[0].components[0].x, -112.04, "polygon returned with correct first x");
+ t.eq(features[0].geometry.components[0].components[0].y, 45.68, "polygon returned with correct first y");
+ }
+ function test_Format_GeoRSS_w3cgeo(t) {
+ t.plan(2);
+
+ var parser = new OpenLayers.Format.GeoRSS();
+ var data = parser.read('<rss xmlns="http://backend.userland.com/rss2" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"><item><geo:long>-1</geo:long><geo:lat>1</geo:lat></item></rss>');
+ t.eq(data[0].geometry.x, -1, "w3c geo x read correctly");
+ t.eq(data[0].geometry.y, 1, "w3c geo y read correctly");
+ }
+ function test_Format_GeoRSS_reproject_null(t) {
+ t.plan(1);
+
+ var parser = new OpenLayers.Format.GeoRSS({'internalProjection':new OpenLayers.Projection("EPSG:4326"), 'externalProjection': new OpenLayers.Projection("EPSG:4326")});
+ var data = parser.read('<rss xmlns="http://backend.userland.com/rss2" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"><item></item></rss>');
+ t.eq(data.length, 1, "Parsing items with null geometry and reprojection doesn't fail");
+ }
+ function test_Format_GeoRSS_roundtrip(t) {
+ t.plan(input.length);
+ var parser = new OpenLayers.Format.GeoRSS();
+ for(var i=0; i < input.length; i++) {
+ var feed = shell_start+input[i]+shell_end;
+ var data = parser.read(feed);
+ var out = parser.write(data);
+ var expected_result = output[i];
+ t.xml_eq(out, expected_result, "Output gave expected value");
+ }
+ }
+ function test_Format_GeoRSS_gml_roundtrip(t) {
+ t.plan(input_gml.length);
+ var parser = new OpenLayers.Format.GeoRSS();
+ for(var i=0; i < input_gml.length; i++) {
+ var feed = shell_start_gml+input_gml[i]+shell_end_gml;
+ var data = parser.read(feed);
+ var out = parser.write(data);
+ var expected_result = output_gml[i];
+ t.xml_eq(out, expected_result, "Output gave expected value");
+ }
+ }
+
+ function test_leading_space(t) {
+ t.plan(2);
+
+ var parser = new OpenLayers.Format.GeoRSS();
+ var items = parser.read('<rss version="2.0" xmlns:georss="http://www.georss.org/georss"><item><description> <![CDATA[foo]]></description></item></rss>');
+ t.eq(items.length, 1, "item created");
+
+ // when parsing a node composed of both spaces and a cdata section
+ // (e.g. <description> <![DATA[foo]]></description> IE8 ignores
+ // the leading white spaces, and reports that the node does not
+ // include a text node. For that reason, we need to trim the
+ // string value resulting from the parsing.
+
+ var description = OpenLayers.String.trim(items[0].attributes.description);
+ t.eq(description, "foo", "description value is ok");
+ }
+
+ var shell_start = '<feed xmlns="http://www.w3.org/2005/Atom" \n xmlns:georss="http://www.georss.org/georss">\n <title>scribble</title>\n <id>http://featureserver.org/featureserver.cgi/scribble?format=atom</id>\n <author><name>FeatureServer</name></author>\n';
+ var shell_end = '</feed>';
+ var input = ['<entry><id>http://featureserver.org/featureserver.cgi/scribble/562.atom</id><link href="http://featureserver.org/featureserver.cgi/scribble/562.atom"/><title>Feature 2</title><content type="html">&lt;b&gt;strokeColor&lt;/b&gt;: red&lt;br /&gt;&lt;b&gt;title&lt;/b&gt;: Feature 2&lt;br /&gt;&lt;b&gt;author&lt;/b&gt;: Your Name Here</content><georss:polygon>-5.9765625 -131.484375 -58.0078125 -112.5 -50.2734375 -32.34375 52.3828125 -114.609375 -35.5078125 -167.34375 -57.3046875 -146.953125 -34.1015625 -139.921875 -5.9765625 -131.484375</georss:polygon></entry>',
+ '<entry><id>http://featureserver.org/featureserver.cgi/scribble/796.atom</id><link href="http://featureserver.org/featureserver.cgi/scribble/796.atom"/><title>Feature 2</title><content type="html">&lt;b&gt;strokeColor&lt;/b&gt;: 00ccff&lt;br /&gt;&lt;b&gt;title&lt;/b&gt;: Feature 2&lt;br /&gt;&lt;b&gt;author&lt;/b&gt;: Your Name Here</content><georss:point>75.5859375 15.46875</georss:point></entry>',
+ '<entry><id>http://featureserver.org/featureserver.cgi/scribble/794.atom</id><link href="http://featureserver.org/featureserver.cgi/scribble/794.atom"/><title>Feature 5</title><content type="html">&lt;b&gt;strokeColor&lt;/b&gt;: red&lt;br /&gt;&lt;b&gt;title&lt;/b&gt;: Feature 5&lt;br /&gt;&lt;b&gt;author&lt;/b&gt;: Your Name Here</content><georss:line>28.828125 32.6953125 49.921875 34.8046875 39.375 58.0078125 39.375 58.0078125 40.078125 58.0078125 41.484375 58.0078125 43.59375 58.0078125 45.703125 58.7109375 47.8125 58.7109375 49.21875 58.7109375 51.328125 59.4140625 52.03125 59.4140625 54.140625 60.8203125 56.25 61.5234375 56.25 62.2265625 57.65625 62.2265625 57.65625 62.9296875 58.359375 63.6328125 58.359375 65.0390625 58.359375 65.7421875 59.0625 66.4453125 59.0625 67.1484375 59.0625 68.5546875 59.765625 69.9609375 59.765625 72.0703125 59.765625 73.4765625 59.765625 76.2890625 59.765625 78.3984375 59.765625 79.8046875 59.765625 81.9140625 59.765625 83.3203125 59.0625 84.7265625 59.0625 86.8359375 58.359375 87.5390625 58.359375 88.2421875 56.953125 89.6484375 56.25 91.0546875 54.84375 93.8671875 52.03125 96.6796875 51.328125 98.7890625 50.625 100.1953125 49.21875 102.3046875 48.515625 103.7109375 47.8125 104.4140625 47.109375 105.8203125 46.40625 106.5234375 46.40625 107.9296875 45.703125 109.3359375 45 110.7421875 43.59375 112.8515625 43.59375 114.2578125 43.59375 114.9609375 42.890625 117.0703125 42.890625 117.7734375 42.1875 118.4765625 42.1875 119.1796875 42.1875 119.8828125</georss:line></entry>'
+ ];
+ var output= ['<rss xmlns="http://backend.userland.com/rss2"><item><title>Feature 2</title><description>&lt;b&gt;strokeColor&lt;/b&gt;: red&lt;br /&gt;&lt;b&gt;title&lt;/b&gt;: Feature 2&lt;br /&gt;&lt;b&gt;author&lt;/b&gt;: Your Name Here</description><link>http://featureserver.org/featureserver.cgi/scribble/562.atom</link><georss:polygon xmlns:georss="http://www.georss.org/georss">-5.9765625 -131.484375 -58.0078125 -112.5 -50.2734375 -32.34375 52.3828125 -114.609375 -35.5078125 -167.34375 -57.3046875 -146.953125 -34.1015625 -139.921875 -5.9765625 -131.484375</georss:polygon></item></rss>',
+ '<rss xmlns="http://backend.userland.com/rss2"><item><title>Feature 2</title><description>&lt;b&gt;strokeColor&lt;/b&gt;: 00ccff&lt;br /&gt;&lt;b&gt;title&lt;/b&gt;: Feature 2&lt;br /&gt;&lt;b&gt;author&lt;/b&gt;: Your Name Here</description><link>http://featureserver.org/featureserver.cgi/scribble/796.atom</link><georss:point xmlns:georss="http://www.georss.org/georss">75.5859375 15.46875</georss:point></item></rss>',
+ '<rss xmlns="http://backend.userland.com/rss2"><item><title>Feature 5</title><description>&lt;b&gt;strokeColor&lt;/b&gt;: red&lt;br /&gt;&lt;b&gt;title&lt;/b&gt;: Feature 5&lt;br /&gt;&lt;b&gt;author&lt;/b&gt;: Your Name Here</description><link>http://featureserver.org/featureserver.cgi/scribble/794.atom</link><georss:line xmlns:georss="http://www.georss.org/georss">28.828125 32.6953125 49.921875 34.8046875 39.375 58.0078125 39.375 58.0078125 40.078125 58.0078125 41.484375 58.0078125 43.59375 58.0078125 45.703125 58.7109375 47.8125 58.7109375 49.21875 58.7109375 51.328125 59.4140625 52.03125 59.4140625 54.140625 60.8203125 56.25 61.5234375 56.25 62.2265625 57.65625 62.2265625 57.65625 62.9296875 58.359375 63.6328125 58.359375 65.0390625 58.359375 65.7421875 59.0625 66.4453125 59.0625 67.1484375 59.0625 68.5546875 59.765625 69.9609375 59.765625 72.0703125 59.765625 73.4765625 59.765625 76.2890625 59.765625 78.3984375 59.765625 79.8046875 59.765625 81.9140625 59.765625 83.3203125 59.0625 84.7265625 59.0625 86.8359375 58.359375 87.5390625 58.359375 88.2421875 56.953125 89.6484375 56.25 91.0546875 54.84375 93.8671875 52.03125 96.6796875 51.328125 98.7890625 50.625 100.1953125 49.21875 102.3046875 48.515625 103.7109375 47.8125 104.4140625 47.109375 105.8203125 46.40625 106.5234375 46.40625 107.9296875 45.703125 109.3359375 45 110.7421875 43.59375 112.8515625 43.59375 114.2578125 43.59375 114.9609375 42.890625 117.0703125 42.890625 117.7734375 42.1875 118.4765625 42.1875 119.1796875 42.1875 119.8828125</georss:line></item></rss>'
+ ];
+
+ var shell_start_gml = '<feed xmlns="http://www.w3.org/2005/Atom" xmlns:gml="http://www.opengis.net/gml" xmlns:georss="http://www.georss.org/georss"> <title>scribble</title><id>http://featureserver.org/featureserver.cgi/scribble?format=atom</id><author><name>FeatureServer</name></author>';
+ var shell_end_gml = '</feed>';
+ var input_gml = ['<entry><id>http://featureserver.org/featureserver.cgi/scribble/111.atom</id><link href="http://featureserver.org/featureserver.cgi/scribble/111.atom"/><title>Feature 2</title><content type="html">&lt;b&gt;strokeColor&lt;/b&gt;: red&lt;br /&gt;&lt;b&gt;title&lt;/b&gt;: Feature 2&lt;br /&gt;&lt;b&gt;author&lt;/b&gt;: Your Name Here</content><georss:where><gml:Point><gml:pos>0 10</gml:pos></gml:Point></georss:where></entry>',
+ '<entry><id>http://featureserver.org/featureserver.cgi/scribble/111.atom</id><link href="http://featureserver.org/featureserver.cgi/scribble/111.atom"/><title>Feature 2</title><content type="html">&lt;b&gt;strokeColor&lt;/b&gt;: red&lt;br /&gt;&lt;b&gt;title&lt;/b&gt;: Feature 2&lt;br /&gt;&lt;b&gt;author&lt;/b&gt;: Your Name Here</content><georss:where><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>110,-50 110,-10 155,-10 155,-50 110,-50</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></georss:where></entry>',
+ '<entry><id>http://featureserver.org/featureserver.cgi/scribble/111.atom</id><link href="http://featureserver.org/featureserver.cgi/scribble/111.atom"/><title>Feature 2</title><content type="html">&lt;b&gt;strokeColor&lt;/b&gt;: red&lt;br /&gt;&lt;b&gt;title&lt;/b&gt;: Feature 2&lt;br /&gt;&lt;b&gt;author&lt;/b&gt;: Your Name Here</content><georss:where><gml:LineString><gml:coordinates>0,10 10,20</gml:coordinates></gml:LineString></georss:where></entry>',
+ '<entry><id>http://featureserver.org/featureserver.cgi/scribble/111.atom</id><link href="http://featureserver.org/featureserver.cgi/scribble/111.atom"/><title>Feature 2</title><content type="html">&lt;b&gt;strokeColor&lt;/b&gt;: red&lt;br /&gt;&lt;b&gt;title&lt;/b&gt;: Feature 2&lt;br /&gt;&lt;b&gt;author&lt;/b&gt;: Your Name Here</content><georss:where><gml:Envelope><gml:lowerCorner>0 1</gml:lowerCorner><gml:upperCorner>20 21</gml:upperCorner></gml:Envelope></georss:where></entry>'
+ ];
+ var output_gml = ['<rss xmlns="http://backend.userland.com/rss2"><item><title>Feature 2</title><description>&lt;b&gt;strokeColor&lt;/b&gt;: red&lt;br /&gt;&lt;b&gt;title&lt;/b&gt;: Feature 2&lt;br /&gt;&lt;b&gt;author&lt;/b&gt;: Your Name Here</description><link>http://featureserver.org/featureserver.cgi/scribble/111.atom</link><georss:point xmlns:georss="http://www.georss.org/georss">0 10</georss:point></item></rss>',
+ '<rss xmlns="http://backend.userland.com/rss2"><item><title>Feature 2</title><description>&lt;b&gt;strokeColor&lt;/b&gt;: red&lt;br /&gt;&lt;b&gt;title&lt;/b&gt;: Feature 2&lt;br /&gt;&lt;b&gt;author&lt;/b&gt;: Your Name Here</description><link>http://featureserver.org/featureserver.cgi/scribble/111.atom</link><georss:polygon xmlns:georss="http://www.georss.org/georss">110 -50 110 -10 155 -10 155 -50 110 -50</georss:polygon></item></rss>',
+ '<rss xmlns="http://backend.userland.com/rss2"><item><title>Feature 2</title><description>&lt;b&gt;strokeColor&lt;/b&gt;: red&lt;br /&gt;&lt;b&gt;title&lt;/b&gt;: Feature 2&lt;br /&gt;&lt;b&gt;author&lt;/b&gt;: Your Name Here</description><link>http://featureserver.org/featureserver.cgi/scribble/111.atom</link><georss:line xmlns:georss="http://www.georss.org/georss">0 10 10 20</georss:line></item></rss>',
+ '<rss xmlns="http://backend.userland.com/rss2"><item><title>Feature 2</title><description>&lt;b&gt;strokeColor&lt;/b&gt;: red&lt;br /&gt;&lt;b&gt;title&lt;/b&gt;: Feature 2&lt;br /&gt;&lt;b&gt;author&lt;/b&gt;: Your Name Here</description><link>http://featureserver.org/featureserver.cgi/scribble/111.atom</link><georss:polygon xmlns:georss="http://www.georss.org/georss">0 1 0 21 20 21 20 1 0 1</georss:polygon></item></rss>'
+ ];
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/JSON.html b/misc/openlayers/tests/Format/JSON.html
new file mode 100644
index 0000000..84461e1
--- /dev/null
+++ b/misc/openlayers/tests/Format/JSON.html
@@ -0,0 +1,53 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_Format_JSON_constructor(t) {
+ t.plan(4);
+
+ var options = {'foo': 'bar'};
+ var format = new OpenLayers.Format.JSON(options);
+ t.ok(format instanceof OpenLayers.Format.JSON,
+ "new OpenLayers.Format.JSON returns object" );
+ t.eq(format.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a write function");
+ }
+
+ function test_Format_JSON_parser(t) {
+ t.plan(2);
+
+ var format = new OpenLayers.Format.JSON();
+ var data = format.read('{"a":["b"], "c":1}');
+ var obj = {"a":["b"], "c":1};
+ t.eq(obj['a'], data['a'], "element with array parsed correctly.");
+ t.eq(obj['c'], data['c'], "element with number parsed correctly.");
+ }
+
+ function test_Format_JSON_writer(t) {
+ t.plan(1);
+
+ var format = new OpenLayers.Format.JSON();
+ var data = format.write({"a":["b"], "c":1});
+ var obj = '{"a":["b"],"c":1}';
+ t.eq(data, obj, "writing data to json works.");
+ }
+
+
+ function test_keepData(t) {
+ t.plan(2);
+
+ var options = {'keepData': true};
+ var format = new OpenLayers.Format.JSON(options);
+ format.read('{"a":["b"], "c":1}');
+
+ t.ok(format.data != null, 'data property is not null after read with keepData=true');
+ t.eq(format.data.c,1,'keepData keeps the right data');
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/KML.html b/misc/openlayers/tests/Format/KML.html
new file mode 100644
index 0000000..ba87637
--- /dev/null
+++ b/misc/openlayers/tests/Format/KML.html
@@ -0,0 +1,1437 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var test_content = '<kml xmlns="http://earth.google.com/kml/2.0"><Folder><name>OpenLayers export</name><description>Vector geometries from OpenLayers</description><Placemark id="KML.Polygon"><name>OpenLayers.Feature.Vector_344</name><description>A KLM Polygon</description><Polygon><outerBoundaryIs><LinearRing><coordinates>5.001370157823406,49.26855713824488 8.214706453896161,49.630662409673505 8.397385910100951,48.45172350357396 5.001370157823406,49.26855713824488</coordinates></LinearRing></outerBoundaryIs></Polygon></Placemark><Placemark id="KML.LineString"><name>OpenLayers.Feature.Vector_402</name><description>A KML LineString</description><LineString><coordinates>5.838523393080493,49.74814616928052 5.787079558782349,48.410795432216574 8.91427702008381,49.28932499608202</coordinates></LineString></Placemark><Placemark id="KML.Point"><name>OpenLayers.Feature.Vector_451</name><description>A KML Point</description><Point><coordinates>6.985073041685488,49.8682250149058</coordinates></Point></Placemark><Placemark id="KML.MultiGeometry"><name>SF Marina Harbor Master</name><description>KML MultiGeometry</description><MultiGeometry><LineString><coordinates>-122.4425587930444,37.80666418607323 -122.4428379594768,37.80663578323093</coordinates></LineString><LineString><coordinates>-122.4425509770566,37.80662588061205 -122.4428340530617,37.8065999493009</coordinates></LineString></MultiGeometry></Placemark></Folder></kml>';
+ var test_style = '<kml xmlns="http://earth.google.com/kml/2.0"> <Placemark> <Style> <LineStyle> <color>870000ff</color> <width>10</width> </LineStyle> </Style> <LineString> <coordinates> -112,36 -113,37 </coordinates> </LineString> </Placemark></kml>';
+ var test_style_fill = '<kml xmlns="http://earth.google.com/kml/2.0"> <Placemark> <Style> <PolyStyle> <fill>1</fill> <color>870000ff</color> <width>10</width> </PolyStyle> </Style> <LineString> <coordinates> -112,36 -113,37 </coordinates> </LineString> </Placemark><Placemark> <Style> <PolyStyle> <fill>0</fill> <color>870000ff</color> <width>10</width> </PolyStyle> </Style> <LineString> <coordinates> -112,36 -113,37 </coordinates> </LineString> </Placemark></kml>';
+ var test_style_outline = '<kml xmlns="http://earth.google.com/kml/2.0"> <Placemark> <Style> <PolyStyle> <outline>0</outline> <color>870000ff</color> <width>10</width> </PolyStyle> </Style> <LineString> <coordinates> -112,36 -113,37 </coordinates> </LineString> </Placemark></kml>';
+ var test_style_font = '<kml xmlns="http://earth.google.com/kml/2.0"> <Placemark><Style><LabelStyle><color>870000ff</color><scale>1.5</scale></LabelStyle></Style><LineString><coordinates> -112,36 -113,37 </coordinates></LineString></Placemark></kml>';
+ var test_nl = '<kml xmlns="http://earth.google.com/kml/2.2"> <Document> <NetworkLink> <Link> <href>http://maker.geocommons.com/maps/1717/overlays/0</href> </Link> </NetworkLink> </Document></kml>';
+ function test_Format_KML_constructor(t) {
+ t.plan(5);
+
+ var options = {'foo': 'bar'};
+ var format = new OpenLayers.Format.KML(options);
+ t.ok(format instanceof OpenLayers.Format.KML,
+ "new OpenLayers.Format.KML returns object" );
+ t.eq(format.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a write function");
+ t.eq(format.externalProjection.getCode(), "EPSG:4326",
+ "default external projection is EPSG:4326");
+ }
+ function test_Format_KML_multipoint(t) {
+ t.plan(1);
+ var f = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.MultiPoint([
+ new OpenLayers.Geometry.Point(15555162, 4247484)]));
+ var format = new OpenLayers.Format.KML({
+ extractStyles: true,
+ extractAttributes: true,
+ internalProjection: new OpenLayers.Projection("EPSG:900913"),
+ externalProjection: new OpenLayers.Projection("EPSG:4326")
+ });
+ var data = format.write(f);
+ var found = (data.search('139.734') != -1);
+ t.ok(found, "Found 139.734 (correct reprojection) in data output.");
+ }
+ function test_Format_KML_read(t) {
+ t.plan(5);
+ var features = (new OpenLayers.Format.KML()).read(this.test_content);
+ t.eq(features.length, 4, "Number of features read is correct");
+ t.ok(features[0].geometry.toString() == "POLYGON((5.001370157823406 49.26855713824488,8.214706453896161 49.630662409673505,8.397385910100951 48.45172350357396,5.001370157823406 49.26855713824488))", "polygon feature geometry correctly created");
+ t.ok(features[1].geometry.toString() == "LINESTRING(5.838523393080493 49.74814616928052,5.787079558782349 48.410795432216574,8.91427702008381 49.28932499608202)", "linestring feature geometry correctly created");
+ t.ok(features[2].geometry.toString() == "POINT(6.985073041685488 49.8682250149058)", "point feature geometry correctly created");
+ t.ok(features[3].geometry.CLASS_NAME == "OpenLayers.Geometry.Collection",
+ "read geometry collection");
+ }
+
+
+ function test_Format_KML_readCdataAttributes_20(t) {
+ t.plan(2);
+ var cdata = '<kml xmlns="http://earth.google.com/kml/2.0"><Document><Placemark><name><![CDATA[Pezinok]]> </name><description><![CDATA[Full of text.]]></description><styleUrl>#rel1.0</styleUrl><Point> <coordinates>17.266666, 48.283333</coordinates></Point></Placemark></Document></kml>';
+ var features = (new OpenLayers.Format.KML()).read(cdata);
+ t.eq(features[0].attributes.description, "Full of text.", "Description attribute in cdata read correctly");
+ t.eq(features[0].attributes.name, "Pezinok", "title attribute in cdata read correctly");
+
+ }
+
+ function test_Format_KML_networklink(t) {
+ t.plan(1);
+ var f = new OpenLayers.Format.KML({'maxDepth':1});
+ f.fetchLink = function(url) {
+ t.eq(url, "http://maker.geocommons.com/maps/1717/overlays/0", "network link fetched a link correctly.");
+ return '';
+ }
+ f.read(test_nl);
+ }
+ function test_Format_KML_readCdataAttributes_21(t) {
+ t.plan(2);
+ var cdata = '<kml xmlns="http://earth.google.com/kml/2.1"><Document><Placemark><name><![CDATA[Pezinok]]></name><description><![CDATA[Full of text.]]></description><styleUrl>#rel1.0</styleUrl><Point> <coordinates>17.266666, 48.283333</coordinates></Point></Placemark></Document></kml>';
+ var features = (new OpenLayers.Format.KML()).read(cdata);
+ t.eq(features[0].attributes.description, "Full of text.", "Description attribute in cdata read correctly");
+ t.eq(features[0].attributes.name, "Pezinok", "title attribute in cdata read correctly");
+
+ }
+
+ function test_Format_KML_write(t) {
+ // make sure id, name, and description are preserved
+ t.plan(1);
+ var kmlExpected = this.test_content;
+ var options = {
+ foldersName: "OpenLayers export",
+ foldersDesc: "Vector geometries from OpenLayers"
+ };
+
+ var format = new OpenLayers.Format.KML(options);
+ var features = format.read(kmlExpected);
+ var kmlOut = format.write(features);
+ var kmlOut = kmlOut.replace(/<\?[^>]*\?>/, ''); // Remove XML Prolog
+ t.eq(kmlOut, kmlExpected, "correctly writes an KML doc string");
+ }
+
+ function test_Format_KML_write_noNameDesc(t) {
+ t.plan(1);
+ var format = new OpenLayers.Format.KML({
+ foldersName: null,
+ foldersDesc: null
+ });
+ var geom = new OpenLayers.Geometry.Point(0, 0)
+ var feature = new OpenLayers.Feature.Vector(geom);
+ feature.id = 42;
+ var kmlOut = format.write(feature);
+ var expected = '<kml xmlns="http://earth.google.com/kml/2.0"><Folder><Placemark><name>42</name><description>No description available</description><Point><coordinates>0,0</coordinates></Point></Placemark></Folder></kml>'
+ t.eq(kmlOut, expected, "null foldersName or foldersDesc don't create elements");
+ }
+
+ function test_Format_KML_write_multis(t) {
+ /**
+ * KML doesn't have a representation for multi geometries of a
+ * specific type. KML MultiGeometry maps to OL Geometry.Collection.
+ * Because of this, multi-geometries in OL can't make a round trip
+ * through KML (an OL MultiPoint maps to a KML MultiGeometry
+ * containing points, which maps back to an OL Collection containing
+ * points). So we need to acceptance tests for the writing of
+ * multi-geometries specifically instead of relying on the round-trip
+ * write test above.
+ */
+ t.plan(3);
+ var format = new OpenLayers.Format.KML({foldersDesc: "test output"});
+ var multi, feature, output, expected;
+
+ // test multipoint
+ var multi = new OpenLayers.Geometry.MultiPoint([
+ new OpenLayers.Geometry.Point(0, 1)
+ ]);
+ feature = new OpenLayers.Feature.Vector(multi, {name: "test name"});
+ output = format.write(feature);
+ expected = '<kml xmlns="http://earth.google.com/kml/2.0"><Folder><name>OpenLayers export</name><description>test output</description><Placemark><name>test name</name><description>No description available</description><MultiGeometry><Point><coordinates>0,1</coordinates></Point></MultiGeometry></Placemark></Folder></kml>';
+ var output = output.replace(/<\?[^>]*\?>/, ''); // Remove XML Prolog
+ t.eq(output, expected, "multipoint correctly written");
+
+ // test multilinestring
+ var multi = new OpenLayers.Geometry.MultiLineString([
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(1, 0),
+ new OpenLayers.Geometry.Point(0, 1)
+ ])
+ ]);
+ feature = new OpenLayers.Feature.Vector(multi, {name: "test name"});
+ output = format.write(feature);
+ expected = '<kml xmlns="http://earth.google.com/kml/2.0"><Folder><name>OpenLayers export</name><description>test output</description><Placemark><name>test name</name><description>No description available</description><MultiGeometry><LineString><coordinates>1,0 0,1</coordinates></LineString></MultiGeometry></Placemark></Folder></kml>';
+ var output = output.replace(/<\?[^>]*\?>/, ''); // Remove XML Prolog
+ t.eq(output, expected, "multilinestring correctly written");
+
+ // test multipolygon
+ var multi = new OpenLayers.Geometry.MultiPolygon([
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(0, 0),
+ new OpenLayers.Geometry.Point(1, 0),
+ new OpenLayers.Geometry.Point(0, 1)
+ ])
+ ])
+ ]);
+ feature = new OpenLayers.Feature.Vector(multi, {name: "test name"});
+ output = format.write(feature);
+ expected = '<kml xmlns="http://earth.google.com/kml/2.0"><Folder><name>OpenLayers export</name><description>test output</description><Placemark><name>test name</name><description>No description available</description><MultiGeometry><Polygon><outerBoundaryIs><LinearRing><coordinates>0,0 1,0 0,1 0,0</coordinates></LinearRing></outerBoundaryIs></Polygon></MultiGeometry></Placemark></Folder></kml>';
+ var output = output.replace(/<\?[^>]*\?>/, ''); // Remove XML Prolog
+ t.eq(output, expected, "multilinestring correctly written");
+
+ }
+ function test_Format_KML_extractStyle(t) {
+ t.plan(1);
+ var f = new OpenLayers.Format.KML();
+ var features = f.read(test_style);
+ t.ok(features[0].style == undefined, "KML Feature has no style with extractStyle false");
+ }
+ function test_Format_KML_extractStyleFill(t) {
+ t.plan(2);
+ var f = new OpenLayers.Format.KML({extractStyles: true});
+ var features = f.read(test_style_fill);
+ t.eq(features[0].style.fillColor, "#ff0000", "default fill is set");
+ t.eq(features[1].style.fillColor, "none", "KML Feature has none fill");
+ }
+ function test_Format_KML_extractStyleOutline(t) {
+ t.plan(2);
+ var f = new OpenLayers.Format.KML({extractStyles: true});
+ var features = f.read(test_style);
+ t.eq(features[0].style.strokeWidth, "10", "default stroke is set");
+ var features = f.read(test_style_outline);
+ t.eq(features[0].style.strokeWidth, "0", "KML Feature has no outline");
+ }
+ function test_Format_KML_extractStyleFont(t) {
+ t.plan(2);
+ var f = new OpenLayers.Format.KML({extractStyles: true});
+ var features = f.read(test_style_font);
+ t.eq(features[0].style.fontColor, "#ff0000", "font color is set");
+ t.eq(features[0].style.fontOpacity, parseInt('87', 16) / 255, "font opacity is set");
+ }
+ function test_Format_KML_getStyle(t) {
+ t.plan(1);
+ var style = {t: true};
+ var f = new OpenLayers.Format.KML();
+ f.styles = {test: style};
+ var gotStyle = f.getStyle('test');
+ gotStyle.t = false;
+ t.ok(style.t, "getStyle returns copy of style rather than reference");
+ }
+ function test_Format_KML_extendedData(t) {
+ t.plan(6);
+ var f = new OpenLayers.Format.KML();
+ var features = f.read(OpenLayers.Util.getElement("kml_extendeddata").value);
+ t.eq(features[0].attributes.holeYardage.value, "234", "read value from extendeddata correctly.");
+ t.eq(features[0].attributes.holeYardage.displayName, "<b><i>The yardage is </i></b>", "read displayName from extendeddata correctly.");
+ t.eq(f.read(f.write(features[0]))[0].attributes.holeYardage.value, features[0].attributes.holeYardage.value, "attribute value written correctly");
+ t.eq(f.read(f.write(features[0]))[0].attributes.holeYardage.displayName, features[0].attributes.holeYardage.displayName, "attribute displayName written correctly");
+ f.kvpAttributes = true;
+ features = f.read(OpenLayers.Util.getElement("kml_extendeddata").value);
+ t.eq(features[0].attributes.holeYardage, "234", "read kvp value from extendeddata correctly.");
+ t.eq(f.read(f.write(features[0]))[0].attributes.holeYardage, features[0].attributes.holeYardage, "kvp attribute value written correctly");
+ }
+
+ function test_Format_KML_extendedData_SchemaData(t) {
+ t.plan(10);
+ var f = new OpenLayers.Format.KML();
+ var features = f.read(OpenLayers.Util.getElement("kml_extendeddata2").value);
+ t.eq(features[0].attributes.TrailHeadName.value, "Pi in the sky", "read value from extendeddata (schema data) correctly.");
+ t.eq(features[0].attributes.TrailHeadName.displayName, "TrailHeadName", "read displayName from extendeddata correctly");
+ t.eq(features[0].attributes.ElevationGain.value, "10", "read value from extendeddata (schema data) correctly.");
+ t.eq(features[0].attributes.ElevationGain.displayName, "ElevationGain", "read displayName from extendeddata correctly");
+ t.eq(f.read(f.write(features[0]))[0].attributes.TrailHeadName.value, features[0].attributes.TrailHeadName.value, "attribute value from extendeddata (schema data) written correctly");
+ t.eq(f.read(f.write(features[0]))[0].attributes.ElevationGain.value, features[0].attributes.ElevationGain.value, "attribute value from extendeddata (schema data) written correctly");
+ f.kvpAttributes = true;
+ features = f.read(OpenLayers.Util.getElement("kml_extendeddata2").value);
+ t.eq(features[0].attributes.TrailHeadName, "Pi in the sky", "read kvp value from extendeddata (schema data) correctly.");
+ t.eq(features[0].attributes.ElevationGain, "10", "read kvp value from extendeddata (schema data) correctly.");
+ t.eq(f.read(f.write(features[0]))[0].attributes.TrailHeadName, features[0].attributes.TrailHeadName, "kvp attribute value from extendeddata (schema data) written correctly");
+ t.eq(f.read(f.write(features[0]))[0].attributes.ElevationGain, features[0].attributes.ElevationGain, "kvp attribute value from extendeddata (schema data) written correctly");
+ }
+
+ function test_Format_KML_placemarkName(t) {
+ t.plan(3);
+
+ var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,0));
+ var f = new OpenLayers.Format.KML();
+
+ t.eq(f.read(f.write(feature))[0].attributes.name, feature.id, "placemark name from feature.id");
+ feature.style = {
+ label: "placemark name from style.label"
+ };
+ t.eq(f.read(f.write(feature))[0].attributes.name, feature.style.label, "placemark name from style.label");
+
+ feature.attributes.name = "placemark name from attributes.name";
+ t.eq(f.read(f.write(feature))[0].attributes.name, feature.attributes.name, "placemark name from attributes.name");
+ }
+ function test_Format_KML_linestring_projected(t) {
+ t.plan(1);
+ var f = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(15555162, 4247484), new OpenLayers.Geometry.Point(15555163, 4247485)]));
+ var format = new OpenLayers.Format.KML({
+ internalProjection: new OpenLayers.Projection("EPSG:900913"),
+ externalProjection: new OpenLayers.Projection("EPSG:4326")
+ });
+ var data = format.write(f);
+ var found = (data.search('139.734') != -1);
+ t.ok(found, "Found 139.734 (correct reprojection) in data output.");
+ }
+
+ function test_extractTracks(t) {
+
+ t.plan(13);
+
+ var xml = new OpenLayers.Format.XML();
+ var doc = xml.read(document.getElementById("macnoise.kml").firstChild.nodeValue);
+
+ var format = new OpenLayers.Format.KML({
+ extractTracks: true,
+ trackAttributes: ["speed", "num"] // additional custom attributes
+ });
+
+ var features = format.read(doc.documentElement);
+ t.eq(features.length, 170, "got 170 features");
+
+ var attr = features[4].attributes;
+
+ // standard track point attributes
+ t.ok(attr.when instanceof Date, "features have when attribute");
+ t.eq(attr.when.getTime(), 1272736815000, "correct time for fifth feature");
+ t.eq(attr.altitude, 1006, "altitude parsed");
+ t.eq(attr.heading, 230, "heading parsed");
+ t.eq(attr.tilt, 0, "tilt parsed");
+ t.eq(attr.roll, 0, "roll parsed");
+
+ // custom track attributes (all features acquire from the placemark)
+ t.eq(attr.name, "B752", "correct name");
+ t.eq(attr.adflag, "A", "correct adflag");
+ t.eq(attr.flightid, "DAL2973", "correct flightid");
+
+ // additional per point attributes (determined by trackAttributes property)
+ t.eq(attr.speed, "166", "correct speed");
+ t.eq(attr.num, "50", "correct num");
+
+ var exp = new OpenLayers.Geometry.Point(-93.0753620391713, 44.9879724110872);
+ exp.z = 1006;
+ t.geom_eq(features[4].geometry, exp, "correct geometry");
+
+ }
+
+
+ </script>
+</head>
+<body>
+ <textarea id="kml_extendeddata" style="display:none">
+<?xml version="1.0" encoding="UTF-8"?>
+<kml xmlns="http://www.opengis.net/kml/2.2">
+ <Document>
+ <name>Entity-Replacement</name>
+ <Placemark>
+ <name>Club house</name>
+ <ExtendedData>
+ <Data name="holeNumber">
+ <displayName><![CDATA[
+ <b>This is hole </b>
+ ]]></displayName>
+ <value>1</value>
+ </Data>
+ <Data name="holePar">
+ <displayName><![CDATA[
+ <i>The par for this hole is </i>
+ ]]></displayName>
+ <value>4</value>
+ </Data>
+ <Data name="holeYardage">
+ <displayName><![CDATA[<b><i>The yardage is </i></b>]]></displayName>
+ <value>234</value>
+ </Data>
+ </ExtendedData>
+ <Point>
+ <coordinates>-111.956,33.5043</coordinates>
+ </Point>
+ </Placemark>
+ <Placemark>
+ <name>By the lake</name>
+ <ExtendedData>
+ <Data name="holeNumber">
+ <displayName><![CDATA[
+ <b>This is hole </b>
+ ]]></displayName>
+ <value>5</value>
+ </Data>
+ <Data name="holePar">
+ <displayName><![CDATA[
+ <i>The par for this hole is </i>
+ ]]></displayName>
+ <value>5</value>
+ </Data>
+ <Data name="holeYardage">
+ <displayName><![CDATA[
+ <b><i>The yardage is </i></b>
+ ]]></displayName>
+ <value>523</value>
+ </Data>
+ </ExtendedData>
+ <Point>
+ <coordinates>-111.95,33.5024</coordinates>
+ </Point>
+ </Placemark>
+ </Document>
+</kml>
+</textarea>
+ <textarea id="kml_extendeddata2" style="display:none">
+<kml xmlns="http://earth.google.com/kml/2.2">
+<Document>
+<Placemark>
+ <name>Easy trail</name>
+ <ExtendedData>
+ <SchemaData schemaUrl="#TrailHeadTypeId">
+ <SimpleData name="TrailHeadName">Pi in the sky</SimpleData>
+ <SimpleData name="TrailLength">3.14159</SimpleData>
+ <SimpleData name="ElevationGain">10</SimpleData>
+ </SchemaData>
+ </ExtendedData>
+ <Point>
+ <coordinates>-122.000,37.002</coordinates>
+ </Point>
+</Placemark>
+<Placemark>
+ <name>Difficult trail</name>
+ <ExtendedData>
+ <SchemaData schemaUrl="#TrailHeadTypeId">
+ <SimpleData name="TrailHeadName">Mount Everest</SimpleData>
+ <SimpleData name="TrailLength">347.45</SimpleData>
+ <SimpleData name="ElevationGain">10000</SimpleData>
+ </SchemaData>
+ </ExtendedData>
+ <Point>
+ <coordinates>-122.000,37.002</coordinates>
+ </Point>
+</Placemark>
+</Document>
+</kml>
+</textarea>
+
+<div id="macnoise.kml"><!--
+<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2">
+<Document>
+<Camera>
+ <gx:TimeStamp>
+ <when>2010-05-01T13:00:00-05:00</when>
+ </gx:TimeStamp>
+ <longitude>-93.2207</longitude>
+ <latitude>44.882</latitude>
+ <altitude>50000</altitude>
+ <heading>0</heading>
+ <tilt>0</tilt>
+</Camera>
+<Style id="arrival">
+ <IconStyle>
+ <Icon>
+ <href>http://maps.macnoise.com/scripts/plane.png</href>
+ </Icon>
+ </IconStyle>
+ <LineStyle>
+ <color>ff0000ff</color>
+ <width>3</width>
+ </LineStyle>
+ <PolyStyle>
+ <color>7fffffff</color>
+ </PolyStyle>
+</Style>
+<Style id="departure">
+ <IconStyle>
+ <Icon>
+ <href>http://maps.macnoise.com/scripts/plane.png</href>
+ </Icon>
+ </IconStyle>
+ <LineStyle>
+ <color>ff00ff00</color>
+ <width>3</width>
+ </LineStyle>
+ <PolyStyle>
+ <color>7fffffff</color>
+ </PolyStyle>
+</Style>
+<Style id="overflight">
+ <IconStyle>
+ <Icon>
+ <href>http://maps.macnoise.com/scripts/plane.png</href>
+ </Icon>
+ </IconStyle>
+ <LineStyle>
+ <color>ff222222</color>
+ <width>3</width>
+ </LineStyle>
+ <PolyStyle>
+ <color>7fffffff</color>
+ </PolyStyle>
+</Style>
+<Style id='rmt'>
+ <LabelStyle>
+ <color>ff0000cc</color>
+ <colorMode>normal</colorMode>
+ <scale>1</scale>
+ </LabelStyle>
+</Style>
+
+<name>Flight Tracks</name>
+<Folder>
+ <name>Arrivals</name>
+<Placemark>
+ <name>B752</name>
+ <adflag>A</adflag>
+ <flightid>DAL2973</flightid>
+ <styleUrl>#arrival</styleUrl>
+<gx:Track>
+ <altitudeMode>absolute</altitudeMode>
+ <extrude>1</extrude>
+ <when>2010-05-01T13:00:00-05</when>
+ <when>2010-05-01T13:00:01-05</when>
+ <when>2010-05-01T13:00:06-05</when>
+ <when>2010-05-01T13:00:10-05</when>
+ <when>2010-05-01T13:00:15-05</when>
+ <when>2010-05-01T13:00:20-05</when>
+ <when>2010-05-01T13:00:24-05</when>
+ <when>2010-05-01T13:00:29-05</when>
+ <when>2010-05-01T13:00:33-05</when>
+ <when>2010-05-01T13:00:38-05</when>
+ <when>2010-05-01T13:00:43-05</when>
+ <when>2010-05-01T13:00:47-05</when>
+ <when>2010-05-01T13:00:52-05</when>
+ <when>2010-05-01T13:00:57-05</when>
+ <when>2010-05-01T13:01:00-05</when>
+ <gx:coord>-93.0658625188843 44.9949645987875 1036</gx:coord>
+ <gx:coord>-93.0664690096445 44.9945424635331 1036</gx:coord>
+ <gx:coord>-93.0694347065378 44.9923936108644 1036</gx:coord>
+ <gx:coord>-93.0722946883822 44.9901649091109 1006</gx:coord>
+ <gx:coord>-93.0753620391713 44.9879724110872 1006</gx:coord>
+ <gx:coord>-93.078638650624 44.985904678007 975</gx:coord>
+ <gx:coord>-93.0817463907976 44.9836868456013 975</gx:coord>
+ <gx:coord>-93.0847749343212 44.9813998515538 945</gx:coord>
+ <gx:coord>-93.0879207383429 44.9791066547511 914</gx:coord>
+ <gx:coord>-93.091282218058 44.976822731273 914</gx:coord>
+ <gx:coord>-93.0945882606646 44.9745372955479 884</gx:coord>
+ <gx:coord>-93.0979053364864 44.9722421846492 884</gx:coord>
+ <gx:coord>-93.1012678619471 44.9698451058525 853</gx:coord>
+ <gx:coord>-93.1044570741037 44.967424293466 853</gx:coord>
+ <gx:coord>-93.1068079756418 44.9657037851018 853</gx:coord>
+ <gx:angles>230 0 0</gx:angles>
+ <gx:angles>220 0 0</gx:angles>
+ <gx:angles>220 0 0</gx:angles>
+ <gx:angles>230 0 0</gx:angles>
+ <gx:angles>230 0 0</gx:angles>
+ <gx:angles>230 0 0</gx:angles>
+ <gx:angles>230 0 0</gx:angles>
+ <gx:angles>220 0 0</gx:angles>
+ <gx:angles>220 0 0</gx:angles>
+ <gx:angles>230 0 0</gx:angles>
+ <gx:angles>230 0 0</gx:angles>
+ <gx:angles>220 0 0</gx:angles>
+ <gx:angles>220 0 0</gx:angles>
+ <gx:angles>220 0 0</gx:angles>
+ <gx:angles>220 0 0</gx:angles>
+ <speed>162</speed>
+ <speed>160</speed>
+ <speed>159</speed>
+ <speed>165</speed>
+ <speed>166</speed>
+ <speed>174</speed>
+ <speed>170</speed>
+ <speed>172</speed>
+ <speed>180</speed>
+ <speed>176</speed>
+ <speed>177</speed>
+ <speed>177</speed>
+ <speed>180</speed>
+ <speed>184</speed>
+ <speed>177</speed>
+ <num>10</num>
+ <num>20</num>
+ <num>30</num>
+ <num>40</num>
+ <num>50</num>
+ <num>60</num>
+ <num>70</num>
+ <num>80</num>
+ <num>90</num>
+ <num>100</num>
+ <num>110</num>
+ <num>120</num>
+ <num>130</num>
+ <num>140</num>
+ <num>150</num>
+</gx:Track></Placemark>
+<Placemark>
+ <name>E170</name>
+ <adflag>A</adflag>
+ <flightid>TCF7521</flightid>
+ <styleUrl>#arrival</styleUrl>
+<gx:Track>
+ <altitudeMode>absolute</altitudeMode>
+ <extrude>1</extrude>
+ <when>2010-05-01T13:00:00-05</when>
+ <when>2010-05-01T13:00:04-05</when>
+ <when>2010-05-01T13:00:09-05</when>
+ <when>2010-05-01T13:00:13-05</when>
+ <when>2010-05-01T13:00:18-05</when>
+ <when>2010-05-01T13:00:23-05</when>
+ <when>2010-05-01T13:00:27-05</when>
+ <when>2010-05-01T13:00:32-05</when>
+ <when>2010-05-01T13:00:37-05</when>
+ <when>2010-05-01T13:00:41-05</when>
+ <when>2010-05-01T13:00:46-05</when>
+ <when>2010-05-01T13:00:51-05</when>
+ <when>2010-05-01T13:00:55-05</when>
+ <when>2010-05-01T13:01:00-05</when>
+ <gx:coord>-93.3806146339391 44.8823651507134 2743</gx:coord>
+ <gx:coord>-93.3773041814209 44.887531728655 2743</gx:coord>
+ <gx:coord>-93.3742856469083 44.8942041806778 2743</gx:coord>
+ <gx:coord>-93.3722375106026 44.9009231720158 2743</gx:coord>
+ <gx:coord>-93.3711934089417 44.9077495987718 2712</gx:coord>
+ <gx:coord>-93.3707288919852 44.9145219645156 2712</gx:coord>
+ <gx:coord>-93.3703882714439 44.921240089024 2682</gx:coord>
+ <gx:coord>-93.3700882719793 44.9278850664392 2682</gx:coord>
+ <gx:coord>-93.369810041597 44.934389356737 2651</gx:coord>
+ <gx:coord>-93.3696836566166 44.9408553642446 2651</gx:coord>
+ <gx:coord>-93.3695425129226 44.9473561165969 2621</gx:coord>
+ <gx:coord>-93.3693185423471 44.9537360442564 2621</gx:coord>
+ <gx:coord>-93.3693194298816 44.9599975904123 2590</gx:coord>
+ <gx:coord>-93.3694031671108 44.9661411653607 2590</gx:coord>
+ <gx:angles>20 0 0</gx:angles>
+ <gx:angles>20 0 0</gx:angles>
+ <gx:angles>20 0 0</gx:angles>
+ <gx:angles>10 0 0</gx:angles>
+ <gx:angles>10 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>360 0 0</gx:angles>
+ <speed>376</speed>
+ <speed>367</speed>
+ <speed>361</speed>
+ <speed>371</speed>
+ <speed>367</speed>
+ <speed>363</speed>
+ <speed>359</speed>
+ <speed>356</speed>
+ <speed>352</speed>
+ <speed>347</speed>
+ <speed>343</speed>
+ <speed>347</speed>
+ <speed>334</speed>
+ <speed>337</speed>
+ <num>10</num>
+ <num>20</num>
+ <num>30</num>
+ <num>40</num>
+ <num>50</num>
+ <num>60</num>
+ <num>70</num>
+ <num>80</num>
+ <num>90</num>
+ <num>100</num>
+ <num>110</num>
+ <num>120</num>
+ <num>130</num>
+ <num>140</num>
+</gx:Track></Placemark>
+<Placemark>
+ <name>BE33</name>
+ <adflag>A</adflag>
+ <flightid>N38175</flightid>
+ <styleUrl>#arrival</styleUrl>
+<gx:Track>
+ <altitudeMode>absolute</altitudeMode>
+ <extrude>1</extrude>
+ <when>2010-05-01T13:00:00-05</when>
+ <when>2010-05-01T13:00:02-05</when>
+ <when>2010-05-01T13:00:07-05</when>
+ <when>2010-05-01T13:00:12-05</when>
+ <when>2010-05-01T13:00:16-05</when>
+ <when>2010-05-01T13:00:21-05</when>
+ <when>2010-05-01T13:00:25-05</when>
+ <when>2010-05-01T13:00:30-05</when>
+ <when>2010-05-01T13:00:35-05</when>
+ <when>2010-05-01T13:00:39-05</when>
+ <when>2010-05-01T13:00:44-05</when>
+ <when>2010-05-01T13:00:49-05</when>
+ <when>2010-05-01T13:00:53-05</when>
+ <when>2010-05-01T13:00:58-05</when>
+ <when>2010-05-01T13:01:00-05</when>
+ <gx:coord>-93.0144637208028 44.6541474764804 1006</gx:coord>
+ <gx:coord>-93.0162681345228 44.6547274296664 1006</gx:coord>
+ <gx:coord>-93.0196734868835 44.6559915702004 975</gx:coord>
+ <gx:coord>-93.0231899415297 44.657188463998 945</gx:coord>
+ <gx:coord>-93.0267619421777 44.6582849847887 945</gx:coord>
+ <gx:coord>-93.0302021384369 44.6594728216183 914</gx:coord>
+ <gx:coord>-93.0338776768471 44.6606515995762 914</gx:coord>
+ <gx:coord>-93.0375866343814 44.6618806707998 884</gx:coord>
+ <gx:coord>-93.0411146687035 44.6632657982455 884</gx:coord>
+ <gx:coord>-93.0447829038862 44.6646495821585 884</gx:coord>
+ <gx:coord>-93.0486933143218 44.6659856209571 914</gx:coord>
+ <gx:coord>-93.0525604964428 44.6672664774449 884</gx:coord>
+ <gx:coord>-93.0559892061682 44.6686325276705 884</gx:coord>
+ <gx:coord>-93.0595122787868 44.6700360197293 884</gx:coord>
+ <gx:coord>-93.0610274392619 44.6706087373734 884</gx:coord>
+ <gx:angles>300 0 0</gx:angles>
+ <gx:angles>300 0 0</gx:angles>
+ <gx:angles>300 0 0</gx:angles>
+ <gx:angles>300 0 0</gx:angles>
+ <gx:angles>290 0 0</gx:angles>
+ <gx:angles>290 0 0</gx:angles>
+ <gx:angles>300 0 0</gx:angles>
+ <gx:angles>300 0 0</gx:angles>
+ <gx:angles>300 0 0</gx:angles>
+ <gx:angles>300 0 0</gx:angles>
+ <gx:angles>300 0 0</gx:angles>
+ <gx:angles>300 0 0</gx:angles>
+ <gx:angles>300 0 0</gx:angles>
+ <gx:angles>300 0 0</gx:angles>
+ <gx:angles>300 0 0</gx:angles>
+ <speed>150</speed>
+ <speed>156</speed>
+ <speed>152</speed>
+ <speed>156</speed>
+ <speed>151</speed>
+ <speed>152</speed>
+ <speed>160</speed>
+ <speed>157</speed>
+ <speed>159</speed>
+ <speed>158</speed>
+ <speed>158</speed>
+ <speed>160</speed>
+ <speed>155</speed>
+ <speed>155</speed>
+ <speed>156</speed>
+ <num>10</num>
+ <num>20</num>
+ <num>30</num>
+ <num>40</num>
+ <num>50</num>
+ <num>60</num>
+ <num>70</num>
+ <num>80</num>
+ <num>90</num>
+ <num>100</num>
+ <num>110</num>
+ <num>120</num>
+ <num>130</num>
+ <num>140</num>
+ <num>150</num>
+</gx:Track></Placemark>
+<Placemark>
+ <name>A319</name>
+ <adflag>A</adflag>
+ <flightid>DAL1588</flightid>
+ <styleUrl>#arrival</styleUrl>
+<gx:Track>
+ <altitudeMode>absolute</altitudeMode>
+ <extrude>1</extrude>
+ <when>2010-05-01T13:00:00-05</when>
+ <when>2010-05-01T13:00:04-05</when>
+ <when>2010-05-01T13:00:08-05</when>
+ <when>2010-05-01T13:00:13-05</when>
+ <when>2010-05-01T13:00:18-05</when>
+ <when>2010-05-01T13:00:22-05</when>
+ <when>2010-05-01T13:00:27-05</when>
+ <when>2010-05-01T13:00:31-05</when>
+ <when>2010-05-01T13:00:36-05</when>
+ <when>2010-05-01T13:00:41-05</when>
+ <when>2010-05-01T13:00:45-05</when>
+ <when>2010-05-01T13:00:50-05</when>
+ <when>2010-05-01T13:00:55-05</when>
+ <when>2010-05-01T13:00:59-05</when>
+ <when>2010-05-01T13:01:00-05</when>
+ <gx:coord>-93.6927825194056 44.7952011849485 3011</gx:coord>
+ <gx:coord>-93.6850156681578 44.7968042586582 2987</gx:coord>
+ <gx:coord>-93.6752785488692 44.7990458605003 2956</gx:coord>
+ <gx:coord>-93.6657083011645 44.8014897663497 2926</gx:coord>
+ <gx:coord>-93.6560029615388 44.803768841381 2865</gx:coord>
+ <gx:coord>-93.6462045264035 44.8058749817725 2834</gx:coord>
+ <gx:coord>-93.6365671200126 44.8080848199989 2804</gx:coord>
+ <gx:coord>-93.6269933807039 44.8102767000109 2773</gx:coord>
+ <gx:coord>-93.6175405757462 44.8123960709083 2743</gx:coord>
+ <gx:coord>-93.6082528975965 44.8146455509748 2743</gx:coord>
+ <gx:coord>-93.599077315807 44.816765612372 2743</gx:coord>
+ <gx:coord>-93.5899428762254 44.8186933623744 2743</gx:coord>
+ <gx:coord>-93.5809104439923 44.8205403457841 2743</gx:coord>
+ <gx:coord>-93.5720785209701 44.8224608846058 2743</gx:coord>
+ <gx:coord>-93.5703603013364 44.8228739543212 2743</gx:coord>
+ <gx:angles>70 0 0</gx:angles>
+ <gx:angles>70 0 0</gx:angles>
+ <gx:angles>70 0 0</gx:angles>
+ <gx:angles>70 0 0</gx:angles>
+ <gx:angles>70 0 0</gx:angles>
+ <gx:angles>70 0 0</gx:angles>
+ <gx:angles>70 0 0</gx:angles>
+ <gx:angles>70 0 0</gx:angles>
+ <gx:angles>70 0 0</gx:angles>
+ <gx:angles>70 0 0</gx:angles>
+ <gx:angles>70 0 0</gx:angles>
+ <gx:angles>70 0 0</gx:angles>
+ <gx:angles>70 0 0</gx:angles>
+ <gx:angles>70 0 0</gx:angles>
+ <gx:angles>70 0 0</gx:angles>
+ <speed>390</speed>
+ <speed>383</speed>
+ <speed>397</speed>
+ <speed>390</speed>
+ <speed>405</speed>
+ <speed>388</speed>
+ <speed>386</speed>
+ <speed>397</speed>
+ <speed>377</speed>
+ <speed>373</speed>
+ <speed>367</speed>
+ <speed>362</speed>
+ <speed>365</speed>
+ <speed>350</speed>
+ <speed>354</speed>
+ <num>10</num>
+ <num>20</num>
+ <num>30</num>
+ <num>40</num>
+ <num>50</num>
+ <num>60</num>
+ <num>70</num>
+ <num>80</num>
+ <num>90</num>
+ <num>100</num>
+ <num>110</num>
+ <num>120</num>
+ <num>130</num>
+ <num>140</num>
+ <num>150</num>
+</gx:Track></Placemark>
+<Placemark>
+ <name>E145</name>
+ <adflag>A</adflag>
+ <flightid>CHQ1453</flightid>
+ <styleUrl>#arrival</styleUrl>
+<gx:Track>
+ <altitudeMode>absolute</altitudeMode>
+ <extrude>1</extrude>
+ <when>2010-05-01T13:00:00-05</when>
+ <when>2010-05-01T13:00:01-05</when>
+ <when>2010-05-01T13:00:06-05</when>
+ <when>2010-05-01T13:00:11-05</when>
+ <when>2010-05-01T13:00:15-05</when>
+ <when>2010-05-01T13:00:20-05</when>
+ <when>2010-05-01T13:00:24-05</when>
+ <when>2010-05-01T13:00:29-05</when>
+ <when>2010-05-01T13:00:34-05</when>
+ <when>2010-05-01T13:00:38-05</when>
+ <when>2010-05-01T13:00:43-05</when>
+ <when>2010-05-01T13:00:48-05</when>
+ <when>2010-05-01T13:00:52-05</when>
+ <when>2010-05-01T13:00:57-05</when>
+ <when>2010-05-01T13:01:00-05</when>
+ <gx:coord>-92.5727580977974 45.0236058844647 2530</gx:coord>
+ <gx:coord>-92.5742776202954 45.0237913896498 2530</gx:coord>
+ <gx:coord>-92.5803397933112 45.0241784662561 2499</gx:coord>
+ <gx:coord>-92.5865075192046 45.0247891381303 2469</gx:coord>
+ <gx:coord>-92.5926877928765 45.0257073410966 2469</gx:coord>
+ <gx:coord>-92.5986546763805 45.0261844476041 2438</gx:coord>
+ <gx:coord>-92.6046737535477 45.0267206733977 2438</gx:coord>
+ <gx:coord>-92.6106885874739 45.0275061986719 2438</gx:coord>
+ <gx:coord>-92.616359210337 45.027935793162 2438</gx:coord>
+ <gx:coord>-92.6220735719954 45.028379077688 2438</gx:coord>
+ <gx:coord>-92.6280403097635 45.0290552550566 2438</gx:coord>
+ <gx:coord>-92.6341725652711 45.029824064212 2438</gx:coord>
+ <gx:coord>-92.640279209769 45.0304963952702 2438</gx:coord>
+ <gx:coord>-92.6463747377703 45.0311129317319 2438</gx:coord>
+ <gx:coord>-92.650043383232 45.0314890298388 2438</gx:coord>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <speed>235</speed>
+ <speed>246</speed>
+ <speed>239</speed>
+ <speed>244</speed>
+ <speed>234</speed>
+ <speed>232</speed>
+ <speed>238</speed>
+ <speed>227</speed>
+ <speed>228</speed>
+ <speed>229</speed>
+ <speed>229</speed>
+ <speed>232</speed>
+ <speed>228</speed>
+ <speed>232</speed>
+ <speed>236</speed>
+ <num>10</num>
+ <num>20</num>
+ <num>30</num>
+ <num>40</num>
+ <num>50</num>
+ <num>60</num>
+ <num>70</num>
+ <num>80</num>
+ <num>90</num>
+ <num>100</num>
+ <num>110</num>
+ <num>120</num>
+ <num>130</num>
+ <num>140</num>
+ <num>150</num>
+</gx:Track></Placemark>
+<Placemark>
+ <name>E170</name>
+ <adflag>A</adflag>
+ <flightid>CPZ5695</flightid>
+ <styleUrl>#arrival</styleUrl>
+<gx:Track>
+ <altitudeMode>absolute</altitudeMode>
+ <extrude>1</extrude>
+ <when>2010-05-01T13:00:11-05</when>
+ <when>2010-05-01T13:00:15-05</when>
+ <when>2010-05-01T13:00:20-05</when>
+ <when>2010-05-01T13:00:25-05</when>
+ <when>2010-05-01T13:00:29-05</when>
+ <when>2010-05-01T13:00:34-05</when>
+ <when>2010-05-01T13:00:38-05</when>
+ <when>2010-05-01T13:00:43-05</when>
+ <when>2010-05-01T13:00:48-05</when>
+ <when>2010-05-01T13:00:52-05</when>
+ <when>2010-05-01T13:00:57-05</when>
+ <when>2010-05-01T13:01:00-05</when>
+ <gx:coord>-92.3689380245182 45.0389467469425 2804</gx:coord>
+ <gx:coord>-92.3759530819834 45.0380951007958 2773</gx:coord>
+ <gx:coord>-92.3831159633175 45.0369957486846 2712</gx:coord>
+ <gx:coord>-92.3901362714549 45.0355238496347 2651</gx:coord>
+ <gx:coord>-92.3970814910858 45.0339385808083 2621</gx:coord>
+ <gx:coord>-92.4043121546626 45.032585906621 2560</gx:coord>
+ <gx:coord>-92.4118367565321 45.0319048652958 2499</gx:coord>
+ <gx:coord>-92.419078934653 45.030875157485 2469</gx:coord>
+ <gx:coord>-92.4262095560369 45.0291153314744 2438</gx:coord>
+ <gx:coord>-92.4335237384463 45.0273941113051 2438</gx:coord>
+ <gx:coord>-92.4408178608932 45.0260076351757 2438</gx:coord>
+ <gx:coord>-92.4451575746228 45.0254275529773 2438</gx:coord>
+ <gx:angles>260 0 0</gx:angles>
+ <gx:angles>260 0 0</gx:angles>
+ <gx:angles>260 0 0</gx:angles>
+ <gx:angles>250 0 0</gx:angles>
+ <gx:angles>260 0 0</gx:angles>
+ <gx:angles>260 0 0</gx:angles>
+ <gx:angles>260 0 0</gx:angles>
+ <gx:angles>260 0 0</gx:angles>
+ <gx:angles>250 0 0</gx:angles>
+ <gx:angles>250 0 0</gx:angles>
+ <gx:angles>250 0 0</gx:angles>
+ <gx:angles>260 0 0</gx:angles>
+ <speed>277</speed>
+ <speed>288</speed>
+ <speed>283</speed>
+ <speed>291</speed>
+ <speed>283</speed>
+ <speed>284</speed>
+ <speed>298</speed>
+ <speed>288</speed>
+ <speed>288</speed>
+ <speed>278</speed>
+ <speed>283</speed>
+ <speed>288</speed>
+ <num>10</num>
+ <num>20</num>
+ <num>30</num>
+ <num>40</num>
+ <num>50</num>
+ <num>60</num>
+ <num>70</num>
+ <num>80</num>
+ <num>90</num>
+ <num>100</num>
+ <num>110</num>
+ <num>120</num>
+</gx:Track></Placemark>
+<Placemark>
+ <name>DC95</name>
+ <adflag>A</adflag>
+ <flightid>DAL2858</flightid>
+ <styleUrl>#arrival</styleUrl>
+<gx:Track>
+ <altitudeMode>absolute</altitudeMode>
+ <extrude>1</extrude>
+ <when>2010-05-01T13:00:00-05</when>
+ <when>2010-05-01T13:00:03-05</when>
+ <when>2010-05-01T13:00:07-05</when>
+ <when>2010-05-01T13:00:12-05</when>
+ <when>2010-05-01T13:00:17-05</when>
+ <when>2010-05-01T13:00:21-05</when>
+ <when>2010-05-01T13:00:26-05</when>
+ <when>2010-05-01T13:00:30-05</when>
+ <when>2010-05-01T13:00:35-05</when>
+ <when>2010-05-01T13:00:40-05</when>
+ <when>2010-05-01T13:00:44-05</when>
+ <when>2010-05-01T13:00:49-05</when>
+ <when>2010-05-01T13:00:54-05</when>
+ <when>2010-05-01T13:00:58-05</when>
+ <when>2010-05-01T13:01:00-05</when>
+ <gx:coord>-93.1962465696187 44.4584257162471 3078</gx:coord>
+ <gx:coord>-93.1954858158128 44.462643897726 3078</gx:coord>
+ <gx:coord>-93.1945524569257 44.4696206853623 3048</gx:coord>
+ <gx:coord>-93.1935347734104 44.4765680167011 3048</gx:coord>
+ <gx:coord>-93.1921548885013 44.4834366892852 3048</gx:coord>
+ <gx:coord>-93.1912787899895 44.4902740201102 3048</gx:coord>
+ <gx:coord>-93.190869393024 44.496999598511 3048</gx:coord>
+ <gx:coord>-93.190355669541 44.503701889363 3048</gx:coord>
+ <gx:coord>-93.1899042890233 44.510392533924 3048</gx:coord>
+ <gx:coord>-93.1894352972433 44.5171043633827 3048</gx:coord>
+ <gx:coord>-93.1887272976791 44.523838031578 3017</gx:coord>
+ <gx:coord>-93.1882343860587 44.5305421014878 2987</gx:coord>
+ <gx:coord>-93.1878483537445 44.5373007218153 2987</gx:coord>
+ <gx:coord>-93.187206305476 44.5440099500882 2956</gx:coord>
+ <gx:coord>-93.1870547021374 44.5466877366242 2956</gx:coord>
+ <gx:angles>10 0 0</gx:angles>
+ <gx:angles>10 0 0</gx:angles>
+ <gx:angles>10 0 0</gx:angles>
+ <gx:angles>10 0 0</gx:angles>
+ <gx:angles>10 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <gx:angles>0 0 0</gx:angles>
+ <speed>378</speed>
+ <speed>370</speed>
+ <speed>381</speed>
+ <speed>373</speed>
+ <speed>384</speed>
+ <speed>367</speed>
+ <speed>365</speed>
+ <speed>377</speed>
+ <speed>362</speed>
+ <speed>362</speed>
+ <speed>362</speed>
+ <speed>362</speed>
+ <speed>368</speed>
+ <speed>355</speed>
+ <speed>362</speed>
+ <num>10</num>
+ <num>20</num>
+ <num>30</num>
+ <num>40</num>
+ <num>50</num>
+ <num>60</num>
+ <num>70</num>
+ <num>80</num>
+ <num>90</num>
+ <num>100</num>
+ <num>110</num>
+ <num>120</num>
+ <num>130</num>
+ <num>140</num>
+ <num>150</num>
+</gx:Track></Placemark>
+<Placemark>
+ <name>B737</name>
+ <adflag>A</adflag>
+ <flightid>SWA1488</flightid>
+ <styleUrl>#arrival</styleUrl>
+<gx:Track>
+ <altitudeMode>absolute</altitudeMode>
+ <extrude>1</extrude>
+ <when>2010-05-01T13:00:00-05</when>
+ <when>2010-05-01T13:00:01-05</when>
+ <when>2010-05-01T13:00:06-05</when>
+ <when>2010-05-01T13:00:11-05</when>
+ <when>2010-05-01T13:00:15-05</when>
+ <when>2010-05-01T13:00:20-05</when>
+ <when>2010-05-01T13:00:24-05</when>
+ <when>2010-05-01T13:00:29-05</when>
+ <when>2010-05-01T13:00:34-05</when>
+ <when>2010-05-01T13:00:38-05</when>
+ <when>2010-05-01T13:00:43-05</when>
+ <when>2010-05-01T13:00:48-05</when>
+ <when>2010-05-01T13:00:52-05</when>
+ <when>2010-05-01T13:00:57-05</when>
+ <when>2010-05-01T13:01:00-05</when>
+ <gx:coord>-92.7436038977339 45.0176449723009 2438</gx:coord>
+ <gx:coord>-92.745419752639 45.0178405701636 2438</gx:coord>
+ <gx:coord>-92.7525586927583 45.0181852080204 2438</gx:coord>
+ <gx:coord>-92.7599978682742 45.0189437491361 2438</gx:coord>
+ <gx:coord>-92.7673964649616 45.0200176804669 2438</gx:coord>
+ <gx:coord>-92.7743047878147 45.0206512321095 2438</gx:coord>
+ <gx:coord>-92.7812211106102 45.0212438545962 2438</gx:coord>
+ <gx:coord>-92.7880905786106 45.0219352711124 2438</gx:coord>
+ <gx:coord>-92.7948110303679 45.0225135550872 2438</gx:coord>
+ <gx:coord>-92.8016256231407 45.0231539091809 2377</gx:coord>
+ <gx:coord>-92.808436321378 45.0237782407713 2316</gx:coord>
+ <gx:coord>-92.8153060032773 45.0245123996427 2255</gx:coord>
+ <gx:coord>-92.8220950756464 45.0250388052127 2194</gx:coord>
+ <gx:coord>-92.8289929014999 45.0256725515916 2164</gx:coord>
+ <gx:coord>-92.8342709686589 45.0263726025032 2118.25</gx:coord>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <gx:angles>280 0 0</gx:angles>
+ <speed>280</speed>
+ <speed>293</speed>
+ <speed>284</speed>
+ <speed>288</speed>
+ <speed>274</speed>
+ <speed>272</speed>
+ <speed>279</speed>
+ <speed>263</speed>
+ <speed>263</speed>
+ <speed>262</speed>
+ <speed>262</speed>
+ <speed>275</speed>
+ <speed>270</speed>
+ <speed>277</speed>
+ <speed>287</speed>
+ <num>10</num>
+ <num>20</num>
+ <num>30</num>
+ <num>40</num>
+ <num>50</num>
+ <num>60</num>
+ <num>70</num>
+ <num>80</num>
+ <num>90</num>
+ <num>100</num>
+ <num>110</num>
+ <num>120</num>
+ <num>130</num>
+ <num>140</num>
+ <num>150</num>
+</gx:Track></Placemark>
+<Placemark>
+ <name>A318</name>
+ <adflag>A</adflag>
+ <flightid>FFT106</flightid>
+ <styleUrl>#arrival</styleUrl>
+<gx:Track>
+ <altitudeMode>absolute</altitudeMode>
+ <extrude>1</extrude>
+ <when>2010-05-01T13:00:00-05</when>
+ <when>2010-05-01T13:00:05-05</when>
+ <when>2010-05-01T13:00:09-05</when>
+ <when>2010-05-01T13:00:14-05</when>
+ <when>2010-05-01T13:00:19-05</when>
+ <when>2010-05-01T13:00:23-05</when>
+ <when>2010-05-01T13:00:28-05</when>
+ <when>2010-05-01T13:00:33-05</when>
+ <when>2010-05-01T13:00:37-05</when>
+ <when>2010-05-01T13:00:42-05</when>
+ <when>2010-05-01T13:00:47-05</when>
+ <when>2010-05-01T13:00:51-05</when>
+ <when>2010-05-01T13:00:56-05</when>
+ <when>2010-05-01T13:01:00-05</when>
+ <gx:coord>-93.2974568508014 45.0687622602847 1432</gx:coord>
+ <gx:coord>-93.2934457905393 45.0660257042941 1371</gx:coord>
+ <gx:coord>-93.2902010482642 45.0627382200457 1341</gx:coord>
+ <gx:coord>-93.2880735868205 45.0592062737728 1280</gx:coord>
+ <gx:coord>-93.2866251180089 45.0556538417996 1280</gx:coord>
+ <gx:coord>-93.2855706436895 45.0521555770546 1249</gx:coord>
+ <gx:coord>-93.2848929213344 45.0486326683558 1249</gx:coord>
+ <gx:coord>-93.284149302237 45.0450445279501 1219</gx:coord>
+ <gx:coord>-93.2832681542582 45.0414770478452 1219</gx:coord>
+ <gx:coord>-93.2822163760078 45.0378266141909 1219</gx:coord>
+ <gx:coord>-93.2810695206555 45.0339762188888 1249</gx:coord>
+ <gx:coord>-93.2800852709943 45.0300242656845 1249</gx:coord>
+ <gx:coord>-93.2789451826991 45.026165428423 1249</gx:coord>
+ <gx:coord>-93.2776553627852 45.0222881273358 1219</gx:coord>
+ <gx:angles>140 0 0</gx:angles>
+ <gx:angles>150 0 0</gx:angles>
+ <gx:angles>150 0 0</gx:angles>
+ <gx:angles>160 0 0</gx:angles>
+ <gx:angles>170 0 0</gx:angles>
+ <gx:angles>170 0 0</gx:angles>
+ <gx:angles>170 0 0</gx:angles>
+ <gx:angles>170 0 0</gx:angles>
+ <gx:angles>170 0 0</gx:angles>
+ <gx:angles>170 0 0</gx:angles>
+ <gx:angles>170 0 0</gx:angles>
+ <gx:angles>170 0 0</gx:angles>
+ <gx:angles>170 0 0</gx:angles>
+ <gx:angles>170 0 0</gx:angles>
+ <speed>212</speed>
+ <speed>205</speed>
+ <speed>208</speed>
+ <speed>203</speed>
+ <speed>201</speed>
+ <speed>196</speed>
+ <speed>196</speed>
+ <speed>197</speed>
+ <speed>202</speed>
+ <speed>205</speed>
+ <speed>216</speed>
+ <speed>215</speed>
+ <speed>222</speed>
+ <speed>231</speed>
+ <num>10</num>
+ <num>20</num>
+ <num>30</num>
+ <num>40</num>
+ <num>50</num>
+ <num>60</num>
+ <num>70</num>
+ <num>80</num>
+ <num>90</num>
+ <num>100</num>
+ <num>110</num>
+ <num>120</num>
+ <num>130</num>
+ <num>140</num>
+</gx:Track></Placemark>
+<Placemark>
+ <name></name>
+ <adflag>A</adflag>
+ <flightid></flightid>
+ <styleUrl>#arrival</styleUrl>
+<gx:Track>
+ <altitudeMode>absolute</altitudeMode>
+ <extrude>1</extrude>
+ <when>2010-05-01T13:00:00-05</when>
+ <when>2010-05-01T13:00:05-05</when>
+ <when>2010-05-01T13:00:10-05</when>
+ <when>2010-05-01T13:00:14-05</when>
+ <when>2010-05-01T13:00:24-05</when>
+ <when>2010-05-01T13:00:33-05</when>
+ <when>2010-05-01T13:00:37-05</when>
+ <when>2010-05-01T13:00:42-05</when>
+ <when>2010-05-01T13:00:47-05</when>
+ <when>2010-05-01T13:00:51-05</when>
+ <when>2010-05-01T13:00:56-05</when>
+ <when>2010-05-01T13:01:00-05</when>
+ <gx:coord>-93.5287325331323 45.3502794027397 731</gx:coord>
+ <gx:coord>-93.5305174337715 45.3463816209029 731</gx:coord>
+ <gx:coord>-93.532323089283 45.3433065196778 731</gx:coord>
+ <gx:coord>-93.5344374505075 45.3397938806867 731</gx:coord>
+ <gx:coord>-93.5365879669744 45.3355152994798 731</gx:coord>
+ <gx:coord>-93.538455345577 45.3317693717468 731</gx:coord>
+ <gx:coord>-93.5402440337749 45.3288175816964 731</gx:coord>
+ <gx:coord>-93.5420054353005 45.3261482119682 701</gx:coord>
+ <gx:coord>-93.5437972875724 45.3236486426325 701</gx:coord>
+ <gx:coord>-93.5449025453586 45.3213557809437 670</gx:coord>
+ <gx:coord>-93.5460939368394 45.3190373998605 670</gx:coord>
+ <gx:coord>-93.5479457332637 45.3165177805485 670</gx:coord>
+ <gx:angles>200 0 0</gx:angles>
+ <gx:angles>200 0 0</gx:angles>
+ <gx:angles>200 0 0</gx:angles>
+ <gx:angles>200 0 0</gx:angles>
+ <gx:angles>200 0 0</gx:angles>
+ <gx:angles>200 0 0</gx:angles>
+ <gx:angles>200 0 0</gx:angles>
+ <gx:angles>200 0 0</gx:angles>
+ <gx:angles>200 0 0</gx:angles>
+ <gx:angles>200 0 0</gx:angles>
+ <gx:angles>200 0 0</gx:angles>
+ <gx:angles>200 0 0</gx:angles>
+ <speed>202</speed>
+ <speed>180</speed>
+ <speed>166</speed>
+ <speed>171</speed>
+ <speed>162</speed>
+ <speed>157</speed>
+ <speed>143</speed>
+ <speed>145</speed>
+ <speed>156</speed>
+ <speed>147</speed>
+ <speed>147</speed>
+ <speed>150</speed>
+ <num>10</num>
+ <num>20</num>
+ <num>30</num>
+ <num>40</num>
+ <num>50</num>
+ <num>60</num>
+ <num>70</num>
+ <num>80</num>
+ <num>90</num>
+ <num>100</num>
+ <num>110</num>
+ <num>120</num>
+</gx:Track></Placemark>
+<Placemark>
+ <name>CRJ2</name>
+ <adflag>A</adflag>
+ <flightid>SKW4805</flightid>
+ <styleUrl>#arrival</styleUrl>
+</Placemark>
+<Placemark>
+ <name>CRJ2</name>
+ <adflag>A</adflag>
+ <flightid>FLG4092</flightid>
+ <styleUrl>#arrival</styleUrl>
+<gx:Track>
+ <altitudeMode>absolute</altitudeMode>
+ <extrude>1</extrude>
+ <when>2010-05-01T13:00:00-05</when>
+ <when>2010-05-01T13:00:01-05</when>
+ <when>2010-05-01T13:00:06-05</when>
+ <when>2010-05-01T13:00:10-05</when>
+ <when>2010-05-01T13:00:15-05</when>
+ <when>2010-05-01T13:00:20-05</when>
+ <when>2010-05-01T13:00:24-05</when>
+ <when>2010-05-01T13:00:29-05</when>
+ <when>2010-05-01T13:00:34-05</when>
+ <when>2010-05-01T13:00:38-05</when>
+ <when>2010-05-01T13:00:44-05</when>
+ <when>2010-05-01T13:00:49-05</when>
+ <when>2010-05-01T13:00:54-05</when>
+ <gx:coord>-93.1836067392297 44.9110362339843 432.2</gx:coord>
+ <gx:coord>-93.1841170614853 44.910663862492 426</gx:coord>
+ <gx:coord>-93.1867007876887 44.908842129317 426</gx:coord>
+ <gx:coord>-93.1893728799637 44.9069842219291 396</gx:coord>
+ <gx:coord>-93.1919479660705 44.9051548529609 365</gx:coord>
+ <gx:coord>-93.1944798212107 44.9032897679148 365</gx:coord>
+ <gx:coord>-93.197164452306 44.9014210542153 335</gx:coord>
+ <gx:coord>-93.1996234874761 44.8995719817206 335</gx:coord>
+ <gx:coord>-93.2021701211426 44.8975674983317 304</gx:coord>
+ <gx:coord>-93.2050345971567 44.8955942303701 304</gx:coord>
+ <gx:coord>-93.2075455037487 44.8938556558558 304</gx:coord>
+ <gx:coord>-93.2100820128846 44.8918590963212 304</gx:coord>
+ <gx:coord>-93.2127524858241 44.89000250047 256</gx:coord>
+ <gx:angles>220 0 0</gx:angles>
+ <gx:angles>230 0 0</gx:angles>
+ <gx:angles>230 0 0</gx:angles>
+ <gx:angles>230 0 0</gx:angles>
+ <gx:angles>230 0 0</gx:angles>
+ <gx:angles>220 0 0</gx:angles>
+ <gx:angles>220 0 0</gx:angles>
+ <gx:angles>220 0 0</gx:angles>
+ <gx:angles>220 0 0</gx:angles>
+ <gx:angles>220 0 0</gx:angles>
+ <gx:angles>220 0 0</gx:angles>
+ <gx:angles>220 0 0</gx:angles>
+ <gx:angles>220 0 0</gx:angles>
+ <speed>141</speed>
+ <speed>138</speed>
+ <speed>136</speed>
+ <speed>141</speed>
+ <speed>141</speed>
+ <speed>142</speed>
+ <speed>143</speed>
+ <speed>139</speed>
+ <speed>140</speed>
+ <speed>134</speed>
+ <speed>136</speed>
+ <speed>136</speed>
+ <speed>123</speed>
+ <num>10</num>
+ <num>20</num>
+ <num>30</num>
+ <num>40</num>
+ <num>50</num>
+ <num>60</num>
+ <num>70</num>
+ <num>80</num>
+ <num>90</num>
+ <num>100</num>
+ <num>110</num>
+ <num>120</num>
+ <num>130</num>
+</gx:Track></Placemark>
+<Placemark>
+ <name>E170</name>
+ <adflag>A</adflag>
+ <flightid>CPZ5667</flightid>
+ <styleUrl>#arrival</styleUrl>
+<gx:Track>
+ <altitudeMode>absolute</altitudeMode>
+ <extrude>1</extrude>
+ <when>2010-05-01T13:00:00-05</when>
+ <when>2010-05-01T13:00:01-05</when>
+ <when>2010-05-01T13:00:06-05</when>
+ <when>2010-05-01T13:00:10-05</when>
+ <when>2010-05-01T13:00:15-05</when>
+ <when>2010-05-01T13:00:20-05</when>
+ <when>2010-05-01T13:00:24-05</when>
+ <when>2010-05-01T13:00:29-05</when>
+ <when>2010-05-01T13:00:34-05</when>
+ <when>2010-05-01T13:00:38-05</when>
+ <when>2010-05-01T13:00:43-05</when>
+ <when>2010-05-01T13:00:47-05</when>
+ <when>2010-05-01T13:00:52-05</when>
+ <when>2010-05-01T13:00:57-05</when>
+ <when>2010-05-01T13:01:00-05</when>
+ <gx:coord>-92.9496238812799 45.0117549407746 1438.2</gx:coord>
+ <gx:coord>-92.9507065768732 45.0116702587604 1432</gx:coord>
+ <gx:coord>-92.9563739191926 45.0116271226204 1432</gx:coord>
+ <gx:coord>-92.9620225732021 45.0115639668496 1432</gx:coord>
+ <gx:coord>-92.9673675587699 45.0113432900049 1402</gx:coord>
+ <gx:coord>-92.9725115032188 45.0111442254373 1402</gx:coord>
+ <gx:coord>-92.9778810091229 45.0112050922639 1371</gx:coord>
+ <gx:coord>-92.9832227114571 45.0112143826731 1371</gx:coord>
+ <gx:coord>-92.9884546803523 45.0110418166788 1341</gx:coord>
+ <gx:coord>-92.9938268606229 45.0109652220709 1341</gx:coord>
+ <gx:coord>-92.9991151069756 45.010802144845 1310</gx:coord>
+ <gx:coord>-93.0041467584036 45.0105516668541 1310</gx:coord>
+ <gx:coord>-93.0090742909164 45.0105233046799 1280</gx:coord>
+ <gx:coord>-93.0139435770527 45.0106265340001 1280</gx:coord>
+ <gx:coord>-93.0174882575928 45.0106328449121 1256.75</gx:coord>
+ <gx:angles>270 0 0</gx:angles>
+ <gx:angles>270 0 0</gx:angles>
+ <gx:angles>270 0 0</gx:angles>
+ <gx:angles>270 0 0</gx:angles>
+ <gx:angles>270 0 0</gx:angles>
+ <gx:angles>270 0 0</gx:angles>
+ <gx:angles>270 0 0</gx:angles>
+ <gx:angles>270 0 0</gx:angles>
+ <gx:angles>270 0 0</gx:angles>
+ <gx:angles>270 0 0</gx:angles>
+ <gx:angles>270 0 0</gx:angles>
+ <gx:angles>270 0 0</gx:angles>
+ <gx:angles>270 0 0</gx:angles>
+ <gx:angles>270 0 0</gx:angles>
+ <gx:angles>270 0 0</gx:angles>
+ <speed>214</speed>
+ <speed>207</speed>
+ <speed>202</speed>
+ <speed>208</speed>
+ <speed>207</speed>
+ <speed>205</speed>
+ <speed>203</speed>
+ <speed>202</speed>
+ <speed>209</speed>
+ <speed>199</speed>
+ <speed>196</speed>
+ <speed>200</speed>
+ <speed>193</speed>
+ <speed>194</speed>
+ <speed>185</speed>
+ <num>10</num>
+ <num>20</num>
+ <num>30</num>
+ <num>40</num>
+ <num>50</num>
+ <num>60</num>
+ <num>70</num>
+ <num>80</num>
+ <num>90</num>
+ <num>100</num>
+ <num>110</num>
+ <num>120</num>
+ <num>130</num>
+ <num>140</num>
+ <num>150</num>
+</gx:Track></Placemark>
+</Folder>
+<Folder>
+ <name>Departures</name>
+</Folder>
+<Folder>
+ <name>Overflights</name>
+</Folder>
+</Document>
+</kml>
+--></div>
+
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/OGCExceptionReport.html b/misc/openlayers/tests/Format/OGCExceptionReport.html
new file mode 100644
index 0000000..7846f94
--- /dev/null
+++ b/misc/openlayers/tests/Format/OGCExceptionReport.html
@@ -0,0 +1,100 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read_exception(t) {
+
+ t.plan(21);
+
+ // OCG WMS 1.3.0 exceptions
+ var text = '<?xml version="1.0" encoding="UTF-8"?> ' +
+'<ServiceExceptionReport version="1.3.0" xmlns="http://www.opengis.net/ogc"' +
+' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
+' xsi:schemaLocation="http://www.opengis.net/ogc' +
+' http://schemas.opengis.net/wms/1.3.0/exceptions_1_3_0.xsd">' +
+' <ServiceException> Plain text message about an error. </ServiceException>' +
+' <ServiceException code="InvalidUpdateSequence"> Another error message, this one with a service exception code supplied. </ServiceException>' +
+' <ServiceException>' +
+' <![CDATA[ Error in module <foo.c>, line 42' +
+'A message that includes angle brackets in text must be enclosed in a Character Data Section as in this example. All XML-like markup is ignored except for this sequence of three closing characters:' +
+']]>' +
+' </ServiceException>' +
+' <ServiceException>' +
+' <![CDATA[ <Module>foo.c</Module> <Error>An error occurred</Error> <Explanation>Similarly, actual XML can be enclosed in a CDATA section. A generic parser will ignore that XML, but application-specific software may choose to process it.</Explanation> ]]>' +
+' </ServiceException>' +
+'</ServiceExceptionReport>';
+
+ var parser = new OpenLayers.Format.OGCExceptionReport();
+ var result = parser.read(text);
+
+ var exceptions = result.exceptionReport.exceptions;
+
+ var testWMS = function(exceptions) {
+ t.eq(exceptions.length, 4, "We expect 4 exception messages");
+ t.eq(exceptions[0].text, " Plain text message about an error. ", "First error message correctly parsed");
+ t.eq(exceptions[1].code, "InvalidUpdateSequence", "Code of second error message correctly parsed");
+ t.eq(exceptions[1].text, " Another error message, this one with a service exception code supplied. ", "Text of second error message correctly parsed");
+ t.eq(OpenLayers.String.trim(exceptions[2].text), "Error in module <foo.c>, line 42A message that includes angle brackets in text must be enclosed in a Character Data Section as in this example. All XML-like markup is ignored except for this sequence of three closing characters:", "Third message correctly parsed");
+ t.eq(OpenLayers.String.trim(exceptions[3].text), "<Module>foo.c</Module> <Error>An error occurred</Error> <Explanation>Similarly, actual XML can be enclosed in a CDATA section. A generic parser will ignore that XML, but application-specific software may choose to process it.</Explanation>", "Fourth message correctly parsed");
+ };
+
+ testWMS(exceptions);
+
+ // OGC WMS 1.1.1 exceptions
+ text = '<?xml version="1.0" encoding="UTF-8" standalone="no" ?> <!DOCTYPE ServiceExceptionReport SYSTEM "http://schemas.opengis.net/wms/1.1.1/WMS_exception_1_1_1.dtd"> ' +
+'<ServiceExceptionReport version="1.1.1">' +
+' <ServiceException> Plain text message about an error. </ServiceException>' +
+' <ServiceException code="InvalidUpdateSequence"> Another error message, this one with a service exception code supplied. </ServiceException>' +
+' <ServiceException>' +
+' <![CDATA[ Error in module <foo.c>, line 42' +
+'A message that includes angle brackets in text must be enclosed in a Character Data Section as in this example. All XML-like markup is ignored except for this sequence of three closing characters:' +
+']]>' +
+' </ServiceException>' +
+' <ServiceException>' +
+' <![CDATA[ <Module>foo.c</Module> <Error>An error occurred</Error> <Explanation>Similarly, actual XML can be enclosed in a CDATA section. A generic parser will ignore that XML, but application-specific software may choose to process it.</Explanation> ]]>' +
+' </ServiceException>' +
+'</ServiceExceptionReport>';
+ result = parser.read(text);
+ exceptions = result.exceptionReport.exceptions;
+ testWMS(exceptions);
+
+ // OGC WFS 1.0.0 exceptions
+ text = '<?xml version="1.0" ?> ' +
+'<ServiceExceptionReport version="1.2.0" xmlns="http://www.opengis.net/ogc"' +
+' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
+' xsi:schemaLocation="http://www.opengis.net/ogc http://schemas.opengis.net/wfs/1.0.0/OGC-exception.xsd">' +
+' <ServiceException code="999" locator="INSERT STMT 01"> parse error: missing closing tag for element WKB_GEOM </ServiceException>' +
+'</ServiceExceptionReport>';
+ result = parser.read(text);
+ t.eq(result.exceptionReport.exceptions[0].code, "999", "code parsed correctly");
+ t.eq(result.exceptionReport.exceptions[0].locator, "INSERT STMT 01", "locator parsed correctly");
+ t.eq(result.exceptionReport.exceptions[0].text, " parse error: missing closing tag for element WKB_GEOM ", "error text parsed correctly");
+
+ // OGC WFS 1.1.0 exceptions that use OWSCommon 1.0
+ text = '<?xml version="1.0" encoding="UTF-8"?>' +
+'<ows:ExceptionReport language="en" version="1.0.0"' +
+' xsi:schemaLocation="http://www.opengis.net/ows http://schemas.opengis.net/ows/1.0.0/owsExceptionReport.xsd"' +
+' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ows="http://www.opengis.net/ows">' +
+' <ows:Exception locator="foo" exceptionCode="InvalidParameterValue">' +
+' <ows:ExceptionText>Update error: Error occured updating features</ows:ExceptionText>' +
+' <ows:ExceptionText>Second exception line</ows:ExceptionText>' +
+' </ows:Exception>' +
+'</ows:ExceptionReport>';
+
+ var result = parser.read(text);
+ var report = result.exceptionReport;
+ t.eq(report.version, "1.0.0", "Version parsed correctly");
+ t.eq(report.language, "en", "Language parsed correctly");
+ var exception = report.exceptions[0];
+ t.eq(exception.code, "InvalidParameterValue", "exceptionCode properly parsed");
+ t.eq(exception.locator, "foo", "locator properly parsed");
+ t.eq(exception.texts[0], "Update error: Error occured updating features", "ExceptionText correctly parsed");
+ t.eq(exception.texts[1], "Second exception line", "Second ExceptionText correctly parsed");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/OSM.html b/misc/openlayers/tests/Format/OSM.html
new file mode 100644
index 0000000..6ceb316
--- /dev/null
+++ b/misc/openlayers/tests/Format/OSM.html
@@ -0,0 +1,115 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script src="../data/osm.js"></script>
+ <script type="text/javascript">
+
+ function test_Format_OSM_constructor(t) {
+ t.plan(5);
+
+ var options = {'foo': 'bar'};
+ var format = new OpenLayers.Format.OSM(options);
+ t.ok(format instanceof OpenLayers.Format.OSM,
+ "new OpenLayers.Format.OSM returns object" );
+ t.eq(format.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a write function");
+ t.eq(format.externalProjection.getCode(), "EPSG:4326",
+ "default external projection is EPSG:4326");
+ }
+ function test_Format_OSM_node(t) {
+ t.plan(4);
+ var f = new OpenLayers.Format.OSM();
+ var features = f.read(osm_test_data['node']);
+ var feat = features[0];
+ t.eq(feat.attributes, {}, "attributes is empty");
+ t.eq(feat.osm_id, 200545, "internal osm_id property set correctly");
+ t.eq(feat.geometry.x, -1.8166417, "lon is correct");
+ t.eq(feat.geometry.y, 52.5503033, "lat is correct");
+ }
+ function test_Format_OSM_node_with_tags(t) {
+ t.plan(5);
+ var f = new OpenLayers.Format.OSM();
+ var features = f.read(osm_test_data['node_with_tags']);
+ var feat = features[0];
+ t.eq(feat.attributes, {'a':'b'}, "attributes match");
+ t.eq(feat.osm_id, 200545, "internal osm_id property set correctly");
+ t.eq(feat.fid, "node.200545", "OSM-based FID set correctly.");
+ t.eq(feat.geometry.x, -1.8166417, "lon is correct");
+ t.eq(feat.geometry.y, 52.5503033, "lat is correct");
+ }
+ function test_Format_OSM_way(t) {
+ t.plan(8);
+ var f = new OpenLayers.Format.OSM();
+ var features = f.read(osm_test_data['way']);
+ t.eq(features.length, 1, "One feature");
+ var feat = features[0];
+ t.eq(feat.osm_id, 4685537, "OSM ID set correctly.");
+ t.eq(feat.fid, "way.4685537", "OSM-based FID set correctly.");
+ t.eq(feat.geometry.CLASS_NAME, "OpenLayers.Geometry.Polygon", "returned as polygon");
+ t.eq(feat.geometry.components[0].components.length, 11, "Correct number of components");
+ t.eq(feat.geometry.components[0].components[0].osm_id, 29783472, "OSM ID set on components");
+ t.eq(feat.geometry.toString(), "POLYGON((-1.8164007 52.5501836,-1.8170311 52.5506035,-1.8164092 52.5509559,-1.8169385 52.5513103,-1.8159626 52.5517893,-1.8145067 52.5518461,-1.8143197 52.5511883,-1.8141177 52.5506446,-1.8151451 52.5501275,-1.8157703 52.5505521,-1.8164007 52.5501836))", "WKT of feature is correct");
+ t.eq(feat.attributes.landuse, "school", "landuse attribute correct");
+ }
+
+ function test_Format_OSM_node_way(t) {
+ t.plan(5)
+ var f = new OpenLayers.Format.OSM();
+ var features = f.read(osm_test_data['node_way']);
+ t.eq(features.length, 1, "One feature");
+ var feat = features[0];
+ t.eq(feat.osm_id, 21329267, "OSM ID set correctly");
+ t.eq(feat.attributes.highway, "unclassified", "highway attribute is correct.");
+ t.eq(feat.geometry.CLASS_NAME, "OpenLayers.Geometry.LineString", "returned as linestring");
+ t.eq(feat.geometry.components.length, 12, "correct number of segments");
+ }
+
+ function test_Format_OSM_node_way_checkTags(t) {
+ t.plan(9)
+ var f = new OpenLayers.Format.OSM({'checkTags': true});
+ var features = f.read(osm_test_data['node_way']);
+ t.eq(features.length, 3, "multiple features");
+
+ var feat = features[1];
+ t.eq(feat.geometry.CLASS_NAME, "OpenLayers.Geometry.Point", "point class");
+ t.ok(feat.attributes != {}, "feature has attributes");
+
+ var feat = features[2];
+ t.eq(feat.geometry.CLASS_NAME, "OpenLayers.Geometry.Point", "point class");
+ t.ok(feat.attributes != {}, "feature has attributes");
+
+ feat = features[0];
+ t.eq(feat.osm_id, 21329267, "OSM ID set correctly");
+ t.eq(feat.attributes.highway, "unclassified", "highway attribute is correct.");
+ t.eq(feat.geometry.CLASS_NAME, "OpenLayers.Geometry.LineString", "returned as linestring");
+ t.eq(feat.geometry.components.length, 12, "correct number of segments");
+ }
+
+ function test_Format_OSM_serialize(t) {
+ t.plan(4);
+ var f = new OpenLayers.Format.OSM({'checkTags': true});
+ for (var key in osm_serialized_data) {
+ var input = f.read(osm_test_data[key]);
+ var output = f.write(input);
+ output = output.replace(/<\?[^>]*\?>/, '');
+ t.eq(output, osm_serialized_data[key], key + " serialized correctly");
+ }
+ }
+ function test_Format_OSM_write_reproject(t) {
+ t.plan(1);
+ var f = new OpenLayers.Format.OSM({'internalProjection': new OpenLayers.Projection("EPSG:900913")});
+ var feat = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(100000, 100000)
+ );
+ var data = f.write([feat]);
+ var f = new OpenLayers.Format.OSM();
+ var features = f.read(data);
+
+ t.eq(OpenLayers.Util.toFloat(features[0].geometry.x, 3), .898, "exported to lonlat and re-read as lonlat correctly")
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/OWSCommon/v1_0_0.html b/misc/openlayers/tests/Format/OWSCommon/v1_0_0.html
new file mode 100644
index 0000000..9d255b2
--- /dev/null
+++ b/misc/openlayers/tests/Format/OWSCommon/v1_0_0.html
@@ -0,0 +1,34 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read_exception(t) {
+ t.plan(6);
+ var text = '<?xml version="1.0" encoding="UTF-8"?>' +
+'<ows:ExceptionReport language="en" version="1.0.0"' +
+' xsi:schemaLocation="http://www.opengis.net/ows http://schemas.opengis.net/ows/1.0.0/owsExceptionReport.xsd"' +
+' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ows="http://www.opengis.net/ows">' +
+' <ows:Exception locator="foo" exceptionCode="InvalidParameterValue">' +
+' <ows:ExceptionText>Update error: Error occured updating features</ows:ExceptionText>' +
+' <ows:ExceptionText>Second exception line</ows:ExceptionText>' +
+' </ows:Exception>' +
+'</ows:ExceptionReport>';
+
+ var format = new OpenLayers.Format.OWSCommon();
+ var result = format.read(text);
+ var report = result.exceptionReport;
+ t.eq(report.version, "1.0.0", "Version parsed correctly");
+ t.eq(report.language, "en", "Language parsed correctly");
+ var exception = report.exceptions[0];
+ t.eq(exception.code, "InvalidParameterValue", "exceptionCode properly parsed");
+ t.eq(exception.locator, "foo", "locator properly parsed");
+ t.eq(exception.texts[0], "Update error: Error occured updating features", "ExceptionText correctly parsed");
+ t.eq(exception.texts[1], "Second exception line", "Second ExceptionText correctly parsed");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/OWSCommon/v1_1_0.html b/misc/openlayers/tests/Format/OWSCommon/v1_1_0.html
new file mode 100644
index 0000000..1cdf7ee
--- /dev/null
+++ b/misc/openlayers/tests/Format/OWSCommon/v1_1_0.html
@@ -0,0 +1,34 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read_exception(t) {
+ t.plan(6);
+ var text = '<?xml version="1.0" encoding="UTF-8"?>' +
+'<ows:ExceptionReport xml:lang="en" version="1.1.0"' +
+' xsi:schemaLocation="http://www.opengis.net/ows http://schemas.opengis.net/ows/1.1.0/owsExceptionReport.xsd"' +
+' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ows="http://www.opengis.net/ows/1.1">' +
+' <ows:Exception locator="foo" exceptionCode="InvalidParameterValue">' +
+' <ows:ExceptionText>Update error: Error occured updating features</ows:ExceptionText>' +
+' <ows:ExceptionText>Second exception line</ows:ExceptionText>' +
+' </ows:Exception>' +
+'</ows:ExceptionReport>';
+
+ var format = new OpenLayers.Format.OWSCommon();
+ var result = format.read(text);
+ var report = result.exceptionReport;
+ t.eq(report.version, "1.1.0", "Version parsed correctly");
+ t.eq(report.language, "en", "Language parsed correctly");
+ var exception = report.exceptions[0];
+ t.eq(exception.code, "InvalidParameterValue", "exceptionCode properly parsed");
+ t.eq(exception.locator, "foo", "locator properly parsed");
+ t.eq(exception.texts[0], "Update error: Error occured updating features", "ExceptionText correctly parsed");
+ t.eq(exception.texts[1], "Second exception line", "Second ExceptionText correctly parsed");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/OWSContext/v0_3_1.html b/misc/openlayers/tests/Format/OWSContext/v0_3_1.html
new file mode 100644
index 0000000..54b63b3
--- /dev/null
+++ b/misc/openlayers/tests/Format/OWSContext/v0_3_1.html
@@ -0,0 +1,278 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read_wmswfs(t) {
+ t.plan(17);
+ // taken from http://www.ogcnetwork.net/schemas/owc/0.3.1/context_nested.xml
+ // adapted: add an extra slash (roads/railways) in the Title of the WMS layer to test nesting
+ var text = '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<OWSContext version="0.3.1" id="ows-context-ex-1-v3" xmlns="http://www.opengis.net/ows-context"' +
+ ' xmlns:gml="http://www.opengis.net/gml" xmlns:kml="http://www.opengis.net/kml/2.2"' +
+ ' xmlns:ogc="http://www.opengis.net/ogc" xmlns:ows="http://www.opengis.net/ows"' +
+ ' xmlns:sld="http://www.opengis.net/sld" xmlns:xlink="http://www.w3.org/1999/xlink"' +
+ ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
+ ' xsi:schemaLocation="http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.1/owsContext.xsd">' +
+ ' <General>' +
+ ' <ows:BoundingBox crs="EPSG:4326">' +
+ ' <ows:LowerCorner>-117 32</ows:LowerCorner>' +
+ ' <ows:UpperCorner>-116 33</ows:UpperCorner>' +
+ ' </ows:BoundingBox>' +
+ ' <ows:Title>OWS Context version 0.3.1 showing nested layers</ows:Title>' +
+ ' </General>' +
+ ' <ResourceList>' +
+ ' <!-- WMS Example -->' +
+ ' <Layer name="topp:major_roads" queryable="1" hidden="1">' +
+ ' <ows:Title>Tiger 2005fe major roads/railways</ows:Title>' +
+ ' <ows:OutputFormat>image/png</ows:OutputFormat>' +
+ ' <Server service="urn:ogc:serviceType:WMS" version="1.1.1">' +
+ ' <OnlineResource' +
+ ' xlink:href="http://sigma.openplans.org:8080/geoserver/wms?SERVICE=WMS"/>' +
+ ' </Server>' +
+ ' <!-- WFS Example -->' +
+ ' <Layer name="topp:gnis_pop" hidden="0">' +
+ ' <ows:Title>GNIS Population</ows:Title>' +
+ ' <Server service="urn:ogc:serviceType:WFS" version="1.0.0">' +
+ ' <OnlineResource xlink:href="geoserver/wfs?"/>' +
+ ' </Server>' +
+ ' </Layer>' +
+ ' </Layer>' +
+ ' </ResourceList>' +
+ '</OWSContext>';
+ var parser = new OpenLayers.Format.OWSContext();
+ var map = new OpenLayers.Map('map', {allOverlays: true, fractionalZoom: true});
+ var context = parser.read(text, {map: map});
+ t.eq(context.layers.length, 2, "2 layers parsed from OWSContext document");
+ t.eq(context.layers[1].metadata.nestingPath[0], "Tiger 2005fe major roads/railways", "Nesting path correctly set");
+ t.ok(context.layers[0] instanceof OpenLayers.Layer.WMS, "First layer is a WMS layer");
+ t.ok(context.layers[1] instanceof OpenLayers.Layer.Vector, "Second layer is a vector layer");
+ t.eq(context.layers[0].params.LAYERS, "topp:major_roads", "WMS layer name correctly read");
+ t.eq(context.layers[0].params.FORMAT, "image/png", "WMS format correctly read");
+ t.eq(context.layers[0].url, "http://sigma.openplans.org:8080/geoserver/wms?SERVICE=WMS", "Layer url correctly read");
+ t.eq(context.layers[0].getVisibility(), false, "WMS Layer is hidden");
+ t.ok(context.layers[0].queryable, "WMS layer is queryable");
+ t.eq(context.layers[0].name, "Tiger 2005fe major roads/railways", "Title correctly set");
+ t.ok(context.layers[1].protocol instanceof OpenLayers.Protocol.WFS.v1_0_0, "Vector layer configured with a WFS Protocol");
+ t.eq(context.layers[1].protocol.url, "geoserver/wfs?", "WFS url set correctly");
+ t.ok(context.layers[1].strategies[0] instanceof OpenLayers.Strategy.BBOX, "BBOX strategy configured correctly");
+ t.eq(context.layers[1].name, "GNIS Population", "Title of second layer correctly set");
+ t.eq(context.layers[1].getVisibility(), true, "Second layer is visible");
+ map.zoomToExtent(new OpenLayers.Bounds(-117, 32, -116, 33));
+ var owc = parser.write(map, {id: 'ows-context-ex-1-v3', title: 'OWS Context version 0.3.1 showing nested layers'});
+ t.xml_eq(text, owc, "Can we roundtrip this nested OWSContext with a WMS and WFS layer?");
+ t.eq(context.layers[1].metadata.nestingPath[0], "Tiger 2005fe major roads/railways", "Nesting path is preserved even after calling write");
+ }
+
+ function test_write_wmswfs(t) {
+ t.plan(1);
+ var lon = 5;
+ var lat = 40;
+ var zoom = 5;
+ var map = new OpenLayers.Map( 'map' );
+ var layer = new OpenLayers.Layer.WMS(
+ "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'},
+ {singleTile: true}
+ );
+ var wfs = new OpenLayers.Layer.Vector("myroads", {
+ strategies: [new OpenLayers.Strategy.BBOX()],
+ protocol: new OpenLayers.Protocol.WFS({
+ url: "foo/wfs?",
+ featureType: "roads",
+ featureNS: "http://foo/myns"
+ })
+ });
+ map.addLayers([layer, wfs]);
+ map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+
+ var owc = new OpenLayers.Format.OWSContext();
+ var output = owc.write(map, {id: 'foo'});
+ var expected = '<OWSContext xmlns="http://www.opengis.net/ows-context" version="0.3.1" id="foo" xsi:schemaLocation="http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.1/owsContext.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><General><ows:BoundingBox xmlns:ows="http://www.opengis.net/ows" crs="EPSG:4326"><ows:LowerCorner>-5.986328125 29.013671875</ows:LowerCorner><ows:UpperCorner>15.986328125 50.986328125</ows:UpperCorner></ows:BoundingBox><ows:Title xmlns:ows="http://www.opengis.net/ows">OpenLayers OWSContext</ows:Title></General><ResourceList><Layer name="basic" queryable="0" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">OpenLayers WMS</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/jpeg</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://labs.metacarta.com/wms/vmap0" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server></Layer><Layer name="feature:roads" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">myroads</ows:Title><Server version="1.0.0" service="urn:ogc:serviceType:WFS"><OnlineResource xlink:href="foo/wfs?" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server></Layer></ResourceList></OWSContext>';
+ t.xml_eq(output, expected, "OWSContext with a WMS and a WFS layer generated correctly");
+ }
+
+ function test_write_wmsinlinegml(t) {
+ t.plan(1);
+ var lon = 5;
+ var lat = 40;
+ var zoom = 5;
+ var map = new OpenLayers.Map( 'map' );
+ var layer = new OpenLayers.Layer.WMS(
+ "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'},
+ {singleTile: true}
+ );
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+
+ var vector = new OpenLayers.Layer.Vector();
+ map.addLayer(vector);
+ var feature1 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,1));
+ var feature2 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,0));
+ vector.addFeatures(feature1);
+ vector.addFeatures(feature2);
+ var owc = new OpenLayers.Format.OWSContext();
+ var output = owc.write(map, {id: 'foo'});
+ var expected = '<OWSContext xmlns="http://www.opengis.net/ows-context" version="0.3.1" id="foo" xsi:schemaLocation="http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.1/owsContext.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><General><ows:BoundingBox xmlns:ows="http://www.opengis.net/ows" crs="EPSG:4326"><ows:LowerCorner>-5.986328125 29.013671875</ows:LowerCorner><ows:UpperCorner>15.986328125 50.986328125</ows:UpperCorner></ows:BoundingBox><ows:Title xmlns:ows="http://www.opengis.net/ows">OpenLayers OWSContext</ows:Title></General><ResourceList><Layer name="basic" queryable="0" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">OpenLayers WMS</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/jpeg</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://labs.metacarta.com/wms/vmap0" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server></Layer><Layer name="vector" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows"/><InlineGeometry><gml:boundedBy xmlns:gml="http://www.opengis.net/gml"><gml:Box><gml:coordinates decimal="." cs="," ts=" ">0,0 1,1</gml:coordinates></gml:Box></gml:boundedBy><gml:featureMember xmlns:gml="http://www.opengis.net/gml"><feature:vector xmlns:feature="http://mapserver.gis.umn.edu/mapserver"><feature:geometry><gml:Point><gml:coordinates decimal="." cs="," ts=" ">0,1</gml:coordinates></gml:Point></feature:geometry></feature:vector></gml:featureMember><gml:featureMember xmlns:gml="http://www.opengis.net/gml"><feature:vector xmlns:feature="http://mapserver.gis.umn.edu/mapserver"><feature:geometry><gml:Point><gml:coordinates decimal="." cs="," ts=" ">1,0</gml:coordinates></gml:Point></feature:geometry></feature:vector></gml:featureMember></InlineGeometry></Layer></ResourceList></OWSContext>';
+ t.xml_eq(output, expected, "OWSContext with a WMS and an inline GML vector layer generated correctly");
+ }
+
+ function test_write_inlinegml_no_features(t){
+ var lon = 5,
+ lat = 40,
+ zoom = 5,
+ map = new OpenLayers.Map( 'map' ),
+ layer = new OpenLayers.Layer.WMS(
+ "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'},
+ {singleTile: true}
+ ),
+ vector = new OpenLayers.Layer.Vector();
+
+ map.addLayers( [ layer, vector ] );
+ map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+
+ var owc = new OpenLayers.Format.OWSContext(),
+ output,
+ caughtException = false,
+ expectedXml = '<OWSContext xmlns="http://www.opengis.net/ows-context" version="0.3.1" id="foo" xsi:schemaLocation="http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.1/owsContext.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><General><ows:BoundingBox xmlns:ows="http://www.opengis.net/ows" crs="EPSG:4326"><ows:LowerCorner>-5.986328125 29.013671875</ows:LowerCorner><ows:UpperCorner>15.986328125 50.986328125</ows:UpperCorner></ows:BoundingBox><ows:Title xmlns:ows="http://www.opengis.net/ows">OpenLayers OWSContext</ows:Title></General><ResourceList><Layer name="basic" queryable="0" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">OpenLayers WMS</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/jpeg</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://labs.metacarta.com/wms/vmap0" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server></Layer><Layer name="vector" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows"/><InlineGeometry/></Layer></ResourceList></OWSContext>';
+
+ try {
+ output = owc.write(map, {id: 'foo'});
+ } catch (e){
+ caughtException = true;
+ }
+
+ if (caughtException) {
+ t.plan(1);
+ t.fail('OWSContext with a WMS and an inline vector layer failed and threw an exception');
+ } else {
+ t.plan(2);
+ t.ok(true, 'OWSContext with a WMS and an inline vector layer generated without exception');
+ t.xml_eq(output, expectedXml, "OWSContext with a WMS and an inline vector layer generated correctly");
+ }
+ }
+
+ function test_read_inline(t) {
+ t.plan(10);
+ var text = '<?xml version="1.0" encoding="UTF-8"?><OWSContext xmlns="http://www.opengis.net/ows-context" xmlns:gml="http://www.opengis.net/gml" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:ogc="http://www.opengis.net/ogc" xmlns:ows="http://www.opengis.net/ows" xmlns:sld="http://www.opengis.net/sld" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="0.3.1" id="ows-context-ex-1-v3" xsi:schemaLocation="http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.0/owsContext.xsd"><General><ows:BoundingBox crs="urn:ogc:def:crs:EPSG:6.6:4326"><ows:LowerCorner>-117.44667178362664 32.57086210449395</ows:LowerCorner><ows:UpperCorner>-116.74066794885977 32.921986352104064</ows:UpperCorner></ows:BoundingBox><ows:Title>OWS Context version 0.3.0 Inline KML and GML examples</ows:Title></General><ResourceList><!-- WMS Example --><Layer name="topp:major_roads" queryable="1" hidden="1"> <ows:Title>Tiger 2005fe major roads</ows:Title> <ows:OutputFormat>image/png</ows:OutputFormat><Server service="urn:ogc:serviceType:WMS" version="1.1.1"><OnlineResource xlink:href="http://sigma.openplans.org:8080/geoserver/wms?SERVICE=WMS"/></Server></Layer><!-- Inline KML Example --><Layer name="archsites"><ows:Title>Architectural Sites</ows:Title><kml:Document><kml:name>opengeo:archsites 1 to 100</kml:name><kml:Style id="archsitesStyle"><kml:IconStyle><kml:color>ffffffff</kml:color><kml:colorMode>normal</kml:colorMode><kml:Icon><kml:href>http://maps.google.com/mapfiles/kml/pal4/icon25.png</kml:href></kml:Icon></kml:IconStyle></kml:Style><kml:Placemark id="archsites.1"><kml:name>Signature Rock</kml:name><kml:description>Signature Rock Description</kml:description><kml:styleUrl>#archsitesStyle</kml:styleUrl><kml:Point><kml:coordinates>-103.82681673,44.38162255</kml:coordinates></kml:Point></kml:Placemark></kml:Document></Layer><!-- Inline GML Example --><Layer name="coastg"><ows:Title>Coastg as GML Points</ows:Title><InlineGeometry><gml:boundedBy><gml:Box><gml:coord><gml:X>-43.379</gml:X><gml:Y>72.746</gml:Y></gml:coord><gml:coord><gml:X>-43.390</gml:X><gml:Y>72.755</gml:Y></gml:coord></gml:Box></gml:boundedBy><gml:featureMember><au1:coastg xmlns:au1="http://www.ionicsoft.com/wfs" fid="coastg.0"><au1:MERGE>1</au1:MERGE><au1:AREA>0.0020000000000000005</au1:AREA><au1:PERIMETER>0.167</au1:PERIMETER><au1:GEOMETRY><gml:Polygon srsName="urn:ogc:def:crs:EPSG:6.6:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>129.29167335893825,71.9583353847737 129.29167335893825,72.0000014248896 129.33332733905414,72.0000014248896 129.33332733905414,71.9583353847737 129.29167335893825,71.9583353847737</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></au1:GEOMETRY></au1:coastg></gml:featureMember><gml:featureMember><au1:coastg xmlns:au1="http://www.ionicsoft.com/wfs" fid="coastg.1"><au1:MERGE>1</au1:MERGE><au1:AREA>0.0020000000000000005</au1:AREA><au1:PERIMETER>0.167</au1:PERIMETER><au1:GEOMETRY><gml:Polygon srsName="urn:ogc:def:crs:EPSG:6.6:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>135.45832829609282,35.66666796381659 135.41667179597695,35.66666796381659 135.41667179597695,35.70833202393249 135.45832829609282,35.70833202393249 135.45832829609282,35.66666796381659</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></au1:GEOMETRY></au1:coastg></gml:featureMember></InlineGeometry></Layer></ResourceList></OWSContext>';
+ var parser = new OpenLayers.Format.OWSContext();
+ var context = parser.read(text, {map: 'map'});
+ t.ok(context.layers[1] instanceof OpenLayers.Layer.Vector, "Inline KML results in a vector layer");
+ t.eq(context.layers[1].features.length, 1, "Inline KML layer has one feature");
+ t.ok(context.layers[1].features[0].geometry instanceof OpenLayers.Geometry.Point, "KML feature is a point");
+ t.eq(context.layers[1].features[0].attributes.description, "Signature Rock Description", "KML Description correctly parsed");
+ t.eq(context.layers[1].features[0].fid, "archsites.1", "KML feature id correctly parsed");
+ t.eq(context.layers[1].features[0].style.externalGraphic, "http://maps.google.com/mapfiles/kml/pal4/icon25.png", "Style url for KML feature correctly parsed");
+ t.ok(context.layers[2] instanceof OpenLayers.Layer.Vector, "Inline GML results in a vector layer");
+ t.eq(context.layers[2].features.length, 2, "Inline GML layer has two features");
+ t.ok(context.layers[2].features[0].geometry instanceof OpenLayers.Geometry.Polygon, "GML feature is a polygon");
+ t.eq(context.layers[2].features[0].attributes.MERGE, "1", "GML attribute read correctly");
+ }
+
+ function test_read_gml(t) {
+ t.plan(5);
+ var text = '<?xml version="1.0" encoding="UTF-8"?><OWSContext version="0.3.0" id="ows-context-ex-1-v3" xmlns="http://www.opengis.net/ows-context" xmlns:gml="http://www.opengis.net/gml" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:ogc="http://www.opengis.net/ogc" xmlns:ows="http://www.opengis.net/ows" xmlns:sld="http://www.opengis.net/sld" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.0/owsContext.xsd"><General><ows:BoundingBox crs="urn:ogc:def:crs:EPSG:6.6:4326"><ows:LowerCorner>-117.44667178362664 32.57086210449395</ows:LowerCorner><ows:UpperCorner>-116.74066794885977 32.921986352104064</ows:UpperCorner></ows:BoundingBox><ows:Title>OWS Context version 0.3.0 examples</ows:Title></General><ResourceList><Layer name="basic" queryable="0" hidden="0" opacity="1"><ows:Title xmlns:ows="http://www.opengis.net/ows">OpenLayers WMS</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/jpeg</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://labs.metacarta.com/wms/vmap0" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server></Layer><!-- Referenced GML Example --><Layer name="Landuse"><ows:Title>Boston Landuse Polygons</ows:Title><Server service="urn:ogc:serviceType:GML" version="2.1.2" title="Cadcorp GeognoSIS.NET Web Feature Service"><OnlineResource xlink:href="gml/MassGIS/LandUse.gml"/></Server><sld:MinScaleDenominator>5000</sld:MinScaleDenominator><sld:MaxScaleDenominator>50000</sld:MaxScaleDenominator><MaxFeatures>99</MaxFeatures></Layer></ResourceList></OWSContext>';
+ var parser = new OpenLayers.Format.OWSContext();
+ var context = parser.read(text, {map: 'map'});
+ t.ok(context.layers[1].protocol instanceof OpenLayers.Protocol.HTTP, "serviceType GML is translated into an HTTP Protocol");
+ t.eq(context.layers[1].protocol.url, "gml/MassGIS/LandUse.gml", "Url of GML file correctly set");
+ t.ok(context.layers[1].protocol.format instanceof OpenLayers.Format.GML, "GML Format associated with protocol");
+ t.eq(Math.round(context.layers[1].minScale), 50000, "Minscale correctly read");
+ t.eq(Math.round(context.layers[1].maxScale), 5000, "Maxscale correctly read");
+ }
+
+ function test_read_kml(t) {
+ t.plan(3);
+ var text = '<OWSContext xmlns="http://www.opengis.net/ows-context" version="0.3.1" id="foo" xsi:schemaLocation="http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.1/owsContext.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><General><ows:BoundingBox xmlns:ows="http://www.opengis.net/ows" crs="EPSG:4326"><ows:LowerCorner>-5.986328125 27.9150390625</ows:LowerCorner><ows:UpperCorner>15.986328125 52.0849609375</ows:UpperCorner></ows:BoundingBox><ows:Title xmlns:ows="http://www.opengis.net/ows">OpenLayers OWSContext</ows:Title></General><ResourceList><Layer name="basic" queryable="0" hidden="0" opacity="1"><ows:Title xmlns:ows="http://www.opengis.net/ows">OpenLayers WMS</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/jpeg</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://labs.metacarta.com/wms/vmap0" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server></Layer><Layer><ows:Title xmlns:ows="http://www.opengis.net/ows">KML</ows:Title><Server version="2.2" service="urn:ogc:serviceType:KML"><OnlineResource xlink:href="foo/sundials.kml" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server></Layer></ResourceList></OWSContext>';
+ var parser = new OpenLayers.Format.OWSContext();
+ var context = parser.read(text, {map: 'map'});
+ t.ok(context.layers[1].protocol instanceof OpenLayers.Protocol.HTTP, "serviceType KML is translated into an HTTP Protocol");
+ t.eq(context.layers[1].protocol.url, "foo/sundials.kml", "Url of KML file correctly set");
+ t.ok(context.layers[1].protocol.format instanceof OpenLayers.Format.KML, "KML Format associated with protocol");
+ }
+
+ function test_write_gml(t) {
+ t.plan(1);
+ var lon = 5;
+ var lat = 40;
+ var zoom = 5;
+ var map = new OpenLayers.Map( 'map' );
+ var layer = new OpenLayers.Layer.WMS(
+ "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'},
+ {singleTile: true}
+ );
+ var sundials = new OpenLayers.Layer.Vector("GML", {
+ projection: map.displayProjection,
+ strategies: [new OpenLayers.Strategy.Fixed()],
+ protocol: new OpenLayers.Protocol.HTTP({
+ url: "foo/sundials.gml",
+ format: new OpenLayers.Format.GML()
+ })
+ });
+ map.addLayers([layer, sundials]);
+ map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+
+ var owc = new OpenLayers.Format.OWSContext();
+ var output = owc.write(map, {id: 'foo'});
+ var expected = '<OWSContext xmlns="http://www.opengis.net/ows-context" version="0.3.1" id="foo" xsi:schemaLocation="http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.1/owsContext.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><General><ows:BoundingBox xmlns:ows="http://www.opengis.net/ows" crs="EPSG:4326"><ows:LowerCorner>-5.986328125 29.013671875</ows:LowerCorner><ows:UpperCorner>15.986328125 50.986328125</ows:UpperCorner></ows:BoundingBox><ows:Title xmlns:ows="http://www.opengis.net/ows">OpenLayers OWSContext</ows:Title></General><ResourceList><Layer name="basic" queryable="0" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">OpenLayers WMS</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/jpeg</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://labs.metacarta.com/wms/vmap0" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server></Layer><Layer><ows:Title xmlns:ows="http://www.opengis.net/ows">GML</ows:Title><Server version="2.1.2" service="urn:ogc:serviceType:GML"><OnlineResource xlink:href="foo/sundials.gml" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server></Layer></ResourceList></OWSContext>';
+ t.xml_eq(output, expected, "OWSContext with a WMS and a GML vector layer generated correctly");
+ }
+
+ function test_write_kml(t) {
+ t.plan(1);
+ var lon = 5;
+ var lat = 40;
+ var zoom = 5;
+ var map = new OpenLayers.Map( 'map' );
+ var layer = new OpenLayers.Layer.WMS(
+ "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'},
+ {singleTile: true}
+ );
+ var sundials = new OpenLayers.Layer.Vector("KML", {
+ projection: map.displayProjection,
+ strategies: [new OpenLayers.Strategy.Fixed()],
+ protocol: new OpenLayers.Protocol.HTTP({
+ url: "foo/sundials.kml",
+ format: new OpenLayers.Format.KML({
+ extractStyles: true
+ })
+ })
+ });
+ map.addLayers([layer, sundials]);
+ map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+
+ var owc = new OpenLayers.Format.OWSContext();
+ var output = owc.write(map, {id: 'foo'});
+ var expected = '<OWSContext xmlns="http://www.opengis.net/ows-context" version="0.3.1" id="foo" xsi:schemaLocation="http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.1/owsContext.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><General><ows:BoundingBox xmlns:ows="http://www.opengis.net/ows" crs="EPSG:4326"><ows:LowerCorner>-5.986328125 29.013671875</ows:LowerCorner><ows:UpperCorner>15.986328125 50.986328125</ows:UpperCorner></ows:BoundingBox><ows:Title xmlns:ows="http://www.opengis.net/ows">OpenLayers OWSContext</ows:Title></General><ResourceList><Layer name="basic" queryable="0" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">OpenLayers WMS</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/jpeg</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://labs.metacarta.com/wms/vmap0" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server></Layer><Layer><ows:Title xmlns:ows="http://www.opengis.net/ows">KML</ows:Title><Server version="2.2" service="urn:ogc:serviceType:KML"><OnlineResource xlink:href="foo/sundials.kml" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server></Layer></ResourceList></OWSContext>';
+ t.xml_eq(output, expected, "OWSContext with a WMS and a KML vector layer generated correctly");
+ }
+
+ function test_nested(t) {
+ t.plan(4);
+ var text = '<OWSContext xmlns="http://www.opengis.net/ows-context" version="0.3.1" id="machu" xsi:schemaLocation="http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.1/owsContext.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><General><ows:BoundingBox xmlns:ows="http://www.opengis.net/ows" crs="EPSG:4326"><ows:LowerCorner>-40 30</ows:LowerCorner><ows:UpperCorner>55 125</ows:UpperCorner></ows:BoundingBox><ows:Title xmlns:ows="http://www.opengis.net/ows">OpenLayers OWSContext</ows:Title></General><ResourceList><Layer><ows:Title xmlns:ows="http://www.opengis.net/ows">General Bathymetric Chart</ows:Title><Layer><ows:Title xmlns:ows="http://www.opengis.net/ows">GEBCO</ows:Title><Layer name="GEBCO" queryable="1" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">GEBCO</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/jpeg</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://foo/bar_bathymetry?" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server><StyleList><Style><Name>default</Name><Title>default</Title><LegendURL><OnlineResource xlink:href="http://foo/services/geoservices/legends/machu/gebco.png" xmlns:xlink="http://www.w3.org/1999/xlink"/></LegendURL></Style></StyleList></Layer></Layer></Layer><Layer><ows:Title xmlns:ows="http://www.opengis.net/ows">Administrative boundaries</ows:Title><Layer><ows:Title xmlns:ows="http://www.opengis.net/ows">National boundaries</ows:Title><Layer name="GAUL" queryable="1" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">GAUL</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/png</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://foo/bar_topography?" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server><StyleList><Style><Name>default</Name><Title>default</Title><LegendURL><OnlineResource xlink:href="http://foo/services/geoservices/legends/machu/administrative_boundaries_land.png" xmlns:xlink="http://www.w3.org/1999/xlink"/></LegendURL></Style></StyleList></Layer></Layer><Layer><ows:Title xmlns:ows="http://www.opengis.net/ows">Maritime boundaries</ows:Title><Layer name="World_Maritime_Boundaries_v4_20090811" queryable="1" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">World_Maritime_Boundaries_v4_20090811</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/png</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://foo/bar_topography?" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server><StyleList><Style><Name>default</Name><Title>default</Title><LegendURL><OnlineResource xlink:href="http://foo/services/geoservices/legends/machu/administrative_boundaries_sea.png" xmlns:xlink="http://www.w3.org/1999/xlink"/></LegendURL></Style></StyleList></Layer></Layer></Layer><Layer><ows:Title xmlns:ows="http://www.opengis.net/ows">Cultural Heritage Underwater</ows:Title><Layer><ows:Title xmlns:ows="http://www.opengis.net/ows">Sites</ows:Title><Layer name="ARCH_NL" queryable="1" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">ARCH_NL</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/png</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://foo/bar_nl?" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server><StyleList><Style><Name>default</Name><Title>default</Title><LegendURL><OnlineResource xlink:href="http://foo/services/geoservices/legends/machu/cultural_heritage_underwater.png" xmlns:xlink="http://www.w3.org/1999/xlink"/></LegendURL></Style></StyleList></Layer><Layer name="ARCH_PL" queryable="1" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">ARCH_PL</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/png</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://foo/bar_pl?" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server><StyleList><Style><Name>default</Name><Title>default</Title><LegendURL><OnlineResource xlink:href="http://foo/services/geoservices/legends/machu/cultural_heritage_underwater.png" xmlns:xlink="http://www.w3.org/1999/xlink"/></LegendURL></Style></StyleList></Layer><Layer name="ARCH_PT" queryable="1" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">ARCH_PT</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/png</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://foo/bar_pt?" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server><StyleList><Style><Name>default</Name><Title>default</Title><LegendURL><OnlineResource xlink:href="http://foo/services/geoservices/legends/machu/cultural_heritage_underwater.png" xmlns:xlink="http://www.w3.org/1999/xlink"/></LegendURL></Style></StyleList></Layer><Layer name="ARCH_BE" queryable="1" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">ARCH_BE</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/png</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://foo/bar_be?" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server><StyleList><Style><Name>default</Name><Title>default</Title><LegendURL><OnlineResource xlink:href="http://foo/services/geoservices/legends/machu/cultural_heritage_underwater.png" xmlns:xlink="http://www.w3.org/1999/xlink"/></LegendURL></Style></StyleList></Layer><Layer name="ARCH_SE" queryable="1" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">ARCH_SE</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/png</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://foo/bar_se?" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server><StyleList><Style><Name>default</Name><Title>default</Title><LegendURL><OnlineResource xlink:href="http://foo/services/geoservices/legends/machu/cultural_heritage_underwater.png" xmlns:xlink="http://www.w3.org/1999/xlink"/></LegendURL></Style></StyleList></Layer><Layer name="ARCH_DE" queryable="1" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">ARCH_DE</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/png</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://foo/bar_de?" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server><StyleList><Style><Name>default</Name><Title>default</Title><LegendURL><OnlineResource xlink:href="http://foo/services/geoservices/legends/machu/cultural_heritage_underwater.png" xmlns:xlink="http://www.w3.org/1999/xlink"/></LegendURL></Style></StyleList></Layer><Layer name="ARCH_UK" queryable="1" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">ARCH_UK</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/png</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://foo/bar_uk?" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server><StyleList><Style><Name>default</Name><Title>default</Title><LegendURL><OnlineResource xlink:href="http://foo/services/geoservices/legends/machu/cultural_heritage_underwater.png" xmlns:xlink="http://www.w3.org/1999/xlink"/></LegendURL></Style></StyleList></Layer></Layer></Layer><Layer><ows:Title xmlns:ows="http://www.opengis.net/ows">Theme1</ows:Title><Layer><ows:Title xmlns:ows="http://www.opengis.net/ows">layer1</ows:Title><Layer name="TEST_AREA_BE" queryable="1" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">TEST_AREA_BE</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/png</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://foo/bar_be?" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server></Layer><Layer name="TEST_AREA_PT" queryable="1" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">TEST_AREA_PT</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/png</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://foo/bar_pt?" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server></Layer></Layer></Layer><Layer><ows:Title xmlns:ows="http://www.opengis.net/ows">Theme2</ows:Title><Layer><ows:Title xmlns:ows="http://www.opengis.net/ows">layer1</ows:Title><Layer name="TEST_AREA_SE" queryable="1" hidden="0"><ows:Title xmlns:ows="http://www.opengis.net/ows">TEST_AREA_SE</ows:Title><ows:OutputFormat xmlns:ows="http://www.opengis.net/ows">image/png</ows:OutputFormat><Server version="1.1.1" service="urn:ogc:serviceType:WMS"><OnlineResource xlink:href="http://foo/bar_se?" xmlns:xlink="http://www.w3.org/1999/xlink"/></Server></Layer></Layer></Layer></ResourceList></OWSContext>';
+ var parser = new OpenLayers.Format.OWSContext();
+ var map = new OpenLayers.Map('map', {allOverlays: true, fractionalZoom: true});
+ var context = parser.read(text, {map: map});
+ t.eq(map.layers.length, 13, "13 layers parsed from document");
+ t.eq(map.layers[0].metadata.nestingPath.join("/"), "General Bathymetric Chart/GEBCO", "Category layers read correctly");
+ t.eq(map.layers[0].metadata.styles[0].legend.url, "http://foo/services/geoservices/legends/machu/gebco.png", "Legend url correctly parsed");
+ map.zoomToExtent(new OpenLayers.Bounds(-40, 30, 55, 125));
+ var owc = parser.write(map, {id: 'machu'});
+ t.xml_eq(text, owc, "Can we roundtrip nested OWSContext successfully?");
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:500px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/QueryStringFilter.html b/misc/openlayers/tests/Format/QueryStringFilter.html
new file mode 100644
index 0000000..b38d1e4
--- /dev/null
+++ b/misc/openlayers/tests/Format/QueryStringFilter.html
@@ -0,0 +1,306 @@
+<html>
+<head>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(4);
+ var options = {'foo': 'bar'};
+ var format = new OpenLayers.Format.QueryStringFilter(options);
+ t.ok(format instanceof OpenLayers.Format.QueryStringFilter,
+ "new OpenLayers.Format.QueryStringFilter object");
+ t.eq(format.foo, "bar", "constructor sets options correctly")
+ t.eq(typeof format.write, 'function', 'format has a write function');
+ t.eq(format.options, options, "format.options correctly set");
+ }
+
+ function test_write(t) {
+ t.plan(30);
+
+ // setup
+
+ var format, filter, params;
+
+ format = new OpenLayers.Format.QueryStringFilter();
+
+ // 1 test
+ filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ value: new OpenLayers.Bounds(0, 1, 2, 3)
+ });
+ params = format.write(filter);
+ t.eq(params.bbox, [0, 1, 2, 3], "correct bbox param if passed a BBOX filter");
+
+ // 3 tests
+ var lon = 100, lat = 200, tolerance = 10;
+ filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.DWITHIN,
+ value: new OpenLayers.Geometry.Point(lon, lat),
+ distance: tolerance
+ });
+ params = format.write(filter);
+ t.eq(params.lon, lon, "correct lon param if passed a DWITHIN filter");
+ t.eq(params.lat, lat, "correct lat param if passed a DWITHIN filter");
+ t.eq(params.tolerance, tolerance, "correct tolerance param if passed a DWITHIN filter");
+
+ // 2 tests
+ filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.WITHIN,
+ value: new OpenLayers.Geometry.Point(lon, lat)
+ });
+ params = format.write(filter);
+ t.eq(params.lon, lon, "correct lon param if passed a WITHIN filter");
+ t.eq(params.lat, lat, "correct lat param if passed a WITHIN filter");
+
+ // Some bbox filters used in the next tests.
+
+ var bboxFilter1 = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ value: new OpenLayers.Bounds(0, 0, 10, 10)
+ });
+
+ var bboxFilter2 = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ value: new OpenLayers.Bounds(0, 0, 20, 20)
+ });
+
+ // 1 test
+ filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.AND,
+ filters: []
+ });
+ params = format.write(filter);
+ t.eq(params, {}, "returns empty object if given empty AND Logical filter");
+
+ // 1 test
+ filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.OR,
+ filters: [
+ bboxFilter1
+ ]
+ });
+ params = format.write(filter);
+ t.eq(params, {}, "does not support OR Logical filter");
+
+ // 1 test
+ filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.AND,
+ filters: [
+ bboxFilter1
+ ]
+ });
+ params = format.write(filter);
+ t.eq(params.bbox, [0, 0, 10, 10],
+ "correct bbox param if passed a Logical filter containing a BBOX");
+
+ // 1 test
+ filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.AND,
+ filters: [
+ bboxFilter1, bboxFilter2
+ ]
+ });
+ params = format.write(filter);
+ t.eq(params.bbox, [0, 0, 20, 20],
+ "correct bbox param if passed multiple BBOX filter in a Logical filter");
+
+ // 2 tests
+ filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "foo",
+ value: "bar"
+ });
+ params = format.write(filter);
+ t.eq(params.queryable[0], "foo",
+ "correct queryable param if passed an EQUAL_TO filter");
+ t.eq(params["foo__eq"], "bar",
+ "correct param key and value if passed an EQUAL_TO filter");
+
+ // 2 tests
+ filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+ property: "foo",
+ value: "bar"
+ });
+ params = format.write(filter);
+ t.eq(params.queryable[0], "foo",
+ "correct queryable param if passed an NOT_EQUAL_TO filter");
+ t.eq(params["foo__ne"], "bar",
+ "correct param key and value if passed an NOT_EQUAL_TO filter");
+
+ // 2 tests
+ filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LESS_THAN,
+ property: "foo",
+ value: "bar"
+ });
+ var params = format.write(filter);
+ t.eq(params.queryable[0], "foo",
+ "correct queryable param if passed an LESS_THAN filter");
+ t.eq(params["foo__lt"], "bar",
+ "correct param key and value if passed an LESS_THAN filter");
+
+ // 2 tests
+ filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO,
+ property: "foo",
+ value: "bar"
+ });
+ var params = format.write(filter);
+ t.eq(params.queryable[0], "foo",
+ "correct queryable param if passed an LESS_THAN_OR_EQUAL_TO filter");
+ t.eq(params["foo__lte"], "bar",
+ "correct param key and value if passed an LESS_THAN_OR_EQUAL_TO filter");
+
+ // 2 tests
+ filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.GREATER_THAN,
+ property: "foo",
+ value: "bar"
+ });
+ params = format.write(filter);
+ t.eq(params.queryable[0], "foo",
+ "correct queryable param if passed an GREATER_THAN filter");
+ t.eq(params["foo__gt"], "bar",
+ "correct param key and value if passed an GREATER_THAN filter");
+
+ // 2 tests
+ filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,
+ property: "foo",
+ value: "bar"
+ });
+ params = format.write(filter);
+ t.eq(params.queryable[0], "foo",
+ "correct queryable param if passed an GREATER_THAN_OR_EQUAL_TO filter");
+ t.eq(params["foo__gte"], "bar",
+ "correct param key and value if passed an GREATER_THAN_OR_EQUAL_TO filter");
+
+ // 2 tests
+ filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LIKE,
+ property: "foo",
+ value: "bar"
+ });
+ params = format.write(filter);
+ t.eq(params.queryable[0], "foo",
+ "correct queryable param if passed a LIKE filter");
+ t.eq(params["foo__ilike"], "bar",
+ "correct param key and value if passed an LIKE filter");
+
+ // 4 tests
+ filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.AND,
+ filters: [
+ new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "foo",
+ value: "bar"
+ }),
+ new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LESS_THAN,
+ property: "foo2",
+ value: "baz"
+ })
+ ]
+ });
+ params = format.write(filter);
+ t.eq(params.queryable[0], "foo",
+ "correct queryable param if passed an EQUAL_TO filter within a AND filter");
+ t.eq(params["foo__eq"], "bar",
+ "correct param key and value if passed an EQUAL_TO filter within a AND filter");
+ t.eq(params.queryable[1], "foo2",
+ "correct queryable param if passed a LESS_THAN filter within a AND filter");
+ t.eq(params["foo2__lt"], "baz",
+ "correct param key and value if passed a LESS_THAN filter within a AND filter");
+
+ // 2 tests
+ format = new OpenLayers.Format.QueryStringFilter({wildcarded: true});
+ filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LIKE,
+ property: "foo",
+ value: "bar"
+ });
+ params = format.write(filter);
+ t.eq(params.queryable[0], "foo",
+ "correct queryable param if passed a LIKE filter (wildcarded true)");
+ t.eq(params["foo__ilike"], "%bar%",
+ "correct param key and value if passed an LIKE filter (wildcarded true)");
+ }
+
+ function test_regex2value(t) {
+ t.plan(16);
+
+ // setup
+
+ var format = new OpenLayers.Format.QueryStringFilter();
+
+ var value;
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LIKE,
+ property: "prop"
+ });
+
+ function serialize(value) {
+ filter.value = value;
+ return format.write(filter).prop__ilike;
+ }
+
+ // test
+
+ value = serialize("foo");
+ t.eq(value, "foo", 'regex2value converts "foo" to "foo"');
+
+ value = serialize("foo%");
+ t.eq(value, "foo\\%", 'regex2value converts "foo%" to "foo\\%"');
+
+ value = serialize("foo.*");
+ t.eq(value, "foo%", 'regex2value converts "foo.*" to "foo%"');
+
+ value = serialize("f.*oo.*");
+ t.eq(value, "f%oo%", 'regex2value converts "f.*oo.*" to "f%oo%"');
+
+ value = serialize("foo.");
+ t.eq(value, "foo_", 'regex2value converts "foo." to "foo_"');
+
+ value = serialize("f.oo.");
+ t.eq(value, "f_oo_", 'regex2value converts "f.oo." to "f_oo_"');
+
+ value = serialize("f.oo.*");
+ t.eq(value, "f_oo%", 'regex2value converts "f.oo.*" to "f_oo%"');
+
+ value = serialize("foo\\\\");
+ t.eq(value, "foo\\\\", 'regex2value converts "foo\\\\" to "foo\\\\"');
+
+ value = serialize("foo\\.");
+ t.eq(value, "foo.", 'regex2value converts "foo\\." to "foo."');
+
+ value = serialize("foo\\\\.");
+ t.eq(value, "foo\\\\_", 'regex2value converts "foo\\\\." to "foo\\\\_"');
+
+ value = serialize("foo\\*");
+ t.eq(value, "foo*", 'regex2value converts "foo\\*" to "foo*"');
+
+ value = serialize("foo\\\\*");
+ t.eq(value, "foo\\\\*", 'regex2value converts "foo\\\\*" to "foo\\\\*"');
+
+ value = serialize("foo\\\\.*");
+ t.eq(value, "foo\\\\%", 'regex2value converts "foo\\\\.*" to "foo\\\\%"');
+
+ value = serialize("fo\\.o.*");
+ t.eq(value, "fo.o%", 'regex2value converts from "fo\\.o.*" to "fo.o%"');
+
+ value = serialize("fo.*o\\.");
+ t.eq(value, "fo%o.", 'regex2value converts from "fo.*o\\." to "to%o."');
+
+ value = serialize("\\*\\..*.\\\\.*\\\\.%");
+ t.eq(value, "*.%_\\\\%\\\\_\\%",
+ 'regex2value converts from "\\*\\..*.\\\\.*\\\\.%" ' +
+ 'to "*.%_\\\\%\\\\_\\%"');
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/SLD.html b/misc/openlayers/tests/Format/SLD.html
new file mode 100644
index 0000000..bc4bd82
--- /dev/null
+++ b/misc/openlayers/tests/Format/SLD.html
@@ -0,0 +1,36 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var test_content = '<sld:StyledLayerDescriptor xmlns:sld="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml"><sld:NamedLayer><sld:Name>TestLayer</sld:Name><sld:UserStyle><sld:Name>foo</sld:Name><sld:FeatureTypeStyle><sld:Rule><sld:Name>bar</sld:Name><ogc:Filter></ogc:Filter><sld:PolygonSymbolizer><sld:Fill><sld:CssParameter name="fill"><ogc:Literal>blue</ogc:Literal></sld:CssParameter></sld:Fill></sld:PolygonSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer></sld:StyledLayerDescriptor>';
+
+ function test_Format_SLD_constructor(t) {
+ t.plan(3);
+
+ var options = {'foo': 'bar'};
+ var format = new OpenLayers.Format.SLD(options);
+ t.ok(format instanceof OpenLayers.Format.SLD,
+ "new OpenLayers.Format.SLD returns object" );
+ t.eq(format.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof format.read, "function", "format has a read function");
+ }
+
+ function test_Format_SLD_read(t) {
+ t.plan(4);
+ var sld = new OpenLayers.Format.SLD().read(this.test_content);
+
+ var testLayer = sld.namedLayers["TestLayer"];
+ var userStyles = testLayer.userStyles;
+
+ t.eq(userStyles[0].name, "foo", "SLD correctly reads a UserStyle named 'foo'");
+ t.eq(userStyles[0].rules.length, 1, "The number of rules for the UserStyle is correct");
+ t.eq(userStyles[0].rules[0].name, "bar", "The first rule's name is 'bar'");
+ t.eq(userStyles[0].rules[0].symbolizer.Polygon.fillColor, "blue", "The fillColor for the Polygon symbolizer is correct");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/SLD/v1_0_0.html b/misc/openlayers/tests/Format/SLD/v1_0_0.html
new file mode 100644
index 0000000..fbc18a6
--- /dev/null
+++ b/misc/openlayers/tests/Format/SLD/v1_0_0.html
@@ -0,0 +1,1028 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var xml = new OpenLayers.Format.XML();
+ function readXML(id) {
+ return xml.read(document.getElementById(id).firstChild.nodeValue);
+ }
+
+ var sld =
+ '<StyledLayerDescriptor version="1.0.0" ' +
+ 'xmlns="http://www.opengis.net/sld" ' +
+ 'xmlns:gml="http://www.opengis.net/gml" ' +
+ 'xmlns:ogc="http://www.opengis.net/ogc" ' +
+ 'xmlns:xlink="http://www.w3.org/1999/xlink" ' +
+ 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
+ 'xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd">' +
+ '<NamedLayer>' +
+ '<Name>AAA161</Name>' +
+ '<UserStyle>' +
+ '<FeatureTypeStyle>' +
+ '<Rule>' +
+ '<Name>stortsteen</Name>' +
+ '<ogc:Filter>' +
+ '<ogc:PropertyIsEqualTo>' +
+ '<ogc:PropertyName>CTE</ogc:PropertyName>' +
+ '<ogc:Literal>V0305</ogc:Literal>' +
+ '</ogc:PropertyIsEqualTo>' +
+ '</ogc:Filter>' +
+ '<MaxScaleDenominator>50000</MaxScaleDenominator>' +
+ '<PolygonSymbolizer>' +
+ '<Fill>' +
+ '<CssParameter name="fill">#ffffff</CssParameter>' +
+ '</Fill>' +
+ '<Stroke>' +
+ '<CssParameter name="stroke">#000000</CssParameter>' +
+ '</Stroke>' +
+ '</PolygonSymbolizer>' +
+ '<TextSymbolizer>' +
+ '<Label>' +
+ 'A <ogc:PropertyName>FOO</ogc:PropertyName> label' +
+ '</Label>' +
+ '<Font>' +
+ '<CssParameter name="font-family">Arial</CssParameter>' +
+ '<CssParameter name="font-size">14</CssParameter>' +
+ '<CssParameter name="font-weight">bold</CssParameter>' +
+ '<CssParameter name="font-style">normal</CssParameter>' +
+ '</Font>' +
+ '<LabelPlacement>' +
+ '<PointPlacement>' +
+ '<AnchorPoint>' +
+ '<AnchorPointX>0.5</AnchorPointX>' +
+ '<AnchorPointY>0.5</AnchorPointY>' +
+ '</AnchorPoint>' +
+ '<Displacement>' +
+ '<DisplacementX>5</DisplacementX>' +
+ '<DisplacementY>5</DisplacementY>' +
+ '</Displacement>' +
+ '<Rotation>45</Rotation>' +
+ '</PointPlacement>' +
+ '</LabelPlacement>' +
+ '<Halo>' +
+ '<Radius>3</Radius>' +
+ '<Fill>' +
+ '<CssParameter name="fill">#ffffff</CssParameter>' +
+ '</Fill>' +
+ '</Halo>' +
+ '<Fill>' +
+ '<CssParameter name="fill">#000000</CssParameter>' +
+ '</Fill>' +
+ '</TextSymbolizer>' +
+ '</Rule>' +
+ '<Rule>' +
+ '<Name>betonbekleding</Name>' +
+ '<ogc:Filter>' +
+ '<ogc:PropertyIsLessThan>' +
+ '<ogc:PropertyName>CTE</ogc:PropertyName>' +
+ '<ogc:Literal>1000</ogc:Literal>' +
+ '</ogc:PropertyIsLessThan>' +
+ '</ogc:Filter>' +
+ '<MaxScaleDenominator>50000</MaxScaleDenominator>' +
+ '<PolygonSymbolizer>' +
+ '<Fill>' +
+ '<CssParameter name="fill">#ffff00</CssParameter>' +
+ '</Fill>' +
+ '<Stroke>' +
+ '<CssParameter name="stroke">#0000ff</CssParameter>' +
+ '</Stroke>' +
+ '</PolygonSymbolizer>' +
+ '</Rule>' +
+ '</FeatureTypeStyle>' +
+ '</UserStyle>' +
+ '</NamedLayer>' +
+ '<NamedLayer>' +
+ '<Name>Second Layer</Name>' +
+ '<UserStyle>' +
+ '<FeatureTypeStyle>' +
+ '<Rule>' +
+ '<Name>first rule second layer</Name>' +
+ '<ogc:Filter>' +
+ '<ogc:Or>' +
+ '<ogc:PropertyIsBetween>' +
+ '<ogc:PropertyName>number</ogc:PropertyName>' +
+ '<ogc:LowerBoundary>' +
+ '<ogc:Literal>1064866676</ogc:Literal>' +
+ '</ogc:LowerBoundary>' +
+ '<ogc:UpperBoundary>' +
+ '<ogc:Literal>1065512599</ogc:Literal>' +
+ '</ogc:UpperBoundary>' +
+ '</ogc:PropertyIsBetween>' +
+ '<ogc:PropertyIsLike wildCard="*" singleChar="." escape="!">' +
+ '<ogc:PropertyName>cat</ogc:PropertyName>' +
+ '<ogc:Literal>*dog.food!*good</ogc:Literal>' +
+ '</ogc:PropertyIsLike>' +
+ '<ogc:Not>' +
+ '<ogc:PropertyIsLessThanOrEqualTo>' +
+ '<ogc:PropertyName>FOO</ogc:PropertyName>' +
+ '<ogc:Literal>5000</ogc:Literal>' +
+ '</ogc:PropertyIsLessThanOrEqualTo>' +
+ '</ogc:Not>' +
+ '</ogc:Or>' +
+ '</ogc:Filter>' +
+ '<MaxScaleDenominator>10000</MaxScaleDenominator>' +
+ '<PointSymbolizer>' +
+ '<Graphic>' +
+ '<Mark>' +
+ '<WellKnownName>star</WellKnownName>' +
+ '<Fill>' +
+ '<CssParameter name="fill">lime</CssParameter>' +
+ '</Fill>' +
+ '<Stroke>' +
+ '<CssParameter name="stroke">olive</CssParameter>' +
+ '<CssParameter name="stroke-width">2</CssParameter>' +
+ '</Stroke>' +
+ '</Mark>' +
+ '<Size><ogc:PropertyName>SIZE</ogc:PropertyName></Size>' +
+ '</Graphic>' +
+ '</PointSymbolizer>' +
+ '</Rule>' +
+ '</FeatureTypeStyle>' +
+ '</UserStyle>' +
+ '</NamedLayer>' +
+ '</StyledLayerDescriptor>';
+
+ function test_read(t) {
+ t.plan(23);
+
+ var xml = new OpenLayers.Format.XML();
+ var sldxml = xml.read(sld);
+
+ // test that format options are considered in read
+ var parser = new OpenLayers.Format.SLD({
+ version: "1.0.0",
+ namedLayersAsArray: true
+ });
+ var obj = parser.read(sldxml);
+ t.ok(obj.namedLayers instanceof Array, "namedLayersAsArray option for read works");
+
+ parser = new OpenLayers.Format.SLD.v1_0_0();
+ var obj = parser.read(sldxml, {namedLayersAsArray: true});
+ t.ok(obj.namedLayers instanceof Array, "namedLayersAsArray option for read works");
+ var arrayLen = obj.namedLayers.length;
+
+ var obj = parser.read(sldxml);
+ t.eq(typeof obj.namedLayers, "object", "read returns a namedLayers object by default");
+ // test the named layer count
+ var count = 0;
+ for(var key in obj.namedLayers) {
+ ++count;
+ }
+ t.eq(count, arrayLen, "number of named layers in array equals number of named layers in object");
+
+ var layer, style, rule;
+
+ // check the first named layer
+ layer = obj.namedLayers["AAA161"];
+ t.ok(layer, "first named layer exists");
+ t.ok(layer.userStyles instanceof Array, "(AAA161) layer has array of user styles");
+ t.eq(layer.userStyles.length, 1, "(AAA161) first layer has a single user style");
+ t.eq(layer.userStyles[0].rules.length, 2, "(AAA161) first style has two rules");
+ var rule = layer.userStyles[0].rules[0];
+ t.ok(rule.filter, "(AAA161) first rule has a filter");
+ var symbolizer = rule.symbolizer;
+ t.ok(symbolizer, "(AAA161) first rule has a symbolizer");
+ var poly = symbolizer["Polygon"];
+ t.eq(poly.fillColor, "#ffffff", "(AAA161) first rule has proper fill");
+ t.eq(poly.strokeColor, "#000000", "(AAA161) first rule has proper stroke");
+ var text = symbolizer["Text"];
+ t.eq(text.label, "A ${FOO} label", "(AAA161) first rule has proper text label");
+ t.eq(layer.userStyles[0].propertyStyles["label"], true, "label added to propertyStyles");
+ t.eq(text.fontFamily, "Arial", "(AAA161) first rule has proper font family");
+ t.eq(text.fontColor, "#000000", "(AAA161) first rule has proper text fill");
+ t.eq(text.haloRadius, "3", "(AAA161) first rule has proper halo radius");
+ t.eq(text.haloColor, "#ffffff", "(AAA161) first rule has proper halo color");
+
+
+ // check the first user style
+ style = layer.userStyles[0];
+ t.ok(style instanceof OpenLayers.Style, "(AAA161,0) user style is instance of OpenLayers.Style");
+ t.eq(style.rules.length, 2, "(AAA161,0) user style has 2 rules");
+
+ // check the second rule
+ rule = style.rules[1];
+ var feature = {
+ layer: {
+ map: {
+ getScale: function(){
+ return 40000;
+ }
+ }
+ },
+ attributes: {
+ CTE: "900"
+ }
+ };
+ t.ok(typeof rule.maxScaleDenominator == "number", "MaxScaleDenominator is a number");
+ t.eq(rule.evaluate(feature), true, "numeric filter comparison evaluates correctly");
+
+ // check for PropertyName size
+ layer = obj.namedLayers["Second Layer"];
+ style = layer.userStyles[0];
+ rule = style.rules[0];
+ t.eq(rule.symbolizer["Point"].graphicWidth, "${SIZE}", "dynamic size correctly set on graphicWidth");
+
+ // etc. I'm convinced read works, really wanted to test write (since examples don't test that)
+ // I'll add more tests here later.
+
+ }
+
+ function test_write(t) {
+ t.plan(3);
+
+ // read first - testing that write produces the SLD above
+ var parser = new OpenLayers.Format.SLD.v1_0_0();
+ var xml = new OpenLayers.Format.XML();
+ var sldxml = xml.read(sld);
+ var obj = parser.read(sldxml);
+
+ var node = parser.write(obj);
+ t.xml_eq(node, sld, "SLD correctly written");
+
+ obj = parser.read(sldxml, {namedLayersAsArray: true});
+ node = parser.write(obj);
+ t.xml_eq(node, sld, "SLD from namedLayers array correctly written");
+
+ // test that 0 fill opacity gets written
+ var symbolizer = {
+ fillColor: "red",
+ fillOpacity: 0
+ };
+ var root = parser.createElementNSPlus("PolygonSymbolizer");
+ var got = parser.writeNode("Fill", symbolizer, root);
+ var expect =
+ '<Fill xmlns="http://www.opengis.net/sld">' +
+ '<CssParameter name="fill">red</CssParameter>' +
+ '<CssParameter name="fill-opacity">0</CssParameter>' +
+ '</Fill>';
+ t.xml_eq(got, expect, "zero fill opacity written");
+ }
+
+ function test_writePointSymbolizer(t) {
+
+ t.plan(3);
+
+ var parser = new OpenLayers.Format.SLD.v1_0_0();
+ var symbolizer, node, exp;
+
+ // test symbolizer with fill color only
+ symbolizer = {
+ "fillColor": "blue"
+ };
+ node = parser.writeNode("sld:PointSymbolizer", symbolizer);
+ exp =
+ '<PointSymbolizer xmlns="http://www.opengis.net/sld">' +
+ '<Graphic>' +
+ '<Mark>' +
+ '<Fill>' +
+ '<CssParameter name="fill">blue</CssParameter>' +
+ '</Fill>' +
+ '<Stroke/>' +
+ '</Mark>' +
+ '</Graphic>' +
+ '</PointSymbolizer>';
+ t.xml_eq(node, exp, "fillColor only written");
+
+ // test symbolizer with stroke color only
+ symbolizer = {
+ "strokeColor": "blue"
+ };
+ node = parser.writeNode("sld:PointSymbolizer", symbolizer);
+ exp =
+ '<PointSymbolizer xmlns="http://www.opengis.net/sld">' +
+ '<Graphic>' +
+ '<Mark>' +
+ '<Fill/>' +
+ '<Stroke>' +
+ '<CssParameter name="stroke">blue</CssParameter>' +
+ '</Stroke>' +
+ '</Mark>' +
+ '</Graphic>' +
+ '</PointSymbolizer>';
+ t.xml_eq(node, exp, "strokeColor only written");
+
+ // test symbolizer with graphic name only
+ symbolizer = {
+ "graphicName": "star"
+ };
+ node = parser.writeNode("sld:PointSymbolizer", symbolizer);
+ exp =
+ '<PointSymbolizer xmlns="http://www.opengis.net/sld">' +
+ '<Graphic>' +
+ '<Mark>' +
+ '<WellKnownName>star</WellKnownName>' +
+ '<Fill/>' +
+ '<Stroke/>' +
+ '</Mark>' +
+ '</Graphic>' +
+ '</PointSymbolizer>';
+ t.xml_eq(node, exp, "graphicName only written");
+
+
+ }
+
+
+ function test_writeLineSymbolizer(t) {
+
+ t.plan(1);
+
+ var parser = new OpenLayers.Format.SLD.v1_0_0();
+ var symbolizer, node, exp;
+
+ // test symbolizer with fill color only
+ symbolizer = {
+ strokeDashstyle: "4 4",
+ strokeLinecap: "round",
+ strokeColor: "#0000ff",
+ strokeWidth: 2
+ };
+ node = parser.writeNode("sld:LineSymbolizer", symbolizer);
+ exp =
+ '<LineSymbolizer xmlns="http://www.opengis.net/sld">' +
+ '<Stroke>' +
+ '<CssParameter name="stroke">#0000ff</CssParameter>' +
+ '<CssParameter name="stroke-width">2</CssParameter>' +
+ '<CssParameter name="stroke-dasharray">4 4</CssParameter>' +
+ '<CssParameter name="stroke-linecap">round</CssParameter>' +
+ '</Stroke>' +
+ '</LineSymbolizer>';
+ t.xml_eq(node, exp, "line symbolizer correctly written");
+
+
+ }
+
+ function test_writeTextSymbolizer(t) {
+ t.plan(1);
+ var parser = new OpenLayers.Format.SLD.v1_0_0();
+ var symbolizer = {
+ "Text": {
+ "label": "This is the ${city} in ${state}.",
+ "fontFamily": "Arial",
+ "fontSize": 10,
+ "fontColor": "blue",
+ "fontWeight": "bold",
+ "fontStyle": "normal",
+ "haloRadius": 2,
+ "haloColor": "white"
+ }
+ };
+ var node = parser.writers["sld"]["TextSymbolizer"].apply(
+ parser, [symbolizer["Text"]]
+ );
+
+ var expected =
+ '<TextSymbolizer xmlns="http://www.opengis.net/sld">' +
+ '<Label>' +
+ 'This is the ' +
+ '<ogc:PropertyName xmlns:ogc="http://www.opengis.net/ogc">city</ogc:PropertyName>' +
+ ' in ' +
+ '<ogc:PropertyName xmlns:ogc="http://www.opengis.net/ogc">state</ogc:PropertyName>' +
+ '.' +
+ '</Label>' +
+ '<Font>' +
+ '<CssParameter name="font-family">Arial</CssParameter>' +
+ '<CssParameter name="font-size">10</CssParameter>' +
+ '<CssParameter name="font-weight">bold</CssParameter>' +
+ '<CssParameter name="font-style">normal</CssParameter>' +
+ '</Font>' +
+ '<Halo>' +
+ '<Radius>2</Radius>' +
+ '<Fill>' +
+ '<CssParameter name="fill">white</CssParameter>' +
+ '</Fill>' +
+ '</Halo>' +
+ '<Fill>' +
+ '<CssParameter name="fill">blue</CssParameter>' +
+ '</Fill>' +
+ '</TextSymbolizer>';
+
+ t.xml_eq(node, expected, "TextSymbolizer correctly written");
+
+ }
+
+ function test_writeSpatialFilter(t) {
+
+ t.plan(1);
+
+ var format = new OpenLayers.Format.SLD.v1_0_0();
+
+ var rule = new OpenLayers.Rule({
+ name: "test",
+ filter: new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ value: new OpenLayers.Bounds(0, 0, 10, 10)
+ })
+ });
+
+ var sld = format.writeNode("sld:Rule", rule);
+
+ var expect =
+ '<Rule xmlns="http://www.opengis.net/sld">' +
+ '<Name>test</Name>' +
+ '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<ogc:BBOX>' +
+ '<gml:Box xmlns:gml="http://www.opengis.net/gml">' +
+ '<gml:coordinates decimal="." cs="," ts=" ">0,0 10,10</gml:coordinates>' +
+ '</gml:Box>' +
+ '</ogc:BBOX>' +
+ '</ogc:Filter>' +
+ '</Rule>';
+
+ t.xml_eq(sld, expect, "rule with spatial filter correctly written");
+
+ }
+
+ function test_RasterSymbolizer(t) {
+ t.plan(4);
+
+ var format = new OpenLayers.Format.SLD.v1_0_0();
+
+ var snippet =
+ '<sld:RasterSymbolizer xmlns:sld="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc">' +
+ '<sld:Geometry>' +
+ '<ogc:PropertyName>geom</ogc:PropertyName>' +
+ '</sld:Geometry>' +
+ '<sld:Opacity>1</sld:Opacity>' +
+ '<sld:ColorMap>' +
+ '<sld:ColorMapEntry color="#000000" opacity="0.5" quantity="0" label="nodata"/>' +
+ '<sld:ColorMapEntry color="#00FFFF" quantity="1" label="values"/>' +
+ '<sld:ColorMapEntry color="#FF0000" quantity="1000" label="values"/>' +
+ '</sld:ColorMap>' +
+ '</sld:RasterSymbolizer>';
+ var expected = new OpenLayers.Format.XML().read(snippet).documentElement;
+
+ var symbolizer = {};
+ format.readNode(expected, {symbolizer: symbolizer});
+
+ t.eq(symbolizer.Raster.colorMap[0].quantity, 0, "quantity set correctly");
+ t.eq(symbolizer.Raster.colorMap[0].opacity, 0.5, "opacity set correctly");
+ t.eq(symbolizer.Raster.colorMap[1].opacity, undefined, "non-existent opacity results in undefined");
+
+ var got = format.writeNode("sld:RasterSymbolizer", symbolizer["Raster"]);
+
+ t.xml_eq(got, expected, "Successfully round tripped RasterSymbolizer");
+ }
+
+ function test_zIndex(t) {
+ t.plan(1);
+
+ var format = new OpenLayers.Format.SLD.v1_0_0({
+ multipleSymbolizers: true
+ });
+
+ // three zIndex values -> three FeatureTypeStyle elements
+ var style = new OpenLayers.Style2({
+ rules: [
+ new OpenLayers.Rule({
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "foo",
+ value: "bar"
+ }),
+ minScaleDenominator: 100000,
+ maxScaleDenominator: 200000,
+ symbolizers: [
+ new OpenLayers.Symbolizer.Line({
+ strokeColor: "green",
+ strokeWidth: 2,
+ zIndex: 2
+ }),
+ new OpenLayers.Symbolizer.Line({
+ strokeColor: "red",
+ strokeWidth: 3,
+ zIndex: -1
+ }),
+ new OpenLayers.Symbolizer.Line({
+ strokeColor: "blue",
+ strokeWidth: 1,
+ zIndex: 5
+ })
+ ]
+ }),
+ new OpenLayers.Rule({
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "foo",
+ value: "baz"
+ }),
+ symbolizers: [
+ new OpenLayers.Symbolizer.Line({
+ strokeColor: "#000000",
+ strokeWidth: 2,
+ zIndex: 2
+ })
+ ]
+ })
+ ]
+ });
+
+ var got = format.writeNode("sld:UserStyle", style);
+ var exp = readXML("zindex_test.sld").documentElement;
+ t.xml_eq(got, exp, "duplicated rules to write zIndex as FeatureTypeStyle elements");
+
+ }
+
+ function test_whitespace(t) {
+ t.plan(1);
+ var xml = readXML("propertyisbetweenwhitespace.sld");
+ var output = new OpenLayers.Format.SLD().read(xml);
+ var filter = output.namedLayers['geonode:US_Stat0'].userStyles[0].rules[0].filter;
+ t.eq(filter.lowerBoundary, 29.7, "whitespace ignored in values and value transformed to number");
+ }
+
+ function test_label_LinePlacement(t) {
+ t.plan(1);
+ var format = new OpenLayers.Format.SLD.v1_0_0({
+ multipleSymbolizers: true
+ });
+ // labelPerpendicularOffset takes precedence over labelAlign
+ var style = new OpenLayers.Style2({
+ rules: [
+ new OpenLayers.Rule({
+ symbolizers: [
+ new OpenLayers.Symbolizer.Line({
+ strokeColor: "red",
+ strokeWidth: 3
+ }),
+ new OpenLayers.Symbolizer.Text({
+ label: "${FOO}",
+ labelPerpendicularOffset: 10,
+ labelAlign: "rb"
+ })
+ ]
+ })
+ ]
+ });
+ var got = format.writeNode("sld:UserStyle", style);
+ var exp = readXML("label_lineplacement_test.sld").documentElement;
+ t.xml_eq(got, exp, "LinePlacement written out correctly");
+ }
+
+ function test_labelAlignToAnchorPosition(t) {
+ t.plan(1);
+ var format = new OpenLayers.Format.SLD.v1_0_0({
+ multipleSymbolizers: true
+ });
+ var style = new OpenLayers.Style2({
+ rules: [
+ new OpenLayers.Rule({
+ symbolizers: [
+ new OpenLayers.Symbolizer.Text({
+ label: "${FOO}",
+ labelAlign: "rb"
+ })
+ ]
+ })
+ ]
+ });
+ var got = format.writeNode("sld:UserStyle", style);
+ var exp = readXML("label_pointplacement_test.sld").documentElement;
+ t.xml_eq(got, exp, "PointPlacement with labelAlign written out correctly");
+ }
+
+ function test_read_FeatureTypeStyles(t) {
+
+ t.plan(13);
+
+ var format = new OpenLayers.Format.SLD.v1_0_0({
+ multipleSymbolizers: true,
+ namedLayersAsArray: true
+ });
+ var doc = readXML("line_linewithborder.sld");
+
+ var obj = format.read(doc);
+
+ t.eq(obj.namedLayers.length, 1, "got one named layer");
+ var namedLayer = obj.namedLayers[0];
+
+ t.eq(namedLayer.userStyles.length, 1, "got one user style");
+ var userStyle = namedLayer.userStyles[0];
+ t.ok(userStyle instanceof OpenLayers.Style2, "user style represented with OpenLayers.Style2");
+
+ // check rules and symbolizers
+ var rule, symbolizer;
+
+ t.eq(userStyle.rules.length, 2, "pulled two rules (from two FeatureTypeStyle elements)");
+ rule = userStyle.rules[0];
+ t.ok(rule instanceof OpenLayers.Rule, "first rule is an OpenLayers.Rule");
+
+ t.eq(rule.symbolizers && rule.symbolizers.length, 1, "first rule has one symbolizer");
+ symbolizer = rule.symbolizers[0];
+ t.ok(symbolizer instanceof OpenLayers.Symbolizer, "first symbolizer in first rule is an OpenLayers.Symbolizer");
+ t.ok(symbolizer instanceof OpenLayers.Symbolizer.Line, "first symbolizer in first rule is an OpenLayers.Symbolizer.Line");
+ t.eq(symbolizer.zIndex, 0, "symbolizer from first FeatureTypeStyle element has zIndex 0");
+
+ rule = userStyle.rules[1];
+ t.eq(rule.symbolizers && rule.symbolizers.length, 1, "second rule has one symbolizer");
+ symbolizer = rule.symbolizers[0];
+ t.ok(symbolizer instanceof OpenLayers.Symbolizer, "first symbolizer in second rule is an OpenLayers.Symbolizer");
+ t.ok(symbolizer instanceof OpenLayers.Symbolizer.Line, "first symbolizer in second rule is an OpenLayers.Symbolizer.Line");
+ t.eq(symbolizer.zIndex, 1, "symbolizer from second FeatureTypeStyle element has zIndex 1");
+
+ }
+
+ function test_roundtrip(t) {
+
+ t.plan(5);
+
+ var format = new OpenLayers.Format.SLD.v1_0_0({
+ multipleSymbolizers: true,
+ namedLayersAsArray: true
+ });
+ var doc, out;
+
+ // two FeatureTypeStyle elements and line symbolizers
+ doc = readXML("line_linewithborder.sld");
+ out = format.write(format.read(doc));
+ t.xml_eq(out, doc.documentElement, "round-tripped line_linewithborder.sld");
+
+ // three FeatureTypeStyle elements and line symbolizers
+ doc = readXML("line_attributebasedline.sld");
+ out = format.write(format.read(doc));
+ t.xml_eq(out, doc.documentElement, "round-tripped line_attributebasedline.sld");
+
+ // point symbolizer and text symbolizer
+ doc = readXML("point_pointwithdefaultlabel.sld");
+ out = format.write(format.read(doc));
+ t.xml_eq(out, doc.documentElement, "round-tripped point_pointwithdefaultlabel.sld");
+
+ // polygon symbolizer with fill only
+ doc = readXML("polygon_simplepolygon.sld");
+ out = format.write(format.read(doc));
+ t.xml_eq(out, doc.documentElement, "round-tripped polygon_simplepolygon.sld");
+
+ // polygon symbolizer and text symbolizer with halo
+ doc = readXML("polygon_labelhalo.sld");
+ out = format.write(format.read(doc));
+ t.xml_eq(out, doc.documentElement, "round-tripped polygon_labelhalo.sld");
+ }
+
+ </script>
+</head>
+<body>
+<div id="line_linewithborder.sld"><!--
+<StyledLayerDescriptor version="1.0.0"
+ xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd"
+ xmlns="http://www.opengis.net/sld"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <NamedLayer>
+ <Name>Line with border</Name>
+ <UserStyle>
+ <Title>SLD Cook Book: Line w2th border</Title>
+ <FeatureTypeStyle>
+ <Rule>
+ <LineSymbolizer>
+ <Stroke>
+ <CssParameter name="stroke">#333333</CssParameter>
+ <CssParameter name="stroke-width">5</CssParameter>
+ <CssParameter name="stroke-linecap">round</CssParameter>
+ </Stroke>
+ </LineSymbolizer>
+ </Rule>
+ </FeatureTypeStyle>
+ <FeatureTypeStyle>
+ <Rule>
+ <LineSymbolizer>
+ <Stroke>
+ <CssParameter name="stroke">#6699FF</CssParameter>
+ <CssParameter name="stroke-width">3</CssParameter>
+ <CssParameter name="stroke-linecap">round</CssParameter>
+ </Stroke>
+ </LineSymbolizer>
+ </Rule>
+ </FeatureTypeStyle>
+ </UserStyle>
+ </NamedLayer>
+</StyledLayerDescriptor>
+--></div>
+<div id="line_attributebasedline.sld"><!--
+<StyledLayerDescriptor version="1.0.0"
+ xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd"
+ xmlns="http://www.opengis.net/sld"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <NamedLayer>
+ <Name>Attribute-based line</Name>
+ <UserStyle>
+ <Title>SLD Cook Book: Attribute-based line</Title>
+ <FeatureTypeStyle>
+ <Rule>
+ <Name>local-road</Name>
+ <ogc:Filter>
+ <ogc:PropertyIsEqualTo>
+ <ogc:PropertyName>type</ogc:PropertyName>
+ <ogc:Literal>local-road</ogc:Literal>
+ </ogc:PropertyIsEqualTo>
+ </ogc:Filter>
+ <LineSymbolizer>
+ <Stroke>
+ <CssParameter name="stroke">#009933</CssParameter>
+ <CssParameter name="stroke-width">2</CssParameter>
+ </Stroke>
+ </LineSymbolizer>
+ </Rule>
+ </FeatureTypeStyle>
+ <FeatureTypeStyle>
+ <Rule>
+ <Name>secondary</Name>
+ <ogc:Filter>
+ <ogc:PropertyIsEqualTo>
+ <ogc:PropertyName>type</ogc:PropertyName>
+ <ogc:Literal>secondary</ogc:Literal>
+ </ogc:PropertyIsEqualTo>
+ </ogc:Filter>
+ <LineSymbolizer>
+ <Stroke>
+ <CssParameter name="stroke">#0055CC</CssParameter>
+ <CssParameter name="stroke-width">3</CssParameter>
+ </Stroke>
+ </LineSymbolizer>
+ </Rule>
+ </FeatureTypeStyle>
+ <FeatureTypeStyle>
+ <Rule>
+ <Name>highway</Name>
+ <ogc:Filter>
+ <ogc:PropertyIsEqualTo>
+ <ogc:PropertyName>type</ogc:PropertyName>
+ <ogc:Literal>highway</ogc:Literal>
+ </ogc:PropertyIsEqualTo>
+ </ogc:Filter>
+ <LineSymbolizer>
+ <Stroke>
+ <CssParameter name="stroke">#FF0000</CssParameter>
+ <CssParameter name="stroke-width">6</CssParameter>
+ </Stroke>
+ </LineSymbolizer>
+ </Rule>
+ </FeatureTypeStyle>
+ </UserStyle>
+ </NamedLayer>
+</StyledLayerDescriptor>
+--></div>
+<div id="point_pointwithdefaultlabel.sld"><!--
+<StyledLayerDescriptor version="1.0.0"
+ xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd"
+ xmlns="http://www.opengis.net/sld"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <NamedLayer>
+ <Name>Point with default label</Name>
+ <UserStyle>
+ <Title>GeoServer SLD Cook Book: Point with default label</Title>
+ <FeatureTypeStyle>
+ <Rule>
+ <PointSymbolizer>
+ <Graphic>
+ <Mark>
+ <WellKnownName>circle</WellKnownName>
+ <Fill>
+ <CssParameter name="fill">#FF0000</CssParameter>
+ </Fill>
+ </Mark>
+ <Size>6</Size>
+ </Graphic>
+ </PointSymbolizer>
+ <TextSymbolizer>
+ <Label>
+ <ogc:PropertyName>name</ogc:PropertyName>
+ </Label>
+ <Fill>
+ <CssParameter name="fill">#000000</CssParameter>
+ </Fill>
+ </TextSymbolizer>
+ </Rule>
+ </FeatureTypeStyle>
+ </UserStyle>
+ </NamedLayer>
+</StyledLayerDescriptor>
+--></div>
+<div id="polygon_simplepolygon.sld"><!--
+<StyledLayerDescriptor version="1.0.0"
+ xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd"
+ xmlns="http://www.opengis.net/sld"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <NamedLayer>
+ <Name>Simple polygon</Name>
+ <UserStyle>
+ <Title>SLD Cook Book: Simple polygon</Title>
+ <FeatureTypeStyle>
+ <Rule>
+ <PolygonSymbolizer>
+ <Fill>
+ <CssParameter name="fill">#000080</CssParameter>
+ </Fill>
+ </PolygonSymbolizer>
+ </Rule>
+ </FeatureTypeStyle>
+ </UserStyle>
+ </NamedLayer>
+</StyledLayerDescriptor>
+--></div>
+<div id="polygon_labelhalo.sld"><!--
+<StyledLayerDescriptor version="1.0.0"
+ xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd"
+ xmlns="http://www.opengis.net/sld"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <NamedLayer>
+ <Name>Label halo</Name>
+ <UserStyle>
+ <Title>SLD Cook Book: Label halo</Title>
+ <FeatureTypeStyle>
+ <Rule>
+ <PolygonSymbolizer>
+ <Fill>
+ <CssParameter name="fill">#40FF40</CssParameter>
+ </Fill>
+ <Stroke>
+ <CssParameter name="stroke">#FFFFFF</CssParameter>
+ <CssParameter name="stroke-width">2</CssParameter>
+ </Stroke>
+ </PolygonSymbolizer>
+ <TextSymbolizer>
+ <Label>
+ <ogc:PropertyName>name</ogc:PropertyName>
+ </Label>
+ <Halo>
+ <Radius>3</Radius>
+ <Fill>
+ <CssParameter name="fill">#FFFFFF</CssParameter>
+ </Fill>
+ </Halo>
+ </TextSymbolizer>
+ </Rule>
+ </FeatureTypeStyle>
+ </UserStyle>
+ </NamedLayer>
+</StyledLayerDescriptor>
+--></div>
+<div id="zindex_test.sld"><!--
+<sld:UserStyle xmlns:sld="http://www.opengis.net/sld">
+ <sld:FeatureTypeStyle>
+ <sld:Rule>
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:PropertyIsEqualTo>
+ <ogc:PropertyName>foo</ogc:PropertyName>
+ <ogc:Literal>bar</ogc:Literal>
+ </ogc:PropertyIsEqualTo>
+ </ogc:Filter>
+ <sld:MinScaleDenominator>100000</sld:MinScaleDenominator>
+ <sld:MaxScaleDenominator>200000</sld:MaxScaleDenominator>
+ <sld:LineSymbolizer>
+ <sld:Stroke>
+ <sld:CssParameter name="stroke">red</sld:CssParameter>
+ <sld:CssParameter name="stroke-width">3</sld:CssParameter>
+ </sld:Stroke>
+ </sld:LineSymbolizer>
+ </sld:Rule>
+ </sld:FeatureTypeStyle>
+ <sld:FeatureTypeStyle>
+ <sld:Rule>
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:PropertyIsEqualTo>
+ <ogc:PropertyName>foo</ogc:PropertyName>
+ <ogc:Literal>bar</ogc:Literal>
+ </ogc:PropertyIsEqualTo>
+ </ogc:Filter>
+ <sld:MinScaleDenominator>100000</sld:MinScaleDenominator>
+ <sld:MaxScaleDenominator>200000</sld:MaxScaleDenominator>
+ <sld:LineSymbolizer>
+ <sld:Stroke>
+ <sld:CssParameter name="stroke">green</sld:CssParameter>
+ <sld:CssParameter name="stroke-width">2</sld:CssParameter>
+ </sld:Stroke>
+ </sld:LineSymbolizer>
+ </sld:Rule>
+ <sld:Rule>
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:PropertyIsEqualTo>
+ <ogc:PropertyName>foo</ogc:PropertyName>
+ <ogc:Literal>baz</ogc:Literal>
+ </ogc:PropertyIsEqualTo>
+ </ogc:Filter>
+ <sld:LineSymbolizer>
+ <sld:Stroke>
+ <sld:CssParameter name="stroke">#000000</sld:CssParameter>
+ <sld:CssParameter name="stroke-width">2</sld:CssParameter>
+ </sld:Stroke>
+ </sld:LineSymbolizer>
+ </sld:Rule>
+ </sld:FeatureTypeStyle>
+ <sld:FeatureTypeStyle>
+ <sld:Rule>
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:PropertyIsEqualTo>
+ <ogc:PropertyName>foo</ogc:PropertyName>
+ <ogc:Literal>bar</ogc:Literal>
+ </ogc:PropertyIsEqualTo>
+ </ogc:Filter>
+ <sld:MinScaleDenominator>100000</sld:MinScaleDenominator>
+ <sld:MaxScaleDenominator>200000</sld:MaxScaleDenominator>
+ <sld:LineSymbolizer>
+ <sld:Stroke>
+ <sld:CssParameter name="stroke">blue</sld:CssParameter>
+ <sld:CssParameter name="stroke-width">1</sld:CssParameter>
+ </sld:Stroke>
+ </sld:LineSymbolizer>
+ </sld:Rule>
+ </sld:FeatureTypeStyle>
+</sld:UserStyle>
+--></div>
+<div id="label_lineplacement_test.sld"><!--
+<sld:UserStyle xmlns:sld="http://www.opengis.net/sld">
+ <sld:FeatureTypeStyle>
+ <sld:Rule>
+ <sld:LineSymbolizer>
+ <sld:Stroke>
+ <sld:CssParameter name="stroke">red</sld:CssParameter>
+ <sld:CssParameter name="stroke-width">3</sld:CssParameter>
+ </sld:Stroke>
+ </sld:LineSymbolizer>
+ <sld:TextSymbolizer>
+ <sld:Label><ogc:PropertyName xmlns:ogc="http://www.opengis.net/ogc">FOO</ogc:PropertyName></sld:Label>
+ <sld:LabelPlacement>
+ <sld:LinePlacement>
+ <sld:PerpendicularOffset>10</sld:PerpendicularOffset>
+ </sld:LinePlacement>
+ </sld:LabelPlacement>
+ </sld:TextSymbolizer>
+ </sld:Rule>
+ </sld:FeatureTypeStyle>
+</sld:UserStyle>
+--></div>
+<div id="label_pointplacement_test.sld"><!--
+<sld:UserStyle xmlns:sld="http://www.opengis.net/sld">
+ <sld:FeatureTypeStyle>
+ <sld:Rule>
+ <sld:TextSymbolizer>
+ <sld:Label><ogc:PropertyName xmlns:ogc="http://www.opengis.net/ogc">FOO</ogc:PropertyName></sld:Label>
+ <sld:LabelPlacement>
+ <sld:PointPlacement>
+ <sld:AnchorPoint>
+ <sld:AnchorPointX>1</sld:AnchorPointX>
+ <sld:AnchorPointY>0</sld:AnchorPointY>
+ </sld:AnchorPoint>
+ </sld:PointPlacement>
+ </sld:LabelPlacement>
+ </sld:TextSymbolizer>
+ </sld:Rule>
+ </sld:FeatureTypeStyle>
+</sld:UserStyle>
+--></div>
+<div id="propertyisbetweenwhitespace.sld"><!--
+<sld:StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:sld="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml" version="1.0.0">
+ <sld:NamedLayer>
+ <sld:Name>geonode:US_Stat0</sld:Name>
+ <sld:UserStyle>
+ <sld:Name>US_Stat0_5cbbe918</sld:Name>
+ <sld:Title>BMI&lt;25</sld:Title>
+ <sld:FeatureTypeStyle>
+ <sld:Name>name</sld:Name>
+ <sld:Rule>
+ <sld:Title>BMI&lt;25</sld:Title>
+ <ogc:Filter>
+ <ogc:PropertyIsBetween>
+ <ogc:PropertyName>Hlt_st_BMI</ogc:PropertyName>
+ <ogc:LowerBoundary>
+ <ogc:Literal>
+
+
+ 29.7
+
+
+ </ogc:Literal>
+ </ogc:LowerBoundary>
+ <ogc:UpperBoundary>
+ <ogc:Literal>
+
+
+ 36.2
+
+
+ </ogc:Literal>
+ </ogc:UpperBoundary>
+ </ogc:PropertyIsBetween>
+ </ogc:Filter>
+ <sld:PolygonSymbolizer>
+ <sld:Fill>
+ <sld:CssParameter name="fill">#C0F58C</sld:CssParameter>
+ </sld:Fill>
+ <sld:Stroke/>
+ </sld:PolygonSymbolizer>
+ </sld:Rule>
+ </sld:FeatureTypeStyle>
+ </sld:UserStyle>
+ </sld:NamedLayer>
+</sld:StyledLayerDescriptor>
+--></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/SLD/v1_0_0_GeoServer.html b/misc/openlayers/tests/Format/SLD/v1_0_0_GeoServer.html
new file mode 100644
index 0000000..96a3ef6
--- /dev/null
+++ b/misc/openlayers/tests/Format/SLD/v1_0_0_GeoServer.html
@@ -0,0 +1,228 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var xml = new OpenLayers.Format.XML();
+ function readXML(id) {
+ return xml.read(document.getElementById(id).firstChild.nodeValue);
+ }
+
+ function test_VendorExtensions(t) {
+
+ var cases = [
+ "poly_label.sld"
+ ];
+ var len = cases.length;
+ t.plan(len+1);
+
+ var format = new OpenLayers.Format.SLD({
+ profile: "GeoServer",
+ multipleSymbolizers: true,
+ namedLayersAsArray: true,
+ schemaLocation: "http://www.opengis.net/sld StyledLayerDescriptor.xsd"
+ });
+
+ var c, doc, data, out;
+ for (var i=0; i<len; ++i) {
+ c = cases[i];
+ doc = readXML(c);
+ data = format.read(doc);
+ out = format.write(data);
+ t.xml_eq(out, doc.documentElement, "round-tripped " + c);
+ }
+ doc = readXML("poly_label.sld");
+ data = format.read(doc);
+ data.namedLayers[0].userStyles[0].rules[0].symbolizers[1].graphic = false;
+ out = format.write(data);
+ t.xml_eq(out, readXML("poly_label_nographic.sld").documentElement, "If graphic is false no Graphic is outputted");
+ }
+
+ function test_readTextSymbolizer(t) {
+ t.plan(1);
+ var format = new OpenLayers.Format.SLD({
+ profile: "GeoServer",
+ multipleSymbolizers: true,
+ namedLayersAsArray: true
+ });
+ doc = readXML("point_pointwithdefaultlabel.sld");
+ var sld = format.read(doc);
+ t.eq(sld.namedLayers[0].userStyles[0].rules[0].symbolizers[1].graphic, false, "graphic set to false on TextSymbolizer");
+ }
+
+ </script>
+</head>
+<body>
+<div id="poly_label.sld"><!--
+<StyledLayerDescriptor version="1.0.0"
+ xsi:schemaLocation="http://www.opengis.net/sld StyledLayerDescriptor.xsd"
+ xmlns="http://www.opengis.net/sld"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <NamedLayer>
+ <Name>Polygon with styled label</Name>
+ <UserStyle>
+ <Title>SLD Cook Book: Polygon with styled label</Title>
+ <FeatureTypeStyle>
+ <Rule>
+ <PolygonSymbolizer>
+ <Fill>
+ <CssParameter name="fill">#40FF40</CssParameter>
+ </Fill>
+ <Stroke>
+ <CssParameter name="stroke">#FFFFFF</CssParameter>
+ <CssParameter name="stroke-width">2</CssParameter>
+ </Stroke>
+ </PolygonSymbolizer>
+ <TextSymbolizer>
+ <Label>
+ <ogc:PropertyName>name</ogc:PropertyName>
+ </Label>
+ <Font>
+ <CssParameter name="font-family">Arial</CssParameter>
+ <CssParameter name="font-size">11</CssParameter>
+ <CssParameter name="font-weight">bold</CssParameter>
+ <CssParameter name="font-style">normal</CssParameter>
+ </Font>
+ <Fill>
+ <CssParameter name="fill">#000000</CssParameter>
+ <CssParameter name="fill-opacity">0.5</CssParameter>
+ </Fill>
+ <Graphic>
+ <Mark>
+ <WellKnownName>square</WellKnownName>
+ <Fill>
+ <CssParameter name="fill">#59BF34</CssParameter>
+ <CssParameter name="fill-opacity">0.8</CssParameter>
+ </Fill>
+ <Stroke>
+ <CssParameter name="stroke">#2D6917</CssParameter>
+ </Stroke>
+ </Mark>
+ <Size>24</Size>
+ </Graphic>
+ <Priority>
+ <ogc:PropertyName>population</ogc:PropertyName>
+ </Priority>
+ <VendorOption name="autoWrap">60</VendorOption>
+ <VendorOption name="followLine">true</VendorOption>
+ <VendorOption name="repeat">300</VendorOption>
+ <VendorOption name="maxDisplacement">150</VendorOption>
+ <VendorOption name="forceLeftToRight">false</VendorOption>
+ <VendorOption name="graphic-margin">3</VendorOption>
+ <VendorOption name="graphic-resize">stretch</VendorOption>
+ <VendorOption name="group">yes</VendorOption>
+ <VendorOption name="spaceAround">10</VendorOption>
+ <VendorOption name="labelAllGroup">true</VendorOption>
+ <VendorOption name="maxAngleDelta">15</VendorOption>
+ <VendorOption name="conflictResolution">false</VendorOption>
+ <VendorOption name="goodnessOfFit">0.3</VendorOption>
+ <VendorOption name="polygonAlign">mbr</VendorOption>
+ </TextSymbolizer>
+ </Rule>
+ </FeatureTypeStyle>
+ </UserStyle>
+ </NamedLayer>
+</StyledLayerDescriptor>
+--></div>
+<div id="poly_label_nographic.sld"><!--
+<StyledLayerDescriptor version="1.0.0"
+ xsi:schemaLocation="http://www.opengis.net/sld StyledLayerDescriptor.xsd"
+ xmlns="http://www.opengis.net/sld"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <NamedLayer>
+ <Name>Polygon with styled label</Name>
+ <UserStyle>
+ <Title>SLD Cook Book: Polygon with styled label</Title>
+ <FeatureTypeStyle>
+ <Rule>
+ <PolygonSymbolizer>
+ <Fill>
+ <CssParameter name="fill">#40FF40</CssParameter>
+ </Fill>
+ <Stroke>
+ <CssParameter name="stroke">#FFFFFF</CssParameter>
+ <CssParameter name="stroke-width">2</CssParameter>
+ </Stroke>
+ </PolygonSymbolizer>
+ <TextSymbolizer>
+ <Label>
+ <ogc:PropertyName>name</ogc:PropertyName>
+ </Label>
+ <Font>
+ <CssParameter name="font-family">Arial</CssParameter>
+ <CssParameter name="font-size">11</CssParameter>
+ <CssParameter name="font-weight">bold</CssParameter>
+ <CssParameter name="font-style">normal</CssParameter>
+ </Font>
+ <Fill>
+ <CssParameter name="fill">#000000</CssParameter>
+ <CssParameter name="fill-opacity">0.5</CssParameter>
+ </Fill>
+ <Priority>
+ <ogc:PropertyName>population</ogc:PropertyName>
+ </Priority>
+ <VendorOption name="autoWrap">60</VendorOption>
+ <VendorOption name="followLine">true</VendorOption>
+ <VendorOption name="repeat">300</VendorOption>
+ <VendorOption name="maxDisplacement">150</VendorOption>
+ <VendorOption name="forceLeftToRight">false</VendorOption>
+ <VendorOption name="graphic-margin">3</VendorOption>
+ <VendorOption name="graphic-resize">stretch</VendorOption>
+ <VendorOption name="group">yes</VendorOption>
+ <VendorOption name="spaceAround">10</VendorOption>
+ <VendorOption name="labelAllGroup">true</VendorOption>
+ <VendorOption name="maxAngleDelta">15</VendorOption>
+ <VendorOption name="conflictResolution">false</VendorOption>
+ <VendorOption name="goodnessOfFit">0.3</VendorOption>
+ <VendorOption name="polygonAlign">mbr</VendorOption>
+ </TextSymbolizer>
+ </Rule>
+ </FeatureTypeStyle>
+ </UserStyle>
+ </NamedLayer>
+</StyledLayerDescriptor>
+--></div>
+<div id="point_pointwithdefaultlabel.sld"><!--
+<StyledLayerDescriptor version="1.0.0"
+ xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd"
+ xmlns="http://www.opengis.net/sld"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <NamedLayer>
+ <Name>Point with default label</Name>
+ <UserStyle>
+ <Title>GeoServer SLD Cook Book: Point with default label</Title>
+ <FeatureTypeStyle>
+ <Rule>
+ <PointSymbolizer>
+ <Graphic>
+ <Mark>
+ <WellKnownName>circle</WellKnownName>
+ <Fill>
+ <CssParameter name="fill">#FF0000</CssParameter>
+ </Fill>
+ </Mark>
+ <Size>6</Size>
+ </Graphic>
+ </PointSymbolizer>
+ <TextSymbolizer>
+ <Label>
+ <ogc:PropertyName>name</ogc:PropertyName>
+ </Label>
+ <Fill>
+ <CssParameter name="fill">#000000</CssParameter>
+ </Fill>
+ </TextSymbolizer>
+ </Rule>
+ </FeatureTypeStyle>
+ </UserStyle>
+ </NamedLayer>
+</StyledLayerDescriptor>
+--></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/SOSCapabilities/v1_0_0.html b/misc/openlayers/tests/Format/SOSCapabilities/v1_0_0.html
new file mode 100644
index 0000000..6713685
--- /dev/null
+++ b/misc/openlayers/tests/Format/SOSCapabilities/v1_0_0.html
@@ -0,0 +1,80 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script src="v1_0_0.js"></script>
+ <script type="text/javascript">
+
+ function test_read(t) {
+
+ t.plan(41);
+
+ var format = new OpenLayers.Format.SOSCapabilities();
+ var obj = format.read(doc);
+
+ t.eq(obj.version, "1.0.0", "Version parsed correctly");
+
+ // service identification (from OWSCommon)
+ t.eq(obj.serviceIdentification.abstract, "WeatherSOS (stable) at IfGI, Muenster, Germany. For more info: http://ifgipedia.uni-muenster.de/kms/documentation/swsl/sos/", "Abstract parsed correctly");
+ t.eq(obj.serviceIdentification.accessConstraints, "NONE", "AccessConstraints parsed correctly");
+ t.eq(obj.serviceIdentification.fees, "NONE", "Fees parsed correctly");
+ for (var key in obj.serviceIdentification.keywords) {
+ t.eq(key, "rain gauge, radiation, pressure, windspeed, winddirection, temperature", "Keywords parsed correctly");
+ }
+ t.eq(obj.serviceIdentification.serviceType.codeSpace, "http://opengeospatial.net", "codeSpace correctly parsed");
+ t.eq(obj.serviceIdentification.serviceType.value, "OGC:SOS", "ServiceType correctly parsed");
+ t.eq(obj.serviceIdentification.serviceTypeVersion, "1.0.0", "ServiceTypeVersion correctly parsed");
+ t.eq(obj.serviceIdentification.title, "IFGI WeatherSOS (stable)", "Title correctly parsed");
+
+ // service provider (from OWSCommon)
+ t.eq(obj.serviceProvider.providerName, "Institute for Geoinformatics, University of Muenster", "ProviderName correctly parsed");
+ t.eq(obj.serviceProvider.providerSite, "http://ifgi.uni-muenster.de", "ProviderSite correctly parsed");
+ t.eq(obj.serviceProvider.serviceContact.individualName, "Eike Hinderk Juerrens", "IndividualName parsed correctly");
+ t.eq(obj.serviceProvider.serviceContact.positionName, "Student Associate", "PositionName parsed correctly");
+ t.eq(obj.serviceProvider.serviceContact.role, "", "Role parsed correctly");
+ t.eq(obj.serviceProvider.serviceContact.contactInfo.address.administrativeArea, "NRW", "AdministrativeArea correctly parsed");
+ t.eq(obj.serviceProvider.serviceContact.contactInfo.address.city, "Muenster", "City correctly parsed");
+ t.eq(obj.serviceProvider.serviceContact.contactInfo.address.country, "Germany", "Country correctly parsed");
+ t.eq(obj.serviceProvider.serviceContact.contactInfo.address.deliveryPoint, "Weselerstrasse 253", "DeliveryPoint correctly parsed");
+ t.eq(obj.serviceProvider.serviceContact.contactInfo.address.electronicMailAddress, "ehjuerrens@uni-muenster.de", "ElectronicMailAddress correctly parsed");
+ t.eq(obj.serviceProvider.serviceContact.contactInfo.address.postalCode, "48149", "Postalcode correctly parsed");
+ t.eq(obj.serviceProvider.serviceContact.contactInfo.phone.voice, "+49-251-83-30088", "Voice phone correctly parsed");
+
+ // operationsMetadata (from OWSCommon)
+ t.eq(obj.operationsMetadata.DescribeSensor.dcp.http.post[0].url, "http://v-swe.uni-muenster.de:8080/WeatherSOS/sos", "POST url for DescribeSensor correctly parsed");
+ var counter = 0;
+ for (var key in obj.operationsMetadata.DescribeSensor.parameters.procedure.allowedValues) {
+ if (counter == 0) {
+ t.eq(key, "urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111", "Allowed value (1) for procedure parameter in DescribeSensor request correctly parsed");
+ } else if (counter == 1) {
+ t.eq(key, "urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93", "Allowed value (2) for procedure parameter in DescribeSensor request correctly parsed");
+ }
+ counter++;
+ }
+ t.eq(obj.operationsMetadata.GetFeatureOfInterest.parameters.location.anyValue, true, "AnyValue parsed correctly");
+
+ t.eq(obj.operationsMetadata.GetObservation.parameters.eventTime.allowedValues.range.maxValue, "2009-11-04T14:45:00+01", "Range maxValue parsed correctly");
+ t.eq(obj.operationsMetadata.GetObservation.parameters.eventTime.allowedValues.range.minValue, "2008-02-14T11:03:02+01", "Range minValue parsed correctly");
+
+ // Contents (from SOS)
+ t.eq(obj.contents.offeringList.ATMOSPHERIC_PRESSURE.name, "Pressure of the atmosphere", "Name of offering correctly parsed");
+ t.eq(obj.contents.offeringList.ATMOSPHERIC_PRESSURE.observedProperties[0], "urn:x-ogc:def:property:OGC::BarometricPressure", "ObservedProperty correctly parsed");
+ t.eq(obj.contents.offeringList.ATMOSPHERIC_PRESSURE.featureOfInterestIds[0], "urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93", "Allowed value (1) for featureOfInterest correctly parsed");
+ t.eq(obj.contents.offeringList.ATMOSPHERIC_PRESSURE.featureOfInterestIds[1], "urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111", "Allowed value (2) for featureOfInterest correctly parsed");
+ t.eq(obj.contents.offeringList.ATMOSPHERIC_PRESSURE.procedures[0], "urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93", "Allowed value (1) for procedures correctly parsed");
+ t.eq(obj.contents.offeringList.ATMOSPHERIC_PRESSURE.procedures[1], "urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111", "Allowed value (2) for procedures correctly parsed");
+ t.eq(obj.contents.offeringList.ATMOSPHERIC_PRESSURE.responseFormats[0], 'text/xml;subtype="om/1.0.0"', "Allowed value (1) for responseFormats correctly parsed");
+ t.eq(obj.contents.offeringList.ATMOSPHERIC_PRESSURE.responseFormats[1], "application/zip", "Allowed value (2) for responseFormats correctly parsed");
+ t.eq(obj.contents.offeringList.ATMOSPHERIC_PRESSURE.responseModes[0], "inline", "Allowed value (1) for responseModes correctly parsed");
+ t.eq(obj.contents.offeringList.ATMOSPHERIC_PRESSURE.responseModes[1], "resultTemplate", "Allowed value (2) for responseModes correctly parsed");
+ t.eq(obj.contents.offeringList.ATMOSPHERIC_PRESSURE.resultModels[0], "ns:Measurement", "Allowed value (1) for resultModels correctly parsed");
+ t.eq(obj.contents.offeringList.ATMOSPHERIC_PRESSURE.resultModels[1], "ns:Observation", "Allowed value (2) for resultModels correctly parsed");
+ t.eq(obj.contents.offeringList.ATMOSPHERIC_PRESSURE.time.timePeriod.beginPosition, "2008-12-20T02:29:27+01:00", "TimePeriod beginPosition correctly parsed");
+ t.eq(obj.contents.offeringList.ATMOSPHERIC_PRESSURE.time.timePeriod.endPosition, "2009-11-04T14:45:00+01:00", "TimePeriod endPosition correctly parsed");
+
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/SOSCapabilities/v1_0_0.js b/misc/openlayers/tests/Format/SOSCapabilities/v1_0_0.js
new file mode 100644
index 0000000..78556f5
--- /dev/null
+++ b/misc/openlayers/tests/Format/SOSCapabilities/v1_0_0.js
@@ -0,0 +1,484 @@
+var doc = new OpenLayers.Format.XML().read(
+'<?xml version="1.0" encoding="UTF-8"?>' +
+'<sos:Capabilities version="1.0.0" updateSequence="2005-12-14T10:12:39+01" xsi:schemaLocation="http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosAll.xsd" xmlns:sos="http://www.opengis.net/sos/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink">' +
+ '<ows:ServiceIdentification xmlns:ogc="http://www.opengis.net/ogc" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:om="http://www.opengis.net/om/1.0" xmlns:swe="http://www.opengis.net/swe/1.0">' +
+ '<ows:Title>IFGI WeatherSOS (stable)</ows:Title>' +
+ '<ows:Abstract>WeatherSOS (stable) at IfGI, Muenster, Germany. For more info: http://ifgipedia.uni-muenster.de/kms/documentation/swsl/sos/</ows:Abstract>' +
+ '<ows:Keywords>' +
+ '<ows:Keyword>rain gauge, radiation, pressure, windspeed, winddirection, temperature</ows:Keyword>' +
+ '</ows:Keywords>' +
+ '<ows:ServiceType codeSpace="http://opengeospatial.net">OGC:SOS</ows:ServiceType>' +
+ '<ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion>' +
+ '<ows:Fees>NONE</ows:Fees>' +
+ '<ows:AccessConstraints>NONE</ows:AccessConstraints>' +
+ '</ows:ServiceIdentification>' +
+ '<ows:ServiceProvider xmlns:ogc="http://www.opengis.net/ogc" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:om="http://www.opengis.net/om/1.0" xmlns:swe="http://www.opengis.net/swe/1.0">' +
+ '<ows:ProviderName>Institute for Geoinformatics, University of Muenster</ows:ProviderName>' +
+ '<ows:ProviderSite xlink:href="http://ifgi.uni-muenster.de"/>' +
+ '<ows:ServiceContact>' +
+ '<ows:IndividualName>Eike Hinderk Juerrens</ows:IndividualName>' +
+ '<ows:PositionName>Student Associate</ows:PositionName>' +
+ '<ows:ContactInfo>' +
+ '<ows:Phone>' +
+ '<ows:Voice>+49-251-83-30088</ows:Voice>' +
+ '</ows:Phone>' +
+ '<ows:Address>' +
+ '<ows:DeliveryPoint>Weselerstrasse 253</ows:DeliveryPoint>' +
+ '<ows:City>Muenster</ows:City>' +
+ '<ows:AdministrativeArea>NRW</ows:AdministrativeArea>' +
+ '<ows:PostalCode>48149</ows:PostalCode>' +
+ '<ows:Country>Germany</ows:Country>' +
+ '<ows:ElectronicMailAddress>ehjuerrens@uni-muenster.de</ows:ElectronicMailAddress>' +
+ '</ows:Address>' +
+ '</ows:ContactInfo>' +
+ '<ows:Role/>' +
+ '</ows:ServiceContact>' +
+ '</ows:ServiceProvider>' +
+ '<ows:OperationsMetadata xmlns:ogc="http://www.opengis.net/ogc" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:om="http://www.opengis.net/om/1.0" xmlns:swe="http://www.opengis.net/swe/1.0">' +
+ '<ows:Operation name="GetCapabilities">' +
+ '<ows:DCP>' +
+ '<ows:HTTP>' +
+ '<ows:Get xlink:href="http://v-swe.uni-muenster.de:8080/WeatherSOS/sos?"/>' +
+ '<ows:Post xlink:href="http://v-swe.uni-muenster.de:8080/WeatherSOS/sos"/>' +
+ '</ows:HTTP>' +
+ '</ows:DCP>' +
+ '<ows:Parameter name="service">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>SOS</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="updateSequence">' +
+ '<ows:AnyValue/>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="AcceptVersions">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>1.0.0</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="Sections">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>ServiceIdentification</ows:Value>' +
+ '<ows:Value>ServiceProvider</ows:Value>' +
+ '<ows:Value>OperationsMetadata</ows:Value>' +
+ '<ows:Value>Contents</ows:Value>' +
+ '<ows:Value>All</ows:Value>' +
+ '<ows:Value>Filter_Capabilities</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="AcceptFormats">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>text/xml</ows:Value>' +
+ '<ows:Value>application/zip</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '</ows:Operation>' +
+ '<ows:Operation name="GetObservation">' +
+ '<ows:DCP>' +
+ '<ows:HTTP>' +
+ '<ows:Post xlink:href="http://v-swe.uni-muenster.de:8080/WeatherSOS/sos"/>' +
+ '</ows:HTTP>' +
+ '</ows:DCP>' +
+ '<ows:Parameter name="version">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>1.0.0</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="service">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>SOS</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="srsName">' +
+ '<ows:AnyValue/>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="offering">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>ATMOSPHERIC_TEMPERATURE</ows:Value>' +
+ '<ows:Value>RAIN_GAUGE</ows:Value>' +
+ '<ows:Value>WIND_DIRECTION</ows:Value>' +
+ '<ows:Value>WIND_SPEED</ows:Value>' +
+ '<ows:Value>HUMIDITY</ows:Value>' +
+ '<ows:Value>LUMINANCE</ows:Value>' +
+ '<ows:Value>ATMOSPHERIC_PRESSURE</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="eventTime">' +
+ '<ows:AllowedValues>' +
+ '<ows:Range>' +
+ '<ows:MinimumValue>2008-02-14T11:03:02+01</ows:MinimumValue>' +
+ '<ows:MaximumValue>2009-11-04T14:45:00+01</ows:MaximumValue>' +
+ '</ows:Range>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="procedure">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111</ows:Value>' +
+ '<ows:Value>urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="observedProperty">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>urn:x-ogc:def:property:OGC::Temperature</ows:Value>' +
+ '<ows:Value>urn:x-ogc:def:property:OGC::Precipitation1Hour</ows:Value>' +
+ '<ows:Value>urn:x-ogc:def:property:OGC::WindDirection</ows:Value>' +
+ '<ows:Value>urn:x-ogc:def:property:OGC::WindSpeed</ows:Value>' +
+ '<ows:Value>urn:x-ogc:def:property:OGC::RelativeHumidity</ows:Value>' +
+ '<ows:Value>urn:x-ogc:def:property:OGC::Luminance</ows:Value>' +
+ '<ows:Value>urn:x-ogc:def:property:OGC::BarometricPressure</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="featureOfInterest">' +
+ '<ows:AnyValue/>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="result">' +
+ '<ows:AnyValue/>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="responseFormat">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>text/xml;subtype="OM/1.0.0"</ows:Value>' +
+ '<ows:Value>application/zip</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="resultModel">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>om:Observation</ows:Value>' +
+ '<ows:Value>om:CategoryObservation</ows:Value>' +
+ '<ows:Value>om:Measurement</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="responseMode">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>resultTemplate</ows:Value>' +
+ '<ows:Value>inline</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '</ows:Operation>' +
+ '<ows:Operation name="GetObservationById">' +
+ '<ows:DCP>' +
+ '<ows:HTTP>' +
+ '<ows:Post xlink:href="http://v-swe.uni-muenster.de:8080/WeatherSOS/sos"/>' +
+ '</ows:HTTP>' +
+ '</ows:DCP>' +
+ '<ows:Parameter name="version">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>1.0.0</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="service">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>SOS</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="srsName">' +
+ '<ows:AnyValue/>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="ObservationId">' +
+ '<ows:AnyValue/>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="responseFormat">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>text/xml;subtype="OM/1.0.0"</ows:Value>' +
+ '<ows:Value>application/zip</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="resultModel">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>om:Observation</ows:Value>' +
+ '<ows:Value>om:CategoryObservation</ows:Value>' +
+ '<ows:Value>om:Measurement</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="responseMode">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>inline</ows:Value>' +
+ '<ows:Value>resultTemplate</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '</ows:Operation>' +
+ '<ows:Operation name="DescribeSensor">' +
+ '<ows:DCP>' +
+ '<ows:HTTP>' +
+ '<ows:Post xlink:href="http://v-swe.uni-muenster.de:8080/WeatherSOS/sos"/>' +
+ '</ows:HTTP>' +
+ '</ows:DCP>' +
+ '<ows:Parameter name="version">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>1.0.0</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="service">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>SOS</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="outputFormat">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>text/xml;subtype="sensorML/1.0.1"</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="procedure">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111</ows:Value>' +
+ '<ows:Value>urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '</ows:Operation>' +
+ '<ows:Operation name="GetFeatureOfInterest">' +
+ '<ows:DCP>' +
+ '<ows:HTTP>' +
+ '<ows:Post xlink:href="http://v-swe.uni-muenster.de:8080/WeatherSOS/sos"/>' +
+ '</ows:HTTP>' +
+ '</ows:DCP>' +
+ '<ows:Parameter name="service">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>SOS</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="version">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>1.0.0</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="featureOfInterestId">' +
+ '<ows:AllowedValues>' +
+ '<ows:Value>urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93</ows:Value>' +
+ '<ows:Value>urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111</ows:Value>' +
+ '</ows:AllowedValues>' +
+ '</ows:Parameter>' +
+ '<ows:Parameter name="location">' +
+ '<ows:AnyValue/>' +
+ '</ows:Parameter>' +
+ '</ows:Operation>' +
+ '</ows:OperationsMetadata>' +
+ '<sos:Filter_Capabilities xmlns:ogc="http://www.opengis.net/ogc" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:om="http://www.opengis.net/om/1.0" xmlns:swe="http://www.opengis.net/swe/1.0">' +
+ '<ogc:Spatial_Capabilities>' +
+ '<ogc:GeometryOperands>' +
+ '<ogc:GeometryOperand>gml:Envelope</ogc:GeometryOperand>' +
+ '<ogc:GeometryOperand>gml:Polygon</ogc:GeometryOperand>' +
+ '<ogc:GeometryOperand>gml:Point</ogc:GeometryOperand>' +
+ '<ogc:GeometryOperand>gml:LineString</ogc:GeometryOperand>' +
+ '</ogc:GeometryOperands>' +
+ '<ogc:SpatialOperators>' +
+ '<ogc:SpatialOperator name="BBOX"/>' +
+ '<ogc:SpatialOperator name="Contains"/>' +
+ '<ogc:SpatialOperator name="Intersects"/>' +
+ '<ogc:SpatialOperator name="Overlaps"/>' +
+ '</ogc:SpatialOperators>' +
+ '</ogc:Spatial_Capabilities>' +
+ '<ogc:Temporal_Capabilities>' +
+ '<ogc:TemporalOperands>' +
+ '<ogc:TemporalOperand>gml:TimeInstant</ogc:TemporalOperand>' +
+ '<ogc:TemporalOperand>gml:TimePeriod</ogc:TemporalOperand>' +
+ '</ogc:TemporalOperands>' +
+ '<ogc:TemporalOperators>' +
+ '<ogc:TemporalOperator name="TM_During"/>' +
+ '<ogc:TemporalOperator name="TM_Equals"/>' +
+ '<ogc:TemporalOperator name="TM_After"/>' +
+ '<ogc:TemporalOperator name="TM_Before"/>' +
+ '</ogc:TemporalOperators>' +
+ '</ogc:Temporal_Capabilities>' +
+ '<ogc:Scalar_Capabilities>' +
+ '<ogc:ComparisonOperators>' +
+ '<ogc:ComparisonOperator>Between</ogc:ComparisonOperator>' +
+ '<ogc:ComparisonOperator>EqualTo</ogc:ComparisonOperator>' +
+ '<ogc:ComparisonOperator>NotEqualTo</ogc:ComparisonOperator>' +
+ '<ogc:ComparisonOperator>LessThan</ogc:ComparisonOperator>' +
+ '<ogc:ComparisonOperator>LessThanEqualTo</ogc:ComparisonOperator>' +
+ '<ogc:ComparisonOperator>GreaterThan</ogc:ComparisonOperator>' +
+ '<ogc:ComparisonOperator>GreaterThanEqualTo</ogc:ComparisonOperator>' +
+ '<ogc:ComparisonOperator>Like</ogc:ComparisonOperator>' +
+ '</ogc:ComparisonOperators>' +
+ '</ogc:Scalar_Capabilities>' +
+ '<ogc:Id_Capabilities>' +
+ '<ogc:FID/>' +
+ '<ogc:EID/>' +
+ '</ogc:Id_Capabilities>' +
+ '</sos:Filter_Capabilities>' +
+ '<sos:Contents>' +
+ '<sos:ObservationOfferingList>' +
+ '<sos:ObservationOffering gml:id="ATMOSPHERIC_TEMPERATURE">' +
+ '<gml:name>Temperature of the atmosphere</gml:name>' +
+ '<gml:boundedBy>' +
+ '<gml:Envelope srsName="urn:ogc:def:crs:EPSG:4326">' +
+ '<gml:lowerCorner>46.611644 7.6103</gml:lowerCorner>' +
+ '<gml:upperCorner>51.9412 13.883498</gml:upperCorner>' +
+ '</gml:Envelope>' +
+ '</gml:boundedBy>' +
+ '<sos:time>' +
+ '<gml:TimePeriod xsi:type="gml:TimePeriodType">' +
+ '<gml:beginPosition>2008-11-20T15:20:22+01:00</gml:beginPosition>' +
+ '<gml:endPosition>2009-11-04T14:45:00+01:00</gml:endPosition>' +
+ '</gml:TimePeriod>' +
+ '</sos:time>' +
+ '<sos:procedure xlink:href="urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93"/>' +
+ '<sos:procedure xlink:href="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111"/>' +
+ '<sos:observedProperty xlink:href="urn:x-ogc:def:property:OGC::Temperature"/>' +
+ '<sos:featureOfInterest xlink:href="urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93"/>' +
+ '<sos:featureOfInterest xlink:href="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111"/>' +
+ '<sos:responseFormat>text/xml;subtype="om/1.0.0"</sos:responseFormat>' +
+ '<sos:responseFormat>application/zip</sos:responseFormat>' +
+ '<sos:resultModel xmlns:ns="http://www.opengis.net/om/1.0">ns:Measurement</sos:resultModel>' +
+ '<sos:resultModel xmlns:ns="http://www.opengis.net/om/1.0">ns:Observation</sos:resultModel>' +
+ '<sos:responseMode>inline</sos:responseMode>' +
+ '<sos:responseMode>resultTemplate</sos:responseMode>' +
+ '</sos:ObservationOffering>' +
+ '<sos:ObservationOffering gml:id="RAIN_GAUGE">' +
+ '<gml:name>Rain</gml:name>' +
+ '<gml:boundedBy>' +
+ '<gml:Envelope srsName="urn:ogc:def:crs:EPSG:4326">' +
+ '<gml:lowerCorner>46.611644 7.6103</gml:lowerCorner>' +
+ '<gml:upperCorner>51.9412 13.883498</gml:upperCorner>' +
+ '</gml:Envelope>' +
+ '</gml:boundedBy>' +
+ '<sos:time>' +
+ '<gml:TimePeriod xsi:type="gml:TimePeriodType">' +
+ '<gml:beginPosition>2008-11-20T15:35:22+01:00</gml:beginPosition>' +
+ '<gml:endPosition>2009-11-04T14:45:00+01:00</gml:endPosition>' +
+ '</gml:TimePeriod>' +
+ '</sos:time>' +
+ '<sos:procedure xlink:href="urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93"/>' +
+ '<sos:procedure xlink:href="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111"/>' +
+ '<sos:observedProperty xlink:href="urn:x-ogc:def:property:OGC::Precipitation1Hour"/>' +
+ '<sos:featureOfInterest xlink:href="urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93"/>' +
+ '<sos:featureOfInterest xlink:href="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111"/>' +
+ '<sos:responseFormat>text/xml;subtype="om/1.0.0"</sos:responseFormat>' +
+ '<sos:responseFormat>application/zip</sos:responseFormat>' +
+ '<sos:resultModel xmlns:ns="http://www.opengis.net/om/1.0">ns:Measurement</sos:resultModel>' +
+ '<sos:resultModel xmlns:ns="http://www.opengis.net/om/1.0">ns:Observation</sos:resultModel>' +
+ '<sos:responseMode>inline</sos:responseMode>' +
+ '<sos:responseMode>resultTemplate</sos:responseMode>' +
+ '</sos:ObservationOffering>' +
+ '<sos:ObservationOffering gml:id="WIND_DIRECTION">' +
+ '<gml:name>Direction of the wind</gml:name>' +
+ '<gml:boundedBy>' +
+ '<gml:Envelope srsName="urn:ogc:def:crs:EPSG:4326">' +
+ '<gml:lowerCorner>46.611644 7.6103</gml:lowerCorner>' +
+ '<gml:upperCorner>51.9412 13.883498</gml:upperCorner>' +
+ '</gml:Envelope>' +
+ '</gml:boundedBy>' +
+ '<sos:time>' +
+ '<gml:TimePeriod xsi:type="gml:TimePeriodType">' +
+ '<gml:beginPosition>2008-11-20T15:20:22+01:00</gml:beginPosition>' +
+ '<gml:endPosition>2009-11-04T14:45:00+01:00</gml:endPosition>' +
+ '</gml:TimePeriod>' +
+ '</sos:time>' +
+ '<sos:procedure xlink:href="urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93"/>' +
+ '<sos:procedure xlink:href="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111"/>' +
+ '<sos:observedProperty xlink:href="urn:x-ogc:def:property:OGC::WindDirection"/>' +
+ '<sos:featureOfInterest xlink:href="urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93"/>' +
+ '<sos:featureOfInterest xlink:href="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111"/>' +
+ '<sos:responseFormat>text/xml;subtype="om/1.0.0"</sos:responseFormat>' +
+ '<sos:responseFormat>application/zip</sos:responseFormat>' +
+ '<sos:resultModel xmlns:ns="http://www.opengis.net/om/1.0">ns:Measurement</sos:resultModel>' +
+ '<sos:resultModel xmlns:ns="http://www.opengis.net/om/1.0">ns:Observation</sos:resultModel>' +
+ '<sos:responseMode>inline</sos:responseMode>' +
+ '<sos:responseMode>resultTemplate</sos:responseMode>' +
+ '</sos:ObservationOffering>' +
+ '<sos:ObservationOffering gml:id="WIND_SPEED">' +
+ '<gml:name>Speed of the wind</gml:name>' +
+ '<gml:boundedBy>' +
+ '<gml:Envelope srsName="urn:ogc:def:crs:EPSG:4326">' +
+ '<gml:lowerCorner>46.611644 7.6103</gml:lowerCorner>' +
+ '<gml:upperCorner>51.9412 13.883498</gml:upperCorner>' +
+ '</gml:Envelope>' +
+ '</gml:boundedBy>' +
+ '<sos:time>' +
+ '<gml:TimePeriod xsi:type="gml:TimePeriodType">' +
+ '<gml:beginPosition>2008-11-20T15:20:22+01:00</gml:beginPosition>' +
+ '<gml:endPosition>2009-11-04T14:45:00+01:00</gml:endPosition>' +
+ '</gml:TimePeriod>' +
+ '</sos:time>' +
+ '<sos:procedure xlink:href="urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93"/>' +
+ '<sos:procedure xlink:href="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111"/>' +
+ '<sos:observedProperty xlink:href="urn:x-ogc:def:property:OGC::WindSpeed"/>' +
+ '<sos:featureOfInterest xlink:href="urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93"/>' +
+ '<sos:featureOfInterest xlink:href="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111"/>' +
+ '<sos:responseFormat>text/xml;subtype="om/1.0.0"</sos:responseFormat>' +
+ '<sos:responseFormat>application/zip</sos:responseFormat>' +
+ '<sos:resultModel xmlns:ns="http://www.opengis.net/om/1.0">ns:Measurement</sos:resultModel>' +
+ '<sos:resultModel xmlns:ns="http://www.opengis.net/om/1.0">ns:Observation</sos:resultModel>' +
+ '<sos:responseMode>inline</sos:responseMode>' +
+ '<sos:responseMode>resultTemplate</sos:responseMode>' +
+ '</sos:ObservationOffering>' +
+ '<sos:ObservationOffering gml:id="HUMIDITY">' +
+ '<gml:name>Humidity of the atmosphere</gml:name>' +
+ '<gml:boundedBy>' +
+ '<gml:Envelope srsName="urn:ogc:def:crs:EPSG:4326">' +
+ '<gml:lowerCorner>46.611644 7.6103</gml:lowerCorner>' +
+ '<gml:upperCorner>51.9412 13.883498</gml:upperCorner>' +
+ '</gml:Envelope>' +
+ '</gml:boundedBy>' +
+ '<sos:time>' +
+ '<gml:TimePeriod xsi:type="gml:TimePeriodType">' +
+ '<gml:beginPosition>2008-02-14T11:03:02+01:00</gml:beginPosition>' +
+ '<gml:endPosition>2009-11-04T14:45:00+01:00</gml:endPosition>' +
+ '</gml:TimePeriod>' +
+ '</sos:time>' +
+ '<sos:procedure xlink:href="urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93"/>' +
+ '<sos:procedure xlink:href="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111"/>' +
+ '<sos:observedProperty xlink:href="urn:x-ogc:def:property:OGC::RelativeHumidity"/>' +
+ '<sos:featureOfInterest xlink:href="urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93"/>' +
+ '<sos:featureOfInterest xlink:href="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111"/>' +
+ '<sos:responseFormat>text/xml;subtype="om/1.0.0"</sos:responseFormat>' +
+ '<sos:responseFormat>application/zip</sos:responseFormat>' +
+ '<sos:resultModel xmlns:ns="http://www.opengis.net/om/1.0">ns:Measurement</sos:resultModel>' +
+ '<sos:resultModel xmlns:ns="http://www.opengis.net/om/1.0">ns:Observation</sos:resultModel>' +
+ '<sos:responseMode>inline</sos:responseMode>' +
+ '<sos:responseMode>resultTemplate</sos:responseMode>' +
+ '</sos:ObservationOffering>' +
+ '<sos:ObservationOffering gml:id="LUMINANCE">' +
+ '<gml:name>Luminance</gml:name>' +
+ '<gml:boundedBy>' +
+ '<gml:Envelope srsName="urn:ogc:def:crs:EPSG:4326">' +
+ '<gml:lowerCorner>46.611644 7.6103</gml:lowerCorner>' +
+ '<gml:upperCorner>51.9412 13.883498</gml:upperCorner>' +
+ '</gml:Envelope>' +
+ '</gml:boundedBy>' +
+ '<sos:time>' +
+ '<gml:TimePeriod xsi:type="gml:TimePeriodType">' +
+ '<gml:beginPosition>2008-11-20T15:20:22+01:00</gml:beginPosition>' +
+ '<gml:endPosition>2009-11-04T14:45:00+01:00</gml:endPosition>' +
+ '</gml:TimePeriod>' +
+ '</sos:time>' +
+ '<sos:procedure xlink:href="urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93"/>' +
+ '<sos:procedure xlink:href="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111"/>' +
+ '<sos:observedProperty xlink:href="urn:x-ogc:def:property:OGC::Luminance"/>' +
+ '<sos:featureOfInterest xlink:href="urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93"/>' +
+ '<sos:featureOfInterest xlink:href="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111"/>' +
+ '<sos:responseFormat>text/xml;subtype="om/1.0.0"</sos:responseFormat>' +
+ '<sos:responseFormat>application/zip</sos:responseFormat>' +
+ '<sos:resultModel xmlns:ns="http://www.opengis.net/om/1.0">ns:Measurement</sos:resultModel>' +
+ '<sos:resultModel xmlns:ns="http://www.opengis.net/om/1.0">ns:Observation</sos:resultModel>' +
+ '<sos:responseMode>inline</sos:responseMode>' +
+ '<sos:responseMode>resultTemplate</sos:responseMode>' +
+ '</sos:ObservationOffering>' +
+ '<sos:ObservationOffering gml:id="ATMOSPHERIC_PRESSURE">' +
+ '<gml:name>Pressure of the atmosphere</gml:name>' +
+ '<gml:boundedBy>' +
+ '<gml:Envelope srsName="urn:ogc:def:crs:EPSG:4326">' +
+ '<gml:lowerCorner>46.611644 7.6103</gml:lowerCorner>' +
+ '<gml:upperCorner>51.9412 13.883498</gml:upperCorner>' +
+ '</gml:Envelope>' +
+ '</gml:boundedBy>' +
+ '<sos:time>' +
+ '<gml:TimePeriod xsi:type="gml:TimePeriodType">' +
+ '<gml:beginPosition>2008-12-20T02:29:27+01:00</gml:beginPosition>' +
+ '<gml:endPosition>2009-11-04T14:45:00+01:00</gml:endPosition>' +
+ '</gml:TimePeriod>' +
+ '</sos:time>' +
+ '<sos:procedure xlink:href="urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93"/>' +
+ '<sos:procedure xlink:href="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111"/>' +
+ '<sos:observedProperty xlink:href="urn:x-ogc:def:property:OGC::BarometricPressure"/>' +
+ '<sos:featureOfInterest xlink:href="urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93"/>' +
+ '<sos:featureOfInterest xlink:href="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111"/>' +
+ '<sos:responseFormat>text/xml;subtype="om/1.0.0"</sos:responseFormat>' +
+ '<sos:responseFormat>application/zip</sos:responseFormat>' +
+ '<sos:resultModel xmlns:ns="http://www.opengis.net/om/1.0">ns:Measurement</sos:resultModel>' +
+ '<sos:resultModel xmlns:ns="http://www.opengis.net/om/1.0">ns:Observation</sos:resultModel>' +
+ '<sos:responseMode>inline</sos:responseMode>' +
+ '<sos:responseMode>resultTemplate</sos:responseMode>' +
+ '</sos:ObservationOffering>' +
+ '</sos:ObservationOfferingList>' +
+ '</sos:Contents>' +
+'</sos:Capabilities>'
+); \ No newline at end of file
diff --git a/misc/openlayers/tests/Format/SOSGetFeatureOfInterest.html b/misc/openlayers/tests/Format/SOSGetFeatureOfInterest.html
new file mode 100644
index 0000000..c80078f
--- /dev/null
+++ b/misc/openlayers/tests/Format/SOSGetFeatureOfInterest.html
@@ -0,0 +1,80 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read_SOSGetFeatureOfInterest_single(t) {
+ t.plan(6);
+
+ var parser = new OpenLayers.Format.SOSGetFeatureOfInterest();
+ var text =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<sa:SamplingPoint xmlns:sa="http://www.opengis.net/sampling/1.0" xmlns:gml="http://www.opengis.net/gml" gml:id="urn:ogc:object:feature:OSIRIS-HWS:4fc335bc-06d7-4d5e-a72a-1ac73b9f3b56">' +
+ '<gml:name>Roof of the IfGI</gml:name>' +
+ '<sa:position>' +
+ '<gml:Point>' +
+ '<gml:pos srsName="urn:ogc:def:crs:EPSG:4326">52.1524 5.3722</gml:pos>' +
+ '</gml:Point>' +
+ '</sa:position>' +
+ '</sa:SamplingPoint>';
+
+ var res = parser.read(text);
+ t.eq(res.length, 1, "One feature parsed from response");
+ t.eq(res[0].attributes.id, "urn:ogc:object:feature:OSIRIS-HWS:4fc335bc-06d7-4d5e-a72a-1ac73b9f3b56", "gml:id correctly parsed");
+ t.eq(res[0].attributes.name, "Roof of the IfGI", "gml:name correctly parsed");
+ t.eq(res[0].geometry instanceof OpenLayers.Geometry.Point, true, "Geometry is a point geometry");
+ t.eq(res[0].geometry.x, 5.3722, "Geometry x coordinate correctly parsed");
+ t.eq(res[0].geometry.y, 52.1524, "Geometry y coordinate correctly parsed");
+ }
+
+ function test_read_SOSGetFeatureOfInterest_multiple(t) {
+ t.plan(6);
+
+ var parser = new OpenLayers.Format.SOSGetFeatureOfInterest();
+ var text =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<gml:FeatureCollection xmlns:gml="http://www.opengis.net/gml" xmlns:sa="http://www.opengis.net/sampling/1.0">' +
+ '<gml:featureMember>' +
+ '<sa:SamplingPoint gml:id="urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93">' +
+ '<gml:name>weather @ roof of the ifgi, MS, Germany</gml:name>' +
+ '<sa:position>' +
+ '<gml:Point>' +
+ '<gml:pos srsName="urn:ogc:def:crs:EPSG:4326">51.9412 7.6103</gml:pos>' +
+ '</gml:Point>' +
+ '</sa:position>' +
+ '</sa:SamplingPoint>' +
+ '</gml:featureMember>' +
+ '<gml:featureMember>' +
+ '<sa:SamplingPoint gml:id="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111">' +
+ '<gml:name>waether @ roof of the FH Kaernten, Villach, Austria</gml:name>' +
+ '<sa:position>' +
+ '<gml:Point>' +
+ '<gml:pos srsName="urn:ogc:def:crs:EPSG:4326">46.611644 13.883498</gml:pos>' +
+ '</gml:Point>' +
+ '</sa:position>' +
+ '</sa:SamplingPoint>' +
+ '</gml:featureMember>' +
+ '</gml:FeatureCollection>';
+
+ var res = parser.read(text);
+ t.eq(res.length, 2, "Two features parsed from response");
+ t.eq(res[0].attributes.id, "urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93", "gml:id correctly parsed");
+ t.eq(res[1].attributes.name, "waether @ roof of the FH Kaernten, Villach, Austria", "gml:name correctly parsed");
+ t.eq(res[1].geometry instanceof OpenLayers.Geometry.Point, true, "Geometry is a point geometry");
+ t.eq(res[1].geometry.x, 13.883498, "Geometry x coordinate correctly parsed");
+ t.eq(res[1].geometry.y, 46.611644, "Geometry y coordinate correctly parsed");
+ }
+
+ function test_write_SOSGetFeatureOfInterest(t) {
+ t.plan(1);
+ var expect = '<GetFeatureOfInterest xmlns="http://www.opengis.net/sos/1.0" version="1.0.0" service="SOS" xsi:schemaLocation="http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosAll.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FeatureOfInterestId>urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93</FeatureOfInterestId><FeatureOfInterestId>urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111</FeatureOfInterestId></GetFeatureOfInterest>';
+ var format = new OpenLayers.Format.SOSGetFeatureOfInterest();
+ var output = format.writeNode("sos:GetFeatureOfInterest", {fois: ['urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93', 'urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111']});
+ t.xml_eq(output, expect, "Request XML is written out correctly");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/SOSGetObservation.html b/misc/openlayers/tests/Format/SOSGetObservation.html
new file mode 100644
index 0000000..3256d5a
--- /dev/null
+++ b/misc/openlayers/tests/Format/SOSGetObservation.html
@@ -0,0 +1,183 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read_SOSGetObservation(t) {
+ t.plan(13);
+
+ var parser = new OpenLayers.Format.SOSGetObservation();
+ var text =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<om:ObservationCollection xmlns:om="http://www.opengis.net/om/1.0" xmlns:gml="http://www.opengis.net/gml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sa="http://www.opengis.net/sampling/1.0" gml:id="oc_0" xsi:schemaLocation="http://www.opengis.net/om/1.0 http://schemas.opengis.net/om/1.0.0/om.xsd http://www.opengis.net/sampling/1.0 http://schemas.opengis.net/sampling/1.0.0/sampling.xsd">' +
+ '<gml:boundedBy>' +
+ '<gml:Envelope srsName="urn:ogc:def:crs:EPSG:4326">' +
+ '<gml:lowerCorner>52.1524 5.3722</gml:lowerCorner>' +
+ '<gml:upperCorner>52.1524 5.3722</gml:upperCorner>' +
+ '</gml:Envelope>' +
+ '</gml:boundedBy>' +
+ '<om:member>' +
+ '<om:Measurement gml:id="o_51082">' +
+ '<om:samplingTime>' +
+ '<gml:TimeInstant xsi:type="gml:TimeInstantType">' +
+ '<gml:timePosition>2009-12-02T10:35:00.000+01:00</gml:timePosition>' +
+ '</gml:TimeInstant>' +
+ '</om:samplingTime>' +
+ '<om:procedure xlink:href="urn:ogc:object:feature:OSIRIS-HWS:4fc335bc-06d7-4d5e-a72a-1ac73b9f3b56"/>' +
+ '<om:observedProperty xlink:href="urn:x-ogc:def:property:OGC::Temperature"/>' +
+ '<om:featureOfInterest>' +
+ '<sa:SamplingPoint gml:id="urn:ogc:object:feature:OSIRIS-HWS:4fc335bc-06d7-4d5e-a72a-1ac73b9f3b56">' +
+ '<gml:name>Roof of the IfGI</gml:name>' +
+ '<sa:position>' +
+ '<gml:Point>' +
+ '<gml:pos srsName="urn:ogc:def:crs:EPSG:4326">52.1524 5.3722</gml:pos>' +
+ '</gml:Point>' +
+ '</sa:position>' +
+ '</sa:SamplingPoint>' +
+ '</om:featureOfInterest>' +
+ '<om:result uom="Cel">4.9</om:result>' +
+ '</om:Measurement>' +
+ '</om:member>' +
+ '</om:ObservationCollection>';
+
+ var res = parser.read(text);
+ t.eq(res.measurements.length, 1, "One measurement parsed");
+ t.eq(res.id, "oc_0", "Observation collection id correctly parsed");
+ var measurement = res.measurements[0];
+ t.eq(measurement.observedProperty, "urn:x-ogc:def:property:OGC::Temperature", "Observed property correctly parsed");
+ t.eq(measurement.procedure, "urn:ogc:object:feature:OSIRIS-HWS:4fc335bc-06d7-4d5e-a72a-1ac73b9f3b56", "Procedure correctly parsed");
+ t.eq(measurement.result.uom, "Cel", "Units of measurement correctly parsed");
+ t.eq(measurement.result.value, "4.9", "Value correctly parsed");
+ t.eq(measurement.samplingTime.timeInstant.timePosition, "2009-12-02T10:35:00.000+01:00", "Sampling time correctly parsed");
+
+ var response = [];
+ response.push('<?xml version="1.0" encoding="UTF-8"?>',
+'<om:ObservationCollection gml:id="oc_0" xsi:schemaLocation="http://www.opengis.net/om/1.0 http://schemas.opengis.net/om/1.0.0/om.xsd http://www.opengis.net/sampling/1.0 http://schemas.opengis.net/sampling/1.0.0/sampling.xsd" xmlns:om="http://www.opengis.net/om/1.0" xmlns:gml="http://www.opengis.net/gml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:swe="http://www.opengis.net/swe/1.0.1" xmlns:sa="http://www.opengis.net/sampling/1.0">',
+' <gml:boundedBy>',
+' <gml:Envelope srsName="urn:ogc:def:crs:EPSG:4326">',
+' <gml:lowerCorner>46.611644 7.6103</gml:lowerCorner>',
+' <gml:upperCorner>51.9412 13.883498</gml:upperCorner>',
+' </gml:Envelope>',
+' </gml:boundedBy>',
+' <om:member>',
+' <om:Observation gml:id="ot_583227">',
+' <om:samplingTime>',
+' <gml:TimePeriod xsi:type="gml:TimePeriodType">',
+' <gml:beginPosition>2009-09-28T13:45:00.000+02:00</gml:beginPosition>',
+' <gml:endPosition>2009-09-28T13:45:00.000+02:00</gml:endPosition>',
+' </gml:TimePeriod>',
+' </om:samplingTime>',
+' <om:procedure xlink:href="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111"/>',
+' <om:observedProperty>',
+' <swe:CompositePhenomenon gml:id="cpid0" dimension="1">',
+' <gml:name>resultComponents</gml:name>',
+' <swe:component xlink:href="urn:ogc:data:time:iso8601"/>',
+' <swe:component xlink:href="urn:ogc:def:property:OGC::Precipitation1Hour"/>',
+' </swe:CompositePhenomenon>',
+' </om:observedProperty>',
+' <om:featureOfInterest>',
+' <gml:FeatureCollection>',
+' <gml:featureMember>',
+' <sa:SamplingPoint gml:id="urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111" xsi:schemaLocation=" http://www.opengis.net/sampling/1.0 http://schemas.opengis.net/sampling/1.0.0/sampling.xsd">',
+' <gml:name>waether @ roof of the FH Kaernten, Villach, Austria</gml:name>',
+' <sa:sampledFeature xlink:href="urn:ogc:def:nil:OGC:unknown"/>',
+' <sa:position>',
+' <gml:Point>',
+' <gml:pos srsName="urn:ogc:def:crs:EPSG:4326">46.611644 13.883498</gml:pos>',
+' </gml:Point>',
+' </sa:position>',
+' </sa:SamplingPoint>',
+' </gml:featureMember>',
+' </gml:FeatureCollection>',
+' </om:featureOfInterest>',
+' <om:result>',
+' <swe:DataArray>',
+' <swe:elementCount>',
+' <swe:Count>',
+' <swe:value>1</swe:value>',
+' </swe:Count>',
+' </swe:elementCount>',
+' <swe:elementType name="Components">',
+' <swe:DataRecord>',
+' <swe:field name="Time">',
+' <swe:Time definition="urn:ogc:data:time:iso8601"/>',
+' </swe:field>',
+' <swe:field name="feature">',
+' <swe:Text definition="urn:ogc:data:feature"/>',
+' </swe:field>',
+' <swe:field name="urn:ogc:def:property:OGC::Precipitation1Hour">',
+' <swe:Quantity definition="urn:ogc:def:property:OGC::Precipitation1Hour">',
+' <swe:uom code="mm"/>',
+' </swe:Quantity>',
+' </swe:field>',
+' </swe:DataRecord>',
+' </swe:elementType>',
+' <swe:encoding>',
+' <swe:TextBlock decimalSeparator="." tokenSeparator="," blockSeparator=";"/>',
+' </swe:encoding>',
+' <swe:values>2009-09-28T13:45:00.000+02:00,urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111,0.0;</swe:values>',
+' </swe:DataArray>',
+' </om:result>',
+' </om:Observation>',
+' </om:member>',
+'</om:ObservationCollection>');
+ text = response.join("");
+ var res = parser.read(text);
+ t.eq(res.observations.length, 1, "1 observation parsed");
+ var observation = res.observations[0];
+ t.eq(observation.procedure, "urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111", "procedure parsed correctly");
+ t.eq(observation.fois.length, 1, "One foi parsed for the observation");
+ var foi = observation.fois[0];
+ var feature = foi.features[0];
+ t.eq(feature.attributes.id, "urn:ogc:object:feature:OSIRIS-HWS:efeb807b-bd24-4128-a920-f6729bcdd111", "Foi id correctly parsed");
+ t.eq(feature.attributes.name, "waether @ roof of the FH Kaernten, Villach, Austria", "Foi name correctly parsed");
+ t.ok(feature.geometry instanceof OpenLayers.Geometry.Point, "Geometry correctly parsed");
+ }
+
+ function test_write_SOSGetObservation(t) {
+ t.plan(2);
+ var expect = '<GetObservation xmlns="http://www.opengis.net/sos/1.0" version="1.0.0" service="SOS" xsi:schemaLocation="http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosGetObservation.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><offering>TEMPERATURE</offering><eventTime><ogc:TM_Equals xmlns:ogc="http://www.opengis.net/ogc"><ogc:PropertyName>urn:ogc:data:time:iso8601</ogc:PropertyName><gml:TimeInstant xmlns:gml="http://www.opengis.net/gml"><gml:timePosition>latest</gml:timePosition></gml:TimeInstant></ogc:TM_Equals></eventTime><procedure>urn:ogc:object:feature:OSIRIS-HWS:4fc335bc-06d7-4d5e-a72a-1ac73b9f3b56</procedure><observedProperty>urn:x-ogc:def:property:OGC::Temperature</observedProperty><responseFormat>text/xml;subtype="om/1.0.0"</responseFormat><resultModel>Measurement</resultModel><responseMode>inline</responseMode></GetObservation>';
+ var format = new OpenLayers.Format.SOSGetObservation();
+ var output = format.write({eventTime: 'latest', resultModel: 'Measurement', responseMode: 'inline',
+ procedures: ['urn:ogc:object:feature:OSIRIS-HWS:4fc335bc-06d7-4d5e-a72a-1ac73b9f3b56'], responseFormat: 'text/xml;subtype="om/1.0.0"',
+ offering: 'TEMPERATURE', observedProperties: ['urn:x-ogc:def:property:OGC::Temperature']});
+ t.xml_eq(output, expect, "Request XML is written out correctly");
+
+ var expected = [];
+
+ expected.push('<?xml version="1.0" encoding="UTF-8"?>',
+'<GetObservation xmlns="http://www.opengis.net/sos/1.0"',
+' xmlns:gml="http://www.opengis.net/gml"',
+' xmlns:om="http://www.opengis.net/om/1.0"',
+' xmlns:ogc="http://www.opengis.net/ogc"',
+' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"',
+' xsi:schemaLocation="http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosGetObservation.xsd"',
+' service="SOS" version="1.0.0">',
+' <offering>RAIN_GAUGE</offering>',
+' <eventTime>',
+' <ogc:TM_Equals>',
+' <ogc:PropertyName>urn:ogc:data:time:iso8601</ogc:PropertyName>',
+' <gml:TimeInstant>',
+' <gml:timePosition>latest</gml:timePosition>',
+' </gml:TimeInstant>',
+' </ogc:TM_Equals>',
+' </eventTime>',
+' <observedProperty>urn:ogc:def:property:OGC::Precipitation1Hour</observedProperty>',
+' <featureOfInterest>',
+' <ObjectID>urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93</ObjectID>',
+' </featureOfInterest>',
+' <responseFormat>text/xml;subtype="om/1.0.0"</responseFormat>',
+'</GetObservation>');
+ expect = expected.join("");
+ var output = format.write({eventTime: 'latest', offering: 'RAIN_GAUGE',
+ observedProperties: ['urn:ogc:def:property:OGC::Precipitation1Hour'],
+ responseFormat: 'text/xml;subtype="om/1.0.0"',
+ foi: {objectId: 'urn:ogc:object:feature:OSIRIS-HWS:3d3b239f-7696-4864-9d07-15447eae2b93'}});
+ t.xml_eq(output, expect, "Request XML is written out correctly");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/Text.html b/misc/openlayers/tests/Format/Text.html
new file mode 100644
index 0000000..9b18bb5
--- /dev/null
+++ b/misc/openlayers/tests/Format/Text.html
@@ -0,0 +1,49 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_basic(t) {
+ t.plan(5);
+ var format = new OpenLayers.Format.Text({extractStyles: true});
+ var features = format.read(OpenLayers.Util.getElement("content").value);
+ t.eq(features[0].style.externalGraphic, format.defaultStyle.externalGraphic, "style is set to defaults if no style props set in text file");
+ var features = format.read(OpenLayers.Util.getElement("contentMarker").value);
+ t.eq(features[0].style.externalGraphic, OpenLayers.Util.getImagesLocation() + "marker.png", "marker set correctly by default.");
+
+ var features = format.read(OpenLayers.Util.getElement("content2").value);
+ t.eq(features.length, 2, "two features read");
+ t.eq(features[0].style.externalGraphic, "marker.png", "marker set correctly from data.");
+ // t.eq(format.defaultStyle.externalGraphic, "../../img/marker.png", "defaultStyle externalGraphic not changed by pulling from data");
+
+ var format = new OpenLayers.Format.Text({extractStyles: false});
+ var features = format.read(OpenLayers.Util.getElement("content2").value);
+ t.eq(features[0].style, null, "extractStyles: false results in null style property, even with style properties used");
+ }
+ function test_extra(t) {
+ t.plan(1);
+ var format = new OpenLayers.Format.Text();
+ var features = format.read(OpenLayers.Util.getElement("content3").value);
+ t.eq(features[0].attributes.whee, "chicken", "extra attributes are stored for later use");
+ }
+ </script>
+</head>
+<body>
+<textarea id="content">
+point
+5,5
+</textarea>
+<textarea id="contentMarker">
+point iconSize
+5,5 8,8
+</textarea>
+<textarea id="content2">
+point icon
+5,5 marker.png
+10,10 marker2.png
+</textarea>
+<textarea id="content3">
+point whee
+5,5 chicken
+</textarea>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WCSCapabilities.html b/misc/openlayers/tests/Format/WCSCapabilities.html
new file mode 100644
index 0000000..b3e90b6
--- /dev/null
+++ b/misc/openlayers/tests/Format/WCSCapabilities.html
@@ -0,0 +1,43 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read(t) {
+ t.plan(4);
+
+ var _v1_0_0 = OpenLayers.Format.WCSCapabilities.v1_0_0.prototype.read;
+ var _v1_1_0 = OpenLayers.Format.WCSCapabilities.v1_1_0.prototype.read;
+
+ var parser = new OpenLayers.Format.WCSCapabilities();
+
+ // version 1.0.0
+ var text =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<wcs:WCS_Capabilities version="1.0.0" xmlns:wcs="http://www.opengis.net/wcs"></wcs:WCS_Capabilities>';
+ OpenLayers.Format.WCSCapabilities.v1_0_0.prototype.read = function() {
+ t.ok(true, "Version 1.0.0 detected");
+ return {};
+ }
+ var res = parser.read(text);
+ t.eq(res.version, "1.0.0", "version 1.0.0 written to result object");
+ OpenLayers.Format.WCSCapabilities.v1_1_0.prototype.read = _v1_1_0;
+
+ // version 1.1.0
+ var text =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<wcs:WCS_Capabilities version="1.1.0" xmlns:wcs="http://www.opengis.net/wcs/1.1"></wcs:WCS_Capabilities>';
+ OpenLayers.Format.WCSCapabilities.v1_1_0.prototype.read = function() {
+ t.ok(true, "Version 1.1.0 detected");
+ return {};
+ }
+ var res = parser.read(text);
+ t.eq(res.version, "1.1.0", "version 1.1.0 written to result object");
+ OpenLayers.Format.WCSCapabilities.v1_1_0.prototype.read = _v1_1_0;
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WCSCapabilities/v1.html b/misc/openlayers/tests/Format/WCSCapabilities/v1.html
new file mode 100644
index 0000000..180edc7
--- /dev/null
+++ b/misc/openlayers/tests/Format/WCSCapabilities/v1.html
@@ -0,0 +1,87 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read_exception(t) {
+ t.plan(1);
+ var parser = new OpenLayers.Format.WCSCapabilities();
+ var text = '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<ows:ExceptionReport language="en" version="1.0.0"' +
+ ' xsi:schemaLocation="http://www.opengis.net/ows http://schemas.opengis.net/ows/1.0.0/owsExceptionReport.xsd"' +
+ ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ows="http://www.opengis.net/ows">' +
+ ' <ows:Exception locator="foo" exceptionCode="InvalidParameterValue">' +
+ ' <ows:ExceptionText>Update error: Error occured updating features</ows:ExceptionText>' +
+ ' <ows:ExceptionText>Second exception line</ows:ExceptionText>' +
+ ' </ows:Exception>' +
+ '</ows:ExceptionReport>';
+
+ var obj = parser.read(text);
+ t.ok(!!obj.error, "Error reported correctly"); // The above should place an error in obj.error
+ }
+
+ function test_read(t) {
+ t.plan(34); // Number of tests performed: If you add a test below, be sure to increment this accordingly
+
+ var parser = new OpenLayers.Format.WCSCapabilities();
+
+ // MapServer, v1.0.0
+ var text = '<?xml version="1.0" encoding="UTF-8"?><WCS_Capabilities xmlns="http://www.opengis.net/wcs" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:gml="http://www.opengis.net/gml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0.0" updateSequence="0" xsi:schemaLocation="http://www.opengis.net/wcs http://schemas.opengis.net/wcs/1.0.0/wcsCapabilities.xsd"><Service><name>MapServer WCS</name><label>WCS Sample Data Server 1.0.0</label><keywords><keyword>Geospatial WebServices</keyword><keyword>Luxembourg!</keyword></keywords><responsibleParty><individualName>Franko Lemmer</individualName><organisationName>CRP Henri Tudor</organisationName><positionName>R+D engineer</positionName><contactInfo><phone><voice>6463320</voice><facsimile>6465955</facsimile></phone><address><deliveryPoint>66, rue de Luxembourg</deliveryPoint><city>Esch-sur-Alzette</city><administrativeArea/><postalCode>97202</postalCode><country>Luxembourg</country><electronicMailAddress>franko.lemmer@flensburger.de</electronicMailAddress></address><onlineResource xlink:type="simple" xlink:href="http://services.magnificent.lu/cgi-bin/mapserv?map=/var/www/MapFiles/wcs_test.map&amp;"/></contactInfo></responsibleParty><fees>mucho dinero</fees><accessConstraints>Open to the public</accessConstraints></Service><Capability><Request><GetCapabilities><DCPType><HTTP><Get><OnlineResource xlink:type="simple" xlink:href="http://services.magnificent.get.lu/cgi-bin/mapserv?map=/var/www/MapFiles/wcs_test.map&amp;"/></Get></HTTP></DCPType><DCPType><HTTP><Post><OnlineResource xlink:type="simple" xlink:href="http://services.magnificent.post.lu/cgi-bin/mapserv?map=/var/www/MapFiles/wcs_test.map&amp;"/></Post></HTTP></DCPType></GetCapabilities><DescribeCoverage><DCPType><HTTP><Get><OnlineResource xlink:type="simple" xlink:href="http://services.magnificent.lu/cgi-bin/mapserv?map=/var/www/MapFiles/wcs_test.map&amp;"/></Get></HTTP></DCPType><DCPType><HTTP><Post><OnlineResource xlink:type="simple" xlink:href="http://services.magnificent.lu/cgi-bin/mapserv?map=/var/www/MapFiles/wcs_test.map&amp;"/></Post></HTTP></DCPType></DescribeCoverage><GetCoverage><DCPType><HTTP><Get><OnlineResource xlink:type="simple" xlink:href="http://services.magnificent.lu/cgi-bin/mapserv?map=/var/www/MapFiles/wcs_test.map&amp;"/></Get></HTTP></DCPType><DCPType><HTTP><Post><OnlineResource xlink:type="simple" xlink:href="http://services.magnificent.lu/cgi-bin/mapserv?map=/var/www/MapFiles/wcs_test.map&amp;"/></Post></HTTP></DCPType></GetCoverage></Request><Exception><Format>application/vnd.ogc.se_xml</Format></Exception></Capability><ContentMetadata><CoverageOfferingBrief><name>ro_dsm</name><label>Rotterdam DSM</label><lonLatEnvelope srsName="urn:ogc:def:crs:OGC:1.3:CRS84"><gml:pos>4.44444 51.515151</gml:pos><gml:pos>5.55555 52.525252</gml:pos></lonLatEnvelope></CoverageOfferingBrief><CoverageOfferingBrief><name>ro_dsm_mini</name><label>ro_dsm_mini</label><lonLatEnvelope srsName="urn:ogc:def:crs:OGC:1.3:CRS84"><gml:pos>4.47489346945755 51.9159453786927</gml:pos><gml:pos>4.47687824892444 51.9170706688033</gml:pos></lonLatEnvelope></CoverageOfferingBrief><CoverageOfferingBrief><name>ro_irra</name><label>ro_irra</label><lonLatEnvelope srsName="urn:ogc:def:crs:OGC:1.3:CRS84"><gml:pos>4.471333734139 51.912813427383</gml:pos><gml:pos>4.4808508475645 51.9248713705576</gml:pos></lonLatEnvelope></CoverageOfferingBrief><CoverageOfferingBrief><name>ro_irra_ext</name><label>ro_irra_ext</label><lonLatEnvelope srsName="urn:ogc:def:crs:OGC:1.3:CRS84"><gml:pos>4.10024171314823 51.9359764992844</gml:pos><gml:pos>4.21909054278063 52.001415228243</gml:pos></lonLatEnvelope></CoverageOfferingBrief></ContentMetadata></WCS_Capabilities>';
+
+ var res = parser.read(text);
+
+ t.ok(!res.error, "Parsing XML generated no errors");
+ t.eq(res.service.fees, "mucho dinero", "Service>Fees correctly parsed");
+ t.eq(res.service.accessConstraints, "Open to the public", "Service>AccessConstraints correctly parsed");
+ t.eq(res.service.keywords.length, 2, "Correct number of Service>Keywords found");
+ t.eq(res.service.keywords[0], "Geospatial WebServices", "Service>Keywords correctly parsed");
+ t.eq(res.service.label, "WCS Sample Data Server 1.0.0", "Service>Label correctly parsed");
+ t.eq(res.service.name, "MapServer WCS", "Service>Name correctly parsed");
+
+ var responsibleParty = res.service.responsibleParty;
+ t.eq(responsibleParty.individualName, "Franko Lemmer", "Service>ResponsibleParty>IndividualName correctly parsed");
+ t.eq(responsibleParty.organisationName, "CRP Henri Tudor", "Service>ResponsibleParty>OrganisationName correctly parsed");
+ t.eq(responsibleParty.positionName, "R+D engineer", "Service>ResponsibleParty>PositionName correctly parsed");
+ t.eq(responsibleParty.contactInfo.address.city, "Esch-sur-Alzette", "Service>responsibleParty>ContactInfo>Address>City correctly parsed");
+ t.eq(responsibleParty.contactInfo.address.country, "Luxembourg", "Service>responsibleParty>ContactInfo>Address>Country correctly parsed");
+ t.eq(responsibleParty.contactInfo.address.deliveryPoint, "66, rue de Luxembourg", "Service>responsibleParty>ContactInfo>Address>DeliveryPoint correctly parsed");
+ t.eq(responsibleParty.contactInfo.address.electronicMailAddress, "franko.lemmer@flensburger.de", "Service>responsibleParty>ContactInfo>Address>ElectronicMailAddress correctly parsed");
+ t.eq(responsibleParty.contactInfo.address.postalCode, "97202", "Service>responsibleParty>ContactInfo>Address>PostalCode correctly parsed");
+ t.eq(responsibleParty.contactInfo.phone.facsimile, "6465955", "Service>responsibleParty>ContactInfo>Phone>Facsimile correctly parsed");
+ t.eq(responsibleParty.contactInfo.phone.voice, "6463320", "Service>responsibleParty>ContactInfo>Phone>Voice correctly parsed");
+
+ var metadata = res.contentMetadata[0];
+
+ t.eq(metadata.name, "ro_dsm", "ContentMetadata>Name correctly parsed");
+ t.eq(metadata.label, "Rotterdam DSM", "ContentMetadata>Label correctly parsed");
+ t.eq(metadata.lonLatEnvelope.min.x, 51.515151, "ContentMetadata>lonLatEnvelope>Min>Lat correctly parsed");
+ t.eq(metadata.lonLatEnvelope.min.y, 4.44444, "ContentMetadata>lonLatEnvelope>Min>Lon correctly parsed");
+ t.eq(metadata.lonLatEnvelope.max.x, 52.525252, "ContentMetadata>lonLatEnvelope>Max>Lat correctly parsed");
+ t.eq(metadata.lonLatEnvelope.max.y, 5.55555, "ContentMetadata>lonLatEnvelope>Max>Lon correctly parsed");
+ t.eq(metadata.lonLatEnvelope.srsName, "urn:ogc:def:crs:OGC:1.3:CRS84", "ContentMetadata>lonLatEnvelope>SrsName correctly parsed");
+ t.eq(res.contentMetadata.length, 4, "Correct number of metadata records found");
+
+
+ // MapServer, v1.1.0
+ text = '<?xml version="1.0" encoding="UTF-8"?><Capabilities xmlns="http://www.opengis.net/wcs/1.1" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ogc="http://www.opengis.net/ogc" version="1.1.0" xsi:schemaLocation="http://www.opengis.net/wcs/1.1 http://schemas.opengis.net/wcs/1.1/wcsGetCapabilities.xsd http://www.opengis.net/ows/1.1 http://schemas.opengis.net/ows/1.1.0/owsAll.xsd"><ows:ServiceIdentification><ows:Title>Web-Service Demo with data stored at TUDOR site</ows:Title><ows:Abstract>This installation serves different Web-Service types (WMS, WFS) for testing</ows:Abstract><ows:Keywords><ows:Keyword>Geospatial WebServices</ows:Keyword><ows:Keyword>Keyword One</ows:Keyword><ows:Keyword>Keyword Two!!</ows:Keyword></ows:Keywords><ows:ServiceType codeSpace="OGC">OGC WCS</ows:ServiceType><ows:ServiceTypeVersion>1.1.0</ows:ServiceTypeVersion><ows:Fees>No fee!</ows:Fees><ows:AccessConstraints>Unconstrained!</ows:AccessConstraints></ows:ServiceIdentification><ows:ServiceProvider><ows:ProviderName>CRP Henri Tudor</ows:ProviderName><ows:ProviderSite xlink:type="simple" xlink:href="http://services.testorama.lu/cgi-bin/mapserv?map=/var/www/MapFiles/RO_localOWS_test.map&amp;"/><ows:ServiceContact><ows:IndividualName>Roy Dumerde</ows:IndividualName><ows:PositionName>R+D engineer</ows:PositionName><ows:ContactInfo><ows:Phone><ows:Voice>6463320</ows:Voice><ows:Facsimile>6465955</ows:Facsimile></ows:Phone><ows:Address><ows:DeliveryPoint>66, rue de Luxembourg</ows:DeliveryPoint><ows:City>Esch-sur-Alzette</ows:City><ows:AdministrativeArea/><ows:PostalCode>97202</ows:PostalCode><ows:Country>Luxembourg</ows:Country><ows:ElectronicMailAddress>flappy@tutones.com</ows:ElectronicMailAddress></ows:Address><ows:OnlineResource xlink:type="simple" xlink:href="http://services.testorama.lu/cgi-bin/mapserv?map=/var/www/MapFiles/RO_localOWS_test.map&amp;"/><ows:HoursOfService>24/7</ows:HoursOfService><ows:ContactInstructions>by phone</ows:ContactInstructions></ows:ContactInfo><ows:Role>GIS-Analyst</ows:Role></ows:ServiceContact></ows:ServiceProvider><ows:OperationsMetadata><ows:Operation name="GetCapabilities"><ows:DCP><ows:HTTP><ows:Get xlink:type="simple" xlink:href="http://services.testorama.lu/cgi-bin/mapserv?map=/var/www/MapFiles/RO_localOWS_test.map&amp;"/><ows:Post xlink:type="simple" xlink:href="http://services.testorama.lu/cgi-bin/mapserv?map=/var/www/MapFiles/RO_localOWS_test.map&amp;"/></ows:HTTP></ows:DCP><ows:Parameter name="service"><ows:AllowedValues><ows:Value>WCS</ows:Value></ows:AllowedValues></ows:Parameter><ows:Parameter name="version"><ows:AllowedValues><ows:Value>1.1.0</ows:Value></ows:AllowedValues></ows:Parameter></ows:Operation><ows:Operation name="DescribeCoverage"><ows:DCP><ows:HTTP><ows:Get xlink:type="simple" xlink:href="http://services.testorama.lu/cgi-bin/mapserv?map=/var/www/MapFiles/RO_localOWS_test.map&amp;"/><ows:Post xlink:type="simple" xlink:href="http://services.testorama.lu/cgi-bin/mapserv?map=/var/www/MapFiles/RO_localOWS_test.map&amp;"/></ows:HTTP></ows:DCP><ows:Parameter name="service"><ows:AllowedValues><ows:Value>WCS</ows:Value></ows:AllowedValues></ows:Parameter><ows:Parameter name="version"><ows:AllowedValues><ows:Value>1.1.0</ows:Value></ows:AllowedValues></ows:Parameter><ows:Parameter name="identifiers"><ows:AllowedValues><ows:Value>ro_dsm</ows:Value><ows:Value>ro_dsm_mini</ows:Value><ows:Value>ro_irra</ows:Value><ows:Value>ro_irra_ext</ows:Value></ows:AllowedValues></ows:Parameter></ows:Operation><ows:Operation name="GetCoverage"><ows:DCP><ows:HTTP><ows:Get xlink:type="simple" xlink:href="http://services.testorama.lu/cgi-bin/mapserv?map=/var/www/MapFiles/RO_localOWS_test.map&amp;"/><ows:Post xlink:type="simple" xlink:href="http://services.testorama.lu/cgi-bin/mapserv?map=/var/www/MapFiles/RO_localOWS_test.map&amp;"/></ows:HTTP></ows:DCP><ows:Parameter name="service"><ows:AllowedValues><ows:Value>WCS</ows:Value></ows:AllowedValues></ows:Parameter><ows:Parameter name="version"><ows:AllowedValues><ows:Value>1.1.0</ows:Value></ows:AllowedValues></ows:Parameter><ows:Parameter name="Identifier"><ows:AllowedValues><ows:Value>ro_dsm</ows:Value><ows:Value>ro_dsm_mini</ows:Value><ows:Value>ro_irra</ows:Value><ows:Value>ro_irra_ext</ows:Value></ows:AllowedValues></ows:Parameter><ows:Parameter name="InterpolationType"><ows:AllowedValues><ows:Value>NEAREST_NEIGHBOUR</ows:Value><ows:Value>BILINEAR</ows:Value></ows:AllowedValues></ows:Parameter><ows:Parameter name="format"><ows:AllowedValues><ows:Value>image/tiff</ows:Value><ows:Value>image/png</ows:Value><ows:Value>image/jpeg</ows:Value><ows:Value>image/gif</ows:Value><ows:Value>image/png; mode=8bit</ows:Value></ows:AllowedValues></ows:Parameter><ows:Parameter name="store"><ows:AllowedValues><ows:Value>false</ows:Value></ows:AllowedValues></ows:Parameter><ows:Parameter name="GridBaseCRS"><ows:AllowedValues><ows:Value>urn:ogc:def:crs:epsg::4326</ows:Value></ows:AllowedValues></ows:Parameter></ows:Operation></ows:OperationsMetadata><Contents><CoverageSummary><ows:Title>Rotterdam DSM</ows:Title><ows:Abstract>Digital Surface Model (DSM) raster data set of inner city Rotterdam</ows:Abstract><ows:WGS84BoundingBox dimensions="2"><ows:LowerCorner>4.471333734139 51.912813427383</ows:LowerCorner><ows:UpperCorner>4.4808508475645 51.9248713705576</ows:UpperCorner></ows:WGS84BoundingBox><SupportedCRS>urn:ogc:def:crs:EPSG::28992</SupportedCRS><SupportedCRS>urn:ogc:def:crs:EPSG::900913</SupportedCRS><SupportedCRS>urn:ogc:def:crs:EPSG::3857</SupportedCRS><SupportedCRS>urn:ogc:def:crs:EPSG::4326</SupportedCRS><SupportedFormat>image/tiff</SupportedFormat><Identifier>ro_dsm</Identifier></CoverageSummary><CoverageSummary><ows:Title>Rotterdam sample DSM subset</ows:Title><ows:Abstract>This a test data set of Rotterdams DSM subset</ows:Abstract><ows:WGS84BoundingBox dimensions="2"><ows:LowerCorner>4.47489346945755 51.9159453786927</ows:LowerCorner><ows:UpperCorner>4.47687824892444 51.9170706688033</ows:UpperCorner></ows:WGS84BoundingBox><SupportedCRS>urn:ogc:def:crs:EPSG::28992</SupportedCRS><SupportedCRS>urn:ogc:def:crs:EPSG::900913</SupportedCRS><SupportedCRS>urn:ogc:def:crs:EPSG::3857</SupportedCRS><SupportedCRS>urn:ogc:def:crs:EPSG::4326</SupportedCRS><SupportedFormat>image/tiff</SupportedFormat><Identifier>ro_dsm_mini</Identifier></CoverageSummary><CoverageSummary><ows:Title>Rotterdam (Ljinbaan) solar irradiation data 2010</ows:Title><ows:Abstract>This a result data set of a solar computation of Ljinbaan area. It shows the sum of kWh/a per sqmeter for 2010</ows:Abstract><ows:WGS84BoundingBox dimensions="2"><ows:LowerCorner>4.471333734139 51.912813427383</ows:LowerCorner><ows:UpperCorner>4.4808508475645 51.9248713705576</ows:UpperCorner></ows:WGS84BoundingBox><SupportedCRS>urn:ogc:def:crs:EPSG::28992</SupportedCRS><SupportedCRS>urn:ogc:def:crs:EPSG::900913</SupportedCRS><SupportedCRS>urn:ogc:def:crs:EPSG::3857</SupportedCRS><SupportedCRS>urn:ogc:def:crs:EPSG::4326</SupportedCRS><SupportedFormat>image/tiff</SupportedFormat><Identifier>ro_irra</Identifier></CoverageSummary><CoverageSummary><ows:Title>Rotterdam (extended) solar irradiation data 2010</ows:Title><ows:Abstract>This a result data set of a solar computation of extended Rotterdam area. It shows the sum of kWh/a per sqmeter for 2010</ows:Abstract><ows:WGS84BoundingBox dimensions="2"><ows:LowerCorner>4.10024171314823 51.9359764992844</ows:LowerCorner><ows:UpperCorner>4.21909054278063 52.001415228243</ows:UpperCorner></ows:WGS84BoundingBox><SupportedCRS>urn:ogc:def:crs:EPSG::28992</SupportedCRS><SupportedCRS>urn:ogc:def:crs:EPSG::900913</SupportedCRS><SupportedCRS>urn:ogc:def:crs:EPSG::3857</SupportedCRS><SupportedCRS>urn:ogc:def:crs:EPSG::4326</SupportedCRS><SupportedFormat>image/tiff</SupportedFormat><Identifier>ro_irra_ext</Identifier></CoverageSummary></Contents></Capabilities>';
+
+ res = parser.read(text);
+
+ // Most of the parsing is handled by other objects, so not much actually requires testing here
+ t.ok(!res.error, "Parsing XML generated no errors");
+ t.eq(res.contentMetadata.length, 4, "number of features correct");
+
+ var metadata = res.contentMetadata[0];
+ t.eq(metadata.identifier, "ro_dsm", "correct identifier");
+ t.eq(metadata.title, "Rotterdam DSM", "correct title");
+ t.eq(metadata.abstract, "Digital Surface Model (DSM) raster data set of inner city Rotterdam", "correct abstract");
+ t.eq(metadata.supportedFormat.length, 1, "correct number of supported formats");
+ t.eq(metadata.supportedFormat[0], "image/tiff", "correct format");
+ t.eq(metadata.supportedCRS.length, 4, "correct number of CRS records");
+ t.eq(metadata.supportedCRS[2], "urn:ogc:def:crs:EPSG::3857", "correct CRS");
+ }
+
+ </script>
+</head>
+<body> </body>
+</html>
diff --git a/misc/openlayers/tests/Format/WCSGetCoverage.html b/misc/openlayers/tests/Format/WCSGetCoverage.html
new file mode 100644
index 0000000..6379b2b
--- /dev/null
+++ b/misc/openlayers/tests/Format/WCSGetCoverage.html
@@ -0,0 +1,80 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_write_WCSGetCoverage(t) {
+ t.plan(1);
+ var expected = '<?xml version="1.0" encoding="UTF-8"?>' +
+'<GetCoverage xmlns="http://www.opengis.net/wcs/1.1" xmlns:ows="http://www.opengis.net/ows/1.1"' +
+' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
+' xsi:schemaLocation="http://www.opengis.net/wcs/1.1 http://schemas.opengis.net/wcs/1.1/wcsGetCoverage.xsd"' +
+' service="WCS" version="1.1.2">' +
+' <ows:Identifier>Cov123</ows:Identifier>' +
+' <DomainSubset>' +
+' <ows:BoundingBox crs="urn:ogc:def:crs:OGC:2:84">' +
+' <ows:LowerCorner>-71 47</ows:LowerCorner>' +
+' <ows:UpperCorner>-66 51</ows:UpperCorner>' +
+' </ows:BoundingBox>' +
+' <TemporalSubset>' +
+' <TimePeriod>' +
+' <BeginPosition>2006-08-01</BeginPosition>' +
+' <EndPosition>2006-09-01</EndPosition>' +
+' <TimeResolution>P1D</TimeResolution>' +
+' </TimePeriod>' +
+' <TimePeriod>' +
+' <BeginPosition>2007-08-01</BeginPosition>' +
+' <EndPosition>2007-09-01</EndPosition>' +
+' <TimeResolution>P1D</TimeResolution>' +
+' </TimePeriod>' +
+' </TemporalSubset>' +
+' </DomainSubset>' +
+' <Output format="image/netcdf">' +
+' <GridCRS>' +
+' <GridBaseCRS>urn:ogc:def:crs:EPSG:6.6:32618</GridBaseCRS>' +
+' <GridType>urn:ogc:def:method:WCS:1.1:2dGridin2dCrs</GridType>' +
+' <GridOrigin>3000 4000</GridOrigin>' +
+' <GridOffsets>6.0 8.0 -8.0 6.0</GridOffsets>' +
+' <GridCS>urn:ogc:def:cs:OGC:0.0:Grid2dSquareCS</GridCS>' +
+' </GridCRS>' +
+' </Output>' +
+'</GetCoverage>';
+
+ var format = new OpenLayers.Format.WCSGetCoverage();
+ var result = format.write({
+ identifier: 'Cov123',
+ domainSubset: {
+ boundingBox: {projection: 'urn:ogc:def:crs:OGC:2:84', bounds: new OpenLayers.Bounds(-71, 47, -66, 51)},
+ temporalSubset: {
+ timePeriods: [
+ {
+ begin: '2006-08-01',
+ end: '2006-09-01',
+ resolution: 'P1D'
+ }, {
+ begin: '2007-08-01',
+ end: '2007-09-01',
+ resolution: 'P1D'
+ }
+ ]
+ }
+ },
+ output: {
+ format: 'image/netcdf',
+ gridCRS: {
+ baseCRS: 'urn:ogc:def:crs:EPSG:6.6:32618',
+ type: 'urn:ogc:def:method:WCS:1.1:2dGridin2dCrs',
+ origin: '3000 4000',
+ offsets: '6.0 8.0 -8.0 6.0',
+ CS: 'urn:ogc:def:cs:OGC:0.0:Grid2dSquareCS'
+ }
+ }
+ });
+ t.xml_eq(result, expected, "WCS GetCoverage written out correctly");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WFS.html b/misc/openlayers/tests/Format/WFS.html
new file mode 100644
index 0000000..7b3b737
--- /dev/null
+++ b/misc/openlayers/tests/Format/WFS.html
@@ -0,0 +1,81 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script>
+ function test_wfs_update_node(t) {
+ t.plan(2);
+ var expected = readXML("Update");
+ var updateFeature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(1,2),
+ {foo: "bar"});
+ updateFeature.fid = "fid.42";
+ updateFeature.state = OpenLayers.State.UPDATE;
+ var format = new OpenLayers.Format.WFS({
+ 'featureNS':'http://www.openplans.org/topp',
+ 'featureName': 'states',
+ 'geometryName': 'the_geom',
+ 'featurePrefix': 'topp'
+ }, {options:{}});
+ var updateNode = format.update(updateFeature);
+ t.xml_eq(updateNode, expected, "update node matches expected XML value.");
+ var format = new OpenLayers.Format.WFS({
+ 'featurePrefix': 'topp'
+ }, {options:{typename: 'states', 'featureNS': 'http://www.openplans.org/topp', 'geometry_column': 'the_geom' }});
+ var updateNode = format.update(updateFeature);
+ t.xml_eq(updateNode, expected, "update node matches expected XML value.");
+ }
+ function test_wfs_delete_node(t) {
+ t.plan(2);
+ var expected = readXML("Delete");
+ var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,0));
+ feature.state = OpenLayers.State.DELETE;
+ feature.fid = "fid.37";
+ var format = new OpenLayers.Format.WFS({
+ 'featureNS':'http://www.openplans.org/topp',
+ 'featureName': 'states',
+ 'featurePrefix': 'topp'
+ }, {options:{}});
+ var deleteNode = format.remove(feature);
+ t.xml_eq(deleteNode, expected, "delete node matches expected XML value.");
+ var format = new OpenLayers.Format.WFS({
+ 'featurePrefix': 'topp'
+ }, {options:{typename: 'states', 'featureNS': 'http://www.openplans.org/topp'}});
+ var deleteNode = format.remove(feature);
+ t.xml_eq(deleteNode, expected, "delete node matches expected XML value.");
+ }
+ function readXML(id) {
+ var xml = document.getElementById(id).firstChild.nodeValue;
+ return new OpenLayers.Format.XML().read(xml).documentElement;
+ }
+
+ </script>
+</head>
+<body>
+<div id="Update"><!--
+<wfs:Update xmlns:wfs="http://www.opengis.net/wfs" typeName="topp:states" xmlns:topp="http://www.openplans.org/topp">
+ <wfs:Property>
+ <wfs:Name>the_geom</wfs:Name>
+ <wfs:Value>
+ <gml:Point xmlns:gml="http://www.opengis.net/gml">
+ <gml:coordinates decimal="." cs="," ts=" ">1,2</gml:coordinates>
+ </gml:Point>
+ </wfs:Value>
+ </wfs:Property>
+ <wfs:Property>
+ <wfs:Name>foo</wfs:Name>
+ <wfs:Value>bar</wfs:Value>
+ </wfs:Property>
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:FeatureId fid="fid.42"/>
+ </ogc:Filter>
+</wfs:Update>
+--></div>
+<div id="Delete"><!--
+<wfs:Delete xmlns:wfs="http://www.opengis.net/wfs" typeName="topp:states" xmlns:topp="http://www.openplans.org/topp">
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:FeatureId fid="fid.37"/>
+ </ogc:Filter>
+</wfs:Delete>
+--></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WFSCapabilities.html b/misc/openlayers/tests/Format/WFSCapabilities.html
new file mode 100644
index 0000000..fc2ff2d
--- /dev/null
+++ b/misc/openlayers/tests/Format/WFSCapabilities.html
@@ -0,0 +1,43 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read(t) {
+ t.plan(4);
+
+ var _v1_0_0 = OpenLayers.Format.WFSCapabilities.v1_0_0.prototype.read;
+ var _v1_1_0 = OpenLayers.Format.WFSCapabilities.v1_1_0.prototype.read;
+
+ var parser = new OpenLayers.Format.WFSCapabilities();
+
+ // version 1.0.0
+ var text =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<wfs:WFS_Capabilities version="1.0.0" xmlns:wfs="http://www.opengis.net/wfs"></wfs:WFS_Capabilities>';
+ OpenLayers.Format.WFSCapabilities.v1_0_0.prototype.read = function() {
+ t.ok(true, "Version 1.0.0 detected");
+ return {};
+ }
+ var res = parser.read(text);
+ t.eq(res.version, "1.0.0", "version 1.0.0 written to result object");
+ OpenLayers.Format.WFSCapabilities.v1_1_0.prototype.read = _v1_1_0;
+
+ // version 1.1.0
+ var text =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<wfs:WFS_Capabilities version="1.1.0" xmlns:wfs="http://www.opengis.net/wfs"></wfs:WFS_Capabilities>';
+ OpenLayers.Format.WFSCapabilities.v1_1_0.prototype.read = function() {
+ t.ok(true, "Version 1.1.0 detected");
+ return {};
+ }
+ var res = parser.read(text);
+ t.eq(res.version, "1.1.0", "version 1.1.0 written to result object");
+ OpenLayers.Format.WFSCapabilities.v1_1_0.prototype.read = _v1_1_0;
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WFSCapabilities/v1.html b/misc/openlayers/tests/Format/WFSCapabilities/v1.html
new file mode 100644
index 0000000..0bc1705
--- /dev/null
+++ b/misc/openlayers/tests/Format/WFSCapabilities/v1.html
@@ -0,0 +1,179 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read_exception(t) {
+ t.plan(1);
+ var parser = new OpenLayers.Format.WFSCapabilities();
+ var text = '<?xml version="1.0" encoding="UTF-8"?>' +
+'<ows:ExceptionReport language="en" version="1.0.0"' +
+' xsi:schemaLocation="http://www.opengis.net/ows http://schemas.opengis.net/ows/1.0.0/owsExceptionReport.xsd"' +
+' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ows="http://www.opengis.net/ows">' +
+' <ows:Exception locator="foo" exceptionCode="InvalidParameterValue">' +
+' <ows:ExceptionText>Update error: Error occured updating features</ows:ExceptionText>' +
+' <ows:ExceptionText>Second exception line</ows:ExceptionText>' +
+' </ows:Exception>' +
+'</ows:ExceptionReport>';
+
+ var obj = parser.read(text);
+ t.ok(!!obj.error, "Error reported correctly"); // The above should place an error in obj.error
+ }
+
+ function test_read(t) {
+ t.plan(37); // Number of tests performed: If you add a test below, be sure to increment this accordingly
+
+ var parser = new OpenLayers.Format.WFSCapabilities();
+
+ // GeoServer, v1.1.0
+ var text = '<?xml version="1.0" encoding="UTF-8"?><wfs:WFS_Capabilities version="1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.opengis.net/wfs" xmlns:wfs="http://www.opengis.net/wfs" xmlns:ows="http://www.opengis.net/ows" xmlns:gml="http://www.opengis.net/gml" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/wfs http://localhost:80/geoserver/schemas/wfs/1.1.0/wfs.xsd" xmlns:it.geosolutions="http://www.geo-solutions.it" xmlns:cite="http://www.opengeospatial.net/cite" xmlns:tiger="http://www.census.gov" xmlns:sde="http://geoserver.sf.net" xmlns:topp="http://www.openplans.org/topp" xmlns:sf="http://www.openplans.org/spearfish" xmlns:nurc="http://www.nurc.nato.int" updateSequence="57"><ows:ServiceIdentification><ows:Title>GeoServer Web Feature Service</ows:Title><ows:Abstract>This is the reference implementation of WFS 1.0.0 and WFS 1.1.0, supports all WFS operations including Transaction.</ows:Abstract><ows:Keywords><ows:Keyword>WFS</ows:Keyword><ows:Keyword>WMS</ows:Keyword><ows:Keyword>GEOSERVER</ows:Keyword></ows:Keywords><ows:ServiceType>WFS</ows:ServiceType><ows:ServiceTypeVersion>1.1.0</ows:ServiceTypeVersion><ows:Fees>NONE</ows:Fees><ows:AccessConstraints>NONE</ows:AccessConstraints></ows:ServiceIdentification><ows:ServiceProvider><ows:ProviderName>The ancient geographes INC</ows:ProviderName><ows:ServiceContact><ows:IndividualName>Claudius Ptolomaeus</ows:IndividualName><ows:PositionName>Chief geographer</ows:PositionName><ows:ContactInfo><ows:Phone><ows:Voice/><ows:Facsimile/></ows:Phone><ows:Address><ows:City>Alexandria</ows:City><ows:AdministrativeArea/><ows:PostalCode/><ows:Country>Egypt</ows:Country></ows:Address></ows:ContactInfo></ows:ServiceContact></ows:ServiceProvider><ows:OperationsMetadata><ows:Operation name="GetCapabilities"><ows:DCP><ows:HTTP><ows:Get xlink:href="http://localhost:80/geoserver/wfs?"/><ows:Post xlink:href="http://localhost:80/geoserver/wfs?"/></ows:HTTP></ows:DCP><ows:Parameter name="AcceptVersions"><ows:Value>1.0.0</ows:Value><ows:Value>1.1.0</ows:Value></ows:Parameter><ows:Parameter name="AcceptFormats"><ows:Value>text/xml</ows:Value></ows:Parameter></ows:Operation><ows:Operation name="DescribeFeatureType"><ows:DCP><ows:HTTP><ows:Get xlink:href="http://localhost:80/geoserver/wfs?"/><ows:Post xlink:href="http://localhost:80/geoserver/wfs?"/></ows:HTTP></ows:DCP><ows:Parameter name="outputFormat"><ows:Value>text/xml; subtype=gml/3.1.1</ows:Value></ows:Parameter></ows:Operation><ows:Operation name="GetFeature"><ows:DCP><ows:HTTP><ows:Get xlink:href="http://localhost:80/geoserver/wfs?"/><ows:Post xlink:href="http://localhost:80/geoserver/wfs?"/></ows:HTTP></ows:DCP><ows:Parameter name="resultType"><ows:Value>results</ows:Value><ows:Value>hits</ows:Value></ows:Parameter><ows:Parameter name="outputFormat"><ows:Value>text/xml; subtype=gml/3.1.1</ows:Value><ows:Value>GML2</ows:Value><ows:Value>GML2-GZIP</ows:Value><ows:Value>SHAPE-ZIP</ows:Value><ows:Value>csv</ows:Value><ows:Value>gml3</ows:Value><ows:Value>json</ows:Value><ows:Value>text/xml; subtype=gml/2.1.2</ows:Value></ows:Parameter><ows:Constraint name="LocalTraverseXLinkScope"><ows:Value>2</ows:Value></ows:Constraint></ows:Operation><ows:Operation name="GetGmlObject"><ows:DCP><ows:HTTP><ows:Get xlink:href="http://localhost:80/geoserver/wfs?"/><ows:Post xlink:href="http://localhost:80/geoserver/wfs?"/></ows:HTTP></ows:DCP></ows:Operation><ows:Operation name="LockFeature"><ows:DCP><ows:HTTP><ows:Get xlink:href="http://localhost:80/geoserver/wfs?"/><ows:Post xlink:href="http://localhost:80/geoserver/wfs?"/></ows:HTTP></ows:DCP><ows:Parameter name="releaseAction"><ows:Value>ALL</ows:Value><ows:Value>SOME</ows:Value></ows:Parameter></ows:Operation><ows:Operation name="GetFeatureWithLock"><ows:DCP><ows:HTTP><ows:Get xlink:href="http://localhost:80/geoserver/wfs?"/><ows:Post xlink:href="http://localhost:80/geoserver/wfs?"/></ows:HTTP></ows:DCP><ows:Parameter name="resultType"><ows:Value>results</ows:Value><ows:Value>hits</ows:Value></ows:Parameter><ows:Parameter name="outputFormat"><ows:Value>text/xml; subtype=gml/3.1.1</ows:Value><ows:Value>GML2</ows:Value><ows:Value>GML2-GZIP</ows:Value><ows:Value>SHAPE-ZIP</ows:Value><ows:Value>csv</ows:Value><ows:Value>gml3</ows:Value><ows:Value>json</ows:Value><ows:Value>text/xml; subtype=gml/2.1.2</ows:Value></ows:Parameter></ows:Operation><ows:Operation name="Transaction"><ows:DCP><ows:HTTP><ows:Get xlink:href="http://localhost:80/geoserver/wfs?"/><ows:Post xlink:href="http://localhost:80/geoserver/wfs?"/></ows:HTTP></ows:DCP><ows:Parameter name="inputFormat"><ows:Value>text/xml; subtype=gml/3.1.1</ows:Value></ows:Parameter><ows:Parameter name="idgen"><ows:Value>GenerateNew</ows:Value><ows:Value>UseExisting</ows:Value><ows:Value>ReplaceDuplicate</ows:Value></ows:Parameter><ows:Parameter name="releaseAction"><ows:Value>ALL</ows:Value><ows:Value>SOME</ows:Value></ows:Parameter></ows:Operation></ows:OperationsMetadata><FeatureTypeList><Operations><Operation>Query</Operation><Operation>Insert</Operation><Operation>Update</Operation><Operation>Delete</Operation><Operation>Lock</Operation></Operations><FeatureType xmlns:tiger="http://www.census.gov"><Name>tiger:poly_landmarks</Name><Title>Manhattan (NY) landmarks</Title><Abstract>Manhattan landmarks, identifies water, lakes, parks, interesting buildilngs</Abstract><ows:Keywords><ows:Keyword>DS_poly_landmarks</ows:Keyword><ows:Keyword>poly_landmarks</ows:Keyword><ows:Keyword>landmarks</ows:Keyword><ows:Keyword>manhattan</ows:Keyword></ows:Keywords><DefaultSRS>urn:x-ogc:def:crs:EPSG:4326</DefaultSRS><ows:WGS84BoundingBox><ows:LowerCorner>-74.047185 40.679648</ows:LowerCorner><ows:UpperCorner>-73.90782 40.882078</ows:UpperCorner></ows:WGS84BoundingBox></FeatureType><FeatureType xmlns:tiger="http://www.census.gov"><Name>tiger:poi</Name><Title>Manhattan (NY) points of interest</Title><Abstract>Points of interest in New York, New York (on Manhattan). One of the attributes contains the name of a file with a picture of the point of interest.</Abstract><ows:Keywords><ows:Keyword>poi</ows:Keyword><ows:Keyword>DS_poi</ows:Keyword><ows:Keyword>points_of_interest</ows:Keyword><ows:Keyword>Manhattan</ows:Keyword></ows:Keywords><DefaultSRS>urn:x-ogc:def:crs:EPSG:4326</DefaultSRS><ows:WGS84BoundingBox><ows:LowerCorner>-74.0118315772888 40.70754683896324</ows:LowerCorner><ows:UpperCorner>-74.00857344353275 40.711945649065406</ows:UpperCorner></ows:WGS84BoundingBox></FeatureType><FeatureType xmlns:tiger="http://www.census.gov"><Name>tiger:tiger_roads</Name><Title>Manhattan (NY) roads</Title><Abstract>Highly simplified road layout of Manhattan in New York..</Abstract><ows:Keywords><ows:Keyword>DS_tiger_roads</ows:Keyword><ows:Keyword>tiger_roads</ows:Keyword><ows:Keyword>roads</ows:Keyword></ows:Keywords><DefaultSRS>urn:x-ogc:def:crs:EPSG:4326</DefaultSRS><ows:WGS84BoundingBox><ows:LowerCorner>-74.02722 40.684221</ows:LowerCorner><ows:UpperCorner>-73.907005 40.878178</ows:UpperCorner></ows:WGS84BoundingBox></FeatureType><FeatureType xmlns:sf="http://www.openplans.org/spearfish"><Name>sf:archsites</Name><Title>Spearfish archeological sites</Title><Abstract>Sample data from GRASS, archeological sites location, Spearfish, South Dakota, USA</Abstract><ows:Keywords><ows:Keyword>archsites</ows:Keyword><ows:Keyword>sfArchsites</ows:Keyword><ows:Keyword>spearfish</ows:Keyword><ows:Keyword>archeology</ows:Keyword></ows:Keywords><DefaultSRS>urn:x-ogc:def:crs:EPSG:26713</DefaultSRS><ows:WGS84BoundingBox><ows:LowerCorner>-103.8725637911543 44.37740330855979</ows:LowerCorner><ows:UpperCorner>-103.63794182141925 44.48804280772808</ows:UpperCorner></ows:WGS84BoundingBox></FeatureType><FeatureType xmlns:sf="http://www.openplans.org/spearfish"><Name>sf:bugsites</Name><Title>Spearfish bug locations</Title><Abstract>Sample data from GRASS, bug sites location, Spearfish, South Dakota, USA</Abstract><ows:Keywords><ows:Keyword>sfBugsites</ows:Keyword><ows:Keyword>bugsites</ows:Keyword><ows:Keyword>insects</ows:Keyword><ows:Keyword>spearfish</ows:Keyword><ows:Keyword>tiger_beetles</ows:Keyword></ows:Keywords><DefaultSRS>urn:x-ogc:def:crs:EPSG:26713</DefaultSRS><ows:WGS84BoundingBox><ows:LowerCorner>-103.86796131703647 44.373938816704396</ows:LowerCorner><ows:UpperCorner>-103.63773523234195 44.43418821380063</ows:UpperCorner></ows:WGS84BoundingBox></FeatureType><FeatureType xmlns:sf="http://www.openplans.org/spearfish"><Name>sf:restricted</Name><Title>Spearfish restricted areas</Title><Abstract>Sample data from GRASS, restricted areas, Spearfish, South Dakota, USA</Abstract><ows:Keywords><ows:Keyword>restricted</ows:Keyword><ows:Keyword>sfRestricted</ows:Keyword><ows:Keyword>spearfish</ows:Keyword><ows:Keyword>areas</ows:Keyword></ows:Keywords><DefaultSRS>urn:x-ogc:def:crs:EPSG:26713</DefaultSRS><ows:WGS84BoundingBox><ows:LowerCorner>-103.85057172920756 44.39436387625042</ows:LowerCorner><ows:UpperCorner>-103.74741494853805 44.48215752041131</ows:UpperCorner></ows:WGS84BoundingBox></FeatureType><FeatureType xmlns:sf="http://www.openplans.org/spearfish"><Name>sf:roads</Name><Title>Spearfish roads</Title><Abstract>Sample data from GRASS, road layout, Spearfish, South Dakota, USA</Abstract><ows:Keywords><ows:Keyword>sfRoads</ows:Keyword><ows:Keyword>roads</ows:Keyword><ows:Keyword>spearfish</ows:Keyword></ows:Keywords><DefaultSRS>urn:x-ogc:def:crs:EPSG:26713</DefaultSRS><ows:WGS84BoundingBox><ows:LowerCorner>-103.87741691493184 44.37087275281798</ows:LowerCorner><ows:UpperCorner>-103.62231404880659 44.50015918338962</ows:UpperCorner></ows:WGS84BoundingBox></FeatureType><FeatureType xmlns:sf="http://www.openplans.org/spearfish"><Name>sf:streams</Name><Title>Spearfish streams</Title><Abstract>Sample data from GRASS, streams, Spearfish, South Dakota, USA</Abstract><ows:Keywords><ows:Keyword>sfStreams</ows:Keyword><ows:Keyword>streams</ows:Keyword><ows:Keyword>spearfish</ows:Keyword></ows:Keywords><DefaultSRS>urn:x-ogc:def:crs:EPSG:26713</DefaultSRS><ows:WGS84BoundingBox><ows:LowerCorner>-103.87789019829768 44.372335260095554</ows:LowerCorner><ows:UpperCorner>-103.62287788915457 44.502218486214815</ows:UpperCorner></ows:WGS84BoundingBox></FeatureType><FeatureType xmlns:topp="http://www.openplans.org/topp"><Name>topp:tasmania_cities</Name><Title>Tasmania cities</Title><Abstract>Cities in Tasmania (actually, just the capital)</Abstract><ows:Keywords><ows:Keyword>cities</ows:Keyword><ows:Keyword>Tasmania</ows:Keyword></ows:Keywords><DefaultSRS>urn:x-ogc:def:crs:EPSG:4326</DefaultSRS><ows:WGS84BoundingBox><ows:LowerCorner>145.19754 -43.423512</ows:LowerCorner><ows:UpperCorner>148.27298000000002 -40.852802</ows:UpperCorner></ows:WGS84BoundingBox></FeatureType><FeatureType xmlns:topp="http://www.openplans.org/topp"><Name>topp:tasmania_roads</Name><Title>Tasmania roads</Title><Abstract>Main Tasmania roads</Abstract><ows:Keywords><ows:Keyword>Roads</ows:Keyword><ows:Keyword>Tasmania</ows:Keyword></ows:Keywords><DefaultSRS>urn:x-ogc:def:crs:EPSG:4326</DefaultSRS><ows:WGS84BoundingBox><ows:LowerCorner>145.19754 -43.423512</ows:LowerCorner><ows:UpperCorner>148.27298000000002 -40.852802</ows:UpperCorner></ows:WGS84BoundingBox></FeatureType><FeatureType xmlns:topp="http://www.openplans.org/topp"><Name>topp:tasmania_state_boundaries</Name><Title>Tasmania state boundaries</Title><Abstract>Tasmania state boundaries</Abstract><ows:Keywords><ows:Keyword>tasmania_state_boundaries</ows:Keyword><ows:Keyword>Tasmania</ows:Keyword><ows:Keyword>boundaries</ows:Keyword></ows:Keywords><DefaultSRS>urn:x-ogc:def:crs:EPSG:4326</DefaultSRS><ows:WGS84BoundingBox><ows:LowerCorner>143.83482400000003 -43.648056</ows:LowerCorner><ows:UpperCorner>148.47914100000003 -39.573891</ows:UpperCorner></ows:WGS84BoundingBox></FeatureType><FeatureType xmlns:topp="http://www.openplans.org/topp"><Name>topp:tasmania_water_bodies</Name><Title>Tasmania water bodies</Title><Abstract>Tasmania water bodies</Abstract><ows:Keywords><ows:Keyword>Lakes</ows:Keyword><ows:Keyword>Bodies</ows:Keyword><ows:Keyword>Australia</ows:Keyword><ows:Keyword>Water</ows:Keyword><ows:Keyword>Tasmania</ows:Keyword></ows:Keywords><DefaultSRS>urn:x-ogc:def:crs:EPSG:4326</DefaultSRS><ows:WGS84BoundingBox><ows:LowerCorner>145.97161899999998 -43.031944</ows:LowerCorner><ows:UpperCorner>147.219696 -41.775558</ows:UpperCorner></ows:WGS84BoundingBox></FeatureType><FeatureType xmlns:topp="http://www.openplans.org/topp"><Name>topp:states</Name><Title>USA Population</Title><Abstract>This is some census data on the states.</Abstract><ows:Keywords><ows:Keyword>census</ows:Keyword><ows:Keyword>united</ows:Keyword><ows:Keyword>boundaries</ows:Keyword><ows:Keyword>state</ows:Keyword><ows:Keyword>states</ows:Keyword></ows:Keywords><DefaultSRS>urn:x-ogc:def:crs:EPSG:4326</DefaultSRS><ows:WGS84BoundingBox><ows:LowerCorner>-124.731422 24.955967</ows:LowerCorner><ows:UpperCorner>-66.969849 49.371735</ows:UpperCorner></ows:WGS84BoundingBox></FeatureType><FeatureType xmlns:tiger="http://www.census.gov"><Name>tiger:giant_polygon</Name><Title>World rectangle</Title><Abstract>A simple rectangular polygon covering most of the world, it\'s only used for the purpose of providing a background (WMS bgcolor could be used instead)</Abstract><ows:Keywords><ows:Keyword>DS_giant_polygon</ows:Keyword><ows:Keyword>giant_polygon</ows:Keyword></ows:Keywords><DefaultSRS>urn:x-ogc:def:crs:EPSG:4326</DefaultSRS><ows:WGS84BoundingBox><ows:LowerCorner>-180.0 -90.0</ows:LowerCorner><ows:UpperCorner>180.0 90.0</ows:UpperCorner></ows:WGS84BoundingBox></FeatureType></FeatureTypeList><ogc:Filter_Capabilities><ogc:Spatial_Capabilities><ogc:GeometryOperands><ogc:GeometryOperand>gml:Envelope</ogc:GeometryOperand><ogc:GeometryOperand>gml:Point</ogc:GeometryOperand><ogc:GeometryOperand>gml:LineString</ogc:GeometryOperand><ogc:GeometryOperand>gml:Polygon</ogc:GeometryOperand></ogc:GeometryOperands><ogc:SpatialOperators><ogc:SpatialOperator name="Disjoint"/><ogc:SpatialOperator name="Equals"/><ogc:SpatialOperator name="DWithin"/><ogc:SpatialOperator name="Beyond"/><ogc:SpatialOperator name="Intersects"/><ogc:SpatialOperator name="Touches"/><ogc:SpatialOperator name="Crosses"/><ogc:SpatialOperator name="Contains"/><ogc:SpatialOperator name="Overlaps"/><ogc:SpatialOperator name="BBOX"/></ogc:SpatialOperators></ogc:Spatial_Capabilities><ogc:Scalar_Capabilities><ogc:LogicalOperators/><ogc:ComparisonOperators><ogc:ComparisonOperator>LessThan</ogc:ComparisonOperator><ogc:ComparisonOperator>GreaterThan</ogc:ComparisonOperator><ogc:ComparisonOperator>LessThanEqualTo</ogc:ComparisonOperator><ogc:ComparisonOperator>GreaterThanEqualTo</ogc:ComparisonOperator><ogc:ComparisonOperator>EqualTo</ogc:ComparisonOperator><ogc:ComparisonOperator>NotEqualTo</ogc:ComparisonOperator><ogc:ComparisonOperator>Like</ogc:ComparisonOperator><ogc:ComparisonOperator>Between</ogc:ComparisonOperator><ogc:ComparisonOperator>NullCheck</ogc:ComparisonOperator></ogc:ComparisonOperators><ogc:ArithmeticOperators><ogc:SimpleArithmetic/><ogc:Functions><ogc:FunctionNames><ogc:FunctionName nArgs="1">abs</ogc:FunctionName><ogc:FunctionName nArgs="1">abs_2</ogc:FunctionName><ogc:FunctionName nArgs="1">abs_3</ogc:FunctionName><ogc:FunctionName nArgs="1">abs_4</ogc:FunctionName><ogc:FunctionName nArgs="1">acos</ogc:FunctionName><ogc:FunctionName nArgs="1">Area</ogc:FunctionName><ogc:FunctionName nArgs="1">asin</ogc:FunctionName><ogc:FunctionName nArgs="1">atan</ogc:FunctionName><ogc:FunctionName nArgs="2">atan2</ogc:FunctionName><ogc:FunctionName nArgs="3">between</ogc:FunctionName><ogc:FunctionName nArgs="1">boundary</ogc:FunctionName><ogc:FunctionName nArgs="1">boundaryDimension</ogc:FunctionName><ogc:FunctionName nArgs="2">buffer</ogc:FunctionName><ogc:FunctionName nArgs="3">bufferWithSegments</ogc:FunctionName><ogc:FunctionName nArgs="0">Categorize</ogc:FunctionName><ogc:FunctionName nArgs="1">ceil</ogc:FunctionName><ogc:FunctionName nArgs="1">centroid</ogc:FunctionName><ogc:FunctionName nArgs="2">classify</ogc:FunctionName><ogc:FunctionName nArgs="1">Collection_Average</ogc:FunctionName><ogc:FunctionName nArgs="1">Collection_Bounds</ogc:FunctionName><ogc:FunctionName nArgs="1">Collection_Count</ogc:FunctionName><ogc:FunctionName nArgs="1">Collection_Max</ogc:FunctionName><ogc:FunctionName nArgs="1">Collection_Median</ogc:FunctionName><ogc:FunctionName nArgs="1">Collection_Min</ogc:FunctionName><ogc:FunctionName nArgs="1">Collection_Sum</ogc:FunctionName><ogc:FunctionName nArgs="1">Collection_Unique</ogc:FunctionName><ogc:FunctionName nArgs="2">Concatenate</ogc:FunctionName><ogc:FunctionName nArgs="2">contains</ogc:FunctionName><ogc:FunctionName nArgs="1">convexHull</ogc:FunctionName><ogc:FunctionName nArgs="1">cos</ogc:FunctionName><ogc:FunctionName nArgs="2">crosses</ogc:FunctionName><ogc:FunctionName nArgs="2">dateFormat</ogc:FunctionName><ogc:FunctionName nArgs="2">dateParse</ogc:FunctionName><ogc:FunctionName nArgs="2">difference</ogc:FunctionName><ogc:FunctionName nArgs="1">dimension</ogc:FunctionName><ogc:FunctionName nArgs="2">disjoint</ogc:FunctionName><ogc:FunctionName nArgs="2">distance</ogc:FunctionName><ogc:FunctionName nArgs="1">double2bool</ogc:FunctionName><ogc:FunctionName nArgs="1">endPoint</ogc:FunctionName><ogc:FunctionName nArgs="1">envelope</ogc:FunctionName><ogc:FunctionName nArgs="2">EqualInterval</ogc:FunctionName><ogc:FunctionName nArgs="2">equalsExact</ogc:FunctionName><ogc:FunctionName nArgs="3">equalsExactTolerance</ogc:FunctionName><ogc:FunctionName nArgs="2">equalTo</ogc:FunctionName><ogc:FunctionName nArgs="1">exp</ogc:FunctionName><ogc:FunctionName nArgs="1">exteriorRing</ogc:FunctionName><ogc:FunctionName nArgs="1">floor</ogc:FunctionName><ogc:FunctionName nArgs="1">geometryType</ogc:FunctionName><ogc:FunctionName nArgs="1">geomFromWKT</ogc:FunctionName><ogc:FunctionName nArgs="1">geomLength</ogc:FunctionName><ogc:FunctionName nArgs="2">getGeometryN</ogc:FunctionName><ogc:FunctionName nArgs="1">getX</ogc:FunctionName><ogc:FunctionName nArgs="1">getY</ogc:FunctionName><ogc:FunctionName nArgs="1">getZ</ogc:FunctionName><ogc:FunctionName nArgs="2">greaterEqualThan</ogc:FunctionName><ogc:FunctionName nArgs="2">greaterThan</ogc:FunctionName><ogc:FunctionName nArgs="0">id</ogc:FunctionName><ogc:FunctionName nArgs="2">IEEEremainder</ogc:FunctionName><ogc:FunctionName nArgs="3">if_then_else</ogc:FunctionName><ogc:FunctionName nArgs="11">in10</ogc:FunctionName><ogc:FunctionName nArgs="3">in2</ogc:FunctionName><ogc:FunctionName nArgs="4">in3</ogc:FunctionName><ogc:FunctionName nArgs="5">in4</ogc:FunctionName><ogc:FunctionName nArgs="6">in5</ogc:FunctionName><ogc:FunctionName nArgs="7">in6</ogc:FunctionName><ogc:FunctionName nArgs="8">in7</ogc:FunctionName><ogc:FunctionName nArgs="9">in8</ogc:FunctionName><ogc:FunctionName nArgs="10">in9</ogc:FunctionName><ogc:FunctionName nArgs="1">int2bbool</ogc:FunctionName><ogc:FunctionName nArgs="1">int2ddouble</ogc:FunctionName><ogc:FunctionName nArgs="1">interiorPoint</ogc:FunctionName><ogc:FunctionName nArgs="2">interiorRingN</ogc:FunctionName><ogc:FunctionName nArgs="2">intersection</ogc:FunctionName><ogc:FunctionName nArgs="2">intersects</ogc:FunctionName><ogc:FunctionName nArgs="1">isClosed</ogc:FunctionName><ogc:FunctionName nArgs="1">isEmpty</ogc:FunctionName><ogc:FunctionName nArgs="2">isLike</ogc:FunctionName><ogc:FunctionName nArgs="1">isNull</ogc:FunctionName><ogc:FunctionName nArgs="1">isRing</ogc:FunctionName><ogc:FunctionName nArgs="1">isSimple</ogc:FunctionName><ogc:FunctionName nArgs="1">isValid</ogc:FunctionName><ogc:FunctionName nArgs="3">isWithinDistance</ogc:FunctionName><ogc:FunctionName nArgs="1">length</ogc:FunctionName><ogc:FunctionName nArgs="2">lessEqualThan</ogc:FunctionName><ogc:FunctionName nArgs="2">lessThan</ogc:FunctionName><ogc:FunctionName nArgs="1">log</ogc:FunctionName><ogc:FunctionName nArgs="2">max</ogc:FunctionName><ogc:FunctionName nArgs="2">max_2</ogc:FunctionName><ogc:FunctionName nArgs="2">max_3</ogc:FunctionName><ogc:FunctionName nArgs="2">max_4</ogc:FunctionName><ogc:FunctionName nArgs="2">min</ogc:FunctionName><ogc:FunctionName nArgs="2">min_2</ogc:FunctionName><ogc:FunctionName nArgs="2">min_3</ogc:FunctionName><ogc:FunctionName nArgs="2">min_4</ogc:FunctionName><ogc:FunctionName nArgs="1">not</ogc:FunctionName><ogc:FunctionName nArgs="2">notEqualTo</ogc:FunctionName><ogc:FunctionName nArgs="1">numGeometries</ogc:FunctionName><ogc:FunctionName nArgs="1">numInteriorRing</ogc:FunctionName><ogc:FunctionName nArgs="1">numPoints</ogc:FunctionName><ogc:FunctionName nArgs="2">overlaps</ogc:FunctionName><ogc:FunctionName nArgs="1">parseBoolean</ogc:FunctionName><ogc:FunctionName nArgs="1">parseDouble</ogc:FunctionName><ogc:FunctionName nArgs="1">parseInt</ogc:FunctionName><ogc:FunctionName nArgs="0">pi</ogc:FunctionName><ogc:FunctionName nArgs="2">pointN</ogc:FunctionName><ogc:FunctionName nArgs="2">pow</ogc:FunctionName><ogc:FunctionName nArgs="1">PropertyExists</ogc:FunctionName><ogc:FunctionName nArgs="2">Quantile</ogc:FunctionName><ogc:FunctionName nArgs="0">random</ogc:FunctionName><ogc:FunctionName nArgs="2">relate</ogc:FunctionName><ogc:FunctionName nArgs="3">relatePattern</ogc:FunctionName><ogc:FunctionName nArgs="1">rint</ogc:FunctionName><ogc:FunctionName nArgs="1">round</ogc:FunctionName><ogc:FunctionName nArgs="1">round_2</ogc:FunctionName><ogc:FunctionName nArgs="1">roundDouble</ogc:FunctionName><ogc:FunctionName nArgs="1">sin</ogc:FunctionName><ogc:FunctionName nArgs="1">sqrt</ogc:FunctionName><ogc:FunctionName nArgs="2">StandardDeviation</ogc:FunctionName><ogc:FunctionName nArgs="1">startPoint</ogc:FunctionName><ogc:FunctionName nArgs="2">strConcat</ogc:FunctionName><ogc:FunctionName nArgs="2">strEndsWith</ogc:FunctionName><ogc:FunctionName nArgs="2">strEqualsIgnoreCase</ogc:FunctionName><ogc:FunctionName nArgs="2">strIndexOf</ogc:FunctionName><ogc:FunctionName nArgs="2">strLastIndexOf</ogc:FunctionName><ogc:FunctionName nArgs="1">strLength</ogc:FunctionName><ogc:FunctionName nArgs="2">strMatches</ogc:FunctionName><ogc:FunctionName nArgs="4">strReplace</ogc:FunctionName><ogc:FunctionName nArgs="2">strStartsWith</ogc:FunctionName><ogc:FunctionName nArgs="3">strSubstring</ogc:FunctionName><ogc:FunctionName nArgs="2">strSubstringStart</ogc:FunctionName><ogc:FunctionName nArgs="1">strToLowerCase</ogc:FunctionName><ogc:FunctionName nArgs="1">strToUpperCase</ogc:FunctionName><ogc:FunctionName nArgs="1">strTrim</ogc:FunctionName><ogc:FunctionName nArgs="2">symDifference</ogc:FunctionName><ogc:FunctionName nArgs="1">tan</ogc:FunctionName><ogc:FunctionName nArgs="1">toDegrees</ogc:FunctionName><ogc:FunctionName nArgs="1">toRadians</ogc:FunctionName><ogc:FunctionName nArgs="2">touches</ogc:FunctionName><ogc:FunctionName nArgs="1">toWKT</ogc:FunctionName><ogc:FunctionName nArgs="2">union</ogc:FunctionName><ogc:FunctionName nArgs="2">UniqueInterval</ogc:FunctionName><ogc:FunctionName nArgs="2">within</ogc:FunctionName></ogc:FunctionNames></ogc:Functions></ogc:ArithmeticOperators></ogc:Scalar_Capabilities><ogc:Id_Capabilities><ogc:FID/><ogc:EID/></ogc:Id_Capabilities></ogc:Filter_Capabilities></wfs:WFS_Capabilities>';
+ var res = parser.read(text);
+
+ t.ok(!res.error, "Parsing XML generated no errors");
+ t.eq(res.operationsMetadata["GetFeature"].dcp.http.get[0].url, "http://localhost:80/geoserver/wfs?", "GetFeature GET endpoint correctly parsed");
+ t.eq(res.operationsMetadata["GetFeature"].dcp.http.post[0].url, "http://localhost:80/geoserver/wfs?", "GetFeature POST endpoint correctly parsed");
+ var ft = res.featureTypeList.featureTypes;
+ t.eq(ft.length, 14, "number of feature types correct");
+ t.eq(ft[0]["abstract"], "Manhattan landmarks, identifies water, lakes, parks, interesting buildilngs", "abstract of first feature type correct");
+ t.eq(ft[0]["title"], "Manhattan (NY) landmarks", "title of first feature type correct");
+ t.eq(ft[0]["name"], "poly_landmarks", "name of first feature type correct");
+ t.eq(ft[0]["featureNS"], "http://www.census.gov", "ns of first feature type correct");
+ t.eq(ft[0]["srs"], "urn:x-ogc:def:crs:EPSG:4326", "srs of first feature type correct");
+
+ // GeoServer, v1.0.0
+ text = '<?xml version="1.0" encoding="UTF-8"?><WFS_Capabilities version="1.0.0" xmlns="http://www.opengis.net/wfs" xmlns:it.geosolutions="http://www.geo-solutions.it" xmlns:cite="http://www.opengeospatial.net/cite" xmlns:tiger="http://www.census.gov" xmlns:sde="http://geoserver.sf.net" xmlns:topp="http://www.openplans.org/topp" xmlns:sf="http://www.openplans.org/spearfish" xmlns:nurc="http://www.nurc.nato.int" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs http://localhost:80/geoserver/schemas/wfs/1.0.0/WFS-capabilities.xsd"><Service><Name>WFS</Name><Title>GeoServer Web Feature Service</Title><Abstract>This is the reference implementation of WFS 1.0.0 and WFS 1.1.0, supports all WFS operations including Transaction.</Abstract><Keywords>WFS, WMS, GEOSERVER</Keywords><OnlineResource>http://localhost:80/geoserver/wfs</OnlineResource><Fees>NONE</Fees><AccessConstraints>NONE</AccessConstraints></Service><Capability><Request><GetCapabilities><DCPType><HTTP><Get onlineResource="http://localhost:80/geoserver/wfs?request=GetCapabilities"/></HTTP></DCPType><DCPType><HTTP><Post onlineResource="http://localhost:80/geoserver/wfs?"/></HTTP></DCPType></GetCapabilities><DescribeFeatureType><SchemaDescriptionLanguage><XMLSCHEMA/></SchemaDescriptionLanguage><DCPType><HTTP><Get onlineResource="http://localhost:80/geoserver/wfs?request=DescribeFeatureType"/></HTTP></DCPType><DCPType><HTTP><Post onlineResource="http://localhost:80/geoserver/wfs?"/></HTTP></DCPType></DescribeFeatureType><GetFeature><ResultFormat><GML2/><SHAPE-ZIP/><GEOJSON/><csv/><GML3/></ResultFormat><DCPType><HTTP><Get onlineResource="http://localhost:80/geoserver/wfs?request=GetFeature"/></HTTP></DCPType><DCPType><HTTP><Post onlineResource="http://localhost:80/geoserver/wfs?"/></HTTP></DCPType></GetFeature><Transaction><DCPType><HTTP><Get onlineResource="http://localhost:80/geoserver/wfs?request=Transaction"/></HTTP></DCPType><DCPType><HTTP><Post onlineResource="http://localhost:80/geoserver/wfs?"/></HTTP></DCPType></Transaction><LockFeature><DCPType><HTTP><Get onlineResource="http://localhost:80/geoserver/wfs?request=LockFeature"/></HTTP></DCPType><DCPType><HTTP><Post onlineResource="http://localhost:80/geoserver/wfs?"/></HTTP></DCPType></LockFeature><GetFeatureWithLock><ResultFormat><GML2/></ResultFormat><DCPType><HTTP><Get onlineResource="http://localhost:80/geoserver/wfs?request=GetFeatureWithLock"/></HTTP></DCPType><DCPType><HTTP><Post onlineResource="http://localhost:80/geoserver/wfs?"/></HTTP></DCPType></GetFeatureWithLock></Request></Capability><FeatureTypeList><Operations><Query/><Insert/><Update/><Delete/><Lock/></Operations><FeatureType><Name>tiger:poly_landmarks</Name><Title>Manhattan (NY) landmarks</Title><Abstract>Manhattan landmarks, identifies water, lakes, parks, interesting buildilngs</Abstract><Keywords>DS_poly_landmarks, poly_landmarks, landmarks, manhattan</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="-74.047185" miny="40.679648" maxx="-73.90782" maxy="40.882078"/></FeatureType><FeatureType><Name>tiger:poi</Name><Title>Manhattan (NY) points of interest</Title><Abstract>Points of interest in New York, New York (on Manhattan). One of the attributes contains the name of a file with a picture of the point of interest.</Abstract><Keywords>poi, DS_poi, points_of_interest, Manhattan</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="-74.0118315772888" miny="40.70754683896324" maxx="-74.00857344353275" maxy="40.711945649065406"/></FeatureType><FeatureType><Name>tiger:tiger_roads</Name><Title>Manhattan (NY) roads</Title><Abstract>Highly simplified road layout of Manhattan in New York..</Abstract><Keywords>DS_tiger_roads, tiger_roads, roads</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="-74.02722" miny="40.684221" maxx="-73.907005" maxy="40.878178"/></FeatureType><FeatureType><Name>sf:archsites</Name><Title>Spearfish archeological sites</Title><Abstract>Sample data from GRASS, archeological sites location, Spearfish, South Dakota, USA</Abstract><Keywords>archsites, sfArchsites, spearfish, archeology</Keywords><SRS>EPSG:26713</SRS><LatLongBoundingBox minx="-103.8725637911543" miny="44.37740330855979" maxx="-103.63794182141925" maxy="44.48804280772808"/></FeatureType><FeatureType><Name>sf:bugsites</Name><Title>Spearfish bug locations</Title><Abstract>Sample data from GRASS, bug sites location, Spearfish, South Dakota, USA</Abstract><Keywords>sfBugsites, bugsites, insects, spearfish, tiger_beetles</Keywords><SRS>EPSG:26713</SRS><LatLongBoundingBox minx="-103.86796131703647" miny="44.373938816704396" maxx="-103.63773523234195" maxy="44.43418821380063"/></FeatureType><FeatureType><Name>sf:restricted</Name><Title>Spearfish restricted areas</Title><Abstract>Sample data from GRASS, restricted areas, Spearfish, South Dakota, USA</Abstract><Keywords>restricted, sfRestricted, spearfish, areas</Keywords><SRS>EPSG:26713</SRS><LatLongBoundingBox minx="-103.85057172920756" miny="44.39436387625042" maxx="-103.74741494853805" maxy="44.48215752041131"/></FeatureType><FeatureType><Name>sf:roads</Name><Title>Spearfish roads</Title><Abstract>Sample data from GRASS, road layout, Spearfish, South Dakota, USA</Abstract><Keywords>sfRoads, roads, spearfish</Keywords><SRS>EPSG:26713</SRS><LatLongBoundingBox minx="-103.87741691493184" miny="44.37087275281798" maxx="-103.62231404880659" maxy="44.50015918338962"/></FeatureType><FeatureType><Name>sf:streams</Name><Title>Spearfish streams</Title><Abstract>Sample data from GRASS, streams, Spearfish, South Dakota, USA</Abstract><Keywords>sfStreams, streams, spearfish</Keywords><SRS>EPSG:26713</SRS><LatLongBoundingBox minx="-103.87789019829768" miny="44.372335260095554" maxx="-103.62287788915457" maxy="44.502218486214815"/></FeatureType><FeatureType><Name>topp:tasmania_cities</Name><Title>Tasmania cities</Title><Abstract>Cities in Tasmania (actually, just the capital)</Abstract><Keywords>cities, Tasmania</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="145.19754" miny="-43.423512" maxx="148.27298000000002" maxy="-40.852802"/></FeatureType><FeatureType><Name>topp:tasmania_roads</Name><Title>Tasmania roads</Title><Abstract>Main Tasmania roads</Abstract><Keywords>Roads, Tasmania</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="145.19754" miny="-43.423512" maxx="148.27298000000002" maxy="-40.852802"/></FeatureType><FeatureType><Name>topp:tasmania_state_boundaries</Name><Title>Tasmania state boundaries</Title><Abstract>Tasmania state boundaries</Abstract><Keywords>tasmania_state_boundaries, Tasmania, boundaries</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="143.83482400000003" miny="-43.648056" maxx="148.47914100000003" maxy="-39.573891"/></FeatureType><FeatureType><Name>topp:tasmania_water_bodies</Name><Title>Tasmania water bodies</Title><Abstract>Tasmania water bodies</Abstract><Keywords>Lakes, Bodies, Australia, Water, Tasmania</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="145.97161899999998" miny="-43.031944" maxx="147.219696" maxy="-41.775558"/></FeatureType><FeatureType><Name>topp:states</Name><Title>USA Population</Title><Abstract>This is some census data on the states.</Abstract><Keywords>census, united, boundaries, state, states</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="-124.731422" miny="24.955967" maxx="-66.969849" maxy="49.371735"/></FeatureType><FeatureType><Name>tiger:giant_polygon</Name><Title>World rectangle</Title><Abstract>A simple rectangular polygon covering most of the world, it\'s only used for the purpose of providing a background (WMS bgcolor could be used instead)</Abstract><Keywords>DS_giant_polygon, giant_polygon</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="-180.0" miny="-90.0" maxx="180.0" maxy="90.0"/></FeatureType></FeatureTypeList><ogc:Filter_Capabilities><ogc:Spatial_Capabilities><ogc:Spatial_Operators><ogc:Disjoint/><ogc:Equals/><ogc:DWithin/><ogc:Beyond/><ogc:Intersect/><ogc:Touches/><ogc:Crosses/><ogc:Within/><ogc:Contains/><ogc:Overlaps/><ogc:BBOX/></ogc:Spatial_Operators></ogc:Spatial_Capabilities><ogc:Scalar_Capabilities><ogc:Logical_Operators/><ogc:Comparison_Operators><ogc:Simple_Comparisons/><ogc:Between/><ogc:Like/><ogc:NullCheck/></ogc:Comparison_Operators><ogc:Arithmetic_Operators><ogc:Simple_Arithmetic/><ogc:Functions><ogc:Function_Names><ogc:Function_Name nArgs="1">abs</ogc:Function_Name><ogc:Function_Name nArgs="1">abs_2</ogc:Function_Name><ogc:Function_Name nArgs="1">abs_3</ogc:Function_Name><ogc:Function_Name nArgs="1">abs_4</ogc:Function_Name><ogc:Function_Name nArgs="1">acos</ogc:Function_Name><ogc:Function_Name nArgs="1">Area</ogc:Function_Name><ogc:Function_Name nArgs="1">asin</ogc:Function_Name><ogc:Function_Name nArgs="1">atan</ogc:Function_Name><ogc:Function_Name nArgs="2">atan2</ogc:Function_Name><ogc:Function_Name nArgs="3">between</ogc:Function_Name><ogc:Function_Name nArgs="1">boundary</ogc:Function_Name><ogc:Function_Name nArgs="1">boundaryDimension</ogc:Function_Name><ogc:Function_Name nArgs="2">buffer</ogc:Function_Name><ogc:Function_Name nArgs="3">bufferWithSegments</ogc:Function_Name><ogc:Function_Name nArgs="1">ceil</ogc:Function_Name><ogc:Function_Name nArgs="1">centroid</ogc:Function_Name><ogc:Function_Name nArgs="2">classify</ogc:Function_Name><ogc:Function_Name nArgs="1">Collection_Average</ogc:Function_Name><ogc:Function_Name nArgs="1">Collection_Bounds</ogc:Function_Name><ogc:Function_Name nArgs="1">Collection_Count</ogc:Function_Name><ogc:Function_Name nArgs="1">Collection_Max</ogc:Function_Name><ogc:Function_Name nArgs="1">Collection_Median</ogc:Function_Name><ogc:Function_Name nArgs="1">Collection_Min</ogc:Function_Name><ogc:Function_Name nArgs="1">Collection_Sum</ogc:Function_Name><ogc:Function_Name nArgs="1">Collection_Unique</ogc:Function_Name><ogc:Function_Name nArgs="2">Concatenate</ogc:Function_Name><ogc:Function_Name nArgs="2">contains</ogc:Function_Name><ogc:Function_Name nArgs="1">convexHull</ogc:Function_Name><ogc:Function_Name nArgs="1">cos</ogc:Function_Name><ogc:Function_Name nArgs="2">crosses</ogc:Function_Name><ogc:Function_Name nArgs="2">dateFormat</ogc:Function_Name><ogc:Function_Name nArgs="2">dateParse</ogc:Function_Name><ogc:Function_Name nArgs="2">difference</ogc:Function_Name><ogc:Function_Name nArgs="1">dimension</ogc:Function_Name><ogc:Function_Name nArgs="2">disjoint</ogc:Function_Name><ogc:Function_Name nArgs="2">distance</ogc:Function_Name><ogc:Function_Name nArgs="1">double2bool</ogc:Function_Name><ogc:Function_Name nArgs="1">endPoint</ogc:Function_Name><ogc:Function_Name nArgs="1">envelope</ogc:Function_Name><ogc:Function_Name nArgs="2">EqualInterval</ogc:Function_Name><ogc:Function_Name nArgs="2">equalsExact</ogc:Function_Name><ogc:Function_Name nArgs="3">equalsExactTolerance</ogc:Function_Name><ogc:Function_Name nArgs="2">equalTo</ogc:Function_Name><ogc:Function_Name nArgs="1">exp</ogc:Function_Name><ogc:Function_Name nArgs="1">exteriorRing</ogc:Function_Name><ogc:Function_Name nArgs="1">floor</ogc:Function_Name><ogc:Function_Name nArgs="1">geometryType</ogc:Function_Name><ogc:Function_Name nArgs="1">geomFromWKT</ogc:Function_Name><ogc:Function_Name nArgs="1">geomLength</ogc:Function_Name><ogc:Function_Name nArgs="2">getGeometryN</ogc:Function_Name><ogc:Function_Name nArgs="1">getX</ogc:Function_Name><ogc:Function_Name nArgs="1">getY</ogc:Function_Name><ogc:Function_Name nArgs="1">getZ</ogc:Function_Name><ogc:Function_Name nArgs="2">greaterEqualThan</ogc:Function_Name><ogc:Function_Name nArgs="2">greaterThan</ogc:Function_Name><ogc:Function_Name nArgs="0">id</ogc:Function_Name><ogc:Function_Name nArgs="2">IEEEremainder</ogc:Function_Name><ogc:Function_Name nArgs="3">if_then_else</ogc:Function_Name><ogc:Function_Name nArgs="11">in10</ogc:Function_Name><ogc:Function_Name nArgs="3">in2</ogc:Function_Name><ogc:Function_Name nArgs="4">in3</ogc:Function_Name><ogc:Function_Name nArgs="5">in4</ogc:Function_Name><ogc:Function_Name nArgs="6">in5</ogc:Function_Name><ogc:Function_Name nArgs="7">in6</ogc:Function_Name><ogc:Function_Name nArgs="8">in7</ogc:Function_Name><ogc:Function_Name nArgs="9">in8</ogc:Function_Name><ogc:Function_Name nArgs="10">in9</ogc:Function_Name><ogc:Function_Name nArgs="1">int2bbool</ogc:Function_Name><ogc:Function_Name nArgs="1">int2ddouble</ogc:Function_Name><ogc:Function_Name nArgs="1">interiorPoint</ogc:Function_Name><ogc:Function_Name nArgs="2">interiorRingN</ogc:Function_Name><ogc:Function_Name nArgs="2">intersection</ogc:Function_Name><ogc:Function_Name nArgs="2">intersects</ogc:Function_Name><ogc:Function_Name nArgs="1">isClosed</ogc:Function_Name><ogc:Function_Name nArgs="1">isEmpty</ogc:Function_Name><ogc:Function_Name nArgs="2">isLike</ogc:Function_Name><ogc:Function_Name nArgs="1">isNull</ogc:Function_Name><ogc:Function_Name nArgs="1">isRing</ogc:Function_Name><ogc:Function_Name nArgs="1">isSimple</ogc:Function_Name><ogc:Function_Name nArgs="1">isValid</ogc:Function_Name><ogc:Function_Name nArgs="3">isWithinDistance</ogc:Function_Name><ogc:Function_Name nArgs="1">length</ogc:Function_Name><ogc:Function_Name nArgs="2">lessEqualThan</ogc:Function_Name><ogc:Function_Name nArgs="2">lessThan</ogc:Function_Name><ogc:Function_Name nArgs="1">log</ogc:Function_Name><ogc:Function_Name nArgs="2">max</ogc:Function_Name><ogc:Function_Name nArgs="2">max_2</ogc:Function_Name><ogc:Function_Name nArgs="2">max_3</ogc:Function_Name><ogc:Function_Name nArgs="2">max_4</ogc:Function_Name><ogc:Function_Name nArgs="2">min</ogc:Function_Name><ogc:Function_Name nArgs="2">min_2</ogc:Function_Name><ogc:Function_Name nArgs="2">min_3</ogc:Function_Name><ogc:Function_Name nArgs="2">min_4</ogc:Function_Name><ogc:Function_Name nArgs="1">not</ogc:Function_Name><ogc:Function_Name nArgs="2">notEqualTo</ogc:Function_Name><ogc:Function_Name nArgs="1">numGeometries</ogc:Function_Name><ogc:Function_Name nArgs="1">numInteriorRing</ogc:Function_Name><ogc:Function_Name nArgs="1">numPoints</ogc:Function_Name><ogc:Function_Name nArgs="2">overlaps</ogc:Function_Name><ogc:Function_Name nArgs="1">parseBoolean</ogc:Function_Name><ogc:Function_Name nArgs="1">parseDouble</ogc:Function_Name><ogc:Function_Name nArgs="1">parseInt</ogc:Function_Name><ogc:Function_Name nArgs="0">pi</ogc:Function_Name><ogc:Function_Name nArgs="2">pointN</ogc:Function_Name><ogc:Function_Name nArgs="2">pow</ogc:Function_Name><ogc:Function_Name nArgs="1">PropertyExists</ogc:Function_Name><ogc:Function_Name nArgs="2">Quantile</ogc:Function_Name><ogc:Function_Name nArgs="0">random</ogc:Function_Name><ogc:Function_Name nArgs="2">relate</ogc:Function_Name><ogc:Function_Name nArgs="3">relatePattern</ogc:Function_Name><ogc:Function_Name nArgs="1">rint</ogc:Function_Name><ogc:Function_Name nArgs="1">round</ogc:Function_Name><ogc:Function_Name nArgs="1">round_2</ogc:Function_Name><ogc:Function_Name nArgs="1">roundDouble</ogc:Function_Name><ogc:Function_Name nArgs="1">sin</ogc:Function_Name><ogc:Function_Name nArgs="1">sqrt</ogc:Function_Name><ogc:Function_Name nArgs="2">StandardDeviation</ogc:Function_Name><ogc:Function_Name nArgs="1">startPoint</ogc:Function_Name><ogc:Function_Name nArgs="2">strConcat</ogc:Function_Name><ogc:Function_Name nArgs="2">strEndsWith</ogc:Function_Name><ogc:Function_Name nArgs="2">strEqualsIgnoreCase</ogc:Function_Name><ogc:Function_Name nArgs="2">strIndexOf</ogc:Function_Name><ogc:Function_Name nArgs="2">strLastIndexOf</ogc:Function_Name><ogc:Function_Name nArgs="1">strLength</ogc:Function_Name><ogc:Function_Name nArgs="2">strMatches</ogc:Function_Name><ogc:Function_Name nArgs="4">strReplace</ogc:Function_Name><ogc:Function_Name nArgs="2">strStartsWith</ogc:Function_Name><ogc:Function_Name nArgs="3">strSubstring</ogc:Function_Name><ogc:Function_Name nArgs="2">strSubstringStart</ogc:Function_Name><ogc:Function_Name nArgs="1">strToLowerCase</ogc:Function_Name><ogc:Function_Name nArgs="1">strToUpperCase</ogc:Function_Name><ogc:Function_Name nArgs="1">strTrim</ogc:Function_Name><ogc:Function_Name nArgs="2">symDifference</ogc:Function_Name><ogc:Function_Name nArgs="1">tan</ogc:Function_Name><ogc:Function_Name nArgs="1">toDegrees</ogc:Function_Name><ogc:Function_Name nArgs="1">toRadians</ogc:Function_Name><ogc:Function_Name nArgs="2">touches</ogc:Function_Name><ogc:Function_Name nArgs="1">toWKT</ogc:Function_Name><ogc:Function_Name nArgs="2">union</ogc:Function_Name><ogc:Function_Name nArgs="2">UniqueInterval</ogc:Function_Name><ogc:Function_Name nArgs="2">within</ogc:Function_Name></ogc:Function_Names></ogc:Functions></ogc:Arithmetic_Operators></ogc:Scalar_Capabilities></ogc:Filter_Capabilities></WFS_Capabilities>';
+ res = parser.read(text);
+
+ t.ok(!res.error, "Parsing XML generated no errors");
+ ft = res.featureTypeList.featureTypes;
+ t.eq(ft.length, 14, "number of feature types correct");
+ t.eq(ft[0]["abstract"], "Manhattan landmarks, identifies water, lakes, parks, interesting buildilngs", "abstract of first feature type correct");
+ t.eq(ft[0]["title"], "Manhattan (NY) landmarks", "title of first feature type correct");
+ t.eq(ft[0]["name"], "poly_landmarks", "name of first feature type correct");
+ t.eq(ft[0]["featureNS"], "http://www.census.gov", "ns of first feature type correct");
+ t.eq(ft[0]["srs"], "EPSG:4326", "srs of first feature type correct");
+
+ var service = res.service;
+ t.eq(service.name, 'WFS', "service name correct");
+ t.eq(service.title, 'GeoServer Web Feature Service', "service title correct");
+ t.eq(service.abstract, 'This is the reference implementation of WFS 1.0.0 and WFS 1.1.0, supports all WFS operations including Transaction.', "service title correct");
+ t.eq(service.keywords[0], 'WFS', "service keyword [0] correct");
+ t.eq(service.keywords[2], 'GEOSERVER', "service keyword [2] correct");
+ t.eq(service.onlineResource, 'http://localhost:80/geoserver/wfs', "service onlineresource correct");
+ t.ok(typeof service.fees == 'undefined', "service fees correct");
+ t.ok(typeof service.accessConstraints == 'undefined', "service accessconstraints correct");
+
+ t.eq(res.capability.request.getfeature.href.post, "http://localhost:80/geoserver/wfs?", "getfeature request post href correct");
+ t.eq(res.capability.request.getfeature.href.get, "http://localhost:80/geoserver/wfs?request=GetFeature", "getfeature request get href correct");
+
+ t.eq(res.capability.request.getfeature.formats[0], "GML2", "getfeature response format [0] correct");
+ t.eq(res.capability.request.getfeature.formats[4], "GML3", "getfeature response format [4] correct");
+
+ // UMN Mapserer, v1.0.0
+ text =
+ '<?xml version="1.0" encoding="ISO-8859-1" ?>' +
+ '<WFS_Capabilities' +
+ ' version="1.0.0"' +
+ ' updateSequence="0"' +
+ ' xmlns="http://www.opengis.net/wfs"' +
+ ' xmlns:ogc="http://www.opengis.net/ogc"' +
+ ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
+ ' xsi:schemaLocation="http://www.opengis.net/wfs http://ogc.dmsolutions.ca/wfs/1.0.0/WFS-capabilities.xsd">' +
+ '' +
+ '<!-- MapServer version 4.0 (development) OUTPUT=GIF OUTPUT=PNG OUTPUT=JPEG OUTPUT=WBMP OUTPUT=PDF OUTPUT=SWF SUPPORTS=PROJ SUPPORTS=FREETYPE SUPPORTS=WMS_SERVER SUPPORTS=WMS_CLIENT SUPPORTS=WFS_SERVER SUPPORTS=WFS_CLIENT INPUT=POSTGIS INPUT=OGR INPUT=GDAL INPUT=SHAPEFILE -->' +
+ '' +
+ '<Service>' +
+ ' <Name>MapServer WFS</Name>' +
+ ' <Title>GMap WMS Demo Server</Title>' +
+ ' <OnlineResource>http://127.0.0.1/cgi-bin/mapserv_40?map=/msroot/apache/htdocs/gmap/htdocs/gmap75_wfs.map&amp;service=WFS&amp;</OnlineResource>' +
+ '</Service>' +
+ '' +
+ '<Capability>' +
+ ' <Request>' +
+ ' <GetCapabilities>' +
+ ' <DCPType>' +
+ ' <HTTP>' +
+ ' <Get onlineResource="http://127.0.0.1/cgi-bin/mapserv_40?map=/msroot/apache/htdocs/gmap/htdocs/gmap75_wfs.map&amp;service=WFS&amp;" />' +
+ ' </HTTP>' +
+ ' </DCPType>' +
+ ' </GetCapabilities>' +
+ '' +
+ ' <DescribeFeatureType>' +
+ ' <SchemaDescriptionLanguage>' +
+ ' <XMLSCHEMA/>' +
+ ' </SchemaDescriptionLanguage>' +
+ ' <DCPType>' +
+ ' <HTTP>' +
+ ' <Get onlineResource="http://127.0.0.1/cgi-bin/mapserv_40?map=/msroot/apache/htdocs/gmap/htdocs/gmap75_wfs.map&amp;service=WFS&amp;" />' +
+ ' </HTTP>' +
+ ' </DCPType>' +
+ '' +
+ ' </DescribeFeatureType>' +
+ ' <GetFeature>' +
+ ' <ResultFormat>' +
+ ' <GML2/>' +
+ ' </ResultFormat>' +
+ ' <DCPType>' +
+ ' <HTTP>' +
+ ' <Get onlineResource="http://127.0.0.1/cgi-bin/mapserv_40?map=/msroot/apache/htdocs/gmap/htdocs/gmap75_wfs.map&amp;service=WFS&amp;" />' +
+ ' </HTTP>' +
+ '' +
+ ' </DCPType>' +
+ ' </GetFeature>' +
+ ' </Request>' +
+ '</Capability>' +
+ '' +
+ '<FeatureTypeList>' +
+ ' <Operations>' +
+ ' <Query/>' +
+ ' </Operations>' +
+ ' <FeatureType>' +
+ '' +
+ ' <Name>park</Name>' +
+ ' <Title>Parks</Title>' +
+ ' <SRS>EPSG:42304</SRS>' +
+ ' <LatLongBoundingBox minx="-173.433" miny="41.4271" maxx="-13.3643" maxy="83.7466" />' +
+ ' </FeatureType>' +
+ ' <FeatureType>' +
+ ' <Name>popplace</Name>' +
+ '' +
+ ' <Title>Cities</Title>' +
+ ' <SRS>EPSG:42304</SRS>' +
+ ' <LatLongBoundingBox minx="-172.301" miny="36.3541" maxx="-12.9698" maxy="83.4832" />' +
+ ' </FeatureType>' +
+ '</FeatureTypeList>' +
+ '' +
+ '<ogc:Filter_Capabilities>' +
+ ' <ogc:Spatial_Capabilities>' +
+ ' <ogc:Spatial_Operators>' +
+ '' +
+ ' <ogc:BBOX/>' +
+ ' </ogc:Spatial_Operators>' +
+ ' </ogc:Spatial_Capabilities>' +
+ ' <!-- Provide some ScalarCapabilties just for the XML to validate against the schema even if we don\'t support any. (Yes this is stupid!) -->' +
+ ' <ogc:Scalar_Capabilities>' +
+ ' <ogc:Logical_Operators />' +
+ ' </ogc:Scalar_Capabilities>' +
+ '</ogc:Filter_Capabilities>' +
+ '' +
+ '</WFS_Capabilities>';
+ res = parser.read(text);
+ var ft = res.featureTypeList.featureTypes;
+ t.eq(ft.length, 2, "number of feature types correct");
+ t.eq(ft[0]["title"], "Parks", "title of first feature type correct");
+ t.eq(ft[0]["name"], "park", "name of first feature type correct");
+ t.eq(ft[0]["srs"], "EPSG:42304", "srs of first feature type correct");
+
+ var service = res.service;
+ t.eq(service.name, 'MapServer WFS', "service name correct");
+ t.eq(service.title, 'GMap WMS Demo Server', "service title correct");
+ t.eq(service.onlineResource, 'http://127.0.0.1/cgi-bin/mapserv_40?map=/msroot/apache/htdocs/gmap/htdocs/gmap75_wfs.map&service=WFS&', "service onlineresource correct");
+ t.eq(res.capability.request.getfeature.href.get, "http://127.0.0.1/cgi-bin/mapserv_40?map=/msroot/apache/htdocs/gmap/htdocs/gmap75_wfs.map&service=WFS&", "getfeature request get href correct");
+ t.eq(res.capability.request.getfeature.formats[0], "GML2", "getfeature response format [0] correct");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WFSDescribeFeatureType.html b/misc/openlayers/tests/Format/WFSDescribeFeatureType.html
new file mode 100644
index 0000000..77f348d
--- /dev/null
+++ b/misc/openlayers/tests/Format/WFSDescribeFeatureType.html
@@ -0,0 +1,436 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read_WFSDescribeFeatureType(t) {
+ t.plan(39);
+
+ var parser = new OpenLayers.Format.WFSDescribeFeatureType();
+
+ // single typeName from UMN Mapserver
+ var text =
+ '<?xml version="1.0" encoding="ISO-8859-1" ?>' +
+ '<schema' +
+ ' targetNamespace="http://mapserver.gis.umn.edu/mapserver" ' +
+ ' xmlns:rws="http://mapserver.gis.umn.edu/mapserver" ' +
+ ' xmlns:ogc="http://www.opengis.net/ogc"' +
+ ' xmlns:xsd="http://www.w3.org/2001/XMLSchema"' +
+ ' xmlns="http://www.w3.org/2001/XMLSchema"' +
+ ' xmlns:gml="http://www.opengis.net/gml"' +
+ ' elementFormDefault="qualified" version="0.1" >' +
+ ' <import namespace="http://www.opengis.net/gml"' +
+ ' schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd" />' +
+ ' <element name="AAA64" ' +
+ ' type="rws:AAA64Type" ' +
+ ' substitutionGroup="gml:_Feature" />' +
+ ' <complexType name="AAA64Type">' +
+ ' <complexContent>' +
+ ' <extension base="gml:AbstractFeatureType">' +
+ ' <sequence>' +
+ ' <element name="geometry" type="gml:MultiLineStringPropertyType" minOccurs="0" maxOccurs="1"/>' +
+ ' <element name="OBJECTID" type="string"/>' +
+ ' <element name="ROUTE" type="string"/>' +
+ ' <element name="ROUTE_CH" type="string"/>' +
+ ' <element name="COUNT" type="string"/>' +
+ ' <element name="BEHEERDER" type="string"/>' +
+ ' <element name="LENGTH" type="string"/>' +
+ ' <element name="SHAPE" type="string"/>' +
+ ' <element name="SE_ANNO_CAD_DATA" type="string"/>' +
+ ' </sequence>' +
+ ' </extension>' +
+ ' </complexContent>' +
+ ' </complexType>' +
+ '</schema>';
+ var res = parser.read(text);
+
+ t.eq(res.featureTypes.length, 1,
+ "There is only 1 typename, so length should be 1");
+
+ t.eq(res.featureTypes[0].properties[0].type, 'gml:MultiLineStringPropertyType',
+ "The first attribute is of type multi line string");
+
+ t.eq(res.featureTypes[0].properties[2].name, 'ROUTE',
+ "The third attribute is named ROUTE");
+
+ t.eq(res.featureTypes[0].properties[2].type, 'string',
+ "The third attribute is of type string");
+
+ // three typeNames in one response from UMN Mapserver
+ text =
+ '<?xml version="1.0" encoding="ISO-8859-1" ?>' +
+ '<schema' +
+ ' targetNamespace="http://mapserver.gis.umn.edu/mapserver" ' +
+ ' xmlns:rws="http://mapserver.gis.umn.edu/mapserver" ' +
+ ' xmlns:ogc="http://www.opengis.net/ogc"' +
+ ' xmlns:xsd="http://www.w3.org/2001/XMLSchema"' +
+ ' xmlns="http://www.w3.org/2001/XMLSchema"' +
+ ' xmlns:gml="http://www.opengis.net/gml"' +
+ ' elementFormDefault="qualified" version="0.1" >' +
+ ' <import namespace="http://www.opengis.net/gml"' +
+ ' schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd" />' +
+ ' <element name="KGNAT.VKUNSTWERK" ' +
+ ' type="rws:KGNAT.VKUNSTWERKType" ' +
+ ' substitutionGroup="gml:_Feature" />' +
+ ' <complexType name="KGNAT.VKUNSTWERKType">' +
+ ' <complexContent>' +
+ ' <extension base="gml:AbstractFeatureType">' +
+ ' <sequence>' +
+ ' <element name="geometry" type="gml:MultiPolygonPropertyType" minOccurs="0" maxOccurs="1"/>' +
+ ' <element name="OBJECTID" type="string"/>' +
+ ' <element name="OBJECTSUBCATEGORIE" type="string"/>' +
+ ' <element name="DIENSTCODE" type="string"/>' +
+ ' <element name="DISTRICTNAAM" type="string"/>' +
+ ' <element name="CODEBPN" type="string"/>' +
+ ' <element name="WSD" type="string"/>' +
+ ' <element name="SUBCAT" type="string"/>' +
+ ' <element name="ZIJDE" type="string"/>' +
+ ' <element name="KM" type="string"/>' +
+ ' <element name="ELEMENTCODE" type="string"/>' +
+ ' <element name="COMPLEXCODE" type="string"/>' +
+ ' <element name="BEHEEROBJECTCODE" type="string"/>' +
+ ' <element name="BEGINDATUM" type="string"/>' +
+ ' <element name="NAAMCONTACTPERSOON" type="string"/>' +
+ ' <element name="KMTOT" type="string"/>' +
+ ' <element name="HOOFDWATERSYSTEEM" type="string"/>' +
+ ' <element name="WATERSYSTEEMNAAM" type="string"/>' +
+ ' <element name="OBJECTNAAM" type="string"/>' +
+ ' <element name="HERKOMST" type="string"/>' +
+ ' <element name="BEHEERSREGIME" type="string"/>' +
+ ' <element name="VERSIE" type="string"/>' +
+ ' <element name="KWALITEITSNIVEAU" type="string"/>' +
+ ' <element name="STICHTINGSJAAR" type="string"/>' +
+ ' <element name="OBJECTTYPE" type="string"/>' +
+ ' <element name="OPMERKING" type="string"/>' +
+ ' <element name="OPPERVLAKTE" type="string"/>' +
+ ' <element name="SE_ANNO_CAD_DATA" type="string"/>' +
+ ' <element name="SHAPE" type="string"/>' +
+ ' </sequence>' +
+ ' </extension>' +
+ ' </complexContent>' +
+ ' </complexType>' +
+ ' <element name="KGNAT.LKUNSTWERK" ' +
+ ' type="rws:KGNAT.LKUNSTWERKType" ' +
+ ' substitutionGroup="gml:_Feature" />' +
+ ' <complexType name="KGNAT.LKUNSTWERKType">' +
+ ' <complexContent>' +
+ ' <extension base="gml:AbstractFeatureType">' +
+ ' <sequence>' +
+ ' <element name="geometry" type="gml:MultiLineStringPropertyType" minOccurs="0" maxOccurs="1"/>' +
+ ' <element name="OBJECTID" type="string"/>' +
+ ' <element name="OBJECTSUBCATEGORIE" type="string"/>' +
+ ' <element name="DIENSTCODE" type="string"/>' +
+ ' <element name="DISTRICTNAAM" type="string"/>' +
+ ' <element name="CODEBPN" type="string"/>' +
+ ' <element name="WSD" type="string"/>' +
+ ' <element name="SUBCAT" type="string"/>' +
+ ' <element name="ZIJDE" type="string"/>' +
+ ' <element name="KM" type="string"/>' +
+ ' <element name="ELEMENTCODE" type="string"/>' +
+ ' <element name="COMPLEXCODE" type="string"/>' +
+ ' <element name="BEHEEROBJECTCODE" type="string"/>' +
+ ' <element name="BEGINDATUM" type="string"/>' +
+ ' <element name="NAAMCONTACTPERSOON" type="string"/>' +
+ ' <element name="KMTOT" type="string"/>' +
+ ' <element name="HOOFDWATERSYSTEEM" type="string"/>' +
+ ' <element name="WATERSYSTEEMNAAM" type="string"/>' +
+ ' <element name="OBJECTNAAM" type="string"/>' +
+ ' <element name="HERKOMST" type="string"/>' +
+ ' <element name="BEHEERSREGIME" type="string"/>' +
+ ' <element name="VERSIE" type="string"/>' +
+ ' <element name="KWALITEITSNIVEAU" type="string"/>' +
+ ' <element name="STICHTINGSJAAR" type="string"/>' +
+ ' <element name="OBJECTTYPE" type="string"/>' +
+ ' <element name="OPMERKING" type="string"/>' +
+ ' <element name="LENGTE" type="string"/>' +
+ ' <element name="SE_ANNO_CAD_DATA" type="string"/>' +
+ ' <element name="SHAPE" type="string"/>' +
+ ' </sequence>' +
+ ' </extension>' +
+ ' </complexContent>' +
+ ' </complexType>' +
+ ' <element name="KGNAT.PKUNSTWERK" ' +
+ ' type="rws:KGNAT.PKUNSTWERKType" ' +
+ ' substitutionGroup="gml:_Feature" />' +
+ ' <complexType name="KGNAT.PKUNSTWERKType">' +
+ ' <complexContent>' +
+ ' <extension base="gml:AbstractFeatureType">' +
+ ' <sequence>' +
+ ' <element name="geometry" type="gml:MultiPointPropertyType" minOccurs="0" maxOccurs="1"/>' +
+ ' <element name="OBJECTID" type="string"/>' +
+ ' <element name="OBJECTSUBCATEGORIE" type="string"/>' +
+ ' <element name="DIENSTCODE" type="string"/>' +
+ ' <element name="DISTRICTNAAM" type="string"/>' +
+ ' <element name="CODEBPN" type="string"/>' +
+ ' <element name="WSD" type="string"/>' +
+ ' <element name="SUBCAT" type="string"/>' +
+ ' <element name="ZIJDE" type="string"/>' +
+ ' <element name="KM" type="string"/>' +
+ ' <element name="ELEMENTCODE" type="string"/>' +
+ ' <element name="COMPLEXCODE" type="string"/>' +
+ ' <element name="BEHEEROBJECTCODE" type="string"/>' +
+ ' <element name="BEGINDATUM" type="string"/>' +
+ ' <element name="NAAMCONTACTPERSOON" type="string"/>' +
+ ' <element name="KMTOT" type="string"/>' +
+ ' <element name="HOOFDWATERSYSTEEM" type="string"/>' +
+ ' <element name="WATERSYSTEEMNAAM" type="string"/>' +
+ ' <element name="OBJECTNAAM" type="string"/>' +
+ ' <element name="HERKOMST" type="string"/>' +
+ ' <element name="BEHEERSREGIME" type="string"/>' +
+ ' <element name="VERSIE" type="string"/>' +
+ ' <element name="KWALITEITSNIVEAU" type="string"/>' +
+ ' <element name="STICHTINGSJAAR" type="string"/>' +
+ ' <element name="OBJECTTYPE" type="string"/>' +
+ ' <element name="OPMERKING" type="string"/>' +
+ ' <element name="X" type="string"/>' +
+ ' <element name="Y" type="string"/>' +
+ ' <element name="SE_ANNO_CAD_DATA" type="string"/>' +
+ ' <element name="SHAPE" type="string"/>' +
+ ' </sequence>' +
+ ' </extension>' +
+ ' </complexContent>' +
+ ' </complexType>' +
+ '</schema>';
+
+ parser = new OpenLayers.Format.WFSDescribeFeatureType();
+ res = parser.read(text);
+
+ t.eq(res.featureTypes.length, 3,
+ "There are 3 typenames, so length should be 3");
+
+ t.eq(res.featureTypes[0].typeName, 'KGNAT.VKUNSTWERK',
+ "There name of the first typename is KGNAT.VKUNSTWERK");
+
+ t.eq(res.featureTypes[2].properties.length, 30,
+ "We expect 30 attributes in the third typename");
+
+ t.eq(res.featureTypes[2].properties[1].name, 'OBJECTID',
+ "The second attribute has name OBJECTID");
+
+ t.eq(res.featureTypes[2].properties[1].type, 'string',
+ "The second attribute has type string");
+
+ t.eq(res.targetNamespace, 'http://mapserver.gis.umn.edu/mapserver',
+ "The targetNamespace should be http://mapserver.gis.umn.edu/mapserver");
+
+ t.eq(res.targetPrefix, 'rws', "the targetPrefix should be rws");
+
+ // response from Ionic WFS, taken from:
+ // http://webservices.ionicsoft.com/ionicweb/wfs/BOSTON_ORA?service=WFS&request=DescribeFeatureType&version=1.0.0&typename=wfs:highways
+ text =
+ "<?xml version='1.0' encoding='utf-8' ?>" +
+ ' <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" xmlns:wfs="http://www.ionicsoft.com/wfs" targetNamespace="http://www.ionicsoft.com/wfs" xmlns:xlink="http://www.w3.org/1999/xlink" elementFormDefault="qualified" version="0.1">' +
+ ' <xsd:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>' +
+ ' <xsd:element name="highways" substitutionGroup="gml:_Feature" type="wfs:highways"/>' +
+ ' <xsd:complexType name="highways">' +
+ ' <xsd:complexContent>' +
+ ' <xsd:extension base="gml:AbstractFeatureType">' +
+ ' <xsd:sequence>' +
+ ' <xsd:element name="ROUTE_" minOccurs="0" nillable="true" type="xsd:int"/>' +
+ ' <xsd:element name="ROUTE_ID" minOccurs="0" nillable="true" type="xsd:int"/>' +
+ ' <xsd:element name="RT_NUMBER" minOccurs="0" nillable="true" type="xsd:string"/>' +
+ ' <xsd:element name="GEOMETRY" minOccurs="0" nillable="true" type="gml:GeometryAssociationType"/>' +
+ ' </xsd:sequence>' +
+ ' </xsd:extension>' +
+ ' </xsd:complexContent>' +
+ ' </xsd:complexType>' +
+ ' </xsd:schema>';
+
+ parser = new OpenLayers.Format.WFSDescribeFeatureType();
+ res = parser.read(text);
+
+ t.eq(res.featureTypes.length, 1,
+ "There is 1 typename, so length should be 1");
+
+ t.eq(res.featureTypes[0].typeName, "highways",
+ "The name of the typename is highways");
+
+ t.eq(res.featureTypes[0].properties.length, 4,
+ "We expect 4 attributes in the first typename");
+
+ t.eq(res.featureTypes[0].properties[1].name, 'ROUTE_ID',
+ "The second attribute has name ROUTE_ID");
+
+ t.eq(res.featureTypes[0].properties[1].type, 'xsd:int',
+ "The second attribute has type integer");
+
+ t.eq(parseInt(res.featureTypes[0].properties[1].minOccurs), 0,
+ "The second attribute has minOccurs 0");
+
+ t.eq(res.targetNamespace, 'http://www.ionicsoft.com/wfs',
+ "The targetNamespace should be http://www.ionicsoft.com/wfs");
+
+ t.eq(res.targetPrefix, 'wfs', "the targetPrefix should be wfs");
+
+
+ // GeoServer tests
+
+ function geoServerTests(text) {
+ parser = new OpenLayers.Format.WFSDescribeFeatureType();
+ res = parser.read(text);
+
+ t.eq(res.featureTypes.length, 1,
+ "There is 1 typename, so length should be 1");
+
+ t.eq(res.featureTypes[0].typeName, "railroads",
+ "The name of the typeName is railroads");
+
+ t.eq(res.featureTypes[0].properties.length, 2,
+ "We expect 2 attributes in the typename");
+
+ t.eq(res.featureTypes[0].properties[0].name, 'cat',
+ "The first attribute has name cat");
+
+ t.eq(res.featureTypes[0].properties[0].type.split(":")[1], 'long',
+ "The first attribute has type long with a prefix");
+
+ t.eq(res.featureTypes[0].properties[0].localType, 'long',
+ "The first attribute has localType long");
+
+ t.eq(res.targetNamespace, 'http://opengeo.org',
+ "The targetNamespace should be http://opengeo.org");
+
+ t.eq(res.targetPrefix, 'opengeo', "the targetPrefix should be opengeo");
+ }
+
+
+ // Read Geoserver WFS 1.0.0 response
+ // Taken from: http://demo.opengeo.org/geoserver/wfs?service=WFS&request=DescribeFeatureType&typename=opengeo:railroads&version=1.0.0
+ text =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<xs:schema targetNamespace="http://opengeo.org" xmlns:opengeo="http://opengeo.org" xmlns:gml="http://www.opengis.net/gml" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified" version="1.0">' +
+ ' <xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://demo.opengeo.org:80/geoserver/schemas/gml/2.1.2.1/feature.xsd"/>' +
+ ' <xs:complexType xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/2001/XMLSchema" name="railroads_Type">' +
+ ' <xs:complexContent>' +
+ ' <xs:extension base="gml:AbstractFeatureType">' +
+ ' <xs:sequence>' +
+ ' <xs:element name="cat" minOccurs="0" nillable="true" type="xs:long"/>' +
+ ' <xs:element name="the_geom" minOccurs="0" nillable="true" type="gml:MultiLineStringPropertyType"/>' +
+ ' </xs:sequence>' +
+ ' </xs:extension>' +
+ ' </xs:complexContent>' +
+ ' </xs:complexType>' +
+ ' <xs:element name="railroads" type="opengeo:railroads_Type" substitutionGroup="gml:_Feature"/>' +
+ '</xs:schema>';
+
+ geoServerTests(text);
+
+
+ // Read GeoServer WFS 1.1.0 response
+ // taken from http://demo.opengeo.org/geoserver/wfs?service=WFS&request=DescribeFeatureType&typename=opengeo:railroads&version=1.1.0
+ text =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ ' <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" xmlns:opengeo="http://opengeo.org" elementFormDefault="qualified" targetNamespace="http://opengeo.org">' +
+ ' <xsd:import namespace="http://www.opengis.net/gml" schemaLocation="http://demo.opengeo.org:80/geoserver/wfs/schemas/gml/3.1.1/base/gml.xsd"/>' +
+ ' <xsd:complexType name="railroadsType">' +
+ ' <xsd:complexContent>' +
+ ' <xsd:extension base="gml:AbstractFeatureType">' +
+ ' <xsd:sequence>' +
+ ' <xsd:element maxOccurs="1" minOccurs="0" name="cat" nillable="true" type="xsd:long"/>' +
+ ' <xsd:element maxOccurs="1" minOccurs="0" name="the_geom" nillable="true" type="gml:MultiLineStringPropertyType"/>' +
+ ' </xsd:sequence>' +
+ ' </xsd:extension>' +
+ ' </xsd:complexContent>' +
+ ' </xsd:complexType>' +
+ ' <xsd:element name="railroads" substitutionGroup="gml:_Feature" type="opengeo:railroadsType"/>' +
+ ' </xsd:schema>';
+
+ geoServerTests(text);
+
+ // Another GeoServer response with type restrictions
+ // taken from http://sigma.openplans.org/geoserver/wfs?service=WFS&request=DescribeFeatureType&typename=topp:states&version=1.0.0
+ text = '<?xml version="1.0" encoding="UTF-8"?><xs:schema targetNamespace="http://www.openplans.org/topp" xmlns:topp="http://www.openplans.org/topp" xmlns:gml="http://www.opengis.net/gml" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified" version="1.0"><xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://sigma.openplans.org:80/geoserver/schemas/gml/2.1.2.1/feature.xsd"/><xs:complexType xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/2001/XMLSchema" name="states_Type"><xs:complexContent><xs:extension base="gml:AbstractFeatureType"><xs:sequence><xs:element name="the_geom" minOccurs="0" nillable="true" type="gml:MultiPolygonPropertyType"/><xs:element name="STATE_NAME" minOccurs="0" nillable="true"><xs:simpleType><xs:restriction base="xs:string"><xs:maxLength value="25"/></xs:restriction></xs:simpleType></xs:element><xs:element name="STATE_FIPS" minOccurs="0" nillable="true"><xs:simpleType><xs:restriction base="xs:string"><xs:maxLength value="2"/></xs:restriction></xs:simpleType></xs:element><xs:element name="SUB_REGION" minOccurs="0" nillable="true"><xs:simpleType><xs:restriction base="xs:string"><xs:maxLength value="7"/></xs:restriction></xs:simpleType></xs:element><xs:element name="STATE_ABBR" minOccurs="0" nillable="true"><xs:simpleType><xs:restriction base="xs:string"><xs:maxLength value="2"/></xs:restriction></xs:simpleType></xs:element><xs:element name="LAND_KM" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="WATER_KM" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="PERSONS" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="FAMILIES" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="HOUSHOLD" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="MALE" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="FEMALE" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="WORKERS" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="DRVALONE" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="CARPOOL" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="PUBTRANS" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="EMPLOYED" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="UNEMPLOY" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="SERVICE" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="MANUAL" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="P_MALE" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="P_FEMALE" minOccurs="0" nillable="true" type="xs:double"/><xs:element name="SAMP_POP" minOccurs="0" nillable="true" type="xs:double"/></xs:sequence></xs:extension></xs:complexContent></xs:complexType><xs:element name="states" type="topp:states_Type" substitutionGroup="gml:_Feature"/></xs:schema>';
+
+ parser = new OpenLayers.Format.WFSDescribeFeatureType();
+ res = parser.read(text);
+
+ t.eq(res.featureTypes[0].properties[1].name, "STATE_NAME",
+ "name of 2nd property of 1st featureType should be 'STATE_NAME'");
+
+ t.eq(res.featureTypes[0].properties[1].type, "xs:string",
+ "type of 2nd property of 1st featureType should be 'xs:string'");
+
+ t.eq(res.featureTypes[0].properties[1].localType, "string",
+ "localType of 2nd property of 1st featureType should be 'string'");
+
+ t.eq(res.featureTypes[0].properties[1].restriction.maxLength, "25",
+ "the maxLength restriction should be 25");
+ }
+
+ function test_readRestriction(t) {
+ t.plan(2);
+ var text =
+ '<restriction xmlns="http://www.w3.org/2001/XMLSchema" base="xs:string">' +
+ ' <enumeration value="One"/>' +
+ ' <enumeration value="Two"/>' +
+ '</restriction>';
+ var doc = OpenLayers.Format.XML.prototype.read(text).documentElement;
+ var obj = {};
+ new OpenLayers.Format.WFSDescribeFeatureType().readRestriction(doc, obj);
+
+ t.eq(obj.enumeration.length, 2, "enumeration has a length of 2");
+ t.eq(obj.enumeration[1], "Two", "2nd enumeration value is 'Two'");
+ // other functionality of readRestriction already tested in the last
+ // GeoServer example above
+ }
+
+ function test_read_exception(t) {
+ t.plan(1);
+ var text =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<ows:ExceptionReport version="1.0.0"' +
+ ' xsi:schemaLocation="http://www.opengis.net/ows http://localhost:8080/geoserver/schemas/ows/1.0.0/owsExceptionReport.xsd"' +
+ ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ows="http://www.opengis.net/ows">' +
+ ' <ows:Exception exceptionCode="NoApplicableCode">' +
+ ' <ows:ExceptionText>Could not find type: {http://geonode.org/}_map_4_annotations</ows:ExceptionText>' +
+ ' </ows:Exception>' +
+ '</ows:ExceptionReport>';
+ var format = new OpenLayers.Format.WFSDescribeFeatureType();
+ var obj = format.read(text);
+ t.ok(!!obj.error, "Error reported correctly");
+ }
+
+ function test_read_annotation(t) {
+ t.plan(2);
+ var text =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"' +
+ ' xmlns:analytics="http://opengeo.org/analytics" xmlns:cite="http://www.opengeospatial.net/cite"' +
+ ' xmlns:gml="http://www.opengis.net/gml" xmlns:it.geosolutions="http://www.geo-solutions.it"' +
+ ' xmlns:nurc="http://www.nurc.nato.int" xmlns:og="http://opengeo.org"' +
+ ' xmlns:sde="http://geoserver.sf.net" xmlns:sf="http://www.openplans.org/spearfish"' +
+ ' xmlns:tiger="http://www.census.gov" xmlns:tike="http://opengeo.org/#tike"' +
+ ' xmlns:topp="http://www.openplans.org/topp" xmlns:usgs="http://www.usgs.gov/"' +
+ ' xmlns:za="http://opengeo.org/za" elementFormDefault="qualified"' +
+ ' targetNamespace="http://www.openplans.org/topp">' +
+ ' <xsd:import namespace="http://www.opengis.net/gml"' +
+ ' schemaLocation="http://demo.opengeo.org/geoserver/schemas/gml/3.1.1/base/gml.xsd"/>' +
+ ' <xsd:complexType name="statesType">' +
+ ' <xsd:complexContent>' +
+ ' <xsd:extension base="gml:AbstractFeatureType">' +
+ ' <xsd:sequence>' +
+ ' <xsd:element maxOccurs="1" minOccurs="0" name="PERSONS" nillable="true" type="xsd:double">' +
+ ' <xsd:annotation>' +
+ ' <xsd:appinfo>{"title":{"en":"Population"}}</xsd:appinfo>' +
+ ' <xsd:documentation xml:lang="en"> Number of persons living in the state' +
+ ' </xsd:documentation>' +
+ ' </xsd:annotation>' +
+ ' </xsd:element>' +
+ ' </xsd:sequence>' +
+ ' </xsd:extension>' +
+ ' </xsd:complexContent>' +
+ ' </xsd:complexType>' +
+ ' <xsd:element name="states" substitutionGroup="gml:_Feature" type="topp:statesType"/>' +
+ '</xsd:schema>';
+ var format = new OpenLayers.Format.WFSDescribeFeatureType();
+ var res = format.read(text);
+ var property = res.featureTypes[0].properties[0];
+ t.eq(property.annotation.appinfo[0], '{"title":{"en":"Population"}}', "appinfo read correctly");
+ t.eq(property.annotation.documentation[0], {lang: "en", textContent: 'Number of persons living in the state'}, "documentation read correctly");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WFST.html b/misc/openlayers/tests/Format/WFST.html
new file mode 100644
index 0000000..9623b05
--- /dev/null
+++ b/misc/openlayers/tests/Format/WFST.html
@@ -0,0 +1,23 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(2);
+
+ var format = new OpenLayers.Format.WFST();
+ t.ok(format instanceof OpenLayers.Format.WFST.v1_0_0, "constructor returns instance with default versioned format");
+
+ format = new OpenLayers.Format.WFST({
+ version: "1.1.0"
+ });
+ t.ok(format instanceof OpenLayers.Format.WFST.v1_1_0, "constructor returns instance with custom versioned format");
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:512px; height:256px"> </div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WFST/v1.html b/misc/openlayers/tests/Format/WFST/v1.html
new file mode 100644
index 0000000..6cfb1ca
--- /dev/null
+++ b/misc/openlayers/tests/Format/WFST/v1.html
@@ -0,0 +1,455 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read(t) {
+ t.plan(1);
+
+ var data = readXML("FeatureCollection");
+ var format = new OpenLayers.Format.WFST({
+ featureNS: "http://www.openplans.org/topp",
+ featureType: "states"
+ });
+ var features = format.read(data);
+
+ t.eq(features.length, 1, "number of features read from FeatureCollection is correct");
+ }
+
+ function test_write(t) {
+
+ var format = new OpenLayers.Format.WFST({
+ featureNS: "http://www.openplans.org/topp",
+ featureType: "states",
+ featurePrefix: "topp",
+ geometryName: "the_geom"
+ });
+
+ var feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(1,2),
+ {foo: "bar"}
+ );
+
+ var insertFeature = feature.clone();
+ // null value does not show up in insert
+ insertFeature.attributes.nul = null;
+ insertFeature.state = OpenLayers.State.INSERT;
+ var updateFeature = feature.clone();
+ // undefined value means don't create a Property element
+ updateFeature.attributes.unwritten = undefined;
+ // null value gets Property element with no Value
+ updateFeature.attributes.nul = null;
+ updateFeature.fid = "fid.42";
+ updateFeature.state = OpenLayers.State.UPDATE;
+ var deleteFeature = feature.clone();
+ deleteFeature.state = OpenLayers.State.DELETE;
+ deleteFeature.fid = "fid.37";
+
+ t.plan(8);
+ var snippets = {
+ "GetFeature": {handle: "handle_g", maxFeatures: 1, outputFormat: 'json'},
+ "Transaction": {handle: "handle_t"},
+ "Insert": {feature: insertFeature, options: {handle: "handle_i"}},
+ "Update": {feature: updateFeature, options: {handle: "handle_u"}},
+ "Delete": {feature: deleteFeature, options: {handle: "handle_d"}}
+ }
+
+ var arg;
+ for(var snippet in snippets) {
+ arg = snippets[snippet]
+ var expected = readXML(snippet);
+ var got = format.writers["wfs"][snippet].apply(format, [arg]);
+ t.xml_eq(got, expected, snippet + " request created correctly");
+ }
+
+ updateFeature.modified = {geometry: updateFeature.geometry.clone()};
+ updateFeature.geometry = new OpenLayers.Geometry.Point(2,3);
+ var expected = readXML("UpdateModified");
+ var got = format.writers["wfs"]["Update"].apply(format, [{feature: updateFeature}]);
+ t.xml_eq(got, expected, "Update request for feature with modified geometry created correctly");
+
+ updateFeature.modified.attributes = {foo: "bar"};
+ updateFeature.attributes.foo = "baz";
+ delete updateFeature.modified.geometry;
+ var expected = readXML("UpdateModifiedNoGeometry");
+ var got = format.writers["wfs"]["Update"].apply(format, [{feature: updateFeature}]);
+ t.xml_eq(got, expected, "Update request for feature with no modified geometry but modified attributes created correctly");
+
+ // test for a feature that originally had a null geometry and a null value for the attribute
+ updateFeature.modified = {attributes: {foo: null, nul: "nul"}, geometry: null};
+ updateFeature.attributes.foo = "bar";
+ updateFeature.geometry = new OpenLayers.Geometry.Point(2,3);
+ var expected = readXML("UpdateModified");
+ var got = format.writers["wfs"]["Update"].apply(format, [{feature: updateFeature}]);
+ t.xml_eq(got, expected, "Update request for feature with modified geometry created correctly even if original geometry was null");
+ }
+
+ function test_writeNative(t) {
+ t.plan(1);
+ var format = new OpenLayers.Format.WFST({
+ featureNS: "http://www.openplans.org/topp",
+ featureType: "states",
+ version: "1.1.0",
+ featurePrefix: "topp",
+ geometryName: null
+ });
+ var output = format.write(null, {nativeElements: [
+ {
+ vendorId: "ORACLE",
+ safeToIgnore: true,
+ value: "ALTER SESSION ENABLE PARALLEL DML"
+ }, {
+ vendorId: "ORACLE",
+ safeToIgnore: false,
+ value: "Another native line goes here"
+ }]
+ });
+ var expected = '<wfs:Transaction xmlns:wfs="http://www.opengis.net/wfs" service="WFS" version="1.1.0" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><wfs:Native vendorId="ORACLE" safeToIgnore="true">ALTER SESSION ENABLE PARALLEL DML</wfs:Native><wfs:Native vendorId="ORACLE" safeToIgnore="false">Another native line goes here</wfs:Native></wfs:Transaction>';
+ t.xml_eq(output, expected, "Native elements written out correctly");
+ }
+
+ function test_write_no_geometry(t) {
+ var format = new OpenLayers.Format.WFST({
+ featureNS: "http://www.openplans.org/topp",
+ featureType: "states",
+ featurePrefix: "topp",
+ geometryName: null
+ });
+
+ var feature = new OpenLayers.Feature.Vector(null, {foo: "bar"});
+ feature.state = OpenLayers.State.UPDATE;
+ feature.fid = "fid.36";
+
+ t.plan(1);
+ var snippets = {
+ "UpdateNoGeometry": {feature: feature}
+ }
+
+ var arg;
+ for(var snippet in snippets) {
+ arg = snippets[snippet]
+ var expected = readXML(snippet);
+ var got = format.writers["wfs"]["Update"].apply(format, [arg]);
+ t.xml_eq(got, expected, snippet + " request without geometry created correctly");
+ }
+ }
+
+ function test_setFilterProperty(t) {
+ t.plan(2);
+ var format = new OpenLayers.Format.WFST({
+ geometryName: "foo"
+ });
+ var filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.AND,
+ filters: [new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ value: new OpenLayers.Bounds(1,2,3,4)
+ }), new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.DWITHIN,
+ property: "bar",
+ value: new OpenLayers.Geometry.Point(1,2),
+ distance: 10
+ })]
+ });
+ format.setFilterProperty(filter);
+ t.eq(filter.filters[0].property, "foo", "property set if not set on filter");
+ t.eq(filter.filters[1].property, "bar", "property not set if set on filter");
+ }
+
+ function test_update_null_geometry(t) {
+ var format = new OpenLayers.Format.WFST({
+ featureNS: "http://www.openplans.org/topp",
+ featureType: "states",
+ featurePrefix: "topp",
+ geometryName: "the_geom"
+ });
+
+ var feature = new OpenLayers.Feature.Vector(null, {foo: "bar"});
+ feature.state = OpenLayers.State.UPDATE;
+ feature.fid = "fid.36";
+
+ t.plan(1);
+ var snippets = {
+ "UpdateNullGeometry": {feature: feature}
+ };
+
+ var arg;
+ for (var snippet in snippets) {
+ arg = snippets[snippet]
+ var expected = readXML(snippet);
+ var got = format.writers["wfs"]["Update"].apply(format, [arg]);
+ t.xml_eq(got, expected, snippet + " request with null geometry created correctly");
+ }
+ }
+
+ function test_write_multiple(t) {
+
+ var format = new OpenLayers.Format.WFST({
+ featureNS: "http://www.openplans.org/topp",
+ featureType: ["states", "cities"],
+ featurePrefix: "topp",
+ geometryName: "the_geom"
+ });
+
+ t.plan(1);
+ var snippets = {
+ "GetFeatureMultiple": {}
+ }
+
+ var arg;
+ for(var snippet in snippets) {
+ arg = snippets[snippet]
+ var expected = readXML(snippet);
+ var got = format.writers["wfs"]["GetFeature"].apply(format, [arg]);
+ t.xml_eq(got, expected, snippet + " request created correctly with multiple typenames");
+ }
+ }
+
+ function test_write_multi(t) {
+ t.plan(2);
+ var format = new OpenLayers.Format.WFST({
+ featureNS: "http://www.openplans.org/topp",
+ featureType: "states",
+ featurePrefix: "topp",
+ geometryName: "the_geom"
+ });
+
+ var feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(1,2),
+ {foo: "bar"}
+ );
+
+ var insertFeature = feature.clone();
+ // null value does not show up in insert
+ insertFeature.attributes.nul = null;
+ insertFeature.state = OpenLayers.State.INSERT;
+ var updateFeature = feature.clone();
+ // undefined value means don't create a Property element
+ updateFeature.attributes.unwritten = undefined;
+ // null value gets Property element with no Value
+ updateFeature.attributes.nul = null;
+ updateFeature.fid = "fid.42";
+ updateFeature.state = OpenLayers.State.UPDATE;
+ var features = [insertFeature, updateFeature];
+
+ var expected = readXML("TransactionMulti");
+ var geomTypes = OpenLayers.Util.extend({}, format.geometryTypes);
+ var got = format.writers["wfs"]["Transaction"].apply(format, [{
+ features: features,
+ options: {multi: true}}
+ ]);
+ t.xml_eq(got, expected, "Transaction request with multi option created correctly");
+ t.eq(format.geometryTypes, geomTypes, "geometry types unchanged after write with multi option");
+ }
+
+ function readXML(id) {
+ var xml = document.getElementById(id).firstChild.nodeValue;
+ return new OpenLayers.Format.XML().read(xml).documentElement;
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:512px; height:256px"> </div>
+
+<div id="FeatureCollection"><!--
+<wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs" xmlns:topp="http://www.openplans.org/topp" xmlns:gml="http://www.opengis.net/gml">
+ <gml:featureMember>
+ <topp:states fid="states.3">
+ <topp:the_geom>
+ <gml:MultiPolygon srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">
+ <gml:polygonMember>
+ <gml:Polygon>
+ <gml:outerBoundaryIs>
+ <gml:LinearRing>
+ <gml:coordinates decimal="." cs="," ts=" ">-75.70742,38.557476 -75.71106,38.649551 -75.724937,38.83017 -75.752922,39.141548 -75.761658,39.247753 -75.764664,39.295849 -75.772697,39.383007 -75.791435,39.723755 -75.775269,39.724442 -75.745934,39.774818 -75.695114,39.820347 -75.644341,39.838196 -75.583794,39.840008 -75.470345,39.826435 -75.42083,39.79887 -75.412117,39.789658 -75.428009,39.77813 -75.460754,39.763248 -75.475128,39.741718 -75.476334,39.719971 -75.489639,39.714745 -75.610725,39.612793 -75.562996,39.566723 -75.590187,39.463768 -75.515572,39.36694 -75.402481,39.257637 -75.397728,39.073036 -75.324852,39.012386 -75.307899,38.945911 -75.190941,38.80867 -75.083138,38.799812 -75.045998,38.44949 -75.068298,38.449963 -75.093094,38.450451 -75.350204,38.455208 -75.69915,38.463066 -75.70742,38.557476</gml:coordinates>
+ </gml:LinearRing>
+ </gml:outerBoundaryIs>
+ </gml:Polygon>
+ </gml:polygonMember>
+ </gml:MultiPolygon>
+ </topp:the_geom>
+ <topp:STATE_NAME>Delaware</topp:STATE_NAME>
+ <topp:STATE_FIPS>10</topp:STATE_FIPS>
+ <topp:SUB_REGION>S Atl</topp:SUB_REGION>
+ <topp:STATE_ABBR>DE</topp:STATE_ABBR>
+ <topp:LAND_KM>5062.456</topp:LAND_KM>
+ <topp:WATER_KM>1385.022</topp:WATER_KM>
+ <topp:PERSONS>666168.0</topp:PERSONS>
+ <topp:FAMILIES>175867.0</topp:FAMILIES>
+ <topp:HOUSHOLD>247497.0</topp:HOUSHOLD>
+ <topp:MALE>322968.0</topp:MALE>
+ <topp:FEMALE>343200.0</topp:FEMALE>
+ <topp:WORKERS>247566.0</topp:WORKERS>
+ <topp:DRVALONE>258087.0</topp:DRVALONE>
+ <topp:CARPOOL>42968.0</topp:CARPOOL>
+ <topp:PUBTRANS>8069.0</topp:PUBTRANS>
+ <topp:EMPLOYED>335147.0</topp:EMPLOYED>
+ <topp:UNEMPLOY>13945.0</topp:UNEMPLOY>
+ <topp:SERVICE>87973.0</topp:SERVICE>
+ <topp:MANUAL>44140.0</topp:MANUAL>
+ <topp:P_MALE>0.485</topp:P_MALE>
+ <topp:P_FEMALE>0.515</topp:P_FEMALE>
+ <topp:SAMP_POP>102776.0</topp:SAMP_POP>
+ </topp:states>
+ </gml:featureMember>
+</wfs:FeatureCollection>
+--></div>
+
+<div id="GetFeature"><!--
+<wfs:GetFeature xmlns:wfs="http://www.opengis.net/wfs" service="WFS" version="1.0.0" handle="handle_g" outputFormat="json" maxFeatures="1" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <wfs:Query typeName="topp:states" xmlns:topp="http://www.openplans.org/topp"/>
+</wfs:GetFeature>
+--></div>
+<div id="GetFeatureMultiple"><!--
+<wfs:GetFeature xmlns:wfs="http://www.opengis.net/wfs" service="WFS" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <wfs:Query typeName="topp:states" xmlns:topp="http://www.openplans.org/topp"/>
+ <wfs:Query typeName="topp:cities" xmlns:topp="http://www.openplans.org/topp"/>
+</wfs:GetFeature>
+--></div>
+<div id="Transaction"><!--
+<wfs:Transaction xmlns:wfs="http://www.opengis.net/wfs" service="WFS" version="1.0.0"/>
+--></div>
+<div id="TransactionMulti"><!--
+<wfs:Transaction xmlns:wfs="http://www.opengis.net/wfs" service="WFS" version="1.0.0">
+ <wfs:Insert>
+ <feature:states xmlns:feature="http://www.openplans.org/topp">
+ <feature:the_geom>
+ <gml:MultiPoint xmlns:gml="http://www.opengis.net/gml">
+ <gml:pointMember>
+ <gml:Point>
+ <gml:coordinates decimal="." cs="," ts=" ">1,2</gml:coordinates>
+ </gml:Point>
+ </gml:pointMember>
+ </gml:MultiPoint>
+ </feature:the_geom>
+ <feature:foo>bar</feature:foo>
+ </feature:states>
+ </wfs:Insert>
+ <wfs:Update xmlns:wfs="http://www.opengis.net/wfs" typeName="topp:states" xmlns:topp="http://www.openplans.org/topp">
+ <wfs:Property>
+ <wfs:Name>the_geom</wfs:Name>
+ <wfs:Value>
+ <gml:MultiPoint xmlns:gml="http://www.opengis.net/gml">
+ <gml:pointMember>
+ <gml:Point>
+ <gml:coordinates decimal="." cs="," ts=" ">1,2</gml:coordinates>
+ </gml:Point>
+ </gml:pointMember>
+ </gml:MultiPoint>
+ </wfs:Value>
+ </wfs:Property>
+ <wfs:Property>
+ <wfs:Name>foo</wfs:Name>
+ <wfs:Value>bar</wfs:Value>
+ </wfs:Property>
+ <wfs:Property>
+ <wfs:Name>nul</wfs:Name>
+ </wfs:Property>
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:FeatureId fid="fid.42"/>
+ </ogc:Filter>
+ </wfs:Update>
+</wfs:Transaction>
+--></div>
+<div id="Insert"><!--
+<wfs:Insert xmlns:wfs="http://www.opengis.net/wfs" handle="handle_i">
+ <feature:states xmlns:feature="http://www.openplans.org/topp">
+ <feature:the_geom>
+ <gml:Point xmlns:gml="http://www.opengis.net/gml">
+ <gml:coordinates decimal="." cs="," ts=" ">1,2</gml:coordinates>
+ </gml:Point>
+ </feature:the_geom>
+ <feature:foo>bar</feature:foo>
+ </feature:states>
+</wfs:Insert>
+--></div>
+<div id="Update"><!--
+<wfs:Update xmlns:wfs="http://www.opengis.net/wfs" handle="handle_u" typeName="topp:states" xmlns:topp="http://www.openplans.org/topp">
+ <wfs:Property>
+ <wfs:Name>the_geom</wfs:Name>
+ <wfs:Value>
+ <gml:Point xmlns:gml="http://www.opengis.net/gml">
+ <gml:coordinates decimal="." cs="," ts=" ">1,2</gml:coordinates>
+ </gml:Point>
+ </wfs:Value>
+ </wfs:Property>
+ <wfs:Property>
+ <wfs:Name>foo</wfs:Name>
+ <wfs:Value>bar</wfs:Value>
+ </wfs:Property>
+ <wfs:Property>
+ <wfs:Name>nul</wfs:Name>
+ </wfs:Property>
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:FeatureId fid="fid.42"/>
+ </ogc:Filter>
+</wfs:Update>
+--></div>
+<div id="UpdateModified"><!--
+<wfs:Update xmlns:wfs="http://www.opengis.net/wfs" typeName="topp:states" xmlns:topp="http://www.openplans.org/topp">
+ <wfs:Property>
+ <wfs:Name>the_geom</wfs:Name>
+ <wfs:Value>
+ <gml:Point xmlns:gml="http://www.opengis.net/gml">
+ <gml:coordinates decimal="." cs="," ts=" ">2,3</gml:coordinates>
+ </gml:Point>
+ </wfs:Value>
+ </wfs:Property>
+ <wfs:Property>
+ <wfs:Name>foo</wfs:Name>
+ <wfs:Value>bar</wfs:Value>
+ </wfs:Property>
+ <wfs:Property>
+ <wfs:Name>nul</wfs:Name>
+ </wfs:Property>
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:FeatureId fid="fid.42"/>
+ </ogc:Filter>
+</wfs:Update>
+--></div>
+<div id="UpdateModifiedNoGeometry"><!--
+<wfs:Update xmlns:wfs="http://www.opengis.net/wfs" typeName="topp:states" xmlns:topp="http://www.openplans.org/topp">
+ <wfs:Property>
+ <wfs:Name>foo</wfs:Name>
+ <wfs:Value>baz</wfs:Value>
+ </wfs:Property>
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:FeatureId fid="fid.42"/>
+ </ogc:Filter>
+</wfs:Update>
+--></div>
+<div id="Delete"><!--
+<wfs:Delete xmlns:wfs="http://www.opengis.net/wfs" handle="handle_d" typeName="topp:states" xmlns:topp="http://www.openplans.org/topp">
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:FeatureId fid="fid.37"/>
+ </ogc:Filter>
+</wfs:Delete>
+--></div>
+<div id="UpdateNoGeometry"><!--
+<wfs:Update xmlns:wfs="http://www.opengis.net/wfs" typeName="topp:states" xmlns:topp="http://www.openplans.org/topp">
+ <wfs:Property>
+ <wfs:Name>foo</wfs:Name>
+ <wfs:Value>bar</wfs:Value>
+ </wfs:Property>
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:FeatureId fid="fid.36"/>
+ </ogc:Filter>
+</wfs:Update>
+--></div>
+<div id="UpdateNullGeometry"><!--
+<wfs:Update xmlns:wfs="http://www.opengis.net/wfs" typeName="topp:states" xmlns:topp="http://www.openplans.org/topp">
+ <wfs:Property>
+ <wfs:Name>the_geom</wfs:Name>
+ </wfs:Property>
+ <wfs:Property>
+ <wfs:Name>foo</wfs:Name>
+ <wfs:Value>bar</wfs:Value>
+ </wfs:Property>
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:FeatureId fid="fid.36"/>
+ </ogc:Filter>
+</wfs:Update>
+--></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WFST/v1_0_0.html b/misc/openlayers/tests/Format/WFST/v1_0_0.html
new file mode 100644
index 0000000..a8fce79
--- /dev/null
+++ b/misc/openlayers/tests/Format/WFST/v1_0_0.html
@@ -0,0 +1,135 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(1);
+
+ var format = new OpenLayers.Format.WFST.v1_0_0({});
+ t.ok(format instanceof OpenLayers.Format.WFST.v1_0_0, "constructor returns instance");
+ }
+
+ function test_read(t) {
+ t.plan(3);
+
+ var data = readXML("Transaction_Response");
+ var format = new OpenLayers.Format.WFST.v1_0_0({
+ featureNS: "http://www.openplans.org/topp",
+ featureType: "states"
+ });
+ var result = format.read(data);
+ t.eq(result.insertIds[0], "parcelle.40", "First InsertId read correctly");
+ t.eq(result.insertIds[1], "parcelle.41", "Second InsertId read correctly");
+ t.eq(result.success, true, "Success read correctly");
+ }
+
+ function test_write(t) {
+
+ var format = new OpenLayers.Format.WFST.v1_0_0({
+ featureNS: "http://www.openplans.org/topp",
+ featureType: "states",
+ featurePrefix: "topp",
+ geometryName: "the_geom"
+ });
+
+ var cases = [{
+ id: "query0",
+ writer: "wfs:Query",
+ arg: {
+ filter: new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ value: new OpenLayers.Bounds (1,2,3,4)
+ })
+ }
+ }, {
+ id: "query1",
+ writer: "wfs:Query",
+ arg: {
+ srsNameInQuery: true,
+ srsName: "EPSG:900913"
+ }
+ }, {
+ id: "getfeature0",
+ writer: "wfs:GetFeature",
+ arg: {
+ propertyNames: ["STATE_NAME", "STATE_FIPS", "STATE_ABBR"]
+ }
+ }];
+
+ t.plan(cases.length);
+
+ var test, got, exp;
+ for(var i=0; i<cases.length; ++i) {
+ test = cases[i];
+ exp = readXML(test.id);
+ got = format.writeNode(test.writer, test.arg);
+ t.xml_eq(got, exp, test.id + ": correct request");
+ }
+
+ }
+
+ function test_write_poorconfig(t) {
+ t.plan(1);
+ var format = new OpenLayers.Format.WFST.v1_0_0({
+ featureType: "states",
+ featurePrefix: "topp"
+ });
+ var exp = "topp:states";
+ var got = format.writeNode("wfs:Query").getAttribute("typeName");
+ t.eq(got, exp, "Query without featureNS but with featurePrefix queries for the correct featureType");
+ }
+
+ var xmlFormat = new OpenLayers.Format.XML();
+ function readXML(id) {
+ var xml = document.getElementById(id).firstChild.nodeValue;
+ return xmlFormat.read(xml).documentElement;
+ }
+
+ </script>
+</head>
+<body>
+<div id="Transaction_Response"><!--
+<wfs:WFS_TransactionResponse version="1.0.0" xmlns:wfs="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc">
+ <wfs:InsertResult>
+ <ogc:FeatureId fid="parcelle.40"/>
+ <ogc:FeatureId fid="parcelle.41"/>
+ </wfs:InsertResult>
+ <wfs:TransactionResult>
+ <wfs:Status>
+ <wfs:SUCCESS/>
+ </wfs:Status>
+ </wfs:TransactionResult>
+</wfs:WFS_TransactionResponse>
+--></div>
+<div id="query0"><!--
+<wfs:Query xmlns:wfs="http://www.opengis.net/wfs" typeName="topp:states" xmlns:topp="http://www.openplans.org/topp">
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:BBOX>
+ <ogc:PropertyName>the_geom</ogc:PropertyName>
+ <gml:Box xmlns:gml="http://www.opengis.net/gml">
+ <gml:coordinates decimal="." cs="," ts=" ">1,2 3,4</gml:coordinates>
+ </gml:Box>
+ </ogc:BBOX>
+ </ogc:Filter>
+</wfs:Query>
+--></div>
+<div id="query1"><!--
+<wfs:Query xmlns:wfs="http://www.opengis.net/wfs" typeName="topp:states" srsName="EPSG:900913" xmlns:topp="http://www.openplans.org/topp">
+</wfs:Query>
+--></div>
+<div id="getfeature0"><!--
+<wfs:GetFeature service="WFS" version="1.0.0" xmlns:topp="http://www.openplans.org/topp"
+ xmlns:wfs="http://www.opengis.net/wfs"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd">
+ <wfs:Query xmlns:wfs="http://www.opengis.net/wfs" typeName="topp:states" xmlns:topp="http://www.openplans.org/topp">
+ <ogc:PropertyName>STATE_NAME</ogc:PropertyName>
+ <ogc:PropertyName>STATE_FIPS</ogc:PropertyName>
+ <ogc:PropertyName>STATE_ABBR</ogc:PropertyName>
+ </wfs:Query>
+</wfs:GetFeature>
+--></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WFST/v1_1_0.html b/misc/openlayers/tests/Format/WFST/v1_1_0.html
new file mode 100644
index 0000000..52c9cee
--- /dev/null
+++ b/misc/openlayers/tests/Format/WFST/v1_1_0.html
@@ -0,0 +1,236 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(1);
+
+ var format = new OpenLayers.Format.WFST.v1_1_0({});
+ t.ok(format instanceof OpenLayers.Format.WFST.v1_1_0, "constructor returns instance");
+ }
+
+ function test_read(t) {
+ t.plan(3);
+
+ var data = readXML("TransactionResponse");
+ var format = new OpenLayers.Format.WFST.v1_1_0({
+ featureNS: "http://www.openplans.org/topp",
+ featureType: "states"
+ });
+ var result = format.read(data);
+ t.eq(result.insertIds[0], "parcelle.40", "First InsertId read correctly");
+ t.eq(result.insertIds[1], "parcelle.41", "Second InsertId read correctly");
+ t.eq(result.success, true, "Success read correctly");
+ }
+
+ function test_read_hits(t) {
+ t.plan(1);
+ var data = readXML("NumberOfFeatures");
+ var format = new OpenLayers.Format.WFST.v1_1_0({
+ featureNS: "http://mapserver.gis.umn.edu/mapserver",
+ featureType: "AAA64"
+ });
+ var result = format.read(data, {output: "object"});
+ t.eq(result.numberOfFeatures, 625, "numberOfFeatures of FeatureCollection correctly read");
+ }
+
+ function test_read_boundedBy(t) {
+ t.plan(4);
+ var data = readXML("boundedBy");
+ var format = new OpenLayers.Format.WFST.v1_1_0({
+ featureNS: "http://mapserver.gis.umn.edu/mapserver",
+ featureType: "AAA212"
+ });
+ var result = format.read(data, {output: "object"});
+ var bounds = result.bounds;
+ t.eq(bounds.left.toFixed(3), '3197.880', "Left bounds of the feature collection correctly parsed");
+ t.eq(bounds.bottom.toFixed(3), '306457.313', "Bottom bounds of the feature collection correctly parsed");
+ t.eq(bounds.right.toFixed(3), '280339.156', "Right bounds of the feature collection correctly parsed");
+ t.eq(bounds.top.toFixed(3), '613850.438', "Top bounds of the feature collection corectly parsed");
+ }
+
+ function test_write(t) {
+
+ var format = new OpenLayers.Format.WFST.v1_1_0({
+ featureNS: "http://www.openplans.org/topp",
+ featureType: "states",
+ featurePrefix: "topp",
+ srsName: "urn:ogc:def:crs:EPSG::4326",
+ geometryName: "the_geom"
+ });
+
+ var cases = [{
+ id: "query0",
+ writer: "wfs:Query",
+ arg: {
+ filter: new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ value: new OpenLayers.Bounds (1,2,3,4)
+ })
+ }
+ }, {
+ id: "getfeature0",
+ writer: "wfs:GetFeature",
+ arg: {
+ resultType: "hits",
+ propertyNames: ["STATE_NAME", "STATE_FIPS", "STATE_ABBR"]
+ }
+ }, {
+ id: "getfeature1",
+ writer: "wfs:GetFeature",
+ arg: {
+ count: 10,
+ startIndex: 20
+ }
+ }];
+
+ t.plan(cases.length);
+
+ var test, got, exp;
+ for(var i=0; i<cases.length; ++i) {
+ test = cases[i];
+ exp = readXML(test.id);
+ got = format.writeNode(test.writer, test.arg);
+ t.xml_eq(got, exp, test.id + ": correct request");
+ }
+ }
+
+ function test_write_poorconfig(t) {
+ t.plan(1);
+ var format = new OpenLayers.Format.WFST.v1_1_0({
+ featureType: "states",
+ featurePrefix: "topp"
+ });
+ var exp = "topp:states";
+ var got = format.writeNode("wfs:Query").getAttribute("typeName");
+ t.eq(got, exp, "Query without featureNS but with featurePrefix queries for the correct featureType");
+ }
+
+ var xmlFormat = new OpenLayers.Format.XML();
+ function readXML(id) {
+ var xml = document.getElementById(id).firstChild.nodeValue;
+ return xmlFormat.read(xml).documentElement;
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:512px; height:256px"> </div>
+<div id="NumberOfFeatures"><!--
+<?xml version='1.0' encoding="ISO-8859-1" ?>
+<wfs:FeatureCollection
+ xmlns:rws="http://mapserver.gis.umn.edu/mapserver"
+ xmlns:gml="http://www.opengis.net/gml"
+ xmlns:wfs="http://www.opengis.net/wfs"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://mapserver.gis.umn.edu/mapserver http://intranet.rijkswaterstaat.nl/services/geoservices/nwb_wegen?SERVICE=WFS&amp;VERSION=1.1.0&amp;REQUEST=DescribeFeatureType&amp;TYPENAME=feature:AAA64&amp;OUTPUTFORMAT=text/xml; subtype=gml/3.1.1 http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" numberOfFeatures="625">
+</wfs:FeatureCollection>
+--></div>
+<div id="TransactionResponse"><!--
+<wfs:TransactionResponse version="1.1.0" xmlns:ogc="http://www.opengis.net/ogc" xmlns:tiger="http://www.census.gov" xmlns:wfs="http://www.opengis.net/wfs" xmlns:topp="http://www.openplans.org/topp" xmlns:sf="http://www.openplans.org/spearfish" xmlns:ows="http://www.opengis.net/ows" xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <wfs:TransactionSummary>
+ <wfs:totalInserted>0</wfs:totalInserted>
+ <wfs:totalUpdated>1</wfs:totalUpdated>
+ <wfs:totalDeleted>0</wfs:totalDeleted>
+ </wfs:TransactionSummary>
+ <wfs:TransactionResults/>
+ <wfs:InsertResults>
+ <wfs:Feature>
+ <ogc:FeatureId fid="parcelle.40"/>
+ </wfs:Feature>
+ <wfs:Feature>
+ <ogc:FeatureId fid="parcelle.41"/>
+ </wfs:Feature>
+ </wfs:InsertResults>
+</wfs:TransactionResponse>
+--></div>
+<div id="query0"><!--
+<wfs:Query xmlns:wfs="http://www.opengis.net/wfs" typeName="topp:states" srsName="urn:ogc:def:crs:EPSG::4326" xmlns:topp="http://www.openplans.org/topp">
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:BBOX>
+ <ogc:PropertyName>the_geom</ogc:PropertyName>
+ <gml:Envelope xmlns:gml="http://www.opengis.net/gml" srsName="urn:ogc:def:crs:EPSG::4326">
+ <gml:lowerCorner>1 2</gml:lowerCorner>
+ <gml:upperCorner>3 4</gml:upperCorner>
+ </gml:Envelope>
+ </ogc:BBOX>
+ </ogc:Filter>
+</wfs:Query>
+--></div>
+<div id="getfeature0"><!--
+<wfs:GetFeature service="WFS" version="1.1.0" resultType="hits" xmlns:topp="http://www.openplans.org/topp"
+ xmlns:wfs="http://www.opengis.net/wfs"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd">
+ <wfs:Query xmlns:wfs="http://www.opengis.net/wfs" typeName="topp:states" srsName="urn:ogc:def:crs:EPSG::4326" xmlns:topp="http://www.openplans.org/topp">
+ <wfs:PropertyName>STATE_NAME</wfs:PropertyName>
+ <wfs:PropertyName>STATE_FIPS</wfs:PropertyName>
+ <wfs:PropertyName>STATE_ABBR</wfs:PropertyName>
+ </wfs:Query>
+</wfs:GetFeature>
+--></div>
+<div id="getfeature1"><!--
+<wfs:GetFeature service="WFS" version="1.1.0" startIndex="20" count="10" xmlns:topp="http://www.openplans.org/topp"
+ xmlns:wfs="http://www.opengis.net/wfs"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd">
+ <wfs:Query xmlns:wfs="http://www.opengis.net/wfs" typeName="topp:states" srsName="urn:ogc:def:crs:EPSG::4326" xmlns:topp="http://www.openplans.org/topp">
+ </wfs:Query>
+</wfs:GetFeature>
+--></div>
+<div id="boundedBy"><!--
+<?xml version='1.0' encoding="ISO-8859-1" ?>
+<wfs:FeatureCollection
+ xmlns:rws="http://mapserver.gis.umn.edu/mapserver"
+ xmlns:gml="http://www.opengis.net/gml"
+ xmlns:wfs="http://www.opengis.net/wfs"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://mapserver.gis.umn.edu/mapserver http://ontwikkel.intranet.rijkswaterstaat.nl/services/geoservices/ov_zonering?SERVICE=WFS&amp;VERSION=1.1.0&amp;REQUEST=DescribeFeatureType&amp;TYPENAME=AAA212&amp;OUTPUTFORMAT=text/xml; subtype=gml/3.1.1 http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd">
+ <gml:boundedBy>
+ <gml:Envelope srsName="EPSG:28992">
+ <gml:lowerCorner>3197.880000 306457.313000</gml:lowerCorner>
+ <gml:upperCorner>280339.156000 613850.438000</gml:upperCorner>
+ </gml:Envelope>
+ </gml:boundedBy>
+ <gml:featureMember>
+
+ <rws:AAA212 gml:id="AAA212.791">
+ <gml:boundedBy>
+ <gml:Envelope srsName="EPSG:28992">
+ <gml:lowerCorner>196507.469000 502347.938000</gml:lowerCorner>
+ <gml:upperCorner>202430.844000 510383.719000</gml:upperCorner>
+ </gml:Envelope>
+ </gml:boundedBy>
+ <rws:geometry>
+
+ <gml:MultiSurface srsName="EPSG:28992">
+ <gml:surfaceMembers>
+ <gml:Polygon>
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList srsDimension="2">200448.047000 510383.719000 198475.031000 509253.875000 198477.422000 507339.688000 196507.469000 505841.969000 196507.625000 504980.281000 196621.359000 505029.969000 196825.328000 505114.000000 197310.031000 505183.469000 197636.609000 505148.750000 197837.594000 505061.563000 197941.031000 504953.688000 198003.094000 504817.719000 198023.781000 504721.688000 198016.391000 504597.531000 197907.234000 504363.219000 197716.734000 504013.969000 197700.156000 503567.563000 197775.531000 503373.969000 197930.688000 503153.781000 198034.234000 503045.594000 198170.078000 502932.125000 198504.047000 502725.250000 198858.719000 502550.875000 199138.000000 502460.719000 199336.000000 502347.938000 199044.125000 504910.969000 199549.359000 507065.781000 200280.594000 506878.938000 202430.844000 507474.625000 202430.844000 508850.906000 200448.047000 510383.719000 </gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+
+ </gml:Polygon>
+ </gml:surfaceMembers>
+ </gml:MultiSurface>
+ </rws:geometry>
+ <rws:OBJECTID>791</rws:OBJECTID>
+ <rws:HECTARES>1800.89</rws:HECTARES>
+ <rws:ZONENR>4620</rws:ZONENR>
+
+ <rws:NULZONES> </rws:NULZONES>
+ <rws:AREA>0</rws:AREA>
+ <rws:PERIMETER>24305.1</rws:PERIMETER>
+ </rws:AAA212>
+ </gml:featureMember>
+</wfs:FeatureCollection>
+--></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WKT.html b/misc/openlayers/tests/Format/WKT.html
new file mode 100644
index 0000000..bdfc233
--- /dev/null
+++ b/misc/openlayers/tests/Format/WKT.html
@@ -0,0 +1,297 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var points = [];
+ for(var i=0; i<12; ++i) {
+ points.push(new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(Math.random() * 100,
+ Math.random() * 100))
+ );
+ }
+ var multipoint = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.MultiPoint([
+ points[0].geometry,
+ points[1].geometry,
+ points[2].geometry
+ ])
+ );
+
+ var linestrings = [
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LineString([
+ points[0].geometry,
+ points[1].geometry,
+ points[2].geometry
+ ])
+ ),
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LineString([
+ points[3].geometry,
+ points[4].geometry,
+ points[5].geometry
+ ])
+ )
+ ];
+
+ var multilinestring = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.MultiLineString([
+ linestrings[0].geometry,
+ linestrings[1].geometry
+ ])
+ );
+
+ var rings = [
+ new OpenLayers.Geometry.LinearRing([
+ points[0].geometry,
+ points[1].geometry,
+ points[2].geometry
+ ]),
+ new OpenLayers.Geometry.LinearRing([
+ points[3].geometry,
+ points[4].geometry,
+ points[5].geometry
+ ]),
+ new OpenLayers.Geometry.LinearRing([
+ points[6].geometry,
+ points[7].geometry,
+ points[8].geometry
+ ]),
+ new OpenLayers.Geometry.LinearRing([
+ points[9].geometry,
+ points[10].geometry,
+ points[11].geometry
+ ])
+ ];
+
+ var polygons = [
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Polygon([rings[0], rings[1]])
+ ),
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Polygon([rings[2], rings[3]])
+ )
+ ];
+
+ var multipolygon = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.MultiPolygon([
+ polygons[0].geometry,
+ polygons[1].geometry
+ ])
+ );
+
+ var collection = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Collection([
+ points[0].geometry,
+ linestrings[0].geometry
+ ])
+ );
+
+ var geom_array = [points[0], linestrings[0]];
+
+ function test_Format_WKT_constructor(t) {
+ t.plan(4);
+
+ var options = {'foo': 'bar'};
+ var format = new OpenLayers.Format.WKT(options);
+ t.ok(format instanceof OpenLayers.Format.WKT,
+ "new OpenLayers.Format.WKT returns object" );
+ t.eq(format.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a write function");
+ }
+
+ function test_Format_WKT_write(t) {
+ t.plan(8);
+
+ var format = new OpenLayers.Format.WKT();
+
+ // test a point
+
+ t.eq(format.write(points[0]),
+ "POINT(" + points[0].geometry.x + " " + points[0].geometry.y + ")",
+ "format correctly writes Point WKT");
+
+ // test a multipoint
+ t.eq(format.write(multipoint),
+ "MULTIPOINT((" + points[0].geometry.x + " " + points[0].geometry.y + "),(" +
+ points[1].geometry.x + " " + points[1].geometry.y + "),(" +
+ points[2].geometry.x + " " + points[2].geometry.y + "))",
+ "format correctly writes MultiPoint WKT");
+
+ // test a linestring
+ t.eq(format.write(linestrings[0]),
+ "LINESTRING(" + points[0].geometry.x + " " + points[0].geometry.y + "," +
+ points[1].geometry.x + " " + points[1].geometry.y + "," +
+ points[2].geometry.x + " " + points[2].geometry.y + ")",
+ "format correctly writes LineString WKT");
+
+ // test a multilinestring
+ t.eq(format.write(multilinestring),
+ "MULTILINESTRING((" + points[0].geometry.x + " " + points[0].geometry.y + "," +
+ points[1].geometry.x + " " + points[1].geometry.y + "," +
+ points[2].geometry.x + " " + points[2].geometry.y + ")," +
+ "(" + points[3].geometry.x + " " + points[3].geometry.y + "," +
+ points[4].geometry.x + " " + points[4].geometry.y + "," +
+ points[5].geometry.x + " " + points[5].geometry.y + "))",
+ "format correctly writes MultiLineString WKT");
+
+ // test a polygon
+ t.eq(format.write(polygons[0]),
+ "POLYGON((" + points[0].geometry.x + " " + points[0].geometry.y + "," +
+ points[1].geometry.x + " " + points[1].geometry.y + "," +
+ points[2].geometry.x + " " + points[2].geometry.y + "," +
+ points[0].geometry.x + " " + points[0].geometry.y + ")," +
+ "(" + points[3].geometry.x + " " + points[3].geometry.y + "," +
+ points[4].geometry.x + " " + points[4].geometry.y + "," +
+ points[5].geometry.x + " " + points[5].geometry.y + "," +
+ points[3].geometry.x + " " + points[3].geometry.y + "))",
+ "format correctly writes Polygon WKT");
+
+ // test a multipolygon
+ t.eq(format.write(multipolygon),
+ "MULTIPOLYGON(((" + points[0].geometry.x + " " + points[0].geometry.y + "," +
+ points[1].geometry.x + " " + points[1].geometry.y + "," +
+ points[2].geometry.x + " " + points[2].geometry.y + "," +
+ points[0].geometry.x + " " + points[0].geometry.y + ")," +
+ "(" + points[3].geometry.x + " " + points[3].geometry.y + "," +
+ points[4].geometry.x + " " + points[4].geometry.y + "," +
+ points[5].geometry.x + " " + points[5].geometry.y + "," +
+ points[3].geometry.x + " " + points[3].geometry.y + "))," +
+ "((" + points[6].geometry.x + " " + points[6].geometry.y + "," +
+ points[7].geometry.x + " " + points[7].geometry.y + "," +
+ points[8].geometry.x + " " + points[8].geometry.y + "," +
+ points[6].geometry.x + " " + points[6].geometry.y + ")," +
+ "(" + points[9].geometry.x + " " + points[9].geometry.y + "," +
+ points[10].geometry.x + " " + points[10].geometry.y + "," +
+ points[11].geometry.x + " " + points[11].geometry.y + "," +
+ points[9].geometry.x + " " + points[9].geometry.y + ")))",
+ "format correctly writes MultiPolygon WKT");
+
+ // test geometrycollection
+ t.eq(format.write(collection),
+ "GEOMETRYCOLLECTION(POINT(" + points[0].geometry.x + " " + points[0].geometry.y + ")," +
+ "LINESTRING(" + points[0].geometry.x + " " + points[0].geometry.y + "," +
+ points[1].geometry.x + " " + points[1].geometry.y + "," +
+ points[2].geometry.x + " " + points[2].geometry.y + "))",
+ "format correctly writes GeometryCollection WKT");
+
+ // test writing an array of geometries
+ t.eq(format.write(geom_array),
+ "GEOMETRYCOLLECTION(POINT(" + points[0].geometry.x + " " + points[0].geometry.y + ")," +
+ "LINESTRING(" + points[0].geometry.x + " " + points[0].geometry.y + "," +
+ points[1].geometry.x + " " + points[1].geometry.y + "," +
+ points[2].geometry.x + " " + points[2].geometry.y + "))",
+ "format correctly writes WKT for an array of Geometries");
+
+ }
+
+ function test_Format_WKT_read(t) {
+ t.plan(13);
+
+ var format = new OpenLayers.Format.WKT();
+
+ /**
+ * Since we're explicitly testing calls to write, the read tests
+ * just make sure that geometry can make a round trip from read to write.
+ */
+
+ // test a point
+ t.ok(points[0].geometry.equals(format.read(format.write(points[0])).geometry),
+ "format correctly reads Point WKT");
+
+ // test a multipoint
+ t.ok(multipoint.geometry.equals(format.read(format.write(multipoint)).geometry),
+ "format correctly reads MultiPoint WKT");
+
+ // test a multipoint without separating parens
+ t.ok(multipoint.geometry.equals(format.read(
+ "MULTIPOINT(" + points[0].geometry.x + " " + points[0].geometry.y + "," +
+ points[1].geometry.x + " " + points[1].geometry.y + "," +
+ points[2].geometry.x + " " + points[2].geometry.y + ")").geometry),
+ "format correctly reads MultiPoint WKT without parens");
+
+ // test a linestring
+ t.ok(linestrings[0].geometry.equals(format.read(format.write(linestrings[0])).geometry),
+ "format correctly reads LineString WKT");
+
+ // test a multilinestring
+ t.ok(multilinestring.geometry.equals(format.read(format.write(multilinestring)).geometry),
+ "format correctly reads MultiLineString WKT");
+
+ // test a polygon
+ t.ok(polygons[0].geometry.equals(format.read(format.write(polygons[0])).geometry),
+ "format correctly reads Polygon WKT");
+
+ // test a multipolygon
+ t.ok(multipolygon.geometry.equals(format.read(format.write(multipolygon)).geometry),
+ "format correctly reads MultiPolygon WKT");
+
+ // test a collection
+ var wkt = format.write(collection);
+ var got = format.read(wkt);
+ t.ok(got instanceof Array, "by default, reading a collection returns an array");
+ t.eq(got.length, 2, "read two items");
+ t.ok(got[0] instanceof OpenLayers.Feature.Vector, "first item is a feature");
+ t.geom_eq(got[0].geometry, points[0].geometry, "first feature's geometry is the correct point");
+ t.ok(got[1] instanceof OpenLayers.Feature.Vector, "second item is a feature");
+ t.geom_eq(got[1].geometry, linestrings[0].geometry, "second feature's geometry is the correct linestring");
+
+ }
+
+ function test_whitespace(t) {
+ t.plan(3);
+ var wkt = "LINESTRING(7.120068\t43.583917,\n7.120154 43.583652,\n7.120385\t43.582716,\r\n7.12039 43.582568, 7.120712 43.581511,7.120873\n43.580718)";
+ var format = new OpenLayers.Format.WKT();
+ var got = format.read(wkt);
+ t.ok(got instanceof OpenLayers.Feature.Vector, "read a feature");
+ t.ok(got.geometry instanceof OpenLayers.Geometry.LineString, "read a linestring");
+ t.ok(got.geometry.components.length, 6, "read a geometry with 6 components");
+ }
+
+ function test_Format_WKT_read_projection(t) {
+ t.plan(1);
+
+ var projections = {
+ src: new OpenLayers.Projection("EPSG:4326"),
+ dest: new OpenLayers.Projection("EPSG:900913")
+ },
+ points = {
+ src: new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(-87.9, 41.9)
+ ),
+ dest: new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(-9784983.2393667, 5146011.6785665)
+ )
+ },
+ format = new OpenLayers.Format.WKT({
+ externalProjection: projections["src"],
+ internalProjection: projections["dest"]
+ }),
+ gc_wkt_parts = [
+ "GEOMETRYCOLLECTION(",
+ "POINT(",
+ points["src"].geometry.x,
+ " ",
+ points["src"].geometry.y,
+ ")",
+ ")"
+ ],
+ feature = format.read( gc_wkt_parts.join("") )[0],
+ gotGeom = feature.geometry,
+ expectGeom = points["dest"].geometry,
+ // we don't use geometry::toString because we might run into
+ // precision issues
+ precision = 7,
+ got = gotGeom.x.toFixed(precision) + ' ' + gotGeom.y.toFixed(precision),
+ expected = expectGeom.x.toFixed(precision) + ' ' + expectGeom.y.toFixed(precision);
+
+ t.eq(got, expected,
+ "Geometry collections aren't transformed twice when reprojection.");
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WMC.html b/misc/openlayers/tests/Format/WMC.html
new file mode 100644
index 0000000..fbaec81
--- /dev/null
+++ b/misc/openlayers/tests/Format/WMC.html
@@ -0,0 +1,315 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var v1_0_0 = '<ViewContext xmlns="http://www.opengis.net/context" version="1.0.0" id="OpenLayers_Context_233" xsi:schemaLocation="http://www.opengis.net/context http://schemas.opengis.net/context/1.0.0/context.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><General><Window width="512" height="256"/><BoundingBox minx="-109.9709708" miny="27.01451459" maxx="-80.02902918" maxy="41.98548541" SRS="EPSG:4326"/><Title/><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-130.0000000" miny="14.00000000" maxx="-60.00000000" maxy="55.00000000"/></Extension></General><LayerList><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://t1.hypercube.telascience.org/cgi-bin/landsat7"/></Server><Name>landsat7</Name><Title>NASA Global Mosaic</Title><FormatList><Format current="1">image/jpeg</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-130.0000000" miny="14.00000000" maxx="-60.00000000" maxy="55.00000000"/><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">true</ol:isBaseLayer><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">true</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">false</ol:singleTile><ol:tileSize xmlns:ol="http://openlayers.org/context" width="512" height="1024"/></Extension></Layer><Layer queryable="1" hidden="1"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://labs.metacarta.com/wms/vmap0"/></Server><Name>basic</Name><Title>OpenLayers WMS</Title><FormatList><Format current="1">image/jpeg</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-130.0000000" miny="14.00000000" maxx="-60.00000000" maxy="55.00000000"/><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">true</ol:isBaseLayer><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">true</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">false</ol:singleTile></Extension></Layer><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://lioapp.lrc.gov.on.ca/cubeserv/cubeserv.pl"/></Server><Name>na_road:CCRS</Name><Title>Transportation Network</Title><FormatList><Format current="1">image/png</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-166.5320000" miny="4.050460000" maxx="-0.2068180000" maxy="70.28700000"/><ol:transparent xmlns:ol="http://openlayers.org/context">TRUE</ol:transparent><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">false</ol:isBaseLayer><ol:opacity xmlns:ol="http://openlayers.org/context">0.6</ol:opacity><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">false</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">false</ol:singleTile></Extension></Layer><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://columbo.nrlssc.navy.mil/ogcwms/servlet/WMSServlet/AccuWeather_Maps.wms"/></Server><Name>3:1</Name><Title>Radar 3:1</Title><FormatList><Format current="1">image/png</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-131.0294952" miny="14.56289673" maxx="-61.02950287" maxy="54.56289673"/><ol:transparent xmlns:ol="http://openlayers.org/context">TRUE</ol:transparent><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">false</ol:isBaseLayer><ol:opacity xmlns:ol="http://openlayers.org/context">0.8</ol:opacity><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">false</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">true</ol:singleTile></Extension></Layer></LayerList></ViewContext>';
+ var v1_1_0 = '<ViewContext xmlns="http://www.opengis.net/context" version="1.1.0" id="OpenLayers_Context_232" xsi:schemaLocation="http://www.opengis.net/context http://schemas.opengis.net/context/1.1.0/context.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><General><Window width="512" height="256"/><BoundingBox minx="-109.9709708" miny="27.01451459" maxx="-80.02902918" maxy="41.98548541" SRS="EPSG:4326"/><Title/><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-130.0000000" miny="14.00000000" maxx="-60.00000000" maxy="55.00000000"/></Extension></General><LayerList><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://t1.hypercube.telascience.org/cgi-bin/landsat7"/></Server><Name>landsat7</Name><Title>NASA Global Mosaic</Title><sld:MinScaleDenominator xmlns:sld="http://www.opengis.net/sld">6299645.760</sld:MinScaleDenominator><sld:MaxScaleDenominator xmlns:sld="http://www.opengis.net/sld">31498228.80</sld:MaxScaleDenominator><FormatList><Format current="1">image/jpeg</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-130.0000000" miny="14.00000000" maxx="-60.00000000" maxy="55.00000000"/><ol:tileSize xmlns:ol="http://openlayers.org/context" width="512" height="1024"/><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">true</ol:isBaseLayer><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">true</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">false</ol:singleTile></Extension></Layer><Layer queryable="1" hidden="1"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://labs.metacarta.com/wms/vmap0"/></Server><Name>basic</Name><Title>OpenLayers WMS</Title><sld:MinScaleDenominator xmlns:sld="http://www.opengis.net/sld">6299645.760</sld:MinScaleDenominator><sld:MaxScaleDenominator xmlns:sld="http://www.opengis.net/sld">31498228.80</sld:MaxScaleDenominator><FormatList><Format current="1">image/jpeg</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-130.0000000" miny="14.00000000" maxx="-60.00000000" maxy="55.00000000"/><ol:tileSize xmlns:ol="http://openlayers.org/context" width="512" height="1024"/><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">true</ol:isBaseLayer><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">true</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">false</ol:singleTile></Extension></Layer><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://lioapp.lrc.gov.on.ca/cubeserv/cubeserv.pl"/></Server><Name>na_road:CCRS</Name><Title>Transportation Network</Title><sld:MinScaleDenominator xmlns:sld="http://www.opengis.net/sld">6200000.000</sld:MinScaleDenominator><sld:MaxScaleDenominator xmlns:sld="http://www.opengis.net/sld">32000000.00</sld:MaxScaleDenominator><FormatList><Format current="1">image/png</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-166.5320000" miny="4.050460000" maxx="-0.2068180000" maxy="70.28700000"/><ol:tileSize xmlns:ol="http://openlayers.org/context" width="512" height="1024"/><ol:transparent xmlns:ol="http://openlayers.org/context">TRUE</ol:transparent><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">false</ol:isBaseLayer><ol:opacity xmlns:ol="http://openlayers.org/context">0.6</ol:opacity><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">false</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">false</ol:singleTile></Extension></Layer><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://columbo.nrlssc.navy.mil/ogcwms/servlet/WMSServlet/AccuWeather_Maps.wms"/></Server><Name>3:1</Name><Title>Radar 3:1</Title><sld:MinScaleDenominator xmlns:sld="http://www.opengis.net/sld">6299645.760</sld:MinScaleDenominator><sld:MaxScaleDenominator xmlns:sld="http://www.opengis.net/sld">31498228.80</sld:MaxScaleDenominator><FormatList><Format current="1">image/png</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-131.0294952" miny="14.56289673" maxx="-61.02950287" maxy="54.56289673"/><ol:transparent xmlns:ol="http://openlayers.org/context">TRUE</ol:transparent><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">false</ol:isBaseLayer><ol:opacity xmlns:ol="http://openlayers.org/context">0.8</ol:opacity><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">true</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">true</ol:singleTile></Extension></Layer></LayerList></ViewContext>';
+ var polar = '<ViewContext xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:default="http://www.opengis.net/context" xmlns:ol="http://openlayers.org/context" xmlns="http://www.opengis.net/context" xmlns:wms="http://www.opengis.net/wms" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1.0" id="OpenLayers_Context_466" xsi:schemaLocation="http://www.opengis.net/context http://schemas.opengis.net/context/1.1.0/context.xsd"><General><BoundingBox minx="-3000000" miny="-3000000" maxx="7000000" maxy="7000000" SRS="EPSG:32661"/><Title>WMS viewer</Title><Extension><ol:maxExtent minx="-3000000" miny="-3000000" maxx="7000000" maxy="7000000"/><ol:units>m</ol:units></Extension></General><LayerList xmlns="http://www.opengis.net/context"><Layer queryable="0" hidden="0"><Server service="OGC:WMS" version="1.1.1" foo="bar"><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://wms.met.no/maps/northpole.map"/></Server><Name>world</Name><Title>The World</Title><FormatList><Format>image/jpeg</Format><Format current="1">image/png</Format></FormatList></Layer></LayerList></ViewContext>';
+ var fulldoc = '<ViewContext xmlns="http://www.opengis.net/context" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sld="http://www.opengis.net/sld" version="1.1.0" id="eos_data_gateways" xsi:schemaLocation="http://www.opengis.net/context context.xsd"><General><Window width="500" height="300"/><BoundingBox SRS="EPSG:4326" minx="-180.000000" miny="-90.000000" maxx="180.000000" maxy="90.000000"/><Title>EOS Data Gateways</Title><KeywordList><Keyword>EOS</Keyword><Keyword>EOSDIS</Keyword><Keyword>NASA</Keyword><Keyword>CCRS</Keyword><Keyword>CEOS</Keyword><Keyword>OGC</Keyword></KeywordList><Abstract>Map View of EOSDIS partners locations</Abstract><LogoURL width="130" height="74" format="image/gif"><OnlineResource xlink:type="simple" xlink:href="http://redhook.gsfc.nasa.gov/~imswww/pub/icons/logo.gif"/></LogoURL><DescriptionURL format="text/html"><OnlineResource xlink:type="simple" xlink:href="http://eos.nasa.gov/imswelcome"/></DescriptionURL><ContactInformation><ContactPersonPrimary><ContactPerson>Tom Kralidis</ContactPerson><ContactOrganization>Environment Canada</ContactOrganization></ContactPersonPrimary><ContactPosition>Systems Scientist</ContactPosition><ContactAddress><AddressType>postal</AddressType><Address>867 Lakeshore Road</Address><City>Burlington</City><StateOrProvince>Ontario</StateOrProvince><PostCode>L7R 4A6</PostCode><Country>Canada</Country></ContactAddress><ContactVoiceTelephone>+01-905-336-4409</ContactVoiceTelephone><ContactFacsimileTelephone>+01-905-336-4499</ContactFacsimileTelephone><ContactElectronicMailAddress>tom.kralidis@ec.gc.ca</ContactElectronicMailAddress></ContactInformation></General><LayerList><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1" title="ESA CubeSERV"><OnlineResource xlink:type="simple" xlink:href="http://mapserv2.esrin.esa.it/cubestor/cubeserv/cubeserv.cgi"/></Server><Name>WORLD_MODIS_1KM:MapAdmin</Name><Title>WORLD_MODIS_1KM</Title><Abstract>Global maps derived from various Earth Observation sensors / WORLD_MODIS_1KM:MapAdmin</Abstract><SRS>EPSG:4326</SRS><FormatList><Format current="1">image/png</Format><Format>image/gif</Format><Format>image/jpeg</Format></FormatList><StyleList><Style current="1"><Name>default</Name><Title>default</Title><LegendURL width="16" height="16" format="image/gif"><OnlineResource xlink:type="simple" xlink:href="http://mapserv2.esrin.esa.it/cubestor/cubeserv/cubeserv.cgi?version=1.1.1&amp;request=GetLegendGraphic&amp;layer=WORLD_MODIS_1KM:MapAdmin&amp;style=default&amp;format=image/gif"/></LegendURL></Style></StyleList></Layer><Layer queryable="0" hidden="0"><Server service="OGC:WMS" version="1.1.1" title="The GLOBE Program Visualization Server"><OnlineResource xlink:type="simple" xlink:href="http://globe.digitalearth.gov/viz-bin/wmt.cgi"/></Server><Name>COASTLINES</Name><Title>Coastlines</Title><Abstract>Context layer: Coastlines</Abstract><SRS>EPSG:4326</SRS><SRS>EPSG:900913</SRS><FormatList><Format current="1">image/gif</Format><Format>image/png</Format></FormatList><StyleList><Style current="1"><Name>default</Name><Title>Default</Title><LegendURL width="180" format="image/gif" height="50"><OnlineResource xlink:type="simple" xlink:href="http://globe.digitalearth.gov/globe/en/icons/colorbars/COASTLINES.gif"/></LegendURL></Style></StyleList><DimensionList><Dimension name="time" units="ISO8601" nearestValue="1">2011-03-31,2011-04-01</Dimension></DimensionList></Layer><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1" title="The GLOBE Program Visualization Server"><OnlineResource xlink:type="simple" xlink:href="http://globe.digitalearth.gov/viz-bin/wmt.cgi"/></Server><Name>NATIONAL</Name><Title>National Boundaries</Title><Abstract>Context layer: National Boundaries</Abstract><DataURL><OnlineResource xlink:type="simple" xlink:href="http://globe.digitalearth.gov/data/national.gml"/></DataURL><MetadataURL><OnlineResource xlink:type="simple" xlink:href="http://globe.digitalearth.gov/metadata/national.txt"/></MetadataURL><SRS>EPSG:4326</SRS><FormatList><Format current="1">image/gif</Format><Format>image/png</Format></FormatList><StyleList><Style current="1"><Name>default</Name><Title>Default</Title><LegendURL width="180" format="image/gif" height="50"><OnlineResource xlink:type="simple" xlink:href="http://globe.digitalearth.gov/globe/en/icons/colorbars/NATIONAL.gif"/></LegendURL></Style></StyleList></Layer><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1" title="Canada Centre for Remote Sensing Web Map Service"><OnlineResource xlink:type="simple" xlink:href="http://ceoware2.ccrs.nrcan.gc.ca/cubewerx/cubeserv/cubeserv.cgi"/></Server><Name>EOS_DATA_GATEWAYS:CEOWARE2</Name><Title>EOS Data Gateways</Title><Abstract>Locations of EOS Data Gateway Locations. The same services and data are available through each gateway location.</Abstract><sld:MinScaleDenominator>1000</sld:MinScaleDenominator><sld:MaxScaleDenominator>500000</sld:MaxScaleDenominator><SRS>EPSG:4326</SRS><FormatList><Format current="1">image/gif</Format><Format>image/png</Format><Format>image/jpeg</Format></FormatList><StyleList><Style current="1"><Name>default</Name><Title>default</Title><LegendURL width="16" height="16" format="image/gif"><OnlineResource xlink:type="simple" xlink:href="http://ceoware2.ccrs.nrcan.gc.ca/cubewerx/cubeserv/cubeserv.cgi?version=1.1.1&amp;request=GetLegendGraphic&amp;layer=EOS_DATA_GATEWAYS:CEOWARE2&amp;style=default&amp;format=image/gif"/></LegendURL></Style><Style><SLD><Title>Default</Title><sld:StyledLayerDescriptor xmlns:ogc="http://www.opengis.net/ogc" xmlns:sld="http://www.opengis.net/sld" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengeospatial.net/sld/1.0.0/StyledLayerDescriptor.xsd"><sld:NamedLayer><sld:Name>AAA212</sld:Name><sld:UserStyle><sld:FeatureTypeStyle><sld:Rule><sld:TextSymbolizer><sld:Label><ogc:PropertyName>ZONENR</ogc:PropertyName></sld:Label><sld:Font><sld:CssParameter name="font-family">Arial</sld:CssParameter><sld:CssParameter name="font-size">10</sld:CssParameter></sld:Font><sld:Fill><sld:CssParameter name="fill">#FF9900</sld:CssParameter></sld:Fill></sld:TextSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer></sld:StyledLayerDescriptor></SLD></Style><Style><SLD><Title>Default</Title><sld:FeatureTypeStyle xmlns:ogc="http://www.opengis.net/ogc" xmlns:sld="http://www.opengis.net/sld" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengeospatial.net/sld/1.0.0/StyledLayerDescriptor.xsd"><sld:Rule><sld:TextSymbolizer><sld:Label><PropertyName>ZONENR</PropertyName></sld:Label><sld:Font><sld:CssParameter name="font-family">Arial</sld:CssParameter><sld:CssParameter name="font-size">10</sld:CssParameter></sld:Font><sld:Fill><sld:CssParameter name="fill">#FF9900</sld:CssParameter></sld:Fill></sld:TextSymbolizer></sld:Rule></sld:FeatureTypeStyle></SLD></Style></StyleList></Layer></LayerList></ViewContext>';
+
+ function test_Format_WMC_read(t) {
+ t.plan(64);
+
+ var format = new OpenLayers.Format.WMC();
+ var map, layer;
+
+ // test v1.0.0
+ map = format.read(v1_0_0, {map: "map"});
+ t.eq(map.center.lon.toPrecision(6), (-95).toPrecision(6), "(v1.0.0) map center correctly set");
+ t.eq(map.center.lat.toPrecision(6), (34.5).toPrecision(6), "(v1.0.0) map center correctly set");
+ t.eq(map.maxExtent.left.toPrecision(6), (-130).toPrecision(6), "(v1.0.0) map maxExtent correctly set");
+ t.eq(map.layers.length, 4, "(v1.0.0) correct number of layers");
+ t.eq(map.projection, "EPSG:4326", "(v1.0.0) map projection set correctly");
+ // check out first base layer
+ layer = map.layers[0];
+ t.eq(layer.queryable, true, "(1.0.0) Layer is queryable");
+ t.ok(layer instanceof OpenLayers.Layer.WMS,
+ "(v1.0.0) wms layer correctly instantiated");
+ t.eq(layer.url, "http://t1.hypercube.telascience.org/cgi-bin/landsat7",
+ "(v1.0.0) layer url correctly set");
+ t.eq(layer.maxExtent.right.toPrecision(6), (-60).toPrecision(6),
+ "(v1.0.0) layer maxExtent correctly set");
+ t.eq(layer.numZoomLevels, 4,
+ "(v1.0.0) layer numZoomLevels correctly set");
+ t.eq(layer.isBaseLayer, true,
+ "(v1.0.0) layer isBaseLayer correctly set for base layer");
+ t.eq(layer.tileSize.w, 512,
+ "(v1.0.0) layer tileSize width correctly set");
+ t.eq(layer.tileSize.h, 1024,
+ "(v1.0.0) layer tileSize height correctly set");
+ // check out layer not in switcher
+ layer = map.layers[2];
+ t.eq(layer.displayInLayerSwitcher, false,
+ "(v1.0.0) layer displayInLayerSwitcher correctly set");
+ t.eq(layer.params["TRANSPARENT"], "TRUE",
+ "(v1.0.0) layer param.transparent correctly set");
+ t.eq(layer.isBaseLayer, false,
+ "(v1.0.0) layer isBaseLayer correctly set for overlay");
+ // check out single tile overlay
+ layer = map.layers[3];
+ t.eq(layer.singleTile, true,
+ "(v1.0.0) layer singleTile correctly set");
+ t.eq(layer.opacity, 0.8,
+ "(v1.0.0) layer opacity correctly set");
+ map.destroy();
+
+ // test v1.1.0
+ map = format.read(v1_1_0, {map: "map"});
+ t.eq(map.center.lon.toPrecision(6), (-95).toPrecision(6), "(v1.1.0) map center correctly set");
+ t.eq(map.center.lat.toPrecision(6), (34.5).toPrecision(6), "(v1.1.0) map center correctly set");
+ t.eq(map.maxExtent.left.toPrecision(6), (-130).toPrecision(6), "(v1.1.0) map maxExtent correctly set");
+ t.eq(map.layers.length, 4, "(v1.1.0) correct number of layers");
+ t.eq(map.projection, "EPSG:4326", "(v1.1.0) map projection set correctly");
+ // check out first baseLayer
+ layer = map.layers[0];
+ t.ok(layer instanceof OpenLayers.Layer.WMS,
+ "(v1.1.0) wms layer correctly instantiated");
+ t.eq(layer.url, "http://t1.hypercube.telascience.org/cgi-bin/landsat7",
+ "(v1.1.0) layer url correctly set");
+ t.eq(layer.maxExtent.right.toPrecision(6), (-60).toPrecision(6),
+ "(v1.1.0) layer maxExtent correctly set");
+ t.eq(layer.numZoomLevels, 4,
+ "(v1.1.0) layer numZoomLevels correctly set");
+ t.eq(layer.isBaseLayer, true,
+ "(v1.1.0) layer isBaseLayer correctly set for overlay");
+ // check out layer not in switcher
+ layer = map.layers[2];
+ t.eq(layer.displayInLayerSwitcher, false,
+ "(v1.1.0) layer displayInLayerSwitcher correctly set");
+ t.eq(layer.params["TRANSPARENT"], "TRUE",
+ "(v1.1.0) layer param.transparent correctly set");
+ t.eq(layer.isBaseLayer, false,
+ "(v1.1.0) layer isBaseLayer correctly set for overlay");
+ t.eq(layer.minScale.toPrecision(6), (32000000).toPrecision(6),
+ "(v1.1.0) layer minScale correctly set");
+ t.eq(layer.maxScale.toPrecision(6), (6200000).toPrecision(6),
+ "(v1.1.0) layer maxScale correctly set");
+ // check out single tile overlay
+ layer = map.layers[3];
+ t.eq(layer.singleTile, true,
+ "(v1.1.0) layer singleTile correctly set");
+ t.eq(layer.opacity, 0.8,
+ "(v1.1.0) layer opacity correctly set");
+ map.destroy();
+
+ // Check if maxResolution is set correctly
+ map = format.read(polar, {map: "map"});
+ t.eq(map.maxResolution, 39062.5,
+ "maxResolution correctly set");
+ map.destroy();
+
+ // test mapOptions
+ map = format.read(v1_1_0, {map: {foo: 'bar', div: 'map'}});
+ t.eq(map.foo, "bar",
+ "mapOptions correctly passed to the created map object");
+ map.destroy();
+
+ map = format.read(fulldoc, {map: "map"});
+
+ var meta = map.metadata;
+
+ // Check if ContextInformation is set properly
+ var cinfo = meta.contactInformation;
+
+ t.eq(cinfo.personPrimary.person, "Tom Kralidis", "got correct person");
+ t.eq(cinfo.personPrimary.organization, "Environment Canada", "got correct organization");
+
+ t.eq(cinfo.contactAddress.address, "867 Lakeshore Road", "got correct address");
+ t.eq(cinfo.contactAddress.city, "Burlington", "got correct city");
+ t.eq(cinfo.contactAddress.country, "Canada", "got correct country");
+ t.eq(cinfo.contactAddress.postcode, "L7R 4A6", "got correct postcode");
+ t.eq(cinfo.contactAddress.stateOrProvince, "Ontario", "got correct stateOrProvince");
+ t.eq(cinfo.contactAddress.type, "postal", "got correct address type");
+
+ t.eq(cinfo.email, "tom.kralidis@ec.gc.ca", "got correct email");
+ t.eq(cinfo.fax, "+01-905-336-4499", "got correct fax number");
+ t.eq(cinfo.phone, "+01-905-336-4409", "got correct phone number");
+ t.eq(cinfo.position, "Systems Scientist", "got correct position");
+
+ // Check if LogoURL is read properly
+ var logo = meta.logo;
+ t.eq(logo, {
+ href: "http://redhook.gsfc.nasa.gov/~imswww/pub/icons/logo.gif",
+ width: "130",
+ height: "74",
+ format: "image/gif"},
+ "got currect logo");
+
+ t.eq(meta.descriptionURL, "http://eos.nasa.gov/imswelcome", "got correct descriptionURL");
+
+ t.eq(meta.keywords,
+ ["EOS", "EOSDIS", "NASA", "CCRS", "CEOS", "OGC"],
+ "got correct keywords");
+
+ layer = map.layers[1];
+
+ t.eq(layer.metadata.servertitle,
+ "The GLOBE Program Visualization Server",
+ "got correct title for server");
+
+ t.eq(layer.srs,
+ {"EPSG:4326": true, "EPSG:900913": true},
+ "SRS read correctly");
+
+ t.eq(layer.metadata.formats,
+ [{value: "image/gif", current: true},
+ {value: "image/png"}],
+ "formats read correctly");
+
+ var style = layer.metadata.styles[0];
+ t.eq(style.legend, {
+ href: "http://globe.digitalearth.gov/globe/en/icons/colorbars/COASTLINES.gif",
+ width: "180",
+ height: "50",
+ format: "image/gif"},
+ "got currect legend");
+
+
+ var dim = layer.dimensions["time"];
+ t.eq(dim.name, "time", "got correct name of dimension");
+ t.eq(dim.units, "ISO8601", "got correct units for dimension");
+ t.eq(dim.nearestValue, true, "got correct value for nearestValue");
+ t.eq(dim.values, ["2011-03-31", "2011-04-01"], "got correct values for dimension");
+
+ layer = map.layers[2];
+ t.eq(layer.metadata.dataURL,
+ "http://globe.digitalearth.gov/data/national.gml",
+ "got correct dataURL");
+ t.eq(layer.metadataURL,
+ "http://globe.digitalearth.gov/metadata/national.txt",
+ "got correct metadataURL");
+
+ layer = map.layers[3];
+ var sld_body = '<sld:StyledLayerDescriptor xmlns:ogc="http://www.opengis.net/ogc" xmlns:sld="http://www.opengis.net/sld" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengeospatial.net/sld/1.0.0/StyledLayerDescriptor.xsd"><sld:NamedLayer><sld:Name>AAA212</sld:Name><sld:UserStyle><sld:FeatureTypeStyle><sld:Rule><sld:TextSymbolizer><sld:Label><ogc:PropertyName>ZONENR</ogc:PropertyName></sld:Label><sld:Font><sld:CssParameter name="font-family">Arial</sld:CssParameter><sld:CssParameter name="font-size">10</sld:CssParameter></sld:Font><sld:Fill><sld:CssParameter name="fill">#FF9900</sld:CssParameter></sld:Fill></sld:TextSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer></sld:StyledLayerDescriptor>';
+ var styles = layer.metadata.styles;
+ t.xml_eq(styles[1].body, sld_body, "StyledLayerDescriptor body read correctly");
+
+ sld_body = '<sld:FeatureTypeStyle xmlns:ogc="http://www.opengis.net/ogc" xmlns:sld="http://www.opengis.net/sld" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengeospatial.net/sld/1.0.0/StyledLayerDescriptor.xsd"><sld:Rule><sld:TextSymbolizer><sld:Label><PropertyName xmlns="http://www.opengis.net/context">ZONENR</PropertyName></sld:Label><sld:Font><sld:CssParameter name="font-family">Arial</sld:CssParameter><sld:CssParameter name="font-size">10</sld:CssParameter></sld:Font><sld:Fill><sld:CssParameter name="fill">#FF9900</sld:CssParameter></sld:Fill></sld:TextSymbolizer></sld:Rule></sld:FeatureTypeStyle>';
+ t.xml_eq(styles[2].body, sld_body, "FeatureTypeStyle body read correctly");
+
+ }
+
+ function test_Format_WMC_write(t) {
+
+ var format = new OpenLayers.Format.WMC();
+ var map = format.read(v1_1_0, {map: "map"});
+ var wmc;
+
+ function sanitize(str) {
+ // id is unique so we don't expect it to be the same
+ str = str.replace(/OpenLayers_Context_\d+/, "foo");
+ // FF and IE differ on ordering of attribute nodes
+ // a better way to compare xml would be nice
+ str = str.replace(/\s*xsi:schemaLocation=".*?"/, "");
+ str = str.replace(/\s*xmlns:xlink=".*?"/g, "");
+ // and of course floating point precision is an issue
+ str = str.replace(/(-?\d+\.\d+)/g, function(m) {return parseFloat(m).toPrecision(6)});
+ return str;
+ }
+
+ /**
+ * Version 1.0.0 doesn't make the round trip because min/max resolutions
+ * are not stored. Version 1.1.0 introduced minScaleDenominator and
+ * maxScaleDenominator. The parser still writes valid WMC v1.0.0, you
+ * just need a validator to prove it.
+ *
+ * The wmc.html example can be used to produce WMC docs for validation.
+ * Prior to validation, the Extension tags need to be removed unless someone
+ * cares to write and maintain some XSD. Both version 1.0.0 and 1.1.0
+ * validate on http://www.validome.org/xml/.
+ */
+
+ // test v1.1.0
+ if(OpenLayers.BROWSER_NAME== "opera") {
+ t.plan(0);
+ t.debug_print("WMC writing works but is not tested in Opera");
+ } else {
+ t.plan(11);
+
+ map = format.read(v1_1_0, {map: "map"});
+ wmc = format.write(map);
+ t.eq(sanitize(wmc), sanitize(v1_1_0),
+ "(v1.1.0) write gives what read got");
+ map.destroy();
+
+ var parser = format.getParser("1.1.0");
+ map = format.read(fulldoc, {map: "map"});
+ var context = format.toContext(map);
+
+ // KeywordList
+ var expected = '<KeywordList xmlns="http://www.opengis.net/context"><Keyword>EOS</Keyword><Keyword>EOSDIS</Keyword><Keyword>NASA</Keyword><Keyword>CCRS</Keyword><Keyword>CEOS</Keyword><Keyword>OGC</Keyword></KeywordList>';
+ t.xml_eq(parser.write_wmc_KeywordList(context.keywords),
+ expected,
+ "keywordlist written correctly");
+
+ // ContactInformation
+ expected = '<ContactInformation xmlns="http://www.opengis.net/context"><ContactPersonPrimary><ContactPerson>Tom Kralidis</ContactPerson><ContactOrganization>Environment Canada</ContactOrganization></ContactPersonPrimary><ContactPosition>Systems Scientist</ContactPosition><ContactAddress><AddressType>postal</AddressType><Address>867 Lakeshore Road</Address><City>Burlington</City><StateOrProvince>Ontario</StateOrProvince><PostCode>L7R 4A6</PostCode><Country>Canada</Country></ContactAddress><ContactVoiceTelephone>+01-905-336-4409</ContactVoiceTelephone><ContactFacsimileTelephone>+01-905-336-4499</ContactFacsimileTelephone><ContactElectronicMailAddress>tom.kralidis@ec.gc.ca</ContactElectronicMailAddress></ContactInformation>';
+ t.xml_eq(parser.write_wmc_ContactInformation(context.contactInformation),
+ expected,
+ "contactInformation written correctly");
+
+ // LogoURL
+ expected = '<LogoURL xmlns="http://www.opengis.net/context" width="130" height="74" format="image/gif"><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://redhook.gsfc.nasa.gov/~imswww/pub/icons/logo.gif"/></LogoURL>';
+ t.xml_eq(parser.write_wmc_URLType("LogoURL", context.logo.href, context.logo),
+ expected,
+ "LogoURL written correctly");
+
+ // DescriptionURL
+ expected = '<DescriptionURL xmlns="http://www.opengis.net/context"><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://eos.nasa.gov/imswelcome"/></DescriptionURL>';
+ t.xml_eq(parser.write_wmc_URLType("DescriptionURL", context.descriptionURL),
+ expected,
+ "DescriptionURL written correctly");
+
+
+ var layerContext = context.layersContext[1];
+
+ // Server
+ expected = '<Server xmlns="http://www.opengis.net/context" service="OGC:WMS" version="1.1.1" title="The GLOBE Program Visualization Server"><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://globe.digitalearth.gov/viz-bin/wmt.cgi"/></Server>';
+ t.xml_eq(parser.write_wmc_Server(layerContext),
+ expected,
+ "Server written correctly");
+
+ // FormatList
+ expected = '<FormatList xmlns="http://www.opengis.net/context"><Format current="1">image/gif</Format><Format>image/png</Format></FormatList>';
+ t.xml_eq(parser.write_wmc_FormatList(layerContext),
+ expected,
+ "FormatList written correctly");
+
+ // DimensionList
+ expected = '<DimensionList xmlns="http://www.opengis.net/context"><Dimension name="time" units="ISO8601" nearestValue="1" unitSymbol="" userValue="" multipleValues="0" current="0" default="">2011-03-31,2011-04-01</Dimension></DimensionList>';
+ t.xml_eq(parser.write_wmc_DimensionList(layerContext),
+ expected,
+ "DimensionList written correctly");
+
+ // LegendURL
+ var legend = layerContext.styles[0].legend;
+ expected = '<LegendURL xmlns="http://www.opengis.net/context" width="180" format="image/gif" height="50"><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://globe.digitalearth.gov/globe/en/icons/colorbars/COASTLINES.gif"/></LegendURL>';
+ t.xml_eq(parser.write_wmc_URLType("LegendURL", legend.href, legend),
+ expected,
+ "LegendURL written correctly");
+
+ layerContext = context.layersContext[2];
+
+ // DataURL
+ expected = '<DataURL xmlns="http://www.opengis.net/context"><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://globe.digitalearth.gov/data/national.gml" /></DataURL>';
+ t.xml_eq(parser.write_wmc_URLType("DataURL", layerContext.dataURL),
+ expected,
+ "DataURL written correctly");
+
+ // MetadataURL
+ expected = '<MetadataURL xmlns="http://www.opengis.net/context"><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://globe.digitalearth.gov/metadata/national.txt" /></MetadataURL>';
+ t.xml_eq(parser.write_wmc_URLType("MetadataURL", layerContext.metadataURL),
+ expected,
+ "MetadataURL written correctly");
+
+ }
+
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 512px; height: 256px;"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WMC/v1.html b/misc/openlayers/tests/Format/WMC/v1.html
new file mode 100644
index 0000000..05e6078
--- /dev/null
+++ b/misc/openlayers/tests/Format/WMC/v1.html
@@ -0,0 +1,266 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_write_wmc_StyleList(t) {
+ t.plan(4);
+
+ var layer, layerContext, got, expected;
+
+ var format = new OpenLayers.Format.WMC();
+ var parser = format.getParser("1");
+ var name = "test";
+ var url = "http://foo";
+
+ // test named style
+ layer = new OpenLayers.Layer.WMS(name, url, {
+ styles: "mystyle"
+ });
+ layerContext = format.layerToContext(layer);
+ got = parser.write_wmc_StyleList(layerContext);
+ expected =
+ "<StyleList xmlns='http://www.opengis.net/context'>" +
+ "<Style current='1'>" +
+ "<Name>mystyle</Name><Title>Default</Title>" +
+ "</Style>" +
+ "</StyleList>";
+
+ t.xml_eq(got, expected, "named style correctly written");
+ layer.destroy();
+
+ // test linked style
+ layer = new OpenLayers.Layer.WMS(name, url, {
+ sld: "http://linked.sld"
+ });
+ layerContext = format.layerToContext(layer);
+ got = parser.write_wmc_StyleList(layerContext);
+ expected =
+ "<StyleList xmlns='http://www.opengis.net/context'>" +
+ "<Style current='1'>" +
+ "<SLD>" +
+ "<Title>Default</Title>" +
+ "<OnlineResource xmlns:xlink='http://www.w3.org/1999/xlink' "+
+ "xlink:type='simple' " +
+ "xlink:href='http://linked.sld' />" +
+ "</SLD>" +
+ "</Style>" +
+ "</StyleList>";
+
+ t.xml_eq(got, expected, "linked style correctly written");
+ layer.destroy();
+
+ // test inline style
+ layer = new OpenLayers.Layer.WMS(name, url, {
+ sld_body:
+ "<sld:StyledLayerDescriptor version='1.0.0' " +
+ "xmlns:ogc='http://www.opengis.net/ogc' " +
+ "xmlns:sld='http://www.opengis.net/sld' " +
+ "xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' " +
+ "xsi:schemaLocation='http://www.opengis.net/sld http://schemas.opengeospatial.net/sld/1.0.0/StyledLayerDescriptor.xsd'>" +
+ "<sld:NamedLayer>" +
+ "<sld:Name>AAA212</sld:Name>" +
+ "<sld:UserStyle>" +
+ "<sld:FeatureTypeStyle>" +
+ "<sld:Rule>" +
+ "<sld:TextSymbolizer>" +
+ "<sld:Label>" +
+ "<ogc:PropertyName>ZONENR</ogc:PropertyName>" +
+ "</sld:Label>" +
+ "<sld:Font>" +
+ "<sld:CssParameter name='font-family'>Arial</sld:CssParameter>" +
+ "<sld:CssParameter name='font-size'>10</sld:CssParameter>" +
+ "</sld:Font>" +
+ "<sld:Fill>" +
+ "<sld:CssParameter name='fill'>#FF9900</sld:CssParameter>" +
+ "</sld:Fill>" +
+ "</sld:TextSymbolizer>" +
+ "</sld:Rule>" +
+ "</sld:FeatureTypeStyle>" +
+ "</sld:UserStyle>" +
+ "</sld:NamedLayer>" +
+ "</sld:StyledLayerDescriptor>"
+ });
+
+ layerContext = format.layerToContext(layer);
+ got = parser.write_wmc_StyleList(layerContext);
+ expected =
+ "<StyleList xmlns='http://www.opengis.net/context'>" +
+ "<Style current='1'>" +
+ "<SLD>" +
+ "<Title>Default</Title>" +
+ "<sld:StyledLayerDescriptor version='1.0.0' " +
+ "xmlns:sld='http://www.opengis.net/sld' " +
+ "xmlns:ogc='http://www.opengis.net/ogc' " +
+ "xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' " +
+ "xsi:schemaLocation='http://www.opengis.net/sld http://schemas.opengeospatial.net/sld/1.0.0/StyledLayerDescriptor.xsd'>" +
+ "<sld:NamedLayer>" +
+ "<sld:Name>AAA212</sld:Name>" +
+ "<sld:UserStyle>" +
+ "<sld:FeatureTypeStyle>" +
+ "<sld:Rule>" +
+ "<sld:TextSymbolizer>" +
+ "<sld:Label>" +
+ "<ogc:PropertyName>ZONENR</ogc:PropertyName>" +
+ "</sld:Label>" +
+ "<sld:Font>" +
+ "<sld:CssParameter name='font-family'>Arial</sld:CssParameter>" +
+ "<sld:CssParameter name='font-size'>10</sld:CssParameter>" +
+ "</sld:Font>" +
+ "<sld:Fill>" +
+ "<sld:CssParameter name='fill'>#FF9900</sld:CssParameter>" +
+ "</sld:Fill>" +
+ "</sld:TextSymbolizer>" +
+ "</sld:Rule>" +
+ "</sld:FeatureTypeStyle>" +
+ "</sld:UserStyle>" +
+ "</sld:NamedLayer>" +
+ "</sld:StyledLayerDescriptor>" +
+ "</SLD>" +
+ "</Style>" +
+ "</StyleList>";
+
+ t.xml_eq(got, expected, "inline style correctly written");
+ layer.destroy();
+
+ // test inline FeatureTypeStyle
+ layer = new OpenLayers.Layer.WMS(name, url, {
+ sld_body:
+ "<sld:FeatureTypeStyle version='1.0.0' " +
+ "xmlns:sld='http://www.opengis.net/sld' " +
+ "xmlns:ogc='http://www.opengis.net/ogc' " +
+ "xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' " +
+ "xsi:schemaLocation='http://www.opengis.net/sld http://schemas.opengeospatial.net/sld/1.0.0/StyledLayerDescriptor.xsd'>" +
+ "<sld:Rule>" +
+ "<sld:TextSymbolizer>" +
+ "<sld:Label>" +
+ "<ogc:PropertyName>ZONENR</ogc:PropertyName>" +
+ "</sld:Label>" +
+ "<sld:Font>" +
+ "<sld:CssParameter name='font-family'>Arial</sld:CssParameter>" +
+ "<sld:CssParameter name='font-size'>10</sld:CssParameter>" +
+ "</sld:Font>" +
+ "<sld:Fill>" +
+ "<sld:CssParameter name='fill'>#FF9900</sld:CssParameter>" +
+ "</sld:Fill>" +
+ "</sld:TextSymbolizer>" +
+ "</sld:Rule>" +
+ "</sld:FeatureTypeStyle>"
+ });
+
+ layerContext = format.layerToContext(layer);
+ got = parser.write_wmc_StyleList(layerContext);
+ expected =
+ "<StyleList xmlns='http://www.opengis.net/context'>" +
+ "<Style current='1'>" +
+ "<SLD>" +
+ "<Title>Default</Title>" +
+ "<sld:FeatureTypeStyle version='1.0.0' " +
+ "xmlns:sld='http://www.opengis.net/sld' " +
+ "xmlns:ogc='http://www.opengis.net/ogc' " +
+ "xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' " +
+ "xsi:schemaLocation='http://www.opengis.net/sld http://schemas.opengeospatial.net/sld/1.0.0/StyledLayerDescriptor.xsd'>" +
+ "<sld:Rule>" +
+ "<sld:TextSymbolizer>" +
+ "<sld:Label>" +
+ "<ogc:PropertyName>ZONENR</ogc:PropertyName>" +
+ "</sld:Label>" +
+ "<sld:Font>" +
+ "<sld:CssParameter name='font-family'>Arial</sld:CssParameter>" +
+ "<sld:CssParameter name='font-size'>10</sld:CssParameter>" +
+ "</sld:Font>" +
+ "<sld:Fill>" +
+ "<sld:CssParameter name='fill'>#FF9900</sld:CssParameter>" +
+ "</sld:Fill>" +
+ "</sld:TextSymbolizer>" +
+ "</sld:Rule>" +
+ "</sld:FeatureTypeStyle>" +
+ "</SLD>" +
+ "</Style>" +
+ "</StyleList>";
+
+ t.xml_eq(got, expected, "inline FeatureTypeStyle correctly written");
+ layer.destroy();
+ }
+
+ function test_read_wmc_StyleList(t) {
+ t.plan(3);
+
+ var xml = new OpenLayers.Format.XML();
+ var format = new OpenLayers.Format.WMC();
+ var parser = format.getParser("1");
+ var node, text, layerContext, layer;
+
+ // test named style
+ text =
+ "<StyleList xmlns='http://www.opengis.net/context'>" +
+ "<Style current='1'>" +
+ "<Name>mystyle</Name><Title>Default</Title>" +
+ "</Style>" +
+ "</StyleList>";
+ node = xml.read(text).documentElement;
+ layerContext = {
+ styles: []
+ };
+ parser.read_wmc_StyleList(layerContext, node);
+ layer = format.getLayerFromContext(layerContext);
+ t.eq(layer.params.STYLES, "mystyle", "named style correctly read");
+
+ // test linked style
+ text =
+ "<StyleList xmlns='http://www.opengis.net/context'>" +
+ "<Style current='1'>" +
+ "<SLD>" +
+ "<OnlineResource xmlns:xlink='http://www.w3.org/1999/xlink' "+
+ "xlink:type='simple' " +
+ "xlink:href='http://linked.sld' />" +
+ "</SLD>" +
+ "</Style>" +
+ "</StyleList>";
+ node = xml.read(text).documentElement;
+ layerContext = {
+ styles: []
+ };
+ parser.read_wmc_StyleList(layerContext, node);
+ layer = format.getLayerFromContext(layerContext);
+ t.eq(layer.params.SLD, "http://linked.sld", "linked style correctly read");
+
+ // test inline style
+ // any valid xml under the StyledLayerDescriptor node should make the
+ // round trip from string to node and back
+ text =
+ "<StyleList xmlns='http://www.opengis.net/context'>" +
+ "<Style current='1'>" +
+ "<SLD>" +
+ "<sld:StyledLayerDescriptor version='1.0.0' " +
+ "xmlns:sld='http://www.opengis.net/sld' " +
+ "xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' " +
+ "xsi:schemaLocation='http://www.opengis.net/sld http://schemas.opengeospatial.net/sld/1.0.0/StyledLayerDescriptor.xsd'>" +
+ "<foo>bar<more/></foo>" +
+ "</sld:StyledLayerDescriptor>" +
+ "</SLD>" +
+ "</Style>" +
+ "</StyleList>";
+ node = xml.read(text).documentElement;
+ layerContext = {
+ styles: []
+ };
+ parser.read_wmc_StyleList(layerContext, node);
+ layer = format.getLayerFromContext(layerContext);
+ var expected =
+ "<sld:StyledLayerDescriptor version='1.0.0' " +
+ "xmlns:sld='http://www.opengis.net/sld' " +
+ "xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' " +
+ "xsi:schemaLocation='http://www.opengis.net/sld http://schemas.opengeospatial.net/sld/1.0.0/StyledLayerDescriptor.xsd'>" +
+ "<foo xmlns='http://www.opengis.net/context'>bar<more/></foo>" +
+ "</sld:StyledLayerDescriptor>";
+ t.xml_eq(layer.params.SLD_BODY, expected, "inline style correctly read");
+
+ }
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WMC/v1_1_0.html b/misc/openlayers/tests/Format/WMC/v1_1_0.html
new file mode 100644
index 0000000..815d3bf
--- /dev/null
+++ b/misc/openlayers/tests/Format/WMC/v1_1_0.html
@@ -0,0 +1,86 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_write_wmc_Layer(t) {
+ if (OpenLayers.BROWSER_NAME == "safari") {
+ t.plan(0);
+ t.debug_print("Safari has wierd behavior with getElementsByTagNameNS: the result is that we can't run these tests there. Patches welcome.");
+ return;
+ }
+ t.plan(12);
+
+ // direct construction of a parser for a unit test
+ var format = new OpenLayers.Format.WMC();
+ var parser = format.getParser("1_1_0");
+ var sldNS = parser.namespaces["sld"];
+
+ // test that Min/MaxScaleDenominator is not written out when no
+ // resolution related options are set
+ var layer = new OpenLayers.Layer.WMS(
+ "test", "http://foo", {},
+ {maxExtent: new OpenLayers.Bounds(1, 2, 3, 4)}
+ );
+ var layerContext = format.layerToContext(layer);
+ var node = parser.write_wmc_Layer(layerContext);
+ var minList = parser.getElementsByTagNameNS(node, sldNS, "MinScaleDenominator");
+ t.eq(minList.length, 0, "(none) node not written with MinScaleDenominator");
+ var maxList = parser.getElementsByTagNameNS(node, sldNS, "MaxScaleDenominator");
+ t.eq(maxList.length, 0, "(none) node not written with MaxScaleDenominator");
+
+ // test that Min/MaxScaleDenominator is written out for explicit
+ // resolutions array
+ layer = new OpenLayers.Layer.WMS(
+ "test", "http://foo", {},
+ {resolutions: [4, 2, 1], maxExtent: new OpenLayers.Bounds(1, 2, 3, 4)}
+ );
+ layer.minScale = Math.random();
+ layer.maxScale = Math.random();
+ sldNS = parser.namespaces["sld"];
+ layerContext = format.layerToContext(layer);
+ node = parser.write_wmc_Layer(layerContext);
+ minList = parser.getElementsByTagNameNS(node, sldNS, "MinScaleDenominator");
+ t.eq(minList.length, 1, "(resolutions) node written with MinScaleDenominator");
+ t.eq(layer.maxScale.toPrecision(16), parser.getChildValue(minList[0]),
+ "(resolutions) node written with correct MinScaleDenominator value");
+ maxList = parser.getElementsByTagNameNS(node, sldNS, "MaxScaleDenominator");
+ t.eq(maxList.length, 1, "(resolutions) node written with MaxScaleDenominator");
+ t.eq(layer.minScale.toPrecision(16), parser.getChildValue(maxList[0]),
+ "(resolutions) node written with correct MaxScaleDenominator value");
+
+ layer = new OpenLayers.Layer.WMS(
+ "test", "http://foo", {},
+ {scales: [4, 2, 1], maxExtent: new OpenLayers.Bounds(1, 2, 3, 4)}
+ );
+ layer.minScale = Math.random();
+ layer.maxScale = Math.random();
+ layerContext = format.layerToContext(layer);
+ node = parser.write_wmc_Layer(layerContext);
+ minList = parser.getElementsByTagNameNS(node, sldNS, "MinScaleDenominator");
+ var f = new OpenLayers.Format.XML();
+ t.eq(minList.length, 1, "(scales) node written with MinScaleDenominator");
+ t.eq(layer.maxScale.toPrecision(16), parser.getChildValue(minList[0]),
+ "(scales) node written with correct MinScaleDenominator value");
+ maxList = parser.getElementsByTagNameNS(node, sldNS, "MaxScaleDenominator");
+ t.eq(maxList.length, 1, "(scales) node written with MaxScaleDenominator");
+ t.eq(layer.minScale.toPrecision(16), parser.getChildValue(maxList[0]),
+ "(scales) node written with correct MaxScaleDenominator value");
+
+ layer.metadataURL = 'http://foo';
+ layerContext = format.layerToContext(layer);
+ node = parser.write_wmc_Layer(layerContext);
+ t.eq(node.childNodes[3].localName || node.childNodes[3].nodeName.split(":").pop(),
+ 'MetadataURL', "MinScaleDenominator is written after MetadataURL, so third node should be MetadataURL");
+ t.eq(node.childNodes[4].localName || node.childNodes[4].nodeName.split(":").pop(),
+ 'MinScaleDenominator', "MinScaleDenominator is written after MetadataURL, so fourth node should be MinScaleDenominator");
+
+ }
+
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 512px; height: 256px;"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WMSCapabilities.html b/misc/openlayers/tests/Format/WMSCapabilities.html
new file mode 100644
index 0000000..a447bdd
--- /dev/null
+++ b/misc/openlayers/tests/Format/WMSCapabilities.html
@@ -0,0 +1,20 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+
+ t.plan(1);
+ var format = new OpenLayers.Format.WMSCapabilities({
+ version: "foo"
+ });
+ t.eq(format.version, "foo", "version set on format");
+
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WMSCapabilities/v1_1_1.html b/misc/openlayers/tests/Format/WMSCapabilities/v1_1_1.html
new file mode 100644
index 0000000..e3b0863
--- /dev/null
+++ b/misc/openlayers/tests/Format/WMSCapabilities/v1_1_1.html
@@ -0,0 +1,5209 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read_exception(t) {
+ t.plan(1);
+ var xml = document.getElementById("exceptionsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+ var format = new OpenLayers.Format.WMSCapabilities();
+ var obj = format.read(doc);
+ t.ok(!!obj.error, "Error reported correctly");
+ }
+
+ function test_read(t) {
+
+ t.plan(24);
+
+ var xml = document.getElementById("gssample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+ var format = new OpenLayers.Format.WMSCapabilities();
+ var obj = format.read(doc);
+
+ var capability = obj.capability;
+ t.ok(capability, "object contains capability property");
+
+ var getmap = capability.request.getmap;
+ t.eq(getmap.formats.length, 28, "getmap formats parsed");
+ t.eq(
+ getmap.href,
+ "http://publicus.opengeo.org:80/geoserver/wms?SERVICE=WMS&",
+ "getmap href parsed"
+ );
+ t.eq(
+ getmap.get.href,
+ getmap.href,
+ "getmap.get.href parsed"
+ );
+ t.eq(
+ getmap.post,
+ undefined,
+ "getmap.post not available"
+ );
+
+ var describelayer = capability.request.describelayer;
+ t.eq(
+ describelayer.href,
+ "http://publicus.opengeo.org:80/geoserver/wms?SERVICE=WMS&",
+ "describelayer href parsed"
+ );
+ t.eq(
+ describelayer.get.href,
+ describelayer.href,
+ "describelayer.get.href parsed"
+ );
+ t.eq(
+ describelayer.post,
+ undefined,
+ "describelayer.post not available"
+ );
+
+ var getfeatureinfo = capability.request.getfeatureinfo;
+ t.eq(
+ getfeatureinfo.href,
+ "http://publicus.opengeo.org:80/geoserver/wms?SERVICE=WMS&",
+ "getfeatureinfo href parsed"
+ );
+ t.eq(
+ getfeatureinfo.get.href,
+ getfeatureinfo.href,
+ "getmap.get.href parsed"
+ );
+ t.eq(
+ getfeatureinfo.post.href,
+ "http://publicus.opengeo.org:80/geoserver/wms?SERVICE=WMS&",
+ "getfeatureinfo.post set correctly"
+ );
+
+ t.ok(capability.layers, "layers parsed");
+ t.eq(capability.layers.length, 22, "correct number of layers parsed");
+
+ var layer = capability.layers[2];
+ t.eq(layer.infoFormats, ["text/plain", "text/html", "application/vnd.ogc.gml"], "infoFormats set on layer");
+ t.eq(layer.name, "tiger:tiger_roads", "[2] correct layer name");
+ t.eq(layer.prefix, "tiger", "[2] correct layer prefix");
+ t.eq(layer.title, "Manhattan (NY) roads", "[2] correct layer title");
+ t.eq(
+ layer["abstract"],
+ "Highly simplified road layout of Manhattan in New York..",
+ "[2] correct layer abstract"
+ );
+ t.eq(
+ layer.llbbox,
+ [-74.08769307536667, 40.660618924633326, -73.84653192463333, 40.90178007536667],
+ "[2] correct layer bbox"
+ );
+ t.eq(layer.styles.length, 1, "[2] correct styles length");
+ t.eq(layer.styles[0].name, "tiger_roads", "[2] correct style name");
+ t.eq(
+ layer.styles[0].legend.href,
+ "http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&FORMAT=image/png&WIDTH=20&HEIGHT=20&LAYER=tiger:tiger_roads",
+ "[2] correct legend url"
+ );
+ t.eq(
+ layer.styles[0].legend.format, "image/png",
+ "[2] correct legend format"
+ );
+ t.eq(layer.queryable, true, "[2] correct queryable attribute");
+
+
+ }
+
+ function test_layers(t) {
+
+ t.plan(24);
+
+ var xml = document.getElementById("ogcsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+
+ var obj = new OpenLayers.Format.WMSCapabilities().read(doc);
+ var capability = obj.capability;
+
+ var layers = {};
+ for (var i=0, len=capability.layers.length; i<len; i++) {
+ if ("name" in capability.layers[i]) {
+ layers[ capability.layers[i].name ] = capability.layers[i];
+ }
+ }
+
+ var rootlayer = capability.layers[ capability.layers.length - 1];
+
+ t.eq(rootlayer.srs,
+ {"EPSG:4326": true},
+ "SRS parsed correctly for root layer");
+ t.eq(layers["ROADS_RIVERS"].srs,
+ {"EPSG:4326": true, "EPSG:26986": true},
+ "Inheritance of SRS handled correctly when adding SRSes");
+ t.eq(layers["Temperature"].srs,
+ {"EPSG:4326": true},
+ "Inheritance of SRS handled correctly when redeclaring an inherited SRS");
+
+ var bbox = layers["ROADS_RIVERS"].bbox["EPSG:26986"];
+ t.eq(bbox.bbox,
+ [189000, 834000, 285000, 962000],
+ "Correct bbox from BoundingBox");
+ t.eq(bbox.res, {x: 1, y: 1}, "Correct resolution");
+ bbox = layers["ROADS_RIVERS"].bbox["EPSG:4326"];
+ t.eq(bbox.bbox,
+ [-71.63, 41.75, -70.78, 42.90],
+ "Correct bbox from BoundingBox (override)");
+ t.eq(bbox.res, {x: 0.01, y: 0.01}, "Correct resolution (override)");
+ bbox = layers["ROADS_1M"].bbox["EPSG:26986"];
+ t.eq(bbox.bbox,
+ [189000, 834000, 285000, 962000],
+ "Correctly inherited bbox");
+ t.eq(bbox.res, {x: 1, y: 1}, "Correctly inherited resolution");
+
+
+ var identifiers = layers["ROADS_RIVERS"].identifiers;
+ var authorities = layers["ROADS_RIVERS"].authorityURLs;
+
+ t.ok(identifiers, "got identifiers from layer ROADS_RIVERS");
+ t.ok("DIF_ID" in identifiers,
+ "authority attribute from Identifiers parsed correctly");
+ t.eq(identifiers["DIF_ID"],
+ "123456",
+ "Identifier value parsed correctly");
+ t.ok("DIF_ID" in authorities,
+ "AuthorityURLs parsed and inherited correctly");
+ t.eq(authorities["DIF_ID"],
+ "http://gcmd.gsfc.nasa.gov/difguide/whatisadif.html",
+ "OnlineResource in AuthorityURLs parsed correctly");
+
+ var featurelist = layers["ROADS_RIVERS"].featureListURL;
+ t.ok(featurelist, "layer has FeatureListURL");
+ t.eq(featurelist.format,
+ "application/vnd.ogc.se_xml",
+ "FeatureListURL format parsed correctly");
+ t.eq(featurelist.href,
+ "http://www.university.edu/data/roads_rivers.gml",
+ "FeatureListURL OnlineResource parsed correctly");
+
+ t.eq(layers["Pressure"].queryable,
+ true,
+ "queryable property inherited correctly");
+ t.eq(layers["ozone_image"].queryable,
+ false,
+ "queryable property has correct default value");
+ t.eq(layers["population"].cascaded,
+ 1,
+ "cascaded property parsed correctly");
+ t.eq(layers["ozone_image"].fixedWidth,
+ 512,
+ "fixedWidth property correctly parsed");
+ t.eq(layers["ozone_image"].fixedHeight,
+ 256,
+ "fixedHeight property correctly parsed");
+ t.eq(layers["ozone_image"].opaque,
+ true,
+ "opaque property parsed correctly");
+ t.eq(layers["ozone_image"].noSubsets,
+ true,
+ "noSubsets property parsed correctly");
+
+
+ }
+
+ function test_dimensions(t) {
+
+ t.plan(8);
+
+ var xml = document.getElementById("ogcsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+
+ var obj = new OpenLayers.Format.WMSCapabilities().read(doc);
+ var capability = obj.capability;
+
+ var layers = {};
+ for (var i=0, len=capability.layers.length; i<len; i++) {
+ if ("name" in capability.layers[i]) {
+ layers[ capability.layers[i].name ] = capability.layers[i];
+ }
+ }
+
+ var time = layers["Clouds"].dimensions.time;
+ t.eq(time["default"], "2000-08-22", "Default time value parsed correctly");
+ t.eq(time.values.length, 1, "Currect number of time extent values/periods");
+ t.eq(time.values[0], "1999-01-01/2000-08-22/P1D", "Time extent values parsed correctly");
+
+ var elevation = layers["Pressure"].dimensions.elevation;
+ t.eq(elevation.units, "EPSG:5030", "Dimension units parsed correctly");
+ t.eq(elevation["default"], "0", "Default elevation value parsed correctly");
+ t.eq(elevation.nearestVal, true, "NearestValue parsed correctly");
+ t.eq(elevation.multipleVal, false, "Absense of MultipleValues handled correctly");
+ t.eq(elevation.values,
+ ["0","1000","3000","5000","10000"],
+ "Parsing of comma-separated values done correctly");
+
+
+ }
+
+ function test_contactinfo(t) {
+ t.plan(15);
+
+ var xml = document.getElementById("ogcsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+
+ var obj = new OpenLayers.Format.WMSCapabilities().read(doc);
+ var service = obj.service;
+
+ var contactinfo = service.contactInformation;
+ t.ok(contactinfo, "object contains contactInformation property");
+
+ var personPrimary = contactinfo.personPrimary;
+ t.ok(personPrimary, "object contains personPrimary property");
+
+ t.eq(personPrimary.person, "Jeff deLaBeaujardiere", "ContactPerson parsed correctly");
+ t.eq(personPrimary.organization, "NASA", "ContactOrganization parsed correctly");
+
+ t.eq(contactinfo.position,
+ "Computer Scientist",
+ "ContactPosition parsed correctly");
+
+
+ var addr = contactinfo.contactAddress;
+ t.ok(addr, "object contains contactAddress property");
+
+ t.eq(addr.type, "postal", "AddressType parsed correctly");
+ t.eq(addr.address,
+ "NASA Goddard Space Flight Center, Code 933",
+ "Address parsed correctly");
+ t.eq(addr.city, "Greenbelt", "City parsed correctly");
+ t.eq(addr.stateOrProvince, "MD", "StateOrProvince parsed correctly");
+ t.eq(addr.postcode, "20771", "PostCode parsed correctly");
+ t.eq(addr.country, "USA", "Country parsed correctly");
+
+ t.eq(contactinfo.phone,
+ "+1 301 286-1569",
+ "ContactVoiceTelephone parsed correctly");
+ t.eq(contactinfo.fax,
+ "+1 301 286-1777",
+ "ContactFacsimileTelephone parsed correctly");
+ t.eq(contactinfo.email,
+ "delabeau@iniki.gsfc.nasa.gov",
+ "ContactElectronicMailAddress parsed correctly");
+ }
+
+ function test_feesAndConstraints(t) {
+ t.plan(2);
+
+ var xml = document.getElementById("gssample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+ var obj = new OpenLayers.Format.WMSCapabilities().read(doc);
+ var service = obj.service;
+
+ t.ok(! ("fees" in service), "Fees=none handled correctly");
+ t.ok(! ("accessConstraints" in service), "AccessConstraints=none handled correctly");
+ }
+
+ function test_requests(t) {
+ t.plan(13);
+
+ var xml = document.getElementById("gssample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+ var obj = new OpenLayers.Format.WMSCapabilities().read(doc);
+ var request = obj.capability.request;
+
+ t.ok(request, "request property exists");
+ t.ok("getmap" in request, "got GetMap request");
+
+ t.ok("getfeatureinfo" in request, "got GetFeatureInfo request");
+ t.eq(request.getfeatureinfo.formats,
+ ["text/plain", "text/html", "application/vnd.ogc.gml"],
+ "GetFeatureInfo formats correctly parsed");
+
+ t.ok("describelayer" in request, "got DescribeLayer request");
+
+ t.ok("getlegendgraphic" in request, "got GetLegendGraphic request");
+
+ var exception = obj.capability.exception;
+ t.ok(exception, "exception property exists");
+ t.eq(exception.formats,
+ ["application/vnd.ogc.se_xml"],
+ "Exception Format parsed");
+
+ var userSymbols = obj.capability.userSymbols;
+ t.ok(userSymbols, "userSymbols property exists");
+ t.eq(userSymbols.supportSLD, true, "supportSLD parsed");
+ t.eq(userSymbols.userLayer, true, "userLayer parsed");
+ t.eq(userSymbols.userStyle, true, "userStyle parsed");
+ t.eq(userSymbols.remoteWFS, true, "remoteWFS parsed");
+
+ }
+ function test_ogc(t) {
+ t.plan(16)
+
+ /*
+ * Set up
+ */
+
+ // needed for the minScale/maxScale test, see below
+ var dpi = OpenLayers.DOTS_PER_INCH;
+ OpenLayers.DOTS_PER_INCH = 90.710230403857;
+
+ var xml = document.getElementById("ogcsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+
+ var obj = new OpenLayers.Format.WMSCapabilities().read(doc);
+ var capability = obj.capability;
+
+ /*
+ * Test
+ */
+
+ var attribution = capability.layers[2].attribution;
+ t.eq(attribution.title, "State College University", "attribution title parsed correctly.");
+ t.eq(attribution.href, "http://www.university.edu/", "attribution href parsed correctly.")
+ t.eq(attribution.logo.href, "http://www.university.edu/icons/logo.gif", "attribution logo url parsed correctly.");
+ t.eq(attribution.logo.format, "image/gif", "attribution logo format parsed correctly.");
+ t.eq(attribution.logo.width, "100", "attribution logo width parsed correctly.");
+ t.eq(attribution.logo.height, "100", "attribution logo height parsed correctly.");
+
+ var keywords = capability.layers[0].keywords;
+ t.eq(keywords.length, 3, "layer has 3 keywords.");
+ t.eq(keywords[0], "road", "1st keyword parsed correctly.");
+
+ var metadataURLs = capability.layers[0].metadataURLs;
+ t.eq(metadataURLs.length, 2, "layer has 2 metadata urls.");
+ t.eq(metadataURLs[0].type, "FGDC", "type parsed correctly.");
+ t.eq(metadataURLs[0].format, "text/plain", "format parsed correctly.");
+ t.eq(metadataURLs[0].href, "http://www.university.edu/metadata/roads.txt", "href parsed correctly.");
+
+ /*
+ Test minScale and maxScale
+
+ For Mapserver
+
+ <ScaleHint min="0.395998292216226" max="98.9995730540565" />
+
+ corresponds to (RESOLUTION keyword in MAP file has value of 90.710230403857):
+
+ MAXSCALE 250000
+ MINSCALE 1000
+
+ */
+ t.eq(capability.layers[0].minScale, 250000, "layer.minScale is correct");
+ t.eq(capability.layers[0].maxScale, 1000, "layer.maxScale is correct");
+
+ t.eq(capability.layers[1].minScale, undefined, "layer.minScale for max='Infinity' is correct");
+ t.eq(capability.layers[1].maxScale, undefined, "layer.maxScale for min='0' is correct");
+ /*
+ * Tear down
+ */
+
+ OpenLayers.DOTS_PER_INCH = dpi;
+ }
+
+ </script>
+</head>
+<body>
+
+<!--
+OGC example below taken from
+http://schemas.opengis.net/wms/1.1.1/capabilities_1_1_1.xml
+Copyright © 1994-2008 Open Geospatial Consortium, Inc. All Rights Reserved.
+http://www.opengeospatial.org/ogc/document
+Changes:
+* fixed DTD URL
+* removed comments
+-->
+<div id="ogcsample"><!--
+<?xml version='1.0' encoding="UTF-8" standalone="no" ?>
+<!DOCTYPE WMT_MS_Capabilities SYSTEM
+ "http://schemas.opengis.net/wms/1.1.1/capabilities_1_1_1.dtd"
+ [
+ <!ELEMENT VendorSpecificCapabilities EMPTY>
+ ]>
+
+<WMT_MS_Capabilities version="1.1.1" updateSequence="0">
+<Service>
+
+ <Name>OGC:WMS</Name>
+ <Title>Acme Corp. Map Server</Title>
+ <Abstract>WMT Map Server maintained by Acme Corporation. Contact: webmaster@wmt.acme.com. High-quality maps showing roadrunner nests and possible ambush locations.</Abstract>
+ <KeywordList>
+
+ <Keyword>bird</Keyword>
+ <Keyword>roadrunner</Keyword>
+ <Keyword>ambush</Keyword>
+ </KeywordList>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
+ xlink:href="http://hostname/" />
+
+ <ContactInformation>
+ <ContactPersonPrimary>
+ <ContactPerson>Jeff deLaBeaujardiere</ContactPerson>
+ <ContactOrganization>NASA</ContactOrganization>
+ </ContactPersonPrimary>
+ <ContactPosition>Computer Scientist</ContactPosition>
+ <ContactAddress>
+
+ <AddressType>postal</AddressType>
+ <Address>NASA Goddard Space Flight Center, Code 933</Address>
+ <City>Greenbelt</City>
+ <StateOrProvince>MD</StateOrProvince>
+ <PostCode>20771</PostCode>
+ <Country>USA</Country>
+
+ </ContactAddress>
+ <ContactVoiceTelephone>+1 301 286-1569</ContactVoiceTelephone>
+ <ContactFacsimileTelephone>+1 301 286-1777</ContactFacsimileTelephone>
+ <ContactElectronicMailAddress>delabeau@iniki.gsfc.nasa.gov</ContactElectronicMailAddress>
+ </ContactInformation>
+ <Fees>none</Fees>
+
+ <AccessConstraints>none</AccessConstraints>
+</Service>
+<Capability>
+ <Request>
+ <GetCapabilities>
+ <Format>application/vnd.ogc.wms_xml</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://hostname:port/path" />
+ </Get>
+ <Post>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://hostname:port/path" />
+ </Post>
+ </HTTP>
+ </DCPType>
+
+ </GetCapabilities>
+ <GetMap>
+ <Format>image/gif</Format>
+ <Format>image/png</Format>
+ <Format>image/jpeg</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://hostname:port/path/get" />
+ </Get>
+ <Post>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://hostname:port/path/post" />
+ </Post>
+ </HTTP>
+ </DCPType>
+ </GetMap>
+ <GetFeatureInfo>
+ <Format>application/vnd.ogc.gml</Format>
+
+ <Format>text/plain</Format>
+ <Format>text/html</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://hostname:port/path" />
+ </Get>
+ </HTTP>
+
+ </DCPType>
+ </GetFeatureInfo>
+ <DescribeLayer>
+ <Format>application/vnd.ogc.gml</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://hostname:port/path" />
+
+ </Get>
+ </HTTP>
+ </DCPType>
+ </DescribeLayer>
+ </Request>
+ <Exception>
+ <Format>application/vnd.ogc.se_xml</Format>
+ <Format>application/vnd.ogc.se_inimage</Format>
+
+ <Format>application/vnd.ogc.se_blank</Format>
+ </Exception>
+ <VendorSpecificCapabilities />
+ <UserDefinedSymbolization SupportSLD="1" UserLayer="1" UserStyle="1"
+ RemoteWFS="1" />
+
+ <Layer>
+ <Title>Acme Corp. Map Server</Title>
+ <SRS>EPSG:4326</SRS>
+ <BoundingBox SRS="EPSG:4326"
+ minx="-1" miny="-1" maxx="1" maxy="1" resx="0.0" resy="0.0"/>
+ <AuthorityURL name="DIF_ID">
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
+ xlink:href="http://gcmd.gsfc.nasa.gov/difguide/whatisadif.html" />
+ </AuthorityURL>
+ <Layer>
+ <Name>ROADS_RIVERS</Name>
+ <Title>Roads and Rivers</Title>
+ <SRS>EPSG:26986</SRS>
+ <LatLonBoundingBox minx="-71.63" miny="41.75" maxx="-70.78" maxy="42.90"/>
+ <BoundingBox SRS="EPSG:4326"
+ minx="-71.63" miny="41.75" maxx="-70.78" maxy="42.90" resx="0.01" resy="0.01"/>
+
+ <BoundingBox SRS="EPSG:26986"
+ minx="189000" miny="834000" maxx="285000" maxy="962000" resx="1" resy="1" />
+ <Attribution>
+ <Title>State College University</Title>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
+ xlink:href="http://www.university.edu/" />
+ <LogoURL width="100" height="100">
+ <Format>image/gif</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://www.university.edu/icons/logo.gif" />
+
+ </LogoURL>
+ </Attribution>
+ <Identifier authority="DIF_ID">123456</Identifier>
+ <FeatureListURL>
+ <Format>application/vnd.ogc.se_xml</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
+ xlink:href="http://www.university.edu/data/roads_rivers.gml" />
+ </FeatureListURL>
+
+ <Style>
+ <Name>USGS</Name>
+ <Title>USGS Topo Map Style</Title>
+ <Abstract>Features are shown in a style like that used in USGS topographic maps.</Abstract>
+ <LegendURL width="72" height="72">
+ <Format>image/gif</Format>
+
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://www.university.edu/legends/usgs.gif" />
+ </LegendURL>
+ <StyleSheetURL>
+ <Format>text/xsl</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://www.university.edu/stylesheets/usgs.xsl" />
+ </StyleSheetURL>
+ </Style>
+
+
+ <Layer queryable="1">
+ <Name>ROADS_1M</Name>
+ <Title>Roads at 1:1M scale</Title>
+ <Abstract>Roads at a scale of 1 to 1 million.</Abstract>
+ <KeywordList>
+ <Keyword>road</Keyword>
+
+ <Keyword>transportation</Keyword>
+ <Keyword>atlas</Keyword>
+ </KeywordList>
+ <Identifier authority="DIF_ID">123456</Identifier>
+ <MetadataURL type="FGDC">
+ <Format>text/plain</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://www.university.edu/metadata/roads.txt" />
+ </MetadataURL>
+ <MetadataURL type="FGDC">
+ <Format>text/xml</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://www.university.edu/metadata/roads.xml" />
+ </MetadataURL>
+ <Style>
+
+ <Name>ATLAS</Name>
+ <Title>Road atlas style</Title>
+ <Abstract>Roads are shown in a style like that used in a commercial road atlas.</Abstract>
+ <LegendURL width="72" height="72">
+ <Format>image/gif</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://www.university.edu/legends/atlas.gif" />
+ </LegendURL>
+
+ </Style>
+ <ScaleHint min="0.395998292216226" max="98.9995730540565" />
+ </Layer>
+ <Layer queryable="1">
+ <Name>RIVERS_1M</Name>
+ <Title>Rivers at 1:1M scale</Title>
+ <Abstract>Rivers at a scale of 1 to 1 million.</Abstract>
+ <KeywordList>
+
+ <Keyword>river</Keyword>
+ <Keyword>canal</Keyword>
+ <Keyword>waterway</Keyword>
+ </KeywordList>
+ <ScaleHint min="0" max="Infinity" />
+ </Layer>
+ </Layer>
+ <Layer queryable="1">
+
+ <Title>Weather Forecast Data</Title>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="-180" miny="-90" maxx="180" maxy="90" />
+ <Dimension name="time" units="ISO8601" />
+ <Extent name="time" default="2000-08-22">1999-01-01/2000-08-22/P1D</Extent>
+
+ <Layer>
+ <Name>Clouds</Name>
+ <Title>Forecast cloud cover</Title>
+ </Layer>
+
+ <Layer>
+ <Name>Temperature</Name>
+ <Title>Forecast temperature</Title>
+ </Layer>
+
+ <Layer>
+ <Name>Pressure</Name>
+ <Title>Forecast barometric pressure</Title>
+ <Dimension name="time" units="ISO8601" />
+ <Dimension name="elevation" units="EPSG:5030" />
+ <Extent name="time" default="2000-08-22">1999-01-01/2000-08-22/P1D</Extent>
+ <Extent name="elevation" default="0" nearestValue="1">0,1000,3000,5000,10000</Extent>
+ </Layer>
+
+ </Layer>
+
+ <Layer opaque="1" noSubsets="1" fixedWidth="512" fixedHeight="256">
+ <Name>ozone_image</Name>
+ <Title>Global ozone distribution (1992)</Title>
+ <LatLonBoundingBox minx="-180" miny="-90" maxx="180" maxy="90" />
+ <Extent name="time" default="1992">1992</Extent>
+ </Layer>
+
+ <Layer cascaded="1">
+ <Name>population</Name>
+ <Title>World population, annual</Title>
+ <LatLonBoundingBox minx="-180" miny="-90" maxx="180" maxy="90" />
+ <Extent name="time" default="2000">1990/2000/P1Y</Extent>
+ </Layer>
+
+ </Layer>
+
+
+</Capability>
+</WMT_MS_Capabilities>
+--></div>
+<div id="exceptionsample"><!--
+<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
+<!DOCTYPE ServiceExceptionReport SYSTEM "http://schemas.opengis.net/wms/1.1.1/WMS_exception_1_1_1.dtd">
+<ServiceExceptionReport version="1.1.1"><ServiceException> Plain text message about an error. </ServiceException>
+</ServiceExceptionReport>
+--></div>
+<!--
+GeoServer example below taken from
+http://publicus.opengeo.org/geoserver/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetCapabilities
+Changes:
+* fixed DTD URL (publicus is no longer available)
+* removed comments
+-->
+<div id="gssample"><!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE WMT_MS_Capabilities SYSTEM "http://schemas.opengis.net/wms/1.1.1/capabilities_1_1_1.dtd">
+<WMT_MS_Capabilities version="1.1.1" updateSequence="57">
+ <Service>
+ <Name>OGC:WMS</Name>
+ <Title>GeoServer Web Map Service</Title>
+ <Abstract>A compliant implementation of WMS 1.1.1 plus most of the SLD 1.0 extension (dynamic styling). Can also generate PDF, SVG, KML, GeoRSS</Abstract>
+ <KeywordList>
+ <Keyword>WFS</Keyword>
+ <Keyword>WMS</Keyword>
+ <Keyword>GEOSERVER</Keyword>
+ </KeywordList>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms"/>
+ <ContactInformation>
+ <ContactPersonPrimary>
+ <ContactPerson>Claudius Ptolomaeus</ContactPerson>
+ <ContactOrganization>The ancient geographes INC</ContactOrganization>
+ </ContactPersonPrimary>
+ <ContactPosition>Chief geographer</ContactPosition>
+ <ContactAddress>
+ <AddressType>Work</AddressType>
+ <Address/>
+ <City>Alexandria</City>
+ <StateOrProvince/>
+ <PostCode/>
+ <Country>Egypt</Country>
+ </ContactAddress>
+ <ContactVoiceTelephone/>
+ <ContactFacsimileTelephone/>
+ <ContactElectronicMailAddress>claudius.ptolomaeus@gmail.com</ContactElectronicMailAddress>
+ </ContactInformation>
+ <Fees>NONE</Fees>
+ <AccessConstraints>NONE</AccessConstraints>
+ </Service>
+ <Capability>
+ <Request>
+ <GetCapabilities>
+ <Format>application/vnd.ogc.wms_xml</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms?SERVICE=WMS&amp;"/>
+ </Get>
+ <Post>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms?SERVICE=WMS&amp;"/>
+ </Post>
+ </HTTP>
+ </DCPType>
+ </GetCapabilities>
+ <GetMap>
+ <Format>image/png</Format>
+ <Format>application/atom xml</Format>
+ <Format>application/atom+xml</Format>
+ <Format>application/openlayers</Format>
+ <Format>application/pdf</Format>
+ <Format>application/rss xml</Format>
+ <Format>application/rss+xml</Format>
+ <Format>application/vnd.google-earth.kml</Format>
+ <Format>application/vnd.google-earth.kml xml</Format>
+ <Format>application/vnd.google-earth.kml+xml</Format>
+ <Format>application/vnd.google-earth.kmz</Format>
+ <Format>application/vnd.google-earth.kmz xml</Format>
+ <Format>application/vnd.google-earth.kmz+xml</Format>
+ <Format>atom</Format>
+ <Format>image/geotiff</Format>
+ <Format>image/geotiff8</Format>
+ <Format>image/gif</Format>
+ <Format>image/jpeg</Format>
+ <Format>image/png8</Format>
+ <Format>image/svg</Format>
+ <Format>image/svg xml</Format>
+ <Format>image/svg+xml</Format>
+ <Format>image/tiff</Format>
+ <Format>image/tiff8</Format>
+ <Format>kml</Format>
+ <Format>kmz</Format>
+ <Format>openlayers</Format>
+ <Format>rss</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms?SERVICE=WMS&amp;"/>
+ </Get>
+ </HTTP>
+ </DCPType>
+ </GetMap>
+ <GetFeatureInfo>
+ <Format>text/plain</Format>
+ <Format>text/html</Format>
+ <Format>application/vnd.ogc.gml</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms?SERVICE=WMS&amp;"/>
+ </Get>
+ <Post>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms?SERVICE=WMS&amp;"/>
+ </Post>
+ </HTTP>
+ </DCPType>
+ </GetFeatureInfo>
+ <DescribeLayer>
+ <Format>application/vnd.ogc.wms_xml</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms?SERVICE=WMS&amp;"/>
+ </Get>
+ </HTTP>
+ </DCPType>
+ </DescribeLayer>
+ <GetLegendGraphic>
+ <Format>image/png</Format>
+ <Format>image/jpeg</Format>
+ <Format>image/gif</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms?SERVICE=WMS&amp;"/>
+ </Get>
+ </HTTP>
+ </DCPType>
+ </GetLegendGraphic>
+ </Request>
+ <Exception>
+ <Format>application/vnd.ogc.se_xml</Format>
+ </Exception>
+ <UserDefinedSymbolization SupportSLD="1" UserLayer="1" UserStyle="1" RemoteWFS="1"/>
+ <Layer>
+ <Title>GeoServer Web Map Service</Title>
+ <Abstract>A compliant implementation of WMS 1.1.1 plus most of the SLD 1.0 extension (dynamic styling). Can also generate PDF, SVG, KML, GeoRSS</Abstract>
+ <SRS>EPSG:WGS84(DD)</SRS>
+ <SRS>EPSG:2000</SRS>
+ <SRS>EPSG:2001</SRS>
+ <SRS>EPSG:2002</SRS>
+ <SRS>EPSG:2003</SRS>
+ <SRS>EPSG:2004</SRS>
+ <SRS>EPSG:2005</SRS>
+ <SRS>EPSG:2006</SRS>
+ <SRS>EPSG:2007</SRS>
+ <SRS>EPSG:2008</SRS>
+ <SRS>EPSG:2009</SRS>
+ <SRS>EPSG:2010</SRS>
+ <SRS>EPSG:2011</SRS>
+ <SRS>EPSG:2012</SRS>
+ <SRS>EPSG:2013</SRS>
+ <SRS>EPSG:2014</SRS>
+ <SRS>EPSG:2015</SRS>
+ <SRS>EPSG:2016</SRS>
+ <SRS>EPSG:2017</SRS>
+ <SRS>EPSG:2018</SRS>
+ <SRS>EPSG:2019</SRS>
+ <SRS>EPSG:2020</SRS>
+ <SRS>EPSG:2021</SRS>
+ <SRS>EPSG:2022</SRS>
+ <SRS>EPSG:2023</SRS>
+ <SRS>EPSG:2024</SRS>
+ <SRS>EPSG:2025</SRS>
+ <SRS>EPSG:2026</SRS>
+ <SRS>EPSG:2027</SRS>
+ <SRS>EPSG:2028</SRS>
+ <SRS>EPSG:2029</SRS>
+ <SRS>EPSG:2030</SRS>
+ <SRS>EPSG:2031</SRS>
+ <SRS>EPSG:2032</SRS>
+ <SRS>EPSG:2033</SRS>
+ <SRS>EPSG:2034</SRS>
+ <SRS>EPSG:2035</SRS>
+ <SRS>EPSG:2036</SRS>
+ <SRS>EPSG:2037</SRS>
+ <SRS>EPSG:2038</SRS>
+ <SRS>EPSG:2039</SRS>
+ <SRS>EPSG:2040</SRS>
+ <SRS>EPSG:2041</SRS>
+ <SRS>EPSG:2042</SRS>
+ <SRS>EPSG:2043</SRS>
+ <SRS>EPSG:2044</SRS>
+ <SRS>EPSG:2045</SRS>
+ <SRS>EPSG:2046</SRS>
+ <SRS>EPSG:2047</SRS>
+ <SRS>EPSG:2048</SRS>
+ <SRS>EPSG:2049</SRS>
+ <SRS>EPSG:2050</SRS>
+ <SRS>EPSG:2051</SRS>
+ <SRS>EPSG:2052</SRS>
+ <SRS>EPSG:2053</SRS>
+ <SRS>EPSG:2054</SRS>
+ <SRS>EPSG:2055</SRS>
+ <SRS>EPSG:2056</SRS>
+ <SRS>EPSG:2057</SRS>
+ <SRS>EPSG:2058</SRS>
+ <SRS>EPSG:2059</SRS>
+ <SRS>EPSG:2060</SRS>
+ <SRS>EPSG:2061</SRS>
+ <SRS>EPSG:2062</SRS>
+ <SRS>EPSG:2063</SRS>
+ <SRS>EPSG:2064</SRS>
+ <SRS>EPSG:2065</SRS>
+ <SRS>EPSG:2066</SRS>
+ <SRS>EPSG:2067</SRS>
+ <SRS>EPSG:2068</SRS>
+ <SRS>EPSG:2069</SRS>
+ <SRS>EPSG:2070</SRS>
+ <SRS>EPSG:2071</SRS>
+ <SRS>EPSG:2072</SRS>
+ <SRS>EPSG:2073</SRS>
+ <SRS>EPSG:2074</SRS>
+ <SRS>EPSG:2075</SRS>
+ <SRS>EPSG:2076</SRS>
+ <SRS>EPSG:2077</SRS>
+ <SRS>EPSG:2078</SRS>
+ <SRS>EPSG:2079</SRS>
+ <SRS>EPSG:2080</SRS>
+ <SRS>EPSG:2081</SRS>
+ <SRS>EPSG:2082</SRS>
+ <SRS>EPSG:2083</SRS>
+ <SRS>EPSG:2084</SRS>
+ <SRS>EPSG:2085</SRS>
+ <SRS>EPSG:2086</SRS>
+ <SRS>EPSG:2087</SRS>
+ <SRS>EPSG:2088</SRS>
+ <SRS>EPSG:2089</SRS>
+ <SRS>EPSG:2090</SRS>
+ <SRS>EPSG:2091</SRS>
+ <SRS>EPSG:2092</SRS>
+ <SRS>EPSG:2093</SRS>
+ <SRS>EPSG:2094</SRS>
+ <SRS>EPSG:2095</SRS>
+ <SRS>EPSG:2096</SRS>
+ <SRS>EPSG:2097</SRS>
+ <SRS>EPSG:2098</SRS>
+ <SRS>EPSG:2099</SRS>
+ <SRS>EPSG:2100</SRS>
+ <SRS>EPSG:2101</SRS>
+ <SRS>EPSG:2102</SRS>
+ <SRS>EPSG:2103</SRS>
+ <SRS>EPSG:2104</SRS>
+ <SRS>EPSG:2105</SRS>
+ <SRS>EPSG:2106</SRS>
+ <SRS>EPSG:2107</SRS>
+ <SRS>EPSG:2108</SRS>
+ <SRS>EPSG:2109</SRS>
+ <SRS>EPSG:2110</SRS>
+ <SRS>EPSG:2111</SRS>
+ <SRS>EPSG:2112</SRS>
+ <SRS>EPSG:2113</SRS>
+ <SRS>EPSG:2114</SRS>
+ <SRS>EPSG:2115</SRS>
+ <SRS>EPSG:2116</SRS>
+ <SRS>EPSG:2117</SRS>
+ <SRS>EPSG:2118</SRS>
+ <SRS>EPSG:2119</SRS>
+ <SRS>EPSG:2120</SRS>
+ <SRS>EPSG:2121</SRS>
+ <SRS>EPSG:2122</SRS>
+ <SRS>EPSG:2123</SRS>
+ <SRS>EPSG:2124</SRS>
+ <SRS>EPSG:2125</SRS>
+ <SRS>EPSG:2126</SRS>
+ <SRS>EPSG:2127</SRS>
+ <SRS>EPSG:2128</SRS>
+ <SRS>EPSG:2129</SRS>
+ <SRS>EPSG:2130</SRS>
+ <SRS>EPSG:2131</SRS>
+ <SRS>EPSG:2132</SRS>
+ <SRS>EPSG:2133</SRS>
+ <SRS>EPSG:2134</SRS>
+ <SRS>EPSG:2135</SRS>
+ <SRS>EPSG:2136</SRS>
+ <SRS>EPSG:2137</SRS>
+ <SRS>EPSG:2138</SRS>
+ <SRS>EPSG:2139</SRS>
+ <SRS>EPSG:2140</SRS>
+ <SRS>EPSG:2141</SRS>
+ <SRS>EPSG:2142</SRS>
+ <SRS>EPSG:2143</SRS>
+ <SRS>EPSG:2144</SRS>
+ <SRS>EPSG:2145</SRS>
+ <SRS>EPSG:2146</SRS>
+ <SRS>EPSG:2147</SRS>
+ <SRS>EPSG:2148</SRS>
+ <SRS>EPSG:2149</SRS>
+ <SRS>EPSG:2150</SRS>
+ <SRS>EPSG:2151</SRS>
+ <SRS>EPSG:2152</SRS>
+ <SRS>EPSG:2153</SRS>
+ <SRS>EPSG:2154</SRS>
+ <SRS>EPSG:2155</SRS>
+ <SRS>EPSG:2156</SRS>
+ <SRS>EPSG:2157</SRS>
+ <SRS>EPSG:2158</SRS>
+ <SRS>EPSG:2159</SRS>
+ <SRS>EPSG:2160</SRS>
+ <SRS>EPSG:2161</SRS>
+ <SRS>EPSG:2162</SRS>
+ <SRS>EPSG:2163</SRS>
+ <SRS>EPSG:2164</SRS>
+ <SRS>EPSG:2165</SRS>
+ <SRS>EPSG:2166</SRS>
+ <SRS>EPSG:2167</SRS>
+ <SRS>EPSG:2168</SRS>
+ <SRS>EPSG:2169</SRS>
+ <SRS>EPSG:2170</SRS>
+ <SRS>EPSG:2171</SRS>
+ <SRS>EPSG:2172</SRS>
+ <SRS>EPSG:2173</SRS>
+ <SRS>EPSG:2174</SRS>
+ <SRS>EPSG:2175</SRS>
+ <SRS>EPSG:2176</SRS>
+ <SRS>EPSG:2177</SRS>
+ <SRS>EPSG:2178</SRS>
+ <SRS>EPSG:2179</SRS>
+ <SRS>EPSG:2180</SRS>
+ <SRS>EPSG:2188</SRS>
+ <SRS>EPSG:2189</SRS>
+ <SRS>EPSG:2190</SRS>
+ <SRS>EPSG:2191</SRS>
+ <SRS>EPSG:2192</SRS>
+ <SRS>EPSG:2193</SRS>
+ <SRS>EPSG:2194</SRS>
+ <SRS>EPSG:2195</SRS>
+ <SRS>EPSG:2196</SRS>
+ <SRS>EPSG:2197</SRS>
+ <SRS>EPSG:2198</SRS>
+ <SRS>EPSG:2199</SRS>
+ <SRS>EPSG:2200</SRS>
+ <SRS>EPSG:2201</SRS>
+ <SRS>EPSG:2202</SRS>
+ <SRS>EPSG:2203</SRS>
+ <SRS>EPSG:2204</SRS>
+ <SRS>EPSG:2205</SRS>
+ <SRS>EPSG:2206</SRS>
+ <SRS>EPSG:2207</SRS>
+ <SRS>EPSG:2208</SRS>
+ <SRS>EPSG:2209</SRS>
+ <SRS>EPSG:2210</SRS>
+ <SRS>EPSG:2211</SRS>
+ <SRS>EPSG:2212</SRS>
+ <SRS>EPSG:2213</SRS>
+ <SRS>EPSG:2214</SRS>
+ <SRS>EPSG:2215</SRS>
+ <SRS>EPSG:2216</SRS>
+ <SRS>EPSG:2217</SRS>
+ <SRS>EPSG:2218</SRS>
+ <SRS>EPSG:2219</SRS>
+ <SRS>EPSG:2220</SRS>
+ <SRS>EPSG:2221</SRS>
+ <SRS>EPSG:2222</SRS>
+ <SRS>EPSG:2223</SRS>
+ <SRS>EPSG:2224</SRS>
+ <SRS>EPSG:2225</SRS>
+ <SRS>EPSG:2226</SRS>
+ <SRS>EPSG:2227</SRS>
+ <SRS>EPSG:2228</SRS>
+ <SRS>EPSG:2229</SRS>
+ <SRS>EPSG:2230</SRS>
+ <SRS>EPSG:2231</SRS>
+ <SRS>EPSG:2232</SRS>
+ <SRS>EPSG:2233</SRS>
+ <SRS>EPSG:2234</SRS>
+ <SRS>EPSG:2235</SRS>
+ <SRS>EPSG:2236</SRS>
+ <SRS>EPSG:2237</SRS>
+ <SRS>EPSG:2238</SRS>
+ <SRS>EPSG:2239</SRS>
+ <SRS>EPSG:2240</SRS>
+ <SRS>EPSG:2241</SRS>
+ <SRS>EPSG:2242</SRS>
+ <SRS>EPSG:2243</SRS>
+ <SRS>EPSG:2244</SRS>
+ <SRS>EPSG:2245</SRS>
+ <SRS>EPSG:2246</SRS>
+ <SRS>EPSG:2247</SRS>
+ <SRS>EPSG:2248</SRS>
+ <SRS>EPSG:2249</SRS>
+ <SRS>EPSG:2250</SRS>
+ <SRS>EPSG:2251</SRS>
+ <SRS>EPSG:2252</SRS>
+ <SRS>EPSG:2253</SRS>
+ <SRS>EPSG:2254</SRS>
+ <SRS>EPSG:2255</SRS>
+ <SRS>EPSG:2256</SRS>
+ <SRS>EPSG:2257</SRS>
+ <SRS>EPSG:2258</SRS>
+ <SRS>EPSG:2259</SRS>
+ <SRS>EPSG:2260</SRS>
+ <SRS>EPSG:2261</SRS>
+ <SRS>EPSG:2262</SRS>
+ <SRS>EPSG:2263</SRS>
+ <SRS>EPSG:2264</SRS>
+ <SRS>EPSG:2265</SRS>
+ <SRS>EPSG:2266</SRS>
+ <SRS>EPSG:2267</SRS>
+ <SRS>EPSG:2268</SRS>
+ <SRS>EPSG:2269</SRS>
+ <SRS>EPSG:2270</SRS>
+ <SRS>EPSG:2271</SRS>
+ <SRS>EPSG:2272</SRS>
+ <SRS>EPSG:2273</SRS>
+ <SRS>EPSG:2274</SRS>
+ <SRS>EPSG:2275</SRS>
+ <SRS>EPSG:2276</SRS>
+ <SRS>EPSG:2277</SRS>
+ <SRS>EPSG:2278</SRS>
+ <SRS>EPSG:2279</SRS>
+ <SRS>EPSG:2280</SRS>
+ <SRS>EPSG:2281</SRS>
+ <SRS>EPSG:2282</SRS>
+ <SRS>EPSG:2283</SRS>
+ <SRS>EPSG:2284</SRS>
+ <SRS>EPSG:2285</SRS>
+ <SRS>EPSG:2286</SRS>
+ <SRS>EPSG:2287</SRS>
+ <SRS>EPSG:2288</SRS>
+ <SRS>EPSG:2289</SRS>
+ <SRS>EPSG:2290</SRS>
+ <SRS>EPSG:2291</SRS>
+ <SRS>EPSG:2292</SRS>
+ <SRS>EPSG:2294</SRS>
+ <SRS>EPSG:2295</SRS>
+ <SRS>EPSG:2296</SRS>
+ <SRS>EPSG:2297</SRS>
+ <SRS>EPSG:2298</SRS>
+ <SRS>EPSG:2299</SRS>
+ <SRS>EPSG:2300</SRS>
+ <SRS>EPSG:2301</SRS>
+ <SRS>EPSG:2302</SRS>
+ <SRS>EPSG:2303</SRS>
+ <SRS>EPSG:2304</SRS>
+ <SRS>EPSG:2305</SRS>
+ <SRS>EPSG:2306</SRS>
+ <SRS>EPSG:2307</SRS>
+ <SRS>EPSG:2308</SRS>
+ <SRS>EPSG:2309</SRS>
+ <SRS>EPSG:2310</SRS>
+ <SRS>EPSG:2311</SRS>
+ <SRS>EPSG:2312</SRS>
+ <SRS>EPSG:2313</SRS>
+ <SRS>EPSG:2314</SRS>
+ <SRS>EPSG:2315</SRS>
+ <SRS>EPSG:2316</SRS>
+ <SRS>EPSG:2317</SRS>
+ <SRS>EPSG:2318</SRS>
+ <SRS>EPSG:2319</SRS>
+ <SRS>EPSG:2320</SRS>
+ <SRS>EPSG:2321</SRS>
+ <SRS>EPSG:2322</SRS>
+ <SRS>EPSG:2323</SRS>
+ <SRS>EPSG:2324</SRS>
+ <SRS>EPSG:2325</SRS>
+ <SRS>EPSG:2326</SRS>
+ <SRS>EPSG:2327</SRS>
+ <SRS>EPSG:2328</SRS>
+ <SRS>EPSG:2329</SRS>
+ <SRS>EPSG:2330</SRS>
+ <SRS>EPSG:2331</SRS>
+ <SRS>EPSG:2332</SRS>
+ <SRS>EPSG:2333</SRS>
+ <SRS>EPSG:2334</SRS>
+ <SRS>EPSG:2335</SRS>
+ <SRS>EPSG:2336</SRS>
+ <SRS>EPSG:2337</SRS>
+ <SRS>EPSG:2338</SRS>
+ <SRS>EPSG:2339</SRS>
+ <SRS>EPSG:2340</SRS>
+ <SRS>EPSG:2341</SRS>
+ <SRS>EPSG:2342</SRS>
+ <SRS>EPSG:2343</SRS>
+ <SRS>EPSG:2344</SRS>
+ <SRS>EPSG:2345</SRS>
+ <SRS>EPSG:2346</SRS>
+ <SRS>EPSG:2347</SRS>
+ <SRS>EPSG:2348</SRS>
+ <SRS>EPSG:2349</SRS>
+ <SRS>EPSG:2350</SRS>
+ <SRS>EPSG:2351</SRS>
+ <SRS>EPSG:2352</SRS>
+ <SRS>EPSG:2353</SRS>
+ <SRS>EPSG:2354</SRS>
+ <SRS>EPSG:2355</SRS>
+ <SRS>EPSG:2356</SRS>
+ <SRS>EPSG:2357</SRS>
+ <SRS>EPSG:2358</SRS>
+ <SRS>EPSG:2359</SRS>
+ <SRS>EPSG:2360</SRS>
+ <SRS>EPSG:2361</SRS>
+ <SRS>EPSG:2362</SRS>
+ <SRS>EPSG:2363</SRS>
+ <SRS>EPSG:2364</SRS>
+ <SRS>EPSG:2365</SRS>
+ <SRS>EPSG:2366</SRS>
+ <SRS>EPSG:2367</SRS>
+ <SRS>EPSG:2368</SRS>
+ <SRS>EPSG:2369</SRS>
+ <SRS>EPSG:2370</SRS>
+ <SRS>EPSG:2371</SRS>
+ <SRS>EPSG:2372</SRS>
+ <SRS>EPSG:2373</SRS>
+ <SRS>EPSG:2374</SRS>
+ <SRS>EPSG:2375</SRS>
+ <SRS>EPSG:2376</SRS>
+ <SRS>EPSG:2377</SRS>
+ <SRS>EPSG:2378</SRS>
+ <SRS>EPSG:2379</SRS>
+ <SRS>EPSG:2380</SRS>
+ <SRS>EPSG:2381</SRS>
+ <SRS>EPSG:2382</SRS>
+ <SRS>EPSG:2383</SRS>
+ <SRS>EPSG:2384</SRS>
+ <SRS>EPSG:2385</SRS>
+ <SRS>EPSG:2386</SRS>
+ <SRS>EPSG:2387</SRS>
+ <SRS>EPSG:2388</SRS>
+ <SRS>EPSG:2389</SRS>
+ <SRS>EPSG:2390</SRS>
+ <SRS>EPSG:2391</SRS>
+ <SRS>EPSG:2392</SRS>
+ <SRS>EPSG:2393</SRS>
+ <SRS>EPSG:2394</SRS>
+ <SRS>EPSG:2395</SRS>
+ <SRS>EPSG:2396</SRS>
+ <SRS>EPSG:2397</SRS>
+ <SRS>EPSG:2398</SRS>
+ <SRS>EPSG:2399</SRS>
+ <SRS>EPSG:2400</SRS>
+ <SRS>EPSG:2401</SRS>
+ <SRS>EPSG:2402</SRS>
+ <SRS>EPSG:2403</SRS>
+ <SRS>EPSG:2404</SRS>
+ <SRS>EPSG:2405</SRS>
+ <SRS>EPSG:2406</SRS>
+ <SRS>EPSG:2407</SRS>
+ <SRS>EPSG:2408</SRS>
+ <SRS>EPSG:2409</SRS>
+ <SRS>EPSG:2410</SRS>
+ <SRS>EPSG:2411</SRS>
+ <SRS>EPSG:2412</SRS>
+ <SRS>EPSG:2413</SRS>
+ <SRS>EPSG:2414</SRS>
+ <SRS>EPSG:2415</SRS>
+ <SRS>EPSG:2416</SRS>
+ <SRS>EPSG:2417</SRS>
+ <SRS>EPSG:2418</SRS>
+ <SRS>EPSG:2419</SRS>
+ <SRS>EPSG:2420</SRS>
+ <SRS>EPSG:2421</SRS>
+ <SRS>EPSG:2422</SRS>
+ <SRS>EPSG:2423</SRS>
+ <SRS>EPSG:2424</SRS>
+ <SRS>EPSG:2425</SRS>
+ <SRS>EPSG:2426</SRS>
+ <SRS>EPSG:2427</SRS>
+ <SRS>EPSG:2428</SRS>
+ <SRS>EPSG:2429</SRS>
+ <SRS>EPSG:2430</SRS>
+ <SRS>EPSG:2431</SRS>
+ <SRS>EPSG:2432</SRS>
+ <SRS>EPSG:2433</SRS>
+ <SRS>EPSG:2434</SRS>
+ <SRS>EPSG:2435</SRS>
+ <SRS>EPSG:2436</SRS>
+ <SRS>EPSG:2437</SRS>
+ <SRS>EPSG:2438</SRS>
+ <SRS>EPSG:2439</SRS>
+ <SRS>EPSG:2440</SRS>
+ <SRS>EPSG:2441</SRS>
+ <SRS>EPSG:2442</SRS>
+ <SRS>EPSG:2443</SRS>
+ <SRS>EPSG:2444</SRS>
+ <SRS>EPSG:2445</SRS>
+ <SRS>EPSG:2446</SRS>
+ <SRS>EPSG:2447</SRS>
+ <SRS>EPSG:2448</SRS>
+ <SRS>EPSG:2449</SRS>
+ <SRS>EPSG:2450</SRS>
+ <SRS>EPSG:2451</SRS>
+ <SRS>EPSG:2452</SRS>
+ <SRS>EPSG:2453</SRS>
+ <SRS>EPSG:2454</SRS>
+ <SRS>EPSG:2455</SRS>
+ <SRS>EPSG:2456</SRS>
+ <SRS>EPSG:2457</SRS>
+ <SRS>EPSG:2458</SRS>
+ <SRS>EPSG:2459</SRS>
+ <SRS>EPSG:2460</SRS>
+ <SRS>EPSG:2461</SRS>
+ <SRS>EPSG:2462</SRS>
+ <SRS>EPSG:2463</SRS>
+ <SRS>EPSG:2464</SRS>
+ <SRS>EPSG:2465</SRS>
+ <SRS>EPSG:2466</SRS>
+ <SRS>EPSG:2467</SRS>
+ <SRS>EPSG:2468</SRS>
+ <SRS>EPSG:2469</SRS>
+ <SRS>EPSG:2470</SRS>
+ <SRS>EPSG:2471</SRS>
+ <SRS>EPSG:2472</SRS>
+ <SRS>EPSG:2473</SRS>
+ <SRS>EPSG:2474</SRS>
+ <SRS>EPSG:2475</SRS>
+ <SRS>EPSG:2476</SRS>
+ <SRS>EPSG:2477</SRS>
+ <SRS>EPSG:2478</SRS>
+ <SRS>EPSG:2479</SRS>
+ <SRS>EPSG:2480</SRS>
+ <SRS>EPSG:2481</SRS>
+ <SRS>EPSG:2482</SRS>
+ <SRS>EPSG:2483</SRS>
+ <SRS>EPSG:2484</SRS>
+ <SRS>EPSG:2485</SRS>
+ <SRS>EPSG:2486</SRS>
+ <SRS>EPSG:2487</SRS>
+ <SRS>EPSG:2488</SRS>
+ <SRS>EPSG:2489</SRS>
+ <SRS>EPSG:2490</SRS>
+ <SRS>EPSG:2491</SRS>
+ <SRS>EPSG:2492</SRS>
+ <SRS>EPSG:2493</SRS>
+ <SRS>EPSG:2494</SRS>
+ <SRS>EPSG:2495</SRS>
+ <SRS>EPSG:2496</SRS>
+ <SRS>EPSG:2497</SRS>
+ <SRS>EPSG:2498</SRS>
+ <SRS>EPSG:2499</SRS>
+ <SRS>EPSG:2500</SRS>
+ <SRS>EPSG:2501</SRS>
+ <SRS>EPSG:2502</SRS>
+ <SRS>EPSG:2503</SRS>
+ <SRS>EPSG:2504</SRS>
+ <SRS>EPSG:2505</SRS>
+ <SRS>EPSG:2506</SRS>
+ <SRS>EPSG:2507</SRS>
+ <SRS>EPSG:2508</SRS>
+ <SRS>EPSG:2509</SRS>
+ <SRS>EPSG:2510</SRS>
+ <SRS>EPSG:2511</SRS>
+ <SRS>EPSG:2512</SRS>
+ <SRS>EPSG:2513</SRS>
+ <SRS>EPSG:2514</SRS>
+ <SRS>EPSG:2515</SRS>
+ <SRS>EPSG:2516</SRS>
+ <SRS>EPSG:2517</SRS>
+ <SRS>EPSG:2518</SRS>
+ <SRS>EPSG:2519</SRS>
+ <SRS>EPSG:2520</SRS>
+ <SRS>EPSG:2521</SRS>
+ <SRS>EPSG:2522</SRS>
+ <SRS>EPSG:2523</SRS>
+ <SRS>EPSG:2524</SRS>
+ <SRS>EPSG:2525</SRS>
+ <SRS>EPSG:2526</SRS>
+ <SRS>EPSG:2527</SRS>
+ <SRS>EPSG:2528</SRS>
+ <SRS>EPSG:2529</SRS>
+ <SRS>EPSG:2530</SRS>
+ <SRS>EPSG:2531</SRS>
+ <SRS>EPSG:2532</SRS>
+ <SRS>EPSG:2533</SRS>
+ <SRS>EPSG:2534</SRS>
+ <SRS>EPSG:2535</SRS>
+ <SRS>EPSG:2536</SRS>
+ <SRS>EPSG:2537</SRS>
+ <SRS>EPSG:2538</SRS>
+ <SRS>EPSG:2539</SRS>
+ <SRS>EPSG:2540</SRS>
+ <SRS>EPSG:2541</SRS>
+ <SRS>EPSG:2542</SRS>
+ <SRS>EPSG:2543</SRS>
+ <SRS>EPSG:2544</SRS>
+ <SRS>EPSG:2545</SRS>
+ <SRS>EPSG:2546</SRS>
+ <SRS>EPSG:2547</SRS>
+ <SRS>EPSG:2548</SRS>
+ <SRS>EPSG:2549</SRS>
+ <SRS>EPSG:2550</SRS>
+ <SRS>EPSG:2551</SRS>
+ <SRS>EPSG:2552</SRS>
+ <SRS>EPSG:2553</SRS>
+ <SRS>EPSG:2554</SRS>
+ <SRS>EPSG:2555</SRS>
+ <SRS>EPSG:2556</SRS>
+ <SRS>EPSG:2557</SRS>
+ <SRS>EPSG:2558</SRS>
+ <SRS>EPSG:2559</SRS>
+ <SRS>EPSG:2560</SRS>
+ <SRS>EPSG:2561</SRS>
+ <SRS>EPSG:2562</SRS>
+ <SRS>EPSG:2563</SRS>
+ <SRS>EPSG:2564</SRS>
+ <SRS>EPSG:2565</SRS>
+ <SRS>EPSG:2566</SRS>
+ <SRS>EPSG:2567</SRS>
+ <SRS>EPSG:2568</SRS>
+ <SRS>EPSG:2569</SRS>
+ <SRS>EPSG:2570</SRS>
+ <SRS>EPSG:2571</SRS>
+ <SRS>EPSG:2572</SRS>
+ <SRS>EPSG:2573</SRS>
+ <SRS>EPSG:2574</SRS>
+ <SRS>EPSG:2575</SRS>
+ <SRS>EPSG:2576</SRS>
+ <SRS>EPSG:2577</SRS>
+ <SRS>EPSG:2578</SRS>
+ <SRS>EPSG:2579</SRS>
+ <SRS>EPSG:2580</SRS>
+ <SRS>EPSG:2581</SRS>
+ <SRS>EPSG:2582</SRS>
+ <SRS>EPSG:2583</SRS>
+ <SRS>EPSG:2584</SRS>
+ <SRS>EPSG:2585</SRS>
+ <SRS>EPSG:2586</SRS>
+ <SRS>EPSG:2587</SRS>
+ <SRS>EPSG:2588</SRS>
+ <SRS>EPSG:2589</SRS>
+ <SRS>EPSG:2590</SRS>
+ <SRS>EPSG:2591</SRS>
+ <SRS>EPSG:2592</SRS>
+ <SRS>EPSG:2593</SRS>
+ <SRS>EPSG:2594</SRS>
+ <SRS>EPSG:2595</SRS>
+ <SRS>EPSG:2596</SRS>
+ <SRS>EPSG:2597</SRS>
+ <SRS>EPSG:2598</SRS>
+ <SRS>EPSG:2599</SRS>
+ <SRS>EPSG:2600</SRS>
+ <SRS>EPSG:2601</SRS>
+ <SRS>EPSG:2602</SRS>
+ <SRS>EPSG:2603</SRS>
+ <SRS>EPSG:2604</SRS>
+ <SRS>EPSG:2605</SRS>
+ <SRS>EPSG:2606</SRS>
+ <SRS>EPSG:2607</SRS>
+ <SRS>EPSG:2608</SRS>
+ <SRS>EPSG:2609</SRS>
+ <SRS>EPSG:2610</SRS>
+ <SRS>EPSG:2611</SRS>
+ <SRS>EPSG:2612</SRS>
+ <SRS>EPSG:2613</SRS>
+ <SRS>EPSG:2614</SRS>
+ <SRS>EPSG:2615</SRS>
+ <SRS>EPSG:2616</SRS>
+ <SRS>EPSG:2617</SRS>
+ <SRS>EPSG:2618</SRS>
+ <SRS>EPSG:2619</SRS>
+ <SRS>EPSG:2620</SRS>
+ <SRS>EPSG:2621</SRS>
+ <SRS>EPSG:2622</SRS>
+ <SRS>EPSG:2623</SRS>
+ <SRS>EPSG:2624</SRS>
+ <SRS>EPSG:2625</SRS>
+ <SRS>EPSG:2626</SRS>
+ <SRS>EPSG:2627</SRS>
+ <SRS>EPSG:2628</SRS>
+ <SRS>EPSG:2629</SRS>
+ <SRS>EPSG:2630</SRS>
+ <SRS>EPSG:2631</SRS>
+ <SRS>EPSG:2632</SRS>
+ <SRS>EPSG:2633</SRS>
+ <SRS>EPSG:2634</SRS>
+ <SRS>EPSG:2635</SRS>
+ <SRS>EPSG:2636</SRS>
+ <SRS>EPSG:2637</SRS>
+ <SRS>EPSG:2638</SRS>
+ <SRS>EPSG:2639</SRS>
+ <SRS>EPSG:2640</SRS>
+ <SRS>EPSG:2641</SRS>
+ <SRS>EPSG:2642</SRS>
+ <SRS>EPSG:2643</SRS>
+ <SRS>EPSG:2644</SRS>
+ <SRS>EPSG:2645</SRS>
+ <SRS>EPSG:2646</SRS>
+ <SRS>EPSG:2647</SRS>
+ <SRS>EPSG:2648</SRS>
+ <SRS>EPSG:2649</SRS>
+ <SRS>EPSG:2650</SRS>
+ <SRS>EPSG:2651</SRS>
+ <SRS>EPSG:2652</SRS>
+ <SRS>EPSG:2653</SRS>
+ <SRS>EPSG:2654</SRS>
+ <SRS>EPSG:2655</SRS>
+ <SRS>EPSG:2656</SRS>
+ <SRS>EPSG:2657</SRS>
+ <SRS>EPSG:2658</SRS>
+ <SRS>EPSG:2659</SRS>
+ <SRS>EPSG:2660</SRS>
+ <SRS>EPSG:2661</SRS>
+ <SRS>EPSG:2662</SRS>
+ <SRS>EPSG:2663</SRS>
+ <SRS>EPSG:2664</SRS>
+ <SRS>EPSG:2665</SRS>
+ <SRS>EPSG:2666</SRS>
+ <SRS>EPSG:2667</SRS>
+ <SRS>EPSG:2668</SRS>
+ <SRS>EPSG:2669</SRS>
+ <SRS>EPSG:2670</SRS>
+ <SRS>EPSG:2671</SRS>
+ <SRS>EPSG:2672</SRS>
+ <SRS>EPSG:2673</SRS>
+ <SRS>EPSG:2674</SRS>
+ <SRS>EPSG:2675</SRS>
+ <SRS>EPSG:2676</SRS>
+ <SRS>EPSG:2677</SRS>
+ <SRS>EPSG:2678</SRS>
+ <SRS>EPSG:2679</SRS>
+ <SRS>EPSG:2680</SRS>
+ <SRS>EPSG:2681</SRS>
+ <SRS>EPSG:2682</SRS>
+ <SRS>EPSG:2683</SRS>
+ <SRS>EPSG:2684</SRS>
+ <SRS>EPSG:2685</SRS>
+ <SRS>EPSG:2686</SRS>
+ <SRS>EPSG:2687</SRS>
+ <SRS>EPSG:2688</SRS>
+ <SRS>EPSG:2689</SRS>
+ <SRS>EPSG:2690</SRS>
+ <SRS>EPSG:2691</SRS>
+ <SRS>EPSG:2692</SRS>
+ <SRS>EPSG:2693</SRS>
+ <SRS>EPSG:2694</SRS>
+ <SRS>EPSG:2695</SRS>
+ <SRS>EPSG:2696</SRS>
+ <SRS>EPSG:2697</SRS>
+ <SRS>EPSG:2698</SRS>
+ <SRS>EPSG:2699</SRS>
+ <SRS>EPSG:2700</SRS>
+ <SRS>EPSG:2701</SRS>
+ <SRS>EPSG:2702</SRS>
+ <SRS>EPSG:2703</SRS>
+ <SRS>EPSG:2704</SRS>
+ <SRS>EPSG:2705</SRS>
+ <SRS>EPSG:2706</SRS>
+ <SRS>EPSG:2707</SRS>
+ <SRS>EPSG:2708</SRS>
+ <SRS>EPSG:2709</SRS>
+ <SRS>EPSG:2710</SRS>
+ <SRS>EPSG:2711</SRS>
+ <SRS>EPSG:2712</SRS>
+ <SRS>EPSG:2713</SRS>
+ <SRS>EPSG:2714</SRS>
+ <SRS>EPSG:2715</SRS>
+ <SRS>EPSG:2716</SRS>
+ <SRS>EPSG:2717</SRS>
+ <SRS>EPSG:2718</SRS>
+ <SRS>EPSG:2719</SRS>
+ <SRS>EPSG:2720</SRS>
+ <SRS>EPSG:2721</SRS>
+ <SRS>EPSG:2722</SRS>
+ <SRS>EPSG:2723</SRS>
+ <SRS>EPSG:2724</SRS>
+ <SRS>EPSG:2725</SRS>
+ <SRS>EPSG:2726</SRS>
+ <SRS>EPSG:2727</SRS>
+ <SRS>EPSG:2728</SRS>
+ <SRS>EPSG:2729</SRS>
+ <SRS>EPSG:2730</SRS>
+ <SRS>EPSG:2731</SRS>
+ <SRS>EPSG:2732</SRS>
+ <SRS>EPSG:2733</SRS>
+ <SRS>EPSG:2734</SRS>
+ <SRS>EPSG:2735</SRS>
+ <SRS>EPSG:2736</SRS>
+ <SRS>EPSG:2737</SRS>
+ <SRS>EPSG:2738</SRS>
+ <SRS>EPSG:2739</SRS>
+ <SRS>EPSG:2740</SRS>
+ <SRS>EPSG:2741</SRS>
+ <SRS>EPSG:2742</SRS>
+ <SRS>EPSG:2743</SRS>
+ <SRS>EPSG:2744</SRS>
+ <SRS>EPSG:2745</SRS>
+ <SRS>EPSG:2746</SRS>
+ <SRS>EPSG:2747</SRS>
+ <SRS>EPSG:2748</SRS>
+ <SRS>EPSG:2749</SRS>
+ <SRS>EPSG:2750</SRS>
+ <SRS>EPSG:2751</SRS>
+ <SRS>EPSG:2752</SRS>
+ <SRS>EPSG:2753</SRS>
+ <SRS>EPSG:2754</SRS>
+ <SRS>EPSG:2755</SRS>
+ <SRS>EPSG:2756</SRS>
+ <SRS>EPSG:2757</SRS>
+ <SRS>EPSG:2758</SRS>
+ <SRS>EPSG:2759</SRS>
+ <SRS>EPSG:2760</SRS>
+ <SRS>EPSG:2761</SRS>
+ <SRS>EPSG:2762</SRS>
+ <SRS>EPSG:2763</SRS>
+ <SRS>EPSG:2764</SRS>
+ <SRS>EPSG:2765</SRS>
+ <SRS>EPSG:2766</SRS>
+ <SRS>EPSG:2767</SRS>
+ <SRS>EPSG:2768</SRS>
+ <SRS>EPSG:2769</SRS>
+ <SRS>EPSG:2770</SRS>
+ <SRS>EPSG:2771</SRS>
+ <SRS>EPSG:2772</SRS>
+ <SRS>EPSG:2773</SRS>
+ <SRS>EPSG:2774</SRS>
+ <SRS>EPSG:2775</SRS>
+ <SRS>EPSG:2776</SRS>
+ <SRS>EPSG:2777</SRS>
+ <SRS>EPSG:2778</SRS>
+ <SRS>EPSG:2779</SRS>
+ <SRS>EPSG:2780</SRS>
+ <SRS>EPSG:2781</SRS>
+ <SRS>EPSG:2782</SRS>
+ <SRS>EPSG:2783</SRS>
+ <SRS>EPSG:2784</SRS>
+ <SRS>EPSG:2785</SRS>
+ <SRS>EPSG:2786</SRS>
+ <SRS>EPSG:2787</SRS>
+ <SRS>EPSG:2788</SRS>
+ <SRS>EPSG:2789</SRS>
+ <SRS>EPSG:2790</SRS>
+ <SRS>EPSG:2791</SRS>
+ <SRS>EPSG:2792</SRS>
+ <SRS>EPSG:2793</SRS>
+ <SRS>EPSG:2794</SRS>
+ <SRS>EPSG:2795</SRS>
+ <SRS>EPSG:2796</SRS>
+ <SRS>EPSG:2797</SRS>
+ <SRS>EPSG:2798</SRS>
+ <SRS>EPSG:2799</SRS>
+ <SRS>EPSG:2800</SRS>
+ <SRS>EPSG:2801</SRS>
+ <SRS>EPSG:2802</SRS>
+ <SRS>EPSG:2803</SRS>
+ <SRS>EPSG:2804</SRS>
+ <SRS>EPSG:2805</SRS>
+ <SRS>EPSG:2806</SRS>
+ <SRS>EPSG:2807</SRS>
+ <SRS>EPSG:2808</SRS>
+ <SRS>EPSG:2809</SRS>
+ <SRS>EPSG:2810</SRS>
+ <SRS>EPSG:2811</SRS>
+ <SRS>EPSG:2812</SRS>
+ <SRS>EPSG:2813</SRS>
+ <SRS>EPSG:2814</SRS>
+ <SRS>EPSG:2815</SRS>
+ <SRS>EPSG:2816</SRS>
+ <SRS>EPSG:2817</SRS>
+ <SRS>EPSG:2818</SRS>
+ <SRS>EPSG:2819</SRS>
+ <SRS>EPSG:2820</SRS>
+ <SRS>EPSG:2821</SRS>
+ <SRS>EPSG:2822</SRS>
+ <SRS>EPSG:2823</SRS>
+ <SRS>EPSG:2824</SRS>
+ <SRS>EPSG:2825</SRS>
+ <SRS>EPSG:2826</SRS>
+ <SRS>EPSG:2827</SRS>
+ <SRS>EPSG:2828</SRS>
+ <SRS>EPSG:2829</SRS>
+ <SRS>EPSG:2830</SRS>
+ <SRS>EPSG:2831</SRS>
+ <SRS>EPSG:2832</SRS>
+ <SRS>EPSG:2833</SRS>
+ <SRS>EPSG:2834</SRS>
+ <SRS>EPSG:2835</SRS>
+ <SRS>EPSG:2836</SRS>
+ <SRS>EPSG:2837</SRS>
+ <SRS>EPSG:2838</SRS>
+ <SRS>EPSG:2839</SRS>
+ <SRS>EPSG:2840</SRS>
+ <SRS>EPSG:2841</SRS>
+ <SRS>EPSG:2842</SRS>
+ <SRS>EPSG:2843</SRS>
+ <SRS>EPSG:2844</SRS>
+ <SRS>EPSG:2845</SRS>
+ <SRS>EPSG:2846</SRS>
+ <SRS>EPSG:2847</SRS>
+ <SRS>EPSG:2848</SRS>
+ <SRS>EPSG:2849</SRS>
+ <SRS>EPSG:2850</SRS>
+ <SRS>EPSG:2851</SRS>
+ <SRS>EPSG:2852</SRS>
+ <SRS>EPSG:2853</SRS>
+ <SRS>EPSG:2854</SRS>
+ <SRS>EPSG:2855</SRS>
+ <SRS>EPSG:2856</SRS>
+ <SRS>EPSG:2857</SRS>
+ <SRS>EPSG:2858</SRS>
+ <SRS>EPSG:2859</SRS>
+ <SRS>EPSG:2860</SRS>
+ <SRS>EPSG:2861</SRS>
+ <SRS>EPSG:2862</SRS>
+ <SRS>EPSG:2863</SRS>
+ <SRS>EPSG:2864</SRS>
+ <SRS>EPSG:2865</SRS>
+ <SRS>EPSG:2866</SRS>
+ <SRS>EPSG:2867</SRS>
+ <SRS>EPSG:2868</SRS>
+ <SRS>EPSG:2869</SRS>
+ <SRS>EPSG:2870</SRS>
+ <SRS>EPSG:2871</SRS>
+ <SRS>EPSG:2872</SRS>
+ <SRS>EPSG:2873</SRS>
+ <SRS>EPSG:2874</SRS>
+ <SRS>EPSG:2875</SRS>
+ <SRS>EPSG:2876</SRS>
+ <SRS>EPSG:2877</SRS>
+ <SRS>EPSG:2878</SRS>
+ <SRS>EPSG:2879</SRS>
+ <SRS>EPSG:2880</SRS>
+ <SRS>EPSG:2881</SRS>
+ <SRS>EPSG:2882</SRS>
+ <SRS>EPSG:2883</SRS>
+ <SRS>EPSG:2884</SRS>
+ <SRS>EPSG:2885</SRS>
+ <SRS>EPSG:2886</SRS>
+ <SRS>EPSG:2887</SRS>
+ <SRS>EPSG:2888</SRS>
+ <SRS>EPSG:2889</SRS>
+ <SRS>EPSG:2890</SRS>
+ <SRS>EPSG:2891</SRS>
+ <SRS>EPSG:2892</SRS>
+ <SRS>EPSG:2893</SRS>
+ <SRS>EPSG:2894</SRS>
+ <SRS>EPSG:2895</SRS>
+ <SRS>EPSG:2896</SRS>
+ <SRS>EPSG:2897</SRS>
+ <SRS>EPSG:2898</SRS>
+ <SRS>EPSG:2899</SRS>
+ <SRS>EPSG:2900</SRS>
+ <SRS>EPSG:2901</SRS>
+ <SRS>EPSG:2902</SRS>
+ <SRS>EPSG:2903</SRS>
+ <SRS>EPSG:2904</SRS>
+ <SRS>EPSG:2905</SRS>
+ <SRS>EPSG:2906</SRS>
+ <SRS>EPSG:2907</SRS>
+ <SRS>EPSG:2908</SRS>
+ <SRS>EPSG:2909</SRS>
+ <SRS>EPSG:2910</SRS>
+ <SRS>EPSG:2911</SRS>
+ <SRS>EPSG:2912</SRS>
+ <SRS>EPSG:2913</SRS>
+ <SRS>EPSG:2914</SRS>
+ <SRS>EPSG:2915</SRS>
+ <SRS>EPSG:2916</SRS>
+ <SRS>EPSG:2917</SRS>
+ <SRS>EPSG:2918</SRS>
+ <SRS>EPSG:2919</SRS>
+ <SRS>EPSG:2920</SRS>
+ <SRS>EPSG:2921</SRS>
+ <SRS>EPSG:2922</SRS>
+ <SRS>EPSG:2923</SRS>
+ <SRS>EPSG:2924</SRS>
+ <SRS>EPSG:2925</SRS>
+ <SRS>EPSG:2926</SRS>
+ <SRS>EPSG:2927</SRS>
+ <SRS>EPSG:2928</SRS>
+ <SRS>EPSG:2929</SRS>
+ <SRS>EPSG:2930</SRS>
+ <SRS>EPSG:2931</SRS>
+ <SRS>EPSG:2932</SRS>
+ <SRS>EPSG:2933</SRS>
+ <SRS>EPSG:2934</SRS>
+ <SRS>EPSG:2935</SRS>
+ <SRS>EPSG:2936</SRS>
+ <SRS>EPSG:2937</SRS>
+ <SRS>EPSG:2938</SRS>
+ <SRS>EPSG:2939</SRS>
+ <SRS>EPSG:2940</SRS>
+ <SRS>EPSG:2941</SRS>
+ <SRS>EPSG:2942</SRS>
+ <SRS>EPSG:2943</SRS>
+ <SRS>EPSG:2944</SRS>
+ <SRS>EPSG:2945</SRS>
+ <SRS>EPSG:2946</SRS>
+ <SRS>EPSG:2947</SRS>
+ <SRS>EPSG:2948</SRS>
+ <SRS>EPSG:2949</SRS>
+ <SRS>EPSG:2950</SRS>
+ <SRS>EPSG:2951</SRS>
+ <SRS>EPSG:2952</SRS>
+ <SRS>EPSG:2953</SRS>
+ <SRS>EPSG:2954</SRS>
+ <SRS>EPSG:2955</SRS>
+ <SRS>EPSG:2956</SRS>
+ <SRS>EPSG:2957</SRS>
+ <SRS>EPSG:2958</SRS>
+ <SRS>EPSG:2959</SRS>
+ <SRS>EPSG:2960</SRS>
+ <SRS>EPSG:2961</SRS>
+ <SRS>EPSG:2962</SRS>
+ <SRS>EPSG:2963</SRS>
+ <SRS>EPSG:2964</SRS>
+ <SRS>EPSG:2965</SRS>
+ <SRS>EPSG:2966</SRS>
+ <SRS>EPSG:2967</SRS>
+ <SRS>EPSG:2968</SRS>
+ <SRS>EPSG:2969</SRS>
+ <SRS>EPSG:2970</SRS>
+ <SRS>EPSG:2971</SRS>
+ <SRS>EPSG:2972</SRS>
+ <SRS>EPSG:2973</SRS>
+ <SRS>EPSG:2975</SRS>
+ <SRS>EPSG:2976</SRS>
+ <SRS>EPSG:2977</SRS>
+ <SRS>EPSG:2978</SRS>
+ <SRS>EPSG:2979</SRS>
+ <SRS>EPSG:2980</SRS>
+ <SRS>EPSG:2981</SRS>
+ <SRS>EPSG:2982</SRS>
+ <SRS>EPSG:2983</SRS>
+ <SRS>EPSG:2984</SRS>
+ <SRS>EPSG:2985</SRS>
+ <SRS>EPSG:2986</SRS>
+ <SRS>EPSG:2987</SRS>
+ <SRS>EPSG:2988</SRS>
+ <SRS>EPSG:2989</SRS>
+ <SRS>EPSG:2990</SRS>
+ <SRS>EPSG:2991</SRS>
+ <SRS>EPSG:2992</SRS>
+ <SRS>EPSG:2993</SRS>
+ <SRS>EPSG:2994</SRS>
+ <SRS>EPSG:2995</SRS>
+ <SRS>EPSG:2996</SRS>
+ <SRS>EPSG:2997</SRS>
+ <SRS>EPSG:2998</SRS>
+ <SRS>EPSG:2999</SRS>
+ <SRS>EPSG:3000</SRS>
+ <SRS>EPSG:3001</SRS>
+ <SRS>EPSG:3002</SRS>
+ <SRS>EPSG:3003</SRS>
+ <SRS>EPSG:3004</SRS>
+ <SRS>EPSG:3005</SRS>
+ <SRS>EPSG:3006</SRS>
+ <SRS>EPSG:3007</SRS>
+ <SRS>EPSG:3008</SRS>
+ <SRS>EPSG:3009</SRS>
+ <SRS>EPSG:3010</SRS>
+ <SRS>EPSG:3011</SRS>
+ <SRS>EPSG:3012</SRS>
+ <SRS>EPSG:3013</SRS>
+ <SRS>EPSG:3014</SRS>
+ <SRS>EPSG:3015</SRS>
+ <SRS>EPSG:3016</SRS>
+ <SRS>EPSG:3017</SRS>
+ <SRS>EPSG:3018</SRS>
+ <SRS>EPSG:3019</SRS>
+ <SRS>EPSG:3020</SRS>
+ <SRS>EPSG:3021</SRS>
+ <SRS>EPSG:3022</SRS>
+ <SRS>EPSG:3023</SRS>
+ <SRS>EPSG:3024</SRS>
+ <SRS>EPSG:3025</SRS>
+ <SRS>EPSG:3026</SRS>
+ <SRS>EPSG:3027</SRS>
+ <SRS>EPSG:3028</SRS>
+ <SRS>EPSG:3029</SRS>
+ <SRS>EPSG:3030</SRS>
+ <SRS>EPSG:3031</SRS>
+ <SRS>EPSG:3032</SRS>
+ <SRS>EPSG:3033</SRS>
+ <SRS>EPSG:3034</SRS>
+ <SRS>EPSG:3035</SRS>
+ <SRS>EPSG:3036</SRS>
+ <SRS>EPSG:3037</SRS>
+ <SRS>EPSG:3038</SRS>
+ <SRS>EPSG:3039</SRS>
+ <SRS>EPSG:3040</SRS>
+ <SRS>EPSG:3041</SRS>
+ <SRS>EPSG:3042</SRS>
+ <SRS>EPSG:3043</SRS>
+ <SRS>EPSG:3044</SRS>
+ <SRS>EPSG:3045</SRS>
+ <SRS>EPSG:3046</SRS>
+ <SRS>EPSG:3047</SRS>
+ <SRS>EPSG:3048</SRS>
+ <SRS>EPSG:3049</SRS>
+ <SRS>EPSG:3050</SRS>
+ <SRS>EPSG:3051</SRS>
+ <SRS>EPSG:3052</SRS>
+ <SRS>EPSG:3053</SRS>
+ <SRS>EPSG:3054</SRS>
+ <SRS>EPSG:3055</SRS>
+ <SRS>EPSG:3056</SRS>
+ <SRS>EPSG:3057</SRS>
+ <SRS>EPSG:3058</SRS>
+ <SRS>EPSG:3059</SRS>
+ <SRS>EPSG:3060</SRS>
+ <SRS>EPSG:3061</SRS>
+ <SRS>EPSG:3062</SRS>
+ <SRS>EPSG:3063</SRS>
+ <SRS>EPSG:3064</SRS>
+ <SRS>EPSG:3065</SRS>
+ <SRS>EPSG:3066</SRS>
+ <SRS>EPSG:3067</SRS>
+ <SRS>EPSG:3068</SRS>
+ <SRS>EPSG:3069</SRS>
+ <SRS>EPSG:3070</SRS>
+ <SRS>EPSG:3071</SRS>
+ <SRS>EPSG:3072</SRS>
+ <SRS>EPSG:3073</SRS>
+ <SRS>EPSG:3074</SRS>
+ <SRS>EPSG:3075</SRS>
+ <SRS>EPSG:3076</SRS>
+ <SRS>EPSG:3077</SRS>
+ <SRS>EPSG:3078</SRS>
+ <SRS>EPSG:3079</SRS>
+ <SRS>EPSG:3080</SRS>
+ <SRS>EPSG:3081</SRS>
+ <SRS>EPSG:3082</SRS>
+ <SRS>EPSG:3083</SRS>
+ <SRS>EPSG:3084</SRS>
+ <SRS>EPSG:3085</SRS>
+ <SRS>EPSG:3086</SRS>
+ <SRS>EPSG:3087</SRS>
+ <SRS>EPSG:3088</SRS>
+ <SRS>EPSG:3089</SRS>
+ <SRS>EPSG:3090</SRS>
+ <SRS>EPSG:3091</SRS>
+ <SRS>EPSG:3092</SRS>
+ <SRS>EPSG:3093</SRS>
+ <SRS>EPSG:3094</SRS>
+ <SRS>EPSG:3095</SRS>
+ <SRS>EPSG:3096</SRS>
+ <SRS>EPSG:3097</SRS>
+ <SRS>EPSG:3098</SRS>
+ <SRS>EPSG:3099</SRS>
+ <SRS>EPSG:3100</SRS>
+ <SRS>EPSG:3101</SRS>
+ <SRS>EPSG:3102</SRS>
+ <SRS>EPSG:3103</SRS>
+ <SRS>EPSG:3104</SRS>
+ <SRS>EPSG:3105</SRS>
+ <SRS>EPSG:3106</SRS>
+ <SRS>EPSG:3107</SRS>
+ <SRS>EPSG:3108</SRS>
+ <SRS>EPSG:3109</SRS>
+ <SRS>EPSG:3110</SRS>
+ <SRS>EPSG:3111</SRS>
+ <SRS>EPSG:3112</SRS>
+ <SRS>EPSG:3113</SRS>
+ <SRS>EPSG:3114</SRS>
+ <SRS>EPSG:3115</SRS>
+ <SRS>EPSG:3116</SRS>
+ <SRS>EPSG:3117</SRS>
+ <SRS>EPSG:3118</SRS>
+ <SRS>EPSG:3119</SRS>
+ <SRS>EPSG:3120</SRS>
+ <SRS>EPSG:3121</SRS>
+ <SRS>EPSG:3122</SRS>
+ <SRS>EPSG:3123</SRS>
+ <SRS>EPSG:3124</SRS>
+ <SRS>EPSG:3125</SRS>
+ <SRS>EPSG:3126</SRS>
+ <SRS>EPSG:3127</SRS>
+ <SRS>EPSG:3128</SRS>
+ <SRS>EPSG:3129</SRS>
+ <SRS>EPSG:3130</SRS>
+ <SRS>EPSG:3131</SRS>
+ <SRS>EPSG:3132</SRS>
+ <SRS>EPSG:3133</SRS>
+ <SRS>EPSG:3134</SRS>
+ <SRS>EPSG:3135</SRS>
+ <SRS>EPSG:3136</SRS>
+ <SRS>EPSG:3137</SRS>
+ <SRS>EPSG:3138</SRS>
+ <SRS>EPSG:3139</SRS>
+ <SRS>EPSG:3140</SRS>
+ <SRS>EPSG:3141</SRS>
+ <SRS>EPSG:3142</SRS>
+ <SRS>EPSG:3143</SRS>
+ <SRS>EPSG:3144</SRS>
+ <SRS>EPSG:3145</SRS>
+ <SRS>EPSG:3146</SRS>
+ <SRS>EPSG:3147</SRS>
+ <SRS>EPSG:3148</SRS>
+ <SRS>EPSG:3149</SRS>
+ <SRS>EPSG:3150</SRS>
+ <SRS>EPSG:3151</SRS>
+ <SRS>EPSG:3152</SRS>
+ <SRS>EPSG:3153</SRS>
+ <SRS>EPSG:3154</SRS>
+ <SRS>EPSG:3155</SRS>
+ <SRS>EPSG:3156</SRS>
+ <SRS>EPSG:3157</SRS>
+ <SRS>EPSG:3158</SRS>
+ <SRS>EPSG:3159</SRS>
+ <SRS>EPSG:3160</SRS>
+ <SRS>EPSG:3161</SRS>
+ <SRS>EPSG:3162</SRS>
+ <SRS>EPSG:3163</SRS>
+ <SRS>EPSG:3164</SRS>
+ <SRS>EPSG:3165</SRS>
+ <SRS>EPSG:3166</SRS>
+ <SRS>EPSG:3167</SRS>
+ <SRS>EPSG:3168</SRS>
+ <SRS>EPSG:3169</SRS>
+ <SRS>EPSG:3170</SRS>
+ <SRS>EPSG:3171</SRS>
+ <SRS>EPSG:3172</SRS>
+ <SRS>EPSG:3173</SRS>
+ <SRS>EPSG:3174</SRS>
+ <SRS>EPSG:3175</SRS>
+ <SRS>EPSG:3176</SRS>
+ <SRS>EPSG:3177</SRS>
+ <SRS>EPSG:3178</SRS>
+ <SRS>EPSG:3179</SRS>
+ <SRS>EPSG:3180</SRS>
+ <SRS>EPSG:3181</SRS>
+ <SRS>EPSG:3182</SRS>
+ <SRS>EPSG:3183</SRS>
+ <SRS>EPSG:3184</SRS>
+ <SRS>EPSG:3185</SRS>
+ <SRS>EPSG:3186</SRS>
+ <SRS>EPSG:3187</SRS>
+ <SRS>EPSG:3188</SRS>
+ <SRS>EPSG:3189</SRS>
+ <SRS>EPSG:3190</SRS>
+ <SRS>EPSG:3191</SRS>
+ <SRS>EPSG:3192</SRS>
+ <SRS>EPSG:3193</SRS>
+ <SRS>EPSG:3194</SRS>
+ <SRS>EPSG:3195</SRS>
+ <SRS>EPSG:3196</SRS>
+ <SRS>EPSG:3197</SRS>
+ <SRS>EPSG:3198</SRS>
+ <SRS>EPSG:3199</SRS>
+ <SRS>EPSG:3200</SRS>
+ <SRS>EPSG:3201</SRS>
+ <SRS>EPSG:3202</SRS>
+ <SRS>EPSG:3203</SRS>
+ <SRS>EPSG:3204</SRS>
+ <SRS>EPSG:3205</SRS>
+ <SRS>EPSG:3206</SRS>
+ <SRS>EPSG:3207</SRS>
+ <SRS>EPSG:3208</SRS>
+ <SRS>EPSG:3209</SRS>
+ <SRS>EPSG:3210</SRS>
+ <SRS>EPSG:3211</SRS>
+ <SRS>EPSG:3212</SRS>
+ <SRS>EPSG:3213</SRS>
+ <SRS>EPSG:3214</SRS>
+ <SRS>EPSG:3215</SRS>
+ <SRS>EPSG:3216</SRS>
+ <SRS>EPSG:3217</SRS>
+ <SRS>EPSG:3218</SRS>
+ <SRS>EPSG:3219</SRS>
+ <SRS>EPSG:3220</SRS>
+ <SRS>EPSG:3221</SRS>
+ <SRS>EPSG:3222</SRS>
+ <SRS>EPSG:3223</SRS>
+ <SRS>EPSG:3224</SRS>
+ <SRS>EPSG:3225</SRS>
+ <SRS>EPSG:3226</SRS>
+ <SRS>EPSG:3227</SRS>
+ <SRS>EPSG:3228</SRS>
+ <SRS>EPSG:3229</SRS>
+ <SRS>EPSG:3230</SRS>
+ <SRS>EPSG:3231</SRS>
+ <SRS>EPSG:3232</SRS>
+ <SRS>EPSG:3233</SRS>
+ <SRS>EPSG:3234</SRS>
+ <SRS>EPSG:3235</SRS>
+ <SRS>EPSG:3236</SRS>
+ <SRS>EPSG:3237</SRS>
+ <SRS>EPSG:3238</SRS>
+ <SRS>EPSG:3239</SRS>
+ <SRS>EPSG:3240</SRS>
+ <SRS>EPSG:3241</SRS>
+ <SRS>EPSG:3242</SRS>
+ <SRS>EPSG:3243</SRS>
+ <SRS>EPSG:3244</SRS>
+ <SRS>EPSG:3245</SRS>
+ <SRS>EPSG:3246</SRS>
+ <SRS>EPSG:3247</SRS>
+ <SRS>EPSG:3248</SRS>
+ <SRS>EPSG:3249</SRS>
+ <SRS>EPSG:3250</SRS>
+ <SRS>EPSG:3251</SRS>
+ <SRS>EPSG:3252</SRS>
+ <SRS>EPSG:3253</SRS>
+ <SRS>EPSG:3254</SRS>
+ <SRS>EPSG:3255</SRS>
+ <SRS>EPSG:3256</SRS>
+ <SRS>EPSG:3257</SRS>
+ <SRS>EPSG:3258</SRS>
+ <SRS>EPSG:3259</SRS>
+ <SRS>EPSG:3260</SRS>
+ <SRS>EPSG:3261</SRS>
+ <SRS>EPSG:3262</SRS>
+ <SRS>EPSG:3263</SRS>
+ <SRS>EPSG:3264</SRS>
+ <SRS>EPSG:3265</SRS>
+ <SRS>EPSG:3266</SRS>
+ <SRS>EPSG:3267</SRS>
+ <SRS>EPSG:3268</SRS>
+ <SRS>EPSG:3269</SRS>
+ <SRS>EPSG:3270</SRS>
+ <SRS>EPSG:3271</SRS>
+ <SRS>EPSG:3272</SRS>
+ <SRS>EPSG:3273</SRS>
+ <SRS>EPSG:3274</SRS>
+ <SRS>EPSG:3275</SRS>
+ <SRS>EPSG:3276</SRS>
+ <SRS>EPSG:3277</SRS>
+ <SRS>EPSG:3278</SRS>
+ <SRS>EPSG:3279</SRS>
+ <SRS>EPSG:3280</SRS>
+ <SRS>EPSG:3281</SRS>
+ <SRS>EPSG:3282</SRS>
+ <SRS>EPSG:3283</SRS>
+ <SRS>EPSG:3284</SRS>
+ <SRS>EPSG:3285</SRS>
+ <SRS>EPSG:3286</SRS>
+ <SRS>EPSG:3287</SRS>
+ <SRS>EPSG:3288</SRS>
+ <SRS>EPSG:3289</SRS>
+ <SRS>EPSG:3290</SRS>
+ <SRS>EPSG:3291</SRS>
+ <SRS>EPSG:3292</SRS>
+ <SRS>EPSG:3293</SRS>
+ <SRS>EPSG:3294</SRS>
+ <SRS>EPSG:3295</SRS>
+ <SRS>EPSG:3296</SRS>
+ <SRS>EPSG:3297</SRS>
+ <SRS>EPSG:3298</SRS>
+ <SRS>EPSG:3299</SRS>
+ <SRS>EPSG:3300</SRS>
+ <SRS>EPSG:3301</SRS>
+ <SRS>EPSG:3302</SRS>
+ <SRS>EPSG:3303</SRS>
+ <SRS>EPSG:3304</SRS>
+ <SRS>EPSG:3305</SRS>
+ <SRS>EPSG:3306</SRS>
+ <SRS>EPSG:3307</SRS>
+ <SRS>EPSG:3308</SRS>
+ <SRS>EPSG:3309</SRS>
+ <SRS>EPSG:3310</SRS>
+ <SRS>EPSG:3311</SRS>
+ <SRS>EPSG:3312</SRS>
+ <SRS>EPSG:3313</SRS>
+ <SRS>EPSG:3314</SRS>
+ <SRS>EPSG:3315</SRS>
+ <SRS>EPSG:3316</SRS>
+ <SRS>EPSG:3317</SRS>
+ <SRS>EPSG:3318</SRS>
+ <SRS>EPSG:3319</SRS>
+ <SRS>EPSG:3320</SRS>
+ <SRS>EPSG:3321</SRS>
+ <SRS>EPSG:3322</SRS>
+ <SRS>EPSG:3323</SRS>
+ <SRS>EPSG:3324</SRS>
+ <SRS>EPSG:3325</SRS>
+ <SRS>EPSG:3326</SRS>
+ <SRS>EPSG:3327</SRS>
+ <SRS>EPSG:3328</SRS>
+ <SRS>EPSG:3329</SRS>
+ <SRS>EPSG:3330</SRS>
+ <SRS>EPSG:3331</SRS>
+ <SRS>EPSG:3332</SRS>
+ <SRS>EPSG:3333</SRS>
+ <SRS>EPSG:3334</SRS>
+ <SRS>EPSG:3335</SRS>
+ <SRS>EPSG:3336</SRS>
+ <SRS>EPSG:3337</SRS>
+ <SRS>EPSG:3338</SRS>
+ <SRS>EPSG:3339</SRS>
+ <SRS>EPSG:3340</SRS>
+ <SRS>EPSG:3341</SRS>
+ <SRS>EPSG:3342</SRS>
+ <SRS>EPSG:3343</SRS>
+ <SRS>EPSG:3344</SRS>
+ <SRS>EPSG:3345</SRS>
+ <SRS>EPSG:3346</SRS>
+ <SRS>EPSG:3347</SRS>
+ <SRS>EPSG:3348</SRS>
+ <SRS>EPSG:3349</SRS>
+ <SRS>EPSG:3350</SRS>
+ <SRS>EPSG:3351</SRS>
+ <SRS>EPSG:3352</SRS>
+ <SRS>EPSG:3353</SRS>
+ <SRS>EPSG:3354</SRS>
+ <SRS>EPSG:3355</SRS>
+ <SRS>EPSG:3356</SRS>
+ <SRS>EPSG:3357</SRS>
+ <SRS>EPSG:3358</SRS>
+ <SRS>EPSG:3359</SRS>
+ <SRS>EPSG:3360</SRS>
+ <SRS>EPSG:3361</SRS>
+ <SRS>EPSG:3362</SRS>
+ <SRS>EPSG:3363</SRS>
+ <SRS>EPSG:3364</SRS>
+ <SRS>EPSG:3365</SRS>
+ <SRS>EPSG:3366</SRS>
+ <SRS>EPSG:3367</SRS>
+ <SRS>EPSG:3368</SRS>
+ <SRS>EPSG:3369</SRS>
+ <SRS>EPSG:3370</SRS>
+ <SRS>EPSG:3371</SRS>
+ <SRS>EPSG:3372</SRS>
+ <SRS>EPSG:3373</SRS>
+ <SRS>EPSG:3374</SRS>
+ <SRS>EPSG:3375</SRS>
+ <SRS>EPSG:3376</SRS>
+ <SRS>EPSG:3377</SRS>
+ <SRS>EPSG:3378</SRS>
+ <SRS>EPSG:3379</SRS>
+ <SRS>EPSG:3380</SRS>
+ <SRS>EPSG:3381</SRS>
+ <SRS>EPSG:3382</SRS>
+ <SRS>EPSG:3383</SRS>
+ <SRS>EPSG:3384</SRS>
+ <SRS>EPSG:3385</SRS>
+ <SRS>EPSG:3386</SRS>
+ <SRS>EPSG:3387</SRS>
+ <SRS>EPSG:3388</SRS>
+ <SRS>EPSG:3389</SRS>
+ <SRS>EPSG:3390</SRS>
+ <SRS>EPSG:3391</SRS>
+ <SRS>EPSG:3392</SRS>
+ <SRS>EPSG:3393</SRS>
+ <SRS>EPSG:3394</SRS>
+ <SRS>EPSG:3395</SRS>
+ <SRS>EPSG:3396</SRS>
+ <SRS>EPSG:3397</SRS>
+ <SRS>EPSG:3398</SRS>
+ <SRS>EPSG:3399</SRS>
+ <SRS>EPSG:3400</SRS>
+ <SRS>EPSG:3401</SRS>
+ <SRS>EPSG:3402</SRS>
+ <SRS>EPSG:3403</SRS>
+ <SRS>EPSG:3404</SRS>
+ <SRS>EPSG:3405</SRS>
+ <SRS>EPSG:3406</SRS>
+ <SRS>EPSG:3407</SRS>
+ <SRS>EPSG:3408</SRS>
+ <SRS>EPSG:3409</SRS>
+ <SRS>EPSG:3410</SRS>
+ <SRS>EPSG:3411</SRS>
+ <SRS>EPSG:3412</SRS>
+ <SRS>EPSG:3413</SRS>
+ <SRS>EPSG:3414</SRS>
+ <SRS>EPSG:3415</SRS>
+ <SRS>EPSG:3416</SRS>
+ <SRS>EPSG:3417</SRS>
+ <SRS>EPSG:3418</SRS>
+ <SRS>EPSG:3419</SRS>
+ <SRS>EPSG:3420</SRS>
+ <SRS>EPSG:3421</SRS>
+ <SRS>EPSG:3422</SRS>
+ <SRS>EPSG:3423</SRS>
+ <SRS>EPSG:3424</SRS>
+ <SRS>EPSG:3425</SRS>
+ <SRS>EPSG:3426</SRS>
+ <SRS>EPSG:3427</SRS>
+ <SRS>EPSG:3428</SRS>
+ <SRS>EPSG:3429</SRS>
+ <SRS>EPSG:3430</SRS>
+ <SRS>EPSG:3431</SRS>
+ <SRS>EPSG:3432</SRS>
+ <SRS>EPSG:3433</SRS>
+ <SRS>EPSG:3434</SRS>
+ <SRS>EPSG:3435</SRS>
+ <SRS>EPSG:3436</SRS>
+ <SRS>EPSG:3437</SRS>
+ <SRS>EPSG:3438</SRS>
+ <SRS>EPSG:3439</SRS>
+ <SRS>EPSG:3440</SRS>
+ <SRS>EPSG:3441</SRS>
+ <SRS>EPSG:3442</SRS>
+ <SRS>EPSG:3443</SRS>
+ <SRS>EPSG:3444</SRS>
+ <SRS>EPSG:3445</SRS>
+ <SRS>EPSG:3446</SRS>
+ <SRS>EPSG:3447</SRS>
+ <SRS>EPSG:3448</SRS>
+ <SRS>EPSG:3449</SRS>
+ <SRS>EPSG:3450</SRS>
+ <SRS>EPSG:3451</SRS>
+ <SRS>EPSG:3452</SRS>
+ <SRS>EPSG:3453</SRS>
+ <SRS>EPSG:3454</SRS>
+ <SRS>EPSG:3455</SRS>
+ <SRS>EPSG:3456</SRS>
+ <SRS>EPSG:3457</SRS>
+ <SRS>EPSG:3458</SRS>
+ <SRS>EPSG:3459</SRS>
+ <SRS>EPSG:3460</SRS>
+ <SRS>EPSG:3461</SRS>
+ <SRS>EPSG:3462</SRS>
+ <SRS>EPSG:3463</SRS>
+ <SRS>EPSG:3464</SRS>
+ <SRS>EPSG:3560</SRS>
+ <SRS>EPSG:3561</SRS>
+ <SRS>EPSG:3562</SRS>
+ <SRS>EPSG:3563</SRS>
+ <SRS>EPSG:3564</SRS>
+ <SRS>EPSG:3565</SRS>
+ <SRS>EPSG:3566</SRS>
+ <SRS>EPSG:3567</SRS>
+ <SRS>EPSG:3568</SRS>
+ <SRS>EPSG:3569</SRS>
+ <SRS>EPSG:3570</SRS>
+ <SRS>EPSG:3571</SRS>
+ <SRS>EPSG:3572</SRS>
+ <SRS>EPSG:3573</SRS>
+ <SRS>EPSG:3574</SRS>
+ <SRS>EPSG:3575</SRS>
+ <SRS>EPSG:3576</SRS>
+ <SRS>EPSG:3577</SRS>
+ <SRS>EPSG:3920</SRS>
+ <SRS>EPSG:3991</SRS>
+ <SRS>EPSG:3992</SRS>
+ <SRS>EPSG:3993</SRS>
+ <SRS>EPSG:4001</SRS>
+ <SRS>EPSG:4002</SRS>
+ <SRS>EPSG:4003</SRS>
+ <SRS>EPSG:4004</SRS>
+ <SRS>EPSG:4005</SRS>
+ <SRS>EPSG:4006</SRS>
+ <SRS>EPSG:4007</SRS>
+ <SRS>EPSG:4008</SRS>
+ <SRS>EPSG:4009</SRS>
+ <SRS>EPSG:4010</SRS>
+ <SRS>EPSG:4011</SRS>
+ <SRS>EPSG:4012</SRS>
+ <SRS>EPSG:4013</SRS>
+ <SRS>EPSG:4014</SRS>
+ <SRS>EPSG:4015</SRS>
+ <SRS>EPSG:4016</SRS>
+ <SRS>EPSG:4018</SRS>
+ <SRS>EPSG:4019</SRS>
+ <SRS>EPSG:4020</SRS>
+ <SRS>EPSG:4021</SRS>
+ <SRS>EPSG:4022</SRS>
+ <SRS>EPSG:4024</SRS>
+ <SRS>EPSG:4025</SRS>
+ <SRS>EPSG:4027</SRS>
+ <SRS>EPSG:4028</SRS>
+ <SRS>EPSG:4029</SRS>
+ <SRS>EPSG:4030</SRS>
+ <SRS>EPSG:4031</SRS>
+ <SRS>EPSG:4032</SRS>
+ <SRS>EPSG:4033</SRS>
+ <SRS>EPSG:4034</SRS>
+ <SRS>EPSG:4035</SRS>
+ <SRS>EPSG:4036</SRS>
+ <SRS>EPSG:4041</SRS>
+ <SRS>EPSG:4042</SRS>
+ <SRS>EPSG:4043</SRS>
+ <SRS>EPSG:4044</SRS>
+ <SRS>EPSG:4045</SRS>
+ <SRS>EPSG:4047</SRS>
+ <SRS>EPSG:4052</SRS>
+ <SRS>EPSG:4053</SRS>
+ <SRS>EPSG:4054</SRS>
+ <SRS>EPSG:4120</SRS>
+ <SRS>EPSG:4121</SRS>
+ <SRS>EPSG:4122</SRS>
+ <SRS>EPSG:4123</SRS>
+ <SRS>EPSG:4124</SRS>
+ <SRS>EPSG:4125</SRS>
+ <SRS>EPSG:4126</SRS>
+ <SRS>EPSG:4127</SRS>
+ <SRS>EPSG:4128</SRS>
+ <SRS>EPSG:4129</SRS>
+ <SRS>EPSG:4130</SRS>
+ <SRS>EPSG:4131</SRS>
+ <SRS>EPSG:4132</SRS>
+ <SRS>EPSG:4133</SRS>
+ <SRS>EPSG:4134</SRS>
+ <SRS>EPSG:4135</SRS>
+ <SRS>EPSG:4136</SRS>
+ <SRS>EPSG:4137</SRS>
+ <SRS>EPSG:4138</SRS>
+ <SRS>EPSG:4139</SRS>
+ <SRS>EPSG:4140</SRS>
+ <SRS>EPSG:4141</SRS>
+ <SRS>EPSG:4142</SRS>
+ <SRS>EPSG:4143</SRS>
+ <SRS>EPSG:4144</SRS>
+ <SRS>EPSG:4145</SRS>
+ <SRS>EPSG:4146</SRS>
+ <SRS>EPSG:4147</SRS>
+ <SRS>EPSG:4148</SRS>
+ <SRS>EPSG:4149</SRS>
+ <SRS>EPSG:4150</SRS>
+ <SRS>EPSG:4151</SRS>
+ <SRS>EPSG:4152</SRS>
+ <SRS>EPSG:4153</SRS>
+ <SRS>EPSG:4154</SRS>
+ <SRS>EPSG:4155</SRS>
+ <SRS>EPSG:4156</SRS>
+ <SRS>EPSG:4157</SRS>
+ <SRS>EPSG:4158</SRS>
+ <SRS>EPSG:4159</SRS>
+ <SRS>EPSG:4160</SRS>
+ <SRS>EPSG:4161</SRS>
+ <SRS>EPSG:4162</SRS>
+ <SRS>EPSG:4163</SRS>
+ <SRS>EPSG:4164</SRS>
+ <SRS>EPSG:4165</SRS>
+ <SRS>EPSG:4166</SRS>
+ <SRS>EPSG:4167</SRS>
+ <SRS>EPSG:4168</SRS>
+ <SRS>EPSG:4169</SRS>
+ <SRS>EPSG:4170</SRS>
+ <SRS>EPSG:4171</SRS>
+ <SRS>EPSG:4172</SRS>
+ <SRS>EPSG:4173</SRS>
+ <SRS>EPSG:4174</SRS>
+ <SRS>EPSG:4175</SRS>
+ <SRS>EPSG:4176</SRS>
+ <SRS>EPSG:4178</SRS>
+ <SRS>EPSG:4179</SRS>
+ <SRS>EPSG:4180</SRS>
+ <SRS>EPSG:4181</SRS>
+ <SRS>EPSG:4182</SRS>
+ <SRS>EPSG:4183</SRS>
+ <SRS>EPSG:4184</SRS>
+ <SRS>EPSG:4185</SRS>
+ <SRS>EPSG:4188</SRS>
+ <SRS>EPSG:4189</SRS>
+ <SRS>EPSG:4190</SRS>
+ <SRS>EPSG:4191</SRS>
+ <SRS>EPSG:4192</SRS>
+ <SRS>EPSG:4193</SRS>
+ <SRS>EPSG:4194</SRS>
+ <SRS>EPSG:4195</SRS>
+ <SRS>EPSG:4196</SRS>
+ <SRS>EPSG:4197</SRS>
+ <SRS>EPSG:4198</SRS>
+ <SRS>EPSG:4199</SRS>
+ <SRS>EPSG:4200</SRS>
+ <SRS>EPSG:4201</SRS>
+ <SRS>EPSG:4202</SRS>
+ <SRS>EPSG:4203</SRS>
+ <SRS>EPSG:4204</SRS>
+ <SRS>EPSG:4205</SRS>
+ <SRS>EPSG:4206</SRS>
+ <SRS>EPSG:4207</SRS>
+ <SRS>EPSG:4208</SRS>
+ <SRS>EPSG:4209</SRS>
+ <SRS>EPSG:4210</SRS>
+ <SRS>EPSG:4211</SRS>
+ <SRS>EPSG:4212</SRS>
+ <SRS>EPSG:4213</SRS>
+ <SRS>EPSG:4214</SRS>
+ <SRS>EPSG:4215</SRS>
+ <SRS>EPSG:4216</SRS>
+ <SRS>EPSG:4218</SRS>
+ <SRS>EPSG:4219</SRS>
+ <SRS>EPSG:4220</SRS>
+ <SRS>EPSG:4221</SRS>
+ <SRS>EPSG:4222</SRS>
+ <SRS>EPSG:4223</SRS>
+ <SRS>EPSG:4224</SRS>
+ <SRS>EPSG:4225</SRS>
+ <SRS>EPSG:4226</SRS>
+ <SRS>EPSG:4227</SRS>
+ <SRS>EPSG:4228</SRS>
+ <SRS>EPSG:4229</SRS>
+ <SRS>EPSG:4230</SRS>
+ <SRS>EPSG:4231</SRS>
+ <SRS>EPSG:4232</SRS>
+ <SRS>EPSG:4233</SRS>
+ <SRS>EPSG:4234</SRS>
+ <SRS>EPSG:4235</SRS>
+ <SRS>EPSG:4236</SRS>
+ <SRS>EPSG:4237</SRS>
+ <SRS>EPSG:4238</SRS>
+ <SRS>EPSG:4239</SRS>
+ <SRS>EPSG:4240</SRS>
+ <SRS>EPSG:4241</SRS>
+ <SRS>EPSG:4242</SRS>
+ <SRS>EPSG:4243</SRS>
+ <SRS>EPSG:4244</SRS>
+ <SRS>EPSG:4245</SRS>
+ <SRS>EPSG:4246</SRS>
+ <SRS>EPSG:4247</SRS>
+ <SRS>EPSG:4248</SRS>
+ <SRS>EPSG:4249</SRS>
+ <SRS>EPSG:4250</SRS>
+ <SRS>EPSG:4251</SRS>
+ <SRS>EPSG:4252</SRS>
+ <SRS>EPSG:4253</SRS>
+ <SRS>EPSG:4254</SRS>
+ <SRS>EPSG:4255</SRS>
+ <SRS>EPSG:4256</SRS>
+ <SRS>EPSG:4257</SRS>
+ <SRS>EPSG:4258</SRS>
+ <SRS>EPSG:4259</SRS>
+ <SRS>EPSG:4260</SRS>
+ <SRS>EPSG:4261</SRS>
+ <SRS>EPSG:4262</SRS>
+ <SRS>EPSG:4263</SRS>
+ <SRS>EPSG:4264</SRS>
+ <SRS>EPSG:4265</SRS>
+ <SRS>EPSG:4266</SRS>
+ <SRS>EPSG:4267</SRS>
+ <SRS>EPSG:4268</SRS>
+ <SRS>EPSG:4269</SRS>
+ <SRS>EPSG:4270</SRS>
+ <SRS>EPSG:4271</SRS>
+ <SRS>EPSG:4272</SRS>
+ <SRS>EPSG:4273</SRS>
+ <SRS>EPSG:4274</SRS>
+ <SRS>EPSG:4275</SRS>
+ <SRS>EPSG:4276</SRS>
+ <SRS>EPSG:4277</SRS>
+ <SRS>EPSG:4278</SRS>
+ <SRS>EPSG:4279</SRS>
+ <SRS>EPSG:4280</SRS>
+ <SRS>EPSG:4281</SRS>
+ <SRS>EPSG:4282</SRS>
+ <SRS>EPSG:4283</SRS>
+ <SRS>EPSG:4284</SRS>
+ <SRS>EPSG:4285</SRS>
+ <SRS>EPSG:4286</SRS>
+ <SRS>EPSG:4287</SRS>
+ <SRS>EPSG:4288</SRS>
+ <SRS>EPSG:4289</SRS>
+ <SRS>EPSG:4291</SRS>
+ <SRS>EPSG:4292</SRS>
+ <SRS>EPSG:4293</SRS>
+ <SRS>EPSG:4294</SRS>
+ <SRS>EPSG:4295</SRS>
+ <SRS>EPSG:4296</SRS>
+ <SRS>EPSG:4297</SRS>
+ <SRS>EPSG:4298</SRS>
+ <SRS>EPSG:4299</SRS>
+ <SRS>EPSG:4300</SRS>
+ <SRS>EPSG:4301</SRS>
+ <SRS>EPSG:4302</SRS>
+ <SRS>EPSG:4303</SRS>
+ <SRS>EPSG:4304</SRS>
+ <SRS>EPSG:4306</SRS>
+ <SRS>EPSG:4307</SRS>
+ <SRS>EPSG:4308</SRS>
+ <SRS>EPSG:4309</SRS>
+ <SRS>EPSG:4310</SRS>
+ <SRS>EPSG:4311</SRS>
+ <SRS>EPSG:4312</SRS>
+ <SRS>EPSG:4313</SRS>
+ <SRS>EPSG:4314</SRS>
+ <SRS>EPSG:4315</SRS>
+ <SRS>EPSG:4316</SRS>
+ <SRS>EPSG:4317</SRS>
+ <SRS>EPSG:4318</SRS>
+ <SRS>EPSG:4319</SRS>
+ <SRS>EPSG:4322</SRS>
+ <SRS>EPSG:4324</SRS>
+ <SRS>EPSG:4326</SRS>
+ <SRS>EPSG:4327</SRS>
+ <SRS>EPSG:4328</SRS>
+ <SRS>EPSG:4329</SRS>
+ <SRS>EPSG:4330</SRS>
+ <SRS>EPSG:4331</SRS>
+ <SRS>EPSG:4332</SRS>
+ <SRS>EPSG:4333</SRS>
+ <SRS>EPSG:4334</SRS>
+ <SRS>EPSG:4335</SRS>
+ <SRS>EPSG:4336</SRS>
+ <SRS>EPSG:4337</SRS>
+ <SRS>EPSG:4338</SRS>
+ <SRS>EPSG:4339</SRS>
+ <SRS>EPSG:4340</SRS>
+ <SRS>EPSG:4341</SRS>
+ <SRS>EPSG:4342</SRS>
+ <SRS>EPSG:4343</SRS>
+ <SRS>EPSG:4344</SRS>
+ <SRS>EPSG:4345</SRS>
+ <SRS>EPSG:4346</SRS>
+ <SRS>EPSG:4347</SRS>
+ <SRS>EPSG:4348</SRS>
+ <SRS>EPSG:4349</SRS>
+ <SRS>EPSG:4350</SRS>
+ <SRS>EPSG:4351</SRS>
+ <SRS>EPSG:4352</SRS>
+ <SRS>EPSG:4353</SRS>
+ <SRS>EPSG:4354</SRS>
+ <SRS>EPSG:4355</SRS>
+ <SRS>EPSG:4356</SRS>
+ <SRS>EPSG:4357</SRS>
+ <SRS>EPSG:4358</SRS>
+ <SRS>EPSG:4359</SRS>
+ <SRS>EPSG:4360</SRS>
+ <SRS>EPSG:4361</SRS>
+ <SRS>EPSG:4362</SRS>
+ <SRS>EPSG:4363</SRS>
+ <SRS>EPSG:4364</SRS>
+ <SRS>EPSG:4365</SRS>
+ <SRS>EPSG:4366</SRS>
+ <SRS>EPSG:4367</SRS>
+ <SRS>EPSG:4368</SRS>
+ <SRS>EPSG:4369</SRS>
+ <SRS>EPSG:4370</SRS>
+ <SRS>EPSG:4371</SRS>
+ <SRS>EPSG:4372</SRS>
+ <SRS>EPSG:4373</SRS>
+ <SRS>EPSG:4374</SRS>
+ <SRS>EPSG:4375</SRS>
+ <SRS>EPSG:4376</SRS>
+ <SRS>EPSG:4377</SRS>
+ <SRS>EPSG:4378</SRS>
+ <SRS>EPSG:4379</SRS>
+ <SRS>EPSG:4380</SRS>
+ <SRS>EPSG:4381</SRS>
+ <SRS>EPSG:4382</SRS>
+ <SRS>EPSG:4383</SRS>
+ <SRS>EPSG:4384</SRS>
+ <SRS>EPSG:4385</SRS>
+ <SRS>EPSG:4386</SRS>
+ <SRS>EPSG:4387</SRS>
+ <SRS>EPSG:4388</SRS>
+ <SRS>EPSG:4389</SRS>
+ <SRS>EPSG:4600</SRS>
+ <SRS>EPSG:4601</SRS>
+ <SRS>EPSG:4602</SRS>
+ <SRS>EPSG:4603</SRS>
+ <SRS>EPSG:4604</SRS>
+ <SRS>EPSG:4605</SRS>
+ <SRS>EPSG:4606</SRS>
+ <SRS>EPSG:4607</SRS>
+ <SRS>EPSG:4608</SRS>
+ <SRS>EPSG:4609</SRS>
+ <SRS>EPSG:4610</SRS>
+ <SRS>EPSG:4611</SRS>
+ <SRS>EPSG:4612</SRS>
+ <SRS>EPSG:4613</SRS>
+ <SRS>EPSG:4614</SRS>
+ <SRS>EPSG:4615</SRS>
+ <SRS>EPSG:4616</SRS>
+ <SRS>EPSG:4617</SRS>
+ <SRS>EPSG:4618</SRS>
+ <SRS>EPSG:4619</SRS>
+ <SRS>EPSG:4620</SRS>
+ <SRS>EPSG:4621</SRS>
+ <SRS>EPSG:4622</SRS>
+ <SRS>EPSG:4623</SRS>
+ <SRS>EPSG:4624</SRS>
+ <SRS>EPSG:4625</SRS>
+ <SRS>EPSG:4626</SRS>
+ <SRS>EPSG:4627</SRS>
+ <SRS>EPSG:4628</SRS>
+ <SRS>EPSG:4629</SRS>
+ <SRS>EPSG:4630</SRS>
+ <SRS>EPSG:4631</SRS>
+ <SRS>EPSG:4632</SRS>
+ <SRS>EPSG:4633</SRS>
+ <SRS>EPSG:4634</SRS>
+ <SRS>EPSG:4635</SRS>
+ <SRS>EPSG:4636</SRS>
+ <SRS>EPSG:4637</SRS>
+ <SRS>EPSG:4638</SRS>
+ <SRS>EPSG:4639</SRS>
+ <SRS>EPSG:4640</SRS>
+ <SRS>EPSG:4641</SRS>
+ <SRS>EPSG:4642</SRS>
+ <SRS>EPSG:4643</SRS>
+ <SRS>EPSG:4644</SRS>
+ <SRS>EPSG:4645</SRS>
+ <SRS>EPSG:4646</SRS>
+ <SRS>EPSG:4657</SRS>
+ <SRS>EPSG:4658</SRS>
+ <SRS>EPSG:4659</SRS>
+ <SRS>EPSG:4660</SRS>
+ <SRS>EPSG:4661</SRS>
+ <SRS>EPSG:4662</SRS>
+ <SRS>EPSG:4663</SRS>
+ <SRS>EPSG:4664</SRS>
+ <SRS>EPSG:4665</SRS>
+ <SRS>EPSG:4666</SRS>
+ <SRS>EPSG:4667</SRS>
+ <SRS>EPSG:4668</SRS>
+ <SRS>EPSG:4669</SRS>
+ <SRS>EPSG:4670</SRS>
+ <SRS>EPSG:4671</SRS>
+ <SRS>EPSG:4672</SRS>
+ <SRS>EPSG:4673</SRS>
+ <SRS>EPSG:4674</SRS>
+ <SRS>EPSG:4675</SRS>
+ <SRS>EPSG:4676</SRS>
+ <SRS>EPSG:4677</SRS>
+ <SRS>EPSG:4678</SRS>
+ <SRS>EPSG:4679</SRS>
+ <SRS>EPSG:4680</SRS>
+ <SRS>EPSG:4681</SRS>
+ <SRS>EPSG:4682</SRS>
+ <SRS>EPSG:4683</SRS>
+ <SRS>EPSG:4684</SRS>
+ <SRS>EPSG:4685</SRS>
+ <SRS>EPSG:4686</SRS>
+ <SRS>EPSG:4687</SRS>
+ <SRS>EPSG:4688</SRS>
+ <SRS>EPSG:4689</SRS>
+ <SRS>EPSG:4690</SRS>
+ <SRS>EPSG:4691</SRS>
+ <SRS>EPSG:4692</SRS>
+ <SRS>EPSG:4693</SRS>
+ <SRS>EPSG:4694</SRS>
+ <SRS>EPSG:4695</SRS>
+ <SRS>EPSG:4696</SRS>
+ <SRS>EPSG:4697</SRS>
+ <SRS>EPSG:4698</SRS>
+ <SRS>EPSG:4699</SRS>
+ <SRS>EPSG:4700</SRS>
+ <SRS>EPSG:4701</SRS>
+ <SRS>EPSG:4702</SRS>
+ <SRS>EPSG:4703</SRS>
+ <SRS>EPSG:4704</SRS>
+ <SRS>EPSG:4705</SRS>
+ <SRS>EPSG:4706</SRS>
+ <SRS>EPSG:4707</SRS>
+ <SRS>EPSG:4708</SRS>
+ <SRS>EPSG:4709</SRS>
+ <SRS>EPSG:4710</SRS>
+ <SRS>EPSG:4711</SRS>
+ <SRS>EPSG:4712</SRS>
+ <SRS>EPSG:4713</SRS>
+ <SRS>EPSG:4714</SRS>
+ <SRS>EPSG:4715</SRS>
+ <SRS>EPSG:4716</SRS>
+ <SRS>EPSG:4717</SRS>
+ <SRS>EPSG:4718</SRS>
+ <SRS>EPSG:4719</SRS>
+ <SRS>EPSG:4720</SRS>
+ <SRS>EPSG:4721</SRS>
+ <SRS>EPSG:4722</SRS>
+ <SRS>EPSG:4723</SRS>
+ <SRS>EPSG:4724</SRS>
+ <SRS>EPSG:4725</SRS>
+ <SRS>EPSG:4726</SRS>
+ <SRS>EPSG:4727</SRS>
+ <SRS>EPSG:4728</SRS>
+ <SRS>EPSG:4729</SRS>
+ <SRS>EPSG:4730</SRS>
+ <SRS>EPSG:4731</SRS>
+ <SRS>EPSG:4732</SRS>
+ <SRS>EPSG:4733</SRS>
+ <SRS>EPSG:4734</SRS>
+ <SRS>EPSG:4735</SRS>
+ <SRS>EPSG:4736</SRS>
+ <SRS>EPSG:4737</SRS>
+ <SRS>EPSG:4738</SRS>
+ <SRS>EPSG:4739</SRS>
+ <SRS>EPSG:4740</SRS>
+ <SRS>EPSG:4741</SRS>
+ <SRS>EPSG:4742</SRS>
+ <SRS>EPSG:4743</SRS>
+ <SRS>EPSG:4744</SRS>
+ <SRS>EPSG:4745</SRS>
+ <SRS>EPSG:4746</SRS>
+ <SRS>EPSG:4747</SRS>
+ <SRS>EPSG:4748</SRS>
+ <SRS>EPSG:4749</SRS>
+ <SRS>EPSG:4750</SRS>
+ <SRS>EPSG:4751</SRS>
+ <SRS>EPSG:4752</SRS>
+ <SRS>EPSG:4753</SRS>
+ <SRS>EPSG:4754</SRS>
+ <SRS>EPSG:4755</SRS>
+ <SRS>EPSG:4756</SRS>
+ <SRS>EPSG:4757</SRS>
+ <SRS>EPSG:4758</SRS>
+ <SRS>EPSG:4801</SRS>
+ <SRS>EPSG:4802</SRS>
+ <SRS>EPSG:4803</SRS>
+ <SRS>EPSG:4804</SRS>
+ <SRS>EPSG:4805</SRS>
+ <SRS>EPSG:4806</SRS>
+ <SRS>EPSG:4807</SRS>
+ <SRS>EPSG:4808</SRS>
+ <SRS>EPSG:4809</SRS>
+ <SRS>EPSG:4810</SRS>
+ <SRS>EPSG:4811</SRS>
+ <SRS>EPSG:4813</SRS>
+ <SRS>EPSG:4814</SRS>
+ <SRS>EPSG:4815</SRS>
+ <SRS>EPSG:4816</SRS>
+ <SRS>EPSG:4817</SRS>
+ <SRS>EPSG:4818</SRS>
+ <SRS>EPSG:4819</SRS>
+ <SRS>EPSG:4820</SRS>
+ <SRS>EPSG:4821</SRS>
+ <SRS>EPSG:4894</SRS>
+ <SRS>EPSG:4895</SRS>
+ <SRS>EPSG:4896</SRS>
+ <SRS>EPSG:4897</SRS>
+ <SRS>EPSG:4898</SRS>
+ <SRS>EPSG:4899</SRS>
+ <SRS>EPSG:4900</SRS>
+ <SRS>EPSG:4901</SRS>
+ <SRS>EPSG:4902</SRS>
+ <SRS>EPSG:4903</SRS>
+ <SRS>EPSG:4904</SRS>
+ <SRS>EPSG:4906</SRS>
+ <SRS>EPSG:4907</SRS>
+ <SRS>EPSG:4908</SRS>
+ <SRS>EPSG:4909</SRS>
+ <SRS>EPSG:4910</SRS>
+ <SRS>EPSG:4911</SRS>
+ <SRS>EPSG:4912</SRS>
+ <SRS>EPSG:4913</SRS>
+ <SRS>EPSG:4914</SRS>
+ <SRS>EPSG:4915</SRS>
+ <SRS>EPSG:4916</SRS>
+ <SRS>EPSG:4917</SRS>
+ <SRS>EPSG:4918</SRS>
+ <SRS>EPSG:4919</SRS>
+ <SRS>EPSG:4920</SRS>
+ <SRS>EPSG:4921</SRS>
+ <SRS>EPSG:4922</SRS>
+ <SRS>EPSG:4923</SRS>
+ <SRS>EPSG:4924</SRS>
+ <SRS>EPSG:4925</SRS>
+ <SRS>EPSG:4926</SRS>
+ <SRS>EPSG:4927</SRS>
+ <SRS>EPSG:4928</SRS>
+ <SRS>EPSG:4929</SRS>
+ <SRS>EPSG:4930</SRS>
+ <SRS>EPSG:4931</SRS>
+ <SRS>EPSG:4932</SRS>
+ <SRS>EPSG:4933</SRS>
+ <SRS>EPSG:4934</SRS>
+ <SRS>EPSG:4935</SRS>
+ <SRS>EPSG:4936</SRS>
+ <SRS>EPSG:4937</SRS>
+ <SRS>EPSG:4938</SRS>
+ <SRS>EPSG:4939</SRS>
+ <SRS>EPSG:4940</SRS>
+ <SRS>EPSG:4941</SRS>
+ <SRS>EPSG:4942</SRS>
+ <SRS>EPSG:4943</SRS>
+ <SRS>EPSG:4944</SRS>
+ <SRS>EPSG:4945</SRS>
+ <SRS>EPSG:4946</SRS>
+ <SRS>EPSG:4947</SRS>
+ <SRS>EPSG:4948</SRS>
+ <SRS>EPSG:4949</SRS>
+ <SRS>EPSG:4950</SRS>
+ <SRS>EPSG:4951</SRS>
+ <SRS>EPSG:4952</SRS>
+ <SRS>EPSG:4953</SRS>
+ <SRS>EPSG:4954</SRS>
+ <SRS>EPSG:4955</SRS>
+ <SRS>EPSG:4956</SRS>
+ <SRS>EPSG:4957</SRS>
+ <SRS>EPSG:4958</SRS>
+ <SRS>EPSG:4959</SRS>
+ <SRS>EPSG:4960</SRS>
+ <SRS>EPSG:4961</SRS>
+ <SRS>EPSG:4962</SRS>
+ <SRS>EPSG:4963</SRS>
+ <SRS>EPSG:4964</SRS>
+ <SRS>EPSG:4965</SRS>
+ <SRS>EPSG:4966</SRS>
+ <SRS>EPSG:4967</SRS>
+ <SRS>EPSG:4968</SRS>
+ <SRS>EPSG:4969</SRS>
+ <SRS>EPSG:4970</SRS>
+ <SRS>EPSG:4971</SRS>
+ <SRS>EPSG:4972</SRS>
+ <SRS>EPSG:4973</SRS>
+ <SRS>EPSG:4974</SRS>
+ <SRS>EPSG:4975</SRS>
+ <SRS>EPSG:4976</SRS>
+ <SRS>EPSG:4977</SRS>
+ <SRS>EPSG:4978</SRS>
+ <SRS>EPSG:4979</SRS>
+ <SRS>EPSG:4980</SRS>
+ <SRS>EPSG:4981</SRS>
+ <SRS>EPSG:4982</SRS>
+ <SRS>EPSG:4983</SRS>
+ <SRS>EPSG:4984</SRS>
+ <SRS>EPSG:4985</SRS>
+ <SRS>EPSG:4986</SRS>
+ <SRS>EPSG:4987</SRS>
+ <SRS>EPSG:4988</SRS>
+ <SRS>EPSG:4989</SRS>
+ <SRS>EPSG:4990</SRS>
+ <SRS>EPSG:4991</SRS>
+ <SRS>EPSG:4992</SRS>
+ <SRS>EPSG:4993</SRS>
+ <SRS>EPSG:4994</SRS>
+ <SRS>EPSG:4995</SRS>
+ <SRS>EPSG:4996</SRS>
+ <SRS>EPSG:4997</SRS>
+ <SRS>EPSG:4998</SRS>
+ <SRS>EPSG:4999</SRS>
+ <SRS>EPSG:5600</SRS>
+ <SRS>EPSG:5601</SRS>
+ <SRS>EPSG:5602</SRS>
+ <SRS>EPSG:5603</SRS>
+ <SRS>EPSG:5604</SRS>
+ <SRS>EPSG:5605</SRS>
+ <SRS>EPSG:5606</SRS>
+ <SRS>EPSG:5607</SRS>
+ <SRS>EPSG:5608</SRS>
+ <SRS>EPSG:5609</SRS>
+ <SRS>EPSG:5701</SRS>
+ <SRS>EPSG:5702</SRS>
+ <SRS>EPSG:5703</SRS>
+ <SRS>EPSG:5704</SRS>
+ <SRS>EPSG:5705</SRS>
+ <SRS>EPSG:5706</SRS>
+ <SRS>EPSG:5709</SRS>
+ <SRS>EPSG:5710</SRS>
+ <SRS>EPSG:5711</SRS>
+ <SRS>EPSG:5712</SRS>
+ <SRS>EPSG:5713</SRS>
+ <SRS>EPSG:5714</SRS>
+ <SRS>EPSG:5715</SRS>
+ <SRS>EPSG:5716</SRS>
+ <SRS>EPSG:5717</SRS>
+ <SRS>EPSG:5718</SRS>
+ <SRS>EPSG:5719</SRS>
+ <SRS>EPSG:5720</SRS>
+ <SRS>EPSG:5721</SRS>
+ <SRS>EPSG:5722</SRS>
+ <SRS>EPSG:5723</SRS>
+ <SRS>EPSG:5724</SRS>
+ <SRS>EPSG:5725</SRS>
+ <SRS>EPSG:5726</SRS>
+ <SRS>EPSG:5727</SRS>
+ <SRS>EPSG:5728</SRS>
+ <SRS>EPSG:5729</SRS>
+ <SRS>EPSG:5730</SRS>
+ <SRS>EPSG:5731</SRS>
+ <SRS>EPSG:5732</SRS>
+ <SRS>EPSG:5733</SRS>
+ <SRS>EPSG:5734</SRS>
+ <SRS>EPSG:5735</SRS>
+ <SRS>EPSG:5736</SRS>
+ <SRS>EPSG:5737</SRS>
+ <SRS>EPSG:5738</SRS>
+ <SRS>EPSG:5739</SRS>
+ <SRS>EPSG:5740</SRS>
+ <SRS>EPSG:5741</SRS>
+ <SRS>EPSG:5742</SRS>
+ <SRS>EPSG:5743</SRS>
+ <SRS>EPSG:5744</SRS>
+ <SRS>EPSG:5745</SRS>
+ <SRS>EPSG:5746</SRS>
+ <SRS>EPSG:5747</SRS>
+ <SRS>EPSG:5748</SRS>
+ <SRS>EPSG:5749</SRS>
+ <SRS>EPSG:5750</SRS>
+ <SRS>EPSG:5751</SRS>
+ <SRS>EPSG:5752</SRS>
+ <SRS>EPSG:5753</SRS>
+ <SRS>EPSG:5754</SRS>
+ <SRS>EPSG:5755</SRS>
+ <SRS>EPSG:5756</SRS>
+ <SRS>EPSG:5757</SRS>
+ <SRS>EPSG:5758</SRS>
+ <SRS>EPSG:5759</SRS>
+ <SRS>EPSG:5760</SRS>
+ <SRS>EPSG:5761</SRS>
+ <SRS>EPSG:5762</SRS>
+ <SRS>EPSG:5763</SRS>
+ <SRS>EPSG:5764</SRS>
+ <SRS>EPSG:5765</SRS>
+ <SRS>EPSG:5766</SRS>
+ <SRS>EPSG:5767</SRS>
+ <SRS>EPSG:5768</SRS>
+ <SRS>EPSG:5769</SRS>
+ <SRS>EPSG:5770</SRS>
+ <SRS>EPSG:5771</SRS>
+ <SRS>EPSG:5772</SRS>
+ <SRS>EPSG:5773</SRS>
+ <SRS>EPSG:5774</SRS>
+ <SRS>EPSG:5775</SRS>
+ <SRS>EPSG:5776</SRS>
+ <SRS>EPSG:5777</SRS>
+ <SRS>EPSG:5778</SRS>
+ <SRS>EPSG:5779</SRS>
+ <SRS>EPSG:5780</SRS>
+ <SRS>EPSG:5781</SRS>
+ <SRS>EPSG:5782</SRS>
+ <SRS>EPSG:5783</SRS>
+ <SRS>EPSG:5784</SRS>
+ <SRS>EPSG:5785</SRS>
+ <SRS>EPSG:5786</SRS>
+ <SRS>EPSG:5787</SRS>
+ <SRS>EPSG:5788</SRS>
+ <SRS>EPSG:5789</SRS>
+ <SRS>EPSG:5790</SRS>
+ <SRS>EPSG:5791</SRS>
+ <SRS>EPSG:5792</SRS>
+ <SRS>EPSG:5793</SRS>
+ <SRS>EPSG:5794</SRS>
+ <SRS>EPSG:5795</SRS>
+ <SRS>EPSG:5796</SRS>
+ <SRS>EPSG:5797</SRS>
+ <SRS>EPSG:5798</SRS>
+ <SRS>EPSG:5799</SRS>
+ <SRS>EPSG:5800</SRS>
+ <SRS>EPSG:5801</SRS>
+ <SRS>EPSG:5802</SRS>
+ <SRS>EPSG:5803</SRS>
+ <SRS>EPSG:5804</SRS>
+ <SRS>EPSG:5805</SRS>
+ <SRS>EPSG:5806</SRS>
+ <SRS>EPSG:5807</SRS>
+ <SRS>EPSG:5808</SRS>
+ <SRS>EPSG:5809</SRS>
+ <SRS>EPSG:5810</SRS>
+ <SRS>EPSG:5811</SRS>
+ <SRS>EPSG:5812</SRS>
+ <SRS>EPSG:5813</SRS>
+ <SRS>EPSG:5814</SRS>
+ <SRS>EPSG:5815</SRS>
+ <SRS>EPSG:5816</SRS>
+ <SRS>EPSG:5817</SRS>
+ <SRS>EPSG:5818</SRS>
+ <SRS>EPSG:7400</SRS>
+ <SRS>EPSG:7401</SRS>
+ <SRS>EPSG:7402</SRS>
+ <SRS>EPSG:7403</SRS>
+ <SRS>EPSG:7404</SRS>
+ <SRS>EPSG:7405</SRS>
+ <SRS>EPSG:7406</SRS>
+ <SRS>EPSG:7407</SRS>
+ <SRS>EPSG:7408</SRS>
+ <SRS>EPSG:7409</SRS>
+ <SRS>EPSG:7410</SRS>
+ <SRS>EPSG:7411</SRS>
+ <SRS>EPSG:7412</SRS>
+ <SRS>EPSG:7413</SRS>
+ <SRS>EPSG:7414</SRS>
+ <SRS>EPSG:7415</SRS>
+ <SRS>EPSG:7416</SRS>
+ <SRS>EPSG:7417</SRS>
+ <SRS>EPSG:7418</SRS>
+ <SRS>EPSG:7419</SRS>
+ <SRS>EPSG:7420</SRS>
+ <SRS>EPSG:20004</SRS>
+ <SRS>EPSG:20005</SRS>
+ <SRS>EPSG:20006</SRS>
+ <SRS>EPSG:20007</SRS>
+ <SRS>EPSG:20008</SRS>
+ <SRS>EPSG:20009</SRS>
+ <SRS>EPSG:20010</SRS>
+ <SRS>EPSG:20011</SRS>
+ <SRS>EPSG:20012</SRS>
+ <SRS>EPSG:20013</SRS>
+ <SRS>EPSG:20014</SRS>
+ <SRS>EPSG:20015</SRS>
+ <SRS>EPSG:20016</SRS>
+ <SRS>EPSG:20017</SRS>
+ <SRS>EPSG:20018</SRS>
+ <SRS>EPSG:20019</SRS>
+ <SRS>EPSG:20020</SRS>
+ <SRS>EPSG:20021</SRS>
+ <SRS>EPSG:20022</SRS>
+ <SRS>EPSG:20023</SRS>
+ <SRS>EPSG:20024</SRS>
+ <SRS>EPSG:20025</SRS>
+ <SRS>EPSG:20026</SRS>
+ <SRS>EPSG:20027</SRS>
+ <SRS>EPSG:20028</SRS>
+ <SRS>EPSG:20029</SRS>
+ <SRS>EPSG:20030</SRS>
+ <SRS>EPSG:20031</SRS>
+ <SRS>EPSG:20032</SRS>
+ <SRS>EPSG:20064</SRS>
+ <SRS>EPSG:20065</SRS>
+ <SRS>EPSG:20066</SRS>
+ <SRS>EPSG:20067</SRS>
+ <SRS>EPSG:20068</SRS>
+ <SRS>EPSG:20069</SRS>
+ <SRS>EPSG:20070</SRS>
+ <SRS>EPSG:20071</SRS>
+ <SRS>EPSG:20072</SRS>
+ <SRS>EPSG:20073</SRS>
+ <SRS>EPSG:20074</SRS>
+ <SRS>EPSG:20075</SRS>
+ <SRS>EPSG:20076</SRS>
+ <SRS>EPSG:20077</SRS>
+ <SRS>EPSG:20078</SRS>
+ <SRS>EPSG:20079</SRS>
+ <SRS>EPSG:20080</SRS>
+ <SRS>EPSG:20081</SRS>
+ <SRS>EPSG:20082</SRS>
+ <SRS>EPSG:20083</SRS>
+ <SRS>EPSG:20084</SRS>
+ <SRS>EPSG:20085</SRS>
+ <SRS>EPSG:20086</SRS>
+ <SRS>EPSG:20087</SRS>
+ <SRS>EPSG:20088</SRS>
+ <SRS>EPSG:20089</SRS>
+ <SRS>EPSG:20090</SRS>
+ <SRS>EPSG:20091</SRS>
+ <SRS>EPSG:20092</SRS>
+ <SRS>EPSG:20135</SRS>
+ <SRS>EPSG:20136</SRS>
+ <SRS>EPSG:20137</SRS>
+ <SRS>EPSG:20138</SRS>
+ <SRS>EPSG:20248</SRS>
+ <SRS>EPSG:20249</SRS>
+ <SRS>EPSG:20250</SRS>
+ <SRS>EPSG:20251</SRS>
+ <SRS>EPSG:20252</SRS>
+ <SRS>EPSG:20253</SRS>
+ <SRS>EPSG:20254</SRS>
+ <SRS>EPSG:20255</SRS>
+ <SRS>EPSG:20256</SRS>
+ <SRS>EPSG:20257</SRS>
+ <SRS>EPSG:20258</SRS>
+ <SRS>EPSG:20348</SRS>
+ <SRS>EPSG:20349</SRS>
+ <SRS>EPSG:20350</SRS>
+ <SRS>EPSG:20351</SRS>
+ <SRS>EPSG:20352</SRS>
+ <SRS>EPSG:20353</SRS>
+ <SRS>EPSG:20354</SRS>
+ <SRS>EPSG:20355</SRS>
+ <SRS>EPSG:20356</SRS>
+ <SRS>EPSG:20357</SRS>
+ <SRS>EPSG:20358</SRS>
+ <SRS>EPSG:20436</SRS>
+ <SRS>EPSG:20437</SRS>
+ <SRS>EPSG:20438</SRS>
+ <SRS>EPSG:20439</SRS>
+ <SRS>EPSG:20440</SRS>
+ <SRS>EPSG:20499</SRS>
+ <SRS>EPSG:20538</SRS>
+ <SRS>EPSG:20539</SRS>
+ <SRS>EPSG:20790</SRS>
+ <SRS>EPSG:20791</SRS>
+ <SRS>EPSG:20822</SRS>
+ <SRS>EPSG:20823</SRS>
+ <SRS>EPSG:20824</SRS>
+ <SRS>EPSG:20934</SRS>
+ <SRS>EPSG:20935</SRS>
+ <SRS>EPSG:20936</SRS>
+ <SRS>EPSG:21035</SRS>
+ <SRS>EPSG:21036</SRS>
+ <SRS>EPSG:21037</SRS>
+ <SRS>EPSG:21095</SRS>
+ <SRS>EPSG:21096</SRS>
+ <SRS>EPSG:21097</SRS>
+ <SRS>EPSG:21100</SRS>
+ <SRS>EPSG:21148</SRS>
+ <SRS>EPSG:21149</SRS>
+ <SRS>EPSG:21150</SRS>
+ <SRS>EPSG:21291</SRS>
+ <SRS>EPSG:21292</SRS>
+ <SRS>EPSG:21413</SRS>
+ <SRS>EPSG:21414</SRS>
+ <SRS>EPSG:21415</SRS>
+ <SRS>EPSG:21416</SRS>
+ <SRS>EPSG:21417</SRS>
+ <SRS>EPSG:21418</SRS>
+ <SRS>EPSG:21419</SRS>
+ <SRS>EPSG:21420</SRS>
+ <SRS>EPSG:21421</SRS>
+ <SRS>EPSG:21422</SRS>
+ <SRS>EPSG:21423</SRS>
+ <SRS>EPSG:21453</SRS>
+ <SRS>EPSG:21454</SRS>
+ <SRS>EPSG:21455</SRS>
+ <SRS>EPSG:21456</SRS>
+ <SRS>EPSG:21457</SRS>
+ <SRS>EPSG:21458</SRS>
+ <SRS>EPSG:21459</SRS>
+ <SRS>EPSG:21460</SRS>
+ <SRS>EPSG:21461</SRS>
+ <SRS>EPSG:21462</SRS>
+ <SRS>EPSG:21463</SRS>
+ <SRS>EPSG:21473</SRS>
+ <SRS>EPSG:21474</SRS>
+ <SRS>EPSG:21475</SRS>
+ <SRS>EPSG:21476</SRS>
+ <SRS>EPSG:21477</SRS>
+ <SRS>EPSG:21478</SRS>
+ <SRS>EPSG:21479</SRS>
+ <SRS>EPSG:21480</SRS>
+ <SRS>EPSG:21481</SRS>
+ <SRS>EPSG:21482</SRS>
+ <SRS>EPSG:21483</SRS>
+ <SRS>EPSG:21500</SRS>
+ <SRS>EPSG:21780</SRS>
+ <SRS>EPSG:21781</SRS>
+ <SRS>EPSG:21817</SRS>
+ <SRS>EPSG:21818</SRS>
+ <SRS>EPSG:21891</SRS>
+ <SRS>EPSG:21892</SRS>
+ <SRS>EPSG:21893</SRS>
+ <SRS>EPSG:21894</SRS>
+ <SRS>EPSG:21896</SRS>
+ <SRS>EPSG:21897</SRS>
+ <SRS>EPSG:21898</SRS>
+ <SRS>EPSG:21899</SRS>
+ <SRS>EPSG:22032</SRS>
+ <SRS>EPSG:22033</SRS>
+ <SRS>EPSG:22091</SRS>
+ <SRS>EPSG:22092</SRS>
+ <SRS>EPSG:22171</SRS>
+ <SRS>EPSG:22172</SRS>
+ <SRS>EPSG:22173</SRS>
+ <SRS>EPSG:22174</SRS>
+ <SRS>EPSG:22175</SRS>
+ <SRS>EPSG:22176</SRS>
+ <SRS>EPSG:22177</SRS>
+ <SRS>EPSG:22181</SRS>
+ <SRS>EPSG:22182</SRS>
+ <SRS>EPSG:22183</SRS>
+ <SRS>EPSG:22184</SRS>
+ <SRS>EPSG:22185</SRS>
+ <SRS>EPSG:22186</SRS>
+ <SRS>EPSG:22187</SRS>
+ <SRS>EPSG:22191</SRS>
+ <SRS>EPSG:22192</SRS>
+ <SRS>EPSG:22193</SRS>
+ <SRS>EPSG:22194</SRS>
+ <SRS>EPSG:22195</SRS>
+ <SRS>EPSG:22196</SRS>
+ <SRS>EPSG:22197</SRS>
+ <SRS>EPSG:22234</SRS>
+ <SRS>EPSG:22235</SRS>
+ <SRS>EPSG:22236</SRS>
+ <SRS>EPSG:22275</SRS>
+ <SRS>EPSG:22277</SRS>
+ <SRS>EPSG:22279</SRS>
+ <SRS>EPSG:22281</SRS>
+ <SRS>EPSG:22283</SRS>
+ <SRS>EPSG:22285</SRS>
+ <SRS>EPSG:22287</SRS>
+ <SRS>EPSG:22289</SRS>
+ <SRS>EPSG:22291</SRS>
+ <SRS>EPSG:22293</SRS>
+ <SRS>EPSG:22300</SRS>
+ <SRS>EPSG:22332</SRS>
+ <SRS>EPSG:22391</SRS>
+ <SRS>EPSG:22392</SRS>
+ <SRS>EPSG:22521</SRS>
+ <SRS>EPSG:22522</SRS>
+ <SRS>EPSG:22523</SRS>
+ <SRS>EPSG:22524</SRS>
+ <SRS>EPSG:22525</SRS>
+ <SRS>EPSG:22700</SRS>
+ <SRS>EPSG:22770</SRS>
+ <SRS>EPSG:22780</SRS>
+ <SRS>EPSG:22832</SRS>
+ <SRS>EPSG:22991</SRS>
+ <SRS>EPSG:22992</SRS>
+ <SRS>EPSG:22993</SRS>
+ <SRS>EPSG:22994</SRS>
+ <SRS>EPSG:23028</SRS>
+ <SRS>EPSG:23029</SRS>
+ <SRS>EPSG:23030</SRS>
+ <SRS>EPSG:23031</SRS>
+ <SRS>EPSG:23032</SRS>
+ <SRS>EPSG:23033</SRS>
+ <SRS>EPSG:23034</SRS>
+ <SRS>EPSG:23035</SRS>
+ <SRS>EPSG:23036</SRS>
+ <SRS>EPSG:23037</SRS>
+ <SRS>EPSG:23038</SRS>
+ <SRS>EPSG:23090</SRS>
+ <SRS>EPSG:23095</SRS>
+ <SRS>EPSG:23239</SRS>
+ <SRS>EPSG:23240</SRS>
+ <SRS>EPSG:23433</SRS>
+ <SRS>EPSG:23700</SRS>
+ <SRS>EPSG:23846</SRS>
+ <SRS>EPSG:23847</SRS>
+ <SRS>EPSG:23848</SRS>
+ <SRS>EPSG:23849</SRS>
+ <SRS>EPSG:23850</SRS>
+ <SRS>EPSG:23851</SRS>
+ <SRS>EPSG:23852</SRS>
+ <SRS>EPSG:23853</SRS>
+ <SRS>EPSG:23866</SRS>
+ <SRS>EPSG:23867</SRS>
+ <SRS>EPSG:23868</SRS>
+ <SRS>EPSG:23869</SRS>
+ <SRS>EPSG:23870</SRS>
+ <SRS>EPSG:23871</SRS>
+ <SRS>EPSG:23872</SRS>
+ <SRS>EPSG:23877</SRS>
+ <SRS>EPSG:23878</SRS>
+ <SRS>EPSG:23879</SRS>
+ <SRS>EPSG:23880</SRS>
+ <SRS>EPSG:23881</SRS>
+ <SRS>EPSG:23882</SRS>
+ <SRS>EPSG:23883</SRS>
+ <SRS>EPSG:23884</SRS>
+ <SRS>EPSG:23886</SRS>
+ <SRS>EPSG:23887</SRS>
+ <SRS>EPSG:23888</SRS>
+ <SRS>EPSG:23889</SRS>
+ <SRS>EPSG:23890</SRS>
+ <SRS>EPSG:23891</SRS>
+ <SRS>EPSG:23892</SRS>
+ <SRS>EPSG:23893</SRS>
+ <SRS>EPSG:23894</SRS>
+ <SRS>EPSG:23946</SRS>
+ <SRS>EPSG:23947</SRS>
+ <SRS>EPSG:23948</SRS>
+ <SRS>EPSG:24047</SRS>
+ <SRS>EPSG:24048</SRS>
+ <SRS>EPSG:24100</SRS>
+ <SRS>EPSG:24200</SRS>
+ <SRS>EPSG:24305</SRS>
+ <SRS>EPSG:24306</SRS>
+ <SRS>EPSG:24311</SRS>
+ <SRS>EPSG:24312</SRS>
+ <SRS>EPSG:24313</SRS>
+ <SRS>EPSG:24342</SRS>
+ <SRS>EPSG:24343</SRS>
+ <SRS>EPSG:24344</SRS>
+ <SRS>EPSG:24345</SRS>
+ <SRS>EPSG:24346</SRS>
+ <SRS>EPSG:24347</SRS>
+ <SRS>EPSG:24370</SRS>
+ <SRS>EPSG:24371</SRS>
+ <SRS>EPSG:24372</SRS>
+ <SRS>EPSG:24373</SRS>
+ <SRS>EPSG:24374</SRS>
+ <SRS>EPSG:24375</SRS>
+ <SRS>EPSG:24376</SRS>
+ <SRS>EPSG:24377</SRS>
+ <SRS>EPSG:24378</SRS>
+ <SRS>EPSG:24379</SRS>
+ <SRS>EPSG:24380</SRS>
+ <SRS>EPSG:24381</SRS>
+ <SRS>EPSG:24382</SRS>
+ <SRS>EPSG:24383</SRS>
+ <SRS>EPSG:24500</SRS>
+ <SRS>EPSG:24547</SRS>
+ <SRS>EPSG:24548</SRS>
+ <SRS>EPSG:24571</SRS>
+ <SRS>EPSG:24600</SRS>
+ <SRS>EPSG:24718</SRS>
+ <SRS>EPSG:24719</SRS>
+ <SRS>EPSG:24720</SRS>
+ <SRS>EPSG:24817</SRS>
+ <SRS>EPSG:24818</SRS>
+ <SRS>EPSG:24819</SRS>
+ <SRS>EPSG:24820</SRS>
+ <SRS>EPSG:24821</SRS>
+ <SRS>EPSG:24877</SRS>
+ <SRS>EPSG:24878</SRS>
+ <SRS>EPSG:24879</SRS>
+ <SRS>EPSG:24880</SRS>
+ <SRS>EPSG:24881</SRS>
+ <SRS>EPSG:24882</SRS>
+ <SRS>EPSG:24891</SRS>
+ <SRS>EPSG:24892</SRS>
+ <SRS>EPSG:24893</SRS>
+ <SRS>EPSG:25000</SRS>
+ <SRS>EPSG:25231</SRS>
+ <SRS>EPSG:25391</SRS>
+ <SRS>EPSG:25392</SRS>
+ <SRS>EPSG:25393</SRS>
+ <SRS>EPSG:25394</SRS>
+ <SRS>EPSG:25395</SRS>
+ <SRS>EPSG:25700</SRS>
+ <SRS>EPSG:25828</SRS>
+ <SRS>EPSG:25829</SRS>
+ <SRS>EPSG:25830</SRS>
+ <SRS>EPSG:25831</SRS>
+ <SRS>EPSG:25832</SRS>
+ <SRS>EPSG:25833</SRS>
+ <SRS>EPSG:25834</SRS>
+ <SRS>EPSG:25835</SRS>
+ <SRS>EPSG:25836</SRS>
+ <SRS>EPSG:25837</SRS>
+ <SRS>EPSG:25838</SRS>
+ <SRS>EPSG:25884</SRS>
+ <SRS>EPSG:25932</SRS>
+ <SRS>EPSG:26191</SRS>
+ <SRS>EPSG:26192</SRS>
+ <SRS>EPSG:26193</SRS>
+ <SRS>EPSG:26194</SRS>
+ <SRS>EPSG:26195</SRS>
+ <SRS>EPSG:26237</SRS>
+ <SRS>EPSG:26331</SRS>
+ <SRS>EPSG:26332</SRS>
+ <SRS>EPSG:26391</SRS>
+ <SRS>EPSG:26392</SRS>
+ <SRS>EPSG:26393</SRS>
+ <SRS>EPSG:26432</SRS>
+ <SRS>EPSG:26591</SRS>
+ <SRS>EPSG:26592</SRS>
+ <SRS>EPSG:26632</SRS>
+ <SRS>EPSG:26692</SRS>
+ <SRS>EPSG:26701</SRS>
+ <SRS>EPSG:26702</SRS>
+ <SRS>EPSG:26703</SRS>
+ <SRS>EPSG:26704</SRS>
+ <SRS>EPSG:26705</SRS>
+ <SRS>EPSG:26706</SRS>
+ <SRS>EPSG:26707</SRS>
+ <SRS>EPSG:26708</SRS>
+ <SRS>EPSG:26709</SRS>
+ <SRS>EPSG:26710</SRS>
+ <SRS>EPSG:26711</SRS>
+ <SRS>EPSG:26712</SRS>
+ <SRS>EPSG:26713</SRS>
+ <SRS>EPSG:26714</SRS>
+ <SRS>EPSG:26715</SRS>
+ <SRS>EPSG:26716</SRS>
+ <SRS>EPSG:26717</SRS>
+ <SRS>EPSG:26718</SRS>
+ <SRS>EPSG:26719</SRS>
+ <SRS>EPSG:26720</SRS>
+ <SRS>EPSG:26721</SRS>
+ <SRS>EPSG:26722</SRS>
+ <SRS>EPSG:26729</SRS>
+ <SRS>EPSG:26730</SRS>
+ <SRS>EPSG:26731</SRS>
+ <SRS>EPSG:26732</SRS>
+ <SRS>EPSG:26733</SRS>
+ <SRS>EPSG:26734</SRS>
+ <SRS>EPSG:26735</SRS>
+ <SRS>EPSG:26736</SRS>
+ <SRS>EPSG:26737</SRS>
+ <SRS>EPSG:26738</SRS>
+ <SRS>EPSG:26739</SRS>
+ <SRS>EPSG:26740</SRS>
+ <SRS>EPSG:26741</SRS>
+ <SRS>EPSG:26742</SRS>
+ <SRS>EPSG:26743</SRS>
+ <SRS>EPSG:26744</SRS>
+ <SRS>EPSG:26745</SRS>
+ <SRS>EPSG:26746</SRS>
+ <SRS>EPSG:26747</SRS>
+ <SRS>EPSG:26748</SRS>
+ <SRS>EPSG:26749</SRS>
+ <SRS>EPSG:26750</SRS>
+ <SRS>EPSG:26751</SRS>
+ <SRS>EPSG:26752</SRS>
+ <SRS>EPSG:26753</SRS>
+ <SRS>EPSG:26754</SRS>
+ <SRS>EPSG:26755</SRS>
+ <SRS>EPSG:26756</SRS>
+ <SRS>EPSG:26757</SRS>
+ <SRS>EPSG:26758</SRS>
+ <SRS>EPSG:26759</SRS>
+ <SRS>EPSG:26760</SRS>
+ <SRS>EPSG:26766</SRS>
+ <SRS>EPSG:26767</SRS>
+ <SRS>EPSG:26768</SRS>
+ <SRS>EPSG:26769</SRS>
+ <SRS>EPSG:26770</SRS>
+ <SRS>EPSG:26771</SRS>
+ <SRS>EPSG:26772</SRS>
+ <SRS>EPSG:26773</SRS>
+ <SRS>EPSG:26774</SRS>
+ <SRS>EPSG:26775</SRS>
+ <SRS>EPSG:26776</SRS>
+ <SRS>EPSG:26777</SRS>
+ <SRS>EPSG:26778</SRS>
+ <SRS>EPSG:26779</SRS>
+ <SRS>EPSG:26780</SRS>
+ <SRS>EPSG:26781</SRS>
+ <SRS>EPSG:26782</SRS>
+ <SRS>EPSG:26783</SRS>
+ <SRS>EPSG:26784</SRS>
+ <SRS>EPSG:26785</SRS>
+ <SRS>EPSG:26786</SRS>
+ <SRS>EPSG:26787</SRS>
+ <SRS>EPSG:26791</SRS>
+ <SRS>EPSG:26792</SRS>
+ <SRS>EPSG:26793</SRS>
+ <SRS>EPSG:26794</SRS>
+ <SRS>EPSG:26795</SRS>
+ <SRS>EPSG:26796</SRS>
+ <SRS>EPSG:26797</SRS>
+ <SRS>EPSG:26798</SRS>
+ <SRS>EPSG:26799</SRS>
+ <SRS>EPSG:26801</SRS>
+ <SRS>EPSG:26802</SRS>
+ <SRS>EPSG:26803</SRS>
+ <SRS>EPSG:26811</SRS>
+ <SRS>EPSG:26812</SRS>
+ <SRS>EPSG:26813</SRS>
+ <SRS>EPSG:26901</SRS>
+ <SRS>EPSG:26902</SRS>
+ <SRS>EPSG:26903</SRS>
+ <SRS>EPSG:26904</SRS>
+ <SRS>EPSG:26905</SRS>
+ <SRS>EPSG:26906</SRS>
+ <SRS>EPSG:26907</SRS>
+ <SRS>EPSG:26908</SRS>
+ <SRS>EPSG:26909</SRS>
+ <SRS>EPSG:26910</SRS>
+ <SRS>EPSG:26911</SRS>
+ <SRS>EPSG:26912</SRS>
+ <SRS>EPSG:26913</SRS>
+ <SRS>EPSG:26914</SRS>
+ <SRS>EPSG:26915</SRS>
+ <SRS>EPSG:26916</SRS>
+ <SRS>EPSG:26917</SRS>
+ <SRS>EPSG:26918</SRS>
+ <SRS>EPSG:26919</SRS>
+ <SRS>EPSG:26920</SRS>
+ <SRS>EPSG:26921</SRS>
+ <SRS>EPSG:26922</SRS>
+ <SRS>EPSG:26923</SRS>
+ <SRS>EPSG:26929</SRS>
+ <SRS>EPSG:26930</SRS>
+ <SRS>EPSG:26931</SRS>
+ <SRS>EPSG:26932</SRS>
+ <SRS>EPSG:26933</SRS>
+ <SRS>EPSG:26934</SRS>
+ <SRS>EPSG:26935</SRS>
+ <SRS>EPSG:26936</SRS>
+ <SRS>EPSG:26937</SRS>
+ <SRS>EPSG:26938</SRS>
+ <SRS>EPSG:26939</SRS>
+ <SRS>EPSG:26940</SRS>
+ <SRS>EPSG:26941</SRS>
+ <SRS>EPSG:26942</SRS>
+ <SRS>EPSG:26943</SRS>
+ <SRS>EPSG:26944</SRS>
+ <SRS>EPSG:26945</SRS>
+ <SRS>EPSG:26946</SRS>
+ <SRS>EPSG:26948</SRS>
+ <SRS>EPSG:26949</SRS>
+ <SRS>EPSG:26950</SRS>
+ <SRS>EPSG:26951</SRS>
+ <SRS>EPSG:26952</SRS>
+ <SRS>EPSG:26953</SRS>
+ <SRS>EPSG:26954</SRS>
+ <SRS>EPSG:26955</SRS>
+ <SRS>EPSG:26956</SRS>
+ <SRS>EPSG:26957</SRS>
+ <SRS>EPSG:26958</SRS>
+ <SRS>EPSG:26959</SRS>
+ <SRS>EPSG:26960</SRS>
+ <SRS>EPSG:26961</SRS>
+ <SRS>EPSG:26962</SRS>
+ <SRS>EPSG:26963</SRS>
+ <SRS>EPSG:26964</SRS>
+ <SRS>EPSG:26965</SRS>
+ <SRS>EPSG:26966</SRS>
+ <SRS>EPSG:26967</SRS>
+ <SRS>EPSG:26968</SRS>
+ <SRS>EPSG:26969</SRS>
+ <SRS>EPSG:26970</SRS>
+ <SRS>EPSG:26971</SRS>
+ <SRS>EPSG:26972</SRS>
+ <SRS>EPSG:26973</SRS>
+ <SRS>EPSG:26974</SRS>
+ <SRS>EPSG:26975</SRS>
+ <SRS>EPSG:26976</SRS>
+ <SRS>EPSG:26977</SRS>
+ <SRS>EPSG:26978</SRS>
+ <SRS>EPSG:26979</SRS>
+ <SRS>EPSG:26980</SRS>
+ <SRS>EPSG:26981</SRS>
+ <SRS>EPSG:26982</SRS>
+ <SRS>EPSG:26983</SRS>
+ <SRS>EPSG:26984</SRS>
+ <SRS>EPSG:26985</SRS>
+ <SRS>EPSG:26986</SRS>
+ <SRS>EPSG:26987</SRS>
+ <SRS>EPSG:26988</SRS>
+ <SRS>EPSG:26989</SRS>
+ <SRS>EPSG:26990</SRS>
+ <SRS>EPSG:26991</SRS>
+ <SRS>EPSG:26992</SRS>
+ <SRS>EPSG:26993</SRS>
+ <SRS>EPSG:26994</SRS>
+ <SRS>EPSG:26995</SRS>
+ <SRS>EPSG:26996</SRS>
+ <SRS>EPSG:26997</SRS>
+ <SRS>EPSG:26998</SRS>
+ <SRS>EPSG:27037</SRS>
+ <SRS>EPSG:27038</SRS>
+ <SRS>EPSG:27039</SRS>
+ <SRS>EPSG:27040</SRS>
+ <SRS>EPSG:27120</SRS>
+ <SRS>EPSG:27200</SRS>
+ <SRS>EPSG:27205</SRS>
+ <SRS>EPSG:27206</SRS>
+ <SRS>EPSG:27207</SRS>
+ <SRS>EPSG:27208</SRS>
+ <SRS>EPSG:27209</SRS>
+ <SRS>EPSG:27210</SRS>
+ <SRS>EPSG:27211</SRS>
+ <SRS>EPSG:27212</SRS>
+ <SRS>EPSG:27213</SRS>
+ <SRS>EPSG:27214</SRS>
+ <SRS>EPSG:27215</SRS>
+ <SRS>EPSG:27216</SRS>
+ <SRS>EPSG:27217</SRS>
+ <SRS>EPSG:27218</SRS>
+ <SRS>EPSG:27219</SRS>
+ <SRS>EPSG:27220</SRS>
+ <SRS>EPSG:27221</SRS>
+ <SRS>EPSG:27222</SRS>
+ <SRS>EPSG:27223</SRS>
+ <SRS>EPSG:27224</SRS>
+ <SRS>EPSG:27225</SRS>
+ <SRS>EPSG:27226</SRS>
+ <SRS>EPSG:27227</SRS>
+ <SRS>EPSG:27228</SRS>
+ <SRS>EPSG:27229</SRS>
+ <SRS>EPSG:27230</SRS>
+ <SRS>EPSG:27231</SRS>
+ <SRS>EPSG:27232</SRS>
+ <SRS>EPSG:27258</SRS>
+ <SRS>EPSG:27259</SRS>
+ <SRS>EPSG:27260</SRS>
+ <SRS>EPSG:27291</SRS>
+ <SRS>EPSG:27292</SRS>
+ <SRS>EPSG:27391</SRS>
+ <SRS>EPSG:27392</SRS>
+ <SRS>EPSG:27393</SRS>
+ <SRS>EPSG:27394</SRS>
+ <SRS>EPSG:27395</SRS>
+ <SRS>EPSG:27396</SRS>
+ <SRS>EPSG:27397</SRS>
+ <SRS>EPSG:27398</SRS>
+ <SRS>EPSG:27429</SRS>
+ <SRS>EPSG:27492</SRS>
+ <SRS>EPSG:27500</SRS>
+ <SRS>EPSG:27561</SRS>
+ <SRS>EPSG:27562</SRS>
+ <SRS>EPSG:27563</SRS>
+ <SRS>EPSG:27564</SRS>
+ <SRS>EPSG:27571</SRS>
+ <SRS>EPSG:27572</SRS>
+ <SRS>EPSG:27573</SRS>
+ <SRS>EPSG:27574</SRS>
+ <SRS>EPSG:27581</SRS>
+ <SRS>EPSG:27582</SRS>
+ <SRS>EPSG:27583</SRS>
+ <SRS>EPSG:27584</SRS>
+ <SRS>EPSG:27591</SRS>
+ <SRS>EPSG:27592</SRS>
+ <SRS>EPSG:27593</SRS>
+ <SRS>EPSG:27594</SRS>
+ <SRS>EPSG:27700</SRS>
+ <SRS>EPSG:28191</SRS>
+ <SRS>EPSG:28192</SRS>
+ <SRS>EPSG:28193</SRS>
+ <SRS>EPSG:28232</SRS>
+ <SRS>EPSG:28348</SRS>
+ <SRS>EPSG:28349</SRS>
+ <SRS>EPSG:28350</SRS>
+ <SRS>EPSG:28351</SRS>
+ <SRS>EPSG:28352</SRS>
+ <SRS>EPSG:28353</SRS>
+ <SRS>EPSG:28354</SRS>
+ <SRS>EPSG:28355</SRS>
+ <SRS>EPSG:28356</SRS>
+ <SRS>EPSG:28357</SRS>
+ <SRS>EPSG:28358</SRS>
+ <SRS>EPSG:28402</SRS>
+ <SRS>EPSG:28403</SRS>
+ <SRS>EPSG:28404</SRS>
+ <SRS>EPSG:28405</SRS>
+ <SRS>EPSG:28406</SRS>
+ <SRS>EPSG:28407</SRS>
+ <SRS>EPSG:28408</SRS>
+ <SRS>EPSG:28409</SRS>
+ <SRS>EPSG:28410</SRS>
+ <SRS>EPSG:28411</SRS>
+ <SRS>EPSG:28412</SRS>
+ <SRS>EPSG:28413</SRS>
+ <SRS>EPSG:28414</SRS>
+ <SRS>EPSG:28415</SRS>
+ <SRS>EPSG:28416</SRS>
+ <SRS>EPSG:28417</SRS>
+ <SRS>EPSG:28418</SRS>
+ <SRS>EPSG:28419</SRS>
+ <SRS>EPSG:28420</SRS>
+ <SRS>EPSG:28421</SRS>
+ <SRS>EPSG:28422</SRS>
+ <SRS>EPSG:28423</SRS>
+ <SRS>EPSG:28424</SRS>
+ <SRS>EPSG:28425</SRS>
+ <SRS>EPSG:28426</SRS>
+ <SRS>EPSG:28427</SRS>
+ <SRS>EPSG:28428</SRS>
+ <SRS>EPSG:28429</SRS>
+ <SRS>EPSG:28430</SRS>
+ <SRS>EPSG:28431</SRS>
+ <SRS>EPSG:28432</SRS>
+ <SRS>EPSG:28462</SRS>
+ <SRS>EPSG:28463</SRS>
+ <SRS>EPSG:28464</SRS>
+ <SRS>EPSG:28465</SRS>
+ <SRS>EPSG:28466</SRS>
+ <SRS>EPSG:28467</SRS>
+ <SRS>EPSG:28468</SRS>
+ <SRS>EPSG:28469</SRS>
+ <SRS>EPSG:28470</SRS>
+ <SRS>EPSG:28471</SRS>
+ <SRS>EPSG:28472</SRS>
+ <SRS>EPSG:28473</SRS>
+ <SRS>EPSG:28474</SRS>
+ <SRS>EPSG:28475</SRS>
+ <SRS>EPSG:28476</SRS>
+ <SRS>EPSG:28477</SRS>
+ <SRS>EPSG:28478</SRS>
+ <SRS>EPSG:28479</SRS>
+ <SRS>EPSG:28480</SRS>
+ <SRS>EPSG:28481</SRS>
+ <SRS>EPSG:28482</SRS>
+ <SRS>EPSG:28483</SRS>
+ <SRS>EPSG:28484</SRS>
+ <SRS>EPSG:28485</SRS>
+ <SRS>EPSG:28486</SRS>
+ <SRS>EPSG:28487</SRS>
+ <SRS>EPSG:28488</SRS>
+ <SRS>EPSG:28489</SRS>
+ <SRS>EPSG:28490</SRS>
+ <SRS>EPSG:28491</SRS>
+ <SRS>EPSG:28492</SRS>
+ <SRS>EPSG:28600</SRS>
+ <SRS>EPSG:28991</SRS>
+ <SRS>EPSG:28992</SRS>
+ <SRS>EPSG:29100</SRS>
+ <SRS>EPSG:29101</SRS>
+ <SRS>EPSG:29118</SRS>
+ <SRS>EPSG:29119</SRS>
+ <SRS>EPSG:29120</SRS>
+ <SRS>EPSG:29121</SRS>
+ <SRS>EPSG:29122</SRS>
+ <SRS>EPSG:29168</SRS>
+ <SRS>EPSG:29169</SRS>
+ <SRS>EPSG:29170</SRS>
+ <SRS>EPSG:29171</SRS>
+ <SRS>EPSG:29172</SRS>
+ <SRS>EPSG:29177</SRS>
+ <SRS>EPSG:29178</SRS>
+ <SRS>EPSG:29179</SRS>
+ <SRS>EPSG:29180</SRS>
+ <SRS>EPSG:29181</SRS>
+ <SRS>EPSG:29182</SRS>
+ <SRS>EPSG:29183</SRS>
+ <SRS>EPSG:29184</SRS>
+ <SRS>EPSG:29185</SRS>
+ <SRS>EPSG:29187</SRS>
+ <SRS>EPSG:29188</SRS>
+ <SRS>EPSG:29189</SRS>
+ <SRS>EPSG:29190</SRS>
+ <SRS>EPSG:29191</SRS>
+ <SRS>EPSG:29192</SRS>
+ <SRS>EPSG:29193</SRS>
+ <SRS>EPSG:29194</SRS>
+ <SRS>EPSG:29195</SRS>
+ <SRS>EPSG:29220</SRS>
+ <SRS>EPSG:29221</SRS>
+ <SRS>EPSG:29333</SRS>
+ <SRS>EPSG:29371</SRS>
+ <SRS>EPSG:29373</SRS>
+ <SRS>EPSG:29375</SRS>
+ <SRS>EPSG:29377</SRS>
+ <SRS>EPSG:29379</SRS>
+ <SRS>EPSG:29381</SRS>
+ <SRS>EPSG:29383</SRS>
+ <SRS>EPSG:29385</SRS>
+ <SRS>EPSG:29635</SRS>
+ <SRS>EPSG:29636</SRS>
+ <SRS>EPSG:29700</SRS>
+ <SRS>EPSG:29701</SRS>
+ <SRS>EPSG:29702</SRS>
+ <SRS>EPSG:29738</SRS>
+ <SRS>EPSG:29739</SRS>
+ <SRS>EPSG:29849</SRS>
+ <SRS>EPSG:29850</SRS>
+ <SRS>EPSG:29871</SRS>
+ <SRS>EPSG:29872</SRS>
+ <SRS>EPSG:29873</SRS>
+ <SRS>EPSG:29900</SRS>
+ <SRS>EPSG:29901</SRS>
+ <SRS>EPSG:29902</SRS>
+ <SRS>EPSG:29903</SRS>
+ <SRS>EPSG:30161</SRS>
+ <SRS>EPSG:30162</SRS>
+ <SRS>EPSG:30163</SRS>
+ <SRS>EPSG:30164</SRS>
+ <SRS>EPSG:30165</SRS>
+ <SRS>EPSG:30166</SRS>
+ <SRS>EPSG:30167</SRS>
+ <SRS>EPSG:30168</SRS>
+ <SRS>EPSG:30169</SRS>
+ <SRS>EPSG:30170</SRS>
+ <SRS>EPSG:30171</SRS>
+ <SRS>EPSG:30172</SRS>
+ <SRS>EPSG:30173</SRS>
+ <SRS>EPSG:30174</SRS>
+ <SRS>EPSG:30175</SRS>
+ <SRS>EPSG:30176</SRS>
+ <SRS>EPSG:30177</SRS>
+ <SRS>EPSG:30178</SRS>
+ <SRS>EPSG:30179</SRS>
+ <SRS>EPSG:30200</SRS>
+ <SRS>EPSG:30339</SRS>
+ <SRS>EPSG:30340</SRS>
+ <SRS>EPSG:30491</SRS>
+ <SRS>EPSG:30492</SRS>
+ <SRS>EPSG:30493</SRS>
+ <SRS>EPSG:30494</SRS>
+ <SRS>EPSG:30729</SRS>
+ <SRS>EPSG:30730</SRS>
+ <SRS>EPSG:30731</SRS>
+ <SRS>EPSG:30732</SRS>
+ <SRS>EPSG:30791</SRS>
+ <SRS>EPSG:30792</SRS>
+ <SRS>EPSG:30800</SRS>
+ <SRS>EPSG:31028</SRS>
+ <SRS>EPSG:31121</SRS>
+ <SRS>EPSG:31154</SRS>
+ <SRS>EPSG:31170</SRS>
+ <SRS>EPSG:31171</SRS>
+ <SRS>EPSG:31251</SRS>
+ <SRS>EPSG:31252</SRS>
+ <SRS>EPSG:31253</SRS>
+ <SRS>EPSG:31254</SRS>
+ <SRS>EPSG:31255</SRS>
+ <SRS>EPSG:31256</SRS>
+ <SRS>EPSG:31257</SRS>
+ <SRS>EPSG:31258</SRS>
+ <SRS>EPSG:31259</SRS>
+ <SRS>EPSG:31265</SRS>
+ <SRS>EPSG:31266</SRS>
+ <SRS>EPSG:31267</SRS>
+ <SRS>EPSG:31268</SRS>
+ <SRS>EPSG:31275</SRS>
+ <SRS>EPSG:31276</SRS>
+ <SRS>EPSG:31277</SRS>
+ <SRS>EPSG:31278</SRS>
+ <SRS>EPSG:31279</SRS>
+ <SRS>EPSG:31281</SRS>
+ <SRS>EPSG:31282</SRS>
+ <SRS>EPSG:31283</SRS>
+ <SRS>EPSG:31284</SRS>
+ <SRS>EPSG:31285</SRS>
+ <SRS>EPSG:31286</SRS>
+ <SRS>EPSG:31287</SRS>
+ <SRS>EPSG:31288</SRS>
+ <SRS>EPSG:31289</SRS>
+ <SRS>EPSG:31290</SRS>
+ <SRS>EPSG:31291</SRS>
+ <SRS>EPSG:31292</SRS>
+ <SRS>EPSG:31293</SRS>
+ <SRS>EPSG:31294</SRS>
+ <SRS>EPSG:31295</SRS>
+ <SRS>EPSG:31296</SRS>
+ <SRS>EPSG:31297</SRS>
+ <SRS>EPSG:31300</SRS>
+ <SRS>EPSG:31370</SRS>
+ <SRS>EPSG:31461</SRS>
+ <SRS>EPSG:31462</SRS>
+ <SRS>EPSG:31463</SRS>
+ <SRS>EPSG:31464</SRS>
+ <SRS>EPSG:31465</SRS>
+ <SRS>EPSG:31466</SRS>
+ <SRS>EPSG:31467</SRS>
+ <SRS>EPSG:31468</SRS>
+ <SRS>EPSG:31469</SRS>
+ <SRS>EPSG:31528</SRS>
+ <SRS>EPSG:31529</SRS>
+ <SRS>EPSG:31600</SRS>
+ <SRS>EPSG:31700</SRS>
+ <SRS>EPSG:31838</SRS>
+ <SRS>EPSG:31839</SRS>
+ <SRS>EPSG:31900</SRS>
+ <SRS>EPSG:31901</SRS>
+ <SRS>EPSG:31965</SRS>
+ <SRS>EPSG:31966</SRS>
+ <SRS>EPSG:31967</SRS>
+ <SRS>EPSG:31968</SRS>
+ <SRS>EPSG:31969</SRS>
+ <SRS>EPSG:31970</SRS>
+ <SRS>EPSG:31971</SRS>
+ <SRS>EPSG:31972</SRS>
+ <SRS>EPSG:31973</SRS>
+ <SRS>EPSG:31974</SRS>
+ <SRS>EPSG:31975</SRS>
+ <SRS>EPSG:31976</SRS>
+ <SRS>EPSG:31977</SRS>
+ <SRS>EPSG:31978</SRS>
+ <SRS>EPSG:31979</SRS>
+ <SRS>EPSG:31980</SRS>
+ <SRS>EPSG:31981</SRS>
+ <SRS>EPSG:31982</SRS>
+ <SRS>EPSG:31983</SRS>
+ <SRS>EPSG:31984</SRS>
+ <SRS>EPSG:31985</SRS>
+ <SRS>EPSG:31986</SRS>
+ <SRS>EPSG:31987</SRS>
+ <SRS>EPSG:31988</SRS>
+ <SRS>EPSG:31989</SRS>
+ <SRS>EPSG:31990</SRS>
+ <SRS>EPSG:31991</SRS>
+ <SRS>EPSG:31992</SRS>
+ <SRS>EPSG:31993</SRS>
+ <SRS>EPSG:31994</SRS>
+ <SRS>EPSG:31995</SRS>
+ <SRS>EPSG:31996</SRS>
+ <SRS>EPSG:31997</SRS>
+ <SRS>EPSG:31998</SRS>
+ <SRS>EPSG:31999</SRS>
+ <SRS>EPSG:32000</SRS>
+ <SRS>EPSG:32001</SRS>
+ <SRS>EPSG:32002</SRS>
+ <SRS>EPSG:32003</SRS>
+ <SRS>EPSG:32005</SRS>
+ <SRS>EPSG:32006</SRS>
+ <SRS>EPSG:32007</SRS>
+ <SRS>EPSG:32008</SRS>
+ <SRS>EPSG:32009</SRS>
+ <SRS>EPSG:32010</SRS>
+ <SRS>EPSG:32011</SRS>
+ <SRS>EPSG:32012</SRS>
+ <SRS>EPSG:32013</SRS>
+ <SRS>EPSG:32014</SRS>
+ <SRS>EPSG:32015</SRS>
+ <SRS>EPSG:32016</SRS>
+ <SRS>EPSG:32017</SRS>
+ <SRS>EPSG:32018</SRS>
+ <SRS>EPSG:32019</SRS>
+ <SRS>EPSG:32020</SRS>
+ <SRS>EPSG:32021</SRS>
+ <SRS>EPSG:32022</SRS>
+ <SRS>EPSG:32023</SRS>
+ <SRS>EPSG:32024</SRS>
+ <SRS>EPSG:32025</SRS>
+ <SRS>EPSG:32026</SRS>
+ <SRS>EPSG:32027</SRS>
+ <SRS>EPSG:32028</SRS>
+ <SRS>EPSG:32029</SRS>
+ <SRS>EPSG:32030</SRS>
+ <SRS>EPSG:32031</SRS>
+ <SRS>EPSG:32033</SRS>
+ <SRS>EPSG:32034</SRS>
+ <SRS>EPSG:32035</SRS>
+ <SRS>EPSG:32036</SRS>
+ <SRS>EPSG:32037</SRS>
+ <SRS>EPSG:32038</SRS>
+ <SRS>EPSG:32039</SRS>
+ <SRS>EPSG:32040</SRS>
+ <SRS>EPSG:32041</SRS>
+ <SRS>EPSG:32042</SRS>
+ <SRS>EPSG:32043</SRS>
+ <SRS>EPSG:32044</SRS>
+ <SRS>EPSG:32045</SRS>
+ <SRS>EPSG:32046</SRS>
+ <SRS>EPSG:32047</SRS>
+ <SRS>EPSG:32048</SRS>
+ <SRS>EPSG:32049</SRS>
+ <SRS>EPSG:32050</SRS>
+ <SRS>EPSG:32051</SRS>
+ <SRS>EPSG:32052</SRS>
+ <SRS>EPSG:32053</SRS>
+ <SRS>EPSG:32054</SRS>
+ <SRS>EPSG:32055</SRS>
+ <SRS>EPSG:32056</SRS>
+ <SRS>EPSG:32057</SRS>
+ <SRS>EPSG:32058</SRS>
+ <SRS>EPSG:32061</SRS>
+ <SRS>EPSG:32062</SRS>
+ <SRS>EPSG:32064</SRS>
+ <SRS>EPSG:32065</SRS>
+ <SRS>EPSG:32066</SRS>
+ <SRS>EPSG:32067</SRS>
+ <SRS>EPSG:32074</SRS>
+ <SRS>EPSG:32075</SRS>
+ <SRS>EPSG:32076</SRS>
+ <SRS>EPSG:32077</SRS>
+ <SRS>EPSG:32081</SRS>
+ <SRS>EPSG:32082</SRS>
+ <SRS>EPSG:32083</SRS>
+ <SRS>EPSG:32084</SRS>
+ <SRS>EPSG:32085</SRS>
+ <SRS>EPSG:32086</SRS>
+ <SRS>EPSG:32098</SRS>
+ <SRS>EPSG:32099</SRS>
+ <SRS>EPSG:32100</SRS>
+ <SRS>EPSG:32104</SRS>
+ <SRS>EPSG:32107</SRS>
+ <SRS>EPSG:32108</SRS>
+ <SRS>EPSG:32109</SRS>
+ <SRS>EPSG:32110</SRS>
+ <SRS>EPSG:32111</SRS>
+ <SRS>EPSG:32112</SRS>
+ <SRS>EPSG:32113</SRS>
+ <SRS>EPSG:32114</SRS>
+ <SRS>EPSG:32115</SRS>
+ <SRS>EPSG:32116</SRS>
+ <SRS>EPSG:32117</SRS>
+ <SRS>EPSG:32118</SRS>
+ <SRS>EPSG:32119</SRS>
+ <SRS>EPSG:32120</SRS>
+ <SRS>EPSG:32121</SRS>
+ <SRS>EPSG:32122</SRS>
+ <SRS>EPSG:32123</SRS>
+ <SRS>EPSG:32124</SRS>
+ <SRS>EPSG:32125</SRS>
+ <SRS>EPSG:32126</SRS>
+ <SRS>EPSG:32127</SRS>
+ <SRS>EPSG:32128</SRS>
+ <SRS>EPSG:32129</SRS>
+ <SRS>EPSG:32130</SRS>
+ <SRS>EPSG:32133</SRS>
+ <SRS>EPSG:32134</SRS>
+ <SRS>EPSG:32135</SRS>
+ <SRS>EPSG:32136</SRS>
+ <SRS>EPSG:32137</SRS>
+ <SRS>EPSG:32138</SRS>
+ <SRS>EPSG:32139</SRS>
+ <SRS>EPSG:32140</SRS>
+ <SRS>EPSG:32141</SRS>
+ <SRS>EPSG:32142</SRS>
+ <SRS>EPSG:32143</SRS>
+ <SRS>EPSG:32144</SRS>
+ <SRS>EPSG:32145</SRS>
+ <SRS>EPSG:32146</SRS>
+ <SRS>EPSG:32147</SRS>
+ <SRS>EPSG:32148</SRS>
+ <SRS>EPSG:32149</SRS>
+ <SRS>EPSG:32150</SRS>
+ <SRS>EPSG:32151</SRS>
+ <SRS>EPSG:32152</SRS>
+ <SRS>EPSG:32153</SRS>
+ <SRS>EPSG:32154</SRS>
+ <SRS>EPSG:32155</SRS>
+ <SRS>EPSG:32156</SRS>
+ <SRS>EPSG:32157</SRS>
+ <SRS>EPSG:32158</SRS>
+ <SRS>EPSG:32161</SRS>
+ <SRS>EPSG:32164</SRS>
+ <SRS>EPSG:32165</SRS>
+ <SRS>EPSG:32166</SRS>
+ <SRS>EPSG:32167</SRS>
+ <SRS>EPSG:32180</SRS>
+ <SRS>EPSG:32181</SRS>
+ <SRS>EPSG:32182</SRS>
+ <SRS>EPSG:32183</SRS>
+ <SRS>EPSG:32184</SRS>
+ <SRS>EPSG:32185</SRS>
+ <SRS>EPSG:32186</SRS>
+ <SRS>EPSG:32187</SRS>
+ <SRS>EPSG:32188</SRS>
+ <SRS>EPSG:32189</SRS>
+ <SRS>EPSG:32190</SRS>
+ <SRS>EPSG:32191</SRS>
+ <SRS>EPSG:32192</SRS>
+ <SRS>EPSG:32193</SRS>
+ <SRS>EPSG:32194</SRS>
+ <SRS>EPSG:32195</SRS>
+ <SRS>EPSG:32196</SRS>
+ <SRS>EPSG:32197</SRS>
+ <SRS>EPSG:32198</SRS>
+ <SRS>EPSG:32199</SRS>
+ <SRS>EPSG:32201</SRS>
+ <SRS>EPSG:32202</SRS>
+ <SRS>EPSG:32203</SRS>
+ <SRS>EPSG:32204</SRS>
+ <SRS>EPSG:32205</SRS>
+ <SRS>EPSG:32206</SRS>
+ <SRS>EPSG:32207</SRS>
+ <SRS>EPSG:32208</SRS>
+ <SRS>EPSG:32209</SRS>
+ <SRS>EPSG:32210</SRS>
+ <SRS>EPSG:32211</SRS>
+ <SRS>EPSG:32212</SRS>
+ <SRS>EPSG:32213</SRS>
+ <SRS>EPSG:32214</SRS>
+ <SRS>EPSG:32215</SRS>
+ <SRS>EPSG:32216</SRS>
+ <SRS>EPSG:32217</SRS>
+ <SRS>EPSG:32218</SRS>
+ <SRS>EPSG:32219</SRS>
+ <SRS>EPSG:32220</SRS>
+ <SRS>EPSG:32221</SRS>
+ <SRS>EPSG:32222</SRS>
+ <SRS>EPSG:32223</SRS>
+ <SRS>EPSG:32224</SRS>
+ <SRS>EPSG:32225</SRS>
+ <SRS>EPSG:32226</SRS>
+ <SRS>EPSG:32227</SRS>
+ <SRS>EPSG:32228</SRS>
+ <SRS>EPSG:32229</SRS>
+ <SRS>EPSG:32230</SRS>
+ <SRS>EPSG:32231</SRS>
+ <SRS>EPSG:32232</SRS>
+ <SRS>EPSG:32233</SRS>
+ <SRS>EPSG:32234</SRS>
+ <SRS>EPSG:32235</SRS>
+ <SRS>EPSG:32236</SRS>
+ <SRS>EPSG:32237</SRS>
+ <SRS>EPSG:32238</SRS>
+ <SRS>EPSG:32239</SRS>
+ <SRS>EPSG:32240</SRS>
+ <SRS>EPSG:32241</SRS>
+ <SRS>EPSG:32242</SRS>
+ <SRS>EPSG:32243</SRS>
+ <SRS>EPSG:32244</SRS>
+ <SRS>EPSG:32245</SRS>
+ <SRS>EPSG:32246</SRS>
+ <SRS>EPSG:32247</SRS>
+ <SRS>EPSG:32248</SRS>
+ <SRS>EPSG:32249</SRS>
+ <SRS>EPSG:32250</SRS>
+ <SRS>EPSG:32251</SRS>
+ <SRS>EPSG:32252</SRS>
+ <SRS>EPSG:32253</SRS>
+ <SRS>EPSG:32254</SRS>
+ <SRS>EPSG:32255</SRS>
+ <SRS>EPSG:32256</SRS>
+ <SRS>EPSG:32257</SRS>
+ <SRS>EPSG:32258</SRS>
+ <SRS>EPSG:32259</SRS>
+ <SRS>EPSG:32260</SRS>
+ <SRS>EPSG:32301</SRS>
+ <SRS>EPSG:32302</SRS>
+ <SRS>EPSG:32303</SRS>
+ <SRS>EPSG:32304</SRS>
+ <SRS>EPSG:32305</SRS>
+ <SRS>EPSG:32306</SRS>
+ <SRS>EPSG:32307</SRS>
+ <SRS>EPSG:32308</SRS>
+ <SRS>EPSG:32309</SRS>
+ <SRS>EPSG:32310</SRS>
+ <SRS>EPSG:32311</SRS>
+ <SRS>EPSG:32312</SRS>
+ <SRS>EPSG:32313</SRS>
+ <SRS>EPSG:32314</SRS>
+ <SRS>EPSG:32315</SRS>
+ <SRS>EPSG:32316</SRS>
+ <SRS>EPSG:32317</SRS>
+ <SRS>EPSG:32318</SRS>
+ <SRS>EPSG:32319</SRS>
+ <SRS>EPSG:32320</SRS>
+ <SRS>EPSG:32321</SRS>
+ <SRS>EPSG:32322</SRS>
+ <SRS>EPSG:32323</SRS>
+ <SRS>EPSG:32324</SRS>
+ <SRS>EPSG:32325</SRS>
+ <SRS>EPSG:32326</SRS>
+ <SRS>EPSG:32327</SRS>
+ <SRS>EPSG:32328</SRS>
+ <SRS>EPSG:32329</SRS>
+ <SRS>EPSG:32330</SRS>
+ <SRS>EPSG:32331</SRS>
+ <SRS>EPSG:32332</SRS>
+ <SRS>EPSG:32333</SRS>
+ <SRS>EPSG:32334</SRS>
+ <SRS>EPSG:32335</SRS>
+ <SRS>EPSG:32336</SRS>
+ <SRS>EPSG:32337</SRS>
+ <SRS>EPSG:32338</SRS>
+ <SRS>EPSG:32339</SRS>
+ <SRS>EPSG:32340</SRS>
+ <SRS>EPSG:32341</SRS>
+ <SRS>EPSG:32342</SRS>
+ <SRS>EPSG:32343</SRS>
+ <SRS>EPSG:32344</SRS>
+ <SRS>EPSG:32345</SRS>
+ <SRS>EPSG:32346</SRS>
+ <SRS>EPSG:32347</SRS>
+ <SRS>EPSG:32348</SRS>
+ <SRS>EPSG:32349</SRS>
+ <SRS>EPSG:32350</SRS>
+ <SRS>EPSG:32351</SRS>
+ <SRS>EPSG:32352</SRS>
+ <SRS>EPSG:32353</SRS>
+ <SRS>EPSG:32354</SRS>
+ <SRS>EPSG:32355</SRS>
+ <SRS>EPSG:32356</SRS>
+ <SRS>EPSG:32357</SRS>
+ <SRS>EPSG:32358</SRS>
+ <SRS>EPSG:32359</SRS>
+ <SRS>EPSG:32360</SRS>
+ <SRS>EPSG:32401</SRS>
+ <SRS>EPSG:32402</SRS>
+ <SRS>EPSG:32403</SRS>
+ <SRS>EPSG:32404</SRS>
+ <SRS>EPSG:32405</SRS>
+ <SRS>EPSG:32406</SRS>
+ <SRS>EPSG:32407</SRS>
+ <SRS>EPSG:32408</SRS>
+ <SRS>EPSG:32409</SRS>
+ <SRS>EPSG:32410</SRS>
+ <SRS>EPSG:32411</SRS>
+ <SRS>EPSG:32412</SRS>
+ <SRS>EPSG:32413</SRS>
+ <SRS>EPSG:32414</SRS>
+ <SRS>EPSG:32415</SRS>
+ <SRS>EPSG:32416</SRS>
+ <SRS>EPSG:32417</SRS>
+ <SRS>EPSG:32418</SRS>
+ <SRS>EPSG:32419</SRS>
+ <SRS>EPSG:32420</SRS>
+ <SRS>EPSG:32421</SRS>
+ <SRS>EPSG:32422</SRS>
+ <SRS>EPSG:32423</SRS>
+ <SRS>EPSG:32424</SRS>
+ <SRS>EPSG:32425</SRS>
+ <SRS>EPSG:32426</SRS>
+ <SRS>EPSG:32427</SRS>
+ <SRS>EPSG:32428</SRS>
+ <SRS>EPSG:32429</SRS>
+ <SRS>EPSG:32430</SRS>
+ <SRS>EPSG:32431</SRS>
+ <SRS>EPSG:32432</SRS>
+ <SRS>EPSG:32433</SRS>
+ <SRS>EPSG:32434</SRS>
+ <SRS>EPSG:32435</SRS>
+ <SRS>EPSG:32436</SRS>
+ <SRS>EPSG:32437</SRS>
+ <SRS>EPSG:32438</SRS>
+ <SRS>EPSG:32439</SRS>
+ <SRS>EPSG:32440</SRS>
+ <SRS>EPSG:32441</SRS>
+ <SRS>EPSG:32442</SRS>
+ <SRS>EPSG:32443</SRS>
+ <SRS>EPSG:32444</SRS>
+ <SRS>EPSG:32445</SRS>
+ <SRS>EPSG:32446</SRS>
+ <SRS>EPSG:32447</SRS>
+ <SRS>EPSG:32448</SRS>
+ <SRS>EPSG:32449</SRS>
+ <SRS>EPSG:32450</SRS>
+ <SRS>EPSG:32451</SRS>
+ <SRS>EPSG:32452</SRS>
+ <SRS>EPSG:32453</SRS>
+ <SRS>EPSG:32454</SRS>
+ <SRS>EPSG:32455</SRS>
+ <SRS>EPSG:32456</SRS>
+ <SRS>EPSG:32457</SRS>
+ <SRS>EPSG:32458</SRS>
+ <SRS>EPSG:32459</SRS>
+ <SRS>EPSG:32460</SRS>
+ <SRS>EPSG:32501</SRS>
+ <SRS>EPSG:32502</SRS>
+ <SRS>EPSG:32503</SRS>
+ <SRS>EPSG:32504</SRS>
+ <SRS>EPSG:32505</SRS>
+ <SRS>EPSG:32506</SRS>
+ <SRS>EPSG:32507</SRS>
+ <SRS>EPSG:32508</SRS>
+ <SRS>EPSG:32509</SRS>
+ <SRS>EPSG:32510</SRS>
+ <SRS>EPSG:32511</SRS>
+ <SRS>EPSG:32512</SRS>
+ <SRS>EPSG:32513</SRS>
+ <SRS>EPSG:32514</SRS>
+ <SRS>EPSG:32515</SRS>
+ <SRS>EPSG:32516</SRS>
+ <SRS>EPSG:32517</SRS>
+ <SRS>EPSG:32518</SRS>
+ <SRS>EPSG:32519</SRS>
+ <SRS>EPSG:32520</SRS>
+ <SRS>EPSG:32521</SRS>
+ <SRS>EPSG:32522</SRS>
+ <SRS>EPSG:32523</SRS>
+ <SRS>EPSG:32524</SRS>
+ <SRS>EPSG:32525</SRS>
+ <SRS>EPSG:32526</SRS>
+ <SRS>EPSG:32527</SRS>
+ <SRS>EPSG:32528</SRS>
+ <SRS>EPSG:32529</SRS>
+ <SRS>EPSG:32530</SRS>
+ <SRS>EPSG:32531</SRS>
+ <SRS>EPSG:32532</SRS>
+ <SRS>EPSG:32533</SRS>
+ <SRS>EPSG:32534</SRS>
+ <SRS>EPSG:32535</SRS>
+ <SRS>EPSG:32536</SRS>
+ <SRS>EPSG:32537</SRS>
+ <SRS>EPSG:32538</SRS>
+ <SRS>EPSG:32539</SRS>
+ <SRS>EPSG:32540</SRS>
+ <SRS>EPSG:32541</SRS>
+ <SRS>EPSG:32542</SRS>
+ <SRS>EPSG:32543</SRS>
+ <SRS>EPSG:32544</SRS>
+ <SRS>EPSG:32545</SRS>
+ <SRS>EPSG:32546</SRS>
+ <SRS>EPSG:32547</SRS>
+ <SRS>EPSG:32548</SRS>
+ <SRS>EPSG:32549</SRS>
+ <SRS>EPSG:32550</SRS>
+ <SRS>EPSG:32551</SRS>
+ <SRS>EPSG:32552</SRS>
+ <SRS>EPSG:32553</SRS>
+ <SRS>EPSG:32554</SRS>
+ <SRS>EPSG:32555</SRS>
+ <SRS>EPSG:32556</SRS>
+ <SRS>EPSG:32557</SRS>
+ <SRS>EPSG:32558</SRS>
+ <SRS>EPSG:32559</SRS>
+ <SRS>EPSG:32560</SRS>
+ <SRS>EPSG:32600</SRS>
+ <SRS>EPSG:32601</SRS>
+ <SRS>EPSG:32602</SRS>
+ <SRS>EPSG:32603</SRS>
+ <SRS>EPSG:32604</SRS>
+ <SRS>EPSG:32605</SRS>
+ <SRS>EPSG:32606</SRS>
+ <SRS>EPSG:32607</SRS>
+ <SRS>EPSG:32608</SRS>
+ <SRS>EPSG:32609</SRS>
+ <SRS>EPSG:32610</SRS>
+ <SRS>EPSG:32611</SRS>
+ <SRS>EPSG:32612</SRS>
+ <SRS>EPSG:32613</SRS>
+ <SRS>EPSG:32614</SRS>
+ <SRS>EPSG:32615</SRS>
+ <SRS>EPSG:32616</SRS>
+ <SRS>EPSG:32617</SRS>
+ <SRS>EPSG:32618</SRS>
+ <SRS>EPSG:32619</SRS>
+ <SRS>EPSG:32620</SRS>
+ <SRS>EPSG:32621</SRS>
+ <SRS>EPSG:32622</SRS>
+ <SRS>EPSG:32623</SRS>
+ <SRS>EPSG:32624</SRS>
+ <SRS>EPSG:32625</SRS>
+ <SRS>EPSG:32626</SRS>
+ <SRS>EPSG:32627</SRS>
+ <SRS>EPSG:32628</SRS>
+ <SRS>EPSG:32629</SRS>
+ <SRS>EPSG:32630</SRS>
+ <SRS>EPSG:32631</SRS>
+ <SRS>EPSG:32632</SRS>
+ <SRS>EPSG:32633</SRS>
+ <SRS>EPSG:32634</SRS>
+ <SRS>EPSG:32635</SRS>
+ <SRS>EPSG:32636</SRS>
+ <SRS>EPSG:32637</SRS>
+ <SRS>EPSG:32638</SRS>
+ <SRS>EPSG:32639</SRS>
+ <SRS>EPSG:32640</SRS>
+ <SRS>EPSG:32641</SRS>
+ <SRS>EPSG:32642</SRS>
+ <SRS>EPSG:32643</SRS>
+ <SRS>EPSG:32644</SRS>
+ <SRS>EPSG:32645</SRS>
+ <SRS>EPSG:32646</SRS>
+ <SRS>EPSG:32647</SRS>
+ <SRS>EPSG:32648</SRS>
+ <SRS>EPSG:32649</SRS>
+ <SRS>EPSG:32650</SRS>
+ <SRS>EPSG:32651</SRS>
+ <SRS>EPSG:32652</SRS>
+ <SRS>EPSG:32653</SRS>
+ <SRS>EPSG:32654</SRS>
+ <SRS>EPSG:32655</SRS>
+ <SRS>EPSG:32656</SRS>
+ <SRS>EPSG:32657</SRS>
+ <SRS>EPSG:32658</SRS>
+ <SRS>EPSG:32659</SRS>
+ <SRS>EPSG:32660</SRS>
+ <SRS>EPSG:32661</SRS>
+ <SRS>EPSG:32662</SRS>
+ <SRS>EPSG:32664</SRS>
+ <SRS>EPSG:32665</SRS>
+ <SRS>EPSG:32666</SRS>
+ <SRS>EPSG:32667</SRS>
+ <SRS>EPSG:32700</SRS>
+ <SRS>EPSG:32701</SRS>
+ <SRS>EPSG:32702</SRS>
+ <SRS>EPSG:32703</SRS>
+ <SRS>EPSG:32704</SRS>
+ <SRS>EPSG:32705</SRS>
+ <SRS>EPSG:32706</SRS>
+ <SRS>EPSG:32707</SRS>
+ <SRS>EPSG:32708</SRS>
+ <SRS>EPSG:32709</SRS>
+ <SRS>EPSG:32710</SRS>
+ <SRS>EPSG:32711</SRS>
+ <SRS>EPSG:32712</SRS>
+ <SRS>EPSG:32713</SRS>
+ <SRS>EPSG:32714</SRS>
+ <SRS>EPSG:32715</SRS>
+ <SRS>EPSG:32716</SRS>
+ <SRS>EPSG:32717</SRS>
+ <SRS>EPSG:32718</SRS>
+ <SRS>EPSG:32719</SRS>
+ <SRS>EPSG:32720</SRS>
+ <SRS>EPSG:32721</SRS>
+ <SRS>EPSG:32722</SRS>
+ <SRS>EPSG:32723</SRS>
+ <SRS>EPSG:32724</SRS>
+ <SRS>EPSG:32725</SRS>
+ <SRS>EPSG:32726</SRS>
+ <SRS>EPSG:32727</SRS>
+ <SRS>EPSG:32728</SRS>
+ <SRS>EPSG:32729</SRS>
+ <SRS>EPSG:32730</SRS>
+ <SRS>EPSG:32731</SRS>
+ <SRS>EPSG:32732</SRS>
+ <SRS>EPSG:32733</SRS>
+ <SRS>EPSG:32734</SRS>
+ <SRS>EPSG:32735</SRS>
+ <SRS>EPSG:32736</SRS>
+ <SRS>EPSG:32737</SRS>
+ <SRS>EPSG:32738</SRS>
+ <SRS>EPSG:32739</SRS>
+ <SRS>EPSG:32740</SRS>
+ <SRS>EPSG:32741</SRS>
+ <SRS>EPSG:32742</SRS>
+ <SRS>EPSG:32743</SRS>
+ <SRS>EPSG:32744</SRS>
+ <SRS>EPSG:32745</SRS>
+ <SRS>EPSG:32746</SRS>
+ <SRS>EPSG:32747</SRS>
+ <SRS>EPSG:32748</SRS>
+ <SRS>EPSG:32749</SRS>
+ <SRS>EPSG:32750</SRS>
+ <SRS>EPSG:32751</SRS>
+ <SRS>EPSG:32752</SRS>
+ <SRS>EPSG:32753</SRS>
+ <SRS>EPSG:32754</SRS>
+ <SRS>EPSG:32755</SRS>
+ <SRS>EPSG:32756</SRS>
+ <SRS>EPSG:32757</SRS>
+ <SRS>EPSG:32758</SRS>
+ <SRS>EPSG:32759</SRS>
+ <SRS>EPSG:32760</SRS>
+ <SRS>EPSG:32761</SRS>
+ <SRS>EPSG:32766</SRS>
+ <SRS>EPSG:61206405</SRS>
+ <SRS>EPSG:61216405</SRS>
+ <SRS>EPSG:61226405</SRS>
+ <SRS>EPSG:61236405</SRS>
+ <SRS>EPSG:61246405</SRS>
+ <SRS>EPSG:61266405</SRS>
+ <SRS>EPSG:61266413</SRS>
+ <SRS>EPSG:61276405</SRS>
+ <SRS>EPSG:61286405</SRS>
+ <SRS>EPSG:61296405</SRS>
+ <SRS>EPSG:61306405</SRS>
+ <SRS>EPSG:61306413</SRS>
+ <SRS>EPSG:61316405</SRS>
+ <SRS>EPSG:61326405</SRS>
+ <SRS>EPSG:61336405</SRS>
+ <SRS>EPSG:61346405</SRS>
+ <SRS>EPSG:61356405</SRS>
+ <SRS>EPSG:61366405</SRS>
+ <SRS>EPSG:61376405</SRS>
+ <SRS>EPSG:61386405</SRS>
+ <SRS>EPSG:61396405</SRS>
+ <SRS>EPSG:61406405</SRS>
+ <SRS>EPSG:61406413</SRS>
+ <SRS>EPSG:61416405</SRS>
+ <SRS>EPSG:61426405</SRS>
+ <SRS>EPSG:61436405</SRS>
+ <SRS>EPSG:61446405</SRS>
+ <SRS>EPSG:61456405</SRS>
+ <SRS>EPSG:61466405</SRS>
+ <SRS>EPSG:61476405</SRS>
+ <SRS>EPSG:61486405</SRS>
+ <SRS>EPSG:61486413</SRS>
+ <SRS>EPSG:61496405</SRS>
+ <SRS>EPSG:61506405</SRS>
+ <SRS>EPSG:61516405</SRS>
+ <SRS>EPSG:61516413</SRS>
+ <SRS>EPSG:61526405</SRS>
+ <SRS>EPSG:61526413</SRS>
+ <SRS>EPSG:61536405</SRS>
+ <SRS>EPSG:61546405</SRS>
+ <SRS>EPSG:61556405</SRS>
+ <SRS>EPSG:61566405</SRS>
+ <SRS>EPSG:61576405</SRS>
+ <SRS>EPSG:61586405</SRS>
+ <SRS>EPSG:61596405</SRS>
+ <SRS>EPSG:61606405</SRS>
+ <SRS>EPSG:61616405</SRS>
+ <SRS>EPSG:61626405</SRS>
+ <SRS>EPSG:61636405</SRS>
+ <SRS>EPSG:61636413</SRS>
+ <SRS>EPSG:61646405</SRS>
+ <SRS>EPSG:61656405</SRS>
+ <SRS>EPSG:61666405</SRS>
+ <SRS>EPSG:61676405</SRS>
+ <SRS>EPSG:61676413</SRS>
+ <SRS>EPSG:61686405</SRS>
+ <SRS>EPSG:61696405</SRS>
+ <SRS>EPSG:61706405</SRS>
+ <SRS>EPSG:61706413</SRS>
+ <SRS>EPSG:61716405</SRS>
+ <SRS>EPSG:61716413</SRS>
+ <SRS>EPSG:61736405</SRS>
+ <SRS>EPSG:61736413</SRS>
+ <SRS>EPSG:61746405</SRS>
+ <SRS>EPSG:61756405</SRS>
+ <SRS>EPSG:61766405</SRS>
+ <SRS>EPSG:61766413</SRS>
+ <SRS>EPSG:61786405</SRS>
+ <SRS>EPSG:61796405</SRS>
+ <SRS>EPSG:61806405</SRS>
+ <SRS>EPSG:61806413</SRS>
+ <SRS>EPSG:61816405</SRS>
+ <SRS>EPSG:61826405</SRS>
+ <SRS>EPSG:61836405</SRS>
+ <SRS>EPSG:61846405</SRS>
+ <SRS>EPSG:61886405</SRS>
+ <SRS>EPSG:61896405</SRS>
+ <SRS>EPSG:61896413</SRS>
+ <SRS>EPSG:61906405</SRS>
+ <SRS>EPSG:61906413</SRS>
+ <SRS>EPSG:61916405</SRS>
+ <SRS>EPSG:61926405</SRS>
+ <SRS>EPSG:61936405</SRS>
+ <SRS>EPSG:61946405</SRS>
+ <SRS>EPSG:61956405</SRS>
+ <SRS>EPSG:61966405</SRS>
+ <SRS>EPSG:61976405</SRS>
+ <SRS>EPSG:61986405</SRS>
+ <SRS>EPSG:61996405</SRS>
+ <SRS>EPSG:62006405</SRS>
+ <SRS>EPSG:62016405</SRS>
+ <SRS>EPSG:62026405</SRS>
+ <SRS>EPSG:62036405</SRS>
+ <SRS>EPSG:62046405</SRS>
+ <SRS>EPSG:62056405</SRS>
+ <SRS>EPSG:62066405</SRS>
+ <SRS>EPSG:62076405</SRS>
+ <SRS>EPSG:62086405</SRS>
+ <SRS>EPSG:62096405</SRS>
+ <SRS>EPSG:62106405</SRS>
+ <SRS>EPSG:62116405</SRS>
+ <SRS>EPSG:62126405</SRS>
+ <SRS>EPSG:62136405</SRS>
+ <SRS>EPSG:62146405</SRS>
+ <SRS>EPSG:62156405</SRS>
+ <SRS>EPSG:62166405</SRS>
+ <SRS>EPSG:62186405</SRS>
+ <SRS>EPSG:62196405</SRS>
+ <SRS>EPSG:62206405</SRS>
+ <SRS>EPSG:62216405</SRS>
+ <SRS>EPSG:62226405</SRS>
+ <SRS>EPSG:62236405</SRS>
+ <SRS>EPSG:62246405</SRS>
+ <SRS>EPSG:62256405</SRS>
+ <SRS>EPSG:62276405</SRS>
+ <SRS>EPSG:62296405</SRS>
+ <SRS>EPSG:62306405</SRS>
+ <SRS>EPSG:62316405</SRS>
+ <SRS>EPSG:62326405</SRS>
+ <SRS>EPSG:62336405</SRS>
+ <SRS>EPSG:62366405</SRS>
+ <SRS>EPSG:62376405</SRS>
+ <SRS>EPSG:62386405</SRS>
+ <SRS>EPSG:62396405</SRS>
+ <SRS>EPSG:62406405</SRS>
+ <SRS>EPSG:62416405</SRS>
+ <SRS>EPSG:62426405</SRS>
+ <SRS>EPSG:62436405</SRS>
+ <SRS>EPSG:62446405</SRS>
+ <SRS>EPSG:62456405</SRS>
+ <SRS>EPSG:62466405</SRS>
+ <SRS>EPSG:62476405</SRS>
+ <SRS>EPSG:62486405</SRS>
+ <SRS>EPSG:62496405</SRS>
+ <SRS>EPSG:62506405</SRS>
+ <SRS>EPSG:62516405</SRS>
+ <SRS>EPSG:62526405</SRS>
+ <SRS>EPSG:62536405</SRS>
+ <SRS>EPSG:62546405</SRS>
+ <SRS>EPSG:62556405</SRS>
+ <SRS>EPSG:62566405</SRS>
+ <SRS>EPSG:62576405</SRS>
+ <SRS>EPSG:62586405</SRS>
+ <SRS>EPSG:62586413</SRS>
+ <SRS>EPSG:62596405</SRS>
+ <SRS>EPSG:62616405</SRS>
+ <SRS>EPSG:62626405</SRS>
+ <SRS>EPSG:62636405</SRS>
+ <SRS>EPSG:62646405</SRS>
+ <SRS>EPSG:62656405</SRS>
+ <SRS>EPSG:62666405</SRS>
+ <SRS>EPSG:62676405</SRS>
+ <SRS>EPSG:62686405</SRS>
+ <SRS>EPSG:62696405</SRS>
+ <SRS>EPSG:62706405</SRS>
+ <SRS>EPSG:62716405</SRS>
+ <SRS>EPSG:62726405</SRS>
+ <SRS>EPSG:62736405</SRS>
+ <SRS>EPSG:62746405</SRS>
+ <SRS>EPSG:62756405</SRS>
+ <SRS>EPSG:62766405</SRS>
+ <SRS>EPSG:62776405</SRS>
+ <SRS>EPSG:62786405</SRS>
+ <SRS>EPSG:62796405</SRS>
+ <SRS>EPSG:62806405</SRS>
+ <SRS>EPSG:62816405</SRS>
+ <SRS>EPSG:62826405</SRS>
+ <SRS>EPSG:62836405</SRS>
+ <SRS>EPSG:62836413</SRS>
+ <SRS>EPSG:62846405</SRS>
+ <SRS>EPSG:62856405</SRS>
+ <SRS>EPSG:62866405</SRS>
+ <SRS>EPSG:62886405</SRS>
+ <SRS>EPSG:62896405</SRS>
+ <SRS>EPSG:62926405</SRS>
+ <SRS>EPSG:62936405</SRS>
+ <SRS>EPSG:62956405</SRS>
+ <SRS>EPSG:62976405</SRS>
+ <SRS>EPSG:62986405</SRS>
+ <SRS>EPSG:62996405</SRS>
+ <SRS>EPSG:63006405</SRS>
+ <SRS>EPSG:63016405</SRS>
+ <SRS>EPSG:63026405</SRS>
+ <SRS>EPSG:63036405</SRS>
+ <SRS>EPSG:63046405</SRS>
+ <SRS>EPSG:63066405</SRS>
+ <SRS>EPSG:63076405</SRS>
+ <SRS>EPSG:63086405</SRS>
+ <SRS>EPSG:63096405</SRS>
+ <SRS>EPSG:63106405</SRS>
+ <SRS>EPSG:63116405</SRS>
+ <SRS>EPSG:63126405</SRS>
+ <SRS>EPSG:63136405</SRS>
+ <SRS>EPSG:63146405</SRS>
+ <SRS>EPSG:63156405</SRS>
+ <SRS>EPSG:63166405</SRS>
+ <SRS>EPSG:63176405</SRS>
+ <SRS>EPSG:63186405</SRS>
+ <SRS>EPSG:63196405</SRS>
+ <SRS>EPSG:63226405</SRS>
+ <SRS>EPSG:63246405</SRS>
+ <SRS>EPSG:63266405</SRS>
+ <SRS>EPSG:63266406</SRS>
+ <SRS>EPSG:63266407</SRS>
+ <SRS>EPSG:63266408</SRS>
+ <SRS>EPSG:63266409</SRS>
+ <SRS>EPSG:63266410</SRS>
+ <SRS>EPSG:63266411</SRS>
+ <SRS>EPSG:63266412</SRS>
+ <SRS>EPSG:63266413</SRS>
+ <SRS>EPSG:63266414</SRS>
+ <SRS>EPSG:63266415</SRS>
+ <SRS>EPSG:63266416</SRS>
+ <SRS>EPSG:63266417</SRS>
+ <SRS>EPSG:63266418</SRS>
+ <SRS>EPSG:63266419</SRS>
+ <SRS>EPSG:63266420</SRS>
+ <SRS>EPSG:66006405</SRS>
+ <SRS>EPSG:66016405</SRS>
+ <SRS>EPSG:66026405</SRS>
+ <SRS>EPSG:66036405</SRS>
+ <SRS>EPSG:66046405</SRS>
+ <SRS>EPSG:66056405</SRS>
+ <SRS>EPSG:66066405</SRS>
+ <SRS>EPSG:66076405</SRS>
+ <SRS>EPSG:66086405</SRS>
+ <SRS>EPSG:66096405</SRS>
+ <SRS>EPSG:66106405</SRS>
+ <SRS>EPSG:66116405</SRS>
+ <SRS>EPSG:66126405</SRS>
+ <SRS>EPSG:66126413</SRS>
+ <SRS>EPSG:66136405</SRS>
+ <SRS>EPSG:66146405</SRS>
+ <SRS>EPSG:66156405</SRS>
+ <SRS>EPSG:66166405</SRS>
+ <SRS>EPSG:66186405</SRS>
+ <SRS>EPSG:66196405</SRS>
+ <SRS>EPSG:66196413</SRS>
+ <SRS>EPSG:66206405</SRS>
+ <SRS>EPSG:66216405</SRS>
+ <SRS>EPSG:66226405</SRS>
+ <SRS>EPSG:66236405</SRS>
+ <SRS>EPSG:66246405</SRS>
+ <SRS>EPSG:66246413</SRS>
+ <SRS>EPSG:66256405</SRS>
+ <SRS>EPSG:66266405</SRS>
+ <SRS>EPSG:66276405</SRS>
+ <SRS>EPSG:66276413</SRS>
+ <SRS>EPSG:66286405</SRS>
+ <SRS>EPSG:66296405</SRS>
+ <SRS>EPSG:66306405</SRS>
+ <SRS>EPSG:66316405</SRS>
+ <SRS>EPSG:66326405</SRS>
+ <SRS>EPSG:66336405</SRS>
+ <SRS>EPSG:66346405</SRS>
+ <SRS>EPSG:66356405</SRS>
+ <SRS>EPSG:66366405</SRS>
+ <SRS>EPSG:66376405</SRS>
+ <SRS>EPSG:66386405</SRS>
+ <SRS>EPSG:66396405</SRS>
+ <SRS>EPSG:66406405</SRS>
+ <SRS>EPSG:66406413</SRS>
+ <SRS>EPSG:66416405</SRS>
+ <SRS>EPSG:66426405</SRS>
+ <SRS>EPSG:66436405</SRS>
+ <SRS>EPSG:66446405</SRS>
+ <SRS>EPSG:66456405</SRS>
+ <SRS>EPSG:66456413</SRS>
+ <SRS>EPSG:66466405</SRS>
+ <SRS>EPSG:66576405</SRS>
+ <SRS>EPSG:66586405</SRS>
+ <SRS>EPSG:66596405</SRS>
+ <SRS>EPSG:66596413</SRS>
+ <SRS>EPSG:66606405</SRS>
+ <SRS>EPSG:66616405</SRS>
+ <SRS>EPSG:66616413</SRS>
+ <SRS>EPSG:66636405</SRS>
+ <SRS>EPSG:66646405</SRS>
+ <SRS>EPSG:66656405</SRS>
+ <SRS>EPSG:66666405</SRS>
+ <SRS>EPSG:66676405</SRS>
+ <SRS>EPSG:68016405</SRS>
+ <SRS>EPSG:68026405</SRS>
+ <SRS>EPSG:68036405</SRS>
+ <SRS>EPSG:68046405</SRS>
+ <SRS>EPSG:68056405</SRS>
+ <SRS>EPSG:68066405</SRS>
+ <SRS>EPSG:68086405</SRS>
+ <SRS>EPSG:68096405</SRS>
+ <SRS>EPSG:68136405</SRS>
+ <SRS>EPSG:68146405</SRS>
+ <SRS>EPSG:68156405</SRS>
+ <SRS>EPSG:68186405</SRS>
+ <SRS>EPSG:68206405</SRS>
+ <SRS>EPSG:69036405</SRS>
+ <SRS>EPSG:42302</SRS>
+ <SRS>EPSG:42301</SRS>
+ <SRS>EPSG:900913</SRS>
+ <SRS>EPSG:45556</SRS>
+ <SRS>EPSG:45555</SRS>
+ <SRS>EPSG:54004</SRS>
+ <SRS>EPSG:41001</SRS>
+ <SRS>EPSG:42311</SRS>
+ <SRS>EPSG:42310</SRS>
+ <SRS>EPSG:18001</SRS>
+ <SRS>EPSG:100003</SRS>
+ <SRS>EPSG:42106</SRS>
+ <SRS>EPSG:100002</SRS>
+ <SRS>EPSG:42105</SRS>
+ <SRS>EPSG:100001</SRS>
+ <SRS>EPSG:42309</SRS>
+ <SRS>EPSG:42104</SRS>
+ <SRS>EPSG:42308</SRS>
+ <SRS>EPSG:42103</SRS>
+ <SRS>EPSG:42307</SRS>
+ <SRS>EPSG:42102</SRS>
+ <SRS>EPSG:42306</SRS>
+ <SRS>EPSG:42101</SRS>
+ <SRS>EPSG:42305</SRS>
+ <SRS>EPSG:42304</SRS>
+ <SRS>EPSG:42303</SRS>
+ <LatLonBoundingBox minx="-257.0843245637291" miny="-257.0843245637291" maxx="257.0843245637291" maxy="257.0843245637291"/>
+ <Layer queryable="1">
+ <Name>tiger:poly_landmarks</Name>
+ <Title>Manhattan (NY) landmarks</Title>
+ <Abstract>Manhattan landmarks, identifies water, lakes, parks, interesting buildilngs</Abstract>
+ <KeywordList>
+ <Keyword>DS_poly_landmarks</Keyword>
+ <Keyword>poly_landmarks</Keyword>
+ <Keyword>landmarks</Keyword>
+ <Keyword>manhattan</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="-74.1008830202198" miny="40.65748247978021" maxx="-73.8541219797802" maxy="40.90424352021979"/>
+ <BoundingBox SRS="EPSG:4326" minx="-74.047185" miny="40.679648" maxx="-73.90782" maxy="40.882078"/>
+ <Style>
+ <Name>poly_landmarks</Name>
+ <Title>Default Styler</Title>
+ <Abstract/>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tiger:poly_landmarks"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>tiger:poi</Name>
+ <Title>Manhattan (NY) points of interest</Title>
+ <Abstract>Points of interest in New York, New York (on Manhattan). One of the attributes contains the name of a file with a picture of the point of interest.</Abstract>
+ <KeywordList>
+ <Keyword>poi</Keyword>
+ <Keyword>DS_poi</Keyword>
+ <Keyword>points_of_interest</Keyword>
+ <Keyword>Manhattan</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="-74.01288357289539" miny="40.70706518152972" maxx="-74.00752144792617" maxy="40.71242730649893"/>
+ <BoundingBox SRS="EPSG:4326" minx="-74.0118315772888" miny="40.70754683896324" maxx="-74.00153046439813" maxy="40.719885123828675"/>
+ <Style>
+ <Name>poi</Name>
+ <Title>Points of interest</Title>
+ <Abstract>Manhattan points of interest</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tiger:poi"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>tiger:tiger_roads</Name>
+ <Title>Manhattan (NY) roads</Title>
+ <Abstract>Highly simplified road layout of Manhattan in New York..</Abstract>
+ <KeywordList>
+ <Keyword>DS_tiger_roads</Keyword>
+ <Keyword>tiger_roads</Keyword>
+ <Keyword>roads</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="-74.08769307536667" miny="40.660618924633326" maxx="-73.84653192463333" maxy="40.90178007536667"/>
+ <BoundingBox SRS="EPSG:4326" minx="-74.02722" miny="40.684221" maxx="-73.907005" maxy="40.878178"/>
+ <Style>
+ <Name>tiger_roads</Name>
+ <Title>Default Styler</Title>
+ <Abstract/>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tiger:tiger_roads"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>sf:archsites</Name>
+ <Title>Spearfish archeological sites</Title>
+ <Abstract>Sample data from GRASS, archeological sites location, Spearfish, South Dakota, USA</Abstract>
+ <KeywordList>
+ <Keyword>archsites</Keyword>
+ <Keyword>sfArchsites</Keyword>
+ <Keyword>spearfish</Keyword>
+ <Keyword>archeology</Keyword>
+ </KeywordList>
+ <SRS>EPSG:26713</SRS>
+ <LatLonBoundingBox minx="-103.89000625326194" miny="44.29796961116877" maxx="-103.62049935931161" maxy="44.5674765051191"/>
+ <BoundingBox SRS="EPSG:26713" minx="588926.6865343997" miny="4913890.332215005" maxx="609271.2114429093" maxy="4927102.448786693"/>
+ <Style>
+ <Name>point</Name>
+ <Title>Default point</Title>
+ <Abstract>A sample style that just prints out a 6px wide red square</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=sf:archsites"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>sf:bugsites</Name>
+ <Title>Spearfish bug locations</Title>
+ <Abstract>Sample data from GRASS, bug sites location, Spearfish, South Dakota, USA</Abstract>
+ <KeywordList>
+ <Keyword>sfBugsites</Keyword>
+ <Keyword>bugsites</Keyword>
+ <Keyword>insects</Keyword>
+ <Keyword>spearfish</Keyword>
+ <Keyword>tiger_beetles</Keyword>
+ </KeywordList>
+ <SRS>EPSG:26713</SRS>
+ <LatLonBoundingBox minx="-103.89041901614995" miny="44.266492773791775" maxx="-103.61527753322848" maxy="44.54163425671326"/>
+ <BoundingBox SRS="EPSG:26713" minx="589311.4871629482" miny="4913787.082099182" maxx="609374.4115724327" maxy="4920844.691225147"/>
+ <Style>
+ <Name>capitals</Name>
+ <Title>Capital cities</Title>
+ <Abstract/>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=sf:bugsites"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>sf:restricted</Name>
+ <Title>Spearfish restricted areas</Title>
+ <Abstract>Sample data from GRASS, restricted areas, Spearfish, South Dakota, USA</Abstract>
+ <KeywordList>
+ <Keyword>restricted</Keyword>
+ <Keyword>sfRestricted</Keyword>
+ <Keyword>spearfish</Keyword>
+ <Keyword>areas</Keyword>
+ </KeywordList>
+ <SRS>EPSG:26713</SRS>
+ <LatLonBoundingBox minx="-103.86063428986338" miny="44.37661974734028" maxx="-103.73735238788223" maxy="44.49990164932145"/>
+ <BoundingBox SRS="EPSG:26713" minx="591175.6988413236" miny="4915754.888027622" maxx="600052.4121365736" maxy="4926353.920417598"/>
+ <Style>
+ <Name>restricted</Name>
+ <Title>Red, translucent style</Title>
+ <Abstract>A sample style that just prints out a transparent red interior with a red outline</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=sf:restricted"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>sf:roads</Name>
+ <Title>Spearfish roads</Title>
+ <Abstract>Sample data from GRASS, road layout, Spearfish, South Dakota, USA</Abstract>
+ <KeywordList>
+ <Keyword>sfRoads</Keyword>
+ <Keyword>roads</Keyword>
+ <Keyword>spearfish</Keyword>
+ </KeywordList>
+ <SRS>EPSG:26713</SRS>
+ <LatLonBoundingBox minx="-103.90534996703491" miny="44.2800314829381" maxx="-103.5943809967035" maxy="44.5910004532695"/>
+ <BoundingBox SRS="EPSG:26713" minx="588430.2387813567" miny="4913303.484828213" maxx="610531.8279023392" maxy="4928766.251023613"/>
+ <Style>
+ <Name>simple_roads</Name>
+ <Title>Default Styler for simple road segments</Title>
+ <Abstract>Light red line, 2px wide</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=sf:roads"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>sf:streams</Name>
+ <Title>Spearfish streams</Title>
+ <Abstract>Sample data from GRASS, streams, Spearfish, South Dakota, USA</Abstract>
+ <KeywordList>
+ <Keyword>sfStreams</Keyword>
+ <Keyword>streams</Keyword>
+ <Keyword>spearfish</Keyword>
+ </KeywordList>
+ <SRS>EPSG:26713</SRS>
+ <LatLonBoundingBox minx="-103.9089219204826" miny="44.278738996398694" maxx="-103.59184616696963" maxy="44.595814749911675"/>
+ <BoundingBox SRS="EPSG:26713" minx="588430.3113926318" miny="4913241.156915463" maxx="610522.3974737043" maxy="4928777.235349244"/>
+ <Style>
+ <Name>simple_streams</Name>
+ <Title>Default Styler for streams segments</Title>
+ <Abstract>Blue lines, 2px wide</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=sf:streams"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>topp:tasmania_cities</Name>
+ <Title>Tasmania cities</Title>
+ <Abstract>Cities in Tasmania (actually, just the capital)</Abstract>
+ <KeywordList>
+ <Keyword>cities</Keyword>
+ <Keyword>Tasmania</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="144.93357593664516" miny="-43.93984106335484" maxx="148.53694406335487" maxy="-40.33647293664516"/>
+ <BoundingBox SRS="EPSG:4326" minx="147.2910004483" miny="-42.851001816890005" maxx="147.2910004483" maxy="-42.851001816890005"/>
+ <Style>
+ <Name>capitals</Name>
+ <Title>Capital cities</Title>
+ <Abstract/>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:tasmania_cities"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>topp:tasmania_roads</Name>
+ <Title>Tasmania roads</Title>
+ <Abstract>Main Tasmania roads</Abstract>
+ <KeywordList>
+ <Keyword>Roads</Keyword>
+ <Keyword>Tasmania</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="144.8607879004856" miny="-44.01262909951439" maxx="148.60973209951442" maxy="-40.26368490048561"/>
+ <BoundingBox SRS="EPSG:4326" minx="145.19754" miny="-43.423512" maxx="148.27298000000002" maxy="-40.852802"/>
+ <Style>
+ <Name>simple_roads</Name>
+ <Title>Default Styler for simple road segments</Title>
+ <Abstract>Light red line, 2px wide</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:tasmania_roads"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>topp:tasmania_state_boundaries</Name>
+ <Title>Tasmania state boundaries</Title>
+ <Abstract>Tasmania state boundaries</Abstract>
+ <KeywordList>
+ <Keyword>tasmania_state_boundaries</Keyword>
+ <Keyword>Tasmania</Keyword>
+ <Keyword>boundaries</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="142.70637712387594" miny="-45.06157887612408" maxx="149.60758787612411" maxy="-38.16036812387592"/>
+ <BoundingBox SRS="EPSG:4326" minx="143.83482400000003" miny="-43.648056" maxx="148.47914100000003" maxy="-39.573891"/>
+ <Style>
+ <Name>green</Name>
+ <Title>Green polygon</Title>
+ <Abstract>Green fill with black outline</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:tasmania_state_boundaries"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>topp:tasmania_water_bodies</Name>
+ <Title>Tasmania water bodies</Title>
+ <Abstract>Tasmania water bodies</Abstract>
+ <KeywordList>
+ <Keyword>Lakes</Keyword>
+ <Keyword>Bodies</Keyword>
+ <Keyword>Australia</Keyword>
+ <Keyword>Water</Keyword>
+ <Keyword>Tasmania</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="145.82989373832018" miny="-43.16951476167979" maxx="147.3614212616798" maxy="-41.63798723832021"/>
+ <BoundingBox SRS="EPSG:4326" minx="145.97161899999998" miny="-43.031944" maxx="147.219696" maxy="-41.775558"/>
+ <Style>
+ <Name>cite_lakes</Name>
+ <Title>Blue lake</Title>
+ <Abstract>A blue fill, solid black outline style</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:tasmania_water_bodies"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>topp:states</Name>
+ <Title>USA Population</Title>
+ <Abstract>This is some census data on the states.</Abstract>
+ <KeywordList>
+ <Keyword>census</Keyword>
+ <Keyword>united</Keyword>
+ <Keyword>boundaries</Keyword>
+ <Keyword>state</Keyword>
+ <Keyword>states</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="-131.05615308855994" miny="1.958333411440066" maxx="-60.645117911440046" maxy="72.36936858855995"/>
+ <BoundingBox SRS="EPSG:4326" minx="-124.73142200000001" miny="24.955967" maxx="-66.969849" maxy="49.371735"/>
+ <Style>
+ <Name>population</Name>
+ <Title>Population in the United States</Title>
+ <Abstract>A sample filter that filters the United States into three
+ categories of population, drawn in different colors</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:states"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>tiger:giant_polygon</Name>
+ <Title>World rectangle</Title>
+ <Abstract>A simple rectangular polygon covering most of the world, it\'s only used for the purpose of providing a background (WMS bgcolor could be used instead)</Abstract>
+ <KeywordList>
+ <Keyword>DS_giant_polygon</Keyword>
+ <Keyword>giant_polygon</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="-257.0843245637291" miny="-257.0843245637291" maxx="257.0843245637291" maxy="257.0843245637291"/>
+ <BoundingBox SRS="EPSG:4326" minx="-180.0" miny="-90.0" maxx="180.0" maxy="90.0"/>
+ <Style>
+ <Name>giant_polygon</Name>
+ <Title>Border-less gray fill</Title>
+ <Abstract>Light gray polygon fill without a border</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tiger:giant_polygon"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>nurc:Arc_Sample</Name>
+ <Title>Global annual rainfall</Title>
+ <Abstract>Global annual rainfall in ArcGrid format</Abstract>
+ <KeywordList>
+ <Keyword>WCS</Keyword>
+ <Keyword>arcGridSample</Keyword>
+ <Keyword>arcGridSample_Coverage</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="-180.0" miny="-90.0" maxx="180.0" maxy="90.0"/>
+ <BoundingBox SRS="EPSG:4326" minx="-180.0" miny="-90.0" maxx="180.0" maxy="90.0"/>
+ <Style>
+ <Name>raster</Name>
+ <Title>Raster</Title>
+ <Abstract>A sample style for rasters, good for displaying imagery</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=nurc:Arc_Sample"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>nurc:Img_Sample</Name>
+ <Title>North America sample imagery</Title>
+ <Abstract>A very rough imagery of North America</Abstract>
+ <KeywordList>
+ <Keyword>WCS</Keyword>
+ <Keyword>worldImageSample</Keyword>
+ <Keyword>worldImageSample_Coverage</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="-130.85168" miny="20.7052" maxx="-62.0054" maxy="54.1141"/>
+ <BoundingBox SRS="EPSG:4326" minx="-130.85168" miny="20.7052" maxx="-62.0054" maxy="54.1141"/>
+ <Style>
+ <Name>raster</Name>
+ <Title>Raster</Title>
+ <Abstract>A sample style for rasters, good for displaying imagery</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=nurc:Img_Sample"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>nurc:mosaic</Name>
+ <Title>Sample PNG mosaic</Title>
+ <Abstract>Subsampled satellite imagery loaded as a mosaic of PNG images</Abstract>
+ <KeywordList>
+ <Keyword>WCS</Keyword>
+ <Keyword>mosaic</Keyword>
+ <Keyword>mosaic</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="6.34617490847439" miny="36.4917718219401" maxx="20.8296831527815" maxy="46.5907669751351"/>
+ <BoundingBox SRS="EPSG:4326" minx="6.34617490847439" miny="36.4917718219401" maxx="20.8296831527815" maxy="46.5907669751351"/>
+ <Style>
+ <Name>raster</Name>
+ <Title>Raster</Title>
+ <Abstract>A sample style for rasters, good for displaying imagery</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=nurc:mosaic"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>nurc:Pk50095</Name>
+ <Title>Sample scanned and georerenced map</Title>
+ <Abstract>This is a sample for the world image format (wld + prj + tiff)</Abstract>
+ <KeywordList>
+ <Keyword>WCS</Keyword>
+ <Keyword>img_sample2</Keyword>
+ <Keyword>Pk50095</Keyword>
+ </KeywordList>
+ <SRS>EPSG:32633</SRS>
+ <LatLonBoundingBox minx="12.999446822650462" miny="46.722110379286" maxx="13.308182612644663" maxy="46.91359611878293"/>
+ <BoundingBox SRS="EPSG:32633" minx="347649.93086859107" miny="5176214.082539256" maxx="370725.976428591" maxy="5196961.352859256"/>
+ <Style>
+ <Name>raster</Name>
+ <Title>Raster</Title>
+ <Abstract>A sample style for rasters, good for displaying imagery</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=nurc:Pk50095"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>sf:sfdem</Name>
+ <Title>sfdem is a Tagged Image File Format with Geographic information</Title>
+ <Abstract>Generated from sfdem</Abstract>
+ <KeywordList>
+ <Keyword>WCS</Keyword>
+ <Keyword>sfdem</Keyword>
+ <Keyword>sfdem</Keyword>
+ </KeywordList>
+ <SRS>EPSG:26713</SRS>
+ <LatLonBoundingBox minx="-103.87108701853181" miny="44.370187074132616" maxx="-103.62940739432703" maxy="44.5016011535299"/>
+ <BoundingBox SRS="EPSG:26713" minx="589980.0" miny="4913700.0" maxx="609000.0" maxy="4928010.0"/>
+ <Style>
+ <Name>dem</Name>
+ <Title>Simple DEM style</Title>
+ <Abstract>Classic elevation color progression</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://publicus.opengeo.org:80/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=sf:sfdem"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="0">
+ <Name>spearfish</Name>
+ <Title>spearfish</Title>
+ <Abstract>Layer-Group type layer: spearfish</Abstract>
+ <SRS>EPSG:26713</SRS>
+ <LatLonBoundingBox minx="-103.87799562257162" miny="44.37244213023845" maxx="-103.62286957414864" maxy="44.5023266635277"/>
+ <BoundingBox SRS="EPSG:26713" minx="589425.9342365642" miny="4913959.224611808" maxx="609518.6719560538" maxy="4928082.949945881"/>
+ </Layer>
+ <Layer queryable="0">
+ <Name>tasmania</Name>
+ <Title>tasmania</Title>
+ <Abstract>Layer-Group type layer: tasmania</Abstract>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="143.83482400000003" miny="-43.648056" maxx="148.47914100000003" maxy="-39.573891"/>
+ <BoundingBox SRS="EPSG:4326" minx="143.83482400000003" miny="-43.648056" maxx="148.47914100000003" maxy="-39.573891"/>
+ </Layer>
+ <Layer queryable="0">
+ <Name>tiger-ny</Name>
+ <Title>tiger-ny</Title>
+ <Abstract>Layer-Group type layer: tiger-ny</Abstract>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="-74.047185" miny="40.679648" maxx="-73.907005" maxy="40.882078"/>
+ <BoundingBox SRS="EPSG:4326" minx="-74.047185" miny="40.679648" maxx="-73.907005" maxy="40.882078"/>
+ </Layer>
+ </Layer>
+ </Capability>
+</WMT_MS_Capabilities>--></div>
+
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WMSCapabilities/v1_1_1_WMSC.html b/misc/openlayers/tests/Format/WMSCapabilities/v1_1_1_WMSC.html
new file mode 100644
index 0000000..044773d
--- /dev/null
+++ b/misc/openlayers/tests/Format/WMSCapabilities/v1_1_1_WMSC.html
@@ -0,0 +1,348 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read(t) {
+
+ t.plan(9);
+
+ var xml = document.getElementById("wmsc").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+ var format = new OpenLayers.Format.WMSCapabilities({profile: "WMSC"});
+ var obj = format.read(doc);
+ var tilesets = obj.capability.vendorSpecific.tileSets;
+ t.eq(tilesets.length, 2, "We expect 2 tilesets to be parsed");
+ var tileset = tilesets[0];
+ t.eq(tileset.bbox["EPSG:900913"].bbox, [-13697515.466796875, 5165920.118906248, -13619243.94984375, 5244191.635859374], "BBOX correctly parsed");
+ t.eq(tileset.format, "image/png", "Format correctly parsed");
+ t.eq(tileset.height, 256, "Height correctly parsed");
+ t.eq(tileset.width, 256, "Width correctly parsed");
+ t.eq(tileset.layers, "medford:hydro", "Layers correctly parsed");
+ t.eq(tileset.srs["EPSG:900913"], true, "SRS correctly parsed");
+ t.eq(tileset.resolutions, [156543.03390625, 78271.516953125, 39135.7584765625, 19567.87923828125, 9783.939619140625, 4891.9698095703125, 2445.9849047851562, 1222.9924523925781, 611.4962261962891, 305.74811309814453, 152.87405654907226, 76.43702827453613, 38.218514137268066, 19.109257068634033, 9.554628534317017, 4.777314267158508, 2.388657133579254, 1.194328566789627, 0.5971642833948135, 0.29858214169740677, 0.14929107084870338, 0.07464553542435169, 0.037322767712175846, 0.018661383856087923, 0.009330691928043961, 0.004665345964021981], "Resolutions correctly parsed");
+ t.eq(tileset.styles, "", "Styles correctly parsed");
+ }
+
+ function test_read_fallback(t) {
+ t.plan(1);
+ var xml = document.getElementById("fallback").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+ var format = new OpenLayers.Format.WMSCapabilities({profile: "WMSC", allowFallback: true});
+ var obj = format.read(doc);
+ t.eq(obj.capability.layers.length, 2, "2 layers parsed with allowFallback true");
+ }
+
+ </script>
+</head>
+<body>
+
+<div id="fallback"><!--
+<?xml version='1.0' encoding="ISO-8859-1" standalone="no" ?>
+<!DOCTYPE WMT_MS_Capabilities SYSTEM "http://schemas.opengis.net/wms/1.1.0/capabilities_1_1_0.dtd"
+ [
+ <!ELEMENT VendorSpecificCapabilities EMPTY>
+ ]>
+<WMT_MS_Capabilities version="1.1.0">
+
+<Service>
+ <Name>OGC:WMS</Name>
+ <Title>i3Geo - i3geo</Title>
+ <Abstract>Web services gerados da base de dados do i3Geo. Para chamar um tema especificamente, veja o sistema de ajuda, digitando no navegador web ogc.php?ajuda=, para uma lista compacta de todos os servicos, digite ogc.php?lista=temas</Abstract>
+ <KeywordList>
+ <Keyword>i3Geo</Keyword>
+ </KeywordList>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://mapas.mma.gov.br/i3geo/ogc.php?"/>
+ <ContactInformation>
+ <ContactPersonPrimary>
+ <ContactPerson>Web Master</ContactPerson>
+ <ContactOrganization>Coordena??o Geral de TI</ContactOrganization>
+ </ContactPersonPrimary>
+ <ContactPosition>Administrador do s?tio web</ContactPosition>
+ <ContactAddress>
+ <AddressType>uri</AddressType>
+ <Address>http://www.mma.gov.br</Address>
+ <City>Brasilia</City>
+ <StateOrProvince>DF</StateOrProvince>
+ <PostCode></PostCode>
+ <Country>Brasil</Country>
+ </ContactAddress>
+ <ContactElectronicMailAddress>geoprocessamento@mma.gov.br</ContactElectronicMailAddress>
+ </ContactInformation>
+ <Fees>none</Fees>
+ <AccessConstraints>vedado o uso comercial</AccessConstraints>
+</Service>
+
+<Capability>
+ <Request>
+ <GetCapabilities>
+ <Format>application/vnd.ogc.wms_xml</Format>
+ <DCPType>
+ <HTTP>
+ <Get><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://mapas.mma.gov.br/i3geo/ogc.php?"/></Get>
+ <Post><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://mapas.mma.gov.br/i3geo/ogc.php?"/></Post>
+ </HTTP>
+ </DCPType>
+ </GetCapabilities>
+ <GetMap>
+ <Format>image/png</Format>
+ <Format>image/jpeg</Format>
+ <Format>image/gif</Format>
+ <Format>image/png; mode=8bit</Format>
+ <Format>application/x-pdf</Format>
+ <Format>image/svg+xml</Format>
+ <Format>image/tiff</Format>
+ <Format>application/vnd.google-earth.kml+xml</Format>
+ <Format>application/vnd.google-earth.kmz</Format>
+ <DCPType>
+ <HTTP>
+ <Get><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://mapas.mma.gov.br/i3geo/ogc.php?"/></Get>
+ <Post><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://mapas.mma.gov.br/i3geo/ogc.php?"/></Post>
+ </HTTP>
+ </DCPType>
+ </GetMap>
+ <GetFeatureInfo>
+ <Format>text/plain</Format>
+ <Format>application/vnd.ogc.gml</Format>
+ <DCPType>
+ <HTTP>
+ <Get><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://mapas.mma.gov.br/i3geo/ogc.php?"/></Get>
+ <Post><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://mapas.mma.gov.br/i3geo/ogc.php?"/></Post>
+ </HTTP>
+ </DCPType>
+ </GetFeatureInfo>
+ <DescribeLayer>
+ <Format>text/xml</Format>
+ <DCPType>
+ <HTTP>
+ <Get><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://mapas.mma.gov.br/i3geo/ogc.php?"/></Get>
+ <Post><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://mapas.mma.gov.br/i3geo/ogc.php?"/></Post>
+ </HTTP>
+ </DCPType>
+ </DescribeLayer>
+ </Request>
+ <Exception>
+ <Format>application/vnd.ogc.se_xml</Format>
+ <Format>application/vnd.ogc.se_inimage</Format>
+ <Format>application/vnd.ogc.se_blank</Format>
+ </Exception>
+ <VendorSpecificCapabilities />
+ <UserDefinedSymbolization SupportSLD="1" UserLayer="0" UserStyle="1" RemoteWFS="0"/>
+ <Layer>
+ <Name>i3geoogc</Name>
+ <Title>i3Geo - i3geo</Title>
+ <Abstract>Web services gerados da base de dados do i3Geo. Para chamar um tema especificamente, veja o sistema de ajuda, digitando no navegador web ogc.php?ajuda=, para uma lista compacta de todos os servicos, digite ogc.php?lista=temas</Abstract>
+ <KeywordList>
+ <Keyword>i3Geo</Keyword>
+ </KeywordList>
+ <SRS></SRS>
+ <LatLonBoundingBox minx="-76.5126" miny="-36.9484" maxx="-29.5852" maxy="7.04601" />
+ <BoundingBox SRS=""
+ minx="-76.5126" miny="-36.9484" maxx="-29.5852" maxy="7.04601" />
+ <Attribution>
+ <Title>i3Geo</Title>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://mapas.mma.gov.br/i3geo"/>
+ <LogoURL width="85" height="56">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://mapas.mma.gov.br/i3geo/imagens/i3geo.png"/>
+ </LogoURL>
+ </Attribution>
+ <Layer queryable="1" opaque="0" cascaded="0">
+ <Name>antigo_caminantes</Name>
+ <Title>Guia de Caminantes - 1817</Title>
+ <SRS> EPSG:4618 EPSG:4291 EPSG:4326 EPSG:22521 EPSG:22522 EPSG:22523 EPSG:22524 EPSG:22525 EPSG:29101 EPSG:29119 EPSG:29120 EPSG:29121 EPSG:29122 EPSG:29177 EPSG:29178 EPSG:29179 EPSG:29180 EPSG:29181 EPSG:29182 EPSG:29183 EPSG:29184 EPSG:29185</SRS>
+ <LatLonBoundingBox minx="-75.2336" miny="-33.7516" maxx="-27.593" maxy="5.27216" />
+ <BoundingBox SRS=""
+ minx="-75.2336" miny="-33.7516" maxx="-27.593" maxy="5.27216" />
+ <MetadataURL type="TC211">
+ <Format>text/html</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://consorcio.bn.br"/>
+ </MetadataURL>
+ </Layer>
+ </Layer>
+</Capability>
+</WMT_MS_Capabilities>
+--></div>
+
+<div id="wmsc"><!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE WMT_MS_Capabilities SYSTEM "http://schemas.opengis.net/wms/1.1.1/capabilities_1_1_1.dtd"[
+<!ELEMENT VendorSpecificCapabilities (TileSet*) >
+<!ELEMENT TileSet (SRS, BoundingBox?, Resolutions, Width, Height, Format, Layers*, Styles*) >
+<!ELEMENT Resolutions (#PCDATA) >
+<!ELEMENT Width (#PCDATA) >
+<!ELEMENT Height (#PCDATA) >
+<!ELEMENT Layers (#PCDATA) >
+<!ELEMENT Styles (#PCDATA) >
+]>
+<WMT_MS_Capabilities version="1.1.1" updateSequence="57">
+ <Service>
+ <Name>OGC:WMS</Name>
+ <Title>GeoServer Web Map Service</Title>
+ <Abstract>A compliant implementation of WMS 1.1.1 plus most of the SLD 1.0 extension (dynamic styling). Can also generate PDF, SVG, KML, GeoRSS</Abstract>
+ <KeywordList>
+ <Keyword>WFS</Keyword>
+ <Keyword>WMS</Keyword>
+ <Keyword>GEOSERVER</Keyword>
+ </KeywordList>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://localhost:8080/geoserver-suite/wms"/>
+ <ContactInformation>
+ <ContactPersonPrimary>
+ <ContactPerson>OpenGeo</ContactPerson>
+ <ContactOrganization>OpenGeo</ContactOrganization>
+ </ContactPersonPrimary>
+ <ContactPosition>Outreach</ContactPosition>
+ <ContactAddress>
+ <AddressType>Work</AddressType>
+ <Address/>
+ <City>New York</City>
+ <StateOrProvince/>
+ <PostCode/>
+ <Country>USA</Country>
+ </ContactAddress>
+ <ContactVoiceTelephone/>
+ <ContactFacsimileTelephone/>
+ <ContactElectronicMailAddress>inquiry@opengeo.org</ContactElectronicMailAddress>
+ </ContactInformation>
+ <Fees>NONE</Fees>
+ <AccessConstraints>NONE</AccessConstraints>
+ </Service>
+ <Capability>
+ <Request>
+ <GetCapabilities>
+ <Format>application/vnd.ogc.wms_xml</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://localhost:8080/geoserver-suite/wms?SERVICE=WMS&amp;"/>
+ </Get>
+ <Post>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://localhost:8080/geoserver-suite/wms?SERVICE=WMS&amp;"/>
+ </Post>
+ </HTTP>
+ </DCPType>
+ </GetCapabilities>
+ <GetMap>
+ <Format>image/png</Format>
+ <Format>application/atom xml</Format>
+ <Format>application/atom+xml</Format>
+ <Format>application/openlayers</Format>
+ <Format>application/pdf</Format>
+ <Format>application/rss xml</Format>
+ <Format>application/rss+xml</Format>
+ <Format>application/vnd.google-earth.kml</Format>
+ <Format>application/vnd.google-earth.kml xml</Format>
+ <Format>application/vnd.google-earth.kml+xml</Format>
+ <Format>application/vnd.google-earth.kmz</Format>
+ <Format>application/vnd.google-earth.kmz xml</Format>
+ <Format>application/vnd.google-earth.kmz+xml</Format>
+ <Format>atom</Format>
+ <Format>image/geotiff</Format>
+ <Format>image/geotiff8</Format>
+ <Format>image/gif</Format>
+ <Format>image/jpeg</Format>
+ <Format>image/png8</Format>
+ <Format>image/svg</Format>
+ <Format>image/svg xml</Format>
+ <Format>image/svg+xml</Format>
+ <Format>image/tiff</Format>
+ <Format>image/tiff8</Format>
+ <Format>kml</Format>
+ <Format>kmz</Format>
+ <Format>openlayers</Format>
+ <Format>rss</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://localhost:8080/geoserver-suite/wms?SERVICE=WMS&amp;"/>
+ </Get>
+ </HTTP>
+ </DCPType>
+ </GetMap>
+ <GetFeatureInfo>
+ <Format>text/plain</Format>
+ <Format>application/vnd.ogc.gml</Format>
+ <Format>text/html</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://localhost:8080/geoserver-suite/wms?SERVICE=WMS&amp;"/>
+ </Get>
+ <Post>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://localhost:8080/geoserver-suite/wms?SERVICE=WMS&amp;"/>
+ </Post>
+ </HTTP>
+ </DCPType>
+ </GetFeatureInfo>
+ <DescribeLayer>
+ <Format>application/vnd.ogc.wms_xml</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://localhost:8080/geoserver-suite/wms?SERVICE=WMS&amp;"/>
+ </Get>
+ </HTTP>
+ </DCPType>
+ </DescribeLayer>
+ <GetLegendGraphic>
+ <Format>image/png</Format>
+ <Format>image/jpeg</Format>
+ <Format>image/gif</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://localhost:8080/geoserver-suite/wms?SERVICE=WMS&amp;"/>
+ </Get>
+ </HTTP>
+ </DCPType>
+ </GetLegendGraphic>
+ <GetStyles>
+ <Format>application/vnd.ogc.sld+xml</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://localhost:8080/geoserver-suite/wms?SERVICE=WMS&amp;"/>
+ </Get>
+ </HTTP>
+ </DCPType>
+ </GetStyles>
+ </Request>
+ <Exception>
+ <Format>application/vnd.ogc.se_xml</Format>
+ <Format>application/vnd.ogc.se_inimage</Format>
+ </Exception>
+ <VendorSpecificCapabilities>
+ <TileSet>
+ <SRS>EPSG:900913</SRS>
+ <BoundingBox SRS="EPSG:900913" minx="-1.3697515466796875E7" miny="5165920.118906248" maxx="-1.361924394984375E7" maxy="5244191.635859374"/>
+ <Resolutions>156543.03390625 78271.516953125 39135.7584765625 19567.87923828125 9783.939619140625 4891.9698095703125 2445.9849047851562 1222.9924523925781 611.4962261962891 305.74811309814453 152.87405654907226 76.43702827453613 38.218514137268066 19.109257068634033 9.554628534317017 4.777314267158508 2.388657133579254 1.194328566789627 0.5971642833948135 0.29858214169740677 0.14929107084870338 0.07464553542435169 0.037322767712175846 0.018661383856087923 0.009330691928043961 0.004665345964021981 </Resolutions>
+ <Width>256</Width>
+ <Height>256</Height>
+ <Format>image/png</Format>
+ <Layers>medford:hydro</Layers>
+ <Styles/>
+ </TileSet>
+ <TileSet>
+ <SRS>EPSG:4326</SRS>
+ <BoundingBox SRS="EPSG:4326" minx="-123.046875" miny="42.1875" maxx="-122.6953125" maxy="42.5390625"/>
+ <Resolutions>0.703125 0.3515625 0.17578125 0.087890625 0.0439453125 0.02197265625 0.010986328125 0.0054931640625 0.00274658203125 0.001373291015625 6.866455078125E-4 3.4332275390625E-4 1.71661376953125E-4 8.58306884765625E-5 4.291534423828125E-5 2.1457672119140625E-5 1.0728836059570312E-5 5.364418029785156E-6 2.682209014892578E-6 1.341104507446289E-6 6.705522537231445E-7 3.3527612686157227E-7 1.6763806343078613E-7 8.381903171539307E-8 4.190951585769653E-8 2.0954757928848267E-8 </Resolutions>
+ <Width>256</Width>
+ <Height>256</Height>
+ <Format>image/gif</Format>
+ <Layers>medford</Layers>
+ <Styles/>
+ </TileSet>
+ </VendorSpecificCapabilities>
+ <UserDefinedSymbolization SupportSLD="1" UserLayer="1" UserStyle="1" RemoteWFS="1"/>
+ <Layer queryable="0" opaque="0" noSubsets="0">
+ <Title>GeoServer Web Map Service</Title>
+ <Abstract>A compliant implementation of WMS 1.1.1 plus most of the SLD 1.0 extension (dynamic styling). Can also generate PDF, SVG, KML, GeoRSS</Abstract>
+ <SRS>EPSG:4326</SRS>
+ <SRS>EPSG:900913</SRS>
+ <LatLonBoundingBox minx="-180.0" miny="-90.0" maxx="180.0" maxy="83.624"/>
+ </Layer>
+ </Capability>
+</WMT_MS_Capabilities>
+--></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WMSCapabilities/v1_3_0.html b/misc/openlayers/tests/Format/WMSCapabilities/v1_3_0.html
new file mode 100644
index 0000000..7120b8c
--- /dev/null
+++ b/misc/openlayers/tests/Format/WMSCapabilities/v1_3_0.html
@@ -0,0 +1,614 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read_exception(t) {
+ t.plan(1);
+ var xml = document.getElementById("exceptionsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+ var format = new OpenLayers.Format.WMSCapabilities();
+ var obj = format.read(doc);
+ t.ok(!!obj.error, "Error reported correctly");
+ }
+
+ function test_layers(t) {
+
+ t.plan(25);
+
+ var xml = document.getElementById("ogcsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+
+ var obj = new OpenLayers.Format.WMSCapabilities().read(doc);
+ var capability = obj.capability;
+
+ var layers = {};
+ for (var i=0, len=capability.layers.length; i<len; i++) {
+ if ("name" in capability.layers[i]) {
+ layers[ capability.layers[i].name ] = capability.layers[i];
+ }
+ }
+
+ var rootlayer = capability.layers[ capability.layers.length - 1];
+
+ t.eq(rootlayer.srs,
+ {"CRS:84": true},
+ "SRS parsed correctly for root layer");
+ t.eq(layers["ROADS_RIVERS"].srs,
+ {"CRS:84": true, "EPSG:26986": true},
+ "Inheritance of SRS handled correctly when adding SRSes");
+ t.eq(layers["Temperature"].srs,
+ {"CRS:84": true},
+ "Inheritance of SRS handled correctly when redeclaring an inherited SRS");
+ t.eq(layers["Temperature"].infoFormats, ["text/xml", "text/plain", "text/html"], "infoFormats set correctly on layer");
+ var bbox = layers["ROADS_RIVERS"].bbox["EPSG:26986"];
+ t.eq(bbox.bbox,
+ [189000, 834000, 285000, 962000],
+ "Correct bbox from BoundingBox");
+ t.eq(bbox.res, {x: 1, y: 1}, "Correct resolution");
+ bbox = layers["ROADS_RIVERS"].bbox["CRS:84"];
+ t.eq(bbox.bbox,
+ [-71.63, 41.75, -70.78, 42.90],
+ "Correct bbox from BoundingBox (override)");
+ t.eq(bbox.res, {x: 0.01, y: 0.01}, "Correct resolution (override)");
+
+ bbox = layers["ROADS_1M"].bbox["EPSG:26986"];
+ t.eq(bbox.bbox,
+ [189000, 834000, 285000, 962000],
+ "Correctly inherited bbox");
+ t.eq(bbox.res, {x: 1, y: 1}, "Correctly inherited resolution");
+
+
+ var identifiers = layers["ROADS_RIVERS"].identifiers;
+ var authorities = layers["ROADS_RIVERS"].authorityURLs;
+
+ t.ok(identifiers, "got identifiers from layer ROADS_RIVERS");
+ t.ok("DIF_ID" in identifiers,
+ "authority attribute from Identifiers parsed correctly");
+ t.eq(identifiers["DIF_ID"],
+ "123456",
+ "Identifier value parsed correctly");
+ t.ok("DIF_ID" in authorities,
+ "AuthorityURLs parsed and inherited correctly");
+ t.eq(authorities["DIF_ID"],
+ "http://gcmd.gsfc.nasa.gov/difguide/whatisadif.html",
+ "OnlineResource in AuthorityURLs parsed correctly");
+
+ var featurelist = layers["ROADS_RIVERS"].featureListURL;
+ t.ok(featurelist, "layer has FeatureListURL");
+ t.eq(featurelist.format,
+ "XML",
+ "FeatureListURL format parsed correctly");
+ t.eq(featurelist.href,
+ "http://www.university.edu/data/roads_rivers.gml",
+ "FeatureListURL OnlineResource parsed correctly");
+
+ t.eq(layers["Pressure"].queryable,
+ true,
+ "queryable property inherited correctly");
+ t.eq(layers["ozone_image"].queryable,
+ false,
+ "queryable property has correct default value");
+ t.eq(layers["population"].cascaded,
+ 1,
+ "cascaded property parsed correctly");
+ t.eq(layers["ozone_image"].fixedWidth,
+ 512,
+ "fixedWidth property correctly parsed");
+ t.eq(layers["ozone_image"].fixedHeight,
+ 256,
+ "fixedHeight property correctly parsed");
+ t.eq(layers["ozone_image"].opaque,
+ true,
+ "opaque property parsed correctly");
+ t.eq(layers["ozone_image"].noSubsets,
+ true,
+ "noSubsets property parsed correctly");
+
+
+ }
+
+ function test_dimensions(t) {
+
+ t.plan(8);
+
+ var xml = document.getElementById("ogcsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+
+ var obj = new OpenLayers.Format.WMSCapabilities().read(doc);
+ var capability = obj.capability;
+
+ var layers = {};
+ for (var i=0, len=capability.layers.length; i<len; i++) {
+ if ("name" in capability.layers[i]) {
+ layers[ capability.layers[i].name ] = capability.layers[i];
+ }
+ }
+
+ var time = layers["Clouds"].dimensions.time;
+ t.eq(time["default"], "2000-08-22", "Default time value parsed correctly");
+ t.eq(time.values.length, 1, "Currect number of time extent values/periods");
+ t.eq(time.values[0], "1999-01-01/2000-08-22/P1D", "Time extent values parsed correctly");
+
+ var elevation = layers["Pressure"].dimensions.elevation;
+ t.eq(elevation.units, "CRS:88", "Dimension units parsed correctly");
+ t.eq(elevation["default"], "0", "Default elevation value parsed correctly");
+ t.eq(elevation.nearestVal, true, "NearestValue parsed correctly");
+ t.eq(elevation.multipleVal, false, "Absense of MultipleValues handled correctly");
+ t.eq(elevation.values,
+ ["0","1000","3000","5000","10000"],
+ "Parsing of comma-separated values done correctly");
+
+
+ }
+
+ function test_contactinfo(t) {
+ t.plan(14);
+
+ var xml = document.getElementById("ogcsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+
+ var obj = new OpenLayers.Format.WMSCapabilities().read(doc);
+ var service = obj.service;
+
+ var contactinfo = service.contactInformation;
+ t.ok(contactinfo, "object contains contactInformation property");
+
+ var personPrimary = contactinfo.personPrimary;
+ t.ok(personPrimary, "object contains personPrimary property");
+
+ t.eq(personPrimary.person, "Jeff Smith", "ContactPerson parsed correctly");
+ t.eq(personPrimary.organization, "NASA", "ContactOrganization parsed correctly");
+
+ t.eq(contactinfo.position,
+ "Computer Scientist",
+ "ContactPosition parsed correctly");
+
+
+ var addr = contactinfo.contactAddress;
+ t.ok(addr, "object contains contactAddress property");
+
+ t.eq(addr.type, "postal", "AddressType parsed correctly");
+ t.eq(addr.address,
+ "NASA Goddard Space Flight Center",
+ "Address parsed correctly");
+ t.eq(addr.city, "Greenbelt", "City parsed correctly");
+ t.eq(addr.stateOrProvince, "MD", "StateOrProvince parsed correctly");
+ t.eq(addr.postcode, "20771", "PostCode parsed correctly");
+ t.eq(addr.country, "USA", "Country parsed correctly");
+
+ t.eq(contactinfo.phone,
+ "+1 301 555-1212",
+ "ContactVoiceTelephone parsed correctly");
+ t.eq(contactinfo.email,
+ "user@host.com",
+ "ContactElectronicMailAddress parsed correctly");
+ }
+
+ function test_feesAndConstraints(t) {
+ t.plan(2);
+
+ var xml = document.getElementById("ogcsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+
+ var obj = new OpenLayers.Format.WMSCapabilities().read(doc);
+ var service = obj.service;
+
+ t.ok(! ("fees" in service), "Fees=none handled correctly");
+ t.ok(! ("accessConstraints" in service), "AccessConstraints=none handled correctly");
+ }
+
+ function test_requests(t) {
+ t.plan(6);
+
+ var xml = document.getElementById("ogcsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+
+ var obj = new OpenLayers.Format.WMSCapabilities().read(doc);
+ var request = obj.capability.request;
+
+ t.ok(request, "request property exists");
+ t.ok("getmap" in request, "got GetMap request");
+
+ t.ok("getfeatureinfo" in request, "got GetFeatureInfo request");
+ t.eq(request.getfeatureinfo.formats,
+ ["text/xml", "text/plain", "text/html"],
+ "GetFeatureInfo formats correctly parsed");
+
+ var exception = obj.capability.exception;
+ t.ok(exception, "exception property exists");
+ t.eq(exception.formats,
+ ["XML", "INIMAGE", "BLANK"],
+ "Exception Format parsed");
+ }
+
+ function test_ogc(t) {
+ t.plan(14);
+
+ /*
+ * Set up
+ */
+
+ // needed for the minScale/maxScale test, see below
+ var dpi = OpenLayers.DOTS_PER_INCH;
+ OpenLayers.DOTS_PER_INCH = 90.71;
+
+ var xml = document.getElementById("ogcsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+
+ var obj = new OpenLayers.Format.WMSCapabilities().read(doc);
+ var capability = obj.capability;
+
+ /*
+ * Test
+ */
+
+ var attribution = capability.layers[2].attribution;
+ t.eq(attribution.title, "State College University", "attribution title parsed correctly.");
+ t.eq(attribution.href, "http://www.university.edu/", "attribution href parsed correctly.")
+ t.eq(attribution.logo.href, "http://www.university.edu/icons/logo.gif", "attribution logo url parsed correctly.");
+ t.eq(attribution.logo.format, "image/gif", "attribution logo format parsed correctly.");
+ t.eq(attribution.logo.width, "100", "attribution logo width parsed correctly.");
+ t.eq(attribution.logo.height, "100", "attribution logo height parsed correctly.");
+
+ var keywords = capability.layers[0].keywords;
+ t.eq(keywords.length, 3, "layer has 3 keywords.");
+ t.eq(keywords[0].value, "road", "1st keyword parsed correctly.");
+
+ var metadataURLs = capability.layers[0].metadataURLs;
+ t.eq(metadataURLs.length, 2, "layer has 2 metadata urls.");
+ t.eq(metadataURLs[0].type, "FGDC:1998", "type parsed correctly.");
+ t.eq(metadataURLs[0].format, "text/plain", "format parsed correctly.");
+ t.eq(metadataURLs[0].href, "http://www.university.edu/metadata/roads.txt", "href parsed correctly.");
+
+ /*
+ Test minScale and maxScale
+ */
+ var minScale = 250000;
+ var maxScale = 1000;
+ t.eq(capability.layers[0].minScale, minScale.toPrecision(16), "layer.minScale is correct");
+ t.eq(capability.layers[0].maxScale, maxScale.toPrecision(16), "layer.maxScale is correct");
+
+ /*
+ * Tear down
+ */
+
+ OpenLayers.DOTS_PER_INCH = dpi;
+ }
+
+ function test_WMS13specials(t) {
+ t.plan(3);
+
+ var xml = document.getElementById("ogcsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+
+ var obj = new OpenLayers.Format.WMSCapabilities().read(doc);
+
+ t.eq(obj.service.layerLimit, 16, "LayerLimit parsed correctly");
+ t.eq(obj.service.maxHeight, 2048, "MaxHeight parsed correctly");
+ t.eq(obj.service.maxWidth, 2048, "MaxWidth parsed correctly");
+
+ }
+
+ </script>
+</head>
+<body>
+
+<div id="exceptionsample"><!--
+<?xml version='1.0' encoding="UTF-8"?>
+<ServiceExceptionReport version="1.3.0" xmlns="http://www.opengis.net/ogc"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.opengis.net/ogc
+ http://schemas.opengis.net/wms/1.3.0/exceptions_1_3_0.xsd">
+ <ServiceException> Plain text message about an error. </ServiceException>
+ <ServiceException code="InvalidUpdateSequence"> Another error message, this one with a service
+ exception code supplied. </ServiceException>
+ <ServiceException>
+ <![CDATA[ Error in module <foo.c>, line 42
+A message that includes angle brackets in text must be enclosed in a Character Data Section as in this example. All XML-like markup is ignored except for this sequence of three closing characters:
+]]>
+ </ServiceException>
+ <ServiceException>
+ <![CDATA[ <Module>foo.c</Module> <Error>An error occurred</Error> <Explanation>Similarly, actual XML can be enclosed in a CDATA section. A generic parser will ignore that XML, but application-specific software may choose to process it.</Explanation> ]]>
+ </ServiceException>
+</ServiceExceptionReport>
+--></div>
+
+<!--
+OGC example below taken from
+http://schemas.opengis.net/wms/1.3.0/capabilities_1_3_0.xml
+Changes:
+-removed comments
+-corrected typo in FeatureListURL Format XML with double quote
+-added MinScaleDenominator and MaxScaleDenominator
+-remove whitespace in Dimension tags
+-->
+<div id="ogcsample"><!--
+<?xml version='1.0' encoding="UTF-8"?>
+<WMS_Capabilities version="1.3.0" xmlns="http://www.opengis.net/wms"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.opengis.net/wms http://schemas.opengis.net/wms/1.3.0/capabilities_1_3_0.xsd">
+<Service>
+ <Name>WMS</Name>
+ <Title>Acme Corp. Map Server</Title>
+ <Abstract>Map Server maintained by Acme Corporation. Contact: webmaster@wmt.acme.com. High-quality maps showing roadrunner nests and possible ambush locations.</Abstract>
+
+ <KeywordList>
+ <Keyword>bird</Keyword>
+ <Keyword>roadrunner</Keyword>
+ <Keyword>ambush</Keyword>
+ </KeywordList>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
+ xlink:href="http://hostname/" />
+
+
+ <ContactInformation>
+ <ContactPersonPrimary>
+ <ContactPerson>Jeff Smith</ContactPerson>
+ <ContactOrganization>NASA</ContactOrganization>
+ </ContactPersonPrimary>
+ <ContactPosition>Computer Scientist</ContactPosition>
+
+ <ContactAddress>
+ <AddressType>postal</AddressType>
+ <Address>NASA Goddard Space Flight Center</Address>
+ <City>Greenbelt</City>
+ <StateOrProvince>MD</StateOrProvince>
+ <PostCode>20771</PostCode>
+
+ <Country>USA</Country>
+ </ContactAddress>
+ <ContactVoiceTelephone>+1 301 555-1212</ContactVoiceTelephone>
+ <ContactElectronicMailAddress>user@host.com</ContactElectronicMailAddress>
+ </ContactInformation>
+
+ <Fees>none</Fees>
+
+ <AccessConstraints>none</AccessConstraints>
+ <LayerLimit>16</LayerLimit>
+ <MaxWidth>2048</MaxWidth>
+ <MaxHeight>2048</MaxHeight>
+</Service>
+<Capability>
+ <Request>
+ <GetCapabilities>
+
+ <Format>text/xml</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://hostname/path?" />
+ </Get>
+ <Post>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://hostname/path?" />
+
+ </Post>
+ </HTTP>
+ </DCPType>
+ </GetCapabilities>
+ <GetMap>
+ <Format>image/gif</Format>
+ <Format>image/png</Format>
+ <Format>image/jpeg</Format>
+
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://hostname/path?" />
+ </Get>
+ </HTTP>
+ </DCPType>
+ </GetMap>
+
+ <GetFeatureInfo>
+ <Format>text/xml</Format>
+ <Format>text/plain</Format>
+ <Format>text/html</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://hostname/path?" />
+ </Get>
+ </HTTP>
+ </DCPType>
+ </GetFeatureInfo>
+ </Request>
+ <Exception>
+ <Format>XML</Format>
+
+ <Format>INIMAGE</Format>
+ <Format>BLANK</Format>
+ </Exception>
+ <Layer>
+ <Title>Acme Corp. Map Server</Title>
+ <CRS>CRS:84</CRS>
+
+ <AuthorityURL name="DIF_ID">
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
+ xlink:href="http://gcmd.gsfc.nasa.gov/difguide/whatisadif.html" />
+ </AuthorityURL>
+ <BoundingBox CRS="CRS:84"
+ minx="-1" miny="-1" maxx="1" maxy="1" resx="0.0" resy="0.0"/>
+ <Layer>
+
+ <Name>ROADS_RIVERS</Name>
+ <Title>Roads and Rivers</Title>
+
+ <CRS>EPSG:26986</CRS>
+ <EX_GeographicBoundingBox>
+ <westBoundLongitude>-71.63</westBoundLongitude>
+ <eastBoundLongitude>-70.78</eastBoundLongitude>
+ <southBoundLatitude>41.75</southBoundLatitude>
+ <northBoundLatitude>42.90</northBoundLatitude>
+
+ </EX_GeographicBoundingBox>
+ <BoundingBox CRS="CRS:84"
+ minx="-71.63" miny="41.75" maxx="-70.78" maxy="42.90" resx="0.01" resy="0.01"/>
+ <BoundingBox CRS="EPSG:26986"
+ minx="189000" miny="834000" maxx="285000" maxy="962000" resx="1" resy="1" />
+ <Attribution>
+ <Title>State College University</Title>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
+ xlink:href="http://www.university.edu/" />
+
+ <LogoURL width="100" height="100">
+ <Format>image/gif</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://www.university.edu/icons/logo.gif" />
+ </LogoURL>
+ </Attribution>
+ <Identifier authority="DIF_ID">123456</Identifier>
+ <FeatureListURL>
+
+ <Format>XML</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
+ xlink:href="http://www.university.edu/data/roads_rivers.gml" />
+ </FeatureListURL>
+ <Style>
+ <Name>USGS</Name>
+ <Title>USGS Topo Map Style</Title>
+ <Abstract>Features are shown in a style like that used in USGS topographic maps.</Abstract>
+
+ <LegendURL width="72" height="72">
+ <Format>image/gif</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://www.university.edu/legends/usgs.gif" />
+ </LegendURL>
+ <StyleSheetURL>
+ <Format>text/xsl</Format>
+
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://www.university.edu/stylesheets/usgs.xsl" />
+ </StyleSheetURL>
+ </Style>
+ <MinScaleDenominator>1000</MinScaleDenominator>
+ <MaxScaleDenominator>250000</MaxScaleDenominator>
+ <Layer queryable="1">
+ <Name>ROADS_1M</Name>
+ <Title>Roads at 1:1M scale</Title>
+ <Abstract>Roads at a scale of 1 to 1 million.</Abstract>
+
+ <KeywordList>
+ <Keyword>road</Keyword>
+ <Keyword>transportation</Keyword>
+ <Keyword>atlas</Keyword>
+ </KeywordList>
+ <Identifier authority="DIF_ID">123456</Identifier>
+ <MetadataURL type="FGDC:1998">
+
+ <Format>text/plain</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://www.university.edu/metadata/roads.txt" />
+ </MetadataURL>
+ <MetadataURL type="ISO19115:2003">
+ <Format>text/xml</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://www.university.edu/metadata/roads.xml" />
+ </MetadataURL>
+
+ <Style>
+ <Name>ATLAS</Name>
+ <Title>Road atlas style</Title>
+ <Abstract>Roads are shown in a style like that used in a commercial road atlas.</Abstract>
+ <LegendURL width="72" height="72">
+ <Format>image/gif</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
+ xlink:type="simple"
+ xlink:href="http://www.university.edu/legends/atlas.gif" />
+
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>RIVERS_1M</Name>
+ <Title>Rivers at 1:1M scale</Title>
+ <Abstract>Rivers at a scale of 1 to 1 million.</Abstract>
+
+ <KeywordList>
+ <Keyword>river</Keyword>
+ <Keyword>canal</Keyword>
+ <Keyword>waterway</Keyword>
+ </KeywordList>
+ </Layer>
+ </Layer>
+
+ <Layer queryable="1">
+ <Title>Weather Forecast Data</Title>
+ <CRS>CRS:84</CRS>
+
+ <EX_GeographicBoundingBox>
+ <westBoundLongitude>-180</westBoundLongitude>
+ <eastBoundLongitude>180</eastBoundLongitude>
+
+ <southBoundLatitude>-90</southBoundLatitude>
+ <northBoundLatitude>90</northBoundLatitude>
+ </EX_GeographicBoundingBox>
+ <Dimension name="time" units="ISO8601" default="2000-08-22">1999-01-01/2000-08-22/P1D</Dimension>
+ <Layer>
+
+ <Name>Clouds</Name>
+ <Title>Forecast cloud cover</Title>
+ </Layer>
+ <Layer>
+ <Name>Temperature</Name>
+ <Title>Forecast temperature</Title>
+ </Layer>
+
+ <Layer>
+ <Name>Pressure</Name>
+ <Title>Forecast barometric pressure</Title>
+ <Dimension name="elevation" units="EPSG:5030" />
+ <Dimension name="time" units="ISO8601" default="2000-08-22">
+ 1999-01-01/2000-08-22/P1D</Dimension>
+
+ <Dimension name="elevation" units="CRS:88" default="0" nearestValue="1">0,1000,3000,5000,10000</Dimension>
+ </Layer>
+ </Layer>
+ <Layer opaque="1" noSubsets="1" fixedWidth="512" fixedHeight="256">
+ <Name>ozone_image</Name>
+ <Title>Global ozone distribution (1992)</Title>
+
+ <EX_GeographicBoundingBox>
+ <westBoundLongitude>-180</westBoundLongitude>
+ <eastBoundLongitude>180</eastBoundLongitude>
+ <southBoundLatitude>-90</southBoundLatitude>
+ <northBoundLatitude>90</northBoundLatitude>
+ </EX_GeographicBoundingBox>
+ <Dimension name="time" units="ISO8601" default="1992">1992</Dimension>
+
+ </Layer>
+ <Layer cascaded="1">
+ <Name>population</Name>
+ <Title>World population, annual</Title>
+ <EX_GeographicBoundingBox>
+ <westBoundLongitude>-180</westBoundLongitude>
+
+ <eastBoundLongitude>180</eastBoundLongitude>
+ <southBoundLatitude>-90</southBoundLatitude>
+ <northBoundLatitude>90</northBoundLatitude>
+ </EX_GeographicBoundingBox>
+ <Dimension name="time" units="ISO8601" default="2000">1990/2000/P1Y</Dimension>
+ </Layer>
+ </Layer>
+
+</Capability>
+</WMS_Capabilities>
+--></div>
+
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WMSDescribeLayer.html b/misc/openlayers/tests/Format/WMSDescribeLayer.html
new file mode 100644
index 0000000..7a53269
--- /dev/null
+++ b/misc/openlayers/tests/Format/WMSDescribeLayer.html
@@ -0,0 +1,65 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read_WMSDescribeLayer(t) {
+ t.plan(10);
+
+ var parser = new OpenLayers.Format.WMSDescribeLayer();
+
+ var text =
+ '<WMS_DescribeLayerResponse version="1.1.1">' +
+ ' <LayerDescription name="topp:states" wfs="http://geo.openplans.org:80/geoserver/wfs/WfsDispatcher?">' +
+ ' <Query typeName="topp:states"/>' +
+ ' </LayerDescription>' +
+ '</WMS_DescribeLayerResponse>';
+
+ var res = parser.read(text);
+
+ t.eq(res.layerDescriptions.length, 1,
+ "Only one LayerDescription in data, so only one parsed");
+
+ t.eq(res.layerDescriptions[0].owsType, "WFS",
+ "Properly parses owsType as WFS");
+
+ t.eq(res.layerDescriptions[0].owsURL, "http://geo.openplans.org:80/geoserver/wfs/WfsDispatcher?",
+ "Properly parses owsURL");
+
+ t.eq(res.layerDescriptions[0].typeName, "topp:states",
+ "Properly parses typeName");
+
+ t.eq(res.layerDescriptions[0].layerName, "topp:states",
+ "Properly parses name");
+
+ //TODO remove the 5 tests below when we deprecate the old structure
+ t.eq(res.length, 1,
+ "Only one LayerDescription in data, so only one parsed");
+ t.eq(res[0].owsType, "WFS",
+ "Properly parses owsType as WFS");
+ t.eq(res[0].owsURL, "http://geo.openplans.org:80/geoserver/wfs/WfsDispatcher?",
+ "Properly parses owsURL");
+ t.eq(res[0].typeName, "topp:states",
+ "Properly parses typeName");
+ t.eq(res[0].layerName, "topp:states",
+ "Properly parses name");
+
+ }
+
+ function test_read_exception(t) {
+ t.plan(1);
+ var text = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' +
+ '<!DOCTYPE ServiceExceptionReport SYSTEM "http://schemas.opengis.net/wms/1.1.1/WMS_exception_1_1_1.dtd">' +
+ '<ServiceExceptionReport version="1.1.1" > <ServiceException code="LayerNotDefined">' +
+ 'geonode:_map_107_annotations: no such layer on this server' +
+ '</ServiceException></ServiceExceptionReport>';
+ var format = new OpenLayers.Format.WMSDescribeLayer();
+ var obj = format.read(text);
+ t.ok(!!obj.error, "Error reported correctly");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WMSGetFeatureInfo.html b/misc/openlayers/tests/Format/WMSGetFeatureInfo.html
new file mode 100644
index 0000000..1301b65
--- /dev/null
+++ b/misc/openlayers/tests/Format/WMSGetFeatureInfo.html
@@ -0,0 +1,319 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read_FeatureInfoResponse(t) {
+ t.plan(7);
+
+ var parser = new OpenLayers.Format.WMSGetFeatureInfo();
+
+ // read empty response
+ var text =
+ '<?xml version="1.0" encoding="UTF-8" ?>' +
+ '<FeatureInfoResponse>' +
+ '</FeatureInfoResponse>';
+
+ var features = parser.read(text);
+ t.eq(features.length, 0,
+ "Parsing empty FeatureInfoResponse response succesfull");
+
+ // read 1 feature
+ text =
+ '<?xml version="1.0" encoding="UTF-8" ?>' +
+ '<FeatureInfoResponse>' +
+ ' <FIELDS OBJECTID="1188" HECTARES="1819.734" ZONENR="5854" NULZONES=" " AREA="18197340.1426" PERIMETER="19177.4073627" SHAPE="NULL" SE_ANNO_CAD_DATA="NULL" SHAPE.AREA="0" SHAPE.LEN="0"/>' +
+ '</FeatureInfoResponse>';
+
+ features = parser.read(text);
+ t.eq(features.length, 1,
+ "Parsed 1 feature in total");
+
+ t.eq(features[0].attributes.OBJECTID, '1188',
+ "Attribute OBJECTID contains the right value");
+
+ // read multiple features
+ text =
+ '<?xml version="1.0" encoding="UTF-8" ?>' +
+ '<FeatureInfoResponse>' +
+ ' <FIELDS OBJECTID="551" Shape="NULL" NAME="Carbon" STATE_NAME="Wyoming" AREA="7999.91062" POP2000="15639" POP00_SQMI="2" Shape_Length="6.61737274334215" Shape_Area="2.23938983524154"/>' +
+ ' <FIELDS OBJECTID="7" Shape="NULL" AREA="97803.199" STATE_NAME="Wyoming" SUB_REGION="Mtn" STATE_ABBR="WY" POP2000="493782" POP00_SQMI="5" Shape_Length="21.9870297323522" Shape_Area="27.9666881382635"/>' +
+ ' <FIELDS OBJECTID="99" Shape="NULL" LENGTH="378.836" TYPE="Multi-Lane Divided" ADMN_CLASS="Interstate" TOLL_RD="N" RTE_NUM1=" 80" RTE_NUM2=" " ROUTE="Interstate 80" Shape_Length="7.04294883879398"/>' +
+ '</FeatureInfoResponse>';
+
+ features = parser.read(text);
+
+ t.eq(features.length, 3,
+ "Parsed 3 features in total");
+
+ t.eq(features[1].attributes.STATE_NAME, 'Wyoming',
+ "Attribute STATE_NAME contains the right value");
+
+ text = '<FeatureInfoResponse>' +
+ '<FIELDS>' +
+ '<FIELD name="ID" value="B31A0154"/>' +
+ '<FIELD name="FID" value="31AL0011"/>' +
+ '</FIELDS>' +
+ '<FIELDS>' +
+ '<FIELD name="ID" value="B31A0153"/>' +
+ '<FIELD name="FID" value="31AL0011"/>' +
+ '</FIELDS>' +
+ '</FeatureInfoResponse>';
+
+ features = parser.read(text);
+
+ t.eq(features.length, 2,
+ "Parsed 2 features in total");
+
+ t.eq(features[1].attributes.FID, '31AL0011',
+ "Attribute FID contains the right value");
+
+ }
+
+ function test_read_msGMLOutput(t) {
+ t.plan(13);
+
+ var parser = new OpenLayers.Format.WMSGetFeatureInfo();
+
+ // read empty response
+ var text =
+ '<?xml version="1.0" encoding="ISO-8859-1"?>' +
+ '<msGMLOutput ' +
+ ' xmlns:gml="http://www.opengis.net/gml"' +
+ ' xmlns:xlink="http://www.w3.org/1999/xlink"' +
+ ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' +
+ '</msGMLOutput>';
+
+ var features = parser.read(text);
+ t.eq(features.length, 0,
+ "Parsing empty msGMLOutput response succesfull");
+
+ // read empty attribute
+ text =
+ '<?xml version="1.0" encoding="ISO-8859-1"?>' +
+ '<msGMLOutput ' +
+ ' xmlns:gml="http://www.opengis.net/gml"' +
+ ' xmlns:xlink="http://www.w3.org/1999/xlink"' +
+ ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' +
+ ' <AAA64_layer>' +
+ ' <AAA64_feature>' +
+ ' <gml:boundedBy>' +
+ ' <gml:Box srsName="EPSG:28992">' +
+ ' <gml:coordinates>107397.266000,460681.063000 116568.188000,480609.250000</gml:coordinates>' +
+ ' </gml:Box>' +
+ ' </gml:boundedBy>' +
+ ' <FOO>bar</FOO>' +
+ ' <EMPTY></EMPTY>' +
+ ' </AAA64_feature>' +
+ ' </AAA64_layer>' +
+ '</msGMLOutput>';
+ features = parser.read(text);
+ t.eq((features[0].attributes.EMPTY === null), true, "Empty attribute is parsed as null");
+
+ // read 1 feature from 1 layer
+ text =
+ '<?xml version="1.0" encoding="ISO-8859-1"?>' +
+ '<msGMLOutput ' +
+ ' xmlns:gml="http://www.opengis.net/gml"' +
+ ' xmlns:xlink="http://www.w3.org/1999/xlink"' +
+ ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' +
+ ' <AAA64_layer>' +
+ ' <AAA64_feature>' +
+ ' <gml:boundedBy>' +
+ ' <gml:Box srsName="EPSG:28992">' +
+ ' <gml:coordinates>107397.266000,460681.063000 116568.188000,480609.250000</gml:coordinates>' +
+ ' </gml:Box>' +
+ ' </gml:boundedBy>' +
+ ' <OBJECTID>109</OBJECTID>' +
+ ' <ROUTE>N231</ROUTE>' +
+ ' <ROUTE_CH>#N231</ROUTE_CH>' +
+ ' <COUNT>2</COUNT>' +
+ ' <BEHEERDER>P</BEHEERDER>' +
+ ' <LENGTH>28641.7</LENGTH>' +
+ ' <SHAPE>&lt;shape&gt;</SHAPE>' +
+ ' <SE_ANNO_CAD_DATA>&lt;null&gt;</SE_ANNO_CAD_DATA>' +
+ ' </AAA64_feature>' +
+ ' </AAA64_layer>' +
+ '</msGMLOutput>';
+
+ features = parser.read(text);
+
+ t.eq(features.length, 1,
+ "Parsed 1 feature in total");
+
+ t.eq(features[0].attributes.OBJECTID, '109',
+ "Attribute OBJECTID contains the right value");
+
+ t.eq(features[0].type, 'AAA64',
+ "Parsed the layer name correctly");
+
+ var bounds = features[0].bounds;
+ t.ok(bounds instanceof OpenLayers.Bounds, "feature given a bounds");
+ t.eq(bounds.left.toFixed(3), "107397.266", "Bounds left parsed correctly");
+ t.eq(bounds.right.toFixed(3), "116568.188", "Bounds right parsed correctly");
+ t.eq(bounds.bottom.toFixed(3), "460681.063", "Bounds bottom parsed correctly");
+ t.eq(bounds.top.toFixed(3), "480609.250", "Bounds top parsed correctly");
+
+ // read 2 features from 2 layers
+ text =
+ '<?xml version="1.0" encoding="ISO-8859-1"?>' +
+ '<msGMLOutput ' +
+ ' xmlns:gml="http://www.opengis.net/gml"' +
+ ' xmlns:xlink="http://www.w3.org/1999/xlink"' +
+ ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'+
+ ' <AAA64_layer>' +
+ ' <AAA64_feature>' +
+ ' <gml:boundedBy>' +
+ ' <gml:Box srsName="EPSG:28992">' +
+ ' <gml:coordinates>129799.109000,467950.250000 133199.906000,468904.063000</gml:coordinates>' +
+ ' </gml:Box>' +
+ ' </gml:boundedBy>' +
+ ' <OBJECTID>287</OBJECTID>' +
+ ' <ROUTE>N403</ROUTE>' +
+ ' <ROUTE_CH>#N403</ROUTE_CH>' +
+ ' <COUNT>1</COUNT>' +
+ ' <BEHEERDER>P</BEHEERDER>' +
+ ' <LENGTH>4091.25</LENGTH>' +
+ ' <SHAPE>&lt;shape&gt;</SHAPE>' +
+ ' <SE_ANNO_CAD_DATA>&lt;null&gt;</SE_ANNO_CAD_DATA>' +
+ ' </AAA64_feature>' +
+ ' </AAA64_layer>' +
+ ' <AAA62_layer>' +
+ ' <AAA62_feature>' +
+ ' <gml:boundedBy>' +
+ ' <gml:Box srsName="EPSG:28992">' +
+ ' <gml:coordinates>129936.000000,468362.000000 131686.000000,473119.000000</gml:coordinates>' +
+ ' </gml:Box>' +
+ ' </gml:boundedBy>' +
+ ' <OBJECTID>1251</OBJECTID>' +
+ ' <VWK_ID>1515</VWK_ID>' +
+ ' <VWK_BEGDTM>00:00:00 01/01/1998</VWK_BEGDTM>' +
+ ' <VWJ_ID_BEG>1472</VWJ_ID_BEG>' +
+ ' <VWJ_ID_END>1309</VWJ_ID_END>' +
+ ' <VAKTYPE>D</VAKTYPE>' +
+ ' <VRT_CODE>227</VRT_CODE>' +
+ ' <VRT_NAAM>Vecht</VRT_NAAM>' +
+ ' <VWG_NR>2</VWG_NR>' +
+ ' <VWG_NAAM>Vecht</VWG_NAAM>' +
+ ' <BEGKM>18.25</BEGKM>' +
+ ' <ENDKM>23.995</ENDKM>' +
+ ' <LENGTH>5745.09</LENGTH>' +
+ ' <SHAPE>&lt;shape&gt;</SHAPE>' +
+ ' <SE_ANNO_CAD_DATA>&lt;null&gt;</SE_ANNO_CAD_DATA>' +
+ ' </AAA62_feature>' +
+ ' </AAA62_layer>' +
+ '</msGMLOutput>';
+
+ features = parser.read(text);
+
+ t.eq(features.length, 2,
+ "Parsed 2 features in total");
+
+ t.eq((features[0].type == features[1].type), false,
+ "The layer name differs for the two features");
+
+ text =
+ '<?xml version="1.0" encoding="ISO-8859-1"?>' +
+ '<msGMLOutput ' +
+ ' xmlns:gml="http://www.opengis.net/gml"' +
+ ' xmlns:xlink="http://www.w3.org/1999/xlink"' +
+ ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' +
+ ' <wegbeheerderinfo_layer>' +
+ ' <wegbeheerderinfo_feature>' +
+ ' <gml:boundedBy>' +
+ ' <gml:Box srsName="EPSG:28992">' +
+ ' <gml:coordinates>105002.943000,490037.863000 105271.523000,490262.208000</gml:coordinates>' +
+ ' </gml:Box>' +
+ ' </gml:boundedBy>' +
+ ' <geometry>' +
+ ' <gml:MultiLineString srsName="EPSG:28992">' +
+ ' <gml:lineStringMember>' +
+ ' <gml:LineString>' +
+ ' <gml:coordinates>105270.164000,490262.208000 105098.274000,490258.040000 105028.045000,490089.576000 105002.943000,490048.851000 105049.666000,490037.863000 105271.523000,490064.957000 </gml:coordinates>' +
+ ' </gml:LineString>' +
+ ' </gml:lineStringMember>' +
+ ' </gml:MultiLineString>' +
+ ' </geometry>' +
+ ' <OGR_FID>203327</OGR_FID>' +
+ ' </wegbeheerderinfo_feature>' +
+ ' </wegbeheerderinfo_layer>' +
+ '</msGMLOutput>';
+
+ features = parser.read(text);
+
+ t.eq((features[0].geometry instanceof OpenLayers.Geometry.MultiLineString), true,
+ "Parsed geometry is of type multi line string");
+
+ }
+
+ function test_read_GMLFeatureInfoResponse(t) {
+ t.plan(4);
+
+ var parser = new OpenLayers.Format.WMSGetFeatureInfo();
+
+ // read Ionic response, see if parser falls back to GML format
+ // url used:
+ /* http://webservices.ionicsoft.com/ionicweb/wfs/BOSTON_ORA?service=WMS&request=GetFeatureInfo&layers=roads&version=1.1.1&bbox=-71.1,42.25,-71.05,42.3&width=500&height=500&format=image/png&SRS=EPSG:4326&styles=&x=174&y=252&query_layers=roads&info_format=application/vnd.ogc.gml */
+ var text =
+ "<?xml version='1.0' encoding='utf-8' ?>" +
+ ' <ogcwfs:FeatureCollection xsi:schemaLocation="http://www.ionicsoft.com/wfs http://webservices.ionicsoft.com/ionicweb/wfs/BOSTON_ORA?REQUEST=DescribeAllFeatureType&amp;SERVICE=WFS http://www.opengis.net/wfs http://webservices.ionicsoft.com/ionicweb/wfs/BOSTON_ORA/REQUEST/get/DATA/LPR/wfs/1.0.0/WFS-basic.xsd" xmlns:wfs="http://www.ionicsoft.com/wfs" xmlns:gml="http://www.opengis.net/gml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ogcwfs="http://www.opengis.net/wfs">' +
+ ' <gml:boundedBy>' +
+ ' <gml:Box srsName="EPSG:4326">' +
+ ' <gml:coordinates>-71.08301710045646,42.27320863544783 -71.08020014900377,42.27480054530114</gml:coordinates>' +
+ ' </gml:Box>' +
+ ' </gml:boundedBy>' +
+ ' <gml:featureMember>' +
+ ' <wfs:roads fid="roads.9453.0">' +
+ ' <wfs:FNODE_>8943.0</wfs:FNODE_>' +
+ ' <wfs:TNODE_>9070.0</wfs:TNODE_>' +
+ ' <wfs:LPOLY_>0.0</wfs:LPOLY_>' +
+ ' <wfs:RPOLY_>0.0</wfs:RPOLY_>' +
+ ' <wfs:LENGTH>306.875</wfs:LENGTH>' +
+ ' <wfs:MRD_>13109.0</wfs:MRD_>' +
+ ' <wfs:MRD_ID>9453.0</wfs:MRD_ID>' +
+ ' <wfs:TILE_NAME>126</wfs:TILE_NAME>' +
+ ' <wfs:COUNTYCODE>M</wfs:COUNTYCODE>' +
+ ' <wfs:SERIAL_NUM>26000.0</wfs:SERIAL_NUM>' +
+ ' <wfs:CLASS>5.0</wfs:CLASS>' +
+ ' <wfs:ADMIN_TYPE>0.0</wfs:ADMIN_TYPE>' +
+ ' <wfs:ALTRT1TYPE>0.0</wfs:ALTRT1TYPE>' +
+ ' <wfs:STREETNAME>DOCTOR MARY MOORE BEATTY CIRCLE</wfs:STREETNAME>' +
+ ' <wfs:CSN>M 26000</wfs:CSN>' +
+ ' <wfs:GEOMETRY>' +
+ ' <gml:LineString srsName="EPSG:4326">' +
+ ' <gml:coordinates>-71.08300668868151,42.27480054530114 -71.08155305289881,42.27452010256956 -71.08021063085208,42.27320863544783</gml:coordinates>' +
+ ' </gml:LineString>' +
+ ' </wfs:GEOMETRY>' +
+ ' </wfs:roads>' +
+ ' </gml:featureMember>' +
+ ' </ogcwfs:FeatureCollection>';
+
+ var features = parser.read(text);
+
+ t.eq(features.length, 1,
+ "Parsing GML GetFeatureInfo response from Ionic succesfull");
+
+ t.eq(features[0].attributes.TILE_NAME, '126',
+ "Attribute TILE_NAME contains the right value");
+
+ // read Geoserver response
+ // taken from:
+/* http://demo.opengeo.org/geoserver/wms?service=WMS&request=GetFeatureInfo&layers=opengeo:roads&query_layers=opengeo:roads&format=image/png&version=1.1.1&styles=&bbox=-103.9,44.4,-103.7,44.5&srs=EPSG:4326&width=500&height=500&x=158&y=98&info_format=application/vnd.ogc.gml*/
+
+ text = '<?xml version="1.0" encoding="UTF-8"?><wfs:FeatureCollection xmlns="http://www.opengis.net/wfs" xmlns:wfs="http://www.opengis.net/wfs" xmlns:opengeo="http://opengeo.org" xmlns:gml="http://www.opengis.net/gml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://opengeo.org http://demo.opengeo.org:80/geoserver/wfs?service=WFS&amp;version=1.0.0&amp;request=DescribeFeatureType&amp;typeName=opengeo:roads http://www.opengis.net/wfs http://demo.opengeo.org:80/geoserver/schemas/wfs/1.0.0/WFS-basic.xsd"><gml:boundedBy><gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#26713"><gml:coordinates xmlns:gml="http://www.opengis.net/gml" decimal="." cs="," ts=" ">591943.9375,4925605 593045.625,4925845</gml:coordinates></gml:Box></gml:boundedBy><gml:featureMember><opengeo:roads fid="roads.90"><opengeo:cat>3</opengeo:cat><opengeo:label>secondary highway, hard surface</opengeo:label><opengeo:the_geom><gml:MultiLineString srsName="http://www.opengis.net/gml/srs/epsg.xml#26713"><gml:lineStringMember><gml:LineString><gml:coordinates xmlns:gml="http://www.opengis.net/gml" decimal="." cs="," ts=" ">593045.60746465,4925605.0059156 593024.32382915,4925606.79305411 592907.54863574,4925624.85647524 592687.35111096,4925670.76834012 592430.76279218,4925678.79393165 592285.97636109,4925715.70811767 592173.39165655,4925761.83511156 592071.1753393,4925793.95523514 591985.96972625,4925831.59842486 591943.98769455,4925844.93220071</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></opengeo:the_geom></opengeo:roads></gml:featureMember></wfs:FeatureCollection>';
+
+ features = parser.read(text);
+
+ t.eq(features.length, 1,
+ "Parsing GML GetFeatureInfo response from Geoserver succesfull");
+
+ t.eq(features[0].attributes.cat, '3',
+ "Attribute cat contains the right value");
+
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WMTSCapabilities.html b/misc/openlayers/tests/Format/WMTSCapabilities.html
new file mode 100644
index 0000000..e7a51a3
--- /dev/null
+++ b/misc/openlayers/tests/Format/WMTSCapabilities.html
@@ -0,0 +1,20 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+
+ t.plan(1);
+ var format = new OpenLayers.Format.WMTSCapabilities({
+ version: "foo"
+ });
+ t.eq(format.version, "foo", "version set on format");
+
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WMTSCapabilities/v1_0_0.html b/misc/openlayers/tests/Format/WMTSCapabilities/v1_0_0.html
new file mode 100644
index 0000000..f4fadeb
--- /dev/null
+++ b/misc/openlayers/tests/Format/WMTSCapabilities/v1_0_0.html
@@ -0,0 +1,1042 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_ows(t) {
+ t.plan(20);
+ var xml = document.getElementById("ogcsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+ var obj = new OpenLayers.Format.WMTSCapabilities().read(doc);
+ // ows:ServiceIdentification
+ var serviceIdentification = obj.serviceIdentification;
+ t.eq(serviceIdentification.title, "Web Map Tile Service", "ows:ServiceIdentification title is correct");
+ t.eq(serviceIdentification.serviceTypeVersion, "1.0.0", "ows:ServiceIdentification serviceTypeVersion is correct");
+ t.eq(serviceIdentification.serviceType.value, "OGC WMTS", "ows:ServiceIdentification serviceType is correct");
+
+ // ows:ServiceProvider
+ var serviceProvider = obj.serviceProvider;
+ t.eq(serviceProvider.providerName, "MiraMon", "ows:ServiceProvider providerName is correct");
+ t.eq(serviceProvider.providerSite, "http://www.creaf.uab.es/miramon", "ows:ServiceProvider providerSite is correct");
+ t.eq(serviceProvider.serviceContact.individualName, "Joan Maso Pau", "ows:ServiceProvider individualName is correct");
+ t.eq(serviceProvider.serviceContact.positionName, "Senior Software Engineer", "ows:ServiceProvider positionName is correct");
+ t.eq(serviceProvider.serviceContact.contactInfo.address.administrativeArea, "Barcelona", "ows:ServiceProvider address administrativeArea is correct");
+ t.eq(serviceProvider.serviceContact.contactInfo.address.city, "Bellaterra", "ows:ServiceProvider address city is correct");
+ t.eq(serviceProvider.serviceContact.contactInfo.address.country, "Spain", "ows:ServiceProvider address country is correct");
+ t.eq(serviceProvider.serviceContact.contactInfo.address.deliveryPoint, "Fac Ciencies UAB", "ows:ServiceProvider address deliveryPoint is correct");
+ t.eq(serviceProvider.serviceContact.contactInfo.address.electronicMailAddress, "joan.maso@uab.es", "ows:ServiceProvider address electronicMailAddress is correct");
+ t.eq(serviceProvider.serviceContact.contactInfo.address.postalCode, "08193", "ows:ServiceProvider address postalCode is correct");
+ t.eq(serviceProvider.serviceContact.contactInfo.phone.voice, "+34 93 581 1312", "ows:ServiceProvider phone voice is correct");
+
+ // ows:OperationsMetadata
+ var operationsMetadata = obj.operationsMetadata;
+ t.eq(operationsMetadata.GetCapabilities.dcp.http.get[0].url, "http://www.miramon.uab.es/cgi-bin/MiraMon5_0.cgi?", "ows:OperationsMetadata GetCapabilities url is correct");
+ t.eq(operationsMetadata.GetCapabilities.dcp.http.get[0].constraints.GetEncoding.allowedValues,
+ {'KVP': true},
+ "ows:OperationsMetadata GetCapabilities Constraints Get is correct");
+ t.eq(operationsMetadata.GetFeatureInfo.dcp.http.get[0].url, "http://www.miramon.uab.es/cgi-bin/MiraMon5_0.cgi?", "ows:OperationsMetadata GetFeatureInfo url is correct");
+ t.eq(operationsMetadata.GetFeatureInfo.dcp.http.get[0].constraints,
+ undefined,
+ "ows:OperationsMetadata GetFeatureInfo Constraints Get is correct");
+ t.eq(operationsMetadata.GetTile.dcp.http.get[0].url, "http://www.miramon.uab.es/cgi-bin/MiraMon5_0.cgi?", "ows:OperationsMetadata GetTile url is correct");
+ t.eq(operationsMetadata.GetTile.dcp.http.get[0].constraints,
+ undefined,
+ "ows:OperationsMetadata GetTile Constraints Get is correct");
+ }
+
+ function test_layers(t) {
+ t.plan(43);
+ var xml = document.getElementById("ogcsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+
+ var obj = new OpenLayers.Format.WMTSCapabilities().read(doc);
+ var contents = obj.contents;
+
+ var numOfLayers = contents.layers.length;
+ t.eq(numOfLayers, 1, "correct count of layers");
+
+ var layer = contents.layers[0];
+ t.eq(layer['abstract'], "Coastline/shorelines (BA010)", "layer abstract is correct");
+ t.eq(layer.identifier, "coastlines", "layer identifier is correct");
+ t.eq(layer.title, "Coastlines", "layer title is correct");
+
+ var numOfFormats = layer.formats.length;
+ t.eq(numOfFormats, 2, "correct count of formats");
+ t.eq(layer.formats[0], "image/png", "format image/png is correct");
+ t.eq(layer.formats[1], "image/gif", "format image/gif is correct");
+
+ var numOfStyles = layer.styles.length;
+ t.eq(numOfStyles, 2, "correct count of styles");
+ t.eq(layer.styles[0].identifier, "DarkBlue", "style 0 identifier is correct");
+ t.eq(layer.styles[0].isDefault, true, "style 0 isDefault is correct");
+ t.eq(layer.styles[0].title, "Dark Blue", "style 0 title is correct");
+ t.eq(layer.styles[0].legend.href, "http://www.miramon.uab.es/wmts/Coastlines/coastlines_darkBlue.png", "style 0 legend href is correct");
+ t.eq(layer.styles[0].legend.format, "image/png", "style 0 legend format is correct");
+ t.eq(layer.styles[1].identifier, "thickAndRed", "style 1 identifier is correct");
+ t.ok(!layer.styles[1].isDefault, "style 1 isDefault is correct");
+ t.eq(layer.styles[1].title, "Thick And Red", "style 1 title is correct");
+ t.eq(layer.styles[1].legend, undefined, "style 1 legend is not set");
+ //t.eq(layer.styles[1].abstract, "Specify this style if you want your maps to have thick red coastlines. ", "style 1 abstract is correct");
+
+ t.eq(layer.tileMatrixSetLinks.length, 1, "correct count of tileMatrixSetLinks");
+ t.eq(layer.tileMatrixSetLinks[0].tileMatrixSet, "BigWorld", "tileMatrixSet is correct");
+
+ var wgs84Bbox = layer.bounds;
+ t.ok(wgs84Bbox instanceof OpenLayers.Bounds, "wgs84BoudingBox instance of OpenLayers.Bounds");
+ t.eq(wgs84Bbox.left, -180.0, "wgs84BoudingBox left is correct");
+ t.eq(wgs84Bbox.right, 180.0, "wgs84BoudingBox right is correct");
+ t.eq(wgs84Bbox.bottom, -90.0, "wgs84BoudingBox bottom is correct");
+ t.eq(wgs84Bbox.top, 90.0, "wgs84BoudingBox top is correct");
+
+ t.eq(layer.resourceUrl.tile.format, "image/png", "resourceUrl.tile.format is correct");
+ t.eq(layer.resourceUrl.tile.template, "http://www.example.com/wmts/coastlines/{TileMatrix}/{TileRow}/{TileCol}.png",
+ "resourceUrl.tile.template is correct");
+
+ t.eq(layer.resourceUrl.FeatureInfo.format, "application/gml+xml; version=3.1", "resourceUrl.FeatureInfo.format is correct");
+ t.eq(layer.resourceUrl.FeatureInfo.template, "http://www.example.com/wmts/coastlines/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}/{J}/{I}.xml",
+ "resourceUrl.FeatureInfo.template is correct");
+
+ t.eq(layer.resourceUrls[0].format, "image/png", "resourceUrls[0].format is correct");
+ t.eq(layer.resourceUrls[0].resourceType, "tile", "resourceUrls[0].resourceType is correct");
+ t.eq(layer.resourceUrls[0].template, "http://www.example.com/wmts/coastlines/{TileMatrix}/{TileRow}/{TileCol}.png",
+ "resourceUrls[0].template is correct");
+
+ t.eq(layer.resourceUrls[1].format, "application/gml+xml; version=3.1", "resourceUrls[0].format is correct");
+ t.eq(layer.resourceUrls[1].resourceType, "FeatureInfo", "resourceUrls[0].resourceType is correct");
+ t.eq(layer.resourceUrls[1].template, "http://www.example.com/wmts/coastlines/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}/{J}/{I}.xml",
+ "resourceUrls[0].template is correct");
+
+ var dimensions = layer.dimensions;
+ t.eq(dimensions.length, 1, "correct count of dimensions");
+ t.eq(dimensions[0].title, "Time", "first dimension title is correct");
+ t.eq(dimensions[0]['abstract'], "Monthly datasets", "first dimension abstract is correct");
+ t.eq(dimensions[0].identifier, "TIME", "first dimension identifier is correct");
+ t.eq(dimensions[0]['default'], "default", "first dimension default is correct");
+ t.eq(dimensions[0].values.length, 3, "first dimension has correct count of values");
+ t.eq(dimensions[0].values[0], "2007-05", "first value is correct");
+ t.eq(dimensions[0].values[1], "2007-06", "second value is correct");
+ t.eq(dimensions[0].values[2], "2007-07", "third value is correct");
+ }
+
+ function test_tileMatrixSets(t) {
+ t.plan(19);
+ var xml = document.getElementById("ogcsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+
+ var obj = new OpenLayers.Format.WMTSCapabilities().read(doc);
+
+ var tileMatrixSets = obj.contents.tileMatrixSets;
+ t.ok(tileMatrixSets['BigWorld'], "tileMatrixSets 'BigWorld' found");
+ var bigWorld = tileMatrixSets['BigWorld'];
+ t.eq(bigWorld.identifier, "BigWorld", "tileMatrixSets identifier is correct");
+ t.eq(bigWorld.matrixIds.length, 2, "tileMatrix count is correct");
+ t.eq(bigWorld.matrixIds[0].identifier, "1e6", "tileMatrix 0 identifier is correct");
+ t.eq(bigWorld.matrixIds[0].matrixHeight, 50000, "tileMatrix 0 matrixHeight is correct");
+ t.eq(bigWorld.matrixIds[0].matrixWidth, 60000, "tileMatrix 0 matrixWidth is correct");
+ t.eq(bigWorld.matrixIds[0].scaleDenominator, 1000000, "tileMatrix 0 scaleDenominator is correct");
+ t.eq(bigWorld.matrixIds[0].tileWidth, 256, "tileMatrix 0 tileWidth is correct");
+ t.eq(bigWorld.matrixIds[0].tileHeight, 256, "tileMatrix 0 tileHeight is correct");
+ t.eq(bigWorld.matrixIds[0].topLeftCorner.lon, -180, "tileMatrix 0 topLeftCorner.lon is correct");
+ t.eq(bigWorld.matrixIds[0].topLeftCorner.lat, 84, "tileMatrix 0 topLeftCorner.lat is correct");
+
+ t.eq(bigWorld.matrixIds[1].identifier, "2.5e6", "tileMatrix 1 identifier is correct");
+ t.eq(bigWorld.matrixIds[1].matrixHeight, 7000, "tileMatrix 1 matrixHeight is correct");
+ t.eq(bigWorld.matrixIds[1].matrixWidth, 9000, "tileMatrix 1 matrixWidth is correct");
+ t.eq(bigWorld.matrixIds[1].scaleDenominator, 2500000, "tileMatrix 1 scaleDenominator is correct");
+ t.eq(bigWorld.matrixIds[1].tileWidth, 256, "tileMatrix 1 tileWidth is correct");
+ t.eq(bigWorld.matrixIds[1].tileHeight, 256, "tileMatrix 1 tileHeight is correct");
+ t.eq(bigWorld.matrixIds[1].topLeftCorner.lon, -180, "tileMatrix 1 topLeftCorner.lon is correct");
+ t.eq(bigWorld.matrixIds[1].topLeftCorner.lat, 84, "tileMatrix 1 topLeftCorner.lat is correct");
+ }
+
+ function test_createLayer(t) {
+ t.plan(43);
+
+ var format = new OpenLayers.Format.WMTSCapabilities();
+
+ var xml = document.getElementById("ogcsample").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+
+ var caps = format.read(doc);
+ var layer;
+
+ var success = true;
+ try {
+ // incomplete config (missing layer)
+ layer = format.createLayer(caps, {
+ });
+ } catch (err) {
+ success = false;
+ }
+ t.ok(!success, "createLayer throws error if provided incomplete layer config");
+
+ // bogus layer identifier
+ try {
+ layer = format.createLayer(caps, {
+ layer: "foo",
+ matrixSet: "BigWorld"
+ });
+ } catch (err) {
+ success = false;
+ }
+ t.ok(!success, "createLayer returns undefined given bad layer identifier");
+
+ // bogus matrixSet identifier
+ try {
+ layer = format.createLayer(caps, {
+ layer: "coastlines",
+ matrixSet: "TheWorld"
+ });
+ } catch (err) {
+ success = false;
+ }
+ t.ok(!success, "createLayer returns undefined given bad matrixSet identifier");
+
+ layer = format.createLayer(caps, {
+ layer: "coastlines",
+ matrixSet: "BigWorld"
+ });
+ t.ok(layer instanceof OpenLayers.Layer.WMTS, "correct instance");
+
+ // autodetect matrixSet
+ layer = format.createLayer(caps, {
+ layer: "coastlines"
+ });
+ t.ok(layer instanceof OpenLayers.Layer.WMTS, "correct instance, with autodetected matrixSet");
+
+ t.eq(layer.matrixIds.length, 2, "correct matrixIds length");
+ t.eq(layer.name, "Coastlines", "correct layer title");
+ t.eq(layer.style, "DarkBlue", "correct style identifier");
+ t.eq(layer.requestEncoding, "KVP", "correct requestEncoding");
+
+ xml = document.getElementById("restsample").firstChild.nodeValue;
+ doc = new OpenLayers.Format.XML().read(xml);
+ caps = format.read(doc);
+ layer = format.createLayer(caps, {
+ layer: "ch.are.agglomerationen_isolierte_staedte-2000",
+ matrixSet: "21781"
+ });
+ t.ok(layer instanceof OpenLayers.Layer.WMTS, "correct instance");
+ t.eq(layer.url[0], "http://wmts.geo.admin.ch/1.0.0/ch.are.agglomerationen_isolierte_staedte-2000/default/{Time}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png", "correct url");
+ t.eq(layer.url[1], "http://wmts1.geo.admin.ch/1.0.0/ch.are.agglomerationen_isolierte_staedte-2000/default/{Time}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png", "correct url");
+ t.eq(layer.matrixIds.length, 3, "correct matrixIds length");
+ t.eq(layer.requestEncoding, "REST", "correct requestEncoding");
+ t.eq(layer.name, "Agglomérations et villes isolées", "correct layer title");
+ t.eq(layer.style, "ch.are.agglomerationen_isolierte_staedte-2000", "correct style identifier");
+ t.eq(layer.projection.getCode(), "EPSG:21781", "correct projection");
+ t.eq(layer.units, "m", "correct untis");
+ t.ok(layer.serverResolutions === layer.resolutions, "serverResolutions set");
+ t.eq(layer.resolutions.length, 3, "correct resolutions length");
+ t.ok((layer.resolutions[0] - 4000) < 1, "correct first resolution");
+ t.eq(layer.dimensions.length, 1, "correct dimensions length");
+ t.eq(layer.dimensions[0], "Time", "correct dimensions");
+ t.eq(layer.params['TIME'], "20090101", "correct params");
+
+ layer = format.createLayer(caps, {
+ layer: "ch.are.agglomerationen_isolierte_staedte-2000",
+ style: "toto",
+ params: {"Time": "2012"}
+ });
+ t.eq(layer.matrixIds.length, 3, "correct matrixIds length");
+ t.eq(layer.style, "toto", "correct style identifier");
+ t.eq(layer.dimensions.length, 1, "correct dimensions length");
+ t.eq(layer.dimensions[0], "Time", "correct dimensions");
+ t.eq(layer.params['TIME'], "2012", "correct params");
+
+ // test projection and units
+ layer = format.createLayer(caps, {
+ layer: "ch.are.agglomerationen_isolierte_staedte-2000",
+ matrixSet: "21781",
+ units: 'degrees'
+ });
+ t.eq(layer.units, "degrees", "correct units");
+ layer = format.createLayer(caps, {
+ layer: "ch.are.agglomerationen_isolierte_staedte-2000",
+ matrixSet: "21781",
+ projection: "EPSG:4326"
+ });
+ t.eq(layer.projection.getCode(), "EPSG:4326", "correct projection");
+ t.eq(layer.units, "degrees", "correct units");
+ layer = format.createLayer(caps, {
+ layer: "ch.are.agglomerationen_isolierte_staedte-2000",
+ matrixSet: "21781",
+ projection: "EPSG:4326",
+ units: 'm'
+ });
+ t.eq(layer.projection.getCode(), "EPSG:4326", "correct projection");
+ t.eq(layer.units, "m", "correct units");
+ layer = format.createLayer(caps, {
+ layer: "ch.are.agglomerationen_isolierte_staedte-2000",
+ matrixSet: "21781",
+ projection: "EPSG:900913",
+ units: 'degrees'
+ });
+ t.eq(layer.projection.getCode(), "EPSG:900913", "correct projection");
+ t.eq(layer.units, "degrees", "correct units");
+
+
+ // test get the right url #608/3
+ xml = document.getElementById("multi-getile-1").firstChild.nodeValue;
+ doc = new OpenLayers.Format.XML().read(xml);
+ caps = format.read(doc);
+ layer = format.createLayer(caps, {
+ layer: "ch.are.agglomerationen_isolierte_staedte-2000",
+ matrixSet: "21781",
+ requestEncoding: 'REST'
+ });
+ t.eq(layer.url[0], "http://wmts.geo.admin.ch/rest", "correct rest url 1");
+ t.eq(layer.url[1], "http://wmts1.geo.admin.ch/rest", "correct rest url 1");
+ layer = format.createLayer(caps, {
+ layer: "ch.are.agglomerationen_isolierte_staedte-2000",
+ matrixSet: "21781",
+ requestEncoding: 'KVP'
+ });
+ t.eq(layer.url[0], "http://wmts.geo.admin.ch/kvp", "correct kvp url 2");
+ t.eq(layer.url[1], "http://wmts1.geo.admin.ch/kvp", "correct kvp url 2");
+ xml = document.getElementById("multi-getile-2").firstChild.nodeValue;
+ doc = new OpenLayers.Format.XML().read(xml);
+ caps = format.read(doc);
+ layer = format.createLayer(caps, {
+ layer: "ch.are.agglomerationen_isolierte_staedte-2000",
+ matrixSet: "21781",
+ requestEncoding: 'REST'
+ });
+ t.eq(layer.url[0], "http://wmts.geo.admin.ch/rest", "correct rest url 2");
+ layer = format.createLayer(caps, {
+ layer: "ch.are.agglomerationen_isolierte_staedte-2000",
+ matrixSet: "21781",
+ requestEncoding: 'KVP'
+ });
+ t.eq(layer.url[0], "http://wmts.geo.admin.ch/kvp", "correct kvp url 2");
+
+ // test RESTfull
+ xml = document.getElementById("arcgis").firstChild.nodeValue;
+ doc = new OpenLayers.Format.XML().read(xml);
+ caps = format.read(doc);
+ layer = format.createLayer(caps, {
+ layer: "WorldTimeZones"
+ });
+ t.eq(layer.requestEncoding, "REST", "correct requestEncoding (in RESTfull)");
+ }
+
+ function test_parse_projection(t) {
+ t.plan(2);
+
+ var format = new OpenLayers.Format.WMTSCapabilities();
+
+ var xml = document.getElementById("restsample-alternate-proj1").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+ var caps = format.read(doc);
+ var layer = format.createLayer(caps, {
+ layer: "ch.are.agglomerationen_isolierte_staedte-2000",
+ matrixSet: "21781"
+ });
+ t.eq(layer.projection.getCode(), "EPSG:21781", "correct projection");
+
+ xml = document.getElementById("restsample-alternate-proj2").firstChild.nodeValue;
+ doc = new OpenLayers.Format.XML().read(xml);
+ caps = format.read(doc);
+ layer = format.createLayer(caps, {
+ layer: "ch.are.agglomerationen_isolierte_staedte-2000",
+ matrixSet: "21781"
+ });
+ t.eq(layer.projection.getCode(), "EPSG:21781", "correct projection");
+ }
+ </script>
+</head>
+<body>
+
+<!--
+OGC example below taken from
+http://schemas.opengis.net/wmts/1.0/examples/wmtsGetCapabilities_response.xml
+-->
+<div id="ogcsample"><!--
+<?xml version="1.0" encoding="UTF-8"?>
+<Capabilities xmlns="http://www.opengis.net/wmts/1.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml" xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd" version="1.0.0">
+ <ows:ServiceIdentification>
+ <ows:Title>Web Map Tile Service</ows:Title>
+ <ows:Abstract>Service that contrains the map access interface to some TileMatrixSets</ows:Abstract>
+ <ows:Keywords>
+ <ows:Keyword>tile</ows:Keyword>
+ <ows:Keyword>tile matrix set</ows:Keyword>
+ <ows:Keyword>map</ows:Keyword>
+ </ows:Keywords>
+ <ows:ServiceType>OGC WMTS</ows:ServiceType>
+ <ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion>
+ <ows:Fees>none</ows:Fees>
+ <ows:AccessConstraints>none</ows:AccessConstraints>
+ </ows:ServiceIdentification>
+ <ows:ServiceProvider>
+ <ows:ProviderName>MiraMon</ows:ProviderName>
+ <ows:ProviderSite xlink:href="http://www.creaf.uab.es/miramon"/>
+ <ows:ServiceContact>
+ <ows:IndividualName>Joan Maso Pau</ows:IndividualName>
+ <ows:PositionName>Senior Software Engineer</ows:PositionName>
+ <ows:ContactInfo>
+ <ows:Phone>
+ <ows:Voice>+34 93 581 1312</ows:Voice>
+ <ows:Facsimile>+34 93 581 4151</ows:Facsimile>
+ </ows:Phone>
+ <ows:Address>
+ <ows:DeliveryPoint>Fac Ciencies UAB</ows:DeliveryPoint>
+ <ows:City>Bellaterra</ows:City>
+ <ows:AdministrativeArea>Barcelona</ows:AdministrativeArea>
+ <ows:PostalCode>08193</ows:PostalCode>
+ <ows:Country>Spain</ows:Country>
+ <ows:ElectronicMailAddress>joan.maso@uab.es</ows:ElectronicMailAddress>
+ </ows:Address>
+ </ows:ContactInfo>
+ </ows:ServiceContact>
+ </ows:ServiceProvider>
+ <ows:OperationsMetadata>
+ <ows:Operation name="GetCapabilities">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://www.miramon.uab.es/cgi-bin/MiraMon5_0.cgi?">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>KVP</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ <ows:Operation name="GetTile">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://www.miramon.uab.es/cgi-bin/MiraMon5_0.cgi?"/>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ <ows:Operation name="GetFeatureInfo">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://www.miramon.uab.es/cgi-bin/MiraMon5_0.cgi?"/>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ </ows:OperationsMetadata>
+ <Contents>
+ <Layer>
+ <ows:Title>Coastlines</ows:Title>
+ <ows:Abstract>Coastline/shorelines (BA010)</ows:Abstract>
+ <ows:WGS84BoundingBox>
+ <ows:LowerCorner>-180 -90</ows:LowerCorner>
+ <ows:UpperCorner>180 90</ows:UpperCorner>
+ </ows:WGS84BoundingBox>
+ <ows:Identifier>coastlines</ows:Identifier>
+ <ResourceURL format="image/png" resourceType="tile"
+ template="http://www.example.com/wmts/coastlines/{TileMatrix}/{TileRow}/{TileCol}.png" />
+ <ResourceURL format="application/gml+xml; version=3.1" resourceType="FeatureInfo"
+ template="http://www.example.com/wmts/coastlines/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}/{J}/{I}.xml" />
+ <Style isDefault="true">
+ <ows:Title>Dark Blue</ows:Title>
+ <ows:Identifier>DarkBlue</ows:Identifier>
+ <LegendURL format="image/png" xlink:href="http://www.miramon.uab.es/wmts/Coastlines/coastlines_darkBlue.png"/>
+ </Style>
+ <Style>
+ <ows:Title>Thick And Red</ows:Title>
+ <ows:Abstract>Specify this style if you want your maps to have thick red coastlines.
+ </ows:Abstract>
+ <ows:Identifier>thickAndRed</ows:Identifier>
+ </Style>
+ <Format>image/png</Format>
+ <Format>image/gif</Format>
+ <Dimension>
+ <ows:Title>Time</ows:Title>
+ <ows:Abstract>Monthly datasets</ows:Abstract>
+ <ows:Identifier>TIME</ows:Identifier>
+ <Value>2007-05</Value>
+ <Value>2007-06</Value>
+ <Value>2007-07</Value>
+ <Default>default</Default>
+ </Dimension>
+ <TileMatrixSetLink>
+ <TileMatrixSet>BigWorld</TileMatrixSet>
+ </TileMatrixSetLink>
+ </Layer>
+ <TileMatrixSet>
+ <ows:Identifier>BigWorld</ows:Identifier>
+ <ows:SupportedCRS>urn:ogc:def:crs:OGC:1.3:CRS84</ows:SupportedCRS>
+ <TileMatrix>
+ <ows:Identifier>1e6</ows:Identifier>
+ <ScaleDenominator>1e6</ScaleDenominator>
+ <TopLeftCorner>-180 84</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>60000</MatrixWidth>
+ <MatrixHeight>50000</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>2.5e6</ows:Identifier>
+ <ScaleDenominator>2.5e6</ScaleDenominator>
+ <TopLeftCorner>-180 84</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>9000</MatrixWidth>
+ <MatrixHeight>7000</MatrixHeight>
+ </TileMatrix>
+ </TileMatrixSet>
+ </Contents>
+ <Themes>
+ <Theme>
+ <ows:Title>Foundation</ows:Title>
+ <ows:Abstract>"Digital Chart Of The World" data</ows:Abstract>
+ <ows:Identifier>Foundation</ows:Identifier>
+ <Theme>
+ <ows:Title>Boundaries</ows:Title>
+ <ows:Identifier>Boundaries</ows:Identifier>
+ <LayerRef>coastlines</LayerRef>
+ <LayerRef>politicalBoundaries</LayerRef>
+ <LayerRef>depthContours</LayerRef>
+ </Theme>
+ <Theme>
+ <ows:Title>Transportation</ows:Title>
+ <ows:Identifier>Transportation</ows:Identifier>
+ <LayerRef>roads</LayerRef>
+ <LayerRef>railroads</LayerRef>
+ <LayerRef>airports</LayerRef>
+ </Theme>
+ </Theme>
+ <Theme>
+ <ows:Title>World Geology</ows:Title>
+ <ows:Identifier>World Geology</ows:Identifier>
+ <LayerRef>worldAgeRockType</LayerRef>
+ <LayerRef>worldFaultLines</LayerRef>
+ <LayerRef>felsicMagmatic</LayerRef>
+ <LayerRef>maficMagmatic</LayerRef>
+ </Theme>
+ </Themes>
+</Capabilities>
+--></div>
+
+<div id="restsample"><!--
+<?xml version="1.0" encoding="UTF-8"?>
+<Capabilities xmlns="http://www.opengis.net/wmts/1.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml" xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd" version="1.0.0">
+ <ows:ServiceIdentification>
+ <ows:Title>Federal Geodata Infrastructure of Switzerland</ows:Title>
+ <ows:Abstract>Some Geodata are subject to license and fees</ows:Abstract>
+ <ows:Keywords>
+ <ows:Keyword>FGDI</ows:Keyword>
+ <ows:Keyword>Pixelkarte</ows:Keyword>
+ <ows:Keyword>Switzerland</ows:Keyword>
+ </ows:Keywords>
+ <ows:ServiceType>OGC WMTS</ows:ServiceType>
+ <ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion>
+ <ows:Fees>yes</ows:Fees>
+ <ows:AccessConstraints>license</ows:AccessConstraints>
+ </ows:ServiceIdentification>
+ <ows:ServiceProvider>
+ <ows:ProviderName>swisstopo</ows:ProviderName>
+ <ows:ProviderSite xlink:href="http://www.swisstopo.admin.ch"/>
+ <ows:ServiceContact>
+ <ows:IndividualName>David Oesch</ows:IndividualName>
+ <ows:PositionName></ows:PositionName>
+ <ows:ContactInfo>
+ <ows:Phone>
+ <ows:Voice>+41 (0)31 / 963 21 11</ows:Voice>
+ <ows:Facsimile>+41 (0)31 / 963 24 59</ows:Facsimile>
+ </ows:Phone>
+ <ows:Address>
+ <ows:DeliveryPoint>swisstopo</ows:DeliveryPoint>
+ <ows:City>Bern</ows:City>
+ <ows:AdministrativeArea>BE</ows:AdministrativeArea>
+ <ows:PostalCode>3084</ows:PostalCode>
+ <ows:Country>Switzerland</ows:Country>
+ <ows:ElectronicMailAddress/>
+ </ows:Address>
+ </ows:ContactInfo>
+ </ows:ServiceContact>
+ </ows:ServiceProvider>
+ <ows:OperationsMetadata>
+ <ows:Operation name="GetCapabilities">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://wmts.geo.admin.ch/1.0.0/WMTSCapabilities.xml">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>REST</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ <ows:Operation name="GetTile">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://wmts.geo.admin.ch/">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>REST</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ </ows:OperationsMetadata>
+ <Contents>
+ <Layer>
+ <ows:Title>Agglomérations et villes isolées</ows:Title>
+ <ows:Abstract>Les agglomérations et villes isolées (communes non rattachées à une agglomération et comptant au moins 10`000 habitants) font partie des régions d’analyse de la statistique suisse. Ce niveau géographique est défini depuis plus de 100 ans, afin de mesurer l’urbanisation, phénomène fondamental structurant l’organisation du territoire. Sa fonction principale est de permettre une comparaison spatiale entre des espaces urbains inégalement délimités sur le plan institutionnel. Une version ancienne est appliquée pour la première fois en 1930, puis révisée en 1984 et 1990, toujours sur la base des recensements de la population. La version actuelle classe les 2896 communes de Suisse (état 2000) selon leur appartenance ou pas à une agglomération ou ville isolée en fonction de critères statistiques (Etat et évolution de la population, lien de continuité de la zone bâtie, rapport entre population active occupée et population résidante, structure économique et flux de pendulaires). Les agglomérations et les villes isolées forment l`espace urbain, les territoires restant l`espace rural. La définition des agglomérations de l’OFS n’a pas valeur d’obligation légale.</ows:Abstract>
+ <ows:WGS84BoundingBox>
+ <ows:LowerCorner>5.140242 45.398181</ows:LowerCorner>
+ <ows:UpperCorner>11.47757 48.230651</ows:UpperCorner>
+ </ows:WGS84BoundingBox>
+ <ows:Identifier>ch.are.agglomerationen_isolierte_staedte-2000</ows:Identifier>
+ <ows:Metadata xlink:href="http://www.swisstopo.admin.ch/SITiled/world/AdminBoundaries/metadata.htm"/>
+ <Style>
+ <ows:Title>Agglomérations et villes isolées</ows:Title>
+ <ows:Identifier>ch.are.agglomerationen_isolierte_staedte-2000</ows:Identifier>
+ <LegendURL format="image/png" xlink:href="http://api.geo.admin.ch/legend/ch.are.agglomerationen_isolierte_staedte-2000_fr.png" />
+ </Style>
+ <Format>image/png</Format>
+ <Dimension>
+ <ows:Identifier>Time</ows:Identifier>
+ <Default>20090101</Default>
+ <Value>20090101</Value>
+ </Dimension>
+ <TileMatrixSetLink>
+ <TileMatrixSet>21781</TileMatrixSet>
+ </TileMatrixSetLink>
+ <ResourceURL format="image/png" resourceType="tile" template="http://wmts.geo.admin.ch/1.0.0/ch.are.agglomerationen_isolierte_staedte-2000/default/{Time}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png"/>
+ <ResourceURL format="image/png" resourceType="tile" template="http://wmts1.geo.admin.ch/1.0.0/ch.are.agglomerationen_isolierte_staedte-2000/default/{Time}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png"/>
+ </Layer>
+ <TileMatrixSet>
+ <ows:Identifier>21781</ows:Identifier>
+ <ows:SupportedCRS>urn:ogc:def:crs:EPSG::21781</ows:SupportedCRS>
+ <TileMatrix>
+ <ows:Identifier>0</ows:Identifier>
+ <ScaleDenominator>14285750.5715</ScaleDenominator>
+ <TopLeftCorner>420000.0 350000.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1</MatrixWidth>
+ <MatrixHeight>1</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>8</ows:Identifier>
+ <ScaleDenominator>7142875.28575</ScaleDenominator>
+ <TopLeftCorner>420000.0 350000.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1</MatrixWidth>
+ <MatrixHeight>1</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>12</ows:Identifier>
+ <ScaleDenominator>3571437.64288</ScaleDenominator>
+ <TopLeftCorner>420000.0 350000.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>2</MatrixWidth>
+ <MatrixHeight>2</MatrixHeight>
+ </TileMatrix>
+ </TileMatrixSet>
+ </Contents>
+ <ServiceMetadataURL xlink:href="http://www.opengis.uab.es/SITiled/world/1.0.0/WMTSCapabilities.xml"/>
+</Capabilities>
+--></div>
+
+<div id="restsample-alternate-proj1"><!--
+<?xml version="1.0" encoding="UTF-8"?>
+<Capabilities xmlns="http://www.opengis.net/wmts/1.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml" xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd" version="1.0.0">
+ <ows:OperationsMetadata>
+ <ows:Operation name="GetCapabilities">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://wmts.geo.admin.ch/1.0.0/WMTSCapabilities.xml">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>REST</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ <ows:Operation name="GetTile">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://wmts.geo.admin.ch/">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>REST</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ </ows:OperationsMetadata>
+ <Contents>
+ <Layer>
+ <ows:Title>Agglomérations et villes isolées</ows:Title>
+ <ows:Abstract>Les agglomérations et villes isolées (communes non rattachées à une agglomération et comptant au moins 10`000 habitants) font partie des régions d’analyse de la statistique suisse. Ce niveau géographique est défini depuis plus de 100 ans, afin de mesurer l’urbanisation, phénomène fondamental structurant l’organisation du territoire. Sa fonction principale est de permettre une comparaison spatiale entre des espaces urbains inégalement délimités sur le plan institutionnel. Une version ancienne est appliquée pour la première fois en 1930, puis révisée en 1984 et 1990, toujours sur la base des recensements de la population. La version actuelle classe les 2896 communes de Suisse (état 2000) selon leur appartenance ou pas à une agglomération ou ville isolée en fonction de critères statistiques (Etat et évolution de la population, lien de continuité de la zone bâtie, rapport entre population active occupée et population résidante, structure économique et flux de pendulaires). Les agglomérations et les villes isolées forment l`espace urbain, les territoires restant l`espace rural. La définition des agglomérations de l’OFS n’a pas valeur d’obligation légale.</ows:Abstract>
+ <ows:WGS84BoundingBox>
+ <ows:LowerCorner>5.140242 45.398181</ows:LowerCorner>
+ <ows:UpperCorner>11.47757 48.230651</ows:UpperCorner>
+ </ows:WGS84BoundingBox>
+ <ows:Identifier>ch.are.agglomerationen_isolierte_staedte-2000</ows:Identifier>
+ <ows:Metadata xlink:href="http://www.swisstopo.admin.ch/SITiled/world/AdminBoundaries/metadata.htm"/>
+ <Style>
+ <ows:Title>Agglomérations et villes isolées</ows:Title>
+ <ows:Identifier>ch.are.agglomerationen_isolierte_staedte-2000</ows:Identifier>
+ <LegendURL format="image/png" xlink:href="http://api.geo.admin.ch/legend/ch.are.agglomerationen_isolierte_staedte-2000_fr.png" />
+ </Style>
+ <Format>image/png</Format>
+ <Dimension>
+ <ows:Identifier>Time</ows:Identifier>
+ <Default>20090101</Default>
+ <Value>20090101</Value>
+ </Dimension>
+ <TileMatrixSetLink>
+ <TileMatrixSet>21781</TileMatrixSet>
+ </TileMatrixSetLink>
+ <ResourceURL format="image/png" resourceType="tile" template="http://wmts.geo.admin.ch/1.0.0/ch.are.agglomerationen_isolierte_staedte-2000/default/{Time}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png"/>
+ </Layer>
+ <TileMatrixSet>
+ <ows:Identifier>21781</ows:Identifier>
+ <ows:SupportedCRS>urn:ogc:def:crs:EPSG:21781</ows:SupportedCRS>
+ <TileMatrix>
+ <ows:Identifier>0</ows:Identifier>
+ <ScaleDenominator>14285750.5715</ScaleDenominator>
+ <TopLeftCorner>420000.0 350000.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1</MatrixWidth>
+ <MatrixHeight>1</MatrixHeight>
+ </TileMatrix>
+ </TileMatrixSet>
+ </Contents>
+ <ServiceMetadataURL xlink:href="http://www.opengis.uab.es/SITiled/world/1.0.0/WMTSCapabilities.xml"/>
+</Capabilities>
+--></div>
+
+<div id="restsample-alternate-proj2"><!--
+<?xml version="1.0" encoding="UTF-8"?>
+<Capabilities xmlns="http://www.opengis.net/wmts/1.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml" xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd" version="1.0.0">
+ <ows:OperationsMetadata>
+ <ows:Operation name="GetCapabilities">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://wmts.geo.admin.ch/1.0.0/WMTSCapabilities.xml">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>REST</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ <ows:Operation name="GetTile">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://wmts.geo.admin.ch/">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>REST</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ </ows:OperationsMetadata>
+ <Contents>
+ <Layer>
+ <ows:Title>Agglomérations et villes isolées</ows:Title>
+ <ows:Abstract>Les agglomérations et villes isolées (communes non rattachées à une agglomération et comptant au moins 10`000 habitants) font partie des régions d’analyse de la statistique suisse. Ce niveau géographique est défini depuis plus de 100 ans, afin de mesurer l’urbanisation, phénomène fondamental structurant l’organisation du territoire. Sa fonction principale est de permettre une comparaison spatiale entre des espaces urbains inégalement délimités sur le plan institutionnel. Une version ancienne est appliquée pour la première fois en 1930, puis révisée en 1984 et 1990, toujours sur la base des recensements de la population. La version actuelle classe les 2896 communes de Suisse (état 2000) selon leur appartenance ou pas à une agglomération ou ville isolée en fonction de critères statistiques (Etat et évolution de la population, lien de continuité de la zone bâtie, rapport entre population active occupée et population résidante, structure économique et flux de pendulaires). Les agglomérations et les villes isolées forment l`espace urbain, les territoires restant l`espace rural. La définition des agglomérations de l’OFS n’a pas valeur d’obligation légale.</ows:Abstract>
+ <ows:WGS84BoundingBox>
+ <ows:LowerCorner>5.140242 45.398181</ows:LowerCorner>
+ <ows:UpperCorner>11.47757 48.230651</ows:UpperCorner>
+ </ows:WGS84BoundingBox>
+ <ows:Identifier>ch.are.agglomerationen_isolierte_staedte-2000</ows:Identifier>
+ <ows:Metadata xlink:href="http://www.swisstopo.admin.ch/SITiled/world/AdminBoundaries/metadata.htm"/>
+ <Style>
+ <ows:Title>Agglomérations et villes isolées</ows:Title>
+ <ows:Identifier>ch.are.agglomerationen_isolierte_staedte-2000</ows:Identifier>
+ <LegendURL format="image/png" xlink:href="http://api.geo.admin.ch/legend/ch.are.agglomerationen_isolierte_staedte-2000_fr.png" />
+ </Style>
+ <Format>image/png</Format>
+ <Dimension>
+ <ows:Identifier>Time</ows:Identifier>
+ <Default>20090101</Default>
+ <Value>20090101</Value>
+ </Dimension>
+ <TileMatrixSetLink>
+ <TileMatrixSet>21781</TileMatrixSet>
+ </TileMatrixSetLink>
+ <ResourceURL format="image/png" resourceType="tile" template="http://wmts.geo.admin.ch/1.0.0/ch.are.agglomerationen_isolierte_staedte-2000/default/{Time}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png"/>
+ </Layer>
+ <TileMatrixSet>
+ <ows:Identifier>21781</ows:Identifier>
+ <ows:SupportedCRS>urn:ogc:def:crs:EPSG:1.0:21781</ows:SupportedCRS>
+ <TileMatrix>
+ <ows:Identifier>0</ows:Identifier>
+ <ScaleDenominator>14285750.5715</ScaleDenominator>
+ <TopLeftCorner>420000.0 350000.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1</MatrixWidth>
+ <MatrixHeight>1</MatrixHeight>
+ </TileMatrix>
+ </TileMatrixSet>
+ </Contents>
+ <ServiceMetadataURL xlink:href="http://www.opengis.uab.es/SITiled/world/1.0.0/WMTSCapabilities.xml"/>
+</Capabilities>
+--></div>
+
+<div id="multi-getile-1"><!--
+<?xml version="1.0" encoding="UTF-8"?>
+<Capabilities xmlns="http://www.opengis.net/wmts/1.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml" xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd" version="1.0.0">
+ <ows:OperationsMetadata>
+ <ows:Operation name="GetCapabilities">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://wmts.geo.admin.ch/1.0.0/WMTSCapabilities.xml">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>REST</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ <ows:Operation name="GetTile">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://wmts.geo.admin.ch/rest">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>REST</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ <ows:Get xlink:href="http://wmts1.geo.admin.ch/rest">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>REST</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ <ows:Get xlink:href="http://wmts.geo.admin.ch/kvp">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>KVP</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ <ows:Get xlink:href="http://wmts1.geo.admin.ch/kvp">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>KVP</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ </ows:OperationsMetadata>
+ <Contents>
+ <Layer>
+ <ows:Title>Agglomérations et villes isolées</ows:Title>
+ <ows:Abstract>Les agglomérations et villes isolées (communes non rattachées à une agglomération et comptant au moins 10`000 habitants) font partie des régions d’analyse de la statistique suisse. Ce niveau géographique est défini depuis plus de 100 ans, afin de mesurer l’urbanisation, phénomène fondamental structurant l’organisation du territoire. Sa fonction principale est de permettre une comparaison spatiale entre des espaces urbains inégalement délimités sur le plan institutionnel. Une version ancienne est appliquée pour la première fois en 1930, puis révisée en 1984 et 1990, toujours sur la base des recensements de la population. La version actuelle classe les 2896 communes de Suisse (état 2000) selon leur appartenance ou pas à une agglomération ou ville isolée en fonction de critères statistiques (Etat et évolution de la population, lien de continuité de la zone bâtie, rapport entre population active occupée et population résidante, structure économique et flux de pendulaires). Les agglomérations et les villes isolées forment l`espace urbain, les territoires restant l`espace rural. La définition des agglomérations de l’OFS n’a pas valeur d’obligation légale.</ows:Abstract>
+ <ows:WGS84BoundingBox>
+ <ows:LowerCorner>5.140242 45.398181</ows:LowerCorner>
+ <ows:UpperCorner>11.47757 48.230651</ows:UpperCorner>
+ </ows:WGS84BoundingBox>
+ <ows:Identifier>ch.are.agglomerationen_isolierte_staedte-2000</ows:Identifier>
+ <ows:Metadata xlink:href="http://www.swisstopo.admin.ch/SITiled/world/AdminBoundaries/metadata.htm"/>
+ <Style>
+ <ows:Title>Agglomérations et villes isolées</ows:Title>
+ <ows:Identifier>ch.are.agglomerationen_isolierte_staedte-2000</ows:Identifier>
+ <LegendURL format="image/png" xlink:href="http://api.geo.admin.ch/legend/ch.are.agglomerationen_isolierte_staedte-2000_fr.png" />
+ </Style>
+ <Format>image/png</Format>
+ <Dimension>
+ <ows:Identifier>Time</ows:Identifier>
+ <Default>20090101</Default>
+ <Value>20090101</Value>
+ </Dimension>
+ <TileMatrixSetLink>
+ <TileMatrixSet>21781</TileMatrixSet>
+ </TileMatrixSetLink>
+ </Layer>
+ <TileMatrixSet>
+ <ows:Identifier>21781</ows:Identifier>
+ <ows:SupportedCRS>urn:ogc:def:crs:EPSG:1.0:21781</ows:SupportedCRS>
+ <TileMatrix>
+ <ows:Identifier>0</ows:Identifier>
+ <ScaleDenominator>14285750.5715</ScaleDenominator>
+ <TopLeftCorner>420000.0 350000.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1</MatrixWidth>
+ <MatrixHeight>1</MatrixHeight>
+ </TileMatrix>
+ </TileMatrixSet>
+ </Contents>
+ <ServiceMetadataURL xlink:href="http://www.opengis.uab.es/SITiled/world/1.0.0/WMTSCapabilities.xml"/>
+</Capabilities>
+--></div>
+<div id="arcgis"><!--
+<?xml version="1.0" encoding="UTF-8"?>
+<Capabilities xmlns="http://www.opengis.net/wmts/1.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml" xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd" version="1.0.0">
+ <ows:ServiceIdentification>
+ <ows:Title>WorldTimeZones</ows:Title>
+ <ows:ServiceType>OGC WMTS</ows:ServiceType>
+ <ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion>
+ </ows:ServiceIdentification>
+ <ows:OperationsMetadata>
+ <ows:Operation name="GetTile">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://sampleserver6.arcgisonline.com/arcgis/rest/services/WorldTimeZones/MapServer/WMTS/tile/1.0.0/">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>RESTful</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ <ows:Get xlink:href="http://sampleserver6.arcgisonline.com/arcgis/rest/services/WorldTimeZones/MapServer/WMTS?">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>KVP</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ </ows:OperationsMetadata>
+ <Contents>
+ <Layer>
+ <ows:Title>WorldTimeZones</ows:Title>
+ <ows:Identifier>WorldTimeZones</ows:Identifier>
+ <ows:BoundingBox crs="urn:ogc:def:crs:EPSG::102100">
+ <ows:LowerCorner>-2.0037507067161843E7 -3.024097195838617E7</ows:LowerCorner>
+ <ows:UpperCorner>2.0037507067161843E7 3.0240971458386205E7</ows:UpperCorner>
+ </ows:BoundingBox>
+ <ows:WGS84BoundingBox crs="urn:ogc:def:crs:OGC:2:84">
+ <ows:LowerCorner>-179.99999550841463 -88.99999992161119</ows:LowerCorner>
+ <ows:UpperCorner>179.99999550841463 88.99999992161118</ows:UpperCorner>
+ </ows:WGS84BoundingBox>
+ <Style isDefault="true">
+ <ows:Title>Default Style</ows:Title>
+ <ows:Identifier>default</ows:Identifier>
+ </Style>
+ <Format>image/png</Format>
+ <TileMatrixSetLink>
+ <TileMatrixSet>GoogleMapsCompatible</TileMatrixSet>
+ </TileMatrixSetLink>
+ <ResourceURL format="image/png" resourceType="tile" template="http://sampleserver6.arcgisonline.com/arcgis/rest/services/WorldTimeZones/MapServer/WMTS/tile/1.0.0/WorldTimeZones/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png" />
+ </Layer>
+ <TileMatrixSet>
+ <ows:Title>GoogleMapsCompatible</ows:Title>
+ <ows:Abstract>the wellknown 'GoogleMapsCompatible' tile matrix set defined by OGC WMTS specification</ows:Abstract>
+ <ows:Identifier>GoogleMapsCompatible</ows:Identifier>
+ <ows:SupportedCRS>urn:ogc:def:crs:EPSG:6.18:3:3857</ows:SupportedCRS>
+ <WellKnownScaleSet>urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible</WellKnownScaleSet>
+ <TileMatrix>
+ <ows:Identifier>5</ows:Identifier>
+ <ScaleDenominator>17471320.75089743</ScaleDenominator>
+ <TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>32</MatrixWidth>
+ <MatrixHeight>32</MatrixHeight>
+ </TileMatrix>
+ </TileMatrixSet>
+ </Contents>
+ <ServiceMetadataURL xlink:href="http://sampleserver6.arcgisonline.com/arcgis/rest/services/WorldTimeZones/MapServer/WMTS/1.0.0/WMTSCapabilities.xml" />
+</Capabilities>
+--></div>
+
+<div id="multi-getile-2"><!--
+<?xml version="1.0" encoding="UTF-8"?>
+<Capabilities xmlns="http://www.opengis.net/wmts/1.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml" xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd" version="1.0.0">
+ <ows:OperationsMetadata>
+ <ows:Operation name="GetCapabilities">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://wmts.geo.admin.ch/1.0.0/WMTSCapabilities.xml">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>REST</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ <ows:Operation name="GetTile">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://wmts.geo.admin.ch/kvp">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>KVP</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ <ows:Get xlink:href="http://wmts.geo.admin.ch/rest">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>REST</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ </ows:OperationsMetadata>
+ <Contents>
+ <Layer>
+ <ows:Title>Agglomérations et villes isolées</ows:Title>
+ <ows:Abstract>Les agglomérations et villes isolées (communes non rattachées à une agglomération et comptant au moins 10`000 habitants) font partie des régions d’analyse de la statistique suisse. Ce niveau géographique est défini depuis plus de 100 ans, afin de mesurer l’urbanisation, phénomène fondamental structurant l’organisation du territoire. Sa fonction principale est de permettre une comparaison spatiale entre des espaces urbains inégalement délimités sur le plan institutionnel. Une version ancienne est appliquée pour la première fois en 1930, puis révisée en 1984 et 1990, toujours sur la base des recensements de la population. La version actuelle classe les 2896 communes de Suisse (état 2000) selon leur appartenance ou pas à une agglomération ou ville isolée en fonction de critères statistiques (Etat et évolution de la population, lien de continuité de la zone bâtie, rapport entre population active occupée et population résidante, structure économique et flux de pendulaires). Les agglomérations et les villes isolées forment l`espace urbain, les territoires restant l`espace rural. La définition des agglomérations de l’OFS n’a pas valeur d’obligation légale.</ows:Abstract>
+ <ows:WGS84BoundingBox>
+ <ows:LowerCorner>5.140242 45.398181</ows:LowerCorner>
+ <ows:UpperCorner>11.47757 48.230651</ows:UpperCorner>
+ </ows:WGS84BoundingBox>
+ <ows:Identifier>ch.are.agglomerationen_isolierte_staedte-2000</ows:Identifier>
+ <ows:Metadata xlink:href="http://www.swisstopo.admin.ch/SITiled/world/AdminBoundaries/metadata.htm"/>
+ <Style>
+ <ows:Title>Agglomérations et villes isolées</ows:Title>
+ <ows:Identifier>ch.are.agglomerationen_isolierte_staedte-2000</ows:Identifier>
+ <LegendURL format="image/png" xlink:href="http://api.geo.admin.ch/legend/ch.are.agglomerationen_isolierte_staedte-2000_fr.png" />
+ </Style>
+ <Format>image/png</Format>
+ <Dimension>
+ <ows:Identifier>Time</ows:Identifier>
+ <Default>20090101</Default>
+ <Value>20090101</Value>
+ </Dimension>
+ <TileMatrixSetLink>
+ <TileMatrixSet>21781</TileMatrixSet>
+ </TileMatrixSetLink>
+ </Layer>
+ <TileMatrixSet>
+ <ows:Identifier>21781</ows:Identifier>
+ <ows:SupportedCRS>urn:ogc:def:crs:EPSG:1.0:21781</ows:SupportedCRS>
+ <TileMatrix>
+ <ows:Identifier>0</ows:Identifier>
+ <ScaleDenominator>14285750.5715</ScaleDenominator>
+ <TopLeftCorner>420000.0 350000.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1</MatrixWidth>
+ <MatrixHeight>1</MatrixHeight>
+ </TileMatrix>
+ </TileMatrixSet>
+ </Contents>
+ <ServiceMetadataURL xlink:href="http://www.opengis.uab.es/SITiled/world/1.0.0/WMTSCapabilities.xml"/>
+</Capabilities>
+--></div>
+
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WPSCapabilities/v1_0_0.html b/misc/openlayers/tests/Format/WPSCapabilities/v1_0_0.html
new file mode 100644
index 0000000..191f29f
--- /dev/null
+++ b/misc/openlayers/tests/Format/WPSCapabilities/v1_0_0.html
@@ -0,0 +1,30 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script src="v1_0_0.js"></script>
+ <script type="text/javascript">
+
+ function test_read(t) {
+
+ t.plan(7);
+
+ var format = new OpenLayers.Format.WPSCapabilities();
+ var obj = format.read(doc);
+
+ t.eq(obj.version, "1.0.0", "Version parsed correctly");
+
+ t.eq(obj.languages.length, 2, "2 language entries parsed");
+ t.eq(obj.languages[0].isDefault, true, "First language is the default language");
+ t.eq(obj.languages[0].language, "en-US", "First language is US English");
+
+ var buffer = obj.processOfferings["JTS:buffer"];
+ t.eq(buffer.processVersion, "1.0.0", "processVersion for buffer is 1.0.0");
+ t.eq(buffer.abstract, "Buffers a geometry using a certain distance", "Buffer abstract correctly read");
+ t.eq(buffer.title, "Buffers a geometry using a certain distance", "Buffer title correctly read");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WPSCapabilities/v1_0_0.js b/misc/openlayers/tests/Format/WPSCapabilities/v1_0_0.js
new file mode 100644
index 0000000..19d12f2
--- /dev/null
+++ b/misc/openlayers/tests/Format/WPSCapabilities/v1_0_0.js
@@ -0,0 +1,112 @@
+var doc = new OpenLayers.Format.XML().read(
+'<?xml version="1.0" encoding="UTF-8"?>' +
+'<wps:Capabilities xml:lang="en" service="WPS" version="1.0.0"' +
+' xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd"' +
+' xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1"' +
+' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink">' +
+' <ows:ServiceIdentification>' +
+' <ows:Title>Prototype GeoServer WPS</ows:Title>' +
+' <ows:Abstract/>' +
+' <ows:ServiceType>WPS</ows:ServiceType>' +
+' <ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion>' +
+' </ows:ServiceIdentification>' +
+' <ows:ServiceProvider>' +
+' <ows:ProviderName>The ancient geographes INC</ows:ProviderName>' +
+' <ows:ProviderSite xlink:href="http://geoserver.org"/>' +
+' <ows:ServiceContact/>' +
+' </ows:ServiceProvider>' +
+' <ows:OperationsMetadata>' +
+' <ows:Operation name="GetCapabilities">' +
+' <ows:DCP>' +
+' <ows:HTTP>' +
+' <ows:Get xlink:href="http://localhost:8080/geoserver/wps"/>' +
+' <ows:Post xlink:href="http://localhost:8080/geoserver/wps"/>' +
+' </ows:HTTP>' +
+' </ows:DCP>' +
+' </ows:Operation>' +
+' <ows:Operation name="DescribeProcess">' +
+' <ows:DCP>' +
+' <ows:HTTP>' +
+' <ows:Get xlink:href="http://localhost:8080/geoserver/wps"/>' +
+' <ows:Post xlink:href="http://localhost:8080/geoserver/wps"/>' +
+' </ows:HTTP>' +
+' </ows:DCP>' +
+' </ows:Operation>' +
+' <ows:Operation name="Execute">' +
+' <ows:DCP>' +
+' <ows:HTTP>' +
+' <ows:Get xlink:href="http://localhost:8080/geoserver/wps"/>' +
+' <ows:Post xlink:href="http://localhost:8080/geoserver/wps"/>' +
+' </ows:HTTP>' +
+' </ows:DCP>' +
+' </ows:Operation>' +
+' </ows:OperationsMetadata>' +
+' <wps:ProcessOfferings>' +
+' <wps:Process wps:processVersion="1.0.0">' +
+' <ows:Identifier>gt:Intersect</ows:Identifier>' +
+' <ows:Title>Intersection</ows:Title>' +
+' <ows:Abstract>Intersection between two literal geometry</ows:Abstract>' +
+' </wps:Process>' +
+' <wps:Process wps:processVersion="1.0.0">' +
+' <ows:Identifier>JTS:length</ows:Identifier>' +
+' <ows:Title>Returns the geometry perimeters, computed using cartesian geometry' +
+' expressions in the same unit of measure as the geometry (will not return a valid' +
+' perimeter for geometries expressed geographic coordinates</ows:Title>' +
+' <ows:Abstract>Returns the geometry perimeters, computed using cartesian geometry' +
+' expressions in the same unit of measure as the geometry (will not return a valid' +
+' perimeter for geometries expressed geographic coordinates</ows:Abstract>' +
+' </wps:Process>' +
+' <wps:Process wps:processVersion="1.0.0">' +
+' <ows:Identifier>JTS:isEmpty</ows:Identifier>' +
+' <ows:Title>Checks if the provided geometry is empty</ows:Title>' +
+' <ows:Abstract>Checks if the provided geometry is empty</ows:Abstract>' +
+' </wps:Process>' +
+' <wps:Process wps:processVersion="1.0.0">' +
+' <ows:Identifier>JTS:contains</ows:Identifier>' +
+' <ows:Title>Checks if a contains b</ows:Title>' +
+' <ows:Abstract>Checks if a contains b</ows:Abstract>' +
+' </wps:Process>' +
+' <wps:Process wps:processVersion="1.0.0">' +
+' <ows:Identifier>JTS:disjoint</ows:Identifier>' +
+' <ows:Title>Returns true if the two geometries have no points in common</ows:Title>' +
+' <ows:Abstract>Returns true if the two geometries have no points in common</ows:Abstract>' +
+' </wps:Process>' +
+' <wps:Process wps:processVersion="1.0.0">' +
+' <ows:Identifier>JTS:intersects</ows:Identifier>' +
+' <ows:Title>Returns true if the two geometries intersect, false otherwise</ows:Title>' +
+' <ows:Abstract>Returns true if the two geometries intersect, false' +
+' otherwise</ows:Abstract>' +
+' </wps:Process>' +
+' <wps:Process wps:processVersion="1.0.0">' +
+' <ows:Identifier>JTS:isClosed</ows:Identifier>' +
+' <ows:Title>Returns true if the line is closed</ows:Title>' +
+' <ows:Abstract>Returns true if the line is closed</ows:Abstract>' +
+' </wps:Process>' +
+' <wps:Process wps:processVersion="1.0.0">' +
+' <ows:Identifier>JTS:isValid</ows:Identifier>' +
+' <ows:Title>Returns true if the geometry is topologically valid, false' +
+' otherwise</ows:Title>' +
+' <ows:Abstract>Returns true if the geometry is topologically valid, false' +
+' otherwise</ows:Abstract>' +
+' </wps:Process>' +
+' <wps:Process wps:processVersion="1.0.0">' +
+' <ows:Identifier>JTS:buffer</ows:Identifier>' +
+' <ows:Title>Buffers a geometry using a certain distance</ows:Title>' +
+' <ows:Abstract>Buffers a geometry using a certain distance</ows:Abstract>' +
+' </wps:Process>' +
+' <wps:Process wps:processVersion="1.0.0">' +
+' <ows:Identifier>JTS:getY</ows:Identifier>' +
+' <ows:Title>Returns the Y ordinate of the point</ows:Title>' +
+' <ows:Abstract>Returns the Y ordinate of the point</ows:Abstract>' +
+' </wps:Process>' +
+' </wps:ProcessOfferings>' +
+' <wps:Languages>' +
+' <wps:Default>' +
+' <ows:Language>en-US</ows:Language>' +
+' </wps:Default>' +
+' <wps:Supported>' +
+' <ows:Language>en-US</ows:Language>' +
+' </wps:Supported>' +
+' </wps:Languages>' +
+'</wps:Capabilities>'
+);
diff --git a/misc/openlayers/tests/Format/WPSDescribeProcess.html b/misc/openlayers/tests/Format/WPSDescribeProcess.html
new file mode 100644
index 0000000..f52fd21
--- /dev/null
+++ b/misc/openlayers/tests/Format/WPSDescribeProcess.html
@@ -0,0 +1,206 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_read_WPSDescribeProcess(t) {
+ t.plan(17);
+
+ var parser = new OpenLayers.Format.WPSDescribeProcess();
+ var text =
+'<?xml version="1.0" encoding="UTF-8"?>' +
+'<wps:ProcessDescriptions xml:lang="en" service="WPS" version="1.0.0" xmlns:wps="http://www.opengis.net/wps/1.0.0"' +
+' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
+' xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd"' +
+' xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink">' +
+' <ProcessDescription wps:processVersion="1.0.0" statusSupported="false"' +
+' storeSupported="false">' +
+' <ows:Identifier>JTS:buffer</ows:Identifier>' +
+' <ows:Title>Buffers a geometry using a certain distance</ows:Title>' +
+' <ows:Abstract>Buffers a geometry using a certain distance</ows:Abstract>' +
+' <DataInputs>' +
+' <Input maxOccurs="1" minOccurs="1">' +
+' <ows:Identifier>geom</ows:Identifier>' +
+' <ows:Title>geom</ows:Title>' +
+' <ows:Abstract>The geometry to be buffered</ows:Abstract>' +
+' <ComplexData>' +
+' <Default>' +
+' <Format>' +
+' <MimeType>text/xml; subtype=gml/3.1.1</MimeType>' +
+' </Format>' +
+' </Default>' +
+' <Supported>' +
+' <Format>' +
+' <MimeType>text/xml; subtype=gml/3.1.1</MimeType>' +
+' </Format>' +
+' <Format>' +
+' <MimeType>text/xml; subtype=gml/2.1.2</MimeType>' +
+' </Format>' +
+' <Format>' +
+' <MimeType>application/wkt</MimeType>' +
+' </Format>' +
+' <Format>' +
+' <MimeType>application/gml-3.1.1</MimeType>' +
+' </Format>' +
+' <Format>' +
+' <MimeType>application/gml-2.1.2</MimeType>' +
+' </Format>' +
+' </Supported>' +
+' </ComplexData>' +
+' </Input>' +
+' <Input maxOccurs="1" minOccurs="1">' +
+' <ows:Identifier>distance</ows:Identifier>' +
+' <ows:Title>distance</ows:Title>' +
+' <ows:Abstract>The distance (same unit of measure as the geometry)</ows:Abstract>' +
+' <LiteralData>' +
+' <ows:DataType>xs:double</ows:DataType>' +
+' <ows:AnyValue/>' +
+' </LiteralData>' +
+' </Input>' +
+' <Input maxOccurs="1" minOccurs="0">' +
+' <ows:Identifier>quadrantSegments</ows:Identifier>' +
+' <ows:Title>quadrantSegments</ows:Title>' +
+' <ows:Abstract>Number of quadrant segments. Use &gt; 0 for round joins, 0 for' +
+' flat joins, &lt; 0 for mitred joins</ows:Abstract>' +
+' <LiteralData>' +
+' <ows:DataType>xs:int</ows:DataType>' +
+' <ows:AnyValue/>' +
+' </LiteralData>' +
+' </Input>' +
+' <Input maxOccurs="1" minOccurs="0">' +
+' <ows:Identifier>capStyle</ows:Identifier>' +
+' <ows:Title>capStyle</ows:Title>' +
+' <ows:Abstract>The buffer cap style, round, flat, square</ows:Abstract>' +
+' <LiteralData>' +
+' <ows:AllowedValues>' +
+' <ows:Value>Round</ows:Value>' +
+' <ows:Value>Flat</ows:Value>' +
+' <ows:Value>Square</ows:Value>' +
+' </ows:AllowedValues>' +
+' </LiteralData>' +
+' </Input>' +
+' </DataInputs>' +
+' <ProcessOutputs>' +
+' <Output>' +
+' <ows:Identifier>result</ows:Identifier>' +
+' <ows:Title>result</ows:Title>' +
+' <ComplexOutput>' +
+' <Default>' +
+' <Format>' +
+' <MimeType>text/xml; subtype=gml/3.1.1</MimeType>' +
+' </Format>' +
+' </Default>' +
+' <Supported>' +
+' <Format>' +
+' <MimeType>text/xml; subtype=gml/3.1.1</MimeType>' +
+' </Format>' +
+' <Format>' +
+' <MimeType>text/xml; subtype=gml/2.1.2</MimeType>' +
+' </Format>' +
+' <Format>' +
+' <MimeType>application/wkt</MimeType>' +
+' </Format>' +
+' <Format>' +
+' <MimeType>application/gml-3.1.1</MimeType>' +
+' </Format>' +
+' <Format>' +
+' <MimeType>application/gml-2.1.2</MimeType>' +
+' </Format>' +
+' </Supported>' +
+' </ComplexOutput>' +
+' </Output>' +
+' <Output>' +
+' <ows:Identifier>literal</ows:Identifier>' +
+' <ows:Title>literal output</ows:Title>' +
+' <LiteralOutput>' +
+' <ows:DataType ows:reference="http://www.w3.org/TR/xmlschema-2/#integer">integer</ows:DataType>'+
+' </LiteralOutput>' +
+' </Output>' +
+' </ProcessOutputs>' +
+' </ProcessDescription>' +
+'</wps:ProcessDescriptions>';
+
+ var res = parser.read(text);
+ var buffer = res.processDescriptions["JTS:buffer"];
+ t.eq(buffer.statusSupported, false, "statusSupported read correctly");
+ t.eq(buffer.storeSupported, false, "storeSupported read correctly");
+ t.eq(buffer.processVersion, "1.0.0", "processVersion read correctly");
+ var capStyle = buffer.dataInputs[3];
+ t.eq(capStyle.abstract, "The buffer cap style, round, flat, square", "capStyle abstract read correctly");
+ t.eq(capStyle.minOccurs, 0, "capStyle minOccurs read correctly");
+ t.eq(capStyle.maxOccurs, 1, "maxOccurs read correctly");
+ t.eq(capStyle.literalData.allowedValues["Flat"], true, "capStyle allowedValues read correctly");
+ var distance = buffer.dataInputs[1];
+ t.eq(distance.literalData.anyValue, true, "distance anyValue read correctly");
+ t.eq(distance.literalData.dataType, "xs:double", "distance dataType read correctly");
+ var geom = buffer.dataInputs[0];
+ t.eq(geom.complexData["default"].formats["text/xml; subtype=gml/3.1.1"], true, "geom complexData default read correctly");
+ t.eq(geom.complexData["supported"].formats["application/gml-2.1.2"], true, "geom complexData supported read correctly [1/2]");
+ t.eq(geom.complexData["supported"].formats["application/gml-3.1.1"], true, "geom complexData supported read correctly [2/2]");
+ var result = buffer.processOutputs[0];
+ t.eq(result.complexOutput["default"].formats["text/xml; subtype=gml/3.1.1"], true, "processOutputs default format read correctly");
+ t.eq(result.complexOutput["supported"].formats["text/xml; subtype=gml/3.1.1"], true, "processOutputs supported format read correctly [1/2]");
+ t.eq(result.complexOutput["supported"].formats["application/wkt"], true, "processOutputs supported format read correctly [1/2]");
+
+ var literalresult = buffer.processOutputs[1];
+ t.eq(literalresult.literalOutput.dataType, "integer", "processOutputs supported data type read corectly");
+
+ text = '<?xml version="1.0" encoding="UTF-8"?>' +
+'<wps:ProcessDescriptions service="WPS" version="1.0.0" xmlns:wps="http://www.opengis.net/wps/1.0.0"' +
+' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xml:lang="en"' +
+' xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd"' +
+' xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink">' +
+' <ProcessDescription wps:processVersion="1.0.0" statusSupported="false"' +
+' storeSupported="false">' +
+' <ows:Identifier>gt:VectorToRaster</ows:Identifier>' +
+' <ows:Title>Rasterize features</ows:Title>' +
+' <ows:Abstract>Rasterize all or selected features in a FeatureCollection</ows:Abstract>' +
+' <DataInputs>' +
+' <Input maxOccurs="1" minOccurs="0">' +
+' <ows:Identifier>bounds</ows:Identifier>' +
+' <ows:Title>Bounds</ows:Title>' +
+' <ows:Abstract>Bounds of the area to rasterize</ows:Abstract>' +
+' <BoundingBoxData>' +
+' <Default>' +
+' <CRS>EPSG:4326</CRS>' +
+' </Default>' +
+' <Supported>' +
+' <CRS>EPSG:4326</CRS>' +
+' </Supported>' +
+' </BoundingBoxData>' +
+' </Input>' +
+' </DataInputs>' +
+' <ProcessOutputs>' +
+' <Output>' +
+' <ows:Identifier>result</ows:Identifier>' +
+' <ows:Title>Result</ows:Title>' +
+' <ComplexOutput>' +
+' <Default>' +
+' <Format>' +
+' <MimeType>image/tiff</MimeType>' +
+' </Format>' +
+' </Default>' +
+' <Supported>' +
+' <Format>' +
+' <MimeType>image/tiff</MimeType>' +
+' </Format>' +
+' <Format>' +
+' <MimeType>application/arcgrid</MimeType>' +
+' </Format>' +
+' </Supported>' +
+' </ComplexOutput>' +
+' </Output>' +
+' </ProcessOutputs>' +
+' </ProcessDescription>' +
+'</wps:ProcessDescriptions>';
+
+ res = parser.read(text);
+ var vector2Raster = res.processDescriptions["gt:VectorToRaster"];
+ t.eq(vector2Raster.dataInputs[0].boundingBoxData["default"].CRSs["EPSG:4326"], true, "BoundingBoxData CRS parsed correctly");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/WPSExecute.html b/misc/openlayers/tests/Format/WPSExecute.html
new file mode 100644
index 0000000..e820800
--- /dev/null
+++ b/misc/openlayers/tests/Format/WPSExecute.html
@@ -0,0 +1,549 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_write_WPSExecute_WCS(t) {
+ t.plan(1);
+ var expected = '<?xml version="1.0" encoding="UTF-8"?>' +
+'<wps:Execute version="1.0.0" service="WPS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.opengis.net/wps/1.0.0" xmlns:wfs="http://www.opengis.net/wfs" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:gml="http://www.opengis.net/gml" xmlns:ogc="http://www.opengis.net/ogc" xmlns:wcs="http://www.opengis.net/wcs" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd">' +
+' <ows:Identifier>gs:GeorectifyCoverage</ows:Identifier>' +
+' <wps:DataInputs>' +
+' <wps:Input>' +
+' <ows:Identifier>data</ows:Identifier>' +
+' <wps:Reference mimeType="image/tiff" xlink:href="http://geoserver/wcs" method="POST">' +
+' <wps:Body>' +
+' <wcs:GetCoverage service="WCS" version="1.1.2">' +
+' <ows:Identifier>topp:asbuilt</ows:Identifier>' +
+' <wcs:DomainSubset>' +
+' <ows:BoundingBox crs="http://www.opengis.net/gml/srs/epsg.xml#404000">' +
+' <ows:LowerCorner>0 -7070</ows:LowerCorner>' +
+' <ows:UpperCorner>10647 1</ows:UpperCorner>' +
+' </ows:BoundingBox>' +
+' </wcs:DomainSubset>' +
+' <wcs:Output format="image/tiff"/>' +
+' </wcs:GetCoverage>' +
+' </wps:Body>' +
+' </wps:Reference>' +
+' </wps:Input>' +
+' <wps:Input>' +
+' <ows:Identifier>gcp</ows:Identifier>' +
+' <wps:Data>' +
+' <wps:LiteralData>[[[2721, 3263], [-122.472109, 37.73106003]], [[4163, 3285], [-122.4693417, 37.729929851]], [[5773, 4046], [-122.466702461, 37.7271906]], [[8885, 4187], [-122.462333, 37.725167]]]</wps:LiteralData>' +
+' </wps:Data>' +
+' </wps:Input>' +
+' <wps:Input>' +
+' <ows:Identifier>targetCRS</ows:Identifier>' +
+' <wps:Data>' +
+' <wps:LiteralData>EPSG:4326</wps:LiteralData>' +
+' </wps:Data>' +
+' </wps:Input>' +
+' <wps:Input>' +
+' <ows:Identifier>transparent</ows:Identifier>' +
+' <wps:Data>' +
+' <wps:LiteralData>true</wps:LiteralData>' +
+' </wps:Data>' +
+' </wps:Input>' +
+' </wps:DataInputs>' +
+' <wps:ResponseForm>' +
+' <wps:RawDataOutput mimeType="image/tiff">' +
+' <ows:Identifier>result</ows:Identifier>' +
+' </wps:RawDataOutput>' +
+' </wps:ResponseForm>' +
+'</wps:Execute>';
+
+ var format = new OpenLayers.Format.WPSExecute();
+ var result = format.write({
+ identifier: "gs:GeorectifyCoverage",
+ dataInputs: [{
+ identifier: 'data',
+ reference: {
+ mimeType: "image/tiff",
+ href: "http://geoserver/wcs",
+ method: "POST",
+ body: {
+ wcs: {
+ identifier: 'topp:asbuilt',
+ version: '1.1.2',
+ domainSubset: {
+ boundingBox: {
+ projection: 'http://www.opengis.net/gml/srs/epsg.xml#404000',
+ bounds: new OpenLayers.Bounds(0.0, -7070.0, 10647.0, 1.0)
+ }
+ },
+ output: {format: 'image/tiff'}
+ }
+ }
+ }
+ }, {
+ identifier: 'gcp',
+ data: {
+ literalData: {
+ value: '[[[2721, 3263], [-122.472109, 37.73106003]], [[4163, 3285], [-122.4693417, 37.729929851]], [[5773, 4046], [-122.466702461, 37.7271906]], [[8885, 4187], [-122.462333, 37.725167]]]'
+ }
+ }
+ }, {
+ identifier: 'targetCRS',
+ data: {
+ literalData: {
+ value: 'EPSG:4326'
+ }
+ }
+ }, {
+ identifier: 'transparent',
+ data: {
+ literalData: {
+ value: 'true'
+ }
+ }
+ }],
+ responseForm: {
+ rawDataOutput: {
+ mimeType: "image/tiff",
+ identifier: "result"
+ }
+ }
+ });
+ t.xml_eq(result, expected, "WPS Execute with embedded WCS GetCoverage written out correctly");
+
+ }
+
+ function test_write_WPSExecute(t) {
+ t.plan(1);
+ var expected = '<?xml version="1.0" encoding="UTF-8"?>' +
+'<wps:Execute version="1.0.0" service="WPS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
+' xmlns="http://www.opengis.net/wps/1.0.0" xmlns:wfs="http://www.opengis.net/wfs"' +
+' xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1"' +
+' xmlns:gml="http://www.opengis.net/gml" xmlns:ogc="http://www.opengis.net/ogc"' +
+' xmlns:wcs="http://www.opengis.net/wcs/1.1.1" xmlns:xlink="http://www.w3.org/1999/xlink"' +
+' xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd">' +
+' <ows:Identifier>JTS:area</ows:Identifier>' +
+' <wps:DataInputs>' +
+' <wps:Input>' +
+' <ows:Identifier>geom</ows:Identifier>' +
+' <wps:Reference mimeType="text/xml; subtype=gml/3.1.1" xlink:href="http://geoserver/wps"' +
+' method="POST">' +
+' <wps:Body>' +
+' <wps:Execute service="WPS" version="1.0.0">' +
+' <ows:Identifier>gs:CollectGeometries</ows:Identifier>' +
+' <wps:DataInputs>' +
+' <wps:Input>' +
+' <ows:Identifier>features</ows:Identifier>' +
+' <wps:Reference mimeType="text/xml; subtype=wfs-collection/1.0"' +
+' xlink:href="http://geoserver/wfs" method="POST">' +
+' <wps:Body>' +
+' <wfs:GetFeature service="WFS" version="1.0.0"' +
+' outputFormat="GML2">' +
+' <wfs:Query typeName="sf:archsites"/>' +
+' </wfs:GetFeature>' +
+' </wps:Body>' +
+' </wps:Reference>' +
+' </wps:Input>' +
+' </wps:DataInputs>' +
+' <wps:ResponseForm>' +
+' <wps:RawDataOutput mimeType="text/xml; subtype=gml/3.1.1">' +
+' <ows:Identifier>result</ows:Identifier>' +
+' </wps:RawDataOutput>' +
+' </wps:ResponseForm>' +
+' </wps:Execute>' +
+' </wps:Body>' +
+' </wps:Reference>' +
+' </wps:Input>' +
+' </wps:DataInputs>' +
+' <wps:ResponseForm>' +
+' <wps:RawDataOutput>' +
+' <ows:Identifier>result</ows:Identifier>' +
+' </wps:RawDataOutput>' +
+' </wps:ResponseForm>' +
+'</wps:Execute>';
+
+ var format = new OpenLayers.Format.WPSExecute();
+ var result = format.write({
+ identifier: "JTS:area",
+ dataInputs: [{
+ identifier: 'geom',
+ reference: {
+ mimeType: "text/xml; subtype=gml/3.1.1",
+ href: "http://geoserver/wps",
+ method: "POST",
+ body: {
+ identifier: "gs:CollectGeometries",
+ dataInputs: [{
+ identifier: 'features',
+ reference: {
+ mimeType: "text/xml; subtype=wfs-collection/1.0",
+ href: "http://geoserver/wfs",
+ method: "POST",
+ body: {
+ wfs: {
+ version: "1.0.0",
+ outputFormat: "GML2",
+ featureType: "sf:archsites"
+ }
+ }
+ }
+ }],
+ responseForm: {
+ rawDataOutput: {
+ mimeType: "text/xml; subtype=gml/3.1.1",
+ identifier: "result"
+ }
+ }
+ }
+ }
+ }],
+ responseForm: {
+ rawDataOutput: {
+ identifier: "result"
+ }
+ }
+ });
+ t.xml_eq(result, expected, "WPS Execute written out correctly");
+ }
+
+ function test_write_raw_data_output(t) {
+ t.plan(1);
+ // example request taken from: http://geoprocessing.info/wpsdoc/1x0ExecutePOST
+ var expected = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
+'<wps:Execute service="WPS" version="1.0.0" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1" ' +
+'xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd">' +
+' <ows:Identifier>Buffer</ows:Identifier>' +
+' <wps:DataInputs>' +
+' <wps:Input>' +
+' <ows:Identifier>InputPolygon</ows:Identifier>' +
+' <ows:Title>Playground area</ows:Title>' +
+' <wps:Reference xlink:href="http://foo.bar/some_WFS_request.xml"/>' +
+' </wps:Input>' +
+' <wps:Input>' +
+' <ows:Identifier>BufferDistance</ows:Identifier>' +
+' <ows:Title>Distance which people will walk to get to a playground.</ows:Title>' +
+' <wps:Data>' +
+' <wps:LiteralData>400</wps:LiteralData>' +
+' </wps:Data>' +
+' </wps:Input>' +
+' </wps:DataInputs>' +
+' <wps:ResponseForm>' +
+' <wps:RawDataOutput>' +
+' <ows:Identifier>BufferedPolygon</ows:Identifier>' +
+' </wps:RawDataOutput>' +
+' </wps:ResponseForm>' +
+'</wps:Execute>';
+
+ var format = new OpenLayers.Format.WPSExecute();
+ var result = format.write({
+ identifier: "Buffer",
+ dataInputs: [{
+ identifier: 'InputPolygon',
+ title: 'Playground area',
+ reference: {
+ href: 'http://foo.bar/some_WFS_request.xml'
+ }
+ }, {
+ identifier: 'BufferDistance',
+ title: 'Distance which people will walk to get to a playground.',
+ data: {
+ literalData: {
+ value: 400
+ }
+ }
+ }],
+ responseForm: {
+ rawDataOutput: {
+ identifier: "BufferedPolygon"
+ }
+ }
+ });
+ t.xml_eq(result, expected, "WPS Execute written out correctly");
+ }
+
+ function test_write_request_responseDoc_defaultFormat(t) {
+ t.plan(1);
+ // taken from http://geoprocessing.info/schemas/wps/1.0/examples/51_wpsExecute_request_ResponseDocument.xml
+ var expected = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
+'<wps:Execute service="WPS" version="1.0.0" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1" ' +
+'xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd">' +
+' <ows:Identifier>Buffer</ows:Identifier>' +
+' <wps:DataInputs>' +
+' <wps:Input>' +
+' <ows:Identifier>InputPolygon</ows:Identifier>' +
+' <ows:Title>Playground area</ows:Title>' +
+' <wps:Reference xlink:href="http://foo.bar/some_WFS_request.xml"/>' +
+' </wps:Input>' +
+' <wps:Input>' +
+' <ows:Identifier>BufferDistance</ows:Identifier>' +
+' <ows:Title>Distance which people will walk to get to a playground.</ows:Title>' +
+' <wps:Data>' +
+' <wps:LiteralData>400</wps:LiteralData>' +
+' </wps:Data>' +
+' </wps:Input>' +
+' </wps:DataInputs>' +
+' <wps:ResponseForm>' +
+' <wps:ResponseDocument storeExecuteResponse="true">' +
+' <wps:Output asReference="true">' +
+' <ows:Identifier>BufferedPolygon</ows:Identifier>' +
+' <ows:Title>Area serviced by playground.</ows:Title>' +
+' <ows:Abstract>Area within which most users of this playground will live.</ows:Abstract>' +
+' </wps:Output>' +
+' </wps:ResponseDocument>' +
+' </wps:ResponseForm>' +
+'</wps:Execute>';
+
+ var format = new OpenLayers.Format.WPSExecute();
+ var result = format.write({
+ identifier: "Buffer",
+ dataInputs: [{
+ identifier: 'InputPolygon',
+ title: 'Playground area',
+ reference: {
+ href: 'http://foo.bar/some_WFS_request.xml'
+ }
+ }, {
+ identifier: 'BufferDistance',
+ title: 'Distance which people will walk to get to a playground.',
+ data: {
+ literalData: {
+ value: 400
+ }
+ }
+ }],
+ responseForm: {
+ responseDocument: {
+ storeExecuteResponse: true,
+ outputs: [{
+ asReference: true,
+ identifier: 'BufferedPolygon',
+ title: 'Area serviced by playground.',
+ 'abstract': 'Area within which most users of this playground will live.'
+ }]
+ }
+ }
+ });
+ t.xml_eq(result, expected, "WPS Execute written out correctly");
+ }
+
+ function test_write_request_responseDoc_specifiedFormat(t) {
+ t.plan(1);
+ // taken from http://geoprocessing.info/schemas/wps/1.0/examples/52_wpsExecute_request_ResponseDocument.xml
+ var expected = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
+'<wps:Execute service="WPS" version="1.0.0" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1" ' +
+'xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd">' +
+' <ows:Identifier>Buffer</ows:Identifier>' +
+' <wps:DataInputs>' +
+' <wps:Input>' +
+' <ows:Identifier>InputPolygon</ows:Identifier>' +
+' <ows:Title>Playground area</ows:Title>' +
+' <wps:Reference xlink:href="http://foo.bar/some_WFS_request.xml" method="POST" mimeType="text/xml" encoding="UTF-8" schema="http://foo.bar/gml_polygon_schema.xsd"/>' +
+' </wps:Input>' +
+' <wps:Input>' +
+' <ows:Identifier>BufferDistance</ows:Identifier>' +
+' <ows:Title>Distance which people will walk to get to a playground.</ows:Title>' +
+' <wps:Data>' +
+' <wps:LiteralData uom="feet">400</wps:LiteralData>' +
+' </wps:Data>' +
+' </wps:Input>' +
+' </wps:DataInputs>' +
+' <wps:ResponseForm>' +
+' <wps:ResponseDocument storeExecuteResponse="true" lineage="true" status="true">' +
+' <wps:Output asReference="true">' +
+' <ows:Identifier>BufferedPolygon</ows:Identifier>' +
+' <ows:Title>Area serviced by playground.</ows:Title>' +
+' <ows:Abstract>Area within which most users of this playground will live.</ows:Abstract>' +
+' </wps:Output>' +
+' <wps:Output>' +
+' <ows:Identifier>literal</ows:Identifier>' +
+' <ows:Title/>' +
+' <ows:Abstract/>' +
+' </wps:Output>' +
+' </wps:ResponseDocument>' +
+' </wps:ResponseForm>' +
+'</wps:Execute>';
+
+ var format = new OpenLayers.Format.WPSExecute();
+ var result = format.write({
+ identifier: "Buffer",
+ dataInputs: [{
+ identifier: 'InputPolygon',
+ title: 'Playground area',
+ reference: {
+ href: 'http://foo.bar/some_WFS_request.xml',
+ method: "POST",
+ mimeType: "text/xml",
+ encoding: "UTF-8",
+ schema: "http://foo.bar/gml_polygon_schema.xsd"
+ }
+ }, {
+ identifier: 'BufferDistance',
+ title: 'Distance which people will walk to get to a playground.',
+ data: {
+ literalData: {
+ value: 400,
+ uom: 'feet'
+ }
+ }
+ }],
+ responseForm: {
+ responseDocument: {
+ storeExecuteResponse: true,
+ lineage: true,
+ status: true,
+ outputs: [
+ {
+ asReference: true,
+ identifier: 'BufferedPolygon',
+ title: 'Area serviced by playground.',
+ 'abstract': 'Area within which most users of this playground will live.'
+ },
+ {
+ identifier: 'literal'
+ }
+ ]
+ }
+ }
+ });
+ t.xml_eq(result, expected, "WPS Execute written out correctly");
+ }
+
+ function test_write_request_complexData(t) {
+ t.plan(1);
+ // taken from http://geoprocessing.info/schemas/wps/1.0/examples/51_wpsExecute_request_ResponseDocument.xml
+ var expected = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
+ '<wps:Execute service="WPS" version="1.0.0" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1" ' +
+ 'xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd">' +
+ ' <ows:Identifier>Buffer</ows:Identifier>' +
+ ' <wps:DataInputs>' +
+ ' <wps:Input>' +
+ ' <ows:Identifier>InputPolygon</ows:Identifier>' +
+ ' <ows:Title>Playground area</ows:Title>' +
+ ' <wps:Reference xlink:href="http://foo.bar/some_WFS_request.xml"/>' +
+ ' </wps:Input>' +
+ ' <wps:Input>' +
+ ' <ows:Identifier>ResultPage</ows:Identifier>' +
+ ' <ows:Title>Nicely formatted HTML of the result</ows:Title>' +
+ ' <wps:Data>' +
+ ' <wps:ComplexData><![CDATA[<html><head></head><body></body></head>]]></wps:ComplexData>' +
+ ' </wps:Data>' +
+ ' </wps:Input>' +
+ ' <wps:Input>' +
+ ' <ows:Identifier>GMLPoint</ows:Identifier>' +
+ ' <ows:Title>Point as GML</ows:Title>' +
+ ' <wps:Data>' +
+ ' <wps:ComplexData><feature:geometry xmlns:feature="http://www.opengis.net/gml"><feature:Point><feature:pos>10 10</feature:pos></feature:Point></feature:geometry></wps:ComplexData>' +
+ ' </wps:Data>' +
+ ' </wps:Input>' +
+ ' </wps:DataInputs>' +
+ ' <wps:ResponseForm>' +
+ ' <wps:ResponseDocument storeExecuteResponse="true">' +
+ ' <wps:Output asReference="true">' +
+ ' <ows:Identifier>BufferedPolygon</ows:Identifier>' +
+ ' <ows:Title>Area serviced by playground.</ows:Title>' +
+ ' <ows:Abstract>Area within which most users of this playground will live.</ows:Abstract>' +
+ ' </wps:Output>' +
+ ' </wps:ResponseDocument>' +
+ ' </wps:ResponseForm>' +
+ '</wps:Execute>';
+
+ var format = new OpenLayers.Format.WPSExecute();
+ var result = format.write({
+ identifier: "Buffer",
+ dataInputs: [{
+ identifier: 'InputPolygon',
+ title: 'Playground area',
+ reference: {
+ href: 'http://foo.bar/some_WFS_request.xml'
+ }
+ }, {
+ identifier: 'ResultPage',
+ title: 'Nicely formatted HTML of the result',
+ data: {
+ complexData: {
+ value: "<html><head></head><body></body></head>"
+ }
+ }
+ }, {
+ identifier: "GMLPoint",
+ title: "Point as GML",
+ data: {
+ complexData: {
+ value: OpenLayers.Format.GML.v3.prototype.writers.feature["_geometry"].apply(new OpenLayers.Format.GML.v3({curve: true, surface: true}), [new OpenLayers.Geometry.Point(10, 10)])
+ }
+ }
+ }],
+ responseForm: {
+ responseDocument: {
+ storeExecuteResponse: true,
+ outputs: [{
+ asReference: true,
+ identifier: 'BufferedPolygon',
+ title: 'Area serviced by playground.',
+ 'abstract': 'Area within which most users of this playground will live.'
+ }]
+ }
+ }
+ });
+ t.xml_eq(result, expected, "WPS Execute written out correctly");
+ }
+
+ function test_write_WPSExecuteFID(t) {
+ t.plan(1);
+
+ var result,
+ expected,
+ format = ({geometryName: 'the_geom'});
+
+ expected = '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<wps:Execute xmlns:wps="http://www.opengis.net/wps/1.0.0" version="1.0.0" service="WPS" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' +
+ ' <ows:Identifier xmlns:ows="http://www.opengis.net/ows/1.1">gs:Bounds</ows:Identifier>' +
+ ' <wps:DataInputs>' +
+ ' <wps:Input>' +
+ ' <ows:Identifier xmlns:ows="http://www.opengis.net/ows/1.1">features</ows:Identifier>' +
+ ' <wps:Reference mimeType="text/xml" xlink:href="http://geoserver/wfs" xmlns:xlink="http://www.w3.org/1999/xlink" method="POST">' +
+ ' <wps:Body>' +
+ ' <wfs:GetFeature xmlns:wfs="http://www.opengis.net/wfs" service="WFS" version="1.0.0">' +
+ ' <wfs:Query typeName="foo:bar">' +
+ ' <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+ ' <ogc:FeatureId fid="123"/>' +
+ ' </ogc:Filter>' +
+ ' </wfs:Query>' +
+ ' </wfs:GetFeature>' +
+ ' </wps:Body>' +
+ ' </wps:Reference>' +
+ ' </wps:Input>' +
+ ' </wps:DataInputs>' +
+ ' <wps:ResponseForm>' +
+ ' <wps:RawDataOutput>' +
+ ' <ows:Identifier xmlns:ows="http://www.opengis.net/ows/1.1">bounds</ows:Identifier>' +
+ ' </wps:RawDataOutput>' +
+ ' </wps:ResponseForm>' +
+ '</wps:Execute>';
+
+ result = new OpenLayers.Format.WPSExecute().write({
+ identifier: 'gs:Bounds',
+ dataInputs: [{
+ identifier: 'features',
+ reference: {
+ mimeType: 'text/xml',
+ href: 'http://geoserver/wfs',
+ method: 'POST',
+ body: {
+ wfs: {
+ featureType: 'foo:bar',
+ version: '1.0.0',
+ filter: new OpenLayers.Filter.FeatureId({fids: [123]})
+ }
+ }
+ }
+ }],
+ responseForm: {
+ rawDataOutput: {
+ identifier: 'bounds'
+ }
+ }
+ });
+ t.xml_eq(result, expected, 'WPS Execute written out correctly with a FID filter');
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/XLS/v1_1_0.html b/misc/openlayers/tests/Format/XLS/v1_1_0.html
new file mode 100644
index 0000000..8a744f9
--- /dev/null
+++ b/misc/openlayers/tests/Format/XLS/v1_1_0.html
@@ -0,0 +1,98 @@
+<html>
+<head>
+ <script src="../../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ function test_read(t) {
+ t.plan(16);
+ var response = '<xls:GeocodeResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/xls http://schemas.opengis.net/ols/1.1.0/LocationUtilityService.xsd" xmlns:xls="http://www.opengis.net/xls" xmlns:gml="http://www.opengis.net/gml"><xls:GeocodeResponseList numberOfGeocodedAddresses="1"><xls:GeocodedAddress><gml:Point srsName="EPSG:28992"><gml:pos dimension="2">122650 483904</gml:pos></gml:Point><xls:Address countryCode="NL"><xls:StreetAddress><xls:Building number="1"/><xls:Street>president kennedylaan</xls:Street></xls:StreetAddress><xls:Place type="MunicipalitySubdivision">amsterdam</xls:Place><xls:Place type="Municipality">amsterdam</xls:Place><xls:Place type="CountrySubdivision">noord holland</xls:Place><xls:PostalCode>1079MB</xls:PostalCode></xls:Address></xls:GeocodedAddress></xls:GeocodeResponseList></xls:GeocodeResponse>';
+ var format = new OpenLayers.Format.XLS();
+ var output = format.read(response);
+ t.eq(output.responseLists.length, 1, "Output contains 1 responseList");
+ var responseList = output.responseLists[0];
+ t.eq(responseList.numberOfGeocodedAddresses, 1, "Responselist contains 1 geocoded address");
+ t.eq(responseList.features.length, 1, "1 feature parsed");
+ var feature = responseList.features[0];
+ var address = feature.attributes.address;
+ t.eq(address.building["number"], "1", "Building number correctly parsed");
+ t.eq(address.countryCode, "NL", "Country code correctly parsed");
+ t.eq(address.place.CountrySubdivision, "noord holland", "CountrySubDivision correctly parsed");
+ t.eq(address.place.Municipality, "amsterdam", "Municipality correctly parsed");
+ t.eq(address.place.MunicipalitySubdivision, "amsterdam", "MunicipalitySubdivision correctly parsed");
+ t.eq(address.postalCode, "1079MB", "Postalcode correctly parsed");
+ t.eq(address.street[0], "president kennedylaan", "Street correctly parsed");
+ t.eq(feature.geometry.x, 122650, "Geometry [x] correctly parsed");
+ t.eq(feature.geometry.y, 483904, "Geometry [y] correctly parsed");
+
+ var responseList = [];
+ responseList.push('<?xml version="1.0" encoding="UTF-8" ?>',
+'<XLS xmlns="http://www.opengis.net/xls" xmlns:gml="http://www.opengis.net/gml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/xls LocationUtilityService.xsd" version="1.1">',
+' <ResponseHeader/>',
+' <Response version="1.1" requestID="">',
+' <GeocodeResponse>',
+' <GeocodeResponseList numberOfGeocodedAddresses="2">',
+' <GeocodedAddress>',
+' <gml:Point>',
+' <gml:pos>-71.4589837781615 41.8317239069808</gml:pos>',
+' </gml:Point>',
+' <Address countryCode="">',
+' <StreetAddress>',
+' <Street></Street>',
+' <Street/>',
+' </StreetAddress>',
+' <Place type="Municipality"></Place>',
+' <Place type="CountrySubdivision"></Place>',
+' <PostalCode></PostalCode>',
+' </Address>',
+' <GeocodeMatchCode accuracy="100.0"/>',
+' </GeocodedAddress>',
+' <GeocodedAddress>',
+' <gml:Point>',
+' <gml:pos>-71.4087296631643 41.8269575002255</gml:pos>',
+' </gml:Point>',
+' <Address countryCode="">',
+' <StreetAddress>',
+' <Street></Street>',
+' <Street/>',
+' </StreetAddress>',
+' <Place type="Municipality"></Place>',
+' <Place type="CountrySubdivision"></Place>',
+' <PostalCode></PostalCode>',
+' </Address>',
+' <GeocodeMatchCode accuracy="100.0"/>',
+' </GeocodedAddress>',
+' </GeocodeResponseList>',
+' </GeocodeResponse>',
+' </Response>',
+'</XLS>');
+ response = responseList.join("");
+ output = format.read(response);
+ t.eq(output.version, "1.1", "Version correctly parsed");
+ var responseList = output.responseLists[0];
+ t.eq(responseList.numberOfGeocodedAddresses, 2, "2 addresses parsed");
+ t.eq(responseList.features.length, 2, "2 features parsed");
+ t.eq(responseList.features[0].attributes.matchCode.accuracy, 100.0, "Accuracy correctly parsed");
+ }
+
+ function test_write(t) {
+ t.plan(1);
+
+ var format = new OpenLayers.Format.XLS();
+ var address = {
+ countryCode: 'US',
+ street: '1 Freedom Rd',
+ municipality: 'Providence',
+ countrySubdivision: 'RI',
+ postalCode: '02909'
+ };
+ var request = format.write({addresses: [address]});
+
+ var expected = '<xls:XLS xmlns:xls="http://www.opengis.net/xls" version="1.1" xsi:schemaLocation="http://www.opengis.net/xls http://schemas.opengis.net/ols/1.1.0/LocationUtilityService.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><xls:RequestHeader/><xls:Request methodName="GeocodeRequest" requestID="" version="1.1"><xls:GeocodeRequest><xls:Address countryCode="US"><xls:StreetAddress><xls:Street>1 Freedom Rd</xls:Street></xls:StreetAddress><xls:Place type="Municipality">Providence</xls:Place><xls:Place type="CountrySubdivision">RI</xls:Place><xls:PostalCode>02909</xls:PostalCode></xls:Address></xls:GeocodeRequest></xls:Request></xls:XLS>';
+
+ t.xml_eq(request, expected, "XLS geocode request correctly written");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/XML.html b/misc/openlayers/tests/Format/XML.html
new file mode 100644
index 0000000..d139506
--- /dev/null
+++ b/misc/openlayers/tests/Format/XML.html
@@ -0,0 +1,900 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var text =
+ '<?xml version="1.0"?>' +
+ '<ol:root xmlns="http://namespace.default.net" ' +
+ 'xmlns:ol="http://namespace.openlayers.org" ' +
+ 'xmlns:ta="http://namespace.testattribute.net">' +
+ '<ol:child ta:attribute="value1" ' +
+ 'attribute="value2">' +
+ 'junk1' +
+ '<' + '/ol:child>' +
+ '<ol:child>junk2<' + '/ol:child>' +
+ '<ol:child>junk3<' + '/ol:child>' +
+ '<element>junk4<' + '/element>' +
+ '<ol:element>junk5<' + '/ol:element>' +
+ '<ol:p>' +
+ '<ol:a>junk' +
+ '<' + '/ol:a>' +
+ '<ol:b>junk' +
+ '<' + '/ol:b>' +
+ '<ol:a>junk' +
+ '<' + '/ol:a>' +
+ '<' + '/ol:p>' +
+ '<' + '/ol:root>';
+
+ function test_Format_XML_constructor(t) {
+ t.plan(13);
+
+ var options = {'foo': 'bar'};
+ var format = new OpenLayers.Format.XML(options);
+ t.ok(format instanceof OpenLayers.Format.XML,
+ "new OpenLayers.Format.XML returns object" );
+ t.eq(format.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a write function");
+
+ t.ok(!window.ActiveXObject || format.xmldom, "browsers with activeX must have xmldom");
+
+ // test namespaces
+ t.ok(format.namespaces instanceof Object, "format has namespace object");
+ var namespaces = {"foo": "bar"};
+ format = new OpenLayers.Format.XML({namespaces: namespaces});
+ t.eq(format.namespaces, namespaces, "format.namespaces correctly set in constructor");
+
+ // test default prefix
+ t.eq(format.defaultPrefix, null, "defaultPrefix is null by default");
+ format = new OpenLayers.Format.XML({defaultPrefix: "foo"});
+ t.eq(format.defaultPrefix, "foo", "defaultPrefix correctly set in constructor");
+
+ // test readers
+ t.ok(format.readers instanceof Object, "format has readers object");
+ var readers = {"foo": "bar"};
+ format = new OpenLayers.Format.XML({readers: readers});
+ t.eq(format.readers, readers, "format.readers correctly set in constructor");
+
+ // test readers
+ t.ok(format.writers instanceof Object, "format has writers object");
+ var writers = {"foo": "bar"};
+ format = new OpenLayers.Format.XML({writers: writers});
+ t.eq(format.writers, writers, "format.writers correctly set in constructor");
+ }
+
+ function test_destroy(t) {
+ t.plan(1);
+ var format = new OpenLayers.Format.XML();
+ format.destroy();
+ t.eq(format.xmldom, null, "xmldom set to null for all browsers");
+ }
+
+ function test_Format_XML_read(t) {
+
+ var format = new OpenLayers.Format.XML();
+ t.plan(format.xmldom ? 10 : 9);
+
+ var doc = format.read(text);
+ t.eq(doc.nodeType, 9,
+ "doc has the correct node type");
+ t.eq(doc.nodeName, "#document",
+ "doc has the correct node name");
+ t.ok(doc.documentElement,
+ "ok to access doc.documentElement");
+ t.xml_eq(doc.documentElement, text,
+ "doc.documentElement correctly read");
+
+ // read can also be called on the prototype directly
+ doc = OpenLayers.Format.XML.prototype.read(text);
+ t.eq(doc.nodeType, 9,
+ "doc has the correct node type");
+ t.eq(doc.nodeName, "#document",
+ "doc has the correct node name");
+ t.ok(doc.documentElement,
+ "ok to access doc.documentElement");
+ t.xml_eq(doc.documentElement, text,
+ "doc.documentElement correctly read");
+
+ // where appropriate, make sure doc is loaded into xmldom property
+ if(format.xmldom) {
+ t.xml_eq(format.xmldom.documentElement, text,
+ "xmldom.documentElement contains equivalent xml");
+ }
+
+ // test equivalence with different namespace alias
+ var pre1 =
+ "<pre1:parent xmlns:pre1='http://namespace'>" +
+ "<pre1:child1>value2</pre1:child1>" +
+ "<pre1:child2 pre1:attr1='foo'>value2</pre1:child2>" +
+ "<pre1:child3 chicken:attr='hot' xmlns:chicken='http://soup'/>" +
+ "</pre1:parent>";
+ var pre2 =
+ "<pre2:parent xmlns:pre2='http://namespace'>" +
+ "<pre2:child1>value2</pre2:child1>" +
+ "<pre2:child2 pre2:attr1='foo'>value2</pre2:child2>" +
+ "<pre2:child3 pea:attr='hot' xmlns:pea='http://soup'/>" +
+ "</pre2:parent>";
+ var doc1 = format.read(pre1);
+ t.xml_eq(doc1.documentElement, pre2, "read correctly sets namespaces");
+
+ }
+
+ function test_Format_XML_write(t) {
+ t.plan(2);
+
+ var format = new OpenLayers.Format.XML();
+ var doc = format.read(text);
+ var out = format.write(doc);
+ out = out.replace(/[\r\n]/g, '');
+ out = out.replace( /<\?.*\?>/, '')
+ var expected = text.replace(/<\?.*\?>/, '')
+ t.eq(expected, out,
+ "correctly writes an XML DOM doc");
+ var out = format.write(
+ format.getElementsByTagNameNS(doc,
+ "http://namespace.openlayers.org","root")[0]);
+ out = out.replace(/[\r\n]/g, '');
+ out = out.replace( /<\?.*\?>/, '')
+ t.eq(out, expected,
+ "correctly writes an XML DOM node");
+ }
+
+ function test_Format_XML_createElementNS(t) {
+ t.plan(5);
+
+ var format = new OpenLayers.Format.XML();
+ var uri = "http://foo.com";
+ var prefix = "foo";
+ var localName = "bar";
+ var qualifiedName = prefix + ":" + localName;
+ var node = format.createElementNS(uri, qualifiedName);
+ t.eq(node.nodeType, 1,
+ "node has correct type");
+ t.eq(node.nodeName, qualifiedName,
+ "node has correct qualified name");
+ t.eq(node.prefix, prefix,
+ "node has correct prefix");
+ t.eq(node.namespaceURI, uri,
+ "node has correct namespace uri");
+
+ var doc = format.read(text);
+ if (doc.importNode) {
+ node = doc.importNode(node, true);
+ }
+ t.ok(doc.documentElement.appendChild(node),
+ "node can be appended to a doc root");
+ }
+
+ function test_createDocumentFragment(t) {
+ t.plan(3);
+
+ var format = new OpenLayers.Format.XML();
+ var uri = "http://foo.com";
+ var prefix = "foo";
+ var localName = "bar";
+ var qualifiedName = prefix + ":" + localName;
+ var parent = format.createElementNS(uri, qualifiedName);
+
+ var fragment = format.createDocumentFragment();
+ t.eq(fragment.nodeType, 11, "fragment type");
+
+ try {
+ fragment.appendChild(format.createTextNode("one"));
+ fragment.appendChild(format.createTextNode("two"));
+ t.eq(fragment.childNodes.length, 2, "fragment has two child nodes");
+ } catch (err) {
+ t.fail("trouble appending text nodes to fragment: " + err.message);
+ }
+
+ try {
+ parent.appendChild(fragment);
+ t.eq(parent.childNodes.length, 2, "parent has two child nodes");
+ } catch (err) {
+ t.fail("trouble appending fragment to parent: " + err.message);
+ }
+ }
+
+ function test_Format_XML_createTextNode(t) {
+ t.plan(10);
+
+ var format = new OpenLayers.Format.XML();
+ var value, node;
+
+ value = "string";
+ node = format.createTextNode(value);
+ t.eq(node.nodeType, 3,
+ "[string] node has correct type");
+ t.eq(node.nodeName, "#text",
+ "[string] node has correct name");
+ t.eq(node.nodeValue, "string",
+ "[string] node has correct value");
+
+ value = 0.42;
+ node = format.createTextNode(value);
+ t.eq(node.nodeType, 3,
+ "[number] node has correct type");
+ t.eq(node.nodeName, "#text",
+ "[number] node has correct name");
+ t.eq(node.nodeValue, "0.42",
+ "[number] node has correct value");
+
+ value = false;
+ node = format.createTextNode(value);
+ t.eq(node.nodeType, 3,
+ "[boolean] node has correct type");
+ t.eq(node.nodeName, "#text",
+ "[boolean] node has correct name");
+ t.eq(node.nodeValue, "false",
+ "[boolean] node has correct value");
+
+ var doc = format.read(text);
+ if (doc.importNode) {
+ node = doc.importNode(node, true);
+ }
+ t.ok(doc.documentElement.appendChild(node),
+ "node can be appended to a doc root");
+ }
+
+ function test_Format_XML_getElementsByTagNameNS(t) {
+ t.plan(5);
+
+ var format = new OpenLayers.Format.XML();
+ var olUri = "http://namespace.openlayers.org";
+ var name = "child";
+ var doc = format.read(text);
+ var nodes = format.getElementsByTagNameNS(doc.documentElement,
+ olUri, name);
+ t.eq(nodes.length, 3,
+ "gets correct number of nodes");
+ var qualifiedName = nodes[0].prefix + ":" + name;
+ t.eq(nodes[0].nodeName, qualifiedName,
+ "first node has correct qualified name");
+
+ var defaultUri = "http://namespace.default.net";
+ name = "element";
+ nodes = format.getElementsByTagNameNS(doc.documentElement,
+ defaultUri, name);
+ t.eq(nodes.length, 1,
+ "gets correct number of nodes in default namespace");
+
+ var pList = format.getElementsByTagNameNS(doc.documentElement,
+ olUri, "p");
+ t.eq(pList.length, 1, "got one ol:p element");
+ var p = pList[0];
+
+ var aList = format.getElementsByTagNameNS(p, olUri, "a");
+ t.eq(aList.length, 2, "got two child ol:a elements");
+
+
+
+ }
+
+ function test_Format_XML_getAttributeNodeNS(t) {
+ t.plan(5);
+
+ var format = new OpenLayers.Format.XML();
+ var doc = format.read(text);
+ var olUri = "http://namespace.openlayers.org";
+ var taUri = "http://namespace.testattribute.net";
+ var localNodeName = "child";
+ var localAttrName = "attribute";
+ var nodes = format.getElementsByTagNameNS(doc.documentElement,
+ olUri, localNodeName);
+ var attributeNode = format.getAttributeNodeNS(nodes[0],
+ taUri, localAttrName);
+ var qualifiedName = attributeNode.prefix + ":" + localAttrName;
+
+ t.ok(attributeNode,
+ "returns non-null value");
+ t.eq(attributeNode.nodeType, 2,
+ "attribute node has correct type");
+ t.eq(attributeNode.nodeName, qualifiedName,
+ "attribute node has correct qualified name");
+ t.eq(attributeNode.nodeValue, "value1",
+ "attribute node has correct value");
+
+ var nullAttribute = format.getAttributeNodeNS(nodes[0],
+ taUri, "nothing");
+ t.ok(nullAttribute === null,
+ "returns null for nonexistent attribute");
+ }
+
+ function test_Format_XML_getAttributeNS(t) {
+ t.plan(2);
+
+ var format = new OpenLayers.Format.XML();
+ var doc = format.read(text);
+ var olUri = "http://namespace.openlayers.org";
+ var taUri = "http://namespace.testattribute.net";
+ var localNodeName = "child";
+ var localAttrName = "attribute";
+ var nodes = format.getElementsByTagNameNS(doc.documentElement,
+ olUri, localNodeName);
+ var attributeValue = format.getAttributeNS(nodes[0],
+ taUri, localAttrName);
+ t.eq(attributeValue, "value1",
+ "got correct attribute value");
+
+ var emptyValue = format.getAttributeNS(nodes[0],
+ taUri, "nothing");
+ t.ok(emptyValue === "",
+ "returns empty string for nonexistent attributes");
+ }
+
+ function test_Format_XML_hasAttributeNS(t) {
+ t.plan(2);
+
+ var format = new OpenLayers.Format.XML();
+ var doc = format.read(text);
+ var olUri = "http://namespace.openlayers.org";
+ var taUri = "http://namespace.testattribute.net";
+ var localNodeName = "child";
+ var localAttrName = "attribute";
+ var nodes = format.getElementsByTagNameNS(doc.documentElement,
+ olUri, localNodeName);
+ var found = format.hasAttributeNS(nodes[0], taUri, localAttrName);
+ t.ok(found === true, "returns true for good attribute");
+
+ found = format.hasAttributeNS(nodes[0], taUri, "nothing");
+ t.ok(found === false, "returns false for bad attribute");
+ }
+
+ function test_namespaces(t) {
+ t.plan(2);
+
+ var format = new OpenLayers.Format.XML({
+ namespaces: {
+ "def": "http://example.com/default",
+ "foo": "http://example.com/foo",
+ "bar": "http://example.com/bar"
+ },
+ defaultPrefix: "def"
+ });
+
+ // test that prototype has not been altered
+ t.eq(OpenLayers.Format.XML.prototype.namespaces, null,
+ "setting namespaces at construction does not modify prototype");
+
+ // test that namespaceAlias has been set
+ t.eq(format.namespaceAlias["http://example.com/foo"], "foo",
+ "namespaceAlias mapping has been set");
+
+ }
+
+ function test_setNamespace(t) {
+ t.plan(3);
+
+ var format = new OpenLayers.Format.XML();
+
+ // test that namespaces is an object
+ t.ok(format.namespaces instanceof Object, "empty namespace object set");
+
+ format.setNamespace("foo", "http://example.com/foo");
+ t.eq(format.namespaces["foo"], "http://example.com/foo", "alias -> uri mapping set");
+ t.eq(format.namespaceAlias["http://example.com/foo"], "foo", "uri -> alias mapping set");
+
+ }
+
+ function test_readChildNodes(t) {
+
+ var text = "<?xml version='1.0' encoding='UTF-8'?>" +
+ "<container xmlns='http://example.com/foo'>" +
+ "<marker name='my marker 1'>" +
+ "<position>" +
+ "<lon>-180</lon>" +
+ "<lat>90</lat>" +
+ "</position>" +
+ "<detail>some text for first marker</detail>" +
+ "<atom:link xmlns:atom='http://www.w3.org/2005/Atom' href='http://host/path/1'/>" +
+ "</marker>" +
+ "<marker name='my marker 2'>" +
+ "<position>" +
+ "<lon>180</lon>" +
+ "<lat>-90</lat>" +
+ "</position>" +
+ "<detail>some text for second marker</detail>" +
+ "<atom:link xmlns:atom='http://www.w3.org/2005/Atom' href='http://host/path/2'/>" +
+ "</marker>" +
+ "</container>";
+
+ var expect = [
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(-180, 90),
+ {
+ name: 'my marker 1',
+ link: 'http://host/path/1',
+ detail: 'some text for first marker'
+ }
+ ),
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(180, -90),
+ {
+ name: 'my marker 2',
+ link: 'http://host/path/2',
+ detail: 'some text for second marker'
+ }
+ )
+ ];
+
+ var format = new OpenLayers.Format.XML({
+ defaultPrefix: "foo",
+ namespaces: {
+ "foo": "http://example.com/foo",
+ "atom": "http://www.w3.org/2005/Atom"
+ },
+ readers: {
+ "foo": {
+ "container": function(node, obj) {
+ var list = [];
+ this.readChildNodes(node, list);
+ obj.list = list;
+ },
+ "marker": function(node, list) {
+ var feature = new OpenLayers.Feature.Vector();
+ feature.attributes.name = node.getAttribute("name");
+ this.readChildNodes(node, feature);
+ list.push(feature);
+ },
+ "position": function(node, feature) {
+ var obj = {};
+ this.readChildNodes(node, obj);
+ feature.geometry = new OpenLayers.Geometry.Point(obj.x, obj.y);
+ },
+ "lon": function(node, obj) {
+ obj.x = this.getChildValue(node);
+ },
+ "lat": function(node, obj) {
+ obj.y = this.getChildValue(node);
+ },
+ "detail": function(node, feature) {
+ feature.attributes.detail = this.getChildValue(node);
+ }
+ },
+ "atom": {
+ "link": function(node, feature) {
+ feature.attributes.link = node.getAttribute("href");
+ }
+ }
+ }
+ });
+
+ // convert text to document node
+ var doc = format.read(text);
+ // read child nodes to get back some object
+ var obj = format.readChildNodes(doc);
+ // start comparing what we got to what we expect
+ var got = obj.list;
+
+ t.plan(11);
+ t.eq(got.length, expect.length, "correct number of items parsed");
+ t.eq(got[0].geometry.x, expect[0].geometry.x, "correct x coord parsed for marker 1");
+ t.eq(got[0].geometry.y, expect[0].geometry.y, "correct y coord parsed for marker 1");
+ t.eq(got[0].attributes.name, expect[0].attributes.name, "correct name parsed for marker 1");
+ t.eq(got[0].attributes.detail, expect[0].attributes.detail, "correct detail parsed for marker 1");
+ t.eq(got[0].attributes.link, expect[0].attributes.link, "correct link parsed for marker 1");
+ t.eq(got[1].geometry.x, expect[1].geometry.x, "correct x coord parsed for marker 2");
+ t.eq(got[1].geometry.y, expect[1].geometry.y, "correct y coord parsed for marker 2");
+ t.eq(got[1].attributes.name, expect[1].attributes.name, "correct name parsed for marker 2");
+ t.eq(got[1].attributes.detail, expect[1].attributes.detail, "correct detail parsed for marker 2");
+ t.eq(got[1].attributes.link, expect[1].attributes.link, "correct link parsed for marker 2");
+
+ }
+
+ function test_writeNode(t) {
+
+ var features = [
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(-180, 90),
+ {
+ name: 'my marker 1',
+ link: 'http://host/path/1',
+ detail: 'some text for first marker'
+ }
+ ),
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(180, -90),
+ {
+ name: 'my marker 2',
+ link: 'http://host/path/2',
+ detail: 'some text for second marker'
+ }
+ )
+ ];
+
+ var expect = "<?xml version='1.0' encoding='UTF-8'?>" +
+ "<container xmlns='http://example.com/foo'>" +
+ "<marker name='my marker 1'>" +
+ "<position>" +
+ "<lon>-180</lon>" +
+ "<lat>90</lat>" +
+ "</position>" +
+ "<detail>some text for first marker</detail>" +
+ "<atom:link xmlns:atom='http://www.w3.org/2005/Atom' href='http://host/path/1'/>" +
+ "</marker>" +
+ "<marker name='my marker 2'>" +
+ "<position>" +
+ "<lon>180</lon>" +
+ "<lat>-90</lat>" +
+ "</position>" +
+ "<detail>some text for second marker</detail>" +
+ "<atom:link xmlns:atom='http://www.w3.org/2005/Atom' href='http://host/path/2'/>" +
+ "</marker>" +
+ "</container>";
+
+ var format = new OpenLayers.Format.XML({
+ defaultPrefix: "foo",
+ namespaces: {
+ "foo": "http://example.com/foo",
+ "atom": "http://www.w3.org/2005/Atom"
+ },
+ writers: {
+ "foo": {
+ "container": function(features) {
+ var node = this.createElementNSPlus("container");
+ var feature;
+ for(var i=0; i<features.length; ++i) {
+ feature = features[i];
+ this.writeNode("marker", features[i], node);
+ }
+ return node;
+ },
+ "marker": function(feature) {
+ var node = this.createElementNSPlus("marker", {
+ attributes: {name: feature.attributes.name}
+ });
+ this.writeNode("position", feature.geometry, node);
+ this.writeNode("detail", feature.attributes.detail, node);
+ this.writeNode("atom:link", feature.attributes.link, node);
+ return node;
+ },
+ "position": function(geometry) {
+ var node = this.createElementNSPlus("position");
+ this.writeNode("lon", geometry.x, node);
+ this.writeNode("lat", geometry.y, node);
+ return node;
+ },
+ "lon": function(x) {
+ return this.createElementNSPlus("lon", {
+ value: x
+ });
+ },
+ "lat": function(y) {
+ return this.createElementNSPlus("lat", {
+ value: y
+ });
+ },
+ "detail": function(text) {
+ return this.createElementNSPlus("detail", {
+ value: text
+ });
+ }
+ },
+ "atom": {
+ "link": function(href) {
+ return this.createElementNSPlus("atom:link", {
+ attributes: {href: href}
+ });
+ }
+ }
+ }
+
+ });
+
+ t.plan(1);
+ // test that we get what we expect from writeNode
+ var got = format.writeNode("container", features);
+ t.xml_eq(got, expect, "features correctly written");
+ }
+
+ function test_createElementNSPlus(t) {
+
+ var format = new OpenLayers.Format.XML({
+ defaultPrefix: "def",
+ namespaces: {
+ "def": "http://example.com/default",
+ "foo": "http://example.com/foo",
+ "bar": "http://example.com/bar"
+ }
+ });
+
+ var cases = [
+ {
+ description: "unprefixed name with default options",
+ node: format.createElementNSPlus("FooNode"),
+ expect: "<def:FooNode xmlns:def='http://example.com/default'/>"
+ }, {
+ description: "def prefixed name with default options",
+ node: format.createElementNSPlus("def:FooNode"),
+ expect: "<def:FooNode xmlns:def='http://example.com/default'/>"
+ }, {
+ description: "foo prefixed name with default options",
+ node: format.createElementNSPlus("foo:FooNode"),
+ expect: "<foo:FooNode xmlns:foo='http://example.com/foo'/>"
+ }, {
+ description: "unprefixed name with uri option",
+ node: format.createElementNSPlus("FooNode", {
+ uri: "http://example.com/elsewhere"
+ }),
+ expect: "<FooNode xmlns='http://example.com/elsewhere'/>"
+ }, {
+ description: "foo prefixed name with uri option (overriding format.namespaces)",
+ node: format.createElementNSPlus("foo:FooNode", {
+ uri: "http://example.com/elsewhere"
+ }),
+ expect: "<foo:FooNode xmlns:foo='http://example.com/elsewhere'/>"
+ }, {
+ description: "foo prefixed name with attributes option",
+ node: format.createElementNSPlus("foo:FooNode", {
+ attributes: {
+ "id": "123",
+ "foo:attr1": "namespaced attribute 1",
+ "bar:attr2": "namespaced attribute 2"
+ }
+ }),
+ expect: "<foo:FooNode xmlns:foo='http://example.com/foo' xmlns:bar='http://example.com/bar' id='123' foo:attr1='namespaced attribute 1' bar:attr2='namespaced attribute 2'/>"
+ }, {
+ description: "foo prefixed name with attributes and value options",
+ node: format.createElementNSPlus("foo:FooNode", {
+ attributes: {"id": "123"},
+ value: "text value"
+ }),
+ expect: "<foo:FooNode xmlns:foo='http://example.com/foo' id='123'>text value<" + "/foo:FooNode>"
+ }, {
+ description: "value of 0 gets appended as a text node",
+ node: format.createElementNSPlus("foo:bar", {value: 0}),
+ expect: "<foo:bar xmlns:foo='http://example.com/foo'>0</foo:bar>"
+ }, {
+ description: "value of 0.42 gets appended as a text node",
+ node: format.createElementNSPlus("foo:bar", {value: 0.42}),
+ expect: "<foo:bar xmlns:foo='http://example.com/foo'>0.42</foo:bar>"
+ }, {
+ description: "value of true gets appended as a text node",
+ node: format.createElementNSPlus("foo:bar", {value: true}),
+ expect: "<foo:bar xmlns:foo='http://example.com/foo'>true</foo:bar>"
+ }, {
+ description: "value of false gets appended as a text node",
+ node: format.createElementNSPlus("foo:bar", {value: false}),
+ expect: "<foo:bar xmlns:foo='http://example.com/foo'>false</foo:bar>"
+ }, {
+ description: "null value does not get appended as a text node",
+ node: format.createElementNSPlus("foo:bar", {value: null}),
+ expect: "<foo:bar xmlns:foo='http://example.com/foo'/>"
+ }, {
+ description: "undefined value does not get appended as a text node",
+ node: format.createElementNSPlus("foo:bar"),
+ expect: "<foo:bar xmlns:foo='http://example.com/foo'/>"
+ }
+ ];
+
+ t.plan(cases.length);
+ var test;
+ for(var i=0; i<cases.length; ++i) {
+ test = cases[i];
+ t.xml_eq(test.node, test.expect, test.description);
+ }
+
+ }
+
+ function test_setAttributes(t) {
+
+ var format = new OpenLayers.Format.XML({
+ defaultPrefix: "def",
+ namespaces: {
+ "def": "http://example.com/default",
+ "foo": "http://example.com/foo",
+ "bar": "http://example.com/bar"
+ }
+ });
+
+ var cases = [
+ {
+ description: "unprefixed attribute",
+ node: format.createElementNSPlus("foo:Node"),
+ attributes: {"id": "123"},
+ expect: "<foo:Node xmlns:foo='http://example.com/foo' id='123'/>"
+ }, {
+ description: "foo prefixed attribute",
+ node: format.createElementNSPlus("foo:Node"),
+ attributes: {"foo:id": "123"},
+ expect: "<foo:Node xmlns:foo='http://example.com/foo' foo:id='123'/>"
+ }, {
+ description: "foo prefixed attribute with def prefixed node",
+ node: format.createElementNSPlus("def:Node"),
+ attributes: {"foo:id": "123"},
+ expect: "<def:Node xmlns:def='http://example.com/default' xmlns:foo='http://example.com/foo' foo:id='123'/>"
+ }, {
+ description: "multiple attributes",
+ node: format.createElementNSPlus("def:Node"),
+ attributes: {"id": "123", "foo": "bar"},
+ expect: "<def:Node xmlns:def='http://example.com/default' id='123' foo='bar'/>"
+ }
+ ];
+
+ t.plan(cases.length);
+ var test;
+ for(var i=0; i<cases.length; ++i) {
+ test = cases[i];
+ format.setAttributes(test.node, test.attributes);
+ t.xml_eq(test.node, test.expect, test.description);
+ }
+
+ }
+
+ function test_keepData(t) {
+ t.plan(2);
+
+ var options = {'keepData': true};
+ var format = new OpenLayers.Format.XML(options);
+ format.read(text);
+
+ t.ok(format.data != null, 'data property is not null after read with keepData=true');
+ t.eq(format.data.documentElement.tagName,'ol:root','keepData keeps the right data');
+ }
+
+ function test_getChildValue(t) {
+
+ t.plan(1);
+
+ var text =
+ "<?xml version='1.0' encoding='UTF-8'?>" +
+ "<root>" +
+ "x<!-- comment -->y<!-- comment 2 --><![CDATA[z]]>z<foo />&#x79;" +
+ "</root>";
+
+ var format = new OpenLayers.Format.XML();
+ var doc = format.read(text).documentElement;
+
+ t.eq(format.getChildValue(doc), "xyzzy", "child value skips comments, concatenates multiple values, reads through entities");
+
+ }
+
+ function test_getChildEl(t) {
+
+ t.plan(3);
+
+ var text =
+ "<?xml version='1.0' encoding='UTF-8'?>" +
+ "<root>" +
+ "<!-- comment -->" +
+ "<a>x</a>" +
+ "<b>x</b>" +
+ "</root>";
+
+ var format = new OpenLayers.Format.XML();
+ var doc = format.read(text).documentElement;
+
+ var a = format.getChildEl(doc);
+ t.eq(a.nodeName, "a", "first element found correctly");
+
+ a = format.getChildEl(doc, "a");
+ t.eq(b, null, "first child element matches the given name");
+
+ var b = format.getChildEl(doc, "b");
+ t.eq(b, null, "first child element does not match the given name");
+
+ }
+
+ function test_getNextEl(t) {
+ t.plan(5);
+
+ var text =
+ "<?xml version='1.0' encoding='UTF-8'?>" +
+ "<root>" +
+ "<!-- comment -->" +
+ "<a>x</a>" +
+ "<!-- comment -->" +
+ "<b xmlns='urn:example'>x</b>" +
+ "</root>";
+
+ var format = new OpenLayers.Format.XML();
+ var doc = format.read(text).documentElement;
+
+ var a = format.getChildEl(doc);
+
+ var b = format.getNextEl(a);
+ t.eq(b && b.nodeName, "b", "next element correctly found");
+
+ b = format.getNextEl(a, "b");
+ t.eq(b && b.nodeName, "b", "next element correctly found when name supplied");
+
+ b = format.getNextEl(a, "c");
+ t.eq(b, null, "null returned when name does not match next element");
+
+ b = format.getNextEl(a, null, "urn:example");
+ t.eq(b && b.nodeName, "b", "next element correctly found when namespace supplied");
+
+ b = format.getNextEl(a, null, "foo");
+ t.eq(b, null, "null returned when namespace does not match next element");
+
+ }
+
+ function test_isSimpleContent(t) {
+ t.plan(2);
+
+ var text =
+ "<?xml version='1.0' encoding='UTF-8'?>" +
+ "<root>" +
+ "<!-- comment -->" +
+ "<a>x<!-- comment -->y<!-- comment 2 --><![CDATA[z]]>z<foo />&#x79;</a>" +
+ "<!-- comment -->" +
+ "<b>x<!-- comment -->y<!-- comment 2 --><![CDATA[z]]>z&#x79;</b>" +
+ "</root>";
+
+ var format = new OpenLayers.Format.XML();
+ var doc = format.read(text).documentElement;
+
+ var a = format.getChildEl(doc);
+ var b = format.getNextEl(a);
+ t.ok(!format.isSimpleContent(a), "<a> content is not simple");
+ t.ok(format.isSimpleContent(b), "<b> content is simple");
+
+ }
+
+ function test_lookupNamespaceURI(t) {
+
+ t.plan(8);
+
+ var text =
+ "<?xml version='1.0' encoding='UTF-8'?>" +
+ "<root xmlns:baz='urn:baznamespace'>" +
+ "<!-- comment -->" +
+ "<a><foo /></a>" +
+ "<!-- comment -->" +
+ "<b xmlns='urn:example'><!-- comment --><bar foo='value'/></b>" +
+ "</root>";
+
+ var format = new OpenLayers.Format.XML();
+ var doc = format.read(text).documentElement;
+
+ var a = format.getChildEl(doc);
+ t.eq(format.lookupNamespaceURI(a, "baz"), "urn:baznamespace", "prefix lookup on first child");
+
+ var foo = format.getChildEl(a);
+ t.eq(format.lookupNamespaceURI(foo, "baz"), "urn:baznamespace", "prefix lookup on child of first child");
+
+ var b = format.getNextEl(a);
+ t.eq(format.lookupNamespaceURI(b, null), "urn:example", "default namespace lookup on element");
+
+ var bar = format.getChildEl(b);
+ t.eq(format.lookupNamespaceURI(bar, null), "urn:example", "default namespace lookup on child");
+ t.eq(format.lookupNamespaceURI(bar, "baz"), "urn:baznamespace", "prefix lookup on child with different default");
+
+ // test that the alias behaves properly
+ var lookup = OpenLayers.Format.XML.lookupNamespaceURI;
+ t.eq(lookup(bar, "baz"), "urn:baznamespace", "(alias) prefix lookup on child with different default");
+
+ var attr = bar.attributes[0];
+ // Internet Explorer didn't have the ownerElement property until 8.
+ var supportsOwnerElement = !!attr.ownerElement;
+ if(supportsOwnerElement) {
+ t.eq(format.lookupNamespaceURI(attr, null), "urn:example", "default namespace lookup on attribute");
+ t.eq(format.lookupNamespaceURI(attr, "baz"), "urn:baznamespace", "prefix lookup on attribute with different default");
+ } else {
+ t.debug_print("namespace lookup on attributes not supported in this browser");
+ t.ok(true, "namespace lookup on attributes not supported in this browser");
+ t.ok(true, "namespace lookup on attributes not supported in this browser");
+ }
+
+ }
+
+ function test_getXMLDoc(t) {
+ t.plan(2);
+ var format = new OpenLayers.Format.XML();
+ var doc = format.getXMLDoc();
+ t.ok(doc !== document, "document returned from getXMLDoc is not the page's html doc");
+ var root = format.createElementNS("http://test", "root");
+ // appending CDATA created from a different document
+ var cdata = doc.createCDATASection("<foo></foo>");
+ root.appendChild(cdata);
+ var result = format.write(root);
+ var expect = '<root xmlns="http://test"><![CDATA[<foo></foo>]]></root>';
+ t.eq(result, expect, "document with CDATA section serialized correctly");
+ }
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Format/XML/VersionedOGC.html b/misc/openlayers/tests/Format/XML/VersionedOGC.html
new file mode 100644
index 0000000..ca96d63
--- /dev/null
+++ b/misc/openlayers/tests/Format/XML/VersionedOGC.html
@@ -0,0 +1,51 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var snippet = '<foo version="2.0.0"></foo>';
+ var snippet2 = '<foo></foo>';
+
+ function test_Format_Versioned_constructor(t) {
+ t.plan(5);
+
+ var format = new OpenLayers.Format.XML.VersionedOGC({version: "1.0.0"});
+ t.ok(format instanceof OpenLayers.Format.XML.VersionedOGC,
+ "new OpenLayers.Format.XML.VersionedOGC returns object" );
+ t.eq(format.version, "1.0.0", "constructor sets version correctly");
+ t.eq(format.defaultVersion, null, "defaultVersion should be null if not specified");
+ t.eq(typeof format.read, "function", "format has a read function");
+ t.eq(typeof format.write, "function", "format has a read function");
+ }
+
+ function test_getVersion(t) {
+ t.plan(6);
+ var format = new OpenLayers.Format.XML.VersionedOGC();
+ // read
+ var data = new OpenLayers.Format.XML().read(snippet);
+ var root = data.documentElement;
+ var version = format.getVersion(root);
+ t.eq(version, "2.0.0", "Version taken from document");
+ format = new OpenLayers.Format.XML.VersionedOGC({version: "1.0.0"});
+ version = format.getVersion(root);
+ t.eq(version, "1.0.0", "Version taken from parser takes preference");
+ format = new OpenLayers.Format.XML.VersionedOGC({defaultVersion: "3.0.0"});
+ data = new OpenLayers.Format.XML().read(snippet2);
+ root = data.documentElement;
+ version = format.getVersion(root);
+ t.eq(version, "3.0.0", "If nothing else is set, defaultVersion should be returned");
+ // write
+ version = format.getVersion(null, {version: "1.3.0"});
+ t.eq(version, "1.3.0", "Version from options returned");
+ version = format.getVersion(null);
+ t.eq(version, "3.0.0", "defaultVersion returned if no version specified in options and no version on the format");
+ format.version = "2.1.3";
+ version = format.getVersion(null);
+ t.eq(version, "2.1.3", "version returned of the Format if no version specified in options");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Geometry.html b/misc/openlayers/tests/Geometry.html
new file mode 100644
index 0000000..2a4b4c4
--- /dev/null
+++ b/misc/openlayers/tests/Geometry.html
@@ -0,0 +1,356 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script src="data/geos_wkt_intersects.js"></script>
+ <script type="text/javascript">
+ var map;
+
+ function test_Geometry_constructor (t) {
+ t.plan( 2 );
+
+ var g = new OpenLayers.Geometry();
+
+ t.eq(g.CLASS_NAME, "OpenLayers.Geometry", "correct CLASS_NAME")
+ t.ok(OpenLayers.String.startsWith(g.id, "OpenLayers_Geometry_"),
+ "id correctly set");
+ }
+
+
+ function test_Geometry_clone(t) {
+ t.plan(2);
+ var geometry = new OpenLayers.Geometry();
+ var clone = geometry.clone();
+
+ t.eq(clone.CLASS_NAME, "OpenLayers.Geometry", "correct CLASS_NAME")
+ t.ok(OpenLayers.String.startsWith(clone.id, "OpenLayers_Geometry_"),
+ "id correctly set");
+ }
+
+ function test_Geometry_setBounds(t) {
+ t.plan( 2 );
+
+ var g = new OpenLayers.Geometry();
+
+ //null object
+ g.setBounds(null);
+ t.ok(g.bounds == null, "setbounds with null value does not crash or set bounds");
+
+ //no classname object
+ g_clone = {};
+ var object = {
+ 'clone': function() { return g_clone; }
+ };
+ g.setBounds(object);
+ t.ok(g.bounds == g_clone, "setbounds with valid object sets bounds, calls clone");
+ }
+
+ function test_Geometry_extendBounds(t) {
+ t.plan(9);
+
+ OpenLayers.Bounds.prototype._extend =
+ OpenLayers.Bounds.prototype.extend;
+ OpenLayers.Bounds.prototype.extend = function(b) {
+ g_extendBounds = b;
+ };
+
+ var g = new OpenLayers.Geometry();
+
+ //this.bounds null (calculateBounds(), setBounds() called)
+ g.setBounds = function(b) { g_setBounds = b; };
+ g.calculateBounds = function() { g_calculateBounds = {}; };
+ var object = {};
+ g_setBounds = null;
+ g_calculateBounds = null;
+ g_extendBounds = null;
+ g.extendBounds(object);
+ t.ok(g_calculateBounds != null, "calculateBounds() called when this.bounds is null");
+ t.ok(g_setBounds == object, "setBounds() called when this.bounds is null and calculateBounds() is null too");
+ t.ok(g_extendBounds != object, "this.bounds.extend() not called when this.bounds is null and calculateBounds() is null too");
+
+ //this.bounds null (calculateBounds() sets this.bounds:
+ // - setBounds() not called
+ // - this.bounds.extend() called
+ g_calcBounds = new OpenLayers.Bounds(1,2,3,4);
+ g.calculateBounds = function() {
+ g_calculateBounds = {};
+ this.bounds = g_calcBounds;
+ };
+ var object = {};
+
+ g_setBounds = null;
+ g_calculateBounds = null;
+ g_extendBounds = null;
+ g.extendBounds(object);
+ t.ok(g_calculateBounds != null, "calculateBounds() called when this.bounds is null");
+ t.ok(g_setBounds == null, "setBounds() not called when this.bounds is null and calculateBounds() sets this.bounds");
+ t.ok(g_extendBounds == object, "this.bounds.extend() called when this.bounds is null and calculateBounds() sets this.bounds");
+
+
+ //this.bounds non-null thus extend()
+ // - setBounds() not called
+ // - this.bounds.extend() called
+ g_setBounds = null;
+ g_calculateBounds = null;
+ g_extendBounds = null;
+ g.extendBounds(object);
+ t.ok(g_calculateBounds == null, "calculateBounds() not called when this.bounds is non null");
+ t.ok(g_setBounds == null, "setBounds() not called when this.bounds is nonnull");
+ t.ok(g_extendBounds == object, "this.bounds.extend() called when this.bounds is non-null");
+
+ OpenLayers.Bounds.prototype.extend =
+ OpenLayers.Bounds.prototype._extend;
+
+
+ }
+
+ function test_Geometry_getBounds(t) {
+ t.plan(1);
+
+ var g = new OpenLayers.Geometry();
+
+ var testBounds = new OpenLayers.Bounds(1,2,3,4);
+ g.bounds = testBounds.clone();
+
+ t.ok(g.getBounds().equals(testBounds), "getBounds works");
+ }
+
+ function test_Geometry_atPoint(t) {
+ t.plan(6);
+
+ var g = new OpenLayers.Geometry();
+
+ var lonlat = null;
+ var lon = 5;
+ var lat = 10;
+
+ //null lonlat
+ g.bounds = new OpenLayers.Bounds();
+
+ var atPoint = g.atPoint(lonlat, lon, lat);
+ t.ok(!atPoint, "null lonlat")
+
+ //null this.bounds
+ g.bounds = null;
+ lonlat = new OpenLayers.LonLat(1,2);
+
+ atPoint = g.atPoint(lonlat, lon, lat);
+ t.ok(!atPoint, "null this.bounds")
+
+ //toleranceLon/toleranceLat
+
+ //default toleranceLon/toleranceLat
+ OpenLayers.Bounds.prototype._containsLonLat = OpenLayers.Bounds.prototype.containsLonLat;
+ g_Return = {};
+ OpenLayers.Bounds.prototype.containsLonLat = function(ll) {
+ g_bounds = this;
+ return g_Return;
+ }
+
+ var testBounds = new OpenLayers.Bounds(10,20,30,40);
+ g.bounds = testBounds.clone();
+ lonlat = new OpenLayers.LonLat(20,30);
+
+ g_bounds = null;
+ atPoint = g.atPoint(lonlat);
+ t.ok(g_bounds.equals(testBounds), "default toleranceLon/Lat are 0");
+ t.ok(atPoint == g_Return, "default toleranceLon/Lat returns correctly");
+
+ //real toleranceLon/toleranceLat
+ var testBounds = new OpenLayers.Bounds(10,20,30,40);
+ g.bounds = testBounds.clone();
+ lonlat = new OpenLayers.LonLat(20,30);
+
+ g_bounds = null;
+ atPoint = g.atPoint(lonlat, lon, lat);
+ testBounds.left -= lon;
+ testBounds.bottom -= lat;
+ testBounds.right += lon;
+ testBounds.top += lat;
+ t.ok(g_bounds.equals(testBounds), "real toleranceLon/Lat are 0");
+ t.ok(atPoint == g_Return, "real toleranceLon/Lat returns correctly");
+
+ OpenLayers.Bounds.prototype.containsLonLat = OpenLayers.Bounds.prototype._containsLonLat;
+
+ }
+
+ function test_Geometry_getLength(t) {
+ t.plan(1);
+
+ var g = new OpenLayers.Geometry();
+
+ t.eq(g.getLength(), 0, "getLength is 0");
+ }
+
+ function test_Geometry_getArea(t) {
+ t.plan(1);
+
+ var g = new OpenLayers.Geometry();
+
+ t.eq(g.getArea(), 0, "getArea is 0");
+ }
+
+ function test_Geometry_clearBounds(t) {
+ t.plan(2);
+
+ var g = new OpenLayers.Geometry();
+ g.parent = new OpenLayers.Geometry();
+
+ g.bounds = "foo";
+ g.parent.bounds = "bar";
+
+ g.clearBounds();
+ t.ok(g.bounds == null, "bounds is correctly cleared");
+ t.ok(g.parent.bounds == null, "parent geometry bounds is correctly cleared");
+ }
+
+ function test_Geometry_destroy(t) {
+ t.plan( 2 );
+
+ var g = new OpenLayers.Geometry();
+ g.bounds = new OpenLayers.Bounds();
+
+ g_style_destroy = null;
+ g.destroy();
+
+ t.eq(g.id, null, "id nullified");
+
+ t.eq(g.bounds, null, "bounds nullified");
+
+ }
+
+ function test_Geometry_intersects_geos_wkt(t) {
+ var wkt = new OpenLayers.Format.WKT();
+ var failures = [];
+ var intersect12, intersect21, msg;
+ for (var i = 0; i < geos_test_data.length; i++) {
+ var testcase = geos_test_data[i];
+ f1 = wkt.read(testcase['wkt1']);
+ f2 = wkt.read(testcase['wkt2']);
+ intersect12 = f1.geometry.intersects(f2.geometry);
+ intersect21 = f2.geometry.intersects(f1.geometry);
+ if(intersect12 != testcase.result) {
+ msg = "f1 should " + (testcase.result ? "" : "not ") +
+ "intersect f2: f1 = '" + testcase['wkt1'] + "' " +
+ "f2 = '" + testcase['wkt2'] + "'";
+ failures.push(msg);
+ }
+ if(intersect21 != testcase.result) {
+ msg = "f2 should " + (testcase.result ? "" : "not ") +
+ "intersect f1: f1 = '" + testcase['wkt1'] + "' " +
+ "f2 = '" + testcase['wkt2'] + "'";
+ failures.push(msg);
+ }
+ }
+ if(failures.length == 0) {
+ t.plan(1);
+ t.ok(true, "all " + geos_test_data.length + " geos tests pass");
+ } else {
+ t.plan(failures.length);
+ for(var f=0; f<failures.length; ++f) {
+ t.fail(failures[f]);
+ }
+ }
+ }
+
+ function test_distanceToSegment(t) {
+ var dist = OpenLayers.Geometry.distanceToSegment;
+
+ var cases = [{
+ got: dist({x: 0, y: 0}, {x1: 0, y1: 1, x2: 1, y2: 1}),
+ expected: {distance: 1, x: 0, y: 1, along: 0}
+ }, {
+ got: dist({x: 0, y: 0}, {x1: -1, y1: -1, x2: 0, y2: -1}),
+ expected: {distance: 1, x: 0, y: -1, along: 1}
+ }, {
+ got: dist({x: 0, y: 0}, {x1: -1, y1: -1, x2: 1, y2: 1}),
+ expected: {distance: 0, x: 0, y: 0, along: 0.5}
+ }, {
+ got: dist({x: 1, y: 1}, {x1: 2, y1: 0, x2: 2, y2: 3}),
+ expected: {distance: 1, x: 2, y: 1, along: 1/3.}
+ }, {
+ got: dist({x: -1, y: -1}, {x1: -2, y1: -2, x2: -1, y2: -3}),
+ expected: {distance: Math.sqrt(2), x: -2, y: -2, along: 0}
+ }, {
+ got: dist({x: -1, y: 1}, {x1: -3, y1: 1, x2: -1, y2: 3}),
+ expected: {distance: Math.sqrt(2), x: -2, y: 2, along: 0.5}
+ }];
+ t.plan(cases.length);
+ for(var i=0; i<cases.length; ++i) {
+ t.eq(cases[i].got, cases[i].expected, "case " + i);
+ }
+
+ }
+
+ function test_fromWKT(t) {
+
+ var cases = [{
+ wkt: "POINT(1 2)",
+ geom: new OpenLayers.Geometry.Point(1, 2)
+ }, {
+ wkt: "MULTIPOINT((3.5 5.6),(4.8 10.5))",
+ geom: new OpenLayers.Geometry.MultiPoint([
+ new OpenLayers.Geometry.Point(3.5, 5.6),
+ new OpenLayers.Geometry.Point(4.8, 10.5)
+ ])
+ }, {
+ wkt: "LINESTRING(1 2, 3 4)",
+ geom: new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(1, 2),
+ new OpenLayers.Geometry.Point(3, 4)
+ ])
+ }, {
+ wkt: "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))",
+ geom: new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(0, 0),
+ new OpenLayers.Geometry.Point(0, 4),
+ new OpenLayers.Geometry.Point(4, 4),
+ new OpenLayers.Geometry.Point(4, 0),
+ new OpenLayers.Geometry.Point(0, 0)
+ ]),
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(1, 1),
+ new OpenLayers.Geometry.Point(1, 3),
+ new OpenLayers.Geometry.Point(3, 3),
+ new OpenLayers.Geometry.Point(3, 1),
+ new OpenLayers.Geometry.Point(1, 1)
+ ])
+ ])
+ }, {
+ wkt: "GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(4 6,7 10))",
+ geom: new OpenLayers.Geometry.Collection([
+ new OpenLayers.Geometry.Point(4, 6),
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(4, 6),
+ new OpenLayers.Geometry.Point(7, 10)
+ ])
+ ])
+ }];
+
+ t.plan(cases.length);
+ var wkt = OpenLayers.Geometry.fromWKT;
+ for(var i=0; i<cases.length; ++i) {
+ t.geom_eq(wkt(cases[i].wkt), cases[i].geom, "case " + i);
+ }
+ }
+
+ function test_fromWKT_undefined(t) {
+ t.plan(1);
+
+ var WKT = OpenLayers.Format.WKT;
+ OpenLayers.Format.WKT = null;
+ delete OpenLayers.Geometry.fromWKT.format;
+
+ var geom = OpenLayers.Geometry.fromWKT("POINT(1 1)");
+ t.eq(geom, undefined, "undefined when OpenLayers.Format.WKT is not available");
+
+ OpenLayers.Format.WKT = WKT;
+ }
+
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Geometry/Collection.html b/misc/openlayers/tests/Geometry/Collection.html
new file mode 100644
index 0000000..7c9fd62
--- /dev/null
+++ b/misc/openlayers/tests/Geometry/Collection.html
@@ -0,0 +1,431 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var coll;
+
+ function test_Collection_constructor (t) {
+ t.plan( 4 );
+
+ //null param
+ coll = new OpenLayers.Geometry.Collection();
+ t.ok( coll instanceof OpenLayers.Geometry.Collection, "new OpenLayers.Geometry.Collection returns coll object" );
+ t.eq( coll.CLASS_NAME, "OpenLayers.Geometry.Collection", "coll.CLASS_NAME is set correctly");
+ t.eq( coll.components.length, 0, "coll.components is set correctly");
+
+ OpenLayers.Geometry.Collection.prototype._addComponents =
+ OpenLayers.Geometry.Collection.prototype.addComponents;
+ OpenLayers.Geometry.Collection.prototype.addComponents =
+ function(comps) { g_addcomponents = comps; };
+
+ //valid param
+ g_addcomponents = null;
+ var components = {};
+ coll = new OpenLayers.Geometry.Collection(components);
+ t.ok(g_addcomponents, components, "addcomponents called on non-null param")
+
+ OpenLayers.Geometry.Collection.prototype.addComponents =
+ OpenLayers.Geometry.Collection.prototype._addComponents;
+ }
+
+ function test_Collection_addComponents (t) {
+ t.plan( 10 );
+
+ coll = new OpenLayers.Geometry.Collection();
+
+ //null
+ coll.addComponents(null);
+ t.ok(true, "doesn't break on add null components");
+
+ OpenLayers.Geometry.Collection.prototype._addComponent =
+ OpenLayers.Geometry.Collection.prototype.addComponent;
+
+ OpenLayers.Geometry.Collection.prototype.addComponent =
+ function(comp) { g_addComp = comp; g_added++};
+
+
+ //nonarray argument
+ var g_added = 0;
+ var g_addComp = 0;
+ var component = {};
+ coll.addComponents(component);
+ t.eq(g_added, 1, "added once");
+ t.eq(g_addComp, component, "added component");
+
+ //array arg
+ var g_added = 0;
+ var g_addComp = 0;
+ var component1 = {};
+ var component2 = {};
+ coll.addComponents([component1, component2]);
+ t.eq(g_added, 2, "added twice");
+ t.eq(g_addComp, component2, "added component");
+
+ OpenLayers.Geometry.Collection.prototype.addComponent =
+ OpenLayers.Geometry.Collection.prototype._addComponent;
+
+
+
+ coll.addComponents(new OpenLayers.Geometry.Point(0,0));
+ coll.addComponents(new OpenLayers.Geometry.Point(10,10));
+ t.eq( coll.components.length, 2, "added two components to collection" );
+ bounds = coll.getBounds();
+ t.eq( bounds.left, 0, "left bound is 0" );
+ t.eq( bounds.bottom, 0, "bottom bound is 0" );
+ t.eq( bounds.right, 10, "right bound is 10" );
+ t.eq( bounds.top, 10, "top bound is 10" );
+ }
+
+ function test_Collection_clone (t) {
+ t.plan( 3 );
+ coll = new OpenLayers.Geometry.Collection();
+ coll.addComponents(new OpenLayers.Geometry.Point(0,0));
+ coll.addComponents(new OpenLayers.Geometry.Point(10,10));
+ coll2 = coll.clone();
+ t.ok( coll2 instanceof OpenLayers.Geometry.Collection, "coll.clone() returns collection object" );
+ t.eq( coll2.components.length, 2, "coll2.components.length is set correctly");
+ t.ok( coll2.components[0] instanceof OpenLayers.Geometry.Point,
+ "coll2.components.length is set correctly");
+ }
+
+ function test_Collection_removeComponents (t) {
+ t.plan( 5 );
+ coll = new OpenLayers.Geometry.Collection();
+ point = new OpenLayers.Geometry.Point(0,0);
+ coll.addComponents(point);
+ coll.addComponents(new OpenLayers.Geometry.Point(10,10));
+ coll.removeComponents(coll.components[0]);
+ t.eq( coll.components.length, 1, "coll.components.length is smaller after removeComponent" );
+ t.ok( coll.bounds == null, "bounds are nullified after call to remove (to trigger recalc on getBounds()");
+ bounds = coll.getBounds();
+ t.eq( bounds.left, 10, "left bound is 10 after removeComponent" );
+ t.eq( bounds.bottom, 10, "bottom bound is 10 after removeComponent" );
+
+ coll = new OpenLayers.Geometry.Collection();
+ for(var i=0; i<5; ++i) {
+ coll.addComponents(
+ new OpenLayers.Geometry.Point(Math.random(), Math.random())
+ );
+ }
+ coll.removeComponents(coll.components);
+ t.eq(coll.components.length, 0,
+ "remove components even works with multiple components");
+
+ }
+
+ function test_Collection_calculateBounds(t) {
+ t.plan( 9 );
+
+ var coll = new OpenLayers.Geometry.Collection();
+ coll.calculateBounds();
+ t.eq(coll.bounds, null, "null components list gives null bounds on calculation()");
+
+ var p1 = new OpenLayers.Geometry.Point(10,20);
+ var p2 = new OpenLayers.Geometry.Point(30,40);
+
+ var components = [p1, p2];
+ coll = new OpenLayers.Geometry.Collection(components);
+
+ coll.calculateBounds();
+
+ t.eq(coll.bounds.left, 10, "good left bounds");
+ t.eq(coll.bounds.bottom, 20, "good bottom bounds");
+ t.eq(coll.bounds.right, 30, "good right bounds");
+ t.eq(coll.bounds.top, 40, "good top bounds");
+
+ var newPoint = new OpenLayers.Geometry.Point(60,70);
+ coll.addComponent(newPoint);
+ coll.calculateBounds();
+
+ t.eq(coll.bounds.left, 10, "good left bounds");
+ t.eq(coll.bounds.bottom, 20, "good bottom bounds");
+ t.eq(coll.bounds.right, 60, "good right bounds");
+ t.eq(coll.bounds.top, 70, "good top bounds");
+ }
+
+ function test_Collection_equals(t) {
+ t.plan(1);
+ var geom = new OpenLayers.Geometry.Collection();
+ t.ok(!geom.equals(), "collection.equals() returns false for undefined");
+ }
+
+ function test_Collection_addComponent(t) {
+ t.plan(10);
+
+ var coll = new OpenLayers.Geometry.Collection();
+
+ //null
+ coll.addComponent(null);
+ t.ok(!coll.addComponent(null),
+ "addComponent returns false for bad component")
+
+ //good component
+ var component = new OpenLayers.Geometry.Point(3,4);
+ t.ok(coll.addComponent(component),
+ "addComponent returns true for good component");
+ t.ok(coll.bounds == null, "bounds cache correctly cleared");
+
+ var foundComponent = false;
+ for(var i=0; i< coll.components.length; i++) {
+ if (coll.components[i].equals(component)) {
+ foundComponent = true;
+ }
+ }
+ t.ok(foundComponent, "component added to internal array");
+
+ // restricted components
+ coll.componentTypes = ["OpenLayers.Geometry.Point",
+ "OpenLayers.Geometry.LineString"];
+ var point1 = new OpenLayers.Geometry.Point(0,0);
+ var point2 = new OpenLayers.Geometry.Point(1,1);
+ var line = new OpenLayers.Geometry.LineString([point1, point2]);
+ var multipoint = new OpenLayers.Geometry.MultiPoint([point1, point2]);
+
+ t.ok(coll.addComponent(point1),
+ "addComponent returns true for 1st geometry type in componentTypes");
+ t.ok(OpenLayers.Util.indexOf(coll.components, point1) > -1,
+ "addComponent adds 1st restricted type to components array");
+ t.ok(coll.addComponent(line),
+ "addComponent returns true for 2nd geometry type in componentTypes");
+ t.ok(OpenLayers.Util.indexOf(coll.components, point1) > -1,
+ "addComponent adds 2nd restricted type to components array");
+ t.ok(!coll.addComponent(multipoint),
+ "addComponent returns false for geometry type not in componentTypes");
+ t.ok(OpenLayers.Util.indexOf(coll.components, multipoint) == -1,
+ "addComponent doesn't add restricted type to component array");
+
+ }
+
+ function test_collection_getLength(t) {
+ t.plan(2);
+
+ //null
+ var coll = new OpenLayers.Geometry.Collection();
+ t.eq( coll.getLength(), 0, "null coll has 0 getlength");
+
+ //valid
+ coll.components = [
+ { 'getLength': function() { return 50; } },
+ { 'getLength': function() { return 15; } }
+ ];
+ t.eq( coll.getLength(), 65, "coll with valid components correctly sums getlength");
+ }
+
+ function test_collection_getArea(t) {
+ t.plan(2);
+
+ //null
+ var coll = new OpenLayers.Geometry.Collection();
+ t.eq( coll.getArea(), 0, "null coll has 0 getArea");
+
+ //valid
+ coll.components = [
+ { 'getArea': function() { return 50; } },
+ { 'getArea': function() { return 15; } }
+ ];
+ t.eq( coll.getArea(), 65, "coll with valid components correctly sums getArea");
+ }
+
+ function test_transform(t) {
+ t.plan(5);
+ var p1 = new OpenLayers.Geometry.Point(0,0);
+ p1.bounds = "foo";
+ var p2 = new OpenLayers.Geometry.Point(1,1);
+ p2.bounds = "foo";
+ var line = new OpenLayers.Geometry.LineString([p1, p2]);
+ var multipoint = new OpenLayers.Geometry.MultiPoint([p1, p2]);
+ var coll = new OpenLayers.Geometry.Collection([
+ p1, p2, line, multipoint
+ ]);
+ coll.bounds = "foo";
+
+ var wgs84 = new OpenLayers.Projection("EPSG:4326");
+ var sm = new OpenLayers.Projection("EPSG:900913");
+ coll.transform(wgs84, sm);
+
+ t.eq(coll.bounds, null, "coll bounds cleared");
+ t.eq(p1.bounds, null, "p1 component bounds cleared");
+ t.eq(p2.bounds, null, "p2 component bounds cleared");
+ t.eq(line.bounds, null, "line component bounds cleared");
+ t.eq(multipoint.bounds, null, "multipoint component bounds cleared");
+
+ }
+
+ function test_getCentroid_pts_only(t) {
+ t.plan(3);
+
+ coll = new OpenLayers.Geometry.Collection();
+ coll.addComponent(new OpenLayers.Geometry.Point(0,0));
+ coll.addComponent(new OpenLayers.Geometry.Point(1,1));
+
+ centroid = coll.getCentroid(true);
+ t.ok(centroid != null, 'The centroid is not null.');
+ t.eq(centroid.x, 0.5, 'The centroid x coordinate is good.');
+ t.eq(centroid.y, 0.5, 'The centroid y coordinate is good.');
+
+ coll.destroy();
+ }
+
+ function test_getCentroid_poly_nonrecursive(t) {
+ t.plan(3);
+
+ coll = new OpenLayers.Geometry.Collection();
+ coll.addComponent(
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(0,0),
+ new OpenLayers.Geometry.Point(0,1),
+ new OpenLayers.Geometry.Point(1,1),
+ new OpenLayers.Geometry.Point(1,0)
+ ])
+ ])
+ );
+ // performing non-recursive getCentroid means this next polygon
+ // is excluded from the centroid computation
+ coll.addComponent(
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(2,2),
+ new OpenLayers.Geometry.Point(2,3),
+ new OpenLayers.Geometry.Point(3,3),
+ new OpenLayers.Geometry.Point(3,2)
+ ])
+ ])
+ );
+
+ centroid = coll.getCentroid();
+ t.ok(centroid != null, 'The centroid is not null.');
+ t.eq(centroid.x, 0.5, 'The centroid x coordinate is good.');
+ t.eq(centroid.y, 0.5, 'The centroid y coordinate is good.');
+
+ coll.destroy();
+ }
+
+ function test_getCentroid_poly_only(t) {
+ t.plan(3);
+
+ coll = new OpenLayers.Geometry.Collection();
+ coll.addComponent(
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(0,0),
+ new OpenLayers.Geometry.Point(0,1),
+ new OpenLayers.Geometry.Point(1,1),
+ new OpenLayers.Geometry.Point(1,0)
+ ])
+ ])
+ );
+
+ centroid = coll.getCentroid(true);
+ t.ok(centroid != null, 'The centroid is not null.');
+ t.eq(centroid.x, 0.5, 'The centroid x coordinate is good.');
+ t.eq(centroid.y, 0.5, 'The centroid y coordinate is good.');
+
+ coll.destroy();
+ }
+
+ function test_getCentroid_poly_and_pts(t) {
+ t.plan(3);
+
+ coll = new OpenLayers.Geometry.Collection();
+ coll.addComponent(
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(0,0),
+ new OpenLayers.Geometry.Point(0,1),
+ new OpenLayers.Geometry.Point(1,1),
+ new OpenLayers.Geometry.Point(1,0)
+ ])
+ ])
+ );
+
+ // since the polygon above has an area of 1 and these
+ // points have an area of 0, they should not change the centroid
+ coll.addComponent( new OpenLayers.Geometry.Point(2,2) );
+ coll.addComponent( new OpenLayers.Geometry.Point(4,4) );
+
+ centroid = coll.getCentroid(true);
+ t.ok(centroid != null, 'The centroid is not null.');
+ t.eq(centroid.x, 0.5, 'The centroid x coordinate is good.');
+ t.eq(centroid.y, 0.5, 'The centroid y coordinate is good.');
+
+ coll.destroy();
+ }
+
+ function test_getCentroid_poly_big_and_small(t) {
+ t.plan(3);
+
+ coll = new OpenLayers.Geometry.Collection();
+ // polygon w/area=1, centroid=0.5,0.5
+ coll.addComponent(
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(0,0),
+ new OpenLayers.Geometry.Point(0,1),
+ new OpenLayers.Geometry.Point(1,1),
+ new OpenLayers.Geometry.Point(1,0)
+ ])
+ ])
+ );
+
+ // since the polygon above has an area of 1 and this
+ // polygon has an area of 4, the center is weighted 20% toward
+ // the first polygon
+
+ // polygon w/area=4, centroid=5.5,5.5
+ coll.addComponent(
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(4.5,-0.5),
+ new OpenLayers.Geometry.Point(4.5,1.5),
+ new OpenLayers.Geometry.Point(6.5,1.5),
+ new OpenLayers.Geometry.Point(6.5,-0.5)
+ ])
+ ])
+ );
+
+ centroid = coll.getCentroid(true);
+ t.ok(centroid != null, 'The centroid is not null.');
+ t.eq(centroid.x, 4.5, 'The centroid x coordinate is good.');
+ t.eq(centroid.y, 0.5, 'The centroid y coordinate is good.');
+
+ coll.destroy();
+ }
+
+ function test_avoid_infinite_recursion(t) {
+ t.plan(1);
+
+ var g = new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing(),
+ new OpenLayers.Geometry.LinearRing()
+ ]);
+ var bounds;
+ try {
+ bounds = g.getBounds();
+ t.eq(bounds, null, "Polygon with empty linear ring has null bounds");
+ } catch (err) {
+ t.fail("Failed to get bounds of polygon with empty linear ring: " + err.message);
+ }
+
+ }
+
+
+ function test_Collection_destroy(t) {
+ t.plan( 3 );
+ coll = new OpenLayers.Geometry.Collection();
+ coll.addComponents(new OpenLayers.Geometry.Point(0,0));
+ coll.addComponents(new OpenLayers.Geometry.Point(10,10));
+ coll.getBounds();
+ coll.destroy();
+
+ t.ok(coll.components == null, "components array cleared");
+ t.ok(coll.getBounds() == null, "bounds is cleared");
+ t.ok(coll.id == null, "id is cleared");
+
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Geometry/Curve.html b/misc/openlayers/tests/Geometry/Curve.html
new file mode 100644
index 0000000..5afebdf
--- /dev/null
+++ b/misc/openlayers/tests/Geometry/Curve.html
@@ -0,0 +1,157 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var curve;
+ var components = [new OpenLayers.Geometry.Point(10,10),
+ new OpenLayers.Geometry.Point(0,0)];
+
+ function test_Curve_constructor (t) {
+ t.plan( 3 );
+ curve = new OpenLayers.Geometry.Curve();
+ t.ok( curve instanceof OpenLayers.Geometry.Curve, "new OpenLayers.Geometry.Curve returns curve object" );
+ t.eq( curve.CLASS_NAME, "OpenLayers.Geometry.Curve", "curve.CLASS_NAME is set correctly");
+ t.eq( curve.components, [], "curve.components is set correctly");
+ }
+
+ function test_Curve_constructor (t) {
+ t.plan( 2 );
+ curve = new OpenLayers.Geometry.Curve(components);
+ t.ok( curve instanceof OpenLayers.Geometry.Curve, "new OpenLayers.Geometry.Curve returns curve object" );
+ t.eq( curve.components.length, 2, "curve.components.length is set correctly");
+ }
+
+ function test_Curve_clone (t) {
+ t.plan( 2 );
+ curve = new OpenLayers.Geometry.Curve(components);
+ curve2 = curve.clone();
+ t.ok( curve2 instanceof OpenLayers.Geometry.Curve, "curve.clone() returns curve object" );
+ t.eq( curve2.components.length, 2, "curve2.components.length is set correctly");
+ }
+
+ function test_Curve_calculateBounds(t) {
+ t.plan( 17 );
+
+
+ var curve = new OpenLayers.Geometry.Curve();
+ curve.calculateBounds();
+ t.eq(curve.bounds, null, "bounds null when no components");
+
+ var p1 = new OpenLayers.Geometry.Point(10,20);
+ var p2 = new OpenLayers.Geometry.Point(30,40);
+
+ var components = [p1, p2];
+ var curve = new OpenLayers.Geometry.Curve(components);
+
+ curve.calculateBounds();
+
+ t.eq(curve.bounds.left, 10, "good left bounds");
+ t.eq(curve.bounds.bottom, 20, "good bottom bounds");
+ t.eq(curve.bounds.right, 30, "good right bounds");
+ t.eq(curve.bounds.top, 40, "good top bounds");
+
+ var newPoint = new OpenLayers.Geometry.Point(60,70);
+ curve.addComponent(newPoint);
+ curve.calculateBounds();
+
+ t.eq(curve.bounds.left, 10, "good left bounds");
+ t.eq(curve.bounds.bottom, 20, "good bottom bounds");
+ t.eq(curve.bounds.right, 60, "good right bounds");
+ t.eq(curve.bounds.top, 70, "good top bounds");
+
+ //nullifying the bounds
+
+ //before calculation
+ curve = new OpenLayers.Geometry.Curve(components);
+ curve.bounds = null;
+ curve.calculateBounds();
+
+ t.eq(curve.bounds.left, 10, "good left bounds");
+ t.eq(curve.bounds.bottom, 20, "good bottom bounds");
+ t.eq(curve.bounds.right, 30, "good right bounds");
+ t.eq(curve.bounds.top, 40, "good top bounds");
+
+ //before addComponent
+ curve.bounds = null;
+ curve.addComponent(newPoint);
+ curve.calculateBounds();
+
+ t.eq(curve.bounds.left, 10, "good left bounds");
+ t.eq(curve.bounds.bottom, 20, "good bottom bounds");
+ t.eq(curve.bounds.right, 60, "good right bounds");
+ t.eq(curve.bounds.top, 70, "good top bounds");
+
+ }
+
+ function test_Curve_addComponent (t) {
+ t.plan( 8 );
+ curve = new OpenLayers.Geometry.Curve(components);
+ curve.addComponent(new OpenLayers.Geometry.Point(20,30));
+ bounds = curve.getBounds();
+ t.eq( curve.components.length, 3, "new point added to array" );
+ t.eq( bounds.top, 30, "top bound is 30 after addComponent" );
+ t.eq( bounds.right, 20, "right bound is 20 after addComponent" );
+ curve.addComponent(new OpenLayers.Geometry.Point(-20,-30), 1);
+ bounds = curve.getBounds();
+ t.eq( curve.components.length, 4, "new point added to array" );
+ t.eq( bounds.bottom, -30, "bottom bound is -30 after 2nd addComponent" );
+ t.eq( bounds.left, -20, "left bound is 20 after 2nd addComponent" );
+ t.eq( curve.components[1].x, -20, "new point.lon is -20 (index worked)" );
+ t.eq( curve.components[1].y, -30, "new point.lat is -30 (index worked)" );
+ }
+
+ function test_Curve_removeComponent (t) {
+ t.plan( 4 );
+ curve = new OpenLayers.Geometry.Curve(components);
+ curve.removeComponent(curve.components[1]);
+ t.eq( curve.components.length, 1, "curve.components.length is smaller after removeComponent" );
+ t.eq( curve.bounds, null, "curve.bounds nullified after removeComponent (for recalculation)" );
+ bounds = curve.getBounds();
+ t.eq( bounds.left, 10, "left bound is 10 after removeComponent" );
+ t.eq( bounds.bottom, 10, "bottom bound is 10 after removeComponent" );
+ }
+
+ function test_Curve_getLength (t) {
+ t.plan( 4 );
+
+ //no components
+ curve = new OpenLayers.Geometry.Curve();
+ curve.components = null;
+ t.eq(curve.getLength(), 0, "curve with no components has length 0");
+
+ //empty components
+ curve.components = [];
+ t.eq(curve.getLength(), 0, "curve with empty components has length 0");
+
+ //single point curve
+ curve.components = [ new OpenLayers.Geometry.Point(0,0) ];
+ t.eq(curve.getLength(), 0, "curve with only one point has length 0");
+
+ //multipoint
+ var newcomponents = [ new OpenLayers.Geometry.Point(0,0),
+ new OpenLayers.Geometry.Point(0,10),
+ new OpenLayers.Geometry.Point(20,10),
+ new OpenLayers.Geometry.Point(20,-10)
+ ];
+
+ curve = new OpenLayers.Geometry.Curve(newcomponents);
+ t.eq(curve.getLength(), 50, "curve.getLength returns a reasonably accurate length" );
+ }
+
+ function test_Curve_destroy(t) {
+ t.plan(1);
+
+ var curve = new OpenLayers.Geometry.Curve();
+ curve.components = {};
+
+ curve.destroy();
+
+ t.ok( curve.components == null, "components is cleared well in destruction");
+ }
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Geometry/LineString.html b/misc/openlayers/tests/Geometry/LineString.html
new file mode 100644
index 0000000..4b2ec0e
--- /dev/null
+++ b/misc/openlayers/tests/Geometry/LineString.html
@@ -0,0 +1,443 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var line;
+ var components = [new OpenLayers.Geometry.Point(10,15),
+ new OpenLayers.Geometry.Point(0,0)];
+
+ function test_LineString_constructor (t) {
+ t.plan( 3 );
+ line = new OpenLayers.Geometry.LineString();
+ t.ok( line instanceof OpenLayers.Geometry.LineString, "new OpenLayers.Geometry.LineString returns line object" );
+ t.eq( line.CLASS_NAME, "OpenLayers.Geometry.LineString", "line.CLASS_NAME is set correctly");
+ t.eq( line.components, [], "line.components is set correctly");
+ }
+
+ function test_LineString_constructor (t) {
+ t.plan( 3 );
+ line = new OpenLayers.Geometry.LineString(components);
+ t.ok( line instanceof OpenLayers.Geometry.LineString, "new OpenLayers.Geometry.LineString returns line object" );
+ t.eq( line.CLASS_NAME, "OpenLayers.Geometry.LineString", "line.CLASS_NAME is set correctly");
+ // TBD FIXME, recursion
+ // t.eq( line.components, components, "line.components is set correctly");
+ t.eq( line.components.length, 2, "line.components.length is set correctly");
+ }
+
+ function test_LineString_toString(t) {
+ t.plan(1);
+
+ line = new OpenLayers.Geometry.LineString(components);
+ t.eq(line.toString(),
+ "LINESTRING(10 15,0 0)",
+ "toString() returns WKT");
+ }
+
+ function test_LineString_removeComponent(t) {
+ t.plan(2);
+
+ OpenLayers.Geometry.Collection.prototype._removeComponent =
+ OpenLayers.Geometry.Collection.prototype.removeComponent;
+ OpenLayers.Geometry.Collection.prototype.removeComponent =
+ function(point) { g_removeComponent = point; };
+
+ line = new OpenLayers.Geometry.LineString(components);
+
+ g_removeComponent = null;
+ line.removeComponent(components[0]);
+ t.ok(g_removeComponent == null, "point not removed if only 2 points in components");
+
+ line.components.push(new OpenLayers.Geometry.Point(4,4));
+ line.removeComponent(components[0]);
+ t.ok(g_removeComponent, components[0], "point removed if 3 points in components");
+
+ OpenLayers.Geometry.Collection.prototype.removeComponent =
+ OpenLayers.Geometry.Collection.prototype._removeComponent;
+ }
+
+ function test_LineString_move(t) {
+ t.plan(4);
+
+ var components = [new OpenLayers.Geometry.Point(10,15),
+ new OpenLayers.Geometry.Point(0,0)];
+ var line = new OpenLayers.Geometry.LineString(components);
+
+ var x0 = components[0].x;
+ var y0 = components[0].y;
+ var x1 = components[1].x;
+ var y1 = components[1].y;
+
+ var dx = 10 * Math.random();
+ var dy = 10 * Math.random();
+ line.move(dx, dy);
+
+ t.eq(line.components[0].x, x0 + dx, "move() correctly modifies first x");
+ t.eq(line.components[0].y, y0 + dy, "move() correctly modifies first y");
+ t.eq(line.components[1].x, x1 + dx, "move() correctly modifies second x");
+ t.eq(line.components[1].y, y1 + dy, "move() correctly modifies second y");
+ }
+
+ function test_LineString_rotate(t) {
+ t.plan(6);
+
+ var components = [new OpenLayers.Geometry.Point(10,15),
+ new OpenLayers.Geometry.Point(0,0)];
+ var geometry = new OpenLayers.Geometry.LineString(components);
+
+ var originals = [];
+ var comp;
+ var angle = 2 * Math.PI * Math.random();
+ var origin = new OpenLayers.Geometry.Point(10 * Math.random(),
+ 10 * Math.random());
+ for(var i=0; i<geometry.components.length; ++i) {
+ comp = geometry.components[i];
+ originals[i] = comp.rotate;
+ comp.rotate = function(a, o) {
+ t.ok(true, "rotate called for component " + i);
+ t.ok(a == angle, "rotate called with correct angle");
+ t.ok(o == origin, "rotate called with correct origin");
+ }
+ }
+ geometry.rotate(angle, origin);
+
+ // restore the original rotate defs
+ for(var i=0; i<geometry.components.length; ++i) {
+ comp.rotate = originals[i];
+ }
+ }
+
+ function test_LineString_resize(t) {
+ t.plan(8);
+
+ var tolerance = 1e-10;
+
+ var components = [new OpenLayers.Geometry.Point(10 * Math.random(),
+ 10 * Math.random()),
+ new OpenLayers.Geometry.Point(10 * Math.random(),
+ 10 * Math.random())];
+ var geometry = new OpenLayers.Geometry.LineString(components);
+
+ var origin = new OpenLayers.Geometry.Point(10 * Math.random(),
+ 10 * Math.random());
+
+ var scale = 10 * Math.random();
+
+ var oldLength = geometry.getLength();
+ var ret = geometry.resize(scale, origin);
+ t.ok(ret === geometry, "resize returns geometry");
+ var newLength = geometry.getLength();
+ t.ok((((newLength / oldLength) - scale) / scale) < tolerance,
+ "resize correctly changes the length of a linestring")
+
+ var originals = [];
+ var comp;
+ for(var i=0; i<geometry.components.length; ++i) {
+ comp = geometry.components[i];
+ originals[i] = comp.resize;
+ comp.resize = function(s, o) {
+ t.ok(true, "resize called for component " + i);
+ t.ok(s == scale, "resize called with correct scale");
+ t.ok(o == origin, "resize called with correct origin");
+ }
+ }
+ geometry.resize(scale, origin);
+
+ // restore the original resize defs
+ for(var i=0; i<geometry.components.length; ++i) {
+ comp.resize = originals[i];
+ }
+
+ }
+
+ function test_split(t) {
+ var wkt = OpenLayers.Geometry.fromWKT;
+
+ var cases = [{
+ msg: "no intersection",
+ g1: "LINESTRING(0 0, 0 1)",
+ g2: "LINESTRING(1 0, 1 1)",
+ exp: null
+ } , {
+ msg: "intersection at midpoint",
+ g1: "LINESTRING(0 0, 1 1)",
+ g2: "LINESTRING(1 0, 0 1)",
+ exp: ["LINESTRING(1 0, 0.5 0.5)", "LINESTRING(0.5 0.5, 0 1)"]
+ }, {
+ msg: "intersection at midpoint (reverse source/target)",
+ g1: "LINESTRING(1 0, 0 1)",
+ g2: "LINESTRING(0 0, 1 1)",
+ exp: ["LINESTRING(0 0, 0.5 0.5)", "LINESTRING(0.5 0.5, 1 1)"]
+ }, {
+ msg: "intersection at endpoint",
+ g1: "LINESTRING(0 0, 1 1)",
+ g2: "LINESTRING(1 0, 1 1)",
+ exp: null
+ }, {
+ msg: "midpoint intersection, no options",
+ g1: "LINESTRING(0 0, 2 2)",
+ g2: "LINESTRING(0 2, 2 0)",
+ exp: ["LINESTRING(0 2, 1 1)", "LINESTRING(1 1, 2 0)"]
+ }, {
+ msg: "midpoint intersection, edge false",
+ opt: {edge: false},
+ g1: "LINESTRING(0 0, 2 2)",
+ g2: "LINESTRING(0 2, 2 0)",
+ exp: null
+ }, {
+ msg: "midpoint intersection, mutual",
+ opt: {mutual: true},
+ g1: "LINESTRING(0 0, 2 2)",
+ g2: "LINESTRING(0 2, 2 0)",
+ exp: [["LINESTRING(0 0, 1 1)", "LINESTRING(1 1, 2 2)"], ["LINESTRING(0 2, 1 1)", "LINESTRING(1 1, 2 0)"]]
+ }, {
+ msg: "close intersection, no tolerance",
+ g1: "LINESTRING(0 0, 0.9 0.9)",
+ g2: "LINESTRING(0 2, 2 0)",
+ exp: null
+ }, {
+ msg: "close intersection, within tolerance",
+ opt: {tolerance: 0.2},
+ g1: "LINESTRING(0 0, 0.9 0.9)",
+ g2: "LINESTRING(0 2, 2 0)",
+ exp: ["LINESTRING(0 2, 0.9 0.9)", "LINESTRING(0.9 0.9, 2 0)"]
+ }];
+
+ t.plan(cases.length);
+ var c, parts, part, midparts;
+ for(var i=0; i<cases.length; ++i) {
+ c = cases[i];
+ var g1 = wkt(c.g1);
+ var g2 = wkt(c.g2);
+ var got = g1.split(g2, c.opt);
+ var exp = c.exp;
+ if(got instanceof Array) {
+ parts = [];
+ for(var j=0; j<got.length; ++j) {
+ part = got[j];
+ if(part instanceof Array) {
+ midparts = [];
+ for(var k=0; k<part.length; ++k) {
+ midparts.push(part[k].toString());
+ }
+ parts.push("[" + midparts.join(", ") + "]");
+ } else {
+ parts.push(got[j].toString());
+ }
+ }
+ got = parts.join(", ");
+ }
+ if(exp instanceof Array) {
+ parts = [];
+ for(var j=0; j<exp.length; ++j) {
+ part = exp[j];
+ if(part instanceof Array) {
+ midparts = [];
+ for(var k=0; k<part.length; ++k) {
+ midparts.push(wkt(part[k]).toString());
+ }
+ parts.push("[" + midparts.join(", ") + "]");
+ } else {
+ parts.push(wkt(exp[j]).toString());
+ }
+ }
+ exp = parts.join(", ");
+ }
+ t.eq(got, exp, "case " + i + ": " + c.msg);
+ }
+
+ }
+
+
+ function test_distanceTo(t) {
+ var wkt = OpenLayers.Geometry.fromWKT;
+ var geoms = [
+ wkt("POINT(0 0)"),
+ wkt("LINESTRING(-2 0, 0 -2, 2 -1, 2 0)")
+ ];
+
+ var cases = [{
+ got: geoms[1].distanceTo(geoms[0]),
+ expected: Math.sqrt(2)
+ }, {
+ got: geoms[1].distanceTo(geoms[0], {details: true}),
+ expected: {
+ distance: Math.sqrt(2),
+ x0: -1, y0: -1,
+ x1: 0, y1: 0
+ }
+ }];
+
+ t.plan(cases.length);
+ for(var i=0; i<cases.length; ++i) {
+ t.eq(cases[i].got, cases[i].expected, "case " + i);
+ }
+
+ }
+
+ function test_LineString_equals(t) {
+ t.plan(3);
+
+ var x0 = Math.random() * 100;
+ var y0 = Math.random() * 100;
+ var x1 = Math.random() * 100;
+ var y1 = Math.random() * 100;
+ var point0 = new OpenLayers.Geometry.Point(x0, y0);
+ var point1 = new OpenLayers.Geometry.Point(x1, y1);
+ var geometry = new OpenLayers.Geometry.LineString([point0, point1]);
+ var equal = new OpenLayers.Geometry.LineString([point0, point1]);
+ var offX = new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(x0 + 1, y0),
+ new OpenLayers.Geometry.Point(x1 + 1, y1)]);
+ var offY = new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(x0, y0 + 1),
+ new OpenLayers.Geometry.Point(x1, y1 + 1)]);
+ t.ok(geometry.equals(equal),
+ "equals() returns true for a geometry with equivalent coordinates");
+ t.ok(!geometry.equals(offX),
+ "equals() returns false for a geometry with offset x");
+ t.ok(!geometry.equals(offY),
+ "equals() returns false for a geometry with offset y");
+ }
+
+
+ function test_getVertices(t) {
+ t.plan(14);
+
+ var points = [
+ new OpenLayers.Geometry.Point(10, 20),
+ new OpenLayers.Geometry.Point(20, 30),
+ new OpenLayers.Geometry.Point(30, 40),
+ new OpenLayers.Geometry.Point(40, 50)
+ ];
+ var line = new OpenLayers.Geometry.LineString(points);
+
+ var verts = line.getVertices();
+ t.ok(verts instanceof Array, "got back an array");
+ t.eq(verts.length, points.length, "of correct length length");
+ t.geom_eq(verts[0], points[0], "0: correct geometry");
+ t.geom_eq(verts[1], points[1], "1: correct geometry");
+ t.geom_eq(verts[2], points[2], "2: correct geometry");
+ t.geom_eq(verts[3], points[3], "3: correct geometry");
+
+ // get nodes only
+ var nodes = line.getVertices(true);
+ t.ok(nodes instanceof Array, "[nodes only] got back an array");
+ t.eq(nodes.length, 2, "[nodes only] of correct length length");
+ t.geom_eq(nodes[0], points[0], "[nodes only] first: correct geometry");
+ t.geom_eq(nodes[1], points[points.length-1], "[nodes only] last: correct geometry");
+
+ // no nodes
+ var nodes = line.getVertices(false);
+ t.ok(nodes instanceof Array, "[no nodes] got back an array");
+ t.eq(nodes.length, 2, "[no nodes] of correct length length");
+ t.geom_eq(nodes[0], points[1], "[no nodes] first: correct geometry");
+ t.geom_eq(nodes[1], points[2], "[no nodes] last: correct geometry");
+
+ }
+
+
+ function test_LineString_clone(t) {
+ t.plan(2);
+
+ var x0 = Math.random() * 100;
+ var y0 = Math.random() * 100;
+ var x1 = Math.random() * 100;
+ var y1 = Math.random() * 100;
+ var point0 = new OpenLayers.Geometry.Point(x0, y0);
+ var point1 = new OpenLayers.Geometry.Point(x1, y1);
+ var geometry = new OpenLayers.Geometry.LineString([point0, point1]);
+ var clone = geometry.clone();
+ t.ok(clone instanceof OpenLayers.Geometry.LineString,
+ "clone() creates an OpenLayers.Geometry.LineString");
+ t.ok(geometry.equals(clone), "clone has equivalent coordinates");
+ }
+
+ function test_getGeodesicLength(t) {
+
+ // expected values from http://www.movable-type.co.uk/scripts/latlong-vincenty.html
+ var cases = [{
+ wkt: "LINESTRING(0 0, -10 45)",
+ exp: 5081689.690
+ }, {
+ wkt: "LINESTRING(-10 45, 0 0)",
+ exp: 5081689.690
+ }, {
+ wkt: "LINESTRING(0 0, -10 45, -20 50)",
+ exp: 5081689.690 + 935018.062
+ }];
+ t.plan(cases.length);
+
+ var geom, got;
+ for(var i=0; i<cases.length; ++i) {
+ geom = new OpenLayers.Geometry.fromWKT(cases[i].wkt);
+ got = geom.getGeodesicLength();
+ t.eq(Math.round(got), Math.round(cases[i].exp), "[case " + i + "] length calculated");
+ }
+
+ }
+
+ function test_LineString_simplify(t){
+ t.plan(8);
+ var ls1 = new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(0,0),
+ new OpenLayers.Geometry.Point(1,2.1),
+ new OpenLayers.Geometry.Point(1.8,3.8),
+ new OpenLayers.Geometry.Point(2,4),
+ new OpenLayers.Geometry.Point(3,4),
+ new OpenLayers.Geometry.Point(4,4.5),
+ new OpenLayers.Geometry.Point(5,5)
+
+ ]);
+ var ls2 = new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(0,0),
+ new OpenLayers.Geometry.Point(1,2.1),
+ new OpenLayers.Geometry.Point(1.8,3.8),
+ new OpenLayers.Geometry.Point(2,4),
+ new OpenLayers.Geometry.Point(3,4),
+ new OpenLayers.Geometry.Point(4,4.5),
+ new OpenLayers.Geometry.Point(5,5),
+ new OpenLayers.Geometry.Point(0,0)
+
+ ]);
+ var ls3 = new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(0,0),
+ new OpenLayers.Geometry.Point(1,1)
+ ]);
+ var ls5 = new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(0,0),
+ new OpenLayers.Geometry.Point(1,1),
+ new OpenLayers.Geometry.Point(2,2),
+ new OpenLayers.Geometry.Point(3,3),
+ new OpenLayers.Geometry.Point(4,4),
+ new OpenLayers.Geometry.Point(5,5)
+
+ ]);
+ var ls6 = new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(0,0),
+ new OpenLayers.Geometry.Point(1,1),
+ new OpenLayers.Geometry.Point(1,1),
+ new OpenLayers.Geometry.Point(3,2)
+ ]);
+
+ t.ok(ls1 instanceof OpenLayers.Geometry.LineString, 'LineString is instance of OpenLayers.Geometry.LineString');
+ var simplified1 = ls1.simplify(0.5);
+ t.ok(simplified1 instanceof OpenLayers.Geometry.LineString, 'Simplified LineString is instance of OpenLayers.Geometry.LineString');
+ t.ok(simplified1.getVertices().length <= ls1.getVertices().length, 'Simplified LineString has less or equal number of vertices');
+ // The simplified version is derived from PostGIS function ST_SIMPLIFY()
+ t.ok(simplified1.toString() === 'LINESTRING(0 0,1.8 3.8,5 5)', 'LineString 1 was simplified correctly');
+ var simplified2 = ls2.simplify(0.5);
+ // The simplified version is derived from PostGIS function ST_SIMPLIFY()
+ t.ok(simplified2.toString() === 'LINESTRING(0 0,1.8 3.8,5 5,0 0)', 'LineString 2 was simplified correctly');
+ var simplified3 = ls3.simplify(0.5);
+ t.ok(simplified3.toString() === ls3.toString(), 'LineString with 2 vertices is left untouched');
+ var simplified5 = ls5.simplify(0.0);
+ t.ok(simplified5.toString() === 'LINESTRING(0 0,5 5)', 'A tolerance of 0 returns the optimized version needless vertices');
+ var simplified6 = ls6.simplify(0.0);
+ t.ok(simplified6.toString() === 'LINESTRING(0 0,1 1,3 2)', 'A tolerance of 0 returns the optimized version without doubled vertices');
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Geometry/LinearRing.html b/misc/openlayers/tests/Geometry/LinearRing.html
new file mode 100644
index 0000000..cbbba2a
--- /dev/null
+++ b/misc/openlayers/tests/Geometry/LinearRing.html
@@ -0,0 +1,362 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var line;
+ var components = [new OpenLayers.Geometry.Point(10,10),
+ new OpenLayers.Geometry.Point(0,0)];
+
+ function test_LinearRing_constructor (t) {
+ t.plan( 6 );
+
+ //null
+ ring = new OpenLayers.Geometry.LinearRing();
+ t.ok( ring instanceof OpenLayers.Geometry.LinearRing, "new OpenLayers.Geometry.LinearRing returns ring object" );
+ t.eq( ring.CLASS_NAME, "OpenLayers.Geometry.LinearRing", "ring.CLASS_NAME is set correctly");
+ t.eq( ring.components, [], "ring.components is set correctly");
+
+ //valid components
+ ring = new OpenLayers.Geometry.LinearRing(components);
+ t.ok( ring instanceof OpenLayers.Geometry.LinearRing, "new OpenLayers.Geometry.LinearRing returns ring object" );
+ t.eq( ring.CLASS_NAME, "OpenLayers.Geometry.LinearRing", "ring.CLASS_NAME is set correctly");
+ t.eq( ring.components.length, 3, "ring.components.length is set correctly");
+ }
+
+ function test_LinearRing_addComponent(t) {
+ t.plan(13);
+
+ var ring = new OpenLayers.Geometry.LinearRing();
+
+ var point = new OpenLayers.Geometry.Point(0,0);
+ t.ok(ring.addComponent(point),
+ "addComponent returns true for 1st point");
+ t.eq(ring.components.length, 2, "add first point, correct length");
+ t.ok(ring.components[0].equals(point), "point one correct");
+ t.ok(ring.components[0] === ring.components[ring.components.length - 1],
+ "first and last point are the same");
+
+ newPoint = new OpenLayers.Geometry.Point(10,10);
+ t.ok(ring.addComponent( newPoint ),
+ "addComponent returns true for unique point");
+ t.eq(ring.components.length, 3, "correctly adds 3rd point");
+ t.ok(ring.components[0].equals(point), "point one correct");
+ t.ok(ring.components[1].equals(newPoint), "point one correct");
+ t.ok(ring.components[0] === ring.components[ring.components.length - 1],
+ "first and last point are the same");
+
+ var length = ring.components.length;
+ var clone = ring.components[length - 1].clone();
+ t.ok(!ring.addComponent(clone),
+ "addComponent returns false for adding a duplicate last point");
+ t.eq(ring.components.length, length,
+ "components remains unchanged after trying to add duplicate point");
+ t.ok(ring.addComponent(clone, length - 1),
+ "addComponent returns true when adding a duplicate with an index");
+ t.eq(ring.components.length, length + 1,
+ "components increase in length after adding a duplicate point with index");
+
+ }
+
+ function test_LinearRing_removeComponent(t) {
+ t.plan(10);
+
+ var components = [new OpenLayers.Geometry.Point(0,0),
+ new OpenLayers.Geometry.Point(0,10),
+ new OpenLayers.Geometry.Point(15,15),
+ new OpenLayers.Geometry.Point(10,0)
+ ];
+ var ring = new OpenLayers.Geometry.LinearRing(components);
+
+ ring.removeComponent( ring.components[2] );
+ t.eq(ring.components.length, 4, "removing from linear ring with 5 points: length ok");
+ t.ok(ring.components[0].equals(components[0]), "point one correct");
+ t.ok(ring.components[1].equals(components[1]), "point two correct");
+ t.ok(ring.components[2].equals(components[3]), "point three correct");
+ t.ok(ring.components[0] === ring.components[ring.components.length - 1],
+ "first and last point are the same");
+
+ var testBounds = new OpenLayers.Bounds(0,0,10,10);
+ var ringBounds = ring.getBounds();
+ t.ok(ringBounds.equals(testBounds), "bounds correctly recalculated");
+
+ ring.removeComponent( ring.components[2] );
+ ring.removeComponent( ring.components[1] );
+ t.eq(ring.components.length, 3, "cant remove from linear ring with only 3 points. new length ok");
+ t.ok(ring.components[0].equals(components[0]), "point one correct");
+ t.ok(ring.components[1].equals(components[1]), "point two correct");
+ t.ok(ring.components[0] === ring.components[ring.components.length - 1],
+ "first and last point are the same");
+
+ }
+
+ function test_LinearRing_getArea(t) {
+ t.plan(1);
+ var components = [new OpenLayers.Geometry.Point(0,0),
+ new OpenLayers.Geometry.Point(0,10),
+ new OpenLayers.Geometry.Point(10,10),
+ new OpenLayers.Geometry.Point(10,0)
+ ];
+ var ring = new OpenLayers.Geometry.LinearRing(components);
+
+ t.eq(ring.getArea(), 100, "getArea works lovely");
+ }
+
+ function test_LinearRing_getLength(t) {
+ t.plan(1);
+ var components = [
+ new OpenLayers.Geometry.Point(0,0),
+ new OpenLayers.Geometry.Point(0,10),
+ new OpenLayers.Geometry.Point(10,10),
+ new OpenLayers.Geometry.Point(10,0)
+ ];
+ var ring = new OpenLayers.Geometry.LinearRing(components);
+ t.eq(ring.getLength(), 40, "getLength returns the correct perimiter");
+ }
+
+ function test_LinearRing_getCentroid(t) {
+ t.plan(2);
+ var components = [
+ new OpenLayers.Geometry.Point(0,0),
+ new OpenLayers.Geometry.Point(0,10),
+ new OpenLayers.Geometry.Point(10,10),
+ new OpenLayers.Geometry.Point(10,0)
+ ];
+ var ring = new OpenLayers.Geometry.LinearRing(components);
+ var centroid = ring.getCentroid();
+ t.ok(centroid.x === 5 && centroid.y === 5, "getCentroid returns the correct centroid");
+ ring.destroy();
+
+ ring = new OpenLayers.Geometry.LinearRing();
+ t.eq(ring.getCentroid(), null, "getCentroid returns null if no components");
+ }
+
+ function test_LinearRing_move(t) {
+
+ var nvert = 4,
+ x = new Array(nvert),
+ y = new Array(nvert),
+ components = new Array(nvert);
+
+ t.plan(2 * (nvert + 1));
+
+ for(var i=0; i<nvert; ++i) {
+ x[i] = Math.random();
+ y[i] = Math.random();
+ components[i] = new OpenLayers.Geometry.Point(x[i], y[i]);
+ }
+ x.push(x[0]);
+ y.push(y[0]);
+
+ var ring = new OpenLayers.Geometry.LinearRing(components);
+
+ var dx = Math.random();
+ var dy = Math.random();
+
+ ring.move(dx, dy);
+
+ for(var j=0; j<nvert + 1; ++j) {
+ t.eq(ring.components[j].x, x[j] + dx,
+ "move correctly adjust x coord of " + j + " component");
+ t.eq(ring.components[j].y, y[j] + dy,
+ "move correctly adjust y coord of " + j + " component");
+ }
+ }
+
+ function test_LinearRing_rotate(t) {
+ t.plan(10);
+
+ var components = [
+ new OpenLayers.Geometry.Point(10,10),
+ new OpenLayers.Geometry.Point(11,10),
+ new OpenLayers.Geometry.Point(11,11),
+ new OpenLayers.Geometry.Point(10,11)
+ ];
+
+ var ring = new OpenLayers.Geometry.LinearRing(components);
+
+ // rotate a quarter turn around the origin
+ var origin = new OpenLayers.Geometry.Point(0, 0);
+ var angle = 90;
+
+ ring.rotate(angle, origin);
+
+ function withinTolerance(i, j) {
+ return Math.abs(i - j) < 1e-9;
+ }
+
+ t.ok(withinTolerance(ring.components[0].x , -10),
+ "rotate correctly adjusts x of component 0");
+ t.ok(withinTolerance(ring.components[0].y, 10),
+ "rotate correctly adjusts y of component 0");
+ t.ok(withinTolerance(ring.components[1].x, -10),
+ "rotate correctly adjusts x of component 1");
+ t.ok(withinTolerance(ring.components[1].y, 11),
+ "rotate correctly adjusts y of component 1");
+ t.ok(withinTolerance(ring.components[2].x, -11),
+ "rotate correctly adjusts x of component 2");
+ t.ok(withinTolerance(ring.components[2].y, 11),
+ "rotate correctly adjusts y of component 2");
+ t.ok(withinTolerance(ring.components[3].x, -11),
+ "rotate correctly adjusts x of component 3");
+ t.ok(withinTolerance(ring.components[3].y, 10),
+ "rotate correctly adjusts y of component 3");
+ t.ok(withinTolerance(ring.components[4].x, -10),
+ "rotate correctly adjusts x of component 4");
+ t.ok(withinTolerance(ring.components[4].y, 10),
+ "rotate correctly adjusts y of component 4");
+ }
+
+ function test_LinearRing_resize(t) {
+ t.plan(10);
+
+ var components = [
+ new OpenLayers.Geometry.Point(10,10),
+ new OpenLayers.Geometry.Point(11,10),
+ new OpenLayers.Geometry.Point(11,11),
+ new OpenLayers.Geometry.Point(10,11)
+ ];
+
+ var ring = new OpenLayers.Geometry.LinearRing(components);
+
+ // rotate a quarter turn around the origin
+ var origin = new OpenLayers.Geometry.Point(0, 0);
+ var scale = Math.random();
+
+ ring.resize(scale, origin);
+
+ function withinTolerance(i, j) {
+ return Math.abs(i - j) < 1e-9;
+ }
+
+ t.ok(withinTolerance(ring.components[0].x , 10 * scale),
+ "resize correctly adjusts x of component 0");
+ t.ok(withinTolerance(ring.components[0].y, 10 * scale),
+ "resize correctly adjusts y of component 0");
+ t.ok(withinTolerance(ring.components[1].x, 11 * scale),
+ "resize correctly adjusts x of component 1");
+ t.ok(withinTolerance(ring.components[1].y, 10 * scale),
+ "resize correctly adjusts y of component 1");
+ t.ok(withinTolerance(ring.components[2].x, 11 * scale),
+ "resize correctly adjusts x of component 2");
+ t.ok(withinTolerance(ring.components[2].y, 11 * scale),
+ "resize correctly adjusts y of component 2");
+ t.ok(withinTolerance(ring.components[3].x, 10 * scale),
+ "resize correctly adjusts x of component 3");
+ t.ok(withinTolerance(ring.components[3].y, 11 * scale),
+ "resize correctly adjusts y of component 3");
+ t.ok(withinTolerance(ring.components[4].x, 10 * scale),
+ "resize correctly adjusts x of component 4");
+ t.ok(withinTolerance(ring.components[4].y, 10 * scale),
+ "resize correctly adjusts y of component 4");
+ }
+
+ function test_containsPoint(t) {
+
+ /**
+ * The ring:
+ * edge 3
+ * (5, 10) __________ (15, 10)
+ * / /
+ * edge 4 / / edge 2
+ * / /
+ * (0, 0) /_________/ (10, 0)
+ * edge 1
+ */
+ var components = [
+ new OpenLayers.Geometry.Point(0, 0),
+ new OpenLayers.Geometry.Point(10, 0),
+ new OpenLayers.Geometry.Point(15, 10),
+ new OpenLayers.Geometry.Point(5, 10)
+ ];
+
+ var ring = new OpenLayers.Geometry.LinearRing(components);
+
+ function p(x, y) {
+ return new OpenLayers.Geometry.Point(x, y);
+ }
+
+ // contains: 1 (touches), true (within), false (outside)
+ var cases = [{
+ point: p(5, 5), contains: true
+ }, {
+ point: p(20, 20), contains: false
+ }, {
+ point: p(15, 15), contains: false
+ }, {
+ point: p(0, 0), contains: 1 // lower left corner
+ }, {
+ point: p(10, 0), contains: 1 // lower right corner
+ }, {
+ point: p(15, 10), contains: 1 // upper right corner
+ }, {
+ point: p(5, 10), contains: 1 // upper left corner
+ }, {
+ point: p(5, 0), contains: 1 // on edge 1
+ }, {
+ point: p(5, -0.1), contains: false // below edge 1
+ }, {
+ point: p(5, 0.1), contains: true // above edge 1
+ }, {
+ point: p(12.5, 5), contains: 1 // on edge 2
+ }, {
+ point: p(12.4, 5), contains: true // left of edge 2
+ }, {
+ point: p(12.6, 5), contains: false // right of edge 2
+ }, {
+ point: p(10, 10), contains: 1 // on edge 3
+ }, {
+ point: p(10, 9.9), contains: true // below edge 3
+ }, {
+ point: p(10, 10.1), contains: false // above edge 3
+ }, {
+ point: p(2.5, 5), contains: 1 // on edge 4
+ }, {
+ point: p(2.4, 5), contains: false // left of edge 4
+ }, {
+ point: p(2.6, 5), contains: true // right of edge 4
+ }];
+
+ var len = cases.length;
+ t.plan(len);
+ var c;
+ for (var i=0; i<len; ++i) {
+ c = cases[i];
+ t.eq(ring.containsPoint(c.point), c.contains, "case " + i + ": " + c.point);
+ }
+ }
+
+ function test_containsPoint_precision(t) {
+
+ /**
+ * The test for linear ring containment was sensitive to failure when
+ * looking for ray crossings on nearly vertical edges. With a loss
+ * of precision in calculating the x-coordinate for the crossing,
+ * the method would erronously determine that the x-coordinate was
+ * not within the (very narrow) x-range of the nearly vertical edge.
+ *
+ * The test below creates a polygon whose first vertical edge is
+ * nearly horizontal. The test point lies "far" outside the polygon
+ * and we expect the containsPoint method to return false.
+ */
+
+ t.plan(1);
+
+ var components = [
+ new OpenLayers.Geometry.Point(10000020.000001, 1000000),
+ new OpenLayers.Geometry.Point(10000020.000002, 1000010), // nearly vertical
+ new OpenLayers.Geometry.Point(10000030, 1000010),
+ new OpenLayers.Geometry.Point(10000030, 1000000)
+ ];
+
+ var ring = new OpenLayers.Geometry.LinearRing(components);
+ var point = new OpenLayers.Geometry.Point(10000000, 1000001);
+
+ t.eq(ring.containsPoint(point), false, "false for point outside polygon with nearly vertical edge");
+
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Geometry/MultiLineString.html b/misc/openlayers/tests/Geometry/MultiLineString.html
new file mode 100644
index 0000000..34a6e65
--- /dev/null
+++ b/misc/openlayers/tests/Geometry/MultiLineString.html
@@ -0,0 +1,267 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var line;
+
+ function test_MultiLineString_constructor (t) {
+ t.plan( 3 );
+ mline = new OpenLayers.Geometry.MultiLineString();
+ t.ok( mline instanceof OpenLayers.Geometry.MultiLineString, "new OpenLayers.Geometry.MultiLineString returns mline object" );
+ t.eq( mline.CLASS_NAME, "OpenLayers.Geometry.MultiLineString", "mline.CLASS_NAME is set correctly");
+ t.eq( mline.components, [], "line.components is set correctly");
+ }
+
+ function test_MultiLineString_constructor (t) {
+ t.plan( 3 );
+ line = new OpenLayers.Geometry.LineString();
+ mline = new OpenLayers.Geometry.MultiLineString(line);
+ t.ok( mline instanceof OpenLayers.Geometry.MultiLineString, "new OpenLayers.Geometry.MultiLineString returns mline object" );
+ t.eq( mline.CLASS_NAME, "OpenLayers.Geometry.MultiLineString", "mline.CLASS_NAME is set correctly");
+ t.eq( mline.components.length, 1, "mline.components.length is set correctly");
+ }
+
+ function test_split(t) {
+ var wkt = OpenLayers.Geometry.fromWKT;
+
+ var cases = [{
+ msg: "no intersection",
+ g1: "MULTILINESTRING((0 0, 0 1), (2 2, 3 3))",
+ g2: "MULTILINESTRING((1 0, 1 1), (2 2, 3 2))",
+ exp: null
+ } , {
+ msg: "intersection at midpoint",
+ g1: "MULTILINESTRING((0 0, 1 1))",
+ g2: "MULTILINESTRING((1 0, 0 1))",
+ exp: ["MULTILINESTRING((1 0, 0.5 0.5))", "MULTILINESTRING((0.5 0.5, 0 1))"]
+ }, {
+ msg: "intersection at midpoint (reverse source/target)",
+ g1: "MULTILINESTRING((1 0, 0 1))",
+ g2: "MULTILINESTRING((0 0, 1 1))",
+ exp: ["MULTILINESTRING((0 0, 0.5 0.5))", "MULTILINESTRING((0.5 0.5, 1 1))"]
+ }, {
+ msg: "intersection at endpoint",
+ g1: "MULTILINESTRING((0 0, 1 1))",
+ g2: "MULTILINESTRING((1 0, 1 1))",
+ exp: null
+ }, {
+ msg: "midpoint intersection, no options",
+ g1: "MULTILINESTRING((0 0, 2 2))",
+ g2: "MULTILINESTRING((0 2, 2 0))",
+ exp: ["MULTILINESTRING((0 2, 1 1))", "MULTILINESTRING((1 1, 2 0))"]
+ }, {
+ msg: "midpoint intersection, edge false",
+ opt: {edge: false},
+ g1: "MULTILINESTRING((0 0, 2 2))",
+ g2: "MULTILINESTRING((0 2, 2 0))",
+ exp: null
+ }, {
+ msg: "midpoint intersection, mutual",
+ opt: {mutual: true},
+ g1: "MULTILINESTRING((0 0, 2 2))",
+ g2: "MULTILINESTRING((0 2, 2 0))",
+ exp: [["MULTILINESTRING((0 0, 1 1))", "MULTILINESTRING((1 1, 2 2))"], ["MULTILINESTRING((0 2, 1 1))", "MULTILINESTRING((1 1, 2 0))"]]
+ }, {
+ msg: "close intersection, no tolerance",
+ g1: "MULTILINESTRING((0 0, 0.9 0.9))",
+ g2: "MULTILINESTRING((0 2, 2 0))",
+ exp: null
+ }, {
+ msg: "close intersection, within tolerance",
+ opt: {tolerance: 0.2},
+ g1: "MULTILINESTRING((0 0, 0.9 0.9))",
+ g2: "MULTILINESTRING((0 2, 2 0))",
+ exp: ["MULTILINESTRING((0 2, 0.9 0.9))", "MULTILINESTRING((0.9 0.9, 2 0))"]
+ }, {
+ msg: "multi source, single target",
+ g1: "MULTILINESTRING((0 0, 2 2))",
+ g2: "LINESTRING(0 2, 2 0)",
+ exp: ["LINESTRING(0 2, 1 1)", "LINESTRING(1 1, 2 0)"]
+ }, {
+ msg: "multi source, single target, mutual split",
+ opt: {mutual: true},
+ g1: "MULTILINESTRING((0 0, 2 2))",
+ g2: "LINESTRING(0 2, 2 0)",
+ exp: [["MULTILINESTRING((0 0, 1 1))", "MULTILINESTRING((1 1, 2 2))"], ["LINESTRING(0 2, 1 1)", "LINESTRING(1 1, 2 0)"]]
+ }, {
+ msg: "single source, multi target",
+ g1: "LINESTRING(0 2, 2 0)",
+ g2: "MULTILINESTRING((0 0, 2 2))",
+ exp: ["MULTILINESTRING((0 0, 1 1))", "MULTILINESTRING((1 1, 2 2))"]
+ }, {
+ msg: "partial target split",
+ g1: "MULTILINESTRING((2 0, 0 2))",
+ g2: "MULTILINESTRING((0 0, 2 2), (3 3, 4 4))",
+ exp: ["MULTILINESTRING((0 0, 1 1))", "MULTILINESTRING((1 1, 2 2), (3 3, 4 4))"]
+ }, {
+ msg: "partial target split, mutual true",
+ opt: {mutual: true},
+ g1: "MULTILINESTRING((2 0, 0 2))",
+ g2: "MULTILINESTRING((0 0, 2 2), (3 3, 4 4))",
+ exp: [["MULTILINESTRING((2 0, 1 1))", "MULTILINESTRING((1 1, 0 2))"], ["MULTILINESTRING((0 0, 1 1))", "MULTILINESTRING((1 1, 2 2), (3 3, 4 4))"]]
+ }, {
+ msg: "partial source split, mutual true",
+ opt: {mutual: true},
+ g1: "MULTILINESTRING((0 0, 2 2), (3 3, 4 4))",
+ g2: "MULTILINESTRING((2 0, 0 2))",
+ exp: [["MULTILINESTRING((0 0, 1 1))", "MULTILINESTRING((1 1, 2 2), (3 3, 4 4))"], ["MULTILINESTRING((2 0, 1 1))", "MULTILINESTRING((1 1, 0 2))"]]
+ }, {
+ msg: "partial target split with source endpoint",
+ g1: "MULTILINESTRING((1 0, 1 1))",
+ g2: "MULTILINESTRING((0 0, 2 2), (3 3, 4 4))",
+ exp: ["MULTILINESTRING((0 0, 1 1))", "MULTILINESTRING((1 1, 2 2), (3 3, 4 4))"]
+ }, {
+ msg: "partial target split with source endpoint, mutual true",
+ opt: {mutual: true},
+ g1: "MULTILINESTRING((5 5, 6 6), (1 0, 1 1))",
+ g2: "MULTILINESTRING((0 0, 2 2), (3 3, 4 4))",
+ exp: [[], ["MULTILINESTRING((0 0, 1 1))", "MULTILINESTRING((1 1, 2 2), (3 3, 4 4))"]]
+ }, {
+ msg: "partial source split with target endpoint",
+ g1: "MULTILINESTRING((0 0, 2 2), (3 3, 4 4))",
+ g2: "MULTILINESTRING((1 0, 1 1))",
+ exp: null
+ }, {
+ msg: "partial source split with target endpoint, mutual true",
+ opt: {mutual: true},
+ g1: "MULTILINESTRING((0 0, 2 2), (3 3, 4 4), (5 5, 6 6))",
+ g2: "MULTILINESTRING((1 0, 1 1))",
+ exp: [["MULTILINESTRING((0 0, 1 1))", "MULTILINESTRING((1 1, 2 2), (3 3, 4 4), (5 5, 6 6))"], []]
+ }, {
+ msg: "partial target and source split",
+ g1: "MULTILINESTRING((0 5, 2 5), (4 5, 6 5), (8 5, 10 5))",
+ g2: "MULTILINESTRING((5 0, 5 2), (5 4, 5 6), (5 8, 5 10))",
+ exp: ["MULTILINESTRING((5 0, 5 2), (5 4, 5 5))", "MULTILINESTRING((5 5, 5 6), (5 8, 5 10))"]
+ }, {
+ msg: "partial target and source split, mutual true",
+ opt: {mutual: true},
+ g1: "MULTILINESTRING((0 5, 2 5), (4 5, 6 5), (8 5, 10 5))",
+ g2: "MULTILINESTRING((5 0, 5 2), (5 4, 5 6), (5 8, 5 10))",
+ exp: [["MULTILINESTRING((0 5, 2 5), (4 5, 5 5))", "MULTILINESTRING((5 5, 6 5), (8 5, 10 5))"],
+ ["MULTILINESTRING((5 0, 5 2), (5 4, 5 5))", "MULTILINESTRING((5 5, 5 6), (5 8, 5 10))"]]
+ }, {
+ msg: "partial target and source split with source endpoint, mutual true",
+ opt: {mutual: true},
+ g1: "MULTILINESTRING((0 5, 2 5), (4 5, 6 5), (8 5, 10 5))",
+ g2: "MULTILINESTRING((4 0, 4 2), (4 4, 4 6), (4 8, 4 10))",
+ exp: [[], ["MULTILINESTRING((4 0, 4 2), (4 4, 4 5))", "MULTILINESTRING((4 5, 4 6), (4 8, 4 10))"]]
+ }, {
+ msg: "partial target and source split with target endpoint, mutual true",
+ opt: {mutual: true},
+ g1: "MULTILINESTRING((4 0, 4 2), (4 4, 4 6), (4 8, 4 10))",
+ g2: "MULTILINESTRING((0 5, 2 5), (4 5, 6 5), (8 5, 10 5))",
+ exp: [["MULTILINESTRING((4 0, 4 2), (4 4, 4 5))", "MULTILINESTRING((4 5, 4 6), (4 8, 4 10))"], []]
+ }, {
+ msg: "partial target and source split with source vertex, mutual true",
+ opt: {mutual: true},
+ g1: "MULTILINESTRING((0 5, 2 5), (4 5, 5 5, 6 5), (8 5, 10 5))",
+ g2: "MULTILINESTRING((5 0, 5 2), (5 4, 5 6), (5 8, 5 10))",
+ exp: [["MULTILINESTRING((0 5, 2 5), (4 5, 5 5))", "MULTILINESTRING((5 5, 6 5), (8 5, 10 5))"], ["MULTILINESTRING((5 0, 5 2), (5 4, 5 5))", "MULTILINESTRING((5 5, 5 6), (5 8, 5 10))"]]
+ }, {
+ msg: "partial target and source split with target vertex, mutual true",
+ opt: {mutual: true},
+ g1: "MULTILINESTRING((5 0, 5 2), (5 4, 5 6), (5 8, 5 10))",
+ g2: "MULTILINESTRING((0 5, 2 5), (4 5, 5 5, 6 5), (8 5, 10 5))",
+ exp: [["MULTILINESTRING((5 0, 5 2), (5 4, 5 5))", "MULTILINESTRING((5 5, 5 6), (5 8, 5 10))"], ["MULTILINESTRING((0 5, 2 5), (4 5, 5 5))", "MULTILINESTRING((5 5, 6 5), (8 5, 10 5))"]]
+ }];
+
+
+ t.plan(cases.length);
+ var c, parts, part, midparts;
+ for(var i=0; i<cases.length; ++i) {
+ c = cases[i];
+ var g1 = wkt(c.g1);
+ var g2 = wkt(c.g2);
+ var got = g1.split(g2, c.opt);
+ var exp = c.exp;
+ if(got instanceof Array) {
+ parts = [];
+ for(var j=0; j<got.length; ++j) {
+ part = got[j];
+ if(part instanceof Array) {
+ midparts = [];
+ for(var k=0; k<part.length; ++k) {
+ midparts.push(part[k].toString());
+ }
+ parts.push("[" + midparts.join(", ") + "]");
+ } else {
+ parts.push(got[j].toString());
+ }
+ }
+ got = parts.join(", ");
+ }
+ if(exp instanceof Array) {
+ parts = [];
+ for(var j=0; j<exp.length; ++j) {
+ part = exp[j];
+ if(part instanceof Array) {
+ midparts = [];
+ for(var k=0; k<part.length; ++k) {
+ midparts.push(wkt(part[k]).toString());
+ }
+ parts.push("[" + midparts.join(", ") + "]");
+ } else {
+ parts.push(wkt(exp[j]).toString());
+ }
+ }
+ exp = parts.join(", ");
+ }
+ t.eq(got, exp, "case " + i + ": " + c.msg);
+ }
+
+ }
+
+ function test_getVertices(t) {
+ t.plan(22);
+
+ var points = [
+ new OpenLayers.Geometry.Point(10, 20),
+ new OpenLayers.Geometry.Point(20, 30),
+ new OpenLayers.Geometry.Point(30, 40),
+ new OpenLayers.Geometry.Point(40, 50)
+ ];
+
+ var multi = new OpenLayers.Geometry.MultiLineString([
+ new OpenLayers.Geometry.LineString(points),
+ new OpenLayers.Geometry.LineString(points)
+ ]);
+
+ var verts = multi.getVertices();
+ t.ok(verts instanceof Array, "got back an array");
+ t.eq(verts.length, 2 * points.length, "of correct length length");
+ t.geom_eq(verts[0], points[0], "0: correct geometry");
+ t.geom_eq(verts[1], points[1], "1: correct geometry");
+ t.geom_eq(verts[2], points[2], "2: correct geometry");
+ t.geom_eq(verts[3], points[3], "3: correct geometry");
+ t.geom_eq(verts[4], points[0], "4: correct geometry");
+ t.geom_eq(verts[5], points[1], "5: correct geometry");
+ t.geom_eq(verts[6], points[2], "6: correct geometry");
+ t.geom_eq(verts[7], points[3], "7: correct geometry");
+
+ // nodes only
+ var nodes = multi.getVertices(true);
+ t.ok(nodes instanceof Array, "[nodes only] got back an array");
+ t.eq(nodes.length, 4, "[nodes only] of correct length length");
+ t.geom_eq(nodes[0], points[0], "[nodes only] 0: correct geometry");
+ t.geom_eq(nodes[1], points[3], "[nodes only] 1: correct geometry");
+ t.geom_eq(nodes[2], points[0], "[nodes only] 2: correct geometry");
+ t.geom_eq(nodes[3], points[3], "[nodes only] 3: correct geometry");
+
+ // no nodes
+ var nodes = multi.getVertices(false);
+ t.ok(nodes instanceof Array, "[no nodes] got back an array");
+ t.eq(nodes.length, 4, "[no nodes] of correct length length");
+ t.geom_eq(nodes[0], points[1], "[no nodes] 0: correct geometry");
+ t.geom_eq(nodes[1], points[2], "[no nodes] 1: correct geometry");
+ t.geom_eq(nodes[2], points[1], "[no nodes] 2: correct geometry");
+ t.geom_eq(nodes[3], points[2], "[no nodes] 3: correct geometry");
+
+
+ }
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Geometry/MultiPoint.html b/misc/openlayers/tests/Geometry/MultiPoint.html
new file mode 100644
index 0000000..47ce430
--- /dev/null
+++ b/misc/openlayers/tests/Geometry/MultiPoint.html
@@ -0,0 +1,130 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var point = new OpenLayers.Geometry.Point(10, 15);
+
+
+ function test_MultiPoint_constructor (t) {
+ t.plan( 2 );
+ var multipoint = new OpenLayers.Geometry.MultiPoint();
+ t.ok( multipoint instanceof OpenLayers.Geometry.MultiPoint, "new OpenLayers.Geometry.MultiPoint returns multipoint object" );
+ t.eq( multipoint.CLASS_NAME, "OpenLayers.Geometry.MultiPoint", "multipoint.CLASS_NAME is set correctly");
+ }
+
+ function test_MultiPoint_constructor (t) {
+ t.plan( 3 );
+ var multipoint = new OpenLayers.Geometry.MultiPoint([point]);
+ t.ok( multipoint instanceof OpenLayers.Geometry.MultiPoint, "new OpenLayers.Geometry.MultiPoint returns multipoint object" );
+ t.eq( multipoint.CLASS_NAME, "OpenLayers.Geometry.MultiPoint", "multipoint.CLASS_NAME is set correctly");
+ t.eq( multipoint.components.length, 1, "multipolygon.components.length is set correctly");
+ }
+
+ function test_MultiPoint_move(t) {
+ t.plan(2);
+
+ var multipoint = new OpenLayers.Geometry.MultiPoint([point]);
+ var x = point.x;
+ var y = point.y;
+
+ var dx = 10 * Math.random();
+ var dy = 10 * Math.random();
+ multipoint.move(dx, dy);
+ t.eq(multipoint.components[0].x, x + dx, "move() correctly modifies x");
+ t.eq(multipoint.components[0].y, y + dy, "move() correctly modifies y");
+ }
+
+ function test_distanceTo(t) {
+ var points = [
+ new OpenLayers.Geometry.Point(0, 0),
+ new OpenLayers.Geometry.Point(10, 0),
+ new OpenLayers.Geometry.Point(0, 9),
+ new OpenLayers.Geometry.Point(-5, 0),
+ new OpenLayers.Geometry.Point(-5, 4)
+ ];
+
+ var geoms = [
+ new OpenLayers.Geometry.MultiPoint([points[0], points[1]]),
+ new OpenLayers.Geometry.MultiPoint([points[2], points[3]]),
+ points[4]
+ ];
+
+ var cases = [{
+ got: geoms[0].distanceTo(geoms[0]),
+ expected: 0
+ }, {
+ got: geoms[0].distanceTo(geoms[1]),
+ expected: 5
+ }, {
+ got: geoms[1].distanceTo(geoms[2]),
+ expected: 4
+ }, {
+ got: geoms[0].distanceTo(geoms[1], {details: true}),
+ expected: {
+ distance: 5,
+ x0: 0, y0: 0,
+ x1: -5, y1: 0
+ }
+ }, {
+ got: geoms[1].distanceTo(geoms[0], {details: true}),
+ expected: {
+ distance: 5,
+ x0: -5, y0: 0,
+ x1: 0, y1: 0
+ }
+ }, {
+ got: geoms[1].distanceTo(geoms[2], {details: true}),
+ expected: {
+ distance: 4,
+ x0: -5, y0: 0,
+ x1: -5, y1: 4
+ }
+ }];
+
+ t.plan(cases.length);
+ for(var i=0; i<cases.length; ++i) {
+ t.eq(cases[i].got, cases[i].expected, "case " + i);
+ }
+
+ }
+
+ function test_MultiPoint_equals(t) {
+ t.plan(3);
+
+ var x = Math.random() * 100;
+ var y = Math.random() * 100;
+ var geometry = new OpenLayers.Geometry.MultiPoint(
+ [new OpenLayers.Geometry.Point(x, y)]);
+ var equal = new OpenLayers.Geometry.MultiPoint(
+ [new OpenLayers.Geometry.Point(x, y)]);
+ var offX = new OpenLayers.Geometry.MultiPoint(
+ [new OpenLayers.Geometry.Point(x + 1, y)]);
+ var offY = new OpenLayers.Geometry.MultiPoint(
+ [new OpenLayers.Geometry.Point(x, y + 1)]);
+ t.ok(geometry.equals(equal),
+ "equals() returns true for a geometry with equivalent coordinates");
+ t.ok(!geometry.equals(offX),
+ "equals() returns false for a geometry with offset x");
+ t.ok(!geometry.equals(offY),
+ "equals() returns false for a geometry with offset y");
+ }
+
+ function test_MultiPoint_clone(t) {
+ t.plan(2);
+
+ var x = Math.random() * 100;
+ var y = Math.random() * 100;
+ var geometry = new OpenLayers.Geometry.MultiPoint(
+ [new OpenLayers.Geometry.Point(x, y)]);
+ var clone = geometry.clone();
+ t.ok(clone instanceof OpenLayers.Geometry.MultiPoint,
+ "clone() creates an OpenLayers.Geometry.MultiPoint");
+ t.ok(geometry.equals(clone), "clone has equivalent coordinates");
+ }
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Geometry/MultiPolygon.html b/misc/openlayers/tests/Geometry/MultiPolygon.html
new file mode 100644
index 0000000..f44de93
--- /dev/null
+++ b/misc/openlayers/tests/Geometry/MultiPolygon.html
@@ -0,0 +1,34 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var polygon;
+ var components = [new OpenLayers.Geometry.Point(10,10), new OpenLayers.Geometry.Point(0,0)];
+ var components2 = [new OpenLayers.Geometry.Point(10,10), new OpenLayers.Geometry.Point(0,0), new OpenLayers.Geometry.Point(10,0), new OpenLayers.Geometry.Point(10,10)];
+ var linearRing = new OpenLayers.Geometry.LinearRing(components);
+ var linearRing2 = new OpenLayers.Geometry.LinearRing(components2);
+
+ var polygon = new OpenLayers.Geometry.Polygon([linearRing]);
+ var polygon2 = new OpenLayers.Geometry.Polygon([linearRing2]);
+
+ function test_MultiPolygon_constructor (t) {
+ t.plan( 2 );
+ multipolygon = new OpenLayers.Geometry.MultiPolygon();
+ t.ok( multipolygon instanceof OpenLayers.Geometry.MultiPolygon, "new OpenLayers.Geometry.MultiPolygon returns multipolygon object" );
+ t.eq( multipolygon.CLASS_NAME, "OpenLayers.Geometry.MultiPolygon", "multipolygon.CLASS_NAME is set correctly");
+ }
+
+ function test_MultiPolygon_constructor (t) {
+ t.plan( 3 );
+ multipolygon = new OpenLayers.Geometry.MultiPolygon([polygon, polygon2]);
+ t.ok( multipolygon instanceof OpenLayers.Geometry.MultiPolygon, "new OpenLayers.Geometry.MultiPolygon returns multipolygon object" );
+ t.eq( multipolygon.CLASS_NAME, "OpenLayers.Geometry.MultiPolygon", "multipolygon.CLASS_NAME is set correctly");
+ t.eq( multipolygon.components.length, 2, "multipolygon.components.length is set correctly");
+ }
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Geometry/Point.html b/misc/openlayers/tests/Geometry/Point.html
new file mode 100644
index 0000000..e688250
--- /dev/null
+++ b/misc/openlayers/tests/Geometry/Point.html
@@ -0,0 +1,244 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var point;
+
+ function test_Point_constructor (t) {
+ t.plan( 8 );
+
+ //empty
+ point = new OpenLayers.Geometry.Point();
+ t.ok( point instanceof OpenLayers.Geometry.Point, "new OpenLayers.Geometry.Point returns point object" );
+ t.eq( point.CLASS_NAME, "OpenLayers.Geometry.Point", "point.CLASS_NAME is set correctly");
+
+ //valid
+ var x = 10;
+ var y = 20;
+ point = new OpenLayers.Geometry.Point(x, y);
+ t.ok( point instanceof OpenLayers.Geometry.Point, "new OpenLayers.Geometry.Point returns point object" );
+ t.eq( point.CLASS_NAME, "OpenLayers.Geometry.Point", "point.CLASS_NAME is set correctly");
+ t.eq( point.x, x, "point.x is set correctly");
+ t.eq( point.y, y, "point.y is set correctly");
+ t.eq( point.lon, null, "point.lon is not set");
+ t.eq( point.lat, null, "point.lat is not set");
+ }
+
+ function test_Point_calculateBounds (t) {
+ t.plan(4);
+
+ var x = 10;
+ var y = 20;
+ point = new OpenLayers.Geometry.Point(x, y);
+ point.calculateBounds();
+ t.eq( point.bounds.left, x, "bounds.left is 10" );
+ t.eq( point.bounds.right, x, "bounds.right is 10" );
+ t.eq( point.bounds.top, y, "bounds.top is 20" );
+ t.eq( point.bounds.bottom, y, "bounds.bottom is 20" );
+ }
+
+
+ function test_Point_transform_getBounds (t) {
+ t.plan(2);
+
+ var x = 10;
+ var y = 20;
+ point = new OpenLayers.Geometry.Point(x, y);
+ point.calculateBounds();
+ t.ok( point.bounds != null, "bounds calculated by calcBounds" );
+ point.transform(new OpenLayers.Projection("EPSG:4326"),
+ new OpenLayers.Projection("EPSG:900913"));
+ t.eq(point.bounds, null, "Point bounds cleared after transform");
+ }
+
+ function test_Point_transform_string(t) {
+ t.plan(4);
+
+ var x = 10;
+ var y = 20;
+ point = new OpenLayers.Geometry.Point(x, y);
+ point.calculateBounds();
+ t.ok( point.bounds != null, "bounds calculated by calcBounds" );
+ point.transform("EPSG:4326", "EPSG:900913");
+ t.eq(point.bounds, null, "Point bounds cleared after transform");
+ t.eq(point.x.toFixed(2), "1113194.91", "transformed x");
+ t.eq(point.y.toFixed(2), "2273030.93", "transformed y");
+
+ }
+
+ function test_Point_distanceTo(t) {
+ t.plan(7);
+
+ var x1 = 10;
+ var y1 = 20;
+ point1 = new OpenLayers.Geometry.Point(x1, y1);
+
+ var x2 = 100;
+ var y2 = 200;
+ point2 = new OpenLayers.Geometry.Point(x2, y2);
+
+ var dist = point1.distanceTo(point2)
+ t.eq( dist, 201.24611797498107267682563018581, "distances calculating correctly");
+ t.eq( dist, Math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)), "distance calculation correct");
+
+ // test that details are returned (though trivial in this case)
+ var result = point1.distanceTo(point2, {details: true});
+ t.eq(result.distance, point1.distanceTo(point2), "[details] distance property is same as return without details");
+ t.eq(result.x0, x1, "[details] x0 property is correct");
+ t.eq(result.y0, y1, "[details] y0 property is correct");
+ t.eq(result.x1, x2, "[details] x1 property is correct");
+ t.eq(result.y1, y2, "[details] y1 property is correct");
+
+ }
+
+ function test_Point_toString(t) {
+ t.plan(1);
+
+ var x = 10;
+ var y = 20;
+ point = new OpenLayers.Geometry.Point(x, y);
+ t.eq(point.toString(), "POINT(" + x + " " + y + ")",
+ "toString() returns WKT" );
+
+ }
+
+ function test_Point_toString_no_wkt(t) {
+ t.plan(1);
+
+ var WKT = OpenLayers.Format.WKT;
+ OpenLayers.Format.WKT = null;
+
+ var x = 10;
+ var y = 20;
+ point = new OpenLayers.Geometry.Point(x, y);
+ t.eq(point.toString(), "[object Object]", "default string representation");
+
+ OpenLayers.Format.WKT = WKT;
+
+ }
+
+ function test_Point_move(t) {
+ t.plan(3);
+
+ var x = 10;
+ var y = 20;
+ point = new OpenLayers.Geometry.Point(x, y);
+
+ var dx = 10 * Math.random();
+ var dy = 10 * Math.random();
+ point.bounds = "foo";
+ point.move(dx, dy);
+ t.eq(point.x, x + dx, "move() correctly modifies x");
+ t.eq(point.y, y + dy, "move() correctly modifies y");
+
+ t.ok(point.bounds == null, "bounds is cleared after a move()");
+ }
+
+ function test_Point_rotate(t) {
+ t.plan(5);
+
+ var tolerance = 1e-10;
+ var x = 10;
+ var y = 20;
+ var point = new OpenLayers.Geometry.Point(x, y);
+ var origin = new OpenLayers.Geometry.Point(5, 10);
+
+ // rotate a full revolution
+ point.bounds = "foo";
+ point.rotate(360, origin);
+ t.ok(((point.x - x) / x) < tolerance,
+ "rotate by 360 returns to the same y");
+ t.ok(((point.y - y) / y) < tolerance,
+ "rotate by 360 returns to the same y");
+
+ t.ok(point.bounds == null, "bounds is cleared after a rotate()");
+
+ // rotate an 1/8 turn
+ point.rotate(45, origin);
+ t.ok(((point.x - 1.4644660940672636) / 1.4644660940672636) < tolerance,
+ "rotate 1/8 turn correctly");
+ t.ok(((point.y - 20.606601717798213) / 20.606601717798213) < tolerance,
+ "rotate 1/8 turn correctly");
+ }
+
+ function test_Point_resize(t) {
+ t.plan(6);
+
+ var tolerance = 1e-10;
+ var x = 100 * Math.random();
+ var y = 100 * Math.random();
+ var point = new OpenLayers.Geometry.Point(x, y);
+ point.bounds = "foo";
+
+ var i = 100 * Math.random();
+ var j = 100 * Math.random();
+ var origin = new OpenLayers.Geometry.Point(i, j);
+
+ var scale = 10 * Math.random();
+ var oldDistance = origin.distanceTo(point);
+
+ var ret = point.resize(scale, origin);
+ var newDistance = origin.distanceTo(point);
+
+ t.ok(ret === point, "resize returns geometry");
+ t.ok((origin.x == i) && (origin.y == j),
+ "resize leaves the origin untouched");
+ t.ok((((newDistance / oldDistance) - scale) / scale) < tolerance,
+ "resize moves points the correct distance from the origin");
+
+ t.ok(point.bounds == null, "bounds is correctly cleared after a resize()");
+
+ // resize with non uniform scaling (ratio != 1)
+ point = new OpenLayers.Geometry.Point(10, 10);
+ origin = new OpenLayers.Geometry.Point(0, 0);
+ point.resize(2, origin, 4);
+ t.eq(point.x, 80, "non-uniform scaling correctly applied in x dim");
+ t.eq(point.y, 20, "non-uniform scaling correctly applied in y dim");
+
+ }
+
+ function test_Point_equals(t) {
+ t.plan(3);
+
+ var x = Math.random() * 100;
+ var y = Math.random() * 100;
+ var geometry = new OpenLayers.Geometry.Point(x, y);
+ var equal = new OpenLayers.Geometry.Point(x, y);
+ var offX = new OpenLayers.Geometry.Point(x + 1, y);
+ var offY = new OpenLayers.Geometry.Point(x, y + 1);
+ t.ok(geometry.equals(equal),
+ "equals() returns true for a geometry with equivalent coordinates");
+ t.ok(!geometry.equals(offX),
+ "equals() returns false for a geometry with offset x");
+ t.ok(!geometry.equals(offY),
+ "equals() returns false for a geometry with offset y");
+ }
+
+ function test_getVertices(t) {
+ t.plan(3);
+
+ var point = new OpenLayers.Geometry.Point(10, 20);
+ var verts = point.getVertices();
+ t.ok(verts instanceof Array, "got back an array");
+ t.eq(verts.length, 1, "of length 1");
+ t.geom_eq(verts[0], point, "with correct geometry");
+ }
+
+ function test_Point_clone(t) {
+ t.plan(2);
+
+ var x = Math.random() * 100;
+ var y = Math.random() * 100;
+ var geometry = new OpenLayers.Geometry.Point(x, y);
+ var clone = geometry.clone();
+ t.ok(clone instanceof OpenLayers.Geometry.Point,
+ "clone() creates an OpenLayers.Geometry.Point");
+ t.ok(geometry.equals(clone), "clone has equivalent coordinates");
+ }
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Geometry/Polygon.html b/misc/openlayers/tests/Geometry/Polygon.html
new file mode 100644
index 0000000..0df0295
--- /dev/null
+++ b/misc/openlayers/tests/Geometry/Polygon.html
@@ -0,0 +1,420 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var polygon;
+ var components = [new OpenLayers.Geometry.Point(10,14), new OpenLayers.Geometry.Point(5,3)];
+ var components2 = [new OpenLayers.Geometry.Point(12,15), new OpenLayers.Geometry.Point(2,3), new OpenLayers.Geometry.Point(10,0), new OpenLayers.Geometry.Point(10,10)];
+ var linearRing = new OpenLayers.Geometry.LinearRing(components);
+ var linearRing2 = new OpenLayers.Geometry.LinearRing(components2);
+
+ function test_Polygon_constructor (t) {
+ t.plan( 3 );
+ polygon = new OpenLayers.Geometry.Polygon();
+ t.ok( polygon instanceof OpenLayers.Geometry.Polygon, "new OpenLayers.Geometry.Polygon returns polygon object" );
+ t.eq( polygon.CLASS_NAME, "OpenLayers.Geometry.Polygon", "polygon.CLASS_NAME is set correctly");
+ t.eq( polygon.components.length, 0, "polygon.components is set correctly");
+ }
+
+ function test_Polygon_constructor (t) {
+ t.plan( 3 );
+ polygon = new OpenLayers.Geometry.Polygon([linearRing]);
+ t.ok( polygon instanceof OpenLayers.Geometry.Polygon, "new OpenLayers.Geometry.Polygon returns polygon object" );
+ t.eq( polygon.CLASS_NAME, "OpenLayers.Geometry.Polygon", "polygon.CLASS_NAME is set correctly");
+ t.eq( polygon.components.length, 1, "polygon.components.length is set correctly");
+ }
+
+ function test_Polygon_constructor (t) {
+ t.plan( 3 );
+ polygon = new OpenLayers.Geometry.Polygon([linearRing, linearRing2]);
+ t.ok( polygon instanceof OpenLayers.Geometry.Polygon, "new OpenLayers.Geometry.Polygon returns polygon object" );
+ t.eq( polygon.CLASS_NAME, "OpenLayers.Geometry.Polygon", "polygon.CLASS_NAME is set correctly");
+ t.eq( polygon.components.length, 2, "polygon.components.length is set correctly");
+ }
+
+ function test_Polygon_transform_getBounds (t) {
+ t.plan(3);
+
+ var components = [new OpenLayers.Geometry.Point(10,14), new OpenLayers.Geometry.Point(5,3)];
+ var linearRing = new OpenLayers.Geometry.LinearRing(components);
+ polygon = new OpenLayers.Geometry.Polygon([linearRing.clone()]);
+ polygon.calculateBounds();
+ t.ok( polygon.bounds != null, "bounds calculated by calcBounds" );
+ polygon.transform(new OpenLayers.Projection("EPSG:4326"),
+ new OpenLayers.Projection("EPSG:900913"));
+ t.eq(polygon.bounds, null, "Point bounds cleared after transform");
+ t.eq(polygon.getBounds().toBBOX(), "556597.453889,334111.171355,1113194.907778,1574216.547942", "Bounds are correct")
+ }
+
+ function test_Polygon_transform_string (t) {
+ t.plan(3);
+
+ var components = [new OpenLayers.Geometry.Point(10,14), new OpenLayers.Geometry.Point(5,3)];
+ var linearRing = new OpenLayers.Geometry.LinearRing(components);
+ polygon = new OpenLayers.Geometry.Polygon([linearRing.clone()]);
+ polygon.calculateBounds();
+ t.ok( polygon.bounds != null, "bounds calculated by calcBounds" );
+ polygon.transform("EPSG:4326", "EPSG:900913");
+ t.eq(polygon.bounds, null, "Point bounds cleared after transform");
+ t.eq(polygon.getBounds().toBBOX(), "556597.453889,334111.171355,1113194.907778,1574216.547942", "Bounds are correct")
+ }
+
+ function test_Polygon_getArea(t) {
+ t.plan( 5 );
+
+ //no components
+ var polygon = new OpenLayers.Geometry.Polygon();
+ t.eq(polygon.getArea(), 0, "getArea empty polygon is 0");
+
+ var createSquareRing = function(area) {
+ var points = [
+ new OpenLayers.Geometry.Point(0, 0),
+ new OpenLayers.Geometry.Point(0, area),
+ new OpenLayers.Geometry.Point(area, area),
+ new OpenLayers.Geometry.Point(area, 0)
+ ];
+ var ring = new OpenLayers.Geometry.LinearRing(points);
+ return ring;
+ };
+
+
+ //simple polygon
+ var comps = [ createSquareRing(2) ];
+
+ var polygon = new OpenLayers.Geometry.Polygon(comps);
+ t.eq(polygon.getArea(), 4, "getArea simple polygon works lovely");
+
+ //polygon with holes
+ comps = [ createSquareRing(10),
+ createSquareRing(2),
+ createSquareRing(3),
+ createSquareRing(4)
+ ];
+
+ var polygon = new OpenLayers.Geometry.Polygon(comps);
+ t.eq(polygon.getArea(), 71, "getArea polygon with holes works lovely");
+
+ //simple polygon negative
+ comps = [ createSquareRing(-2) ];
+
+ var polygon = new OpenLayers.Geometry.Polygon(comps);
+ t.eq(polygon.getArea(), 4, "getArea simple polygon negative works lovely");
+
+ //polygon with holes negative
+ comps = [ createSquareRing(-10),
+ createSquareRing(-2),
+ createSquareRing(-3),
+ createSquareRing(-4)
+ ];
+
+ var polygon = new OpenLayers.Geometry.Polygon(comps);
+ t.eq(polygon.getArea(), 71, "getArea negative polygon with holes works lovely");
+
+ }
+
+ function test_Polygon_move(t) {
+ t.plan(4);
+
+ polygon = new OpenLayers.Geometry.Polygon([linearRing, linearRing2]);
+
+ var x = linearRing.components[0].x;
+ var y = linearRing.components[0].y;
+ var x2 = linearRing2.components[0].x;
+ var y2 = linearRing2.components[0].y;
+
+ var dx = 10 * Math.random();
+ var dy = 10 * Math.random();
+
+ polygon.move(dx, dy);
+
+ t.eq(polygon.components[0].components[0].x, x + dx, "move() correctly modifies first x");
+ t.eq(polygon.components[0].components[0].y, y + dy, "move() correctly modifies first y");
+ t.eq(polygon.components[1].components[0].x, x2 + dx, "move() correctly modifies second x");
+ t.eq(polygon.components[1].components[0].y, y2 + dy, "move() correctly modifies second y");
+ }
+
+ function test_Polygon_rotate(t) {
+ t.plan(6);
+
+ var geometry = new OpenLayers.Geometry.Polygon([linearRing, linearRing2]);
+
+ var originals = [];
+ var comp;
+ var angle = 2 * Math.PI * Math.random();
+ var origin = new OpenLayers.Geometry.Point(10 * Math.random(),
+ 10 * Math.random());
+ for(var i=0; i<geometry.components.length; ++i) {
+ comp = geometry.components[i];
+ originals[i] = comp.rotate;
+ comp.rotate = function(a, o) {
+ t.ok(true, "rotate called for component " + i);
+ t.ok(a == angle, "rotate called with correct angle");
+ t.ok(o == origin, "rotate called with correct origin");
+ }
+ }
+ geometry.rotate(angle, origin);
+
+ // restore the original rotate defs
+ for(var i=0; i<geometry.components.length; ++i) {
+ comp.rotate = originals[i];
+ }
+ }
+
+ function test_Polygon_resize(t) {
+ t.plan(8);
+
+ var tolerance = 1e-10;
+ var geometry = new OpenLayers.Geometry.Polygon([linearRing, linearRing2]);
+ var origin = new OpenLayers.Geometry.Point(10 * Math.random(),
+ 10 * Math.random());
+ var scale = 10 * Math.random();
+
+ var oldArea = geometry.getArea();
+ var oldPerimeter = geometry.getLength();
+ geometry.resize(scale, origin);
+ var newArea = geometry.getArea();
+ var newPerimeter = geometry.getLength();
+
+ t.ok((((newArea / oldArea) - (scale * scale)) / (scale * scale)) < tolerance,
+ "resize correctly changes the area of a polygon")
+ t.ok((((newPerimeter / oldPerimeter) - scale) / scale) < tolerance,
+ "resize correctly changes the perimeter of a polygon")
+
+ var originals = [];
+ var comp;
+ for(var i=0; i<geometry.components.length; ++i) {
+ comp = geometry.components[i];
+ originals[i] = comp.resize;
+ comp.resize = function(s, o) {
+ t.ok(true, "resize called for component " + i);
+ t.ok(s == scale, "resize called with correct scale");
+ t.ok(o == origin, "resize called with correct origin");
+ }
+ }
+ geometry.resize(scale, origin);
+
+ // restore the original resize defs
+ for(var i=0; i<geometry.components.length; ++i) {
+ comp.resize = originals[i];
+ }
+
+ }
+
+ function test_Polygon_createRegular(t) {
+ t.plan(22);
+ var sides = 40;
+ var poly = OpenLayers.Geometry.Polygon.createRegularPolygon(new OpenLayers.Geometry.Point(5,0), 6, sides);
+ var polyBounds = poly.getBounds();
+ t.eq(polyBounds.toBBOX(), "-0.981504,-5.981504,10.981504,5.981504", sides + " sided figure generates correct bbox.");
+ t.eq(poly.components.length, 1, "Poly has one linear ring");
+ t.eq(poly.components[0].components.length, sides + 1, "ring has 41 components");
+ t.eq(poly.components[0].components[0].id, poly.components[0].components[sides].id, "ring starts and ends with same geom");
+ t.eq(Math.round(poly.getArea()), Math.round(Math.PI * 36), "area of "+sides+" sided poly rounds to same area as a circle.");
+
+ var sides = 3;
+ var poly = OpenLayers.Geometry.Polygon.createRegularPolygon(new OpenLayers.Geometry.Point(5,0), 6, sides);
+ var polyBounds = poly.getBounds();
+ t.eq(polyBounds.toBBOX(), "-0.196152,-3,10.196152,6", sides + " sided figure generates correct bbox.");
+ t.eq(poly.components.length, 1, "Poly has one linear ring");
+ t.eq(poly.components[0].components.length, sides + 1, "ring has correct count of components");
+ t.eq(poly.components[0].components[0].id, poly.components[0].components[sides].id, "ring starts and ends with same geom");
+ t.eq(Math.round(poly.getArea()), 47, "area of 3 sided poly is correct");
+
+ var sides = 3;
+ var poly3 = OpenLayers.Geometry.Polygon.createRegularPolygon(new OpenLayers.Geometry.Point(10,0), 15, sides);
+ var polyBounds = poly3.getBounds();
+ t.eq(polyBounds.toBBOX(), "-2.990381,-7.5,22.990381,15", sides + " sided figure generates correct bbox.");
+ t.eq(Math.round(polyBounds.getCenterLonLat().lon), 10, "longitude of center of bounds is same as origin");
+ t.eq(poly3.components.length, 1, "Poly has one linear ring");
+ t.eq(poly3.components[0].components.length, sides + 1, "ring has correct count of components");
+ t.eq(poly3.components[0].components[0].id, poly3.components[0].components[sides].id, "ring starts and ends with same geom");
+ t.ok(poly3.getArea() > poly.getArea(), "area with radius 15 > poly with radius 6");
+
+ var sides = 4;
+ var poly4 = OpenLayers.Geometry.Polygon.createRegularPolygon(new OpenLayers.Geometry.Point(10,0), 15, sides);
+ var polyBounds = poly4.getBounds();
+ t.eq(polyBounds.toBBOX(), "-0.606602,-10.606602,20.606602,10.606602", sides + " sided figure generates correct bbox.");
+ t.eq(Math.round(polyBounds.getCenterLonLat().lon), 10, "longitude of center of bounds is same as origin");
+ t.eq(poly4.components.length, 1, "Poly has one linear ring");
+ t.eq(poly4.components[0].components.length, sides + 1, "ring has correct count of components");
+ t.eq(poly4.components[0].components[0].id, poly4.components[0].components[sides].id, "ring starts and ends with same geom");
+ t.ok(poly4.getArea() > poly3.getArea(), "square with radius 15 > triangle with radius 15");
+ }
+
+ function test_Polygon_equals(t) {
+ t.plan(3);
+
+ var x0 = Math.random() * 100;
+ var y0 = Math.random() * 100;
+ var x1 = Math.random() * 100;
+ var y1 = Math.random() * 100;
+ var x2 = Math.random() * 100;
+ var y2 = Math.random() * 100;
+ var point0 = new OpenLayers.Geometry.Point(x0, y0);
+ var point1 = new OpenLayers.Geometry.Point(x1, y1);
+ var point2 = new OpenLayers.Geometry.Point(x2, y2);
+ var pointX = new OpenLayers.Geometry.Point(x0 + 1, y0);
+ var pointY = new OpenLayers.Geometry.Point(x0, y0 + 1);
+ var geometry = new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([point0, point1, point2])]);
+ var equal = new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([point0, point1, point2])]);
+ var offX = new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([pointX, point1, point2])]);
+ var offY = new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([pointY, point1, point2])]);
+ t.ok(geometry.equals(equal),
+ "equals() returns true for a geometry with equivalent coordinates");
+ t.ok(!geometry.equals(offX),
+ "equals() returns false for a geometry with offset x");
+ t.ok(!geometry.equals(offY),
+ "equals() returns false for a geometry with offset y");
+ }
+
+ function test_distanceTo(t) {
+ var wkt = OpenLayers.Geometry.fromWKT;
+ var geoms = [
+ wkt("POLYGON((0 3, 1 4, 2 3, 1 2, 0 3))"),
+ wkt("POINT(0 0)"),
+ wkt("LINESTRING(-2 0, 0 -2, 2 -1, 2 0)"),
+ wkt("LINESTRING(0 2, 1 3)"),
+ wkt("POINT(1 3)")
+ ];
+
+ var cases = [{
+ got: geoms[0].distanceTo(geoms[1]),
+ expected: Math.sqrt(5)
+ }, {
+ got: geoms[0].distanceTo(geoms[1], {details: true}),
+ expected: {
+ distance: Math.sqrt(5),
+ x0: 1, y0: 2,
+ x1: 0, y1: 0
+ }
+ }, {
+ got: geoms[0].distanceTo(geoms[2], {details: true}),
+ expected: {
+ distance: Math.sqrt(5),
+ x0: 1, y0: 2,
+ x1: 2, y1: 0
+ }
+ }, {
+ got: geoms[0].distanceTo(geoms[3], {details: true}),
+ expected: {
+ distance: 0,
+ x0: 0.5, y0: 2.5,
+ x1: 0.5, y1: 2.5
+ }
+ }, {
+ got: geoms[0].distanceTo(geoms[4]),
+ expected: Math.sqrt(0.5)
+ }, {
+ got: geoms[0].distanceTo(geoms[4], {edge: false}),
+ expected: 0
+ }];
+
+ t.plan(cases.length);
+ for(var i=0; i<cases.length; ++i) {
+ t.eq(cases[i].got, cases[i].expected, "case " + i);
+ }
+
+ }
+
+ function test_getVertices(t) {
+ t.plan(6);
+
+ var points = [
+ new OpenLayers.Geometry.Point(10, 20),
+ new OpenLayers.Geometry.Point(20, 30),
+ new OpenLayers.Geometry.Point(30, 40),
+ new OpenLayers.Geometry.Point(40, 50)
+ ];
+ var polygon = new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing(points)
+ ]);
+
+ var verts = polygon.getVertices();
+ t.ok(verts instanceof Array, "got back an array");
+ t.eq(verts.length, points.length, "of correct length length");
+ t.geom_eq(verts[0], points[0], "0: correct geometry");
+ t.geom_eq(verts[1], points[1], "1: correct geometry");
+ t.geom_eq(verts[2], points[2], "2: correct geometry");
+ t.geom_eq(verts[3], points[3], "3: correct geometry");
+
+ }
+
+ function test_Polygon_clone(t) {
+ t.plan(2);
+
+ var x0 = Math.random() * 100;
+ var y0 = Math.random() * 100;
+ var x1 = Math.random() * 100;
+ var y1 = Math.random() * 100;
+ var x2 = Math.random() * 100;
+ var y2 = Math.random() * 100;
+ var point0 = new OpenLayers.Geometry.Point(x0, y0);
+ var point1 = new OpenLayers.Geometry.Point(x1, y1);
+ var point2 = new OpenLayers.Geometry.Point(x2, y2);
+ var geometry = new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([point0, point1, point2])]);
+ var clone = geometry.clone();
+ t.ok(clone instanceof OpenLayers.Geometry.Polygon,
+ "clone() creates an OpenLayers.Geometry.Polygon");
+ t.ok(geometry.equals(clone), "clone has equivalent coordinates");
+ }
+
+ function test_getGeodesicArea(t) {
+
+ t.plan(1);
+
+ // from the wfs-states.html example
+ var illinois = OpenLayers.Geometry.fromWKT(
+ "MULTIPOLYGON(((-88.071564 37.51099,-88.087883 37.476273,-88.311707 37.442852,-88.359177 37.409309,-88.419853 37.420292,-88.467644 37.400757,-88.511322 37.296852,-88.501427 37.257782,-88.450699 37.205669,-88.422516 37.15691,-88.45047 37.098671,-88.476799 37.072144,-88.4907 37.06818,-88.517273 37.06477,-88.559273 37.072815,-88.61422 37.109047,-88.68837 37.13541,-88.739113 37.141182,-88.746506 37.152107,-88.863289 37.202194,-88.932503 37.218407,-88.993172 37.220036,-89.065033 37.18586,-89.116821 37.112137,-89.146347 37.093185,-89.169548 37.064236,-89.174332 37.025711,-89.150246 36.99844,-89.12986 36.988113,-89.193512 36.986771,-89.210052 37.028973,-89.237679 37.041733,-89.264053 37.087124,-89.284233 37.091244,-89.303291 37.085384,-89.3097 37.060909,-89.264244 37.027733,-89.262001 37.008686,-89.282768 36.999207,-89.310982 37.009682,-89.38295 37.049213,-89.37999 37.099083,-89.423798 37.137203,-89.440521 37.165318,-89.468216 37.224266,-89.465309 37.253731,-89.489594 37.256001,-89.513885 37.276402,-89.513885 37.304962,-89.50058 37.329441,-89.468742 37.339409,-89.435738 37.355717,-89.427574 37.411018,-89.453621 37.453186,-89.494781 37.491726,-89.524971 37.571957,-89.513367 37.615929,-89.51918 37.650375,-89.513374 37.67984,-89.521523 37.694798,-89.581436 37.706104,-89.666458 37.745453,-89.675858 37.78397,-89.691055 37.804794,-89.728447 37.840992,-89.851715 37.905064,-89.861046 37.905487,-89.866814 37.891876,-89.900551 37.875904,-89.937874 37.878044,-89.978912 37.911884,-89.958229 37.963634,-90.010811 37.969318,-90.041924 37.993206,-90.119339 38.032272,-90.134712 38.053951,-90.207527 38.088905,-90.254059 38.122169,-90.289635 38.166817,-90.336716 38.188713,-90.364769 38.234299,-90.369347 38.323559,-90.358688 38.36533,-90.339607 38.390846,-90.301842 38.427357,-90.265785 38.518688,-90.26123 38.532768,-90.240944 38.562805,-90.183708 38.610271,-90.183578 38.658772,-90.20224 38.700363,-90.196571 38.723965,-90.163399 38.773098,-90.135178 38.785484,-90.121727 38.80051,-90.113121 38.830467,-90.132812 38.853031,-90.243927 38.914509,-90.278931 38.924717,-90.31974 38.924908,-90.413071 38.96233,-90.469841 38.959179,-90.530426 38.891609,-90.570328 38.871326,-90.627213 38.880795,-90.668877 38.935253,-90.70607 39.037792,-90.707588 39.058178,-90.690399 39.0937,-90.716736 39.144211,-90.718193 39.195873,-90.732338 39.224747,-90.738083 39.24781,-90.779343 39.296803,-90.850494 39.350452,-90.947891 39.400585,-91.036339 39.444412,-91.064384 39.473984,-91.093613 39.528927,-91.156189 39.552593,-91.203247 39.600021,-91.317665 39.685917,-91.367088 39.72464,-91.373421 39.761272,-91.381714 39.803772,-91.449188 39.863049,-91.450989 39.885242,-91.434052 39.901829,-91.430389 39.921837,-91.447243 39.946064,-91.487289 40.005753,-91.504005 40.066711,-91.516129 40.134544,-91.506546 40.200459,-91.498932 40.251377,-91.486694 40.309624,-91.448593 40.371902,-91.418816 40.386875,-91.385757 40.392361,-91.372757 40.402988,-91.385399 40.44725,-91.374794 40.503654,-91.382103 40.528496,-91.412872 40.547993,-91.411118 40.572971,-91.37561 40.603439,-91.262062 40.639545,-91.214912 40.643818,-91.162498 40.656311,-91.129158 40.682148,-91.119987 40.705402,-91.092751 40.761547,-91.088905 40.833729,-91.04921 40.879585,-90.983276 40.923927,-90.960709 40.950504,-90.954651 41.070362,-90.957787 41.104359,-90.990341 41.144371,-91.018257 41.165825,-91.05632 41.176258,-91.101524 41.231522,-91.102348 41.267818,-91.07328 41.334896,-91.055786 41.401379,-91.027489 41.423508,-91.000694 41.431084,-90.949654 41.421234,-90.844139 41.444622,-90.7799 41.449821,-90.708214 41.450062,-90.658791 41.462318,-90.6007 41.509586,-90.54084 41.52597,-90.454994 41.527546,-90.434967 41.543579,-90.423004 41.567272,-90.348366 41.586849,-90.339348 41.602798,-90.341133 41.64909,-90.326027 41.722736,-90.304886 41.756466,-90.25531 41.781738,-90.195839 41.806137,-90.154518 41.930775,-90.14267 41.983963,-90.150536 42.033428,-90.168098 42.061043,-90.166649 42.103745,-90.176086 42.120502,-90.191574 42.122688,-90.230934 42.159721,-90.323601 42.197319,-90.367729 42.210209,-90.407173 42.242645,-90.417984 42.263924,-90.427681 42.340633,-90.441597 42.360073,-90.491043 42.388783,-90.563583 42.421837,-90.605827 42.46056,-90.648346 42.475643,-90.651772 42.494698,-90.638329 42.509361,-90.419975 42.508362,-89.923569 42.504108,-89.834618 42.50346,-89.400497 42.49749,-89.359444 42.497906,-88.939079 42.490864,-88.764954 42.490906,-88.70652 42.489655,-88.297897 42.49197,-88.194702 42.489613,-87.79731 42.489132,-87.836945 42.314213,-87.760239 42.156456,-87.670547 42.059822,-87.612625 41.847332,-87.529861 41.723591,-87.532646 41.469715,-87.532448 41.301304,-87.531731 41.173756,-87.532021 41.00993,-87.532669 40.745411,-87.53717 40.49461,-87.535675 40.483246,-87.535339 40.166195,-87.535774 39.887302,-87.535576 39.609341,-87.538567 39.477448,-87.540215 39.350525,-87.597664 39.338268,-87.625237 39.307404,-87.610619 39.297661,-87.615799 39.281418,-87.606895 39.258163,-87.584564 39.248753,-87.588593 39.208466,-87.594208 39.198128,-87.607925 39.196068,-87.644257 39.168507,-87.670326 39.146679,-87.659454 39.130653,-87.662262 39.113468,-87.631668 39.103943,-87.630867 39.088974,-87.612007 39.084606,-87.58532 39.062435,-87.581749 38.995743,-87.591858 38.994083,-87.547905 38.977077,-87.53347 38.963703,-87.530182 38.931919,-87.5392 38.904861,-87.559059 38.869812,-87.550507 38.857891,-87.507889 38.795559,-87.519028 38.776699,-87.508003 38.769722,-87.508316 38.736633,-87.543892 38.685974,-87.588478 38.672169,-87.625191 38.642811,-87.628647 38.622917,-87.619827 38.599209,-87.640594 38.593178,-87.652855 38.573872,-87.672943 38.547424,-87.65139 38.515369,-87.653534 38.500443,-87.679909 38.504005,-87.692818 38.481533,-87.756096 38.466125,-87.758659 38.457096,-87.738953 38.44548,-87.748428 38.417965,-87.784019 38.378124,-87.834503 38.352524,-87.850082 38.286098,-87.863007 38.285362,-87.874039 38.316788,-87.883446 38.315552,-87.888466 38.300659,-87.914108 38.281048,-87.913651 38.302345,-87.925919 38.304771,-87.980019 38.241085,-87.986008 38.234814,-87.977928 38.200714,-87.932289 38.171131,-87.931992 38.157528,-87.950569 38.136913,-87.973503 38.13176,-88.018547 38.103302,-88.012329 38.092346,-87.964867 38.096748,-87.975296 38.073307,-88.034729 38.054085,-88.043091 38.04512,-88.041473 38.038303,-88.021698 38.033531,-88.029213 38.008236,-88.021706 37.975056,-88.042511 37.956264,-88.041771 37.934498,-88.064621 37.929783,-88.078941 37.944,-88.084 37.92366,-88.030441 37.917591,-88.026588 37.905758,-88.044868 37.896004,-88.100082 37.90617,-88.101456 37.895306,-88.075737 37.867809,-88.034241 37.843746,-88.042137 37.827522,-88.089264 37.831249,-88.086029 37.817612,-88.035576 37.805683,-88.072472 37.735401,-88.133636 37.700745,-88.15937 37.660686,-88.157631 37.628479,-88.134171 37.583572,-88.071564 37.51099)))"
+ );
+
+ // two calculations of the area (in square meters)
+ var planar = illinois.getArea() * Math.pow(OpenLayers.INCHES_PER_UNIT['dd'] / OpenLayers.INCHES_PER_UNIT['m'], 2);
+ var geodesic = illinois.getGeodesicArea();
+
+ // from http://en.wikipedia.org/wiki/Illinois
+ var expected = 1.40998e11; // square meters
+
+ var planarErr = Math.abs(planar - expected) / expected;
+ var geodesicErr = Math.abs(geodesic - expected) / expected;
+
+ t.ok(geodesicErr < planarErr, "geodesic measure is better (" + geodesicErr.toFixed(3) + " vs. " + planarErr.toFixed(3) + ")");
+
+ }
+
+ function test_getCentroid(t) {
+ t.plan(5);
+ var bounds = new OpenLayers.Bounds(5, 10, 5, 10);
+ var geometry = bounds.toGeometry();
+ var centroid = geometry.getCentroid();
+ t.eq(geometry.components[0].components.length, 2, "only two vertices since the box has left=right and bottom=top");
+ t.ok(centroid && centroid.x === 5 && centroid.y === 10, "getCentroid returns a point geometry even if the ring of the polygon has only 2 vertices");
+ bounds = new OpenLayers.Bounds(123456789.0, 123456789.0, 123456789.1, 123456789.1);
+ geometry = bounds.toGeometry();
+ centroid = geometry.getCentroid();
+ t.eq(geometry.components[0].components.length, 5, "five vertices expected");
+ var dX = Math.abs(centroid.x - 123456789.05);
+ var dY = Math.abs(centroid.y - 123456789.05);
+ t.ok(centroid && dX < 0.0001 && dY < 0.0001, " getCentroid returns the correct point geometry dX = " + dX + ", dY = " + dY);
+
+ var components = [
+ new OpenLayers.Geometry.Point(0,0), new OpenLayers.Geometry.Point(1,1),
+ new OpenLayers.Geometry.Point(0,1), new OpenLayers.Geometry.Point(1,0)];
+ var linearRing = new OpenLayers.Geometry.LinearRing(components);
+ polygon = new OpenLayers.Geometry.Polygon([linearRing.clone()]);
+ centroid = polygon.getCentroid();
+ var tX = centroid.x;
+ var tY = centroid.y;
+ t.ok( !isNaN(tX) && !isNaN(tY) && tX !== Infinity && tY !== Infinity, " getCentroid for wrong polygon works x = " + tX + ", y = " + tY);
+ }
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Handler.html b/misc/openlayers/tests/Handler.html
new file mode 100644
index 0000000..eb266d7
--- /dev/null
+++ b/misc/openlayers/tests/Handler.html
@@ -0,0 +1,265 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Handler_constructor(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+
+ var callbacks = {foo: "bar"};
+ var options = {bar: "foo"};
+ var handler = new OpenLayers.Handler(control, callbacks, options);
+ t.ok(handler instanceof OpenLayers.Handler,
+ "new OpenLayers.Handler returns object");
+ t.eq(handler.map.id, map.id,
+ "constructing a handler with a map sets the map on the handler");
+ t.eq(handler.callbacks.foo, callbacks.foo,
+ "constructor correctly sets callbacks");
+ t.eq(handler.bar, options.bar,
+ "constructor correctly extends handler with options");
+ }
+
+ function test_Handler_activate(t) {
+ t.plan(52);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+
+ var events = ["mouseover", "mouseout", "mousedown",
+ "mouseup", "mousemove", "click",
+ "dblclick", "resize", "focus", "blur"];
+
+ var handler = new OpenLayers.Handler(control);
+ handler.active = true;
+ var activated = handler.activate();
+ t.ok(!activated,
+ "activate returns false if the handler is already active");
+
+ handler.active = false;
+ map.events.registerPriority = function(type, obj, func) {
+ var r = func();
+ if(typeof r == "string") {
+ // this is one of the mock handler methods
+ t.ok(OpenLayers.Util.indexOf(events, type) > -1,
+ "activate calls registerPriority with browser event: " + type);
+ t.eq(typeof func, "function",
+ "activate calls registerPriority with a function");
+ t.eq(r, type,
+ "activate calls registerPriority with the correct method");
+ t.eq(obj["CLASS_NAME"], "OpenLayers.Handler",
+ "activate calls registerPriority with the handler");
+ } else {
+ // this is the call with handler.setEvent as the func
+ t.ok(r, "activate calls registerPriority with handler.setEvent");
+ }
+ }
+
+ // set browser event like properties on the handler
+ for(var i=0; i<events.length; ++i) {
+ setMethod(events[i]);
+ }
+ function setMethod(key) {
+ handler[key] = function() {return key};
+ }
+ activated = handler.activate();
+ t.ok(activated,
+ "activated returns true if the handler is not already active");
+
+ }
+
+ function test_Handler_deactivate(t) {
+ t.plan(52);
+ var map = new OpenLayers.Map('map', { controls: []});
+ // No controls so that we don't get more thingies than we expect
+ // when we actually clean up after ourselves: r5891 caused this
+ // because we actually destroy things now on the navigation control.
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+
+ var events = ["mouseover", "mouseout", "mousedown",
+ "mouseup", "mousemove", "click",
+ "dblclick", "resize", "focus", "blur"];
+
+ var handler = new OpenLayers.Handler(control);
+ handler.active = false;
+ var deactivated = handler.deactivate();
+ t.ok(!deactivated,
+ "deactivate returns false if the handler is already deactive");
+
+ handler.activate();
+ map.events.unregister = function(type, obj, func) {
+ var r = func();
+ if(typeof r == "string") {
+ // this is one of the mock handler methods
+ t.ok(OpenLayers.Util.indexOf(events, type) > -1,
+ "deactivate calls unregister with browser event: " + type);
+ t.eq(typeof func, "function",
+ "activate calls unregister with a function");
+ t.eq(func(), type,
+ "activate calls unregister with the correct method");
+ t.eq(obj["CLASS_NAME"], "OpenLayers.Handler",
+ "activate calls unregister with the handler");
+ } else {
+ // this is the call with handler.setEvent as the func
+ t.ok(r, "activate calls registerPriority with handler.setEvent");
+ }
+ }
+
+ // set browser event like properties on the handler
+ for(var i=0; i<events.length; ++i) {
+ // add in a closure for key
+ (function(key) {
+ handler[key] = function() {return key};
+ })(events[i]);
+ }
+ deactivated = handler.deactivate();
+ t.ok(deactivated,
+ "deactivated returns true if the handler is already active");
+ map.events.unregister = OpenLayers.Events.prototype.unregister;
+ map.destroy();
+
+ }
+
+ function test_Handler_setEvent(t) {
+ t.plan(5);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler(control);
+ handler.click = function(evt) {
+ }
+ handler.activate();
+ var testEvent = {
+ xy: new OpenLayers.Pixel(Math.random(), Math.random()),
+ altKey: (Math.random() > 0.5),
+ shiftKey: (Math.random() > 0.5),
+ ctrlKey: (Math.random() > 0.5),
+ metaKey: (Math.random() > 0.5)
+ }
+ map.events.triggerEvent("click", testEvent);
+ t.ok(handler.evt.xy.x == testEvent.xy.x &&
+ handler.evt.xy.y == testEvent.xy.y,
+ "handler.evt has proper xy object");
+ t.eq(handler.evt.altKey, testEvent.altKey,
+ "handler.evt.altKey correct");
+ t.eq(handler.evt.shiftKey, testEvent.shiftKey,
+ "handler.evt.shiftKey correct");
+ t.eq(handler.evt.ctrlKey, testEvent.ctrlKey,
+ "handler.evt.ctrlKey correct");
+ t.eq(handler.evt.metaKey, testEvent.metaKey,
+ "handler.evt.metaKey correct");
+ }
+
+ function test_Handler_destroy(t) {
+ t.plan(5);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler(control);
+ var deactivated = false;
+ handler.deactivate = function() {
+ deactivated = true;
+ };
+ t.ok(handler.control,
+ "handler has a control prior to destroy");
+ t.ok(handler.map,
+ "handler has a map prior to destroy");
+ handler.destroy();
+ t.eq(handler.control, null,
+ "hanlder.control is null after destroy");
+ t.eq(handler.map, null,
+ "handler.map is null after destroy");
+ t.ok(deactivated,
+ "handler.deactivate is called by destroy");
+ }
+
+ function test_Handler_checkModifiers(t) {
+ t.plan(62);
+ var handler = new OpenLayers.Handler({});
+ handler.keyMask = null;
+ var proceed = handler.checkModifiers({});
+ t.ok(proceed,
+ "checkModifiers returns true if no keyMask on the handler");
+
+
+ /**
+ * Test checkModifiers for single keyMask values. The method should
+ * return true if the corresponding key is associated with the
+ * event. For example, if evt.shiftKey is true and handler.keyMask
+ * is OpenLayers.Handler.MOD_SHIFT, checkModifiers should return
+ * true.
+ */
+ var constants = {
+ MOD_NONE: null,
+ MOD_SHIFT: "shiftKey",
+ MOD_CTRL: "ctrlKey",
+ MOD_ALT: "altKey",
+ MOD_META: "metaKey"
+ }
+ var proceed, evt, value, c, k;
+ for(c in constants) {
+ handler.keyMask = OpenLayers.Handler[c];
+ // for this key mask, test all single key possibilities
+ for(k in constants) {
+ value = constants[k];
+ evt = {};
+ if(value) {
+ // mimic a key down on an event
+ evt[value] = true;
+ }
+ proceed = handler.checkModifiers(evt);
+ // if k == c, proceed should be true - false otherwise
+ t.eq(k == c, proceed,
+ "returns " + proceed + " if keyMask is " + c +
+ " and " + ((value) ? value : "no key") + " is down");
+ }
+ }
+
+ /**
+ * Test checkModifiers for double keyMask values. The method should
+ * return true if the corresponding key combo is associated with the
+ * event. For example, if evt.shiftKey is true and handler.keyMask
+ * is OpenLayers.Handler.MOD_SHIFT, checkModifiers should return
+ * true.
+ */
+ var constants = ["MOD_SHIFT", "MOD_CTRL", "MOD_ALT", "MOD_META"];
+ var keys = ["shiftKey", "ctrlKey", "altKey", "metaKey"];
+ var proceed, evt, c1, c2, k1, k2;
+ for(var i=0; i<constants.length-1; ++i) {
+ c1 = constants[i];
+ for(var j=i+1; j<constants.length; ++j) {
+ c2 = constants[j];
+ handler.keyMask = OpenLayers.Handler[c1] |
+ OpenLayers.Handler[c2];
+ // for this key mask, test all double key possibilities
+ for(var x=0; x<keys.length-1; ++x) {
+ k1 = keys[x];
+ for(var y=x+1; y<keys.length; ++y) {
+ k2 = keys[y];
+ evt = {};
+ evt[k1] = true;
+ evt[k2] = true;
+ proceed = handler.checkModifiers(evt);
+ // if the combo matches, proceed should be true
+ // at this point we know that i != j and x != y
+ t.eq(((i == x) || (i == y)) &&
+ ((j == x) || (j == y)),
+ proceed,
+ "returns " + proceed + " if " + c1 + " | " + c2 +
+ " and " + k1 + " + " + k2 + " is down");
+ }
+ }
+ }
+ }
+
+ }
+
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Handler/Box.html b/misc/openlayers/tests/Handler/Box.html
new file mode 100644
index 0000000..edb20d0
--- /dev/null
+++ b/misc/openlayers/tests/Handler/Box.html
@@ -0,0 +1,106 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Handler_Box_constructor(t) {
+ t.plan(5);
+ var control = new OpenLayers.Control();
+ control.id = Math.random();
+ var callbacks = {start: "foo", done: "bar"};
+ var options = {bar: "foo"};
+
+ var handler = new OpenLayers.Handler.Box(control, callbacks, options);
+
+ t.eq(handler.control.id, control.id, "handler created with the correct control");
+ t.eq(handler.callbacks.start, "foo", "handler created with the correct start callback");
+ t.eq(handler.callbacks.done, "bar", "handler created with the correct done callback");
+ t.eq(handler.bar, "foo", "handler created with the correct options");
+ t.ok(handler.dragHandler instanceof OpenLayers.Handler.Drag, "drag handler created");
+ }
+
+ function test_Handler_Box_draw(t) {
+
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Box(control, {
+ start: function(e) {
+ t.ok(true, "start callback called");
+ },
+ done: function(e) {
+ t.ok(e.equals(new OpenLayers.Bounds(5, 11, 11, 5)), "box result correct");
+ }
+ });
+ handler.activate();
+
+ // determine whether we can test the box position, the hidden frame
+ // our tests run in causes us problem here in FF and IE:
+ // IE8: left is NaN
+ // FF3: left is NaN
+ // FF4; left is NaN
+ // Chromium 10: left is 0
+ var testdiv = OpenLayers.Util.createDiv('testdiv', new OpenLayers.Pixel(5, 5));
+ map.div.appendChild(testdiv);
+ var left = parseInt(OpenLayers.Element.getStyle(testdiv, 'border-left-width'));
+ map.div.removeChild(testdiv);
+ var testAll = !isNaN(left);
+
+ t.plan(testAll ? 11 : 3);
+
+ // we change NaN values to 0 values in the handler's
+ // boxOffsets object, this is to prevent "invalid
+ // "argument" errors in IE
+ if(!testAll) {
+ var offset = handler.getBoxOffsets();
+ offset.left = 0;
+ offset.right = 0;
+ offset.top = 0;
+ offset.bottom = 0;
+ offset.width = 0;
+ offset.height = 0;
+ }
+
+
+ handler.dragHandler.start = {x: 5, y: 5};
+ handler.startBox();
+ offset = handler.getBoxOffsets();
+ handler.moveBox({x: 10, y: 10});
+ if (testAll) {
+ t.eq(parseInt(handler.zoomBox.style.left), 5 - offset.left, "x position of box correct");
+ t.eq(parseInt(handler.zoomBox.style.top), 5 - offset.top, "y position of box correct");
+ t.eq(parseInt(handler.zoomBox.style.width), 5 + offset.width + 1, "x dimension of box correct");
+ t.eq(parseInt(handler.zoomBox.style.height), 5 + offset.height + 1, "y dimension of box correct");
+ }
+ handler.moveBox({x: 0, y: 0});
+ if (testAll) {
+ t.eq(parseInt(handler.zoomBox.style.left), 0 - offset.left, "new x position of box correct");
+ t.eq(parseInt(handler.zoomBox.style.top), 0 - offset.top, "new y position of box correct");
+ t.eq(parseInt(handler.zoomBox.style.width), 5 + offset.width + 1, "x dimension of box still correct");
+ t.eq(parseInt(handler.zoomBox.style.height), 5 + offset.height + 1, "y dimension of box still correct");
+ }
+ handler.endBox({x: 11, y: 11});
+ t.eq(handler.zoomBox, null, "box removed after endBox");
+ }
+
+ function test_Handler_Box_destroy(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Box(control);
+ handler.activate();
+ try {
+ handler.destroy();
+ t.ok(true, "destroying the box handler should not raise any error");
+ } catch(err) {
+ t.fail("destroying the box handler causes trouble: " + err);
+ }
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 300px; height: 150px;"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Handler/Click.html b/misc/openlayers/tests/Handler/Click.html
new file mode 100644
index 0000000..be508ca
--- /dev/null
+++ b/misc/openlayers/tests/Handler/Click.html
@@ -0,0 +1,735 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function px(x, y) {
+ return new OpenLayers.Pixel(x, y);
+ }
+
+ function test_Handler_Click_constructor(t) {
+ t.plan(3);
+ var control = new OpenLayers.Control();
+ control.id = Math.random();
+ var callbacks = {foo: "bar"};
+ var options = {bar: "foo"};
+
+ var oldInit = OpenLayers.Handler.prototype.initialize;
+
+ OpenLayers.Handler.prototype.initialize = function(con, call, opt) {
+ t.eq(con.id, control.id,
+ "constructor calls parent with the correct control");
+ t.eq(call, callbacks,
+ "constructor calls parent with the correct callbacks");
+ t.eq(opt, options,
+ "constructor calls parent with the correct options");
+ }
+ var handler = new OpenLayers.Handler.Click(control, callbacks, options);
+
+ OpenLayers.Handler.prototype.initialize = oldInit;
+ }
+
+ function test_Handler_Click_activate(t) {
+ t.plan(2);
+ var control = {
+ map: new OpenLayers.Map('map')
+ };
+ var handler = new OpenLayers.Handler.Click(control);
+ handler.active = true;
+ var activated = handler.activate();
+ t.ok(!activated,
+ "activate returns false if the handler was already active");
+ handler.active = false;
+ handler.dragging = true;
+ activated = handler.activate();
+ t.ok(activated,
+ "activate returns true if the handler was not already active");
+
+ }
+
+ function test_Handler_Click_events(t) {
+ t.plan(80);
+
+ var map = new OpenLayers.Map('map');
+ var control = {
+ map: map
+ };
+ map.events.registerPriority = function(type, obj, func) {
+ var f = OpenLayers.Function.bind(func, obj)
+ var r = f({xy:null});
+ if(typeof r == "string") {
+ // this is one of the mock handler methods
+ t.eq(OpenLayers.Util.indexOf(nonevents, type), -1,
+ "registered method is not one of the events " +
+ "that should not be handled");
+ t.ok(OpenLayers.Util.indexOf(events, type) > -1,
+ "activate calls registerPriority with browser event: " + type);
+ t.eq(typeof func, "function",
+ "activate calls registerPriority with a function");
+ t.eq(func(), type,
+ "activate calls registerPriority with the correct method");
+ t.eq(obj["CLASS_NAME"], "OpenLayers.Handler.Click",
+ "activate calls registerPriority with the handler");
+ }
+ }
+ function setMethod(key) {
+ handler[key] = function() {return key};
+ }
+
+ // list below events that should be handled (events) and those
+ // that should not be handled (nonevents) by the handler
+ var events = ["click", "dblclick", "mousedown", "mouseup", "rightclick", "touchstart", "touchmove", "touchend"];
+ var nonevents = ["mousemove", "resize", "focus", "blur"];
+ var handler = new OpenLayers.Handler.Click(control);
+ // set browser event like properties on the handler
+ for(var i=0; i<events.length; ++i) {
+ setMethod(events[i]);
+ }
+ handler.activate();
+
+ // different listeners registered for pixelTolerance option
+ var events = ["click", "dblclick", "mousedown", "mouseup", "rightclick", "touchstart", "touchmove", "touchend"];
+ var nonevents = ["mousemove", "resize", "focus", "blur"];
+ var handler = new OpenLayers.Handler.Click(control, {}, {
+ pixelTolerance: 2
+ });
+ for(var i=0; i<events.length; ++i) {
+ setMethod(events[i]);
+ }
+ handler.activate();
+
+ }
+
+ var callbackMap;
+ function callbackSetup(log, options) {
+ callbackMap = new OpenLayers.Map({
+ div: "map",
+ controls: [], // no controls here because these tests use a custom setTimeout and we only want setTimeout calls from a single handler
+ layers: [new OpenLayers.Layer(null, {isBaseLayer: true})],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 1
+ });
+ var control = new OpenLayers.Control();
+ callbackMap.addControl(control);
+
+ var callbacks = {
+ "click": function(evt) {
+ log.push({callback: "click", evt: evt});
+ },
+ "dblclick": function(evt) {
+ log.push({callback: "dblclick", evt: evt});
+ }
+ };
+ var handler = new OpenLayers.Handler.Click(control, callbacks, options);
+ handler.activate();
+
+
+ var timers = {};
+ window._setTimeout = window.setTimeout;
+ window.setTimeout = function(func, delay) {
+ log.push({method: "setTimeout", func: func, delay: delay});
+ var key = (new Date).getTime() + "-" + Math.random();
+ timers[key] = true;
+ // execute function that is supposed to be delayed
+ func();
+ return key;
+ }
+ window._clearTimeout = window.clearTimeout;
+ window.clearTimeout = function(key) {
+ log.push({
+ method: "clearTimeout",
+ keyExists: (key in timers)
+ });
+ delete timers[key];
+ }
+ return handler;
+ }
+
+ function callbackTeardown() {
+ window.setTimeout = window._setTimeout;
+ window.clearTimeout = window._clearTimeout;
+ callbackMap.destroy();
+ callbackMap = null;
+ }
+
+ function test_callbacks_click_default(t) {
+ t.plan(6);
+
+ var log = [];
+ var handler = callbackSetup(log);
+
+ // set up for single click - three tests here
+ var testEvt = {id: Math.random()};
+ handler.map.events.triggerEvent("click", testEvt);
+ t.eq(log.length, 2, "(click w/ single true) two items logged");
+
+ // first item logged is setTimeout call
+ t.eq(log[0].method, "setTimeout", "setTimeout called");
+ t.eq(typeof log[0].func, "function", "setTimeout called with a function");
+ t.eq(log[0].delay, handler.delay, "setTimeout called with proper delay");
+
+ // second item logged is from click callback
+ t.eq(log[1].callback, "click", "click callback called");
+ t.eq(log[1].evt.id, testEvt.id, "got correct event");
+
+ callbackTeardown();
+ }
+
+ function test_callbacks_dblclick_default(t) {
+ t.plan(1);
+
+ var log = [];
+ var handler = callbackSetup(log);
+ var testEvt = {id: Math.random()};
+ handler.map.events.triggerEvent("dblclick", testEvt);
+
+ t.eq(log.length, 0, "nothing happens by default with dblclick (double is false)");
+
+ callbackTeardown();
+
+ }
+
+ function test_callbacks_dblclick_double(t) {
+ t.plan(3);
+
+ var log = [];
+ var handler = callbackSetup(log, {"double": true});
+ var testEvt = {id: Math.random()};
+ handler.map.events.triggerEvent("dblclick", testEvt);
+
+ t.eq(log.length, 1, "one item logged");
+ t.eq(log[0].callback, "dblclick", "dblclick callback called")
+ t.eq(log[0].evt.id, testEvt.id, "dblclick callback called with event");
+
+ callbackTeardown();
+
+ }
+
+ function test_callbacks_dblclick_sequence(t) {
+ t.plan(8);
+
+ var log = [];
+ var handler = callbackSetup(log, {"double": true});
+ var testEvt = {id: Math.random()};
+
+ // first click - set timer for next
+ handler.map.events.triggerEvent("click", testEvt);
+ t.ok(handler.timerId != null, "timer is set");
+ log.pop(); // because the test setTimeout is synchronous we get the click callback immediately
+ t.eq(log.length, 1, "one item logged (after pop due to synchronous setTimeout call in our tests");
+ t.eq(log[0].method, "setTimeout", "setTimeout called first");
+
+ // second click - timer cleared
+ handler.map.events.triggerEvent("click", testEvt);
+ t.ok(handler.timerId == null, "timer is cleared");
+ t.eq(log.length, 2, "two items logged after second click");
+ t.eq(log[1].method, "clearTimeout", "clearTimeout called second");
+
+ // dblclick event - callback called
+ handler.map.events.triggerEvent("dblclick", testEvt);
+ t.eq(log.length, 3, "three items logged");
+ t.eq(log[2].callback, "dblclick", "dblclick callback called third");
+
+ callbackTeardown();
+
+ }
+
+ function test_callbacks_within_pixelTolerance(t) {
+ t.plan(1);
+
+ var log = [];
+ var handler = callbackSetup(log, {"double": true, pixelTolerance: 2});
+
+ var down = {
+ xy: px(0, 0)
+ };
+ var up = {
+ xy: px(0, 1)
+ };
+
+ handler.map.events.triggerEvent("mousedown", down);
+ handler.map.events.triggerEvent("mouseup", up);
+ handler.map.events.triggerEvent("click", up);
+
+ t.eq(log[log.length-1].callback, "click", "click callback called");
+
+ callbackTeardown();
+
+ }
+
+ function test_callbacks_outside_pixelTolerance(t) {
+ t.plan(2);
+
+ var log = [];
+ var handler = callbackSetup(log, {pixelTolerance: 2});
+
+ var down = {
+ xy: px(0, 0)
+ };
+ var up = {
+ xy: px(2, 3)
+ };
+
+ handler.map.events.triggerEvent("mousedown", down);
+ t.ok(handler.down && handler.down.xy.equals(down.xy), "down position set");
+
+ handler.map.events.triggerEvent("mouseup", up);
+ handler.map.events.triggerEvent("click", up);
+ t.eq(log.length, 0, "nothing logged - event outside tolerance");
+
+ callbackTeardown();
+
+ }
+
+ function test_callbacks_within_dblclickTolerance(t) {
+ t.plan(6);
+
+ var log = [];
+ var handler = callbackSetup(log, {single: false, "double": true, dblclickTolerance: 8});
+
+ var first = {
+ xy: px(0, 0)
+ };
+ var second = {
+ xy: px(0, 5)
+ };
+
+ handler.map.events.triggerEvent("mousedown", first);
+ handler.map.events.triggerEvent("mouseup", first);
+ handler.map.events.triggerEvent("click", first);
+ t.eq(log.length, 1, "one item logged");
+ t.eq(log[0] && log[0].method, "setTimeout", "setTimeout called");
+
+ handler.map.events.triggerEvent("mousedown", second);
+ handler.map.events.triggerEvent("mouseup", second);
+ handler.map.events.triggerEvent("click", second);
+ t.eq(log.length, 2, "two events logged");
+ t.eq(log[1] && log[1].method, "clearTimeout", "clearTimeout called");
+
+ handler.map.events.triggerEvent("dblclick", second);
+ t.eq(log.length, 3, "three items logged");
+ t.eq(log[2] && log[2].callback, "dblclick", "dblclick callback called");
+
+ callbackTeardown();
+ }
+
+ function test_callbacks_outside_dblclickTolerance(t) {
+ t.plan(5);
+
+ var log = [];
+ // default dblclickTolerance is 13
+ var handler = callbackSetup(log, {single: false, "double": true});
+
+ var first = {
+ xy: px(0, 0)
+ };
+ var second = {
+ xy: px(13.5, 0)
+ };
+
+ handler.map.events.triggerEvent("mousedown", first);
+ handler.map.events.triggerEvent("mouseup", first);
+ handler.map.events.triggerEvent("click", first);
+ t.eq(log.length, 1, "one item logged");
+ t.eq(log[0] && log[0].method, "setTimeout", "setTimeout called");
+
+ handler.map.events.triggerEvent("mousedown", second);
+ handler.map.events.triggerEvent("mouseup", second);
+ handler.map.events.triggerEvent("click", second);
+ t.eq(log.length, 2, "two items logged");
+ t.eq(log[1] && log[1].method, "clearTimeout", "clearTimeout called");
+
+ handler.map.events.triggerEvent("dblclick", second);
+ t.eq(log.length, 2, "still two items logged - dblclick callback is not called");
+
+ callbackTeardown();
+ }
+
+ function test_callbacks_multitouch_single(t) {
+
+ t.plan(2);
+
+ var log = [];
+
+ var callbacks = {
+ click: function(evt) {
+ log.push({callback: "click", type: evt.type});
+ },
+ dblclick: function(evt) {
+ log.push({callback: "dblclick", type: evt.type});
+ }
+ };
+
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer(null, {isBaseLayer: true});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 1);
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Click(
+ control, callbacks,
+ {"double": true, single: true, pixelTolerance: 2}
+ );
+
+ // we override here so we don't have to wait for the timeout
+ handler.queuePotentialClick = function(evt) {
+ log.push({potential: true, evt: evt});
+ OpenLayers.Handler.Click.prototype.queuePotentialClick.call(this, evt);
+ }
+
+ handler.activate();
+
+ function handle(o) {
+ var touches = [];
+ if (("x0" in o) && ("y0" in o)) {
+ touches.push({
+ clientX: o.x0, clientY: o.y0
+ });
+ }
+ if (("x1" in o) && ("y1" in o)) {
+ touches.push({
+ clientX: o.x1, clientY: o.y1
+ });
+ }
+ handler.map.events.handleBrowserEvent({
+ type: o.type, touches: touches
+ });
+ }
+
+ // a typical multitouch sequence goes like this:
+ // touchstart, touchstart, touchend, touchend
+ handle({type: "touchstart", x0: 10, y0: 10});
+ handle({type: "touchstart", x0: 10, y0: 10, x1: 30, y1: 15});
+ handle({type: "touchend"});
+ handle({type: "touchend"});
+
+ t.eq(log.length, 1, "one item logged");
+ t.eq(log[0] && log[0].potential, true, "click in queue - no dblclick called");
+
+ map.destroy();
+ }
+
+ function test_Handler_Click_deactivate(t) {
+ t.plan(6);
+ var control = {
+ map: new OpenLayers.Map('map')
+ };
+ var handler = new OpenLayers.Handler.Click(control);
+ handler.active = false;
+ var deactivated = handler.deactivate();
+ t.ok(!deactivated,
+ "deactivate returns false if the handler was not already active");
+ handler.active = true;
+ handler.down = true;
+ handler.timerId = true;
+ handler.touch = true;
+ handler.last = true;
+ deactivated = handler.deactivate();
+ t.ok(deactivated,
+ "deactivate returns true if the handler was active already");
+ t.eq(handler.down, null,
+ "deactivate sets down to null");
+ t.eq(handler.timerId, null,
+ "deactivate sets timerId to null");
+ t.eq(handler.touch, false,
+ "deactivate sets touch to false");
+ t.eq(handler.last, null,
+ "deactivate sets last to null");
+
+ }
+
+ function test_Handler_Click_mouseup(t) {
+ t.plan(11);
+
+ var map = new OpenLayers.Map("map");
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Click(control);
+
+ var testEvent = {id: Math.random()};
+ var propagate = true;
+ var log, got, modMatch, rightClick;
+
+ // override methods to log what is called
+ var temp = OpenLayers.Event.isRightClick;
+ OpenLayers.Event.isRightClick = function(e) {
+ log.push({method: "isRightClick", evt: e});
+ return rightClick;
+ };
+ handler.checkModifiers = function(e) {
+ log.push({method: "checkModifiers", evt: e});
+ return modMatch;
+ };
+ handler.rightclick = function(e) {
+ log.push({method: "rightclick", evt: e});
+ return propagate;
+ };
+
+
+ // simulate an event with non-matching modifiers
+ log = [];
+ modMatch = false;
+ rightClick = false;
+ got = handler.mouseup(testEvent);
+ t.eq(log.length, 1, "one item logged");
+ t.eq(log[0] && log[0].method, "checkModifiers", "a) checkModifiers called first");
+ t.eq(log[0] && log[0].evt, testEvent, "a) first method called with correct event");
+
+
+ // modifiers, handlerightclicks, and isrightclick
+ log = [];
+ rightClick = true;
+ modMatch = true;
+ handler.control.handleRightClicks = true;
+ got = handler.mouseup(testEvent);
+ t.eq(log.length, 3, "three items logged");
+ t.eq(log[0] && log[0].method, "checkModifiers", "b) checkModifiers called first");
+ t.eq(log[0] && log[0].evt, testEvent, "b) first method called with correct event");
+ t.eq(log[1] && log[1].method, "isRightClick", "b) isRightClick called second");
+ t.eq(log[1] && log[1].evt, testEvent, "b) second method called with correct event");
+ t.eq(log[2] && log[2].method, "rightclick", "b) rightclick called third");
+ t.eq(log[2] && log[2].evt, testEvent, "b) third method called with correct event");
+ t.eq(got, propagate, "b) return from handler's rightclick returned from mouseup");
+
+ OpenLayers.Event.isRightClick = temp;
+ map.destroy();
+ }
+
+ function test_touch_click(t) {
+ t.plan(5);
+
+ // set up
+
+ var log;
+
+ var map = new OpenLayers.Map('map');
+ var control = {map: map};
+
+ var callbacks = {
+ 'click': function(e) {
+ log = {x: e.xy.x, y: e.xy.y,
+ lastTouches: e.lastTouches};
+ }
+ };
+
+ var handler = new OpenLayers.Handler.Click(
+ control, callbacks,
+ {'single': true, pixelTolerance: null});
+
+ // test
+
+ // the common case: a touchstart followed by a touchend
+ log = null;
+ handler.touchstart({xy: px(1, 1), touches: ["foo"]});
+ handler.touchend({touches: ["foo"]});
+
+ t.delay_call(1, function() {
+ t.ok(log != null, "click callback called");
+ if(log != null) {
+ t.eq(log.x, 1, "evt.xy.x as expected");
+ t.eq(log.y, 1, "evt.xy.y as expected");
+ t.ok(log.lastTouches, "evt.lastTouches as expected");
+ }
+
+ // now emulate a touch where touchstart doesn't propagate
+ // to the click handler, i.e. the click handler gets a
+ // touchend only
+ log = null;
+ handler.touchend({touches: ["foo"]});
+
+ t.delay_call(1, function() {
+ t.ok(log == null, "click callback not called");
+
+ // tear down
+ map.destroy();
+ });
+ });
+ }
+
+ function test_touch_within_dblclickTolerance(t) {
+ t.plan(4);
+
+ var log;
+
+ var callbacks = {
+ click: function(evt) {
+ log.push({callback: "click", type: evt.type});
+ },
+ dblclick: function(evt) {
+ log.push({callback: "dblclick", type: evt.type});
+ }
+ };
+
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer(null, {isBaseLayer: true});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 1);
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Click(
+ control, callbacks,
+ {"double": true, single: true, pixelTolerance: 2}
+ );
+ handler.activate();
+
+ function handle(type, x, y) {
+ map.events.handleBrowserEvent({
+ type: type,
+ touches: [
+ {clientX: x, clientY: y}
+ ]
+ });
+ }
+
+ // test
+ log = [];
+ // sequence of two clicks on a touch device
+ // click 1
+ handle("touchstart", 10, 10);
+ handle("touchend", 11, 10);
+ handle("mousemove", 11, 10);
+ handle("mousedown", 10, 10);
+ handle("mouseup", 11, 10);
+ handle("click", 11, 10);
+ // click 2
+ handle("touchstart", 12, 10);
+ handle("touchend", 12, 10);
+ handle("mousedown", 12, 10);
+ handle("mouseup", 12, 10);
+ handle("click", 12, 10);
+
+ t.eq(log.length, 1, "one callback called");
+ t.eq(log[0] && log[0].callback, "dblclick", "click callback called");
+ t.eq(log[0] && log[0].type, "touchend", "click callback called with touchend event");
+ t.ok(!handler.timerId, "handler doesn't have a timerId waiting for click")
+
+ // tear down
+ map.destroy();
+ }
+
+ function test_touch_outside_dblclickTolerance(t) {
+ t.plan(2);
+
+ var log;
+
+ var callbacks = {
+ click: function(evt) {
+ log.push({callback: "click", type: evt.type});
+ },
+ dblclick: function(evt) {
+ log.push({callback: "dblclick", type: evt.type});
+ }
+ };
+
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer(null, {isBaseLayer: true});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 1);
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Click(
+ control, callbacks,
+ {"double": true, single: true, pixelTolerance: 2, dblclickTolerance: 8}
+ );
+ handler.activate();
+
+ function handle(type, x, y) {
+ var touches = [];
+ if (x !== undefined && y !== undefined) {
+ touches.push({
+ clientX: x, clientY: y
+ });
+ }
+ map.events.handleBrowserEvent({
+ type: type, touches: touches
+ });
+ }
+
+ // test
+ log = [];
+ // sequence of two clicks on a touch device
+ // click 1
+ handle("touchstart", 10, 10);
+ handle("touchend");
+ handle("mousemove", 11, 10);
+ handle("mousedown", 10, 10);
+ handle("mouseup", 11, 10);
+ handle("click", 11, 10);
+ // click 2
+ handle("touchstart", 20, 10);
+ handle("touchend");
+ handle("mousedown", 20, 10);
+ handle("mouseup", 20, 10);
+ handle("click", 20, 10);
+
+ t.eq(log.length, 0, "no callbacks called");
+ t.ok(!handler.timerId, "handler doesn't have a timerId waiting for click")
+
+ // tear down
+ map.destroy();
+ }
+
+ function test_touchstart(t) {
+ // a test to verify that the touchstart function does
+ // unregister the mouse listeners when it's called the
+ // first time
+
+ t.plan(7);
+
+ // set up
+
+ var map = new OpenLayers.Map("map", {
+ controls: []
+ });
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Click(control, {});
+ control.handler = handler;
+ map.addControl(control);
+ handler.activate();
+
+ function allRegistered() {
+ var eventTypes = ['mousedown', 'mouseup', 'click', 'dblclick'],
+ eventType,
+ listeners,
+ listener,
+ flag;
+ for(var i=0, ilen=eventTypes.length; i<ilen; i++) {
+ flag = false;
+ eventType = eventTypes[i];
+ listeners = map.events.listeners[eventType];
+ for(var j=0, jlen=listeners.length; j<jlen; j++) {
+ listener = listeners[j];
+ if(listener.func === handler[eventType] && listener.obj === handler) {
+ flag = true;
+ break;
+ }
+ }
+ if(!flag) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // test
+
+ t.ok(allRegistered(), 'mouse listeners are registered');
+ handler.touchstart({xy: new OpenLayers.Pixel(0, 0)});
+ t.eq(map.events.listeners.mousedown.length, 0,"mousedown is not registered");
+ t.eq(map.events.listeners.mouseup.length, 0,"mouseup is not registered");
+ t.eq(map.events.listeners.click.length, 0,"click is not registered");
+ t.eq(map.events.listeners.dblclick.length, 0,"dblclick is not registered");
+
+ t.ok(handler.touch, 'handler.touch is set');
+
+ handler.deactivate();
+ t.ok(!handler.touch, 'handler.touch is not set');
+
+ map.destroy();
+ }
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 300px; height: 150px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Handler/Drag.html b/misc/openlayers/tests/Handler/Drag.html
new file mode 100644
index 0000000..fa9a3b2
--- /dev/null
+++ b/misc/openlayers/tests/Handler/Drag.html
@@ -0,0 +1,603 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Handler_Drag_constructor(t) {
+ t.plan(3);
+ var control = new OpenLayers.Control();
+ control.id = Math.random();
+ var callbacks = {foo: "bar"};
+ var options = {bar: "foo"};
+
+ var oldInit = OpenLayers.Handler.prototype.initialize;
+
+ OpenLayers.Handler.prototype.initialize = function(con, call, opt) {
+ t.eq(con.id, control.id,
+ "constructor calls parent with the correct control");
+ t.eq(call, callbacks,
+ "constructor calls parent with the correct callbacks");
+ t.eq(opt, options,
+ "constructor calls parent with the correct options");
+ }
+ var handler = new OpenLayers.Handler.Drag(control, callbacks, options);
+
+ OpenLayers.Handler.prototype.initialize = oldInit;
+ }
+
+ function test_Handler_Drag_activate(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Drag(control);
+ handler.active = true;
+ var activated = handler.activate();
+ t.ok(!activated,
+ "activate returns false if the handler was already active");
+ handler.active = false;
+ handler.dragging = true;
+ activated = handler.activate();
+ t.ok(activated,
+ "activate returns true if the handler was not already active");
+ t.ok(!handler.dragging,
+ "activate sets dragging to false");
+
+ }
+
+ function test_Handler_Drag_events(t) {
+ t.plan(40);
+
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Drag(control);
+
+ // list below events that should be handled (events) and those
+ // that should not be handled (nonevents) by the handler
+ var events = ["mousedown", "mouseup", "mousemove", "mouseout", "click",
+ "touchstart", "touchmove", "touchend"];
+ var nonevents = ["dblclick", "resize", "focus", "blur"];
+ map.events.registerPriority = function(type, obj, func) {
+ var r = func();
+ if(typeof r == "string") {
+ // this is one of the mock handler methods
+ t.eq(OpenLayers.Util.indexOf(nonevents, type), -1,
+ "registered method is not one of the events " +
+ "that should not be handled");
+ t.ok(OpenLayers.Util.indexOf(events, type) > -1,
+ "activate calls registerPriority with browser event: " + type);
+ t.eq(typeof func, "function",
+ "activate calls registerPriority with a function");
+ t.eq(func(), type,
+ "activate calls registerPriority with the correct method");
+ t.eq(obj["CLASS_NAME"], "OpenLayers.Handler.Drag",
+ "activate calls registerPriority with the handler");
+ }
+ }
+
+ // set browser event like properties on the handler
+ for(var i=0; i<events.length; ++i) {
+ setMethod(events[i]);
+ }
+ function setMethod(key) {
+ handler[key] = function() {return key};
+ }
+
+ var activated = handler.activate();
+
+ }
+
+ function test_Handler_Drag_callbacks(t) {
+ t.plan(33);
+
+ var map = new OpenLayers.Map('map', {controls: []});
+
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+
+ // set callback methods (out doesn't get an xy)
+ var events = ["down", "move", "up", "done"];
+ var testEvents = {};
+ var xys = {};
+ var callbacks = {};
+ for(var i=0; i<events.length; ++i) {
+ var px = new OpenLayers.Pixel(Math.random(), Math.random());
+ testEvents[events[i]] = {xy: px};
+ setCallback(events[i]);
+ }
+ function setCallback(key) {
+ callbacks[key] = function(evtxy) {
+ t.ok(evtxy.x == testEvents[key].xy.x &&
+ evtxy.y == testEvents[key].xy.y,
+ key + " callback called with the proper evt.xy");
+ }
+ }
+
+ var handler = new OpenLayers.Handler.Drag(control, callbacks);
+ handler.activate();
+
+ var oldIsLeftClick = OpenLayers.Event.isLeftClick;
+ var oldPreventDefault = OpenLayers.Event.preventDefault;
+ var oldCheckModifiers = handler.checkModifiers;
+
+ // test mousedown with right click
+ OpenLayers.Event.isLeftClick = function() {
+ return false;
+ }
+ handler.checkModifiers = function() {
+ return true;
+ }
+ handler.started = true;
+ handler.start = {x: "foo", y: "bar"};
+ handler.last = {x: "foo", y: "bar"};
+ map.events.triggerEvent("mousedown", testEvents.down);
+ t.ok(!handler.started, "right-click sets started to false");
+ t.eq(handler.start, null, "right-click sets start to null");
+ t.eq(handler.last, null, "right-click sets last to null");
+
+ // test mousedown with improper modifier
+ OpenLayers.Event.isLeftClick = function() {
+ return true;
+ }
+ handler.checkModifiers = function() {
+ return false;
+ }
+ handler.started = true;
+ handler.start = {x: "foo", y: "bar"};
+ handler.last = {x: "foo", y: "bar"};
+ map.events.triggerEvent("mousedown", testEvents.down);
+ t.ok(!handler.started, "bad modifier sets started to false");
+ t.eq(handler.start, null, "bad modifier sets start to null");
+ t.eq(handler.last, null, "bad modifier sets last to null");
+
+ // test mousedown
+ handler.checkModifiers = function(evt) {
+ t.ok(evt.xy.x == testEvents.down.xy.x &&
+ evt.xy.y == testEvents.down.xy.y,
+ "mousedown calls checkModifiers with the proper event");
+ return true;
+ }
+ OpenLayers.Event.isLeftClick = function(evt) {
+ t.ok(evt.xy.x == testEvents.down.xy.x &&
+ evt.xy.y == testEvents.down.xy.y,
+ "mousedown calls isLeftClick with the proper event");
+ return true;
+ }
+ OpenLayers.Event.preventDefault = function(evt) {
+ t.ok(evt.xy.x == testEvents.down.xy.x && evt.xy.y == testEvents.down.xy.y,
+ "mousedown default action is disabled");
+ }
+ map.events.triggerEvent("mousedown", testEvents.down);
+ t.ok(handler.started, "mousedown sets the started flag to true");
+ t.ok(!handler.dragging, "mouse down sets the dragging flag to false");
+ t.ok(handler.start.x == testEvents.down.xy.x &&
+ handler.start.y == testEvents.down.xy.y,
+ "mouse down sets handler.start correctly");
+ t.ok(handler.last.x == testEvents.down.xy.x &&
+ handler.last.y == testEvents.down.xy.y,
+ "mouse down sets handler.last correctly");
+
+ OpenLayers.Event.preventDefault = oldPreventDefault;
+ OpenLayers.Event.isLeftClick = oldIsLeftClick;
+ handler.checkModifiers = oldCheckModifiers;
+
+ // test mouseup before mousemove
+ var realUp = testEvents.up;
+ testEvents.up = testEvents.down;
+ // this will fail with notice about the done callback being called
+ // if done is called when it shouldn't be
+ map.events.triggerEvent("mouseup", testEvents.up);
+ testEvents.up = realUp;
+
+ // test mousemove
+ handler.started = false;
+ map.events.triggerEvent("mousemove", {xy: {x: null, y: null}});
+ // if the handler triggers the move callback, it will be with the
+ // incorrect evt.xy
+ t.ok(true,
+ "mousemove before the handler has started doesn't call move");
+
+ handler.started = true;
+ map.events.triggerEvent("mousemove", testEvents.move);
+ t.ok(handler.dragging, "mousemove sets the dragging flag to true");
+ t.ok(handler.start.x == testEvents.down.xy.x &&
+ handler.start.y == testEvents.down.xy.y,
+ "mouse move leaves handler.start alone");
+ t.ok(handler.last.x == testEvents.move.xy.x &&
+ handler.last.y == testEvents.move.xy.y,
+ "mouse move sets handler.last correctly");
+
+ // a second move with the same evt.xy should not trigger move callback
+ // if it does, the test page will complain about a bad plan number
+ var oldMove = handler.callbacks.move;
+ handler.callbacks.move = function() {
+ t.ok(false,
+ "a second move with the same evt.xy should not trigger a move callback");
+ }
+ map.events.triggerEvent("mousemove", testEvents.move);
+ handler.callbacks.move = oldMove;
+
+ // test mouseup
+ handler.started = false;
+ map.events.triggerEvent("mouseup", {xy: {x: null, y: null}});
+ // if the handler triggers the up callback, it will be with the
+ // incorrect evt.xy
+ t.ok(true,
+ "mouseup before the handler has started doesn't call up");
+
+ handler.started = true;
+ // mouseup triggers the up and done callbacks
+ testEvents.done = testEvents.up;
+ map.events.triggerEvent("mouseup", testEvents.up);
+ t.ok(!this.started, "mouseup sets the started flag to false");
+ t.ok(!this.dragging, "mouseup sets the dragging flag to false");
+
+ // test mouseout
+ handler.started = false;
+ map.events.triggerEvent("mouseout", {xy: {x: null, y: null}});
+ // if the handler triggers the out or done callback, it will be with the
+ // incorrect evt.xy
+ t.ok(true,
+ "mouseout before the handler has started doesn't call out or done");
+
+ handler.started = true;
+ var oldMouseLeft = OpenLayers.Util.mouseLeft;
+ OpenLayers.Util.mouseLeft = function(evt, element) {
+ t.ok(evt.xy.x == testEvents.done.xy.x &&
+ evt.xy.y == testEvents.done.xy.y,
+ "mouseout calls Util.mouseLeft with the correct event");
+ t.eq(element.id, map.viewPortDiv.id,
+ "mouseout calls Util.mouseLeft with the correct element");
+ return true;
+ }
+ // mouseup triggers the out and done callbacks
+ // out callback gets no arguments
+ handler.callbacks.out = function() {
+ t.eq(arguments.length, 0,
+ "mouseout calls out callback with no arguments");
+ }
+ map.events.triggerEvent("mouseout", testEvents.done);
+ t.ok(!handler.started, "mouseout sets started flag to false");
+ t.ok(!handler.dragging, "mouseout sets dragging flag to false");
+ OpenLayers.Util.mouseLeft = oldMouseLeft;
+
+ // test click with the click.html example - the click method on the
+ // drag handler returns (handler.start == handler.last), stopping
+ // propagation of the click event if the mouse moved during a drag.
+
+ // regression tests will assure that the drag handler doesn't mess
+ // with anything else on a click
+ function getProperties(obj) {
+ var props = {};
+ for(key in obj) {
+ if(typeof obj[key] != "function" && typeof obj[key] != "object") {
+ props[key] = obj[key];
+ }
+ }
+ return props;
+ }
+ var before = getProperties(handler);
+ map.events.triggerEvent("click", null);
+ var after = getProperties(handler);
+ t.eq(before, after, "click doesn't mess with handler");
+
+ }
+
+ function test_Handler_Drag_touch(t) {
+ // In this test we verify that "touchstart", "touchmove", and
+ // "touchend" events set expected states in the drag handler.
+ // We also verify that we prevent the default as appropriate.
+
+ t.plan(19);
+
+ // set up
+
+ var m = new OpenLayers.Map('map', {controls: []});
+ var c = new OpenLayers.Control();
+ m.addControl(c);
+ var h = new OpenLayers.Handler.Drag(c, {
+ done: function(px) {
+ log.push(px);
+ }
+ });
+ h.activate();
+
+ var _preventDefault = OpenLayers.Event.preventDefault;
+ OpenLayers.Event.preventDefault = function(e) {
+ log.push(e);
+ };
+
+ var Px = OpenLayers.Pixel, e;
+ var log = [];
+
+ // test
+ e = {touches: [{}], xy: new Px(0, 0)};
+ m.events.triggerEvent('touchstart', e);
+ t.eq(h.started, true, '[touchstart] started is set');
+ t.eq(h.start.x, 0, '[touchstart] start.x is correct');
+ t.eq(h.start.y, 0, '[touchstart] start.y is correct');
+ t.eq(log.length, 1, '[touchstart] one item in log');
+ t.ok(log[0] === e, "touchstart", '[touchstart] event is stopped');
+ t.eq(m.events.listeners.mousedown.length, 0,"mousedown is not registered");
+ t.eq(m.events.listeners.mouseup.length, 0,"mouseup is not registered");
+ t.eq(m.events.listeners.mousemove.length, 0,"mousemove is not registered");
+ t.eq(m.events.listeners.click.length, 0,"click is not registered");
+ t.eq(m.events.listeners.mouseout.length, 0,"mouseout is not registered");
+
+ e = {xy: new Px(1, 1)};
+ m.events.triggerEvent('touchmove', e);
+ t.eq(h.dragging, true, '[touchmove] dragging is set');
+ t.eq(h.last.x, 1, '[touchmove] last.x is correct');
+ t.eq(h.last.y, 1, '[touchmove] last.y is correct');
+ t.eq(log.length, 1, '[touchmove] one item in log (event is not stopped)');
+
+ e = {xy: new Px(2, 2)};
+ m.events.triggerEvent('touchend', e);
+ t.eq(h.started, false, '[touchend] started is reset');
+ t.eq(h.started, false, '[touchend] started is reset');
+ // the "done" callback gets the position of the last touchmove
+ t.eq(log.length, 2, '[touchend] two items in log');
+ t.ok(log[1] instanceof Px, '[touchend] got');
+ t.ok(log[1].equals(e.xy), '[touchend] done callback got correct position');
+
+ // tear down
+
+ OpenLayers.Event.preventDefault = _preventDefault;
+ m.destroy();
+ }
+
+ function test_Handler_Drag_submethods(t) {
+ t.plan(8);
+
+ var map = new OpenLayers.Map('map', {controls: []});
+
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+
+
+ var handler = new OpenLayers.Handler.Drag(control, {});
+ // set test events
+ var events = ["down", "move", "up", "out"];
+ var onselect = {
+ "move": OpenLayers.Function.False,
+ "up": OpenLayers.Function.False,
+ "out": OpenLayers.Function.True
+ }
+ var testEvents = {};
+ var type, px;
+ for(var i=0; i<events.length; ++i) {
+ type = events[i];
+ px = new OpenLayers.Pixel(Math.random(), Math.random());
+ testEvents[type] = {xy: px};
+ setMethod(type);
+ }
+ function setMethod(type) {
+ handler[type] = function(evt) {
+ t.ok(evt.xy.x == testEvents[type].xy.x &&
+ evt.xy.y == testEvents[type].xy.y,
+ "handler." + type + " called with the right event");
+ onselect[type] && t.ok(document.onselectstart === onselect[type], "document.onselectstart listener is correct after " + type);
+ }
+ }
+ handler.activate();
+
+ // pretend that we have gone through a down-move-up-out cycle before
+ handler.oldOnselectstart = OpenLayers.Function.True;
+
+ // test mousedown
+ handler.checkModifiers = function(evt) {
+ return true;
+ }
+ var oldIsLeftClick = OpenLayers.Event.isLeftClick;
+ OpenLayers.Event.isLeftClick = function(evt) {
+ return true;
+ }
+ map.events.triggerEvent("mousedown", testEvents.down);
+ OpenLayers.Event.isLeftClick = oldIsLeftClick;
+
+ // test mousemove
+ map.events.triggerEvent("mousemove", testEvents.move);
+
+ // test mouseup
+ map.events.triggerEvent("mouseup", testEvents.up);
+
+ // test mouseout
+ var oldMouseLeft = OpenLayers.Util.mouseLeft;
+ OpenLayers.Util.mouseLeft = function() {
+ return true;
+ };
+ handler.started = true;
+ map.events.triggerEvent("mouseout", testEvents.out);
+ OpenLayers.Util.mouseLeft = oldMouseLeft;
+
+ t.ok(document.onselectstart === OpenLayers.Function.True, "document.onselectstart listener correct after down-move-up-out cycle");
+
+ }
+
+ function test_Handler_Drag_deactivate(t) {
+ t.plan(7);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Drag(control);
+ handler.active = false;
+ var deactivated = handler.deactivate();
+ t.ok(!deactivated,
+ "deactivate returns false if the handler was not already active");
+ handler.active = true;
+ handler.dragging = true;
+ handler.touch = true;
+ deactivated = handler.deactivate();
+ t.ok(deactivated,
+ "deactivate returns true if the handler was active already");
+ t.ok(!handler.started,
+ "deactivate sets started to false");
+ t.ok(!handler.dragging,
+ "deactivate sets dragging to false");
+ t.ok(handler.start == null,
+ "deactivate sets start to null");
+ t.ok(handler.last == null,
+ "deactivate sets last to null");
+ t.ok(!handler.touch,
+ "deactivate sets touch to false");
+ }
+
+ function test_interval_timer_after_mouseup(t) {
+ t.plan(5);
+
+ // set up
+
+ var map = new OpenLayers.Map('map');
+
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+
+ var moveCnt;
+
+ var handler = new OpenLayers.Handler.Drag(control, {}, {
+ interval: 1,
+ move: function() {
+ moveCnt++;
+ }
+ });
+ handler.activate();
+
+ handler.checkModifiers = function() { return true; };
+
+ var ilc = OpenLayers.Event.isLeftClick;
+ OpenLayers.Event.isLeftClick = function() { return true; };
+
+ // test
+
+ moveCnt = 0;
+
+ var xy1 = new OpenLayers.Pixel(1, 2);
+ handler.mousedown({xy: xy1});
+ t.ok(handler.last == xy1, "[mousedown] last is as expected");
+ var xy2 = new OpenLayers.Pixel(2, 3);
+ handler.mousemove({xy: xy2});
+ t.ok(handler.last == xy2, "[mousemove 1] last is as expected");
+ t.ok(handler.timeoutId != null, "[mousemove 1] timeoutId is set");
+ var xy3 = new OpenLayers.Pixel(3, 4);
+ handler.mousemove({xy: xy3});
+ t.ok(handler.last == xy2, "[mousemove 2] last is as expected");
+ var xy4 = new OpenLayers.Pixel(4, 5);
+ handler.mouseup({xy: xy4});
+
+ t.delay_call(3, function() {
+ // the timer should not cause a move
+ t.eq(moveCnt, 1, "move called once");
+ // tear down
+ OpenLayers.Event.isLeftClick = ilc;
+ });
+ }
+
+ function test_interval_timer_after_mousedown(t) {
+ t.plan(5);
+
+ // set up
+
+ var map = new OpenLayers.Map('map');
+
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+
+ var moveCnt;
+
+ var handler = new OpenLayers.Handler.Drag(control, {}, {
+ interval: 1,
+ move: function() {
+ moveCnt++;
+ }
+ });
+ handler.activate();
+
+ handler.checkModifiers = function() { return true; };
+
+ var ilc = OpenLayers.Event.isLeftClick;
+ OpenLayers.Event.isLeftClick = function() { return true; };
+
+ // test
+
+ moveCnt = 0;
+
+ var xy1 = new OpenLayers.Pixel(1, 2);
+ handler.mousedown({xy: xy1});
+ t.ok(handler.last == xy1, "[mousedown] last is as expected");
+ var xy2 = new OpenLayers.Pixel(2, 3);
+ handler.mousemove({xy: xy2});
+ t.ok(handler.last == xy2, "[mousemove 1] last is as expected");
+ t.ok(handler.timeoutId != null, "[mousemove 1] timeoutId is set");
+ var xy3 = new OpenLayers.Pixel(3, 4);
+ handler.mousemove({xy: xy3});
+ t.ok(handler.last == xy2, "[mousemove 2] last is as expected");
+ var xy4 = new OpenLayers.Pixel(4, 5);
+ handler.mouseup({xy: xy4});
+ var xy5 = new OpenLayers.Pixel(5, 6);
+ handler.mousedown({xy: xy4});
+
+ t.delay_call(3, function() {
+ // the timer should not cause a move
+ t.eq(moveCnt, 1, "move called once");
+ // tear down
+ OpenLayers.Event.isLeftClick = ilc;
+ });
+ }
+
+ function test_interval_timer_before_mouseup(t) {
+ t.plan(5);
+
+ // set up
+
+ var map = new OpenLayers.Map('map');
+
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+
+ var moveCnt;
+
+ var handler = new OpenLayers.Handler.Drag(control, {}, {
+ interval: 1,
+ move: function() {
+ moveCnt++;
+ }
+ });
+ handler.activate();
+
+ handler.checkModifiers = function() { return true; };
+
+ var ilc = OpenLayers.Event.isLeftClick;
+ OpenLayers.Event.isLeftClick = function() { return true; };
+
+ // test
+
+ moveCnt = 0;
+
+ var xy1 = new OpenLayers.Pixel(1, 2);
+ handler.mousedown({xy: xy1});
+ t.ok(handler.last == xy1, "[mousedown] last is as expected");
+ var xy2 = new OpenLayers.Pixel(2, 3);
+ handler.mousemove({xy: xy2});
+ t.ok(handler.last == xy2, "[mousemove 1] last is as expected");
+ t.ok(handler.timeoutId != null, "[mousemove 1] timeoutId is set");
+ var xy3 = new OpenLayers.Pixel(3, 4);
+ handler.mousemove({xy: xy3});
+ t.ok(handler.last == xy2, "[mousemove 2] last is as expected");
+
+ t.delay_call(3, function() {
+ // the timer should cause a move
+ t.eq(moveCnt, 2, "move called twice");
+ var xy4 = new OpenLayers.Pixel(4, 5);
+ handler.mouseup({xy: xy4});
+ // tear down
+ OpenLayers.Event.isLeftClick = ilc;
+ });
+ }
+
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 300px; height: 150px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Handler/Feature.html b/misc/openlayers/tests/Handler/Feature.html
new file mode 100644
index 0000000..4a78e14
--- /dev/null
+++ b/misc/openlayers/tests/Handler/Feature.html
@@ -0,0 +1,698 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_initialize(t) {
+ t.plan(4);
+ var control = new OpenLayers.Control();
+ control.id = Math.random();
+ var layer = "boo";
+ var callbacks = {foo: "bar"};
+ var options = {bar: "foo"};
+
+ var oldInit = OpenLayers.Handler.prototype.initialize;
+
+ OpenLayers.Handler.prototype.initialize = function(con, call, opt) {
+ t.eq(con.id, control.id,
+ "constructor calls parent with the correct control");
+ t.eq(call, callbacks,
+ "constructor calls parent with the correct callbacks");
+ t.eq(opt, options,
+ "constructor calls parent with the correct options");
+ }
+ var handler = new OpenLayers.Handler.Feature(control, layer,
+ callbacks, options);
+
+ t.eq(handler.layer, "boo",
+ "layer property properly set");
+
+ OpenLayers.Handler.prototype.initialize = oldInit;
+ }
+
+ function test_activate(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var layer = new OpenLayers.Layer();
+ map.addLayer(layer);
+ var handler = new OpenLayers.Handler.Feature(control, layer);
+ handler.active = true;
+ var activated = handler.activate();
+ t.ok(!activated,
+ "activate returns false if the handler was already active");
+ handler.active = false;
+
+ var zIndex = layer.div.style.zIndex;
+ activated = handler.activate();
+ t.ok(activated,
+ "activate returns true if the handler was not already active");
+ t.eq(parseInt(layer.div.style.zIndex),
+ map.Z_INDEX_BASE['Feature'],
+ "layer z-index properly adjusted");
+
+ }
+ function test_events(t) {
+ t.plan(35);
+
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var layer = new OpenLayers.Layer();
+ map.addLayer(layer);
+ var handler = new OpenLayers.Handler.Feature(control, layer);
+
+ // list below events that should be handled (events) and those
+ // that should not be handled (nonevents) by the handler
+ var events = ["mousedown", "mouseup", "mousemove", "click", "dblclick", "touchstart", "touchmove"];
+ var nonevents = ["mouseout", "resize", "focus", "blur", "touchend"];
+ map.events.registerPriority = function(type, obj, func) {
+ var output = func();
+ // Don't listen for setEvent handlers (#902)
+ if (typeof output == "string") {
+ t.eq(OpenLayers.Util.indexOf(nonevents, type), -1,
+ "registered method is not one of the events " +
+ "that should not be handled");
+ t.ok(OpenLayers.Util.indexOf(events, type) > -1,
+ "activate calls registerPriority with browser event: " + type);
+ t.eq(typeof func, "function",
+ "activate calls registerPriority with a function");
+ t.eq(func(), type,
+ "activate calls registerPriority with the correct method:"+type);
+ t.eq(obj["CLASS_NAME"], "OpenLayers.Handler.Feature",
+ "activate calls registerPriority with the handler");
+ }
+ }
+
+ // set browser event like properties on the handler
+ for(var i=0; i<events.length; ++i) {
+ setMethod(events[i]);
+ }
+ function setMethod(key) {
+ handler[key] = function() {return key};
+ }
+
+ var activated = handler.activate();
+
+ }
+
+ function test_geometrytype_limit(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var layer = new OpenLayers.Layer();
+ var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,0));
+ feature.layer = layer;
+ layer.getFeatureFromEvent = function(evt) { return feature };
+ map.addLayer(layer);
+ var handler = new OpenLayers.Handler.Feature(control, layer, {}, {'geometryTypes':['OpenLayers.Geometry.Point']});
+ handler.activate();
+ handler.callback = function(type,featurelist) {
+ t.eq(featurelist[0].id, feature.id, "Correct feature called back on");
+ }
+ handler.handle({type: "click"});
+ handler.feature = null;
+ handler.lastFeature = null;
+ handler.callback = function(type,featurelist) {
+ t.fail("Shouldn't have called back on " + featurelist[0].geometry);
+ }
+ feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(0,0));
+ feature.layer = layer;
+ handler.handle("click", {});
+ }
+
+ function test_callbacks(t) {
+ t.plan(14);
+
+ var map = new OpenLayers.Map('map', {controls: []});
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var layer = new OpenLayers.Layer();
+ map.addLayer(layer);
+
+ var callbacks = {};
+ var newFeature, lastFeature;
+ var evtPx = {xy: new OpenLayers.Pixel(Math.random(), Math.random())};
+
+ // define a callback factory function
+ function getCallback(evt, feature) {
+ return function(f) {
+ t.ok(f == feature, evt + " callback called with proper feature");
+ };
+ }
+
+ // override the layer's getFeatureFromEvent func so that it always
+ // returns newFeature
+ layer.getFeatureFromEvent = function(evt) { return newFeature; };
+
+ var handler = new OpenLayers.Handler.Feature(control, layer, callbacks);
+ handler.activate();
+
+ // test click in new feature
+ // only 'click' callback should be called
+ handler.feature = null;
+ lastFeature = null;
+ newFeature = new OpenLayers.Feature.Vector();
+ newFeature.layer = layer;
+ callbacks['click'] = getCallback('click', newFeature);
+ callbacks['clickout'] = getCallback('clickout', lastFeature);
+ evtPx.type = "click";
+ map.events.triggerEvent('click', evtPx);
+
+ // test click in new feature and out of last feature
+ // both 'click' and 'clickout' callbacks should be called
+ lastFeature = newFeature;
+ newFeature = new OpenLayers.Feature.Vector();
+ newFeature.layer = layer;
+ callbacks['click'] = getCallback('click', newFeature);
+ callbacks['clickout'] = getCallback('clickout', lastFeature);
+ evtPx.type = "click";
+ map.events.triggerEvent('click', evtPx);
+
+ // test click out of last feature
+ // only 'clickout' callback should be called
+ lastFeature = newFeature;
+ newFeature = null;
+ callbacks['click'] = getCallback('click', newFeature);
+ callbacks['clickout'] = getCallback('clickout', lastFeature);
+ evtPx.type = "click";
+ map.events.triggerEvent('click', evtPx);
+
+ layer.getFeatureFromEvent = function(evt) { t.fail("mousemove called getFeatureFromEvent without any mousemove callbacks"); };
+ evtPx.type = "mousemove";
+ map.events.triggerEvent('mousemove', evtPx);
+ layer.getFeatureFromEvent = function(evt) { return newFeature; };
+
+ // test over a new feature
+ // only 'over' callback should be called
+ handler.feature = null;
+ lastFeature = null;
+ newFeature = new OpenLayers.Feature.Vector();
+ newFeature.layer = layer;
+ callbacks['over'] = getCallback('over', newFeature);
+ callbacks['out'] = getCallback('out', lastFeature);
+ evtPx.type = "mousemove";
+ map.events.triggerEvent('mousemove', evtPx);
+
+ // test over a new feature and out of last feature
+ // both 'over' and 'out' callbacks should be called
+ lastFeature = newFeature;
+ newFeature = new OpenLayers.Feature.Vector();
+ newFeature.layer = layer;
+ callbacks['over'] = getCallback('over', newFeature);
+ callbacks['out'] = getCallback('out', lastFeature);
+ evtPx.type = "mousemove";
+ map.events.triggerEvent('mousemove', evtPx);
+
+ // test out of last feature
+ // only 'out' callback should be called
+ lastFeature = newFeature;
+ newFeature = null;
+ callbacks['over'] = getCallback('over', newFeature);
+ callbacks['out'] = getCallback('out', lastFeature);
+ evtPx.type = "mousemove";
+ map.events.triggerEvent('mousemove', evtPx);
+
+ // test dblclick on a feature
+ // 'dblclick' callback should be called
+ handler.feature = null;
+ lastFeature = null;
+ newFeature = new OpenLayers.Feature.Vector();
+ newFeature.layer = layer;
+ callbacks['dblclick'] = getCallback('dblclick', newFeature);
+ evtPx.type = "dblclick";
+ map.events.triggerEvent('dblclick', evtPx);
+
+ // test touchstart on a feature
+ // 'click' callback should be called
+ handler.feature = null;
+ lastFeature = null;
+ newFeature = new OpenLayers.Feature.Vector();
+ newFeature.layer = layer;
+ callbacks['click'] = getCallback('click (touch)', newFeature);
+ callbacks['clickout'] = getCallback('clickout (touch)', lastFeature);
+ evtPx.type = "touchstart";
+ map.events.triggerEvent('touchstart', evtPx);
+
+ // test touchstart on the same feature
+ // 'click' callback should be called
+ callbacks['click'] = getCallback('click (touch)', newFeature);
+ evtPx.type = "touchstart";
+ map.events.triggerEvent('touchstart', evtPx);
+
+ // test touchstart in new feature and out of last feature
+ // both 'click' and 'clickout' callbacks should be called
+ lastFeature = newFeature;
+ newFeature = new OpenLayers.Feature.Vector();
+ newFeature.layer = layer;
+ callbacks['click'] = getCallback('click (touch)', newFeature);
+ callbacks['clickout'] = getCallback('clickout (touch)', lastFeature);
+ evtPx.type = "touchstart";
+ map.events.triggerEvent('touchstart', evtPx);
+
+ // test touchstart out of last feature
+ // only 'clickout' callback should be called
+ lastFeature = newFeature;
+ newFeature = null;
+ callbacks['click'] = getCallback('click (touch)', newFeature);
+ callbacks['clickout'] = getCallback('clickout (touch)', lastFeature);
+ evtPx.type = "touchstart";
+ map.events.triggerEvent('touchstart', evtPx);
+ }
+
+ function test_touchstart(t) {
+ // a test to verify that the touchstart function does
+ // unregister the mouse listeners when it's called the
+ // first time
+
+ t.plan(4);
+
+ // set up
+
+ var map = new OpenLayers.Map('map', {controls: []});
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var layer = new OpenLayers.Layer();
+ map.addLayer(layer);
+
+ var handler = new OpenLayers.Handler.Feature(control, layer, {});
+ handler.mousedown = function() {}; // mock mousedown
+ handler.activate();
+
+ var eventTypes = ['mousedown', 'mouseup', 'mousemove', 'click', 'dblclick'];
+
+ function allRegistered() {
+ var eventType,
+ listeners,
+ listener,
+ flag;
+ for(var i=0, ilen=eventTypes.length; i<ilen; i++) {
+ flag = false;
+ eventType = eventTypes[i];
+ listeners = map.events.listeners[eventType];
+ for(var j=0, jlen=listeners.length; j<jlen; j++) {
+ listener = listeners[j];
+ if(listener.func === handler[eventType] && listener.obj === handler) {
+ flag = true;
+ break;
+ }
+ }
+ if(!flag) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ function noneRegistered() {
+ var eventType,
+ times,
+ flag = false;
+ for(var i=0, ilen=eventTypes.length; i<ilen; i++) {
+ eventType = eventTypes[i];
+ times = map.events.listeners[eventType].length;
+ if (times != 0) {
+ t.fail(eventType + " is registered " + times + " times");
+ flag = true;
+ }
+ }
+ return !flag;
+ }
+
+ // test
+
+ t.ok(allRegistered(), 'mouse listeners are registered');
+ handler.touchstart({xy: new OpenLayers.Pixel(0, 0)});
+ t.ok(noneRegistered(), 'mouse listeners are unregistered');
+ t.ok(handler.touch, 'handler.touch is set');
+
+ handler.deactivate();
+ t.ok(!handler.touch, 'handler.touch is not set');
+
+ // tear down
+
+ map.destroy();
+ }
+
+ function test_deactivate(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var layer = new OpenLayers.Layer();
+ map.addLayer(layer);
+ var layerIndex = parseInt(layer.div.style.zIndex);
+
+ var handler = new OpenLayers.Handler.Feature(control, layer);
+ handler.active = false;
+ var deactivated = handler.deactivate();
+ t.ok(!deactivated,
+ "deactivate returns false if the handler was not already active");
+
+ handler.active = true;
+
+ deactivated = handler.deactivate();
+ t.ok(deactivated,
+ "deactivate returns true if the handler was active already");
+ t.eq(parseInt(layer.div.style.zIndex),
+ layerIndex,
+ "deactivate sets the layer z-index back");
+ }
+
+ function test_stopHandled(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var layer = new OpenLayers.Layer();
+ map.addLayer(layer);
+ var handler = new OpenLayers.Handler.Feature(control, layer);
+ handler.activate();
+ handler.handle = function(evt) { return /* handled */ true; };
+ var evtPx = {xy: new OpenLayers.Pixel(Math.random(), Math.random())};
+ map.events.register("click", map, function(e) {
+ t.ok(!handler.stopClick, "clicks propagate with stopClick set to false" );
+ });
+ map.events.register("mousedown", map, function(e) {
+ t.ok(!handler.stopDown, "mousedown propagate with stopDown set to false" );
+ });
+ map.events.register("mouseup", map, function(e) {
+ t.ok(!handler.stopUp, "mouseup propagate with stopUp set to false" );
+ });
+
+ // 0 test
+ map.events.triggerEvent('click', evtPx);
+ // 0 test
+ map.events.triggerEvent('mousedown', evtPx);
+ // 0 test
+ map.events.triggerEvent('mousedown', evtPx);
+
+ // 1 test
+ handler.stopClick = false;
+ map.events.triggerEvent('click', evtPx);
+ // 1 test
+ handler.stopDown = false;
+ map.events.triggerEvent('mousedown', evtPx);
+ // 1 test
+ handler.stopUp = false;
+ map.events.triggerEvent('mouseup', evtPx);
+
+ // 3 tests total
+ }
+
+ function test_destroyed_feature(t) {
+ t.plan(18);
+
+ // setup
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var layer = new OpenLayers.Layer();
+ layer.removeFeatures = function() {};
+ map.addLayer(layer);
+ var handler = new OpenLayers.Handler.Feature(control, layer);
+ var feature, count, lastType, lastHandled;
+ handler.callback = function(type, features) {
+ ++count;
+ lastType = type;
+ lastHandled = features;
+ }
+
+ /**
+ * Test that a destroyed feature doesn't get sent to the "click" callback
+ */
+ count = 0;
+ handler.activate();
+ feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,0));
+ feature.layer = layer;
+ // mock click on a feature
+ layer.getFeatureFromEvent = function(evt) {return feature};
+ handler.handle({type: "click"});
+ // confirm that feature was handled
+ t.eq(count, 1, "[click] callback called once");
+ t.eq(lastType, "click", "[click] correct callback type");
+ t.eq(lastHandled[0].id, feature.id, "[click] correct feature sent to callback");
+
+ // now destroy the feature and confirm that the callback is not called again
+ feature.destroy();
+ handler.handle({type: "click"});
+ if(count === 1) {
+ t.ok(true, "[click] callback not called after destroy");
+ } else {
+ t.fail("[click] callback called after destroy: " + lastType);
+ }
+
+ /**
+ * Test that a destroyed feature doesn't get sent to the "clickout" callback
+ */
+ count = 0;
+ handler.deactivate();
+ handler.activate();
+ feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,0));
+ feature.layer = layer;
+
+ // mock a click on a feature
+ layer.getFeatureFromEvent = function(evt) {return feature};
+ handler.handle({type: "click"});
+ // confirm that callback got feature
+ t.eq(count, 1, "[clickout] callback called once on in");
+ t.eq(lastType, "click", "[clickout] click callback called on in");
+ t.eq(lastHandled.length, 1, "[clickout] callback called with one feature");
+ t.eq(lastHandled[0].id, feature.id, "[clickout] callback called with correct feature");
+
+ // now mock a click off a destroyed feature
+ feature.destroy();
+ layer.getFeatureFromEvent = function(evt) {return null};
+ handler.handle({type: "click"});
+ // confirm that callback does not get called
+ if(count === 1) {
+ t.ok(true, "[clickout] callback not called when clicking out of a destroyed feature");
+ } else {
+ t.fail("[clickout] callback called when clicking out of a destroyed feature: " + lastType);
+ }
+
+ /**
+ * Test that a destroyed feature doesn't get sent to the "over" callback
+ */
+ count = 0;
+ handler.deactivate();
+ handler.activate();
+ feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,0));
+ feature.layer = layer;
+ // mock mousemove over a feature
+ layer.getFeatureFromEvent = function(evt) {return feature};
+ handler.handle({type: "mousemove"});
+ // confirm that feature was handled
+ t.eq(count, 1, "[over] callback called once");
+ t.eq(lastType, "over", "[over] correct callback type");
+ t.eq(lastHandled[0].id, feature.id, "[over] correct feature sent to callback");
+
+ // now destroy the feature and confirm that the callback is not called again
+ feature.destroy();
+ handler.handle({type: "mousemove"});
+ if(count === 1) {
+ t.ok(true, "[over] callback not called after destroy");
+ } else {
+ t.fail("[over] callback called after destroy: " + lastType);
+ }
+
+ /**
+ * Test that a destroyed feature doesn't get sent to the "out" callback
+ */
+ count = 0;
+ handler.deactivate();
+ handler.activate();
+ feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,0));
+ feature.layer = layer;
+
+ // mock a mousemove over a feature
+ layer.getFeatureFromEvent = function(evt) {return feature};
+ handler.handle({type: "mousemove"});
+ // confirm that callback got feature
+ t.eq(count, 1, "[out] callback called once on over");
+ t.eq(lastType, "over", "[out] click callback called on over");
+ t.eq(lastHandled.length, 1, "[out] callback called with one feature");
+ t.eq(lastHandled[0].id, feature.id, "[out] callback called with correct feature");
+
+ // now mock a click off a destroyed feature
+ feature.destroy();
+ layer.getFeatureFromEvent = function(evt) {return null};
+ handler.handle({type: "mousemove"});
+ // confirm that callback does not get called
+ if(count === 1) {
+ t.ok(true, "[out] callback not called when moving out of a destroyed feature");
+ } else {
+ t.fail("[out] callback called when moving out of a destroyed feature: " + lastType);
+ }
+
+ handler.destroy();
+ }
+
+ function test_click_tolerance(t) {
+ t.plan(3);
+
+ var map, control, layer, feature, evtPx;
+ var clicks, callbacks, handler;
+
+ map = new OpenLayers.Map('map', {controls: []});
+ control = new OpenLayers.Control();
+ map.addControl(control);
+ layer = new OpenLayers.Layer();
+ map.addLayer(layer);
+
+ feature = new OpenLayers.Feature.Vector();
+ feature.layer = layer;
+
+ evtPx = {
+ xy: new OpenLayers.Pixel(Math.random(), Math.random()),
+ type: "click"
+ };
+
+ // override the layer's getFeatureFromEvent func so that it always
+ // returns newFeature
+ layer.getFeatureFromEvent = function(evt) { return feature; };
+
+ callbacks = {
+ click: function() {
+ clicks++;
+ }
+ };
+
+ handler = new OpenLayers.Handler.Feature(
+ control, layer, callbacks, {clickTolerance: 4});
+ handler.activate();
+
+ // distance between down and up is 1, which is
+ // lower than clickTolerance so "click" should trigger
+ handler.down = {x: 0, y: 0};
+ handler.up = {x: 1, y: 0};
+ clicks = 0;
+ map.events.triggerEvent("click", evtPx);
+ t.eq(clicks, 1, "click callback triggers when tolerance is not reached (lower than)");
+
+ // distance between down and up is 4, which is
+ // equal to clickTolerance so "click" should trigger
+ handler.down = {x: 0, y: 0}; // cached handler.down cleared (#857)
+ handler.up = {x: 0, y: 4};
+ clicks = 0;
+ map.events.triggerEvent("click", evtPx);
+ t.eq(clicks, 1, "click callback triggers when tolerance is not reached (equal to)");
+
+ // distance between down and up is 5, which is
+ // greater than clickTolerance so "click" should not trigger
+ handler.down = {x: 0, y: 0}; // cached handler.down cleared (#857)
+ handler.up = {x: 5, y: 0};
+ clicks = 0;
+ map.events.triggerEvent("click", evtPx);
+ t.eq(clicks, 0, "click callback does not trigger when tolerance is reached");
+ }
+
+ function test_multitouch_canvas(t) {
+ var supported = OpenLayers.Renderer.Canvas.prototype.supported();
+ if (!supported) { t.plan(0); return; }
+
+ t.plan(1);
+
+ // set up
+
+ var log;
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.Vector('vectors', {
+ renderers: ['Canvas'],
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+
+ var control = new OpenLayers.Control();
+ var handler = new OpenLayers.Handler.Feature(control, layer,
+ {click: function() { log++; }});
+ control.handler = handler;
+ map.addControl(control);
+ control.activate();
+
+ var feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(0, 0));
+ layer.addFeatures(feature);
+
+ map.zoomToMaxExtent();
+
+ // test
+
+ // mock getMousePosition on the events object to make
+ // sure scrolls, offsets and leftop do not interfere
+ map.events.getMousePosition = function(evt) {
+ return new OpenLayers.Pixel(evt.clientX,
+ evt.clientY);
+ };
+
+ log = 0;
+ var evt = {
+ type: 'touchstart',
+ touches: [{
+ clientX: 100,
+ clientY: 75
+ }, {
+ clientX: 200,
+ clientY: 75
+ }]
+ };
+ map.events.handleBrowserEvent(evt);
+ t.eq(log, 0, "no feature selection when multi-touching");
+
+ // tear down
+
+ map.destroy();
+ }
+
+ function test_layerorder(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var base = new OpenLayers.Layer(null, {isBaseLayer: true});
+ var vector = new OpenLayers.Layer.Vector();
+ map.addLayers([base, vector]);
+ map.addControl(new OpenLayers.Control.SelectFeature(vector, {autoActivate: true}));
+ map.zoomToMaxExtent();
+ t.eq(parseInt(vector.getZIndex(), 10), 725, "vector layer's zIndex correct");
+ map.events.triggerEvent("changelayer");
+ t.eq(parseInt(vector.getZIndex(), 10), 725, "vector layer's zIndex still correct after changelayer event");
+
+ }
+
+ function test_clear_event_position_cache(t) {
+ t.plan(2);
+
+ var map, control, layer, feature, evtPx;
+
+ map = new OpenLayers.Map('map', {controls: []});
+ control = new OpenLayers.Control();
+ map.addControl(control);
+ layer = new OpenLayers.Layer();
+ layer.getFeatureFromEvent = function(evt) { return feature; };
+ map.addLayer(layer);
+ feature = new OpenLayers.Feature.Vector();
+ feature.layer = layer;
+
+ evtPx = {
+ xy: new OpenLayers.Pixel(Math.random(), Math.random()),
+ type: "click"
+ };
+
+ handler = new OpenLayers.Handler.Feature(
+ control, layer, {}, {});
+ handler.activate();
+
+ handler.down = {x: 0, y: 0};
+ handler.up = {x: 1, y: 0};
+ map.events.triggerEvent("click", evtPx);
+ t.eq(handler.down, null, "cached mousedown position is cleared after handling click");
+ t.eq(handler.up, null, "cached mouseup position is cleared after handling click")
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 300px; height: 150px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Handler/Hover.html b/misc/openlayers/tests/Handler/Hover.html
new file mode 100644
index 0000000..150218a
--- /dev/null
+++ b/misc/openlayers/tests/Handler/Hover.html
@@ -0,0 +1,136 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Handler_Hover_events(t) {
+ t.plan(10);
+
+ var map = new OpenLayers.Map('map');
+ var control = {
+ map: map
+ };
+ map.events.registerPriority = function(type, obj, func) {
+ var r = func();
+ if(typeof r == "string") {
+ // this is one of the mock handler methods
+ t.eq(OpenLayers.Util.indexOf(nonevents, type), -1,
+ "registered method is not one of the events " +
+ "that should not be handled");
+ t.ok(OpenLayers.Util.indexOf(events, type) > -1,
+ "activate calls registerPriority with browser event: " + type);
+ t.eq(typeof func, "function",
+ "activate calls registerPriority with a function");
+ t.eq(func(), type,
+ "activate calls registerPriority with the correct method");
+ t.eq(obj["CLASS_NAME"], "OpenLayers.Handler.Hover",
+ "activate calls registerPriority with the handler");
+ }
+ }
+ function setMethod(key) {
+ handler[key] = function() {return key};
+ }
+
+ // list below events that should be handled (events) and those
+ // that should not be handled (nonevents) by the handler
+ var events = ["mousemove", "mouseout"];
+ var nonevents = ["mousedown", "mouseup", "click", "dblclick", "resize", "focus", "blur"];
+ var handler = new OpenLayers.Handler.Hover(control);
+ // set browser event like properties on the handler
+ for(var i=0; i<events.length; ++i) {
+ setMethod(events[i]);
+ }
+ handler.activate();
+ }
+
+ function test_Handler_Hover_callbacks(t) {
+ t.plan(8);
+
+ var map = new OpenLayers.Map('map', {controls: []});
+
+ var control = {
+ map: map
+ };
+
+ var timers = {};
+ var sto = window.setTimeout;
+ window.setTimeout = function(func, delay) {
+ var key = Math.random();
+ timers[key] = true;
+ t.ok(typeof func == "function",
+ "setTimeout called with a function");
+ t.eq(delay, handler.delay,
+ "setTimeout called with proper delay");
+ // execute function that is supposed to be delayed
+ func();
+ return key;
+ }
+ var cto = window.clearTimeout;
+ window.clearTimeout = function(key) {
+ if(timers[key] === true) {
+ delete timers[key];
+ } else {
+ t.fail("clearTimeout called with non-existent timerId");
+ }
+ }
+
+ var handler = new OpenLayers.Handler.Hover(control, {});
+ handler.activate();
+ var testEvt;
+
+ // test pause and move callbacks - four tests here (2 from setTimeout above)
+ testEvt = {id: Math.random()};
+ handler.callbacks = {
+ "pause": function(evt) {
+ t.eq(evt.id, testEvt.id,
+ "pause callback called with correct evt");
+ },
+ "move": function(evt) {
+ t.eq(evt.id, testEvt.id,
+ "move callback called with correct evt");
+ }
+ };
+ map.events.triggerEvent("mousemove", testEvt);
+ handler.clearTimer();
+
+ // test pixelTolerance - four tests here (2 from setTimeout above)
+ handler.pixelTolerance = 2;
+ handler.px = new OpenLayers.Pixel(0, 0);
+ testEvt = {
+ xy: new OpenLayers.Pixel(0, 1)
+ };
+ // mouse moves one pixel, callbacks shouldn't be called
+ handler.callbacks = {
+ "pause": function(evt) {
+ t.fail("(pixelTolerance met) pause callback shouldn't be called");
+ },
+ "move": function(evt) {
+ t.fail("(pixelTolerance met) move callback shoudln't be called");
+ }
+ };
+ map.events.triggerEvent("mousemove", testEvt);
+ handler.clearTimer();
+ handler.px = new OpenLayers.Pixel(0, 0);
+ testEvt = {
+ xy: new OpenLayers.Pixel(3, 3)
+ };
+ // mouse moves 3x3 pixels, callbacks should be called
+ handler.callbacks = {
+ "pause": function(evt) {
+ t.ok(evt.xy == testEvt.xy, "(pixelTolerance unmet) pause callback called");
+ },
+ "move": function(evt) {
+ t.ok(evt == testEvt, "(pixelTolerance unmet) move callback called");
+ }
+ };
+ map.events.triggerEvent("mousemove", testEvt);
+ handler.clearTimer();
+
+ window.setTimeout = sto;
+ window.clearTimeout = cto;
+ }
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 300px; height: 150px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Handler/Keyboard.html b/misc/openlayers/tests/Handler/Keyboard.html
new file mode 100644
index 0000000..4a72c92
--- /dev/null
+++ b/misc/openlayers/tests/Handler/Keyboard.html
@@ -0,0 +1,150 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Handler_Keyboard_initialize(t) {
+ t.plan(3);
+ var control = new OpenLayers.Control();
+ control.id = Math.random();
+ var callbacks = {foo: "bar"};
+ var options = {bar: "foo"};
+
+ var oldInit = OpenLayers.Handler.prototype.initialize;
+
+ OpenLayers.Handler.prototype.initialize = function(con, call, opt) {
+ t.eq(con.id, control.id,
+ "constructor calls parent with the correct control");
+ t.eq(call, callbacks,
+ "constructor calls parent with the correct callbacks");
+ t.eq(opt, options,
+ "constructor calls parent with the correct options");
+ }
+ var handler = new OpenLayers.Handler.Keyboard(control, callbacks,
+ options);
+
+ OpenLayers.Handler.prototype.initialize = oldInit;
+ }
+
+ function test_Handler_Keyboard_destroy(t) {
+ t.plan(3);
+ var control = new OpenLayers.Control();
+ var handler = new OpenLayers.Handler.Keyboard(control);
+ var old = OpenLayers.Handler.prototype.destroy;
+ t.ok(handler.eventListener != null,
+ "eventListener is not null before destroy");
+ OpenLayers.Handler.prototype.destroy = function() {
+ t.ok(true, "destroy calls destroy on correct parent");
+ };
+ handler.destroy();
+ t.ok(handler.eventListener == null,
+ "eventListeners is null after destroy");
+ OpenLayers.Handler.prototype.destroy = old;
+ }
+
+ function test_Handler_Keyboard_activate(t) {
+ t.plan(15);
+
+ var log;
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Keyboard(control);
+
+ // mock OpenLayers.Event.observe
+ var old = OpenLayers.Event.stopObserving;
+ OpenLayers.Event.observe = function(obj, type, method) {
+ log[type] = obj;
+ var validType = OpenLayers.Util.indexOf(["keydown", "keyup"], type) != -1;
+ t.ok(validType, "activate calls observe for " + type);
+ t.ok(method == handler.eventListener,
+ "activate calls observing with correct method");
+ };
+
+ handler.active = true;
+ var activated = handler.activate();
+ t.ok(!activated,
+ "activate returns false if the handler was already active");
+
+ log = {};
+ handler.active = false;
+ handler.observeElement = map.div;
+ activated = handler.activate();
+ t.ok(log['keydown'] == map.div,
+ "activate calls observing for keydown with correct object");
+ t.ok(log['keyup'] == map.div,
+ "activate calls observing for keyup with correct object");
+ t.ok(activated,
+ "activate returns true if the handler was not already active");
+
+ log = {};
+ handler.active = false;
+ handler.observeElement = null;
+ activated = handler.activate();
+ t.ok(log['keydown'] == document,
+ "activate calls observing for keydown with correct object");
+ t.ok(log['keyup'] == document,
+ "activate calls observing for keyup with correct object");
+ t.ok(activated,
+ "activate returns true if the handler was not already active");
+
+ OpenLayers.Event.observe = old;
+ map.destroy();
+ }
+
+ function test_Handler_Keyboard_deactivate(t) {
+ t.plan(15);
+
+ var log;
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Keyboard(control);
+
+ // mock OpenLayers.Event.stopObserving
+ var old = OpenLayers.Event.stopObserving;
+ OpenLayers.Event.stopObserving = function(obj, type, method) {
+ log[type] = obj;
+ var validType = OpenLayers.Util.indexOf(["keydown", "keyup"], type) != -1;
+ t.ok(validType, "deactivate calls stopObserving for " + type);
+ t.ok(method == handler.eventListener,
+ "deactivate calls stopObserving with correct method");
+ };
+
+ handler.active = false;
+ var deactivated = handler.deactivate();
+ t.ok(!deactivated,
+ "deactivate returns false if the handler was not already active");
+
+ log = {};
+ handler.active = true;
+ handler.observeElement = map.div;
+ deactivated = handler.deactivate();
+ t.ok(log['keydown'] == map.div,
+ "deactivate calls stopObserving for keydown with correct object");
+ t.ok(log['keyup'] == map.div,
+ "deactivate calls stopObserving for keyup with correct object");
+ t.ok(deactivated,
+ "deactivate returns true if the handler was active already");
+
+ log = {};
+ handler.active = true;
+ handler.observeElement = document;
+ deactivated = handler.deactivate();
+ t.ok(log['keydown'] == document,
+ "deactivate calls stopObserving for keydown with correct object");
+ t.ok(log['keyup'] == document,
+ "deactivate calls stopObserving for keyup with correct object");
+ t.ok(deactivated,
+ "deactivate returns true if the handler was active already");
+
+ OpenLayers.Event.stopObserving = old;
+ map.destroy();
+ }
+
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 300px; height: 150px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Handler/MouseWheel.html b/misc/openlayers/tests/Handler/MouseWheel.html
new file mode 100644
index 0000000..687a31d
--- /dev/null
+++ b/misc/openlayers/tests/Handler/MouseWheel.html
@@ -0,0 +1,182 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Handler_MouseWheel_constructor(t) {
+ t.plan(3);
+ var control = new OpenLayers.Control();
+ control.id = Math.random();
+ var callbacks = {foo: "bar"};
+ var options = {bar: "foo"};
+
+ var oldInit = OpenLayers.Handler.prototype.initialize;
+
+ OpenLayers.Handler.prototype.initialize = function(con, call, opt) {
+ t.eq(con.id, control.id,
+ "constructor calls parent with the correct control");
+ t.eq(call, callbacks,
+ "constructor calls parent with the correct callbacks");
+ t.eq(opt, options,
+ "constructor calls parent with the correct options");
+ }
+ var handler = new OpenLayers.Handler.MouseWheel(control, callbacks, options);
+
+ OpenLayers.Handler.prototype.initialize = oldInit;
+ }
+
+ function test_Handler_MouseWheel_activate(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.MouseWheel(control);
+ handler.active = true;
+ var activated = handler.activate();
+ t.ok(!activated,
+ "activate returns false if the handler was already active");
+ handler.active = false;
+ activated = handler.activate();
+ t.ok(activated,
+ "activate returns true if the handler was not already active");
+ }
+
+ function test_Handler_MouseWheel_mousePosition(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer.WMS("","",{}));
+ map.zoomToMaxExtent();
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var pass = false;
+ var handler = new OpenLayers.Handler.MouseWheel(control, {'up':
+ function (evt) {
+ if (evt.xy) { pass = true; }
+ }
+ });
+ handler.setMap(map);
+ handler.activate();
+ var delta = 120;
+ handler.onWheelEvent({'target':map.layers[0].div, wheelDelta: delta});
+ t.ok(pass, "evt.xy was set even without a mouse move");
+ }
+
+ function test_Handler_MouseWheel_events(t) {
+ t.plan(6);
+
+ var map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer.WMS("","",{}));
+ map.zoomToMaxExtent();
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var deltaZ;
+ var handler = new OpenLayers.Handler.MouseWheel(control, {
+ 'up': function(evt, delta){
+ deltaZ = delta;
+ }
+ }, {interval: 200});
+
+ // list below events that should be handled (events) and those
+ // that should not be handled (nonevents) by the handler
+ var events = ["mousemove"];
+ var nonevents = ["dblclick", "resize", "focus", "blur"];
+ map.events.registerPriority = function(type, obj, func) {
+ var r = func();
+ if(typeof r == "string") {
+ t.eq(OpenLayers.Util.indexOf(nonevents, type), -1,
+ "registered method is not one of the events " +
+ "that should not be handled");
+ t.ok(OpenLayers.Util.indexOf(events, type) > -1,
+ "activate calls registerPriority with browser event: " + type);
+ t.eq(typeof func, "function",
+ "activate calls registerPriority with a function");
+ t.eq(func(), type,
+ "activate calls registerPriority with the correct method");
+ t.eq(obj["CLASS_NAME"], "OpenLayers.Handler.MouseWheel",
+ "activate calls registerPriority with the handler");
+ }
+ }
+
+ // set browser event like properties on the handler
+ for(var i=0; i<events.length; ++i) {
+ setMethod(events[i]);
+ }
+ function setMethod(key) {
+ handler[key] = function() {return key};
+ }
+
+ var activated = handler.activate();
+
+ var delta = 120;
+ handler.onWheelEvent({'target':map.layers[0].div, wheelDelta: delta});
+ handler.onWheelEvent({'target':map.layers[0].div, wheelDelta: delta});
+ t.delay_call(1, function() {
+ t.eq(deltaZ, 2, "Multiple scroll actions triggered one event when interval is set");
+ });
+ }
+
+ function test_Handler_MouseWheel_cumulative(t) {
+ t.plan(2);
+
+ var deltaUp = 0, ticks = 0;
+ var callbacks = {
+ up: function(evt, delta) {
+ deltaUp += delta;
+ ticks++;
+ }
+ };
+
+ var map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer.WMS("","",{}));
+ map.zoomToMaxExtent();
+ var control = new OpenLayers.Control({});
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.MouseWheel(control, callbacks, {
+ interval: 150,
+ cumulative: false,
+ maxDelta: 6
+ });
+
+ var delta = 120;
+ // generate 20 scroll up in non cumulative mode
+ for (var i=0; i < 20; i++) {
+ handler.onWheelEvent({'target':map.layers[0].div, wheelDelta: delta});
+ }
+
+ t.delay_call(2, function() {
+ t.eq(deltaUp / ticks, 1, "Cumulative mode works");
+ t.eq(ticks, 4, "up called 4x with maxDelta of 6");
+ });
+ }
+
+ function test_Handler_MouseWheel_deactivate(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.MouseWheel(control);
+ handler.active = false;
+ var deactivated = handler.deactivate();
+ t.ok(!deactivated,
+ "deactivate returns false if the handler was not already active");
+ handler.active = true;
+ deactivated = handler.deactivate();
+ t.ok(deactivated,
+ "deactivate returns true if the handler was active already");
+ }
+ function test_handler_MouseWheel_destroy(t) {
+ t.plan(1);
+ var control = new OpenLayers.Control();
+ var handler = new OpenLayers.Handler.MouseWheel(control);
+ handler.deactivate = function() {
+ t.ok(true, "Deactivate called one time.");
+ }
+ handler.destroy();
+ }
+
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 300px; height: 150px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Handler/Path.html b/misc/openlayers/tests/Handler/Path.html
new file mode 100644
index 0000000..8351eea
--- /dev/null
+++ b/misc/openlayers/tests/Handler/Path.html
@@ -0,0 +1,1464 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Handler_Path_constructor(t) {
+ t.plan(3);
+ var control = new OpenLayers.Control();
+ control.id = Math.random();
+ var callbacks = {foo: "bar"};
+ var options = {bar: "foo"};
+
+ var oldInit = OpenLayers.Handler.prototype.initialize;
+
+ OpenLayers.Handler.prototype.initialize = function(con, call, opt) {
+ t.eq(con.id, control.id,
+ "constructor calls parent with the correct control");
+ t.eq(call, callbacks,
+ "constructor calls parent with the correct callbacks");
+ t.eq(opt, options,
+ "constructor calls parent with the correct options");
+ }
+ var handler = new OpenLayers.Handler.Path(control, callbacks, options);
+
+ OpenLayers.Handler.prototype.initialize = oldInit;
+ }
+
+ function test_Handler_Path_activation(t) {
+ t.plan(5);
+ var log = [];
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control();
+ var handler = new OpenLayers.Handler.Path(control, {});
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+ handler.active = true;
+
+ var activated = handler.activate();
+ t.ok(!activated,
+ "activate returns false if the handler was already active");
+ handler.active = false;
+ activated = handler.activate();
+ t.ok(activated,
+ "activate returns true if the handler was not already active");
+ t.ok(handler.layer instanceof OpenLayers.Layer.Vector,
+ "activate creates a vector layer");
+ t.ok(handler.layer.map == map,
+ "activate adds the vector layer to the map");
+ activated = handler.deactivate();
+ t.ok(activated,
+ "deactivate returns true if the handler was active already");
+
+ map.destroy();
+ }
+
+ // See: http://trac.osgeo.org/openlayers/ticket/3179
+ function test_activate_before_map_is_centered(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map', {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control();
+ var handler = new OpenLayers.Handler.Path(control, {});
+ control.handler = handler;
+ map.addControl(control);
+
+ var error;
+ try {
+ handler.activate();
+ error = false;
+ } catch(err) {
+ error = true;
+ }
+ t.ok(!error, "no error on activate");
+ }
+
+ function test_bounds(t) {
+ t.plan(4);
+ var geometry;
+ var map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer.WMS("", "", {}));
+ map.zoomToMaxExtent();
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Path(control, {},
+ {stopDown: true, stopUp: true});
+ var activated = handler.activate();
+ // click on (150, 75)
+ var evt = {xy: new OpenLayers.Pixel(150, 75), which: 1};
+ handler.mousemove(evt);
+ handler.mousedown(evt);
+ handler.mouseup(evt);
+ t.eq(handler.layer.features.length, 2,
+ "There are two features in the layer after first click.");
+ // click on (175, 100)
+ evt = {xy: new OpenLayers.Pixel(175, 100), which: 1};
+ handler.mousemove(evt);
+ handler.mousedown(evt);
+ handler.mouseup(evt);
+ t.eq(handler.layer.features.length, 2,
+ "There are two features in the layer after second click.");
+ t.ok(handler.line.geometry.getBounds().equals(
+ new OpenLayers.Bounds(0,-35.15625,35.15625,0)),
+ "Correct bounds");
+ // mousedown on (175, 100)
+ evt = {xy: new OpenLayers.Pixel(175, 100), which: 1};
+ handler.mousedown(evt);
+ // mousemove to (125, 100)
+ evt = {xy: new OpenLayers.Pixel(125, 100), which: 1};
+ handler.mousemove(evt);
+ // test that the bounds have changed
+ t.ok(!handler.line.geometry.getBounds().equals(
+ new OpenLayers.Bounds(0,-35.15625,35.15625,0)),
+ "Correct bounds after dragging without letting go. " +
+ "(Came out as " + handler.line.geometry.getBounds().toBBOX() +
+ ".)");
+ map.destroy();
+ }
+
+ function test_callbacks(t) {
+ t.plan(39);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var logs = [], log;
+ var handler = new OpenLayers.Handler.Path(control, {
+ create: function() {
+ logs.push({type: "create", args: arguments});
+ },
+ point: function() {
+ logs.push({type: "point", args: arguments});
+ },
+ modify: function() {
+ logs.push({type: "modify", args: arguments});
+ },
+ done: function() {
+ logs.push({type: "done", args: arguments});
+ },
+ cancel: function() {
+ logs.push({type: "cancel", args: arguments});
+ }
+ },
+ {
+ pixelTolerance: 0
+ });
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+
+ // mouse move
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ t.eq(logs.length, 2, "[mousemove] called back twice");
+ log = logs.shift();
+ t.eq(log.type, "create", "[mousemove] create called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-150, 75),
+ "[mousemove] correct point");
+ t.ok(log.args[1] === handler.line,
+ "[mousemove] correct feature");
+ log = logs.shift();
+ t.eq(log.type, "modify", "[mousemove] modify called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-150, 75),
+ "[mousemove] correct point");
+ t.ok(log.args[1] === handler.line,
+ "[mousemove] correct feature");
+ // mouse down
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ t.eq(logs.length, 1, "[mousedown] called back");
+ log = logs.shift();
+ t.eq(log.type, "modify", "[mousedown] modify called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-150, 75),
+ "[mousedown] correct point");
+ t.ok(log.args[1] === handler.line,
+ "[mousedown] correct feature");
+ // mouse up
+ handler.mouseup({type: "mouseup", xy: new OpenLayers.Pixel(0, 0)});
+ t.eq(logs.length, 2, "[mouseup] called back twice");
+ log = logs.shift();
+ t.eq(log.type, "point", "[mouseup] point called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-150, 75),
+ "[mouseup] correct point");
+ t.geom_eq(log.args[1],
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(-150, 75),
+ new OpenLayers.Geometry.Point(-150, 75)
+ ]), "[mouseup] correct line");
+ log = logs.shift();
+ t.eq(log.type, "modify", "[mouseup] modify called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-150, 75),
+ "[mouseup] correct point");
+ t.ok(log.args[1] == handler.line,
+ "[mouseup] correct feature");
+ // mouse move
+ handler.mousemove({type: "mousemove",
+ xy: new OpenLayers.Pixel(1, 1)});
+ t.eq(logs.length, 1, "[mousemove] called back");
+ log = logs.shift();
+ t.eq(log.type, "modify", "[mousemove] modify called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-149, 74),
+ "[mousemove] correct point");
+ t.ok(log.args[1] === handler.line,
+ "[mousemove] correct feature");
+ // mouse move
+ handler.mousemove({type: "mousemove",
+ xy: new OpenLayers.Pixel(10, 10)});
+ t.eq(logs.length, 1, "[mousemove] called back");
+ log = logs.shift();
+ t.eq(log.type, "modify", "[mousemove] modify called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-140, 65),
+ "[mousemove] correct point");
+ t.ok(log.args[1] === handler.line,
+ "[mousemove] correct feature");
+ // mouse down
+ handler.mousedown({type: "mousedown",
+ xy: new OpenLayers.Pixel(10, 10)});
+ t.eq(logs.length, 1, "[mousedown] called back");
+ log = logs.shift();
+ t.eq(log.type, "modify", "[mousedown] modify called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-140, 65),
+ "[mousedown] correct point");
+ t.ok(log.args[1] === handler.line,
+ "[mousedown] correct feature");
+ // mouse up ("point", "modify")
+ handler.mouseup({type: "mouseup",
+ xy: new OpenLayers.Pixel(10, 10)});
+ t.eq(logs.length, 2, "[mouseup] called back twice");
+ log = logs.shift();
+ log = logs.shift();
+ // mouse down
+ handler.mousedown({type: "mousedown",
+ xy: new OpenLayers.Pixel(10, 10)});
+ t.eq(logs.length, 0, "[mousedown] called back");
+ // mouse up
+ handler.mouseup({type: "mouseup",
+ xy: new OpenLayers.Pixel(10, 10)});
+ t.eq(logs.length, 0, "[mouseup] not called back");
+ // double click
+ handler.dblclick({type: "dblclick",
+ xy: new OpenLayers.Pixel(10, 10)});
+ t.eq(logs.length, 1, "[dblclick] called back");
+ log = logs.shift();
+ t.eq(log.type, "done", "[dblclick] done called");
+ t.geom_eq(log.args[0],
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(-150, 75),
+ new OpenLayers.Geometry.Point(-140, 65)
+ ]),
+ "[dblclick] correct linestring"
+ );
+ // cancel
+ handler.cancel();
+ t.eq(logs.length, 1, "[cancel] called back");
+ log = logs.shift();
+ t.eq(log.type, "cancel", "[cancel] canced called");
+ t.eq(log.args[0], null, "[cancel] got null"
+ );
+
+ map.destroy();
+ }
+
+ function test_toggle_freehand(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Path(control, {
+ done: function(g) {
+ log++;
+ }
+ }, {persist: true});
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+
+ log = 0;
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ t.eq(log, 1, "feature drawn when shift pressed on mousedown");
+
+ log = 0;
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0), shiftKey: false});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ t.eq(log, 0, "feature not drawn when shift not pressed on mousedown");
+ }
+
+ function test_persist(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Path(control, {});
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+
+ handler.persist = false;
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ var feature1 = handler.line;
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1)});
+ handler.dblclick(
+ {type: "dblclick", xy: new OpenLayers.Pixel(1, 1)});
+ t.ok(feature1.layer == null, "a) feature1 destroyed");
+
+ handler.persist = true;
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ var feature2 = handler.line;
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1)});
+ handler.dblclick(
+ {type: "dblclick", xy: new OpenLayers.Pixel(1, 1)});
+ t.ok(feature2.layer != null, "b) feature2 not destroyed");
+
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ var feature3 = handler.line;
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1)});
+ handler.dblclick(
+ {type: "dblclick", xy: new OpenLayers.Pixel(1, 1)});
+ t.ok(feature3.layer != null, "c) feature3 not destroyed");
+ t.ok(feature2.layer == null, "c) feature2 destroyed");
+
+ map.destroy();
+ }
+
+ function test_persist_freehand(t) {
+ t.plan(6);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Path(control, {});
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+
+ handler.persist = false;
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ var feature1 = handler.line;
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ t.ok(feature1.layer == null, "a) feature1 destroyed");
+
+ handler.persist = true;
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ feature2 = handler.line;
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ t.ok(feature2.layer != null, "b) feature2 not destroyed");
+
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ feature3 = handler.line;
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ t.ok(feature3.layer != null, "c) feature3 not destroyed");
+ t.ok(feature2.layer == null, "c) feature2 destroyed");
+
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ feature4 = handler.line;
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0), shiftKey: false});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ t.ok(feature4.layer != null, "d) feature4 not destroyed");
+ t.ok(feature3.layer == null, "c) feature3 destroyed");
+
+ map.destroy();
+ }
+
+ function test_Handler_Path_destroy(t) {
+ t.plan(6);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer.WMS("", "", {}));
+ map.zoomToMaxExtent();
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Path(control, {foo: 'bar'});
+
+ handler.activate();
+ var evt = {xy: new OpenLayers.Pixel(150, 75), which: 1};
+ handler.mousedown(evt);
+
+ t.ok(handler.layer,
+ "handler has a layer prior to destroy");
+ t.ok(handler.point,
+ "handler has a point prior to destroy");
+ t.ok(handler.line,
+ "handler has a line prior to destroy");
+ handler.destroy();
+ t.eq(handler.layer, null,
+ "handler.layer is null after destroy");
+ t.eq(handler.point, null,
+ "handler.point is null after destroy");
+ t.eq(handler.line, null,
+ "handler.line is null after destroy");
+ map.destroy();
+ }
+
+ function test_maxVertices(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var log = {};
+ var doneCallback = function(evt) {
+ t.ok(evt, 'When maxVertices is reached, the geometry is finalized automatically');
+ };
+ var handler = new OpenLayers.Handler.Path(control, {'done': doneCallback}, {maxVertices: 2});
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ // mock up feature drawing
+ handler.activate();
+ var evt = {xy: new OpenLayers.Pixel(0, 0)};
+ handler.mousemove(evt);
+ handler.mousedown(evt);
+ handler.mouseup(evt);
+ evt = {xy: new OpenLayers.Pixel(20, 20)};
+ handler.mousemove(evt);
+ handler.mousedown(evt);
+ handler.mouseup(evt);
+ evt = {xy: new OpenLayers.Pixel(40, 40)};
+ handler.mousemove(evt);
+ handler.mousedown(evt);
+ handler.mouseup(evt);
+ map.destroy();
+ }
+
+ function test_freehand_maxVertices(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var log = {};
+ var MAX_VERTICES = 2;
+ var doneCallback = function(geo) {
+ t.eq(geo.components.length, MAX_VERTICES,
+ 'When maxVertices is reached, the geometry is finalized automatically');
+ };
+ var handler = new OpenLayers.Handler.Path(control,
+ {'done': doneCallback},
+ {freehand: true,
+ maxVertices: MAX_VERTICES});
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ // mock up feature freehand drawing
+ handler.activate();
+ var evt = {xy: new OpenLayers.Pixel(0, 0)};
+ handler.mousemove(evt);
+ handler.mousedown(evt);
+ evt = {xy: new OpenLayers.Pixel(20, 20)};
+ handler.mousemove(evt);
+ evt = {xy: new OpenLayers.Pixel(40, 40)};
+ handler.mousemove(evt);
+ map.destroy();
+ }
+
+ /**
+ * Helper functions for editing method tests
+ */
+ function editingMethodsSetup() {
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.DrawFeature(
+ layer, OpenLayers.Handler.Path
+ );
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ control.activate();
+ return {
+ handler: control.handler,
+ map: map
+ }
+ }
+ function userClick(handler, x, y) {
+ var px = new OpenLayers.Pixel(x, y);
+ handler.mousemove({type: "mousemove", xy: px});
+ handler.mousedown({type: "mousedown", xy: px});
+ handler.mouseup({type: "mouseup", xy: px});
+ }
+ function userTap(handler, x, y) {
+ var px = new OpenLayers.Pixel(x, y);
+ handler.touchstart({xy: px});
+ handler.touchmove({xy: px});
+ handler.touchend({});
+ }
+
+ /**
+ * Editing method tests: insertXY, insertDeltaXY, insertDirectionXY,
+ * insertDeflectionXY, undo, and redo
+ */
+ function test_insertXY(t) {
+ t.plan(3);
+ var obj = editingMethodsSetup();
+ var map = obj.map;
+ var handler = obj.handler;
+
+ // add points at px(0, 0) and px(10, 10)
+ userClick(handler, 0, 0);
+ userClick(handler, 10, 10);
+ handler.mousemove({type: "mousemove", xy: new OpenLayers.Pixel(50, 50)});
+
+ t.eq(handler.line.geometry.components.length, 3, "line has three points after two clicks");
+
+ // programmatically add a point
+ handler.insertXY(5, 6);
+ t.eq(handler.line.geometry.components.length, 4, "line has four points after insertXY");
+ t.geom_eq(
+ handler.line.geometry.components[2],
+ new OpenLayers.Geometry.Point(5, 6),
+ "third point comes from insertXY"
+ );
+
+ map.destroy();
+
+ }
+
+ function test_insertDeltaXY(t) {
+ t.plan(3);
+ var obj = editingMethodsSetup();
+ var map = obj.map;
+ var handler = obj.handler;
+
+ // add points at px(0, 0) and px(10, 10)
+ userClick(handler, 0, 0);
+ userClick(handler, 10, 10);
+ handler.mousemove({type: "mousemove", xy: new OpenLayers.Pixel(50, 50)});
+
+ t.eq(handler.line.geometry.components.length, 3, "line has three points after two clicks");
+
+ // programmatically add a point
+ handler.insertDeltaXY(1, 2);
+ t.eq(handler.line.geometry.components.length, 4, "line has four points after insert");
+ // expect a point that is offset from previous point
+ var exp = handler.line.geometry.components[1].clone();
+ exp.move(1, 2);
+ t.geom_eq(
+ handler.line.geometry.components[2], exp,
+ "third point is offset by dx,dy from second point"
+ );
+
+ map.destroy();
+ }
+
+ function test_insertDirectionLength(t) {
+ t.plan(4);
+ var obj = editingMethodsSetup();
+ var map = obj.map;
+ var handler = obj.handler;
+
+ // add points at px(0, 0) and px(10, 10)
+ userClick(handler, 0, 0);
+ userClick(handler, 10, 10);
+ handler.mousemove({type: "mousemove", xy: new OpenLayers.Pixel(50, 50)});
+
+ t.eq(handler.line.geometry.components.length, 3, "line has three points after two clicks");
+
+ // programmatically add a point
+ handler.insertDirectionLength(45, 2);
+ t.eq(handler.line.geometry.components.length, 4, "line has four points after insert");
+ var p1 = handler.line.geometry.components[1];
+ var p2 = handler.line.geometry.components[2];
+
+ var direction = Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;
+ t.eq(direction.toFixed(4), (45).toFixed(4), "inserted point offset with correct direction");
+ var length = Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
+ t.eq(length.toFixed(4), (2).toFixed(4), "inserted point offset with correct length");
+
+ map.destroy();
+ }
+
+ function test_insertDeflectionLength(t) {
+ t.plan(4);
+ var obj = editingMethodsSetup();
+ var map = obj.map;
+ var handler = obj.handler;
+
+ // add points at px(0, 0) and px(10, 10)
+ userClick(handler, 0, 0);
+ userClick(handler, 10, 10);
+ handler.mousemove({type: "mousemove", xy: new OpenLayers.Pixel(50, 50)});
+
+ t.eq(handler.line.geometry.components.length, 3, "line has three points after two clicks");
+ var p0 = handler.line.geometry.components[0];
+ var p1 = handler.line.geometry.components[1];
+ // angle of first segment
+ var dir0 = Math.atan2(p1.y - p0.y, p1.x - p0.x) * 180 / Math.PI;
+
+ // programmatically add a point
+ handler.insertDeflectionLength(-30, 5);
+ t.eq(handler.line.geometry.components.length, 4, "line has four points after insert");
+ var p2 = handler.line.geometry.components[2];
+ // angle of second segment
+ var dir1 = Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;
+
+ var deflection = dir1 - dir0;
+ t.eq(deflection.toFixed(4), (-30).toFixed(4), "inserted point offset with correct deflection");
+
+ var length = Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
+ t.eq(length.toFixed(4), (5).toFixed(4), "inserted point offset with correct length");
+
+ map.destroy();
+ }
+
+ function test_undoredo1(t) {
+ t.plan(5);
+ var obj = editingMethodsSetup();
+ var map = obj.map;
+ var handler = obj.handler;
+
+ // add points and move mouse
+ userClick(handler, 0, 0);
+ userClick(handler, 10, 10);
+ userClick(handler, 50, 10);
+ handler.mousemove({type: "mousemove", xy: new OpenLayers.Pixel(50, 50)});
+ var original = handler.line.geometry.clone();
+ var len = original.components.length;
+ t.eq(len, 4, "original has four points after three clicks");
+
+ // one undo
+ handler.undo();
+ var currentLen = handler.line.geometry.components.length;
+ t.eq(currentLen, len-1, "one point removed on undo");
+ t.geom_eq(
+ handler.line.geometry.components[currentLen-1],
+ original.components[len-1],
+ "current point (mouse position) remains the same after undo"
+ );
+ // one redo
+ handler.redo();
+ t.geom_eq(original, handler.line.geometry, "one redo undoes one undo");
+
+ // add point via touch
+ userTap(handler, 10, 50);
+ handler.undo();
+ currentLen = handler.line.geometry.components.length;
+ t.geom_eq(
+ handler.line.geometry.components[currentLen-1],
+ handler.line.geometry.components[currentLen-2],
+ "current point (mouse position) is set to the last digitized " +
+ "point after undo on touch devices"
+ );
+
+ // cleanup
+ map.destroy();
+ }
+
+ function test_undoredo2(t) {
+ t.plan(8);
+ var obj = editingMethodsSetup();
+ var map = obj.map;
+ var handler = obj.handler;
+
+ // add points and move mouse
+ userClick(handler, 0, 0);
+ userClick(handler, 10, 10);
+ userClick(handler, 50, 10);
+ handler.mousemove({type: "mousemove", xy: new OpenLayers.Pixel(50, 50)});
+ var original = handler.line.geometry.clone();
+ var len = original.components.length;
+ t.eq(len, 4, "original has four points after three clicks");
+
+ // two undos
+ handler.undo();
+ handler.undo();
+ var currentLen = handler.line.geometry.components.length;
+ t.eq(currentLen, len-2, "two points removed on two undos");
+ t.geom_eq(
+ handler.line.geometry.components[currentLen-1],
+ original.components[len-1],
+ "current point (mouse position) remains the same after two undos"
+ );
+ // first redo
+ handler.redo();
+ currentLen = handler.line.geometry.components.length;
+ t.eq(currentLen, len-1, "point added in first redo");
+ t.geom_eq(
+ handler.line.geometry.components[currentLen-2],
+ original.components[len-3],
+ "correct point restored in first redo"
+ );
+
+ // second redo
+ handler.redo();
+ currentLen = handler.line.geometry.components.length;
+ t.eq(currentLen, len, "point added in second redo");
+ t.geom_eq(
+ handler.line.geometry.components[currentLen-2],
+ original.components[len-2],
+ "correct point restored in second redo"
+ );
+ t.geom_eq(handler.line.geometry, original, "correct geometry");
+
+ // cleanup
+ map.destroy();
+ }
+
+ function test_undoredo3(t) {
+ t.plan(3);
+ var obj = editingMethodsSetup();
+ var map = obj.map;
+ var handler = obj.handler;
+
+ // add points and move mouse
+ userClick(handler, 0, 0);
+ userClick(handler, 10, 10);
+ userClick(handler, 50, 10);
+ handler.mousemove({type: "mousemove", xy: new OpenLayers.Pixel(50, 50)});
+ var original = handler.line.geometry.clone();
+ var len = original.components.length;
+ t.eq(len, 4, "original has four points after three clicks");
+
+ // gratuitous redos
+ var trouble = false;
+ try {
+ handler.undo();
+ handler.undo();
+ handler.redo();
+ handler.redo();
+ handler.redo();
+ handler.redo();
+ handler.redo();
+ } catch (err) {
+ trouble = true;
+ }
+ t.ok(!trouble, "extra redos cause no ill effects");
+ t.geom_eq(handler.line.geometry, original, "correct geometry");
+
+ // cleanup
+ map.destroy();
+ }
+
+ function test_undoredo4(t) {
+ t.plan(3);
+ var obj = editingMethodsSetup();
+ var map = obj.map;
+ var handler = obj.handler;
+
+ // add points and move mouse
+ userClick(handler, 0, 0);
+ userClick(handler, 10, 10);
+ userClick(handler, 50, 10);
+ handler.mousemove({type: "mousemove", xy: new OpenLayers.Pixel(50, 50)});
+ var original = handler.line.geometry.clone();
+ var len = original.components.length;
+ t.eq(len, 4, "original has four points after three clicks");
+
+ // gratuitous undos
+ var trouble = false;
+ try {
+ handler.undo();
+ handler.undo();
+ handler.undo();
+ handler.undo();
+ handler.undo();
+ handler.undo();
+ handler.undo();
+ } catch (err) {
+ trouble = true;
+ }
+ t.ok(!trouble, "extra undos cause no ill effects");
+ t.eq(handler.line.geometry.components.length, 2, "still left with two points after many undos")
+
+ // cleanup
+ map.destroy();
+ }
+
+ //
+ // Sequence tests
+ //
+ // Sequence tests basically involve executing a sequence of events
+ // and testing the resulting geometry.
+ //
+ // Below are tests for various drawing sequences. Tests can be
+ // added here each a non-working sequence is found.
+ //
+
+ // stopDown:true, stopUp:true, pixelTolerance:1
+ // a) click on (0, 0)
+ // b) mousedown on (1, 1)
+ // c) mouseup on (2, 2)
+ // d) dblclick on (10, 10)
+ function test_sequence1(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Path(control,
+ {done: function(g) { log.geometry = g; }},
+ {stopDown: true, stopUp: true, pixelTolerance: 1}
+ );
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+ log = {};
+
+ // a) click on (0, 0)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0)});
+ // b) mousedown on (1, 1)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(1, 1)});
+ // c) mouseup on (2, 2)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(2, 2)});
+ // d) dblclick on (10, 10)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(10, 10)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(10, 10)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(10, 10)});
+ handler.dblclick(
+ {type: "dblclick", xy: new OpenLayers.Pixel(10, 10)});
+ t.geom_eq(log.geometry,
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(-150, 75), // (0, 0)
+ new OpenLayers.Geometry.Point(-140, 65) // (10, 10)
+ ]), "geometry is correct");
+ }
+
+ // stopDown:false, stopUp:false, pixelTolerance:1
+ // a) click on (0, 0)
+ // b) mousedown on (1, 1)
+ // c) mouseup on (2, 2)
+ // d) dblclick on (10, 10)
+ function test_sequence2(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Path(control,
+ {done: function(g) { log.geometry = g; }},
+ {stopDown: false, stopUp: false, pixelTolerance: 1}
+ );
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+ log = {};
+
+ // a) click on (0, 0)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0)});
+ // b) mousedown on (1, 1)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(1, 1)});
+ // c) mouseup on (2, 2)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(2, 2)});
+ // d) dblclick on (10, 10)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(10, 10)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(10, 10)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(10, 10)});
+ handler.dblclick(
+ {type: "dblclick", xy: new OpenLayers.Pixel(10, 10)});
+ t.geom_eq(log.geometry,
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(-150, 75), // (0, 0)
+ new OpenLayers.Geometry.Point(-140, 65) // (10, 10)
+ ]), "geometry is correct");
+ }
+
+ // a) click
+ // b) dblclick
+ // c) mousedown holding shift key
+ // d) mousemove holding shift key
+ function test_sequence3(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Path(control, {},
+ {
+ pixelTolerance: 0
+ });
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+
+ // a) click on (0, 0)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0)});
+ // b) click on (1, 1)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(1, 1)});
+ // c) click on (1, 1)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(1, 1)});
+ // d) mousemove to (10, 10)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(10, 10), shiftKey: true});
+ t.geom_eq(handler.line.geometry,
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(-150, 75), // (0, 0)
+ new OpenLayers.Geometry.Point(-149, 74), // (1, 1)
+ new OpenLayers.Geometry.Point(-140, 65) // (10, 10)
+ ]), "geometry is correct after mousemove");
+ }
+
+ // a) click
+ // b) dblclick
+ // c) mousedown holding shift key
+ // d) mousemove holding shift key
+ function test_sequence4(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Path(control,
+ {done: function(g) { log.geometry = g; }},
+ {stopDown: false, stopUp: false}
+ );
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+ log = {};
+
+ // a) click on (0, 0)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0)});
+ // b) dblclick on (1, 1)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(1, 1)});
+ handler.dblclick(
+ {type: "dblclick", xy: new OpenLayers.Pixel(1, 1)});
+ t.geom_eq(log.geometry,
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(-150, 75), // (0, 0)
+ new OpenLayers.Geometry.Point(-149, 74) // (1, 1)
+ ]), "geometry is correct after dblclick");
+ // c) mousedown holding shift key on (1, 1)
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(1, 1), shiftKey: true});
+ // d) mousemove holding shift key to (10, 10)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(10, 10), shiftKey: true});
+ t.geom_eq(handler.line.geometry,
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(-149, 74), // (1, 1)
+ new OpenLayers.Geometry.Point(-140, 65) // (10, 10)
+ ]), "geometry is correct after mousemove");
+ }
+
+
+ // a) tap
+ // c) doubletap
+ function test_touch_sequence1(t) {
+ t.plan(19);
+
+ // set up
+
+ var log;
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Path(control, {
+ done: function(g, f) {
+ log = {type: 'done', geometry: g, feature: f};
+ },
+ modify: function(g, f) {
+ log = {type: 'modify', geometry: g, feature: f};
+ }
+ }, {
+ doubleTouchTolerance: 2
+ });
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+ handler.activate();
+
+ // test
+
+ var ret;
+
+ // tap on (1, 0)
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(0, 0)});
+ t.ok(ret, '[touchstart] event propagates');
+ t.eq(log, null, '[touchstart] feature not finalized or modified');
+ ret = handler.touchmove({xy: new OpenLayers.Pixel(1, 0)});
+ t.ok(ret, '[touchmove] event propagates');
+ t.eq(log, null, '[touchmove] feature not finalized or modified');
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log.type, 'modify', '[touchend] feature modified');
+ t.geom_eq(log.geometry, new OpenLayers.Geometry.Point(-149, 75),
+ "[touchend] correct point");
+
+ // doubletap on (10, 10)
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(9, 10)});
+ t.ok(ret, '[touchstart] event propagates');
+ t.eq(log, null, '[touchstart] feature not finalized or modified');
+ ret = handler.touchmove({xy: new OpenLayers.Pixel(10, 10)});
+ t.ok(ret, '[touchmove] event propagates');
+ t.eq(log, null, '[touchmove] feature not finalized or modified');
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log.type, 'modify', '[touchend] feature modified');
+ t.geom_eq(log.geometry, new OpenLayers.Geometry.Point(-140, 65),
+ "[touchend] correct point");
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(11, 10)});
+ t.ok(!ret, '[touchstart] event does not propagate');
+ t.eq(log.type, 'done', '[touchend] feature finalized');
+ t.geom_eq(log.geometry,
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(-149, 75), // (1, 0)
+ new OpenLayers.Geometry.Point(-140, 65) // (10, 10)
+ ]), "[touchstart] final geometry is correct");
+ log = null;
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log, null, '[touchend] feature not finalized or modified');
+
+ // tear down
+
+ map.destroy();
+ }
+
+ // a) tap
+ // b) tap-move
+ // c) doubletap
+ function test_touch_sequence2(t) {
+ t.plan(25);
+
+ // set up
+
+ var log;
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Path(control, {
+ done: function(g, f) {
+ log = {type: 'done', geometry: g, feature: f};
+ },
+ modify: function(g, f) {
+ log = {type: 'modify', geometry: g, feature: f};
+ }
+ }, {
+ doubleTouchTolerance: 2
+ });
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+ handler.activate();
+
+ // test
+
+ var ret;
+
+ // tap on (1, 0)
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(0, 0)});
+ t.ok(ret, '[touchstart] event propagates');
+ t.eq(log, null, '[touchstart] feature not finalized or modified');
+ ret = handler.touchmove({xy: new OpenLayers.Pixel(1, 0)});
+ t.ok(ret, '[touchmove] event propagates');
+ t.eq(log, null, '[touchmove] feature not finalized or modified');
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log.type, 'modify', '[touchend] feature modified');
+ t.geom_eq(log.geometry, new OpenLayers.Geometry.Point(-149, 75),
+ "[touchend] correct point");
+
+ // tap-move
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(9, 10)});
+ t.ok(ret, '[touchstart] event propagates');
+ t.eq(log, null, '[touchstart] feature not finalized or modified');
+ ret = handler.touchmove({xy: new OpenLayers.Pixel(20, 20)});
+ t.ok(ret, '[touchmove] event propagates');
+ t.eq(log, null, '[touchmove] feature not finalized or modified');
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log, null, '[touchend] feature not finalized or modified');
+
+ // doubletap on (10, 10)
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(9, 10)});
+ t.ok(ret, '[touchstart] event propagates');
+ t.eq(log, null, '[touchstart] feature not finalized or modified');
+ ret = handler.touchmove({xy: new OpenLayers.Pixel(10, 10)});
+ t.ok(ret, '[touchmove] event propagates');
+ t.eq(log, null, '[touchmove] feature not finalized or modified');
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log.type, 'modify', '[touchend] feature modified');
+ t.geom_eq(log.geometry, new OpenLayers.Geometry.Point(-140, 65),
+ "[touchend] correct point");
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(11, 10)});
+ t.ok(!ret, '[touchstart] event does not propagate');
+ t.eq(log.type, 'done', '[touchend] feature finalized');
+ t.geom_eq(log.geometry,
+ new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(-149, 75), // (1, 0)
+ new OpenLayers.Geometry.Point(-140, 65) // (10, 10)
+ ]), "[touchstart] final geometry is correct");
+ log = null;
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log, null, '[touchend] feature not finalized or modified');
+
+ // tear down
+
+ map.destroy();
+ }
+
+ function test_persist_one_click_freehand(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Path(control, {}, {persist: true});
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ var feature1 = handler.line;
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1), shiftKey: true});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(1, 1), shiftKey: true});
+ t.ok(feature1.layer != null, "a) feature1 not destroyed");
+
+ // one click freehand
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ var feature2 = handler.line;
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ t.ok(feature2.layer != null, "b) feature2 not destroyed");
+ t.ok(feature1.layer == null, "b) feature1 destroyed");
+
+ map.destroy();
+ }
+
+ function test_set_freehand(t) {
+ t.plan(5);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+
+ var geo, pointsCount;
+ var handler = new OpenLayers.Handler.Path(control, {
+ done: function(g) {
+ geo = g;
+ },
+ point: function() {
+ pointsCount++;
+ }
+ },
+ {freehand: true}
+ );
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+
+ geo = null;
+ pointsCount = 0;
+ // Using mouse events
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(2, 2)});
+ t.ok(geo != null, "feature drawn when mouseup");
+ t.eq(pointsCount, 2, "two points have been added");
+
+ handler.deactivate();
+ var geoMouse = geo;
+
+ handler.activate();
+
+ geo = null;
+ pointsCount = 0;
+ // Using touch events
+ handler.touchstart(
+ {type: "touchstart", xy: new OpenLayers.Pixel(0, 0)});
+ try {
+ handler.touchmove(
+ {type: "touchmove", xy: new OpenLayers.Pixel(1, 1)});
+ handler.touchmove(
+ {type: "touchmove", xy: new OpenLayers.Pixel(2, 2)});
+ handler.touchend(
+ {type: "touchend"});
+ } catch(err) {
+ t.fail("occurred errors using touch events");
+ }
+ t.ok(geo != null, "feature drawn when touchend");
+ t.eq(pointsCount, 2, "two points have been added");
+
+ t.geom_eq(geo, geoMouse,
+ "geometry obtained using the mouse and touch events are the same");
+
+ map.destroy();
+ }
+
+ function test_citeComplaint(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer.OSM());
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Path(control, {});
+ control.handler = handler;
+ map.addControl(control);
+ map.zoomToExtent(new OpenLayers.Bounds(-24225034.496992, -11368938.517442, -14206280.326992, -1350184.3474418));
+ handler.activate();
+ handler.createFeature(new OpenLayers.Pixel(100, 50));
+ t.ok(handler.point.geometry.x < 0, "Geometry started correctly when wrapping the dateline using citeCompliant false");
+ control.deactivate();
+
+ handler = new OpenLayers.Handler.Path(control, {}, {citeCompliant: true});
+ control.handler = handler;
+ control.activate();
+ handler.createFeature(new OpenLayers.Pixel(100, 50));
+ t.ok(handler.point.geometry.x > 0, "Geometry started correctly when wrapping the dateline using citeCompliant true");
+
+ map.destroy();
+ }
+
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 300px; height: 150px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Handler/Pinch.html b/misc/openlayers/tests/Handler/Pinch.html
new file mode 100644
index 0000000..c4883df
--- /dev/null
+++ b/misc/openlayers/tests/Handler/Pinch.html
@@ -0,0 +1,285 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_constructor(t) {
+ t.plan(3);
+ var control = new OpenLayers.Control();
+ control.id = Math.random();
+ var callbacks = {foo: "bar"};
+ var options = {bar: "foo"};
+
+ var oldInit = OpenLayers.Handler.prototype.initialize;
+
+ OpenLayers.Handler.prototype.initialize = function(con, call, opt) {
+ t.eq(con.id, control.id,
+ "constructor calls parent with the correct control");
+ t.eq(call, callbacks,
+ "constructor calls parent with the correct callbacks");
+ t.eq(opt, options,
+ "constructor calls parent with the correct options");
+ };
+ var handler = new OpenLayers.Handler.Pinch(control, callbacks, options);
+
+ OpenLayers.Handler.prototype.initialize = oldInit;
+ }
+
+ function test_activate(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Pinch(control);
+ handler.active = true;
+ var activated = handler.activate();
+ t.ok(!activated,
+ "activate returns false if the handler was already active");
+ handler.active = false;
+ handler.pinching = true;
+ activated = handler.activate();
+ t.ok(activated,
+ "activate returns true if the handler was not already active");
+ t.ok(!handler.pinching,
+ "activate sets pinching to false");
+
+ }
+
+ function test_events(t) {
+ // each handled event should be activated twice when handler is
+ // activated, so:
+ // 27 = 4tests * 2*3events + 1tests * 3events
+ t.plan(27);
+
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Pinch(control);
+
+ // list below events that should be handled (events) and those
+ // that should not be handled (nonevents) by the handler
+ var events = ["touchend", "touchmove", "touchstart"];
+ var nonevents = ["mousedown", "mouseup", "mousemove", "mouseout",
+ "click", "dblclick", "resize", "focus", "blur"];
+ map.events.registerPriority = function(type, obj, func) {
+ // this is one of the mock handler methods
+ t.eq(OpenLayers.Util.indexOf(nonevents, type), -1,
+ "registered method is not one of the events " +
+ "that should not be handled: " + type);
+ t.ok(OpenLayers.Util.indexOf(events, type) > -1,
+ "activate calls registerPriority with browser event: " + type);
+ t.eq(typeof func, "function",
+ "activate calls registerPriority with a function");
+ t.eq(obj["CLASS_NAME"], "OpenLayers.Handler.Pinch",
+ "activate calls registerPriority with the handler");
+ };
+ handler.activate();
+ handler.deactivate();
+
+ // set browser event like properties on the handler
+ for(var i=0; i<events.length; ++i) {
+ setMethod(events[i]);
+ }
+ function setMethod(key) {
+ handler[key] = function() {return key;};
+ }
+
+ map.events.registerPriority = function(type, obj, func) {
+ var r = func();
+ if(typeof r == "string") {
+ t.eq(r, type,
+ "activate calls registerPriority with the correct method");
+ }
+ }
+ handler.activate();
+
+ }
+
+ function test_callbacks(t) {
+ t.plan(32);
+
+ var map = new OpenLayers.Map('map', {controls: [], fallThrough: true});
+
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+
+ // set fake values for touches
+ var testEvents = {
+ start: {
+ type: 'touchstart',
+ touches: [{
+ clientX: 100,
+ clientY: 0
+ }, {
+ clientX: 0,
+ clientY: 0
+ }]
+ },
+ move: {
+ type: 'touchmove',
+ touches: [{
+ clientX: 100,
+ clientY: 0
+ }, {
+ clientX: 20,
+ clientY: 0
+ }]
+ },
+ done: {
+ type: 'touchend',
+ touches: []
+ }
+ };
+
+ // set callback methods
+ var customCb = OpenLayers.Function.False;
+ var cb = function(evt) {
+ var callback = evt.type.replace("touch", "").replace("end", "done");;
+ var tch = testEvents[callback].touches;
+ t.ok(evt.touches[0].clientX == tch[0].clientX &&
+ evt.touches[0].clientY == tch[0].clientY,
+ "touchstart sets first touch position correctly in evt");
+ t.ok(evt.touches[1].clientX == tch[1].clientX &&
+ evt.touches[1].clientY == tch[1].clientY,
+ "touchstart sets second touch position correctly in evt");
+ t.eq(handler.start.distance, 100, "start distance is " +
+ "always the same");
+ customCb.apply(this, arguments);
+ }
+ var callbacks = {
+ start: cb,
+ move: cb,
+ done: function () {
+ customCb.apply(this, arguments);
+ }
+ };
+
+ var handler = new OpenLayers.Handler.Pinch(control, callbacks);
+ handler.activate();
+
+ var old_isMultiTouch = OpenLayers.Event.isMultiTouch;
+ var old_stop = OpenLayers.Event.stop;
+
+ // test single touch
+ OpenLayers.Event.isMultiTouch = function() {
+ return false;
+ }
+
+ // no callbacks with tests expected (pinch not started)
+ map.events.handleBrowserEvent(testEvents.start);
+ // test 1, 2, 3
+ t.ok(!handler.started, "1) touchstart (singletouch) sets started to false");
+ t.eq(handler.start, null, "1) touchstart (singletouch) sets start to null");
+ t.eq(handler.last, null, "1) touchstart (singletouch) sets last to null");
+
+ handler.started = true;
+ handler.start = {
+ distance: 100,
+ delta: 0,
+ scale: 1
+ };
+ handler.last = {
+ distance: 150,
+ delta: 10,
+ scale: 1.5
+ };
+
+ // no callbacks with tests expected (multitouch pinch started, so ignores singletouch)
+ map.events.handleBrowserEvent(testEvents.start);
+ // test 4, 5, 6
+ t.ok(handler.started, "1) touchstart (singletouch) after pinch started is ignored");
+ t.ok(!!handler.start, "1) touchstart (singletouch) after pinch started is ignored");
+ t.ok(!!handler.last, "1) touchstart (singletouch) after pinch started is ignored");
+
+ OpenLayers.Event.stop = function(evt, allowDefault) {
+ if(allowDefault) {
+ t.fail(
+ "touchstart is prevented from falling to other elements");
+ }
+ }
+ OpenLayers.Event.isMultiTouch = function(evt) {
+ var res = old_isMultiTouch(evt);
+ t.ok(res, "fake event is a mutitouch touch event");
+ return res;
+ }
+ customCb = function(evt, pinchdata) {
+ t.eq(pinchdata.distance, 100, "2) calculated distance is correct");
+ t.eq(pinchdata.delta, 0, "2) calculated delta is correct");
+ t.eq(pinchdata.scale, 1, "2) calculated scale is correct");
+ }
+ // test 7, 8, 9, 10, 11, 12, 13
+ map.events.handleBrowserEvent(testEvents.start);
+ // test 14, 15
+ t.ok(handler.started, "2) touchstart sets the started flag to true");
+ t.ok(!handler.pinching, "2) touchstart sets the pinching flag to false");
+
+ customCb = function(evt, pinchdata) {
+ t.eq(pinchdata.distance, 80, "3) calculated distance is correct");
+ t.eq(pinchdata.delta, 20, "3) calculated delta is correct");
+ t.eq(pinchdata.scale, 0.8, "3) calculated scale is correct");
+ }
+ // test 16, 17, 18, 19, 20, 21, 22
+ map.events.handleBrowserEvent(testEvents.move);
+ // test 23, 24
+ t.ok(handler.started, "3) started flag still set to true");
+ t.ok(handler.pinching, "3) touchmove sets the pinching flag to true");
+
+ OpenLayers.Event.isMultiTouch = old_isMultiTouch;
+
+ customCb = function(evt, first, last) {
+ t.eq(first.distance, 100, "4) calculated distance is correct");
+ t.eq(first.delta, 0, "4) calculated delta is correct");
+ t.eq(first.scale, 1, "4) calculated scale is correct");
+ t.eq(last.distance, 80, "4) calculated distance is correct");
+ t.eq(last.delta, 20, "4) calculated delta is correct");
+ t.eq(last.scale, 0.8, "4) calculated scale is correct");
+ }
+ // test 25, 26, 27, 28, 29, 30
+ map.events.handleBrowserEvent(testEvents.done);
+ // test 31, 32
+ t.ok(!handler.started, "4) started flag is set to false");
+ t.ok(!handler.pinching, "4) touchdone sets the pinching flag to false");
+
+ OpenLayers.Event.stop = old_stop;
+
+ // test move or done before start
+ customCb = function(evt) {
+ t.fail("should not pass here")
+ }
+ // no callbacks with tests expected
+ map.events.handleBrowserEvent(testEvents.move);
+ map.events.handleBrowserEvent(testEvents.done);
+
+ }
+
+ function test_deactivate(t) {
+ t.plan(6);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Pinch(control);
+ handler.active = false;
+ var deactivated = handler.deactivate();
+ t.ok(!deactivated,
+ "deactivate returns false if the handler was not already active");
+ handler.active = true;
+ handler.pinching = true;
+ deactivated = handler.deactivate();
+ t.ok(deactivated,
+ "deactivate returns true if the handler was active already");
+ t.ok(!handler.started,
+ "deactivate sets started to false");
+ t.ok(!handler.pinching,
+ "deactivate sets pinching to false");
+ t.ok(handler.start == null,
+ "deactivate sets start to null");
+ t.ok(handler.last == null,
+ "deactivate sets start to null");
+ }
+
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 300px; height: 150px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Handler/Point.html b/misc/openlayers/tests/Handler/Point.html
new file mode 100644
index 0000000..b5a7cf3
--- /dev/null
+++ b/misc/openlayers/tests/Handler/Point.html
@@ -0,0 +1,600 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Handler_Point_constructor(t) {
+ t.plan(3);
+ var control = new OpenLayers.Control();
+ control.id = Math.random();
+ var callbacks = {foo: "bar"};
+ var options = {bar: "foo"};
+
+ var oldInit = OpenLayers.Handler.prototype.initialize;
+
+ OpenLayers.Handler.prototype.initialize = function(con, call, opt) {
+ t.eq(con.id, control.id,
+ "constructor calls parent with the correct control");
+ t.eq(call, callbacks,
+ "constructor calls parent with the correct callbacks");
+ t.eq(opt, options,
+ "constructor calls parent with the correct options");
+ }
+ var handler = new OpenLayers.Handler.Point(control, callbacks, options);
+
+ OpenLayers.Handler.prototype.initialize = oldInit;
+ }
+
+ function test_Handler_Point_activation(t) {
+ t.plan(6);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control();
+ var handler = new OpenLayers.Handler.Point(control, {});
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.active = true;
+ var activated = handler.activate();
+ t.ok(!activated,
+ "activate returns false if the handler was already active");
+ handler.active = false;
+ activated = handler.activate();
+ t.ok(activated,
+ "activate returns true if the handler was not already active");
+ t.ok(handler.layer instanceof OpenLayers.Layer.Vector,
+ "activate creates a vector layer");
+ t.ok(handler.layer.map == map,
+ "activate adds the vector layer to the map");
+ activated = handler.deactivate();
+ t.ok(activated,
+ "deactivate returns true if the handler was active already");
+ var failed = false;
+ try {
+ handler.finalize();
+ msg = "finalizing after deactivation does not throw an error";
+ } catch (err) {
+ failed = true;
+ msg = "finalizing after deactivation throws an error";
+ }
+ t.ok(!failed, msg);
+ map.destroy();
+ }
+
+ // http://trac.osgeo.org/openlayers/ticket/3179
+ function test_activate_before_map_is_centered(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map', {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control();
+ var handler = new OpenLayers.Handler.Point(control, {});
+ control.handler = handler;
+ map.addControl(control);
+
+ var error;
+ try {
+ handler.activate();
+ error = false;
+ } catch(err) {
+ error = true;
+ }
+ t.ok(!error, "no error on activate");
+ }
+
+ function test_Handler_Point_events(t) {
+ t.plan(49);
+ var log = [];
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control();
+ var handler = new OpenLayers.Handler.Point(control, {
+ "create": function(g, f) {
+ log.push({geometry: g, feature: f});
+ }
+ });
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ // list below events that should be handled (events) and those
+ // that should not be handled (nonevents) by the handler
+ var events = ["click", "dblclick", "mousedown", "mouseup", "mousemove", "mouseout", "touchstart", "touchmove", "touchend"];
+ var nonevents = ["resize", "focus", "blur"];
+ map.events.registerPriority = function(type, obj, func) {
+ var r = func();
+ if(typeof r == "string") {
+ // this is one of the mock handler methods
+ t.eq(OpenLayers.Util.indexOf(nonevents, type), -1,
+ "registered method is not one of the events " +
+ "that should not be handled");
+ t.ok(OpenLayers.Util.indexOf(events, type) > -1,
+ "activate calls registerPriority with browser event: " + type);
+ t.eq(typeof func, "function",
+ "activate calls registerPriority with a function");
+ t.eq(func(), type,
+ "activate calls registerPriority with the correct method");
+ t.eq(obj["CLASS_NAME"], "OpenLayers.Handler.Point",
+ "activate calls registerPriority with the handler");
+ }
+ }
+
+ // set browser event like properties on the handler
+ for(var i=0; i<events.length; ++i) {
+ setMethod(events[i]);
+ }
+ function setMethod(key) {
+ handler[key] = function() {return key};
+ }
+
+ var activated = handler.activate();
+ handler.destroy();
+
+ // test that click and dblclick are stopped
+ var handler = new OpenLayers.Handler.Point(control);
+ var oldStop = OpenLayers.Event.stop;
+ OpenLayers.Event.stop = function(evt) {
+ t.ok(evt.type == "click" || evt.type == "dblclick",
+ evt.type + " stopped");
+ }
+ t.eq(handler.click({type: "click"}), false, "click returns false");
+ t.eq(handler.dblclick({type: "dblclick"}), false, "dblclick returns false");
+ OpenLayers.Event.stop = oldStop;
+
+ }
+
+ function test_callbacks(t) {
+ t.plan(24);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var logs = [], log;
+ var handler = new OpenLayers.Handler.Point(control, {
+ create: function() {
+ logs.push({type: "create", args: arguments});
+ },
+ modify: function() {
+ logs.push({type: "modify", args: arguments});
+ },
+ done: function() {
+ logs.push({type: "done", args: arguments});
+ },
+ cancel: function() {
+ logs.push({type: "cancel", args: arguments});
+ }
+ },
+ {
+ pixelTolerance: 0,
+ dblclickTolerance: 0
+ });
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+
+ // mouse down
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ t.eq(logs.length, 2, "[mousedown] called back twice");
+ log = logs.shift();
+ t.eq(log.type, "create", "[mousedown] create called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-150, 75),
+ "[mousedown] correct point");
+ t.geom_eq(log.args[1].geometry,
+ new OpenLayers.Geometry.Point(-150, 75),
+ "[mousedown] correct feature");
+ log = logs.shift();
+ t.eq(log.type, "modify", "[mousedown] modify called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-150, 75),
+ "[mousedown] correct point");
+ t.geom_eq(log.args[1].geometry,
+ new OpenLayers.Geometry.Point(-150, 75),
+ "[mousedown] correct feature");
+ // mouse move
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 0)});
+ t.eq(logs.length, 0, "[mousemove] not called back");
+ // mouse up (no finalize - we moved)
+ handler.mouseup({type: "mouseup", xy: new OpenLayers.Pixel(1, 0)});
+ t.eq(logs.length, 0, "[mouseup] not called back");
+ // mouse move
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 0)});
+ t.eq(logs.length, 1, "[mousemove] called back");
+ log = logs.shift();
+ t.eq(log.type, "modify", "[mousemove] modify called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-148, 75),
+ "[mousemove] correct point");
+ t.geom_eq(log.args[1].geometry,
+ new OpenLayers.Geometry.Point(-148, 75),
+ "[mousemove] correct feature");
+ // mouse down
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(2, 0)});
+ t.eq(logs.length, 1, "[mousedown] called back");
+ log = logs.shift();
+ t.eq(log.type, "modify", "[mousedown] modify called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-148, 75),
+ "[mousedown] correct point");
+ t.geom_eq(log.args[1].geometry,
+ new OpenLayers.Geometry.Point(-148, 75),
+ "[mousedown] correct feature");
+ // mouse up
+ handler.mouseup({type: "mouseup", xy: new OpenLayers.Pixel(2, 0)});
+ t.eq(logs.length, 1, "[mouseup] called back");
+ log = logs.shift();
+ t.eq(log.type, "done", "[mouseup] done called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-148, 75),
+ "[mouseup] correct point");
+ // mouse up on same pixel
+ handler.mouseup({type: "mouseup", xy: new OpenLayers.Pixel(2, 0)});
+ t.eq(logs.length, 0, "[mouseup] not called back");
+ // cancel
+ handler.cancel();
+ t.eq(logs.length, 1, "[cancel] called back");
+ log = logs.shift();
+ t.eq(log.type, "cancel", "[cancel] cancel called");
+ t.eq(log.args[0], null, "[cancel] got null");
+
+ map.destroy();
+ }
+
+ function test_persist(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Point(control, {});
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+
+ handler.persist = false;
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0)});
+ t.eq(handler.layer.features.length, 0,
+ "feature destroyed on mouseup when persist is false");
+
+ handler.persist = true;
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(1, 0)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(1, 0)});
+ t.eq(handler.layer.features.length, 1,
+ "feature not destroyed on mouseup when persist is true");
+ var feature = handler.layer.features[0];
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(2, 0)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(2, 0)});
+ t.ok(handler.layer.features[0] !== feature,
+ "persisted feature destroyed on next mouseup");
+
+ map.destroy();
+ }
+
+
+ function test_Handler_Point_deactivation(t) {
+ t.plan(5);
+ var log = [];
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control();
+ var handler = new OpenLayers.Handler.Point(control, {
+ "cancel": function(g) {
+ log.push({geometry: g});
+ }
+ });
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+ handler.mousemove({xy: new OpenLayers.Pixel(0, 0)});
+ var _layer = handler.layer;
+ var _geometry = handler.point.geometry;
+ handler.deactivate();
+ t.eq(_layer.map, null,
+ "deactivates removes the layer from the map");
+ t.eq(handler.layer, null,
+ "deactivates sets its \"layer\" property to null");
+ t.eq(log.length, 1,
+ "deactivates calls \"cancel\" once");
+ t.ok(log[0].geometry.equals(_geometry),
+ "\"cancel\" called with expected geometry");
+
+ handler.activate();
+ handler.layer.destroy();
+ handler.deactivate();
+ t.eq(handler.layer, null,
+ "deactivate doesn't throw an error if layer was" +
+ " previously destroyed");
+
+ map.destroy();
+ }
+
+ function test_Handler_Point_bounds(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer.WMS("", "", {}));
+ map.zoomToMaxExtent();
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Point(control, {});
+ var activated = handler.activate();
+ var px = new OpenLayers.Pixel(150, 75);
+ var evt = {xy: px, which: 1};
+ handler.mousemove(evt);
+ var lonlat = map.getLonLatFromPixel(px);
+ t.eq(handler.point.geometry.x, lonlat.lon, "X is correct");
+ t.eq(handler.point.geometry.y, lonlat.lat, "Y is correct");
+ t.ok(handler.point.geometry.getBounds().equals(new OpenLayers.Bounds(lonlat.lon,lonlat.lat,lonlat.lon,lonlat.lat)), "Correct bounds");
+ var evt = {xy: new OpenLayers.Pixel(175, 100), which: 1};
+ handler.mousemove(evt);
+ t.ok(!handler.point.geometry.getBounds().equals(new OpenLayers.Bounds(0,0,0,0)), "Bounds changed after moving mouse");
+ }
+
+ function test_Handler_Point_destroy(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer.WMS("", "", {}));
+ map.zoomToMaxExtent();
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Point(control, {foo: 'bar'});
+
+ handler.activate();
+ handler.mousemove({xy: new OpenLayers.Pixel(150, 75)});
+
+ t.ok(handler.layer,
+ "handler has a layer prior to destroy");
+ t.ok(handler.point,
+ "handler has a point prior to destroy");
+ handler.destroy();
+ t.eq(handler.layer, null,
+ "handler.layer is null after destroy");
+ t.eq(handler.point, null,
+ "handler.point is null after destroy");
+ }
+
+ function test_touchstart(t) {
+ // a test to verify that the touchstart function does
+ // unregister the mouse listeners when it's called the
+ // first time
+
+ t.plan(4);
+
+ // set up
+
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1],
+ controls: []
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Point(control, {});
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+ handler.activate();
+
+ var eventTypes = ['mousedown', 'mouseup', 'mousemove', 'click', 'dblclick',
+ 'mouseout'];
+
+ function allRegistered() {
+ var eventType,
+ listeners,
+ listener,
+ flag;
+ for(var i=0, ilen=eventTypes.length; i<ilen; i++) {
+ flag = false;
+ eventType = eventTypes[i];
+ listeners = map.events.listeners[eventType];
+ for(var j=0, jlen=listeners.length; j<jlen; j++) {
+ listener = listeners[j];
+ if(listener.func === handler[eventType] && listener.obj === handler) {
+ flag = true;
+ break;
+ }
+ }
+ if(!flag) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ function noneRegistered() {
+ var eventType,
+ times,
+ flag = false;
+ for(var i=0, ilen=eventTypes.length; i<ilen; i++) {
+ eventType = eventTypes[i];
+ times = map.events.listeners[eventType].length;
+ if (times != 0) {
+ t.fail(eventType + " is registered " + times + " times");
+ flag = true;
+ }
+ }
+ return !flag;
+ }
+
+
+ // test
+
+ t.ok(allRegistered(), 'mouse listeners are registered');
+ handler.touchstart({xy: new OpenLayers.Pixel(0, 0)});
+ t.ok(noneRegistered(), 'mouse listeners are unregistered');
+ t.ok(handler.touch, 'handler.touch is set');
+
+ handler.deactivate();
+ t.ok(!handler.touch, 'handler.touch is not set');
+
+ // tear down
+
+ map.destroy();
+ }
+
+
+ //
+ // Sequence tests
+ //
+ // Sequence tests basically involve executing a sequence of events
+ // and testing the resulting geometry.
+ //
+ // Below are tests for various drawing sequences. Tests can be
+ // added here each a non-working sequence is found.
+ //
+
+ // tap
+ function test_touch_sequence1(t) {
+ t.plan(8);
+
+ // set up
+
+ var log;
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Point(control, {
+ done: function(g, f) {
+ log = {geometry: g, feature: f};
+ }
+ });
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+ handler.activate();
+
+ // test
+
+ var ret;
+
+ // tap on (1, 0)
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(0, 0)});
+ t.ok(ret, '[touchstart] event propagates');
+ t.eq(log, null, '[touchstart] no finalization');
+ t.eq(handler.point, null, '[touchstart] feature not modified');
+ ret = handler.touchmove({xy: new OpenLayers.Pixel(1, 0)});
+ t.ok(ret, '[touchmove] event propagates');
+ t.eq(log, null, '[touchmove] no finalization');
+ t.eq(handler.point, null, '[touchmove] feature not modified');
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.geom_eq(log.geometry, new OpenLayers.Geometry.Point(-149, 75),
+ "[touchend] correct point");
+ // tear down
+
+ map.destroy();
+ }
+
+ // tap-move
+ function test_touch_sequence2(t) {
+ t.plan(9);
+
+ // set up
+
+ var log;
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Point(control, {
+ done: function(g, f) {
+ log = {geometry: g, feature: f};
+ }
+ });
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+ handler.activate();
+
+ // test
+
+ var ret;
+
+ // tap-move (0, 0) -> (9, 0)
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(0, 0)});
+ t.ok(ret, '[touchstart] event propagates');
+ t.eq(log, null, '[touchstart] no finalization');
+ t.eq(handler.point, null, null,
+ '[touchstart] feature not modified');
+ ret = handler.touchmove({xy: new OpenLayers.Pixel(9, 0)});
+ t.ok(ret, '[touchmove] event propagates');
+ t.eq(log, null, '[touchmove] no finalization');
+ t.eq(handler.point, null,
+ '[touchmove] feature not modified');
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log, null, '[touchend] no finalization');
+ t.eq(handler.point, null,
+ '[touchend] feature not modified');
+
+ // tear down
+
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 300px; height: 150px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Handler/Polygon.html b/misc/openlayers/tests/Handler/Polygon.html
new file mode 100644
index 0000000..8fad5dd
--- /dev/null
+++ b/misc/openlayers/tests/Handler/Polygon.html
@@ -0,0 +1,1161 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Handler_Polygon_constructor(t) {
+ t.plan(3);
+ var control = new OpenLayers.Control();
+ control.id = Math.random();
+ var callbacks = {foo: "bar"};
+ var options = {bar: "foo"};
+
+ var oldInit = OpenLayers.Handler.prototype.initialize;
+
+ OpenLayers.Handler.prototype.initialize = function(con, call, opt) {
+ t.eq(con.id, control.id,
+ "constructor calls parent with the correct control");
+ t.eq(call, callbacks,
+ "constructor calls parent with the correct callbacks");
+ t.eq(opt, options,
+ "constructor calls parent with the correct options");
+ }
+ var handler = new OpenLayers.Handler.Polygon(control, callbacks, options);
+
+ OpenLayers.Handler.prototype.initialize = oldInit;
+ }
+
+ function test_Handler_Polygon_activation(t) {
+ t.plan(5);
+ var log = [];
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control();
+ var handler = new OpenLayers.Handler.Polygon(control, {});
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+ handler.active = true;
+
+ var activated = handler.activate();
+ t.ok(!activated,
+ "activate returns false if the handler was already active");
+ handler.active = false;
+ activated = handler.activate();
+ t.ok(activated,
+ "activate returns true if the handler was not already active");
+ t.ok(handler.layer instanceof OpenLayers.Layer.Vector,
+ "activate creates a vector layer");
+ t.ok(handler.layer.map == map,
+ "activate adds the vector layer to the map");
+ activated = handler.deactivate();
+ t.ok(activated,
+ "deactivate returns true if the handler was active already");
+
+ map.destroy();
+ }
+
+ // See: http://trac.osgeo.org/openlayers/ticket/3179
+ function test_activate_before_map_is_centered(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map', {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control();
+ var handler = new OpenLayers.Handler.Polygon(control, {});
+ control.handler = handler;
+ map.addControl(control);
+
+ var error;
+ try {
+ handler.activate();
+ error = false;
+ } catch(err) {
+ error = true;
+ }
+ t.ok(!error, "no error on activate");
+ }
+
+ function test_bounds_stopDown_true(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer.WMS("", "", {}));
+ map.zoomToMaxExtent();
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Polygon(control, {},
+ {stopDown: true, stopUp: true});
+ var activated = handler.activate();
+ // click on (150, 75)
+ var evt = {xy: new OpenLayers.Pixel(150, 75), which: 1};
+ handler.mousemove(evt);
+ handler.mousedown(evt);
+ handler.mouseup(evt);
+ // click on (175, 100)
+ evt = {xy: new OpenLayers.Pixel(175, 100), which: 1};
+ handler.mousemove(evt);
+ handler.mousedown(evt);
+ handler.mouseup(evt);
+ t.ok(handler.line.geometry.getBounds().equals(new OpenLayers.Bounds(0,-35.15625,35.15625,0)), "Correct bounds");
+ // mousedown on (175, 100)
+ evt = {xy: new OpenLayers.Pixel(175, 100), which: 1};
+ handler.mousedown(evt);
+ // mousemove to (125, 100)
+ evt = {xy: new OpenLayers.Pixel(125, 100), which: 1};
+ handler.mousemove(evt);
+ // test that the bounds have changed
+ t.ok(!handler.polygon.geometry.getBounds().equals(new OpenLayers.Bounds(0,-35.15625,35.15625,0)),
+ "Correct bounds after dragging without letting go. (Came out as "+handler.line.geometry.getBounds().toBBOX() + ".)");
+ map.destroy();
+ }
+
+ function test_callbacks(t) {
+ t.plan(39);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({
+ });
+ var logs = [], log;
+ var handler = new OpenLayers.Handler.Polygon(control, {
+ create: function() {
+ logs.push({type: "create", args: arguments});
+ },
+ point: function() {
+ logs.push({type: "point", args: arguments});
+ },
+ modify: function() {
+ logs.push({type: "modify", args: arguments});
+ },
+ done: function() {
+ logs.push({type: "done", args: arguments});
+ },
+ cancel: function() {
+ logs.push({type: "cancel", args: arguments});
+ }
+ },
+ {
+ pixelTolerance: 0
+ });
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ // create polygon
+ handler.activate();
+
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ t.eq(logs.length, 2, "[mousemove] called back");
+ log = logs.shift();
+ t.eq(log.type, "create", "[activate] create called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-150, 75),
+ "[mousemove] correct point");
+ t.ok(log.args[1] == handler.polygon,
+ "[mousemove] correct feature");
+ log = logs.shift();
+ t.eq(log.type, "modify", "[mousemove] modify called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-150, 75),
+ "[mousemove] correct point");
+ t.ok(log.args[1] === handler.polygon,
+ "[mousemove] correct feature");
+ // mouse down
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ t.eq(logs.length, 1, "[mousedown] called back");
+ log = logs.shift();
+ t.eq(log.type, "modify", "[mousedown] modify called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-150, 75),
+ "[mousedown] correct point");
+ t.ok(log.args[1] === handler.polygon,
+ "[mousedown] correct feature");
+ // mouse up
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0)});
+ t.eq(logs.length, 2, "[mouseup] called back twice");
+ log = logs.shift();
+ t.eq(log.type, "point", "[mouseup] point called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-150, 75),
+ "[mouseup] correct point");
+ var geom = new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(-150, 75)
+ ])
+ ]);
+ geom.components[0].addComponent(
+ new OpenLayers.Geometry.Point(-150, 75),
+ geom.components[0].components.length
+ );
+ t.geom_eq(log.args[1], geom, "[mouseup] correct polygon");
+ log = logs.shift();
+ t.eq(log.type, "modify", "[mouseup] modify called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-150, 75),
+ "[mouseup] correct point");
+ t.ok(log.args[1] == handler.polygon,
+ "[mouseup] correct feature");
+ // mouse move
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(10, 10)});
+ t.eq(logs.length, 1, "[mousemove] called back");
+ log = logs.shift();
+ t.eq(log.type, "modify", "[mousemove] modify called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-140, 65),
+ "[mousemove] correct point");
+ t.ok(log.args[1] === handler.polygon,
+ "[mousemove] correct feature");
+ // mouse down
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(10, 10)});
+ t.eq(logs.length, 1, "[mousedown] called back");
+ log = logs.shift();
+ t.eq(log.type, "modify", "[mousedown] modify called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-140, 65),
+ "[mousedown] correct point");
+ t.ok(log.args[1] === handler.polygon,
+ "[mousedown] correct feature");
+ // mouse up
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(10, 10)});
+ log = logs.shift();
+ log = logs.shift();
+ // move to 0, 10 and double click
+ // mouse move
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 10)});
+ t.eq(logs.length, 1, "[mousemove] called back");
+ log = logs.shift();
+ t.eq(log.type, "modify", "[mousemove] modify called");
+ t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-150, 65),
+ "[mousemove] correct point");
+ t.ok(log.args[1] === handler.polygon,
+ "[mousemove] correct feature");
+ // mouse down
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 10)});
+ t.eq(logs.length, 1, "[mousedown] not called back");
+ log = logs.shift();
+ // mouse up
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 10)});
+ t.eq(logs.length, 2, "[mouseup] called back");
+ log = logs.shift();
+ log = logs.shift();
+ // mouse down
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 10)});
+ t.eq(logs.length, 0, "[mousedown] not called back");
+ // mouse up
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 10)});
+ t.eq(logs.length, 0, "[mouseup] not called back");
+ // dblclick
+ handler.dblclick(
+ {type: "dblclick", xy: new OpenLayers.Pixel(0, 10)});
+ t.eq(logs.length, 1, "[dblclick] called back");
+ log = logs.shift();
+ t.eq(log.type, "done", "[dblclick] done called");
+ t.geom_eq(
+ log.args[0],
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(-150, 75),
+ new OpenLayers.Geometry.Point(-140, 65),
+ new OpenLayers.Geometry.Point(-150, 65),
+ new OpenLayers.Geometry.Point(-150, 75)
+ ])
+ ]),
+ "[dblclick] correct polygon"
+ );
+ // cancel
+ handler.cancel();
+ t.eq(logs.length, 1, "[cancel] called back");
+ log = logs.shift();
+ t.eq(log.type, "cancel", "[cancel] canced called");
+
+ map.destroy();
+ }
+
+ function test_toggle_freehand(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Polygon(control, {
+ done: function(g) {
+ log++;
+ }
+ }, {persist: true});
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+
+ log = 0;
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ t.eq(log, 1, "feature drawn when shift pressed on mousedown");
+
+ log = 0;
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0), shiftKey: false});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ t.eq(log, 0, "feature not drawn when shift not pressed on mousedown");
+ }
+
+ function test_persist(t) {
+ t.plan(4);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Polygon(control, {});
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+
+ handler.persist = false;
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ var feature1 = handler.polygon;
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2)});
+ handler.dblclick(
+ {type: "dblclick", xy: new OpenLayers.Pixel(2, 2)});
+ t.ok(feature1.layer == null, "a) feature1 destroyed");
+
+ handler.persist = true;
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ var feature2 = handler.polygon;
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2)});
+ handler.dblclick(
+ {type: "dblclick", xy: new OpenLayers.Pixel(2, 2)});
+ t.ok(feature2.layer != null, "b) feature2 not destroyed");
+
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ var feature3 = handler.polygon;
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2)});
+ handler.dblclick(
+ {type: "dblclick", xy: new OpenLayers.Pixel(2, 2)});
+ t.ok(feature3.layer != null, "c) feature3 not destroyed");
+ t.ok(feature2.layer == null, "c) feature2 destroyed");
+
+ map.destroy();
+ }
+
+ function test_persist_freehand(t) {
+ t.plan(6);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Polygon(control, {});
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+
+ handler.persist = false;
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ var feature1 = handler.polygon;
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ t.ok(feature1.layer == null, "a) feature1 destroyed");
+
+ handler.persist = true;
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ var feature2 = handler.polygon;
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ t.ok(feature2.layer != null, "b) feature2 not destroyed");
+
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ var feature3 = handler.polygon;
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ t.ok(feature3.layer != null, "c) feature3 not destroyed");
+ t.ok(feature2.layer == null, "c) feature2 destroyed");
+
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ feature4 = handler.polygon;
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0), shiftKey: false});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1), shiftKey: true});
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(2, 2), shiftKey: true});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0), shiftKey: true});
+ t.ok(feature4.layer != null, "d) feature4 not destroyed");
+ t.ok(feature3.layer == null, "c) feature3 destroyed");
+
+ map.destroy();
+ }
+
+ function test_rings(t) {
+ t.plan(12);
+
+ var log = [];
+ var map = new OpenLayers.Map({
+ div: "map",
+ resolutions: [1],
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ layers: [
+ new OpenLayers.Layer.Vector(null, {
+ isBaseLayer: true,
+ eventListeners: {
+ featureadded: function(event) {
+ log.push(event);
+ },
+ sketchmodified: function(event) {
+ log.push(event);
+ },
+ sketchcomplete: function(event) {
+ log.push(event);
+ }
+ }
+ })
+ ],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 0
+ });
+
+ // create control for drawing polygons with holes
+ var draw = new OpenLayers.Control.DrawFeature(
+ map.layers[0],
+ OpenLayers.Handler.Polygon,
+ {handlerOptions: {
+ holeModifier: "altKey",
+ pixelTolerance: 0
+ }}
+ );
+ map.addControl(draw);
+ draw.activate();
+
+ var event;
+ function trigger(type, event) {
+ map.events.triggerEvent(type, OpenLayers.Util.extend({}, event));
+ }
+
+ // a) draw a polygon
+ log = [];
+ // start at -9, 9
+ event = {xy: new OpenLayers.Pixel(-9, 9)};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // draw to -1, 9
+ event = {xy: new OpenLayers.Pixel(-1, 9)};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // draw to -1, 1
+ event = {xy: new OpenLayers.Pixel(-1, 1)};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // draw to -9, 1
+ event = {xy: new OpenLayers.Pixel(-9, 1)};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // finish
+ event = {xy: new OpenLayers.Pixel(-9, 1)};
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ trigger("dblclick", event);
+
+ // make assertions
+ t.eq(log.length, 14, "a) correct number of events");
+ t.eq(log[log.length-1].type, "featureadded", "a) featureadded event last");
+ t.eq(log[log.length-1].feature.geometry.getArea(), 64, "a) correct polygon area");
+
+ // b) draw a hole
+ log = [];
+ // start at -6, 6
+ event = {xy: new OpenLayers.Pixel(-6, 6), altKey: true};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // draw to -3, 6
+ event = {xy: new OpenLayers.Pixel(-3, 6), altKey: true};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // draw to -3, 3
+ event = {xy: new OpenLayers.Pixel(-3, 3), altKey: true};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // draw to -6, 3
+ event = {xy: new OpenLayers.Pixel(-6, 3), altKey: true};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // finish
+ event = {xy: new OpenLayers.Pixel(-6, 3), altKey: true};
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ trigger("dblclick", event);
+
+ // make assertions
+ t.eq(log.length, 13, "b) correct number of events");
+ t.eq(log[log.length-1].type, "sketchcomplete", "b) sketchcomplete event last");
+ t.eq(log[log.length-1].feature.geometry.getArea(), 55, "b) correct polygon area");
+
+
+ // c) draw a polygon that overlaps the first
+ log = [];
+ // start at -2, 2
+ event = {xy: new OpenLayers.Pixel(-2, 2)};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // draw to 2, 2
+ event = {xy: new OpenLayers.Pixel(2, 2)};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // draw to 2, -2
+ event = {xy: new OpenLayers.Pixel(2, -2)};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // draw to -2, -2
+ event = {xy: new OpenLayers.Pixel(-2, -2)};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // finish
+ event = {xy: new OpenLayers.Pixel(-2, -2)};
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ trigger("dblclick", event);
+
+ // make assertions
+ t.eq(log.length, 14, "c) correct number of events");
+ t.eq(log[log.length-1].type, "featureadded", "c) featureadded event last");
+ t.eq(log[log.length-1].feature.geometry.getArea(), 16, "c) correct polygon area");
+
+ // d) draw a hole that tries to go outside the exterior ring
+ log = [];
+ // start at -1, 1
+ event = {xy: new OpenLayers.Pixel(-1, 1), altKey: true};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // draw to 1, 1
+ event = {xy: new OpenLayers.Pixel(1, 1), altKey: true};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // try to draw to -8, 8 (ouside active polygon)
+ event = {xy: new OpenLayers.Pixel(-8, 8), altKey: true};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // draw to 1, -1
+ event = {xy: new OpenLayers.Pixel(1, -1), altKey: true};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // draw to -1, -1
+ event = {xy: new OpenLayers.Pixel(-1, -1), altKey: true};
+ trigger("mousemove", event);
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ // finish
+ event = {xy: new OpenLayers.Pixel(-1, 1), altKey: true};
+ trigger("mousedown", event);
+ trigger("mouseup", event);
+ trigger("dblclick", event);
+
+ // make assertions
+ t.eq(log.length, 18, "d) correct number of events");
+ t.eq(log[log.length-1].type, "sketchcomplete", "d) sketchcomplete event last");
+ t.eq(log[log.length-1].feature.geometry.getArea(), 12, "d) correct polygon area");
+
+
+ map.destroy();
+ }
+
+ function test_Handler_Polygon_destroy(t) {
+ t.plan(8);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer.WMS("", "", {}));
+ map.zoomToMaxExtent();
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.Polygon(control, {foo: 'bar'});
+
+ handler.activate();
+ var evt = {xy: new OpenLayers.Pixel(150, 75), which: 1};
+ handler.mousedown(evt);
+
+ t.ok(handler.layer,
+ "handler has a layer prior to destroy");
+ t.ok(handler.point,
+ "handler has a point prior to destroy");
+ t.ok(handler.line,
+ "handler has a line prior to destroy");
+ t.ok(handler.polygon,
+ "handler has a polygon prior to destroy");
+ handler.destroy();
+ t.eq(handler.layer, null,
+ "handler.layer is null after destroy");
+ t.eq(handler.point, null,
+ "handler.point is null after destroy");
+ t.eq(handler.line, null,
+ "handler.line is null after destroy");
+ t.eq(handler.polygon, null,
+ "handler.polygon is null after destroy");
+ map.destroy();
+ }
+
+ function test_insertXY(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control.DrawFeature(
+ layer, OpenLayers.Handler.Polygon
+ );
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ control.activate();
+ var handler = control.handler;
+
+ function userClick(x, y) {
+ var px = new OpenLayers.Pixel(x, y);
+ handler.mousemove({type: "mousemove", xy: px});
+ handler.mousedown({type: "mousedown", xy: px});
+ handler.mouseup({type: "mouseup", xy: px});
+ }
+
+ // add points at px(0, 0) and px(10, 10)
+ userClick(0, 0);
+ userClick(10, 10);
+ t.eq(handler.line.geometry.components.length, 4, "ring has four points after two clicks");
+
+ // programmatically add a point
+ handler.insertXY(5, 6);
+ t.eq(handler.line.geometry.components.length, 5, "ring has five points after insertXY");
+ t.geom_eq(
+ handler.line.geometry.components[2],
+ new OpenLayers.Geometry.Point(5, 6),
+ "third point comes from insertXY"
+ );
+
+ map.destroy();
+
+ }
+
+ //
+ // Sequence tests
+ //
+ // Sequence tests basically involve executing a sequence of events
+ // and testing the resulting geometry.
+ //
+ // Below are tests for various drawing sequences. Tests can be
+ // added here each a non-working sequence is found.
+ //
+
+ // stopDown:true, stopUp:true
+ // a) click on (0, 0)
+ // b) mousedown on (0.5, 0.5)
+ // c) mouseup on (1, 1)
+ // d) click on (0, 10)
+ // e) dblclick on (10, 10)
+ function test_sequence1(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Polygon(control,
+ {done: function(g) { log.geometry = g; }},
+ {stopDown: true, stopUp: true,
+ pixelTolerance: 0}
+ );
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+ log = {};
+
+ // a) click on (0, 0)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0)});
+ // b) mousedown on (0.5, 0.5)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0.5, 0.5)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0.5, 0.5)});
+ // c) mouseup on (1, 1)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(1, 1)});
+ // d) click on (0, 10)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 10)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 10)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 10)});
+ // e) dblclick on (10, 10)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(10, 10)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(10, 10)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(10, 10)});
+ handler.dblclick(
+ {type: "dblclick", xy: new OpenLayers.Pixel(10, 10)});
+ t.geom_eq(log.geometry,
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(-150, 75), // (0, 0)
+ new OpenLayers.Geometry.Point(-150, 65), // (0, 10)
+ new OpenLayers.Geometry.Point(-140, 65) // (10, 10)
+ ])
+ ]), "geometry is correct");
+ }
+
+ // stopDown:false, stopUp:false
+ // a) click on (0, 0)
+ // b) mousedown on (0.5, 0.5)
+ // c) mouseup on (1, 1)
+ // d) click on (0, 10)
+ // e) dblclick on (10, 10)
+ function test_sequence2(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Polygon(control,
+ {done: function(g) { log.geometry = g; }},
+ {stopDown: false, stopUp: false,
+ pixelTolerance: 0}
+ );
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ handler.activate();
+ log = {};
+
+ // a) click on (0, 0)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 0)});
+ // b) mousedown on (0.5, 0.5)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0.5, 0.5)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0.5, 0.5)});
+ // c) mouseup on (1, 1)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(1, 1)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(1, 1)});
+ // d) click on (0, 10)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(0, 10)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(0, 10)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(0, 10)});
+ // e) dblclick on (10, 10)
+ handler.mousemove(
+ {type: "mousemove", xy: new OpenLayers.Pixel(10, 10)});
+ handler.mousedown(
+ {type: "mousedown", xy: new OpenLayers.Pixel(10, 10)});
+ handler.mouseup(
+ {type: "mouseup", xy: new OpenLayers.Pixel(10, 10)});
+ handler.dblclick(
+ {type: "dblclick", xy: new OpenLayers.Pixel(10, 10)});
+ t.geom_eq(log.geometry,
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(-150, 75), // (0, 0)
+ new OpenLayers.Geometry.Point(-150, 65), // (0, 10)
+ new OpenLayers.Geometry.Point(-140, 65) // (10, 10)
+ ])
+ ]), "geometry is correct");
+ }
+
+ // a) tap
+ // b) tap
+ // c) doubletap
+ function test_touch_sequence1(t) {
+ t.plan(26);
+
+ // set up
+
+ var log;
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Polygon(control, {
+ done: function(g, f) {
+ log = {type: 'done', geometry: g, feature: f};
+ },
+ modify: function(g, f) {
+ log = {type: 'modify', geometry: g, feature: f};
+ }
+ }, {
+ doubleTouchTolerance: 2
+ });
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+ handler.activate();
+
+ // test
+
+ var ret;
+
+ // tap on (0, 0)
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(1, 0)});
+ t.ok(ret, '[touchstart] event propagates');
+ t.eq(log, null, '[touchstart] feature not finalized or modified');
+ ret = handler.touchmove({xy: new OpenLayers.Pixel(0, 0)});
+ t.ok(ret, '[touchmove] event propagates');
+ t.eq(log, null, '[touchmove] feature not finalized or modified');
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log.type, 'modify', '[touchend] feature modified');
+ t.geom_eq(log.geometry, new OpenLayers.Geometry.Point(-150, 75),
+ "[touchend] correct point");
+
+ // tap on (0, 10)
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(1, 10)});
+ t.ok(ret, '[touchstart] event propagates');
+ t.eq(log, null, '[touchstart] feature not finalized or modified');
+ ret = handler.touchmove({xy: new OpenLayers.Pixel(0, 10)});
+ t.ok(ret, '[touchmove] event propagates');
+ t.eq(log, null, '[touchmove] feature not finalized or modified');
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log.type, 'modify', '[touchend] feature modified');
+ t.geom_eq(log.geometry, new OpenLayers.Geometry.Point(-150, 65),
+ "[touchend] correct point");
+
+ // doubletap on (10, 10)
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(9, 10)});
+ t.ok(ret, '[touchstart] event propagates');
+ t.eq(log, null, '[touchstart] feature not finalized or modified');
+ ret = handler.touchmove({xy: new OpenLayers.Pixel(10, 10)});
+ t.ok(ret, '[touchmove] event propagates');
+ t.eq(log, null, '[touchmove] feature not finalized or modified');
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log.type, 'modify', '[touchend] feature modified');
+ t.geom_eq(log.geometry, new OpenLayers.Geometry.Point(-140, 65),
+ "[touchend] correct point");
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(11, 10)});
+ t.ok(!ret, '[touchstart] event does not propagate');
+ t.eq(log.type, 'done', '[touchend] feature finalized');
+ t.geom_eq(log.geometry,
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(-150, 75), // (0, 0)
+ new OpenLayers.Geometry.Point(-150, 65), // (0, 10)
+ new OpenLayers.Geometry.Point(-140, 65) // (10, 10)
+ ])
+ ]), "[touchstart] geometry is correct");
+ log = null;
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log, null, '[touchend] feature not finalized or modified');
+
+ // tear down
+
+ map.destroy();
+ }
+
+ // a) tap
+ // b) tap-move
+ // c) tap
+ // d) doubletap
+ function test_touch_sequence2(t) {
+ t.plan(32);
+
+ // set up
+
+ var log;
+ var map = new OpenLayers.Map("map", {
+ resolutions: [1]
+ });
+ var layer = new OpenLayers.Layer.Vector("foo", {
+ maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Polygon(control, {
+ done: function(g, f) {
+ log = {type: 'done', geometry: g, feature: f};
+ },
+ modify: function(g, f) {
+ log = {type: 'modify', geometry: g, feature: f};
+ }
+ }, {
+ doubleTouchTolerance: 2
+ });
+ control.handler = handler;
+ map.addControl(control);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+ handler.activate();
+
+ // test
+
+ var ret;
+
+ // tap on (0, 0)
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(1, 0)});
+ t.ok(ret, '[touchstart] event propagates');
+ t.eq(log, null, '[touchstart] feature not finalized or modified');
+ ret = handler.touchmove({xy: new OpenLayers.Pixel(0, 0)});
+ t.ok(ret, '[touchmove] event propagates');
+ t.eq(log, null, '[touchmove] feature not finalized or modified');
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log.type, 'modify', '[touchend] feature modified');
+ t.geom_eq(log.geometry, new OpenLayers.Geometry.Point(-150, 75),
+ "[touchend] correct point");
+
+ // tap-move
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(1, 10)});
+ t.ok(ret, '[touchstart] event propagates');
+ t.eq(log, null, '[touchstart] feature not finalized or modified');
+ ret = handler.touchmove({xy: new OpenLayers.Pixel(20, 20)});
+ t.ok(ret, '[touchmove] event propagates');
+ t.eq(log, null, '[touchmove] feature not finalized or modified');
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log, null, '[touchend] feature not finalized or modified');
+
+ // tap on (0, 10)
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(1, 10)});
+ t.ok(ret, '[touchstart] event propagates');
+ t.eq(log, null, '[touchstart] feature not finalized or modified');
+ ret = handler.touchmove({xy: new OpenLayers.Pixel(0, 10)});
+ t.ok(ret, '[touchmove] event propagates');
+ t.eq(log, null, '[touchmove] feature not finalized or modified');
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log.type, 'modify', '[touchend] feature modified');
+ t.geom_eq(log.geometry, new OpenLayers.Geometry.Point(-150, 65),
+ "[touchend] correct point");
+
+ // doubletap on (10, 10)
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(9, 10)});
+ t.ok(ret, '[touchstart] event propagates');
+ t.eq(log, null, '[touchstart] feature not finalized or modified');
+ ret = handler.touchmove({xy: new OpenLayers.Pixel(10, 10)});
+ t.ok(ret, '[touchmove] event propagates');
+ t.eq(log, null, '[touchmove] feature not finalized or modified');
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log.type, 'modify', '[touchend] feature modified');
+ t.geom_eq(log.geometry, new OpenLayers.Geometry.Point(-140, 65),
+ "[touchend] correct point");
+ log = null;
+ ret = handler.touchstart({xy: new OpenLayers.Pixel(11, 10)});
+ t.ok(!ret, '[touchstart] event does not propagate');
+ t.eq(log.type, 'done', '[touchend] feature finalized');
+ t.geom_eq(log.geometry,
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(-150, 75), // (0, 0)
+ new OpenLayers.Geometry.Point(-150, 65), // (0, 10)
+ new OpenLayers.Geometry.Point(-140, 65) // (10, 10)
+ ])
+ ]), "[touchstart] geometry is correct");
+ log = null;
+ ret = handler.touchend({});
+ t.ok(ret, '[touchend] event propagates');
+ t.eq(log, null, '[touchend] feature not finalized or modified');
+
+ // tear down
+
+ map.destroy();
+ }
+
+ function test_citeComplaint(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer.OSM());
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var control = new OpenLayers.Control({});
+ var handler = new OpenLayers.Handler.Polygon(control, {});
+ control.handler = handler;
+ map.addControl(control);
+ map.zoomToExtent(new OpenLayers.Bounds(-24225034.496992, -11368938.517442, -14206280.326992, -1350184.3474418));
+ control.activate();
+ handler.createFeature(new OpenLayers.Pixel(100, 50));
+ t.ok(handler.point.geometry.x < 0, "Geometry started correctly when wrapping the dateline using citeCompliant false");
+ control.deactivate();
+
+ var handler = new OpenLayers.Handler.Polygon(control, {}, {citeCompliant: true});
+ control.handler = handler;
+ control.activate();
+ handler.createFeature(new OpenLayers.Pixel(100, 50));
+ t.ok(handler.point.geometry.x > 0, "Geometry started correctly when wrapping the dateline using citeCompliant true");
+
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 300px; height: 150px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Handler/RegularPolygon.html b/misc/openlayers/tests/Handler/RegularPolygon.html
new file mode 100644
index 0000000..ee43dc7
--- /dev/null
+++ b/misc/openlayers/tests/Handler/RegularPolygon.html
@@ -0,0 +1,235 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Handler_RegularPolygon_constructor(t) {
+ t.plan(3);
+ var control = new OpenLayers.Control();
+ control.id = Math.random();
+ var callbacks = {foo: "bar"};
+ var options = {bar: "foo"};
+
+ var oldInit = OpenLayers.Handler.prototype.initialize;
+
+ OpenLayers.Handler.prototype.initialize = function(con, call, opt) {
+ t.eq(con.id, control.id,
+ "constructor calls parent with the correct control");
+ t.eq(call, callbacks,
+ "constructor calls parent with the correct callbacks");
+ t.eq(opt, options,
+ "regular polygon constructor calls parent with the correct options");
+ }
+ var handler = new OpenLayers.Handler.RegularPolygon(control, callbacks, options);
+
+ OpenLayers.Handler.prototype.initialize = oldInit;
+ }
+
+ function test_Handler_RegularPolygon_activation(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.RegularPolygon(control);
+ handler.active = true;
+ var activated = handler.activate();
+ t.ok(!activated,
+ "activate returns false if the handler was already active");
+ handler.active = false;
+ activated = handler.activate();
+ t.ok(activated,
+ "activate returns true if the handler was not already active");
+ activated = handler.deactivate();
+ t.ok(activated,
+ "deactivate returns true if the handler was active already");
+ map.destroy();
+ }
+
+ function test_Handler_RegularPolygon_deactivation(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map');
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+
+ var handler = new OpenLayers.Handler.RegularPolygon(control, {foo: 'bar'});
+ handler.activate();
+ handler.layer.destroy();
+ handler.deactivate();
+ t.eq(handler.layer, null,
+ "deactivate doesn't throw an error if layer was" +
+ " previously destroyed");
+ map.destroy();
+ }
+
+ function test_Handler_RegularPolygon_four_corners(t) {
+ t.plan(7);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer.WMS("", "", {}));
+ map.zoomToMaxExtent();
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.RegularPolygon(control, {});
+ var activated = handler.activate();
+ var evt = {xy: new OpenLayers.Pixel(150, 75), which: 1};
+ handler.down(evt);
+ var evt = {xy: new OpenLayers.Pixel(175, 75), which: 1};
+ handler.move(evt);
+ t.eq(handler.feature.geometry.getBounds().toBBOX(),
+ "-35.15625,-35.15625,35.15625,35.15625",
+ "correct bounds after move");
+ t.eq(handler.feature.geometry.components[0].components.length, 5,
+ "geometry has 5 components");
+ t.eq(handler.feature.geometry.CLASS_NAME,
+ "OpenLayers.Geometry.Polygon",
+ "geometry is a polygon");
+ t.eq(handler.radius, 25*1.40625, "feature radius as set on handler");
+ var evt = {xy: new OpenLayers.Pixel(175, 80), which: 1};
+ handler.move(evt);
+ t.eq(handler.feature.geometry.getBounds().toBBOX(),
+ "-35.15625,-35.15625,35.15625,35.15625",
+ "correct bounds after move with a fixed radius");
+ handler.cancel();
+ handler.setOptions({radius:2 / Math.sqrt(2)});
+ var evt = {xy: new OpenLayers.Pixel(150, 75), which: 1};
+ handler.down(evt);
+
+ t.eq(handler.feature.geometry.getBounds().toBBOX(),
+ "-1,-1,1,1",
+ "bounds with manual radius setting");
+ var evt = {xy: new OpenLayers.Pixel(175, 90), which: 1};
+ handler.move(evt);
+ t.eq(handler.feature.geometry.getBounds().toBBOX(),
+ "34.15625,-22.09375,36.15625,-20.09375",
+ "bounds with manual radius setting and mousemove");
+ map.destroy();
+ }
+
+ function test_Handler_RegularPolygon_circle(t) {
+ t.plan(7);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer.WMS("", "", {}));
+ map.zoomToMaxExtent();
+ var control = new OpenLayers.Control();
+ map.addControl(control);
+ var handler = new OpenLayers.Handler.RegularPolygon(control, {}, {'sides':40});
+ var activated = handler.activate();
+ var evt = {xy: new OpenLayers.Pixel(150, 75), which: 1};
+ handler.down(evt);
+ var evt = {xy: new OpenLayers.Pixel(175, 75), which: 1};
+ handler.move(evt);
+ t.eq(handler.feature.geometry.getBounds().toBBOX(),
+ "-35.15625,-35.15625,35.15625,35.15625",
+ "correct bounds after move");
+ t.eq(handler.feature.geometry.components[0].components.length, 41,
+ "geometry has correct numbre of components");
+ t.eq(handler.feature.geometry.CLASS_NAME,
+ "OpenLayers.Geometry.Polygon",
+ "geometry is a polygon");
+ t.eq(handler.radius, 25*1.40625, "feature radius as set on handler");
+ var evt = {xy: new OpenLayers.Pixel(175, 80), which: 1};
+ handler.move(evt);
+ t.eq(handler.feature.geometry.getBounds().toBBOX(),
+ "-35.823348,-35.823348,35.823348,35.823348",
+ "correct bounds after move with fixed radius");
+ handler.cancel();
+ handler.setOptions({radius:1});
+ var evt = {xy: new OpenLayers.Pixel(150, 75), which: 1};
+ handler.down(evt);
+
+ t.eq(handler.feature.geometry.getBounds().toBBOX(),
+ "-0.996917,-0.996917,0.996917,0.996917",
+ "bounds with manual radius setting");
+ var evt = {xy: new OpenLayers.Pixel(175, 80), which: 1};
+ handler.move(evt);
+ t.eq(handler.feature.geometry.getBounds().toBBOX(),
+ "34.159333,-8.028167,36.153167,-6.034333",
+ "bounds with manual radius setting and mousemove");
+ map.destroy();
+ }
+
+ function test_Handler_RegularPolygon_irregular(t) {
+ t.plan(4);
+ var map = {
+ getResolution: function() {
+ return 1;
+ }
+ };
+ var layer = {
+ addFeatures: function() {},
+ drawFeature: function(feature, style) {
+ var ring = feature.geometry.components[0];
+ t.eq(ring.components[0].x, 20, "correct right");
+ t.eq(ring.components[0].y, 10, "correct bottom");
+ t.eq(ring.components[2].x, 10, "correct left");
+ t.eq(ring.components[2].y, 15, "correct top");
+ },
+ getLonLatFromViewPortPx: function(px) {
+ return {lon: px.x, lat: px.y};
+ }
+ };
+ var control = {};
+ var options = {
+ sides: 4,
+ irregular: true,
+ layer: layer,
+ map: map
+ };
+ var handler = new OpenLayers.Handler.RegularPolygon(
+ control, null, options
+ );
+ handler.origin = new OpenLayers.Geometry.Point(10, 10);
+ handler.feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Polygon(
+ [new OpenLayers.Geometry.LinearRing()]
+ )
+ );
+ // should result in a 10 x 5 rectangle
+ handler.move({xy: {x: 20, y: 15}});
+ }
+
+ function test_callbacks(t) {
+ t.plan(1);
+
+ // setup
+ var map = new OpenLayers.Map("map");
+
+ var control = {"map": map};
+
+ var done = function(geom) {
+ t.ok(true,
+ "done callback called even if no move between down and up");
+ };
+
+ var handler = new OpenLayers.Handler.RegularPolygon(
+ control, {"done": done});
+ handler.activate();
+
+ var xy = new OpenLayers.Pixel(Math.random(), Math.random());
+
+ var isLeftClick = OpenLayers.Event.isLeftClick;
+ OpenLayers.Event.isLeftClick = function() { return true; };
+ handler.layer = {
+ renderer: {
+ clear: OpenLayers.Function.Void
+ },
+ addFeatures: OpenLayers.Function.Void,
+ drawFeature: OpenLayers.Function.Void,
+ destroyFeatures: OpenLayers.Function.Void,
+ getLonLatFromViewPortPx: function() {
+ return xy;
+ }
+ };
+
+ // test
+ map.events.triggerEvent("mousedown", {"xy": xy});
+ map.events.triggerEvent("mouseup", {"xy": xy});
+
+ // tear down
+ OpenLayers.Event.isLeftClick = isLeftClick;
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 300px; height: 150px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Icon.html b/misc/openlayers/tests/Icon.html
new file mode 100644
index 0000000..ac542d2
--- /dev/null
+++ b/misc/openlayers/tests/Icon.html
@@ -0,0 +1,68 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+ var icon;
+
+ function test_Icon_constructor (t) {
+ t.plan( 4 );
+ var size = new OpenLayers.Size(5,6);
+ icon = new OpenLayers.Icon("", size);
+ t.ok( icon instanceof OpenLayers.Icon, "new OpenLayers.Icon returns Icon object" );
+ t.ok( icon.size instanceof OpenLayers.Size, "icon.size returns Size object" );
+ t.ok( icon.size.equals(size), "icon.size returns correct value" );
+ t.eq( icon.url, "", "icon.url returns str object" );
+ }
+ function test_Icon_clone (t) {
+ t.plan( 4 );
+ icon = new OpenLayers.Icon("a",new OpenLayers.Size(5,6));
+ t.ok( icon instanceof OpenLayers.Icon, "new OpenLayers.Icon returns Icon object" );
+ var cloned = icon.clone();
+ t.ok( cloned instanceof OpenLayers.Icon, "clone is an OpenLayers.Icon" );
+ cloned.url = "b"
+ t.eq( icon.url, "a", "icon.url doesn't change with clone's url" );
+ t.eq( cloned.url, "b", "cloned.url does change when edited" );
+ }
+
+ function test_Icon_setOpacity(t) {
+ t.plan( 2 );
+
+ icon = new OpenLayers.Icon("a",new OpenLayers.Size(5,6));
+ t.ok(!icon.imageDiv.style.opacity, "default icon has no opacity");
+
+ icon.setOpacity(0.5);
+ t.eq(parseFloat(icon.imageDiv.style.opacity), 0.5, "icon.setOpacity() works");
+ }
+
+ function test_Icon_isDrawn(t) {
+ t.plan(4);
+
+ var icon = {};
+
+ //no imageDiv
+ var drawn = OpenLayers.Icon.prototype.isDrawn.apply(icon, []);
+ t.ok(!drawn, "icon with no imageDiv not drawn");
+
+ //imageDiv no parentNode
+ icon.imageDiv = {};
+ drawn = OpenLayers.Icon.prototype.isDrawn.apply(icon, []);
+ t.ok(!drawn, "icon with imageDiv with no parentNode not drawn");
+
+ //imageDiv with parent
+ icon.imageDiv.parentNode = {};
+ drawn = OpenLayers.Icon.prototype.isDrawn.apply(icon, []);
+ t.ok(drawn, "icon with imageDiv with parentNode drawn");
+
+ //imageDiv with parent but nodetype 11
+ icon.imageDiv.parentNode = {'nodeType': 11};
+ drawn = OpenLayers.Icon.prototype.isDrawn.apply(icon, []);
+ t.ok(!drawn, "imageDiv with parent but nodetype 11 not drawn");
+ }
+
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Kinetic.html b/misc/openlayers/tests/Kinetic.html
new file mode 100644
index 0000000..ba50b53
--- /dev/null
+++ b/misc/openlayers/tests/Kinetic.html
@@ -0,0 +1,132 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_Kinetic (t) {
+ t.plan(17);
+ var finish = false;
+ var results = {
+ 110: {x: -2.7, y: -3.6, end: false},
+ 120: {x: -2.1, y: -2.8, end: false},
+ 130: {x: -1.5, y: -2.0, end: false},
+ 140: {x: -0.9, y: -1.2, end: false},
+ 150: {x: -0.3, y: -0.4, end: true}
+ };
+
+ var originalGetTime = Date.prototype.getTime;
+ Date.prototype.getTime = function() { return 0 };
+
+ var interval = 10; // arbitrary value for tests
+
+ var originalLoopAnimation = OpenLayers.Animation.start;
+ OpenLayers.Animation.start = function(callback) {
+ while (!finish) {
+ var time = new Date().getTime();
+ Date.prototype.getTime = function() { return time+interval };
+ callback();
+ }
+ };
+
+ var kinetic = new OpenLayers.Kinetic({
+ deceleration: 0.01
+ });
+ kinetic.begin();
+ kinetic.update({x:0, y:0});
+
+ Date.prototype.getTime = function() { return 100 };
+ var measure = kinetic.end({x:30, y:40});
+
+ t.eq(measure.speed, 0.5, "correct speed");
+ t.eq(measure.theta, Math.PI - Math.atan(40/30), "correct angle");
+
+ // fake timer id
+ kinetic.timerId = 0;
+ kinetic.move(measure, function(x, y, end) {
+ var result = results[new Date().getTime()];
+ t.eq(Math.round(x * 1000) / 1000, result.x, "correct x");
+ t.eq(Math.round(y * 1000) / 1000, result.y, "correct y");
+ t.eq(end, result.end, "correct end");
+ finish = end;
+ });
+
+ Date.prototype.getTime = originalGetTime;
+ OpenLayers.Animation.start = originalLoopAnimation;
+ }
+
+ function test_Angle (t) {
+ t.plan(8);
+ var results = [
+ {speed: 0.5, theta: Math.round((Math.PI - Math.atan(40/30)) * 1000000) / 1000000},
+ {speed: 0.5, theta: Math.round((Math.PI + Math.atan(40/30)) * 1000000) / 1000000},
+ {speed: 0.5, theta: Math.round((- Math.atan(40/30)) * 1000000) / 1000000},
+ {speed: 0.5, theta: Math.round((Math.atan(40/30)) * 1000000) / 1000000}
+ ];
+
+ var originalGetTime = Date.prototype.getTime;
+ Date.prototype.getTime = function() { return 0 };
+
+ var kinetic = new OpenLayers.Kinetic();
+ kinetic.begin();
+ kinetic.update({x:0, y:0});
+
+ Date.prototype.getTime = function() { return 100 };
+ var measure = kinetic.end({x:30, y:40});
+
+ t.eq(measure.speed, results[0].speed, "correct speed");
+ t.eq(Math.round(measure.theta * 1000000) / 1000000,
+ results[0].theta, "correct angle");
+
+
+ var originalGetTime = Date.prototype.getTime;
+ Date.prototype.getTime = function() { return 0 };
+
+ var kinetic = new OpenLayers.Kinetic();
+ kinetic.begin();
+ kinetic.update({x:0, y:0});
+
+ Date.prototype.getTime = function() { return 100 };
+ var measure = kinetic.end({x:30, y:-40});
+
+ t.eq(measure.speed, results[1].speed, "correct speed");
+ t.eq(Math.round(measure.theta * 1000000) / 1000000,
+ results[1].theta, "correct angle");
+
+
+ var originalGetTime = Date.prototype.getTime;
+ Date.prototype.getTime = function() { return 0 };
+
+ var kinetic = new OpenLayers.Kinetic();
+ kinetic.begin();
+ kinetic.update({x:0, y:0});
+
+ Date.prototype.getTime = function() { return 100 };
+ var measure = kinetic.end({x:-30, y:-40});
+
+ t.eq(measure.speed, results[2].speed, "correct speed");
+ t.eq(Math.round(measure.theta * 1000000) / 1000000,
+ results[2].theta, "correct angle");
+
+ var originalGetTime = Date.prototype.getTime;
+ Date.prototype.getTime = function() { return 0 };
+
+ var kinetic = new OpenLayers.Kinetic();
+ kinetic.begin();
+ kinetic.update({x:0, y:0});
+
+ Date.prototype.getTime = function() { return 100 };
+ var measure = kinetic.end({x:-30, y:40});
+
+ t.eq(measure.speed, results[3].speed, "correct speed");
+ t.eq(Math.round(measure.theta * 1000000) / 1000000,
+ results[3].theta, "correct angle");
+
+ Date.prototype.getTime = originalGetTime;
+ }
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 600px; height: 300px;"/>
+ <div style="display: none;"><div id="invisimap"></div></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Lang.html b/misc/openlayers/tests/Lang.html
new file mode 100644
index 0000000..9f4fa9b
--- /dev/null
+++ b/misc/openlayers/tests/Lang.html
@@ -0,0 +1,106 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script src="../lib/OpenLayers/Lang/en-CA.js" type="text/javascript"></script>
+ <script src="../lib/OpenLayers/Lang/fr.js" type="text/javascript"></script>
+ <script type="text/javascript">
+
+ function test_setCode(t) {
+ t.plan(4);
+ OpenLayers.Lang.code = null;
+
+ // test with no argument - this could result in the default or the
+ // browser language if a dictionary exists
+ OpenLayers.Lang.setCode();
+ t.ok(OpenLayers.Lang.code != null,
+ "code set when no argument is sent");
+
+ var primary = "xx";
+ var subtag = "XX";
+ var code = primary + "-" + subtag;
+ OpenLayers.Lang[code] = {};
+
+ // test code for dictionary that exists
+ OpenLayers.Lang.setCode(code);
+ t.eq(OpenLayers.Lang.code, code,
+ "code properly set for existing dictionary");
+
+ // test code for dictionary that doesn't exist
+ OpenLayers.Lang.setCode(primary + "-YY");
+ t.eq(OpenLayers.Lang.code, OpenLayers.Lang.defaultCode,
+ "code set to default for non-existing dictionary");
+
+ // test code for existing primary but missing subtag
+ OpenLayers.Lang[primary] = {};
+ OpenLayers.Lang.setCode(primary + "-YY");
+ t.eq(OpenLayers.Lang.code, primary,
+ "code set to primary when subtag dictionary is missing");
+
+ // clean up
+ delete OpenLayers.Lang[code];
+ delete OpenLayers.Lang[primary];
+ OpenLayers.Lang.code = null;
+ }
+
+ function test_getCode(t) {
+ t.plan(3);
+ OpenLayers.Lang.code = null;
+
+ // test that a non-null value is retrieved - could be browser language
+ // or defaultCode
+ var code = OpenLayers.Lang.getCode();
+ t.ok(code != null, "returns a non-null code");
+ t.ok(OpenLayers.Lang.code != null, "sets the code to a non-null value");
+
+ // test that the code is returned if non-null
+ OpenLayers.Lang.code = "foo";
+ t.eq(OpenLayers.Lang.getCode(), "foo", "returns the code if non-null");
+
+ // clean up
+ OpenLayers.Lang.code = null;
+ }
+
+ function test_i18n(t) {
+ t.plan(1);
+ t.ok(OpenLayers.i18n === OpenLayers.Lang.translate,
+ "i18n is an alias for OpenLayers.Lang.translate");
+ }
+
+ function test_translate(t) {
+ var keys = ['test1', 'test3', 'noKey'];
+ var codes = ['en', 'en-CA', 'fr', 'fr-CA', 'sw'];
+ var result = {
+ 'en': {'Overlays':'Overlays',
+ 'unhandledRequest':'Unhandled request return foo',
+ 'noKey':'noKey'},
+ 'en-CA': {'Overlays':'Overlays',
+ 'unhandledRequest':'Unhandled request return foo',
+ 'noKey':'noKey'},
+ 'fr': {'Overlays':'Calques',
+ 'unhandledRequest':'Requête non gérée, retournant foo',
+ 'noKey':'noKey'},
+ 'fr-CA': {'Overlays':'Calques', //this should result in 'fr'
+ 'unhandledRequest':'Requête non gérée, retournant foo',
+ 'noKey':'noKey'},
+ 'sw': {'Overlays':'Overlays', //this should result in 'en'
+ 'unhandledRequest':'Unhandled request return foo',
+ 'noKey':'noKey'}
+ };
+
+ t.plan(keys.length*codes.length);
+
+ for (var i=0; i<codes.length; ++i) {
+ var code = codes[i];
+ OpenLayers.Lang.setCode(code);
+ t.eq(OpenLayers.Lang.translate('Overlays'), result[code]['Overlays'], "simple key lookup in "+code);
+ t.eq(OpenLayers.Lang.translate('unhandledRequest',{'statusText':'foo'}),
+ result[code]['unhandledRequest'], "lookup with argument substitution in "+code);
+ t.eq(OpenLayers.Lang.translate('noKey'), result[code]['noKey'], "invalid key returns the key in "+code);
+ }
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer.html b/misc/openlayers/tests/Layer.html
new file mode 100644
index 0000000..954a363
--- /dev/null
+++ b/misc/openlayers/tests/Layer.html
@@ -0,0 +1,910 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+ var layer;
+
+ function test_Layer_constructor (t) {
+ t.plan( 15 );
+
+ var options = { chicken: 151, foo: "bar", projection: "none" };
+ var layer = new OpenLayers.Layer('Test Layer', options);
+
+ t.ok( layer instanceof OpenLayers.Layer, "new OpenLayers.Layer returns object" );
+ t.eq( layer.CLASS_NAME, "OpenLayers.Layer", "CLASS_NAME variable set correctly");
+
+ t.eq( layer.name, "Test Layer", "layer.name is correct" );
+ t.ok( layer.id != null, "Layer is given an id");
+ t.ok( layer.projection, "none", "default layer projection correctly set");
+ t.ok( ((layer.chicken == 151) && (layer.foo == "bar")), "layer.options correctly set to Layer Object" );
+ t.ok( ((layer.options["chicken"] == 151) && (layer.options["foo"] == "bar")), "layer.options correctly backed up" );
+
+ t.ok( typeof layer.div == "object" , "layer.div is created" );
+ t.eq( layer.div.id, layer.id, "layer.div.id is correct" );
+
+ options.chicken = 552;
+
+ t.eq( layer.options["chicken"], 151 , "layer.options correctly made fresh copy" );
+
+ t.eq( layer.isBaseLayer, false, "Default layer is not base layer" );
+
+ layer = new OpenLayers.Layer('Test Layer');
+ t.ok( layer instanceof OpenLayers.Layer, "new OpenLayers.Layer returns object" );
+ t.eq( layer.name, "Test Layer", "layer.name is correct" );
+ t.ok( layer.projection == null, "default layer projection correctly set");
+ t.ok( layer.options instanceof Object, "layer.options correctly initialized as a non-null Object" );
+ }
+
+
+ function test_Layer_clone (t) {
+ t.plan( 7 );
+
+ var mapone = new OpenLayers.Map('map');
+ var options = { chicken: 151, foo: "bar", maxResolution: "auto", visibility: false };
+ var layer = new OpenLayers.Layer('Test Layer', options);
+ mapone.addLayer(layer);
+ layer.setVisibility(true);
+
+ // randomly assigned property
+ layer.chocolate = 5;
+
+ var clone = layer.clone();
+
+ t.ok( clone.map == null, "cloned layer has map property set to null")
+
+ var maptwo = new OpenLayers.Map('map2');
+ maptwo.addLayer(clone);
+
+ t.ok( clone instanceof OpenLayers.Layer, "new OpenLayers.Layer returns object" );
+ t.eq( clone.name, "Test Layer", "default clone.name is correct" );
+ t.ok( ((clone.options["chicken"] == 151) && (clone.options["foo"] == "bar")), "clone.options correctly set" );
+ t.eq(clone.chocolate, 5, "correctly copied randomly assigned property");
+
+ t.eq(clone.visibility, true, "visibility correctly cloned");
+
+ layer.addOptions({chicken:152});
+ t.eq(clone.options["chicken"], 151, "made a clean copy of options");
+
+ mapone.destroy();
+ maptwo.destroy();
+ }
+
+ function test_Layer_setName (t) {
+
+ t.plan( 1 );
+
+ layer = new OpenLayers.Layer('Test Layer');
+ layer.setName("chicken");
+
+ t.eq(layer.name, "chicken", "setName() works")
+
+ }
+
+ function test_Layer_addOptions (t) {
+
+ t.plan( 20 );
+
+ var map = new OpenLayers.Map("map", {allOverlays: true});
+ var options = { chicken: 151, foo: "bar" };
+ var layer = new OpenLayers.Layer('Test Layer', options);
+ map.addLayer(layer);
+
+ layer.addOptions({bark:55, chicken: 171});
+ t.eq(layer.bark, 55, "addOptions() assigns new option correctly to Layer");
+ t.eq(layer.options.bark, 55, "addOptions() adds new option correctly to backup");
+
+ t.eq(layer.chicken, 171, "addOptions() overwrites option correctly to Layer");
+ t.eq(layer.options.chicken, 171, "addOptions() overwrites option correctly to backup");
+
+ var log;
+ layer.initResolutions = function() {
+ log++;
+ };
+ log = 0;
+ layer.addOptions({bark: 56});
+ t.eq(log, 0, "addOptions doesn't call initResolutions when not given a resolution option");
+
+ log = 0;
+ layer.addOptions({scales: [1, 2]});
+ t.eq(log, 1, "addOptions calls initResolutions when given scales");
+
+ log = 0;
+ layer.addOptions({resolutions: [1, 2]});
+ t.eq(log, 1, "addOptions calls initResolutions when given resolutions");
+
+ log = 0;
+ layer.addOptions({minScale: 4});
+ t.eq(log, 1, "addOptions calls initResolutions when given minScale");
+
+ log = 0;
+ layer.addOptions({maxScale: 4});
+ t.eq(log, 1, "addOptions calls initResolutions when given maxScale");
+
+ log = 0;
+ layer.addOptions({minResolution: 4});
+ t.eq(log, 1, "addOptions calls initResolutions when given minResolution");
+
+ log = 0;
+ layer.addOptions({maxResolution: 4});
+ t.eq(log, 1, "addOptions calls initResolutions when given maxResolution");
+
+ log = 0;
+ layer.addOptions({numZoomLevels: 4});
+ t.eq(log, 1, "addOptions calls initResolutions when given numZoomLevels");
+
+ log = 0;
+ layer.addOptions({maxZoomLevel: 4});
+ t.eq(log, 1, "addOptions calls initResolutions when given maxZoomLevel");
+
+ log = 0;
+ layer.addOptions({projection: new OpenLayers.Projection("EPSG:900913")});
+ t.eq(log, 1, "addOptions calls initResolutions when given projection");
+
+ log = 0;
+ layer.addOptions({units: "m"});
+ t.eq(log, 1, "addOptions calls initResolutions when given units");
+
+ log = 0;
+ layer.addOptions({minExtent: new OpenLayers.Bounds(0, 0, 0, 0)});
+ t.eq(log, 1, "addOptions calls initResolutions when given minExtent");
+
+ log = 0;
+ layer.addOptions({maxExtent: new OpenLayers.Bounds(0, 0, 0, 0)});
+ t.eq(log, 1, "addOptions calls initResolutions when given maxExtent");
+
+ layer.projection = null;
+ layer.addOptions({projection: "EPSG:900913"});
+ t.ok(layer.projection instanceof OpenLayers.Projection,
+ "addOptions creates a Projection object when given a projection string");
+
+ log = null;
+ // adding a 2nd layer to see if it gets reinitialized properly
+ var layer2 = new OpenLayers.Layer(null, {
+ moveTo: function(bounds) {
+ log = bounds;
+ }
+ });
+ map.addLayer(layer2);
+ layer.addOptions({maxResolution: 0.00034332275390625}, true);
+ t.eq(log.toBBOX(), map.getExtent().toBBOX(), "when reinitialize is set to true, changing base layer's resolution property reinitializes all layers.");
+
+ map.removeLayer(layer);
+ log = 0;
+ layer.addOptions({minExtent: new OpenLayers.Bounds(0, 0, 0, 0)});
+ t.eq(log, 0, "addOptions doesn't call initResolutions when layer is not in map");
+ }
+
+ function test_addOptionsScale(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.WMS();
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ layer.addOptions({maxResolution: 0.5, numZoomLevels: 15});
+ t.eq(layer.alwaysInRange, false, "alwaysInRange should not be true anymore");
+ }
+
+ function test_Layer_StandardOptionsAccessors (t) {
+
+ t.plan( 4 );
+
+ var projection = "EPSG:4326";
+ var maxExtent = new OpenLayers.Bounds(50,50,100,100);
+ var maxResolution = 1.5726;
+ var numZoomLevels = 11;
+
+ var options = { projection: projection,
+ maxExtent: maxExtent,
+ maxResolution: maxResolution,
+ numZoomLevels: numZoomLevels
+ };
+
+ var layer = new OpenLayers.Layer('Test Layer', options);
+
+ t.eq(layer.projection.getCode(), projection, "projection set correctly");
+ t.ok(layer.maxExtent.equals(maxExtent), "maxExtent set correctly");
+ t.eq(layer.maxResolution, maxResolution, "maxResolution set correctly");
+ t.eq(layer.numZoomLevels, numZoomLevels, "numZoomLevels set correctly");
+ }
+
+ function test_maxExtent(t) {
+ t.plan(5);
+
+ var layer = new OpenLayers.Layer(
+ null, {maxExtent: [-180, 0, 0, 90]}
+ );
+
+ t.ok(layer.maxExtent instanceof OpenLayers.Bounds, "(array) bounds instance");
+ t.eq(layer.maxExtent.left, -180, "(array) bounds left");
+ t.eq(layer.maxExtent.bottom, 0, "(array) bounds left");
+ t.eq(layer.maxExtent.right, 0, "(array) bounds right");
+ t.eq(layer.maxExtent.top, 90, "(array) bounds top");
+
+ layer.destroy();
+ }
+
+ function test_minExtent(t) {
+ t.plan(5);
+
+ var layer = new OpenLayers.Layer(
+ null, {minExtent: [-180, 0, 0, 90]}
+ );
+
+ t.ok(layer.minExtent instanceof OpenLayers.Bounds, "(array) bounds instance");
+ t.eq(layer.minExtent.left, -180, "(array) bounds left");
+ t.eq(layer.minExtent.bottom, 0, "(array) bounds left");
+ t.eq(layer.minExtent.right, 0, "(array) bounds right");
+ t.eq(layer.minExtent.top, 90, "(array) bounds top");
+
+ layer.destroy();
+ }
+
+
+ function test_eventListeners(t) {
+ t.plan(1);
+
+ var method = OpenLayers.Events.prototype.on;
+ // test that events.on is called at layer construction
+ var options = {
+ eventListeners: {foo: "bar"}
+ };
+ OpenLayers.Events.prototype.on = function(obj) {
+ t.eq(obj, options.eventListeners, "events.on called with eventListeners");
+ }
+ var layer = new OpenLayers.Layer("test", options);
+ OpenLayers.Events.prototype.on = method;
+ layer.destroy();
+
+ // if events.on is called again, this will fail due to an extra test
+ // test layer without eventListeners
+ OpenLayers.Events.prototype.on = function(obj) {
+ t.fail("events.on called without eventListeners");
+ }
+ var layer2 = new OpenLayers.Layer("test");
+ OpenLayers.Events.prototype.on = method;
+ layer2.destroy();
+ }
+
+ function test_initResolutions_alwaysInRange(t) {
+ t.plan(3);
+
+ var map, layer;
+
+ map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer("test", {maxResolution: 80, minResolution: 10});
+ map.addLayer(layer);
+ t.eq(layer.alwaysInRange, false,
+ "alwaysInRange set to false due to passed options");
+ map.destroy();
+
+ map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer("test", {projection: "unknown"});
+ map.addLayer(layer);
+ t.eq(layer.alwaysInRange, true,
+ "alwaysInRange true if unknown projection is set.");
+ map.destroy();
+
+ map = new OpenLayers.Map("map");
+ OpenLayers.Layer.prototype.alwaysInRange = false;
+ layer = new OpenLayers.Layer("test", {'projection': 'EPSG:4326'});
+ map.addLayer(layer);
+ t.eq(layer.alwaysInRange, false,
+ "alwaysInRange true if overridden on prototype.");
+ OpenLayers.Layer.prototype.alwaysInRange = null;
+ map.destroy();
+ }
+
+ function test_initResolutions_resolutions(t) {
+
+ function initResolutionsTest(
+ id, mapOptions, layerOptions,
+ expectedResolutions, expectedMinResolution, expectedMaxResolution) {
+
+ // setup
+ var map = new OpenLayers.Map("map", mapOptions);
+ var layer = new OpenLayers.Layer(null, layerOptions);
+ map.addLayer(layer);
+
+ // make resolution assertions
+ t.eq(
+ layer.resolutions.length, expectedResolutions.length,
+ id + ": correct resolutions length"
+ );
+ // allow for floating point imprecision
+ var got, exp, same = true;
+ for (var i=0; i<expectedResolutions.length; ++i) {
+ got = layer.resolutions[i];
+ exp = expectedResolutions[i];
+ if (got.toFixed(10) !== exp.toFixed(10)) {
+ t.fail(id + ": bad resolutions - index " + i + " got " + got + " but expected " + exp);
+ same = false;
+ break;
+ }
+ }
+ if (same) {
+ t.ok(true, id + ": correct resolutions");
+ }
+ t.eq(
+ layer.maxResolution, expectedMaxResolution,
+ id + ": maxResolution set"
+ );
+ t.eq(
+ layer.minResolution, expectedMinResolution,
+ id + ": minResolution set"
+ );
+
+ // teardown
+ map.destroy();
+ }
+
+ // each case is an array of id, map options, layer options, expected resolutions,
+ // expected min resolution, and expected max resolution
+ var cases = [[
+
+ /*
+ * Batch 1: map defaults and sensible layer options
+ */
+
+ "1.0", null, {resolutions: [400, 200, 100]},
+ [400, 200, 100], 100, 400
+ ], [
+ "1.1", null, {resolutions: [400, 200, 100], minResolution: 150, maxResolution: 300},
+ [400, 200, 100], 150, 300
+ ], [
+ "1.2", null, {maxResolution: 4000, numZoomLevels: 3},
+ [4000, 2000, 1000], 1000, 4000
+ ], [
+ "1.3", null, {maxResolution: 4000, maxZoomLevel: 2},
+ [4000, 2000, 1000], 1000, 4000
+ ], [
+ "1.4", null, {minResolution: 40, numZoomLevels: 3},
+ [160, 80, 40], 40, 160
+ ], [
+ "1.5", null, {minResolution: 40, maxZoomLevel: 2},
+ [160, 80, 40], 40, 160
+ ], [
+ "1.6", null, {minResolution: 10, maxResolution: 40},
+ [40, 20, 10], 10, 40
+ ], [
+ "1.7", null, {minResolution: 10, maxResolution: 40, numZoomLevels: 3},
+ [40, 20, 10], 10, 40
+ ], [
+ "1.8", null, {minResolution: 10, maxResolution: 40, maxZoomLevel: 2},
+ [40, 20, 10], 10, 40
+ ], [
+ "1.9", null, {scales: [400000, 200000, 100000]},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "1.10", null, {scales: [400000, 200000, 100000], minScale: 400000, maxScale: 100000},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "1.11", null, {minScale: 400000, numZoomLevels: 3},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "1.12", null, {minScale: 400000, maxZoomLevel: 2},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "1.13", null, {maxScale: 100000, numZoomLevels: 3},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "1.14", null, {maxScale: 100000, maxZoomLevel: 2},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "1.15", null, {maxScale: 100000, minScale: 400000},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "1.16", null, {maxScale: 100000, minScale: 400000, numZoomLevels: 3},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "1.17", null, {maxScale: 100000, minScale: 400000, maxZoomLevel: 2},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "1.18", null, {scales: [400000, 200000, 100000], units: "m"},
+ [141.11139333389778, 70.55569666694889, 35.277848333474445], 35.277848333474445, 141.11139333389778
+ ], [
+ "1.19", null, {minScale: 400000, numZoomLevels: 3, units: "m"},
+ [141.11139333389778, 70.55569666694889, 35.277848333474445], 35.277848333474445, 141.11139333389778
+ ], [
+ "1.20", null, {maxScale: 100000, numZoomLevels: 3, units: "m"},
+ [141.11139333389778, 70.55569666694889, 35.277848333474445], 35.277848333474445, 141.11139333389778
+ ], [
+ "1.21", null, {numZoomLevels: 2}, // maxResolution calculated based on the projection's maxExtent here
+ [1.40625, 0.703125], 0.703125, 1.40625
+ ], [
+
+ /*
+ * Batch 2: custom map options map and sensible layer options
+ */
+
+ /*
+ * Batch 2.1: resolutions set in the layer options
+ */
+ "2.1.0", {resolutions: [300, 150]}, {resolutions: [400, 200, 100]},
+ [400, 200, 100], 100, 400
+ ], [
+ "2.1.1", {numZoomLevels: 4}, {resolutions: [400, 200, 100]},
+ [400, 200, 100], 100, 400
+ ], [
+ "2.1.2", {maxResolution: 300}, {resolutions: [400, 200, 100]},
+ [400, 200, 100], 100, 400
+ ], [
+ "2.1.3", {minResolution: 300}, {resolutions: [400, 200, 100]},
+ [400, 200, 100], 100, 400
+ ], [
+ "2.1.4", {scales: [4, 2, 1]}, {resolutions: [400, 200, 100]},
+ [400, 200, 100], 100, 400
+ ], [
+ "2.1.5", {minScale: 4}, {resolutions: [400, 200, 100]},
+ [400, 200, 100], 100, 400
+ ], [
+ "2.1.6", {maxScale: 4}, {resolutions: [400, 200, 100]},
+ [400, 200, 100], 100, 400
+ ], [
+ /*
+ * Batch 2.2: minResolution and maxResolution set in the layer options
+ */
+ "2.2.0", {resolutions: [80, 40, 20, 10]}, {minResolution: 12, maxResolution: 48, numZoomLevels: 0},
+ [80, 40, 20, 10], 12, 48
+ ], [
+ "2.2.1", {resolutions: [80, 40, 20, 10]}, {minResolution: 12, maxResolution: 48, numZoomLevels: -1},
+ [80, 40, 20, 10], 12, 48
+ ], [
+ "2.2.2", {resolutions: [80, 40, 20, 10]}, {minResolution: 12, maxResolution: 48, numZoomLevels: null},
+ [80, 40, 20, 10], 12, 48
+ ], [
+ "2.2.3", {resolutions: [80, 40, 20, 10]}, {minResolution: 12, maxResolution: 48, numZoomLevels: undefined},
+ [48, 24, 12], 12, 48
+ ], [
+ "2.2.4", {resolutions: [80, 40, 20, 10]}, {minResolution: 12, maxResolution: 48, numZoomLevels: 3},
+ [48, 24, 12], 12, 48
+ ], [
+ "2.2.5", {resolutions: [80, 40, 20, 10]}, {minResolution: 12, maxResolution: 48},
+ [48, 24, 12], 12, 48
+ ], [
+ /*
+ * Batch 2.3: maxResolution set in the layer options
+ */
+ "2.3.0", {resolutions: [300, 150]}, {maxResolution: 4000, numZoomLevels: 3},
+ [4000, 2000, 1000], 1000, 4000
+ ], [
+ "2.3.1", {numZoomLevels: 2}, {maxResolution: 4000, numZoomLevels: 3},
+ [4000, 2000, 1000], 1000, 4000
+ ], [
+ "2.3.2", {maxResolution: 50}, {maxResolution: 4000, numZoomLevels: 3},
+ [4000, 2000, 1000], 1000, 4000
+ ], [
+ "2.3.3", {scales: [4, 2, 1]}, {maxResolution: 4000, numZoomLevels: 3},
+ [4000, 2000, 1000], 1000, 4000
+ ], [
+ "2.3.4", {minScale: 4}, {maxResolution: 4000, numZoomLevels: 3},
+ [4000, 2000, 1000], 1000, 4000
+ ], [
+ "2.3.5", {resolutions: [300, 150]}, {maxResolution: 250},
+ [300, 150], 150, 250
+ ], [
+ /*
+ * Batch 2.4: minResolution set in the layer options
+ */
+ "2.4.0", {resolutions: [300, 150]}, {minResolution: 40, numZoomLevels: 3},
+ [160, 80, 40], 40, 160
+ ], [
+ "2.4.1", {numZoomLevels: 2}, {minResolution: 40, numZoomLevels: 3},
+ [160, 80, 40], 40, 160
+ ], [
+ "2.4.2", {minResolution: 50}, {minResolution: 40, numZoomLevels: 3},
+ [160, 80, 40], 40, 160
+ ], [
+ "2.4.3", {scales: [4, 2, 1]}, {minResolution: 40, numZoomLevels: 3},
+ [160, 80, 40], 40, 160
+ ], [
+ "2.4.4", {maxScale: 1}, {minResolution: 40, numZoomLevels: 3},
+ [160, 80, 40], 40, 160
+ ], [
+ "2.4.5", {resolutions: [300, 150]}, {minResolution: 250},
+ [300, 150], 250, 300
+ ], [
+ /*
+ * Batch 2.5: scales set in the layer options
+ */
+ "2.5.0", {resolutions: [4, 2, 1]}, {scales: [400000, 200000, 100000]},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "2.5.1", {numZoomLevels: 2}, {scales: [400000, 200000, 100000]},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "2.5.2", {maxResolution: 4}, {scales: [400000, 200000, 100000]},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "2.5.3", {minResolution: 1}, {scales: [400000, 200000, 100000]},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "2.5.4", {units: "m"}, {scales: [400000, 200000, 100000]},
+ [141.11139333389778, 70.55569666694889, 35.277848333474445], 35.277848333474445, 141.11139333389778
+ ], [
+ /*
+ * Batch 2.6: minScale set in the layer options
+ */
+ "2.6.0", {resolutions: [4, 2, 1]}, {minScale: 400000, numZoomLevels: 3},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "2.6.1", {numZoomLevels: 2}, {minScale: 400000, numZoomLevels: 3},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "2.6.2", {maxResolution: 4}, {minScale: 400000, numZoomLevels: 3},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "2.6.3", {scales: [400000, 200000, 100000]}, {minScale: 200000},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0006349563376084181
+ ], [
+ /*
+ * Batch 2.7: maxScale set in the layer options
+ */
+ "2.7.0", {resolutions: [4, 2, 1]}, {maxScale: 100000, numZoomLevels: 3},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "2.7.1", {numZoomLevels: 2}, {maxScale: 100000, numZoomLevels: 3},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "2.7.2", {minResolution: 1}, {maxScale: 100000, numZoomLevels: 3},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.00031747816880420905, 0.0012699126752168362
+ ], [
+ "2.7.3", {scales: [400000, 200000, 100000]}, {maxScale: 200000},
+ [0.0012699126752168362, 0.0006349563376084181, 0.00031747816880420905], 0.0006349563376084181, 0.0012699126752168362
+ ], [
+ /*
+ * Batch 2.8: numZoomLevels set in the layer options
+ */
+ "2.8.0", {maxResolution: 80}, {numZoomLevels: 4}, // maxResolution calculated based on the projection's maxExtent here
+ [1.40625, 0.703125, 0.3515625, 0.17578125], 0.17578125, 1.40625
+ ], [
+ "2.8.1", {maxResolution: 80, numZoomLevels: 4}, {numZoomLevels: null},
+ [80, 40, 20, 10], 10, 80
+ ], [
+ "2.8.2", {maxResolution: 80, numZoomLevels: 4}, {numZoomLevels: undefined},
+ [80, 40, 20, 10], 10, 80
+ ]];
+
+ // run all cases (4 tests each)
+ var i, num = cases.length, c;
+ t.plan(num * 4);
+ for (i=0; i<num; ++i) {
+ c = cases[i];
+ initResolutionsTest(c[0], c[1], c[2], c[3], c[4], c[5]);
+ }
+
+ }
+
+ function test_Layer_visibility(t) {
+
+ t.plan(7);
+
+ var layer = new OpenLayers.Layer('Test Layer');
+
+ t.eq(layer.getVisibility(), true, "default for layer creation is visible");
+
+ layer.setVisibility(false);
+ t.eq(layer.getVisibility(), false, "setVisibility false works");
+
+ layer.setVisibility(true);
+ t.eq(layer.getVisibility(), true, "setVisibility true works");
+
+ // Need a map in order to have moveTo called.
+ // Tests added for #654.
+ var layer = new OpenLayers.Layer.WMS('Test Layer','http://example.com');
+ var m = new OpenLayers.Map('map');
+ m.addLayer(layer);
+ m.zoomToMaxExtent();
+
+ layermoved = false;
+ layer.moveTo = function() { layermoved = true; }
+
+ layer.events.register('visibilitychanged', t, function() {
+ this.ok(true, "Visibility changed calls layer event.");
+ });
+
+ layer.setVisibility(false);
+ t.eq(layermoved, false, "Layer didn't move when calling setvis false");
+
+ layer.setVisibility(true);
+ t.eq(layermoved, true, "Layer moved when calling setvis true.");
+
+ }
+
+
+ function test_Layer_getZoomForResolution(t) {
+
+ t.plan(12);
+
+ var layer = new OpenLayers.Layer('Test Layer');
+ layer.map = {};
+
+ //make some dummy resolutions
+ layer.resolutions = [128, 64, 32, 16, 8, 4, 2];
+
+ t.eq(layer.getZoomForResolution(200), 0, "zoom all the way out");
+ t.eq(layer.getZoomForResolution(25), 2, "zoom in middle");
+ t.eq(layer.getZoomForResolution(3), 5, "zoom allmost all the way in");
+ t.eq(layer.getZoomForResolution(1), 6, "zoom all the way in");
+
+ t.eq(layer.getZoomForResolution(65), 0, "smallest containing res");
+ t.eq(layer.getZoomForResolution(63), 1, "smallest containing res");
+
+ t.eq(layer.getZoomForResolution(65, true), 1, "closest res");
+ t.eq(layer.getZoomForResolution(63, true), 1, "closest res");
+
+ layer.map.fractionalZoom = true;
+ t.eq(layer.getZoomForResolution(64), 1,
+ "(fractionalZoom) correct zoom for res in array");
+ t.eq(layer.getZoomForResolution(48).toPrecision(6), (1.5).toPrecision(6),
+ "(fractionalZoom) linear scaling for res between entries");
+ t.eq(layer.getZoomForResolution(200).toPrecision(6), (0).toPrecision(6),
+ "(fractionalZoom) doesn't return zoom below zero");
+ t.eq(layer.getZoomForResolution(1).toPrecision(6), (layer.resolutions.length - 1).toPrecision(6),
+ "(fractionalZoom) doesn't return zoom above highest index");
+ }
+
+ function test_Layer_redraw(t) {
+ t.plan(11)
+
+ var name = 'Test Layer';
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ var params = { map: '/mapdata/vmap_wms.map',
+ layers: 'basic',
+ format: 'image/jpeg'};
+
+ var layer = new OpenLayers.Layer.WMS(name, url, params);
+
+ t.ok(!layer.redraw(),
+ "redraw on an orphan layer returns false");
+
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+
+ t.ok(!layer.redraw(),
+ "redraw returns false if map does not yet have a center");
+ map.zoomToMaxExtent();
+
+ t.ok(layer.redraw(),
+ "redraw returns true after map has a center");
+
+ layer.setVisibility(false);
+ t.ok(!layer.redraw(),
+ "redraw returns false if a layer is not visible");
+
+ layer.setVisibility(true);
+ t.ok(layer.redraw(),
+ "redraw returns true even if extent has not changed");
+
+ var log = {};
+ var onMoveend = function(e) {
+ log.event = e;
+ };
+ layer.events.on({"moveend": onMoveend});
+ layer.redraw();
+ layer.events.un({"moveend": onMoveend});
+ // test that the moveend event was triggered
+ t.ok(log.event, "an event was logged");
+ t.eq(log.event.type, "moveend", "moveend was triggered");
+ t.eq(log.event.zoomChanged, true, "event says zoomChanged true - poor name");
+
+ layer.moveTo = function(bounds, zoomChanged, dragging) {
+ var extent = layer.map.getExtent();
+ t.ok(bounds.equals(extent),
+ "redraw calls moveTo with the map extent");
+ t.ok(zoomChanged,
+ "redraw calls moveTo with zoomChanged true");
+ t.ok(!dragging,
+ "redraw calls moveTo with dragging false");
+ }
+ layer.redraw();
+ }
+
+ function test_layer_setIsBaseLayer(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer();
+
+ map.events.register("changebaselayer", t, function() {
+ this.ok(true, "setIsBaseLayer() trig changebaselayer event.")
+ });
+
+ map.addLayer(layer);
+ layer.setIsBaseLayer(true);
+ t.ok(layer.isBaseLayer, "setIsBaseLayer() change isBaseLayer property.");
+ }
+
+ function test_layer_setTileSize(t) {
+ t.plan(4);
+
+ layer = new OpenLayers.Layer();
+
+ g_MapTileSize = new OpenLayers.Size(25,67);
+ layer.map = {
+ getTileSize: function() {
+ return g_MapTileSize;
+ }
+ };
+
+ var layerTileSize = new OpenLayers.Size(1,1);
+
+ //TILE SIZE
+ layer.tileSize = layerTileSize;
+
+ //parameter
+ var size = new OpenLayers.Size(2,2);
+ layer.setTileSize(size);
+ t.ok(layer.tileSize.equals(size), "size paramater set correctly to layer's tile size");
+
+ //set on layer
+ layer.tileSize = layerTileSize;
+ layer.setTileSize();
+ t.ok(layer.tileSize.equals(layerTileSize), "layer's tileSize property preserved if no parameter sent in");
+
+ //take it from map
+ layer.tileSize = null;
+ layer.setTileSize();
+ t.ok(layer.tileSize.equals(g_MapTileSize), "layer's tileSize property is null and so correctly taken from the map");
+
+
+
+ //GUTTERS
+ layer.gutter = 15;
+ size = new OpenLayers.Size(10,100);
+ layer.setTileSize(size);
+
+ var desiredImageSize = new OpenLayers.Size(40, 130);
+
+ t.ok(layer.imageSize.equals(desiredImageSize), "image size correctly calculated");
+ }
+
+ function test_Layer_getResolution(t) {
+ t.plan(1);
+ var layer = new OpenLayers.Layer("test");
+ layer.map = {
+ getZoom: function() {return "foo";}
+ };
+ layer.getResolutionForZoom = function(zoom) {
+ t.eq(zoom, "foo", "getResolution calls getResolutionForZoom");
+ }
+ layer.getResolution();
+ layer.map = null;
+ layer.destroy();
+ }
+
+ function test_Layer_getResolutionForZoom(t) {
+ t.plan(8);
+ var layer = new OpenLayers.Layer("test");
+ layer.map = {fractionalZoom: false};
+ layer.resolutions = ["zero", "one", "two"];
+ t.eq(layer.getResolutionForZoom(0), "zero",
+ "(fractionalZoom false) returns resolution for given index");
+ t.eq(layer.getResolutionForZoom(0.9), "one",
+ "(fractionalZoom false) returns resolution for float index");
+
+ layer.resolutions = [2, 4, 6, 8];
+ layer.map.fractionalZoom = true;
+ t.eq(layer.getResolutionForZoom(1).toPrecision(6), (4).toPrecision(6),
+ "(fractionalZoom true) returns resolution for integer zoom");
+
+ t.eq(layer.getResolutionForZoom(1.3).toPrecision(6), (4.6).toPrecision(6),
+ "(fractionalZoom true) for zoom 1.3 should be 4.6");
+
+ t.eq(layer.getResolutionForZoom(1.6).toPrecision(6), (5.2).toPrecision(6),
+ "(fractionalZoom true) for zoom 1.6 should be 5.2");
+
+ t.eq(layer.getResolutionForZoom(1.8).toPrecision(6), (5.6).toPrecision(6),
+ "(fractionalZoom true) for zoom 1.8 should be 5.6");
+
+ t.eq(layer.getResolutionForZoom(1.5).toPrecision(6), (5).toPrecision(6),
+ "(fractionalZoom true) returns resolution for float zoom");
+ t.eq(layer.getResolutionForZoom(3.5).toPrecision(6), (8).toPrecision(6),
+ "(fractionalZoom true) returns resolution for zoom beyond res length - 1");
+
+ }
+
+ function test_afterAdd(t) {
+
+ t.plan(4);
+
+ var log = [];
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer(null, {
+ isBaseLayer: true,
+ eventListeners: {
+ "added": function(evt) {
+ log.push(evt);
+ }
+ }
+ });
+ var hasBase = false;
+ layer.afterAdd = function() {
+ hasBase = !!(layer.map && layer.map.baseLayer);
+ }
+ map.addLayer(layer);
+ t.eq(hasBase, true, "when afterAdd is called, map has a base layer");
+ t.eq(log.length, 1, "added event triggered");
+ t.eq(log[0].map.id, map.id, "added listener argument with correct map");
+ t.eq(log[0].layer.id, layer.id, "added listener argument with correct layer");
+
+ }
+
+ function test_setOpacity(t) {
+ t.plan(5);
+
+ var map, layer, log;
+
+ map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer("");
+ map.addLayer(layer);
+
+ log = [];
+ map.events.register('changelayer', t, function(event) {
+ log.push({layer: event.layer, property: event.property});
+ });
+ layer.setOpacity(0.42);
+ t.eq(layer.opacity, 0.42,
+ "setOpacity() set layer.opacity to correct value");
+ t.eq(log.length, 1,
+ "setOpacity() triggers changelayer once");
+ t.ok(log[0].layer == layer,
+ "changelayer listener called with expected layer");
+ t.eq(log[0].property, "opacity",
+ "changelayer listener called with expected property");
+
+ // This call must not trig the event because the opacity value is the same.
+ log = [];
+ layer.setOpacity(0.42);
+ t.eq(log.length, 0,
+ "setOpacity() does not trigger changelayer if the opacity value is the same");
+ }
+
+
+/******
+ *
+ *
+ * HERE IS WHERE SOME TESTS SHOULD BE PUT TO CHECK ON THE LONLAT-PX TRANSLATION
+ * FUNCTIONS AND RESOLUTION AND GETEXTENT GETZOOMLEVEL, ETC
+ *
+ *
+ */
+
+
+ function test_Layer_destroy (t) {
+ t.plan( 8 );
+
+ var log = [];
+ var map = new OpenLayers.Map('map');
+
+ layer = new OpenLayers.Layer('Test Layer', {
+ eventListeners: {
+ "removed": function(evt) {
+ log.push(evt);
+ }
+ }
+ });
+
+ map.addLayer(layer);
+
+ layer.destroy();
+
+ t.eq( layer.name, null, "layer.name is null after destroy" );
+ t.eq( layer.div, null, "layer.div is null after destroy" );
+ t.eq( layer.map, null, "layer.map is null after destroy" );
+ t.eq( layer.options, null, "layer.options is null after destroy" );
+
+ t.eq(map.layers.length, 0, "layer removed from map");
+ t.eq(log.length, 1, "removed event triggered");
+ t.eq(log[0].map.id, map.id, "removed listener argument with correct map");
+ t.eq(log[0].layer.id, layer.id, "removed listener argument with correct layer");
+
+ map.destroy();
+
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width:500px;height:500px"></div>
+ <div id="map2" style="width:100px;height:100px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/ArcGIS93Rest.html b/misc/openlayers/tests/Layer/ArcGIS93Rest.html
new file mode 100644
index 0000000..ddca6ac
--- /dev/null
+++ b/misc/openlayers/tests/Layer/ArcGIS93Rest.html
@@ -0,0 +1,324 @@
+<html>
+<head>
+ <script type="text/javascript">var oldAlert = window.alert, gMess; window.alert = function(message) {gMess = message; return true;};</script>
+ <script type="text/javascript">window.alert = oldAlert;</script>
+<script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var layer;
+
+ var name = 'Test Layer';
+ var url = "http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer/export";
+ var params = {layers: "show:0,2"};
+
+ function test_Layer_AGS93_constructor (t) {
+ var params = {layers: "show:0,2"};
+ t.plan( 14 );
+
+ var trans_format = "png";
+ if (OpenLayers.Util.alphaHack()) { trans_format = "gif"; }
+
+ layer = new OpenLayers.Layer.ArcGIS93Rest(name, url, params);
+ t.ok( layer instanceof OpenLayers.Layer.ArcGIS93Rest, "new OpenLayers.Layer.ArcGIS93Rest returns object" );
+ t.eq( layer.url, url, "layer.url is correct (HTTPRequest inited)" );
+ t.eq( layer.params.LAYERS, "show:0,2", "params passed in correctly uppercased" );
+
+ t.eq( layer.params.FORMAT, "png", "default params correclty uppercased and copied");
+
+ t.eq(layer.isBaseLayer, true, "no transparency setting, wms is baselayer");
+
+ params.format = 'jpg';
+ layer = new OpenLayers.Layer.ArcGIS93Rest(name, url, params);
+ t.eq( layer.params.FORMAT, "jpg", "default params correclty uppercased and overridden");
+
+ params.TRANSPARENT = "true";
+ var layer2 = new OpenLayers.Layer.ArcGIS93Rest(name, url, params);
+ t.eq(layer2.isBaseLayer, false, "transparency == 'true', wms is not baselayer");
+
+ params.TRANSPARENT = "TRUE";
+ var layer3 = new OpenLayers.Layer.ArcGIS93Rest(name, url, params);
+ t.eq(layer3.isBaseLayer, false, "transparency == 'TRUE', wms is not baselayer");
+ t.eq(layer3.params.FORMAT, trans_format, "transparent = TRUE causes non-image/jpeg format");
+
+ params.TRANSPARENT = "TRuE";
+ var layer4 = new OpenLayers.Layer.ArcGIS93Rest(name, url, params);
+ t.eq(layer4.isBaseLayer, false, "transparency == 'TRuE', wms is not baselayer");
+ t.eq(layer4.params.FORMAT, trans_format, "transparent = TRuE causes non-image/jpeg format");
+
+ params.TRANSPARENT = true;
+ var layer5 = new OpenLayers.Layer.ArcGIS93Rest(name, url, params);
+ t.eq(layer5.isBaseLayer, false, "transparency == true, wms is not baselayer");
+ t.eq(layer5.params.FORMAT, trans_format, "transparent = true causes non-image/jpeg format");
+
+ params.TRANSPARENT = false;
+ var layer6 = new OpenLayers.Layer.ArcGIS93Rest(name, url, params);
+ t.eq(layer6.isBaseLayer, true, "transparency == false, wms is baselayer");
+ }
+
+ function test_Layer_AGS93_addtile (t) {
+ var params = {layers: "show:0,2"};
+ t.plan( 6 );
+
+ layer = new OpenLayers.Layer.ArcGIS93Rest(name, url, params);
+ var map = new OpenLayers.Map('map', {tileManager: null});
+ map.addLayer(layer);
+ var pixel = new OpenLayers.Pixel(5,6);
+ var tile = layer.addTile(new OpenLayers.Bounds(1,2,3,4), pixel);
+ tile.draw();
+
+ var img = tile.imgDiv;
+ var tParams = OpenLayers.Util.extend({},
+ OpenLayers.Util.upperCaseObject(params));
+ tParams = OpenLayers.Util.extend(tParams, {
+ FORMAT: "png", BBOX: "1,2,3,4", SIZE: "256,256", F: "image", BBOXSR: "4326", IMAGESR: "4326"
+ });
+ t.eq( tile.url,
+ url + "?" + OpenLayers.Util.getParameterString(tParams),
+ "image src is created correctly via addtile" );
+ t.eq( tile.getTile().style.top, "6px", "image top is set correctly via addtile" );
+ t.eq( tile.getTile().style.left, "5px", "image top is set correctly via addtile" );
+
+ var firstChild = layer.div.firstChild;
+ t.eq( firstChild.nodeName.toLowerCase(), "img", "div first child is an image object" );
+ t.ok( firstChild == img, "div first child is correct image object" );
+ t.eq( tile.position.toString(), "x=5,y=6", "Position of tile is set correctly." );
+ map.destroy();
+ }
+
+ function test_Layer_AGS93_inittiles (t) {
+ var params = {layers: "show:0,2"};
+ t.plan( 2 );
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.ArcGIS93Rest(name, url, params, {buffer: 2});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0),5);
+ t.eq( layer.grid.length, 8, "Grid rows is correct." );
+ t.eq( layer.grid[0].length, 7, "Grid cols is correct." );
+ map.destroy();
+ }
+
+
+ function test_Layer_AGS93_clone (t) {
+ var params = {layers: "show:0,2"};
+ t.plan(4);
+
+ var options = {tileSize: new OpenLayers.Size(500,50)};
+ var map = new OpenLayers.Map('map', options);
+ layer = new OpenLayers.Layer.ArcGIS93Rest(name, url, params);
+ map.addLayer(layer);
+
+ layer.grid = [ [6, 7],
+ [8, 9]];
+
+ var clone = layer.clone();
+
+ t.ok( clone.grid != layer.grid, "clone does not copy grid");
+
+ t.ok( clone.tileSize.equals(layer.tileSize), "tileSize correctly cloned");
+
+ layer.tileSize.w += 40;
+
+ t.eq( clone.tileSize.w, 500, "changing layer.tileSize does not change clone.tileSize -- a fresh copy was made, not just copied reference");
+
+ t.eq( clone.alpha, layer.alpha, "alpha copied correctly");
+
+ layer.grid = null;
+ map.destroy();
+ }
+
+ function test_Layer_AGS93_isBaseLayer(t) {
+ var params = {layers: "show:0,2"};
+ t.plan(3);
+
+ layer = new OpenLayers.Layer.ArcGIS93Rest(name, url, params);
+ t.ok( layer.isBaseLayer, "baselayer is true by default");
+
+ var newParams = OpenLayers.Util.extend({}, params);
+ newParams.transparent = "true";
+ layer = new OpenLayers.Layer.ArcGIS93Rest(name, url, newParams);
+ t.ok( !layer.isBaseLayer, "baselayer is false when transparent is set to true");
+
+ layer = new OpenLayers.Layer.ArcGIS93Rest(name, url, params, {isBaseLayer: false});
+ t.ok( !layer.isBaseLayer, "baselayer is false when option is set to false" );
+ }
+
+ function test_Layer_AGS93_mergeNewParams (t) {
+ var params = {layers: "show:0,2"};
+ t.plan( 4 );
+
+ var map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer.ArcGIS93Rest(name, url, params);
+
+ var newParams = { layers: 'sooper',
+ chickpeas: 'png'};
+
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ layer.redraw = function() {
+ t.ok(true, "layer is redrawn after new params merged");
+ }
+
+ layer.mergeNewParams(newParams);
+
+ t.eq( layer.params.LAYERS, "sooper", "mergeNewParams() overwrites well");
+ t.eq( layer.params.CHICKPEAS, "png", "mergeNewParams() adds well");
+
+ newParams.CHICKPEAS = 151;
+
+ t.eq( layer.params.CHICKPEAS, "png", "mergeNewParams() makes clean copy of hashtable");
+ map.destroy();
+ }
+
+ function test_Layer_AGS93_getFullRequestString (t) {
+ var params = {layers: "show:0,2"};
+ t.plan( 1 );
+ var map = new OpenLayers.Map('map');
+ map.projection = "xx";
+ tParams = { layers: 'show:0,2',
+ format: 'png'};
+ var tLayer = new OpenLayers.Layer.ArcGIS93Rest(name, url, tParams);
+ map.addLayer(tLayer);
+ str = tLayer.getFullRequestString();
+ var tParams = {
+ LAYERS: "show:0,2", FORMAT: "png"
+ };
+ t.eq(str,
+ url + "?" + OpenLayers.Util.getParameterString(tParams),
+ "getFullRequestString() adds SRS value");
+ map.destroy();
+
+ }
+
+ function test_Layer_AGS93_noGutters (t) {
+ t.plan(2);
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.ArcGIS93Rest("no gutter layer", url, params, {gutter: 0});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+ var tile = layer.grid[0][0];
+ var request = layer.getURL(tile.bounds);
+ var args = OpenLayers.Util.getParameters(request);
+ t.eq(parseInt(args['SIZE'][0]),
+ tile.size.w,
+ "layer without gutter requests images that are as wide as the tile");
+ t.eq(parseInt(args['SIZE'][1]),
+ tile.size.h,
+ "layer without gutter requests images that are as tall as the tile");
+
+ layer.destroy();
+ map.destroy();
+ }
+
+ function test_Layer_AGS93_gutters (t) {
+ var params = {layers: "show:0,2"};
+ t.plan(2);
+ var gutter = 15;
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.ArcGIS93Rest("gutter layer", url, params, {gutter: gutter});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+ var tile = layer.grid[0][0];
+ var request = layer.getURL(tile.bounds);
+ var args = OpenLayers.Util.getParameters(request);
+ t.eq(parseInt(args['SIZE'][0]),
+ tile.size.w + (2 * gutter),
+ "layer with gutter requests images that are wider by twice the gutter");
+ t.eq(parseInt(args['SIZE'][1]),
+ tile.size.h + (2 * gutter),
+ "layer with gutter requests images that are taller by twice the gutter");
+
+ layer.destroy();
+ map.destroy();
+
+ }
+
+ function test_Layer_AGS93_destroy (t) {
+
+ t.plan( 1 );
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.ArcGIS93Rest(name, url, params);
+ map.addLayer(layer);
+
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+
+ //grab a reference to one of the tiles
+ var tile = layer.grid[0][0];
+
+ layer.destroy();
+
+ // checks to make sure superclass (grid) destroy() was called
+
+ t.ok( layer.grid == null, "grid set to null");
+ }
+
+ function test_Layer_ADG93_Filter(t) {
+ var params = {layers: "show:0,2"};
+ t.plan( 9 );
+
+ layer = new OpenLayers.Layer.ArcGIS93Rest(name, url, params);
+ var map = new OpenLayers.Map('map', {tileManager: null});
+ map.addLayer(layer);
+ var pixel = new OpenLayers.Pixel(5,6);
+ var tile = layer.addTile(new OpenLayers.Bounds(1,2,3,4), pixel);
+ // Set up basic params.
+ var tParams = OpenLayers.Util.extend({}, OpenLayers.Util.upperCaseObject(params));
+ tParams = OpenLayers.Util.extend(tParams, {
+ FORMAT: "png", BBOX: "1,2,3,4", SIZE: "256,256", F: "image", BBOXSR: "4326", IMAGESR: "4326"
+ });
+
+ // We need to actually set the "correct" url on a dom element, because doing so encodes things not encoded by getParameterString.
+ var encodingHack = document.createElement("img");
+
+ tile.draw();
+ t.eq( tile.url, url + "?" + OpenLayers.Util.getParameterString(tParams), "image src no filter" );
+
+ layer.setLayerFilter('1', "MR_TOAD = 'FLYING'");
+ tParams["LAYERDEFS"] = "1:MR_TOAD = 'FLYING';";
+ tile.draw();
+ t.eq( tile.url, url + "?" + OpenLayers.Util.getParameterString(tParams), "image src one filter" );
+
+ layer.setLayerFilter('1', "MR_TOAD = 'NOT FLYING'");
+ tParams["LAYERDEFS"] = "1:MR_TOAD = 'NOT FLYING';";
+ tile.draw();
+ t.eq( tile.url, url + "?" + OpenLayers.Util.getParameterString(tParams), "image src change one filter" );
+
+ layer.setLayerFilter('2', "true = false");
+ tParams["LAYERDEFS"] = "1:MR_TOAD = 'NOT FLYING';2:true = false;";
+ tile.draw();
+ t.eq( tile.url, url + "?" + OpenLayers.Util.getParameterString(tParams), "image src two filters" );
+
+ layer.setLayerFilter('99', "some_col > 5");
+ tParams["LAYERDEFS"] = "1:MR_TOAD = 'NOT FLYING';2:true = false;99:some_col > 5;";
+ tile.draw();
+ t.eq( tile.url, url + "?" + OpenLayers.Util.getParameterString(tParams), "image src three filters" );
+
+ layer.clearLayerFilter('2');
+ tParams["LAYERDEFS"] = "1:MR_TOAD = 'NOT FLYING';99:some_col > 5;";
+ tile.draw();
+ t.eq( tile.url, url + "?" + OpenLayers.Util.getParameterString(tParams), "image src removed middle filter" );
+
+ layer.clearLayerFilter('2');
+ tParams["LAYERDEFS"] = "1:MR_TOAD = 'NOT FLYING';99:some_col > 5;";
+ tile.draw();
+ t.eq( tile.url, url + "?" + OpenLayers.Util.getParameterString(tParams), "image src removed missing filter (no change)" );
+
+ layer.clearLayerFilter();
+ delete tParams["LAYERDEFS"];
+ tile.draw();
+ t.eq( tile.url, url + "?" + OpenLayers.Util.getParameterString(tParams), "image src removed all filters" );
+
+ layer.clearLayerFilter();
+ tile.draw();
+ t.eq( tile.url, url + "?" + OpenLayers.Util.getParameterString(tParams), "image src removed all (no) filters" );
+ }
+
+
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/ArcGISCache.html b/misc/openlayers/tests/Layer/ArcGISCache.html
new file mode 100644
index 0000000..b5ed5d5
--- /dev/null
+++ b/misc/openlayers/tests/Layer/ArcGISCache.html
@@ -0,0 +1,256 @@
+<html>
+<head>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script src="../../lib/OpenLayers/Layer/ArcGISCache.js" type="text/javascript"></script>
+ <script src="ArcGISCache.json" type="text/javascript"></script>
+ <script type="text/javascript">
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var layer;
+
+ var name = 'Test Layer';
+ var url = "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer";
+ var options = { };
+
+ function test_Layer_ARCGISCACHE_constructor (t) {
+ t.plan( 1 );
+
+ var layer = new OpenLayers.Layer.ArcGISCache(name, url, options);
+ t.ok( layer instanceof OpenLayers.Layer.ArcGISCache, "returns OpenLayers.Layer.ArcGISCache object" );
+ }
+
+ function test_Layer_ARCGISCACHE_autoConfigure (t) {
+ t.plan( 5 );
+ var layerInfo = capabilitiesObject;
+
+ //initialize the layer using the JSON object from an arcgis server
+ //SEE: ArcGISCache.json
+ var layer = new OpenLayers.Layer.ArcGISCache(name, url, {
+ layerInfo: layerInfo
+ });
+ t.ok( layer instanceof OpenLayers.Layer.ArcGISCache, "returns OpenLayers.Layer.ArcGISCache object" );
+ t.ok( layer.projection = 'EPSG:' + layerInfo.spatialReference.wkid, "projection is set correctly");
+ t.ok( layer.units = 'm', "map units are set correctly");
+ t.ok( layer.resolutions && layer.resolutions.length == 20, "resolutions are initialized from LOD objects properly");
+
+ if (layerInfo.tileInfo) {
+ if (layerInfo.tileInfo.width && layerInfo.tileInfo.height) {
+ var tileSize = new OpenLayers.Size(layerInfo.tileInfo.width, layerInfo.tileInfo.height);
+ t.ok((layer.tileSize.width == tileSize.width) && (layer.tileSize.height == tileSize.height), "tile size is set properly");
+ }
+ else {
+ var tileSize = new OpenLayers.Size(layerInfo.tileInfo.cols, layerInfo.tileInfo.rows);
+ t.ok((layer.tileSize.width == tileSize.width) && (layer.tileSize.height == tileSize.height), "tile size is set properly");
+ }
+ }
+ }
+
+ /**
+ * lets make sure we're getting the correct urls back with a basic auto-configure setup
+ */
+ function test_Layer_ARCGISCACHE_autoConfigure_URLS(t) {
+ var layerInfo = capabilitiesObject;
+
+ //initialize the layer using the JSON object from an arcgis server
+ //SEE: ArcGISCache.json
+ var layer = new OpenLayers.Layer.ArcGISCache(name, url, {
+ layerInfo: layerInfo,
+ params: {foo: "bar"}
+ });
+ var map = new OpenLayers.Map('map', {
+ maxExtent: layer.maxExtent,
+ units: layer.units,
+ resolutions: layer.resolutions,
+ numZoomLevels: layer.numZoomLevels,
+ tileSize: layer.tileSize,
+ projection: layer.displayProjection,
+ StartBounds: layer.initialExtent
+ });
+ map.addLayers([layer]);
+
+ //this set represents a few edge cases, and some more specific cases, it is by no means exhaustive,
+ var urlSets = [
+ {
+ bounds: new OpenLayers.Bounds(-36787612.973083,-22463925.368666, 43362420.398053,17611091.316902),
+ url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/0/0/0"
+ },
+ {
+ bounds: new OpenLayers.Bounds(-31793889.951914,4589319.785415, 8281126.733654,24626828.128199),
+ url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/1/0/0"
+ },
+ {
+ bounds: new OpenLayers.Bounds(-24639873.181971,12676071.933457, -4602364.839187,22694826.104849),
+ url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/2/0/0"
+ },
+ {
+ bounds: new OpenLayers.Bounds(-15521241.455665,11580270.695961, 4516266.887119,21599024.867353),
+ url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/2/0/1"
+ },
+ {
+ bounds: new OpenLayers.Bounds(-9265879.5435993,2870892.9335638, -8639707.4078873,3183979.0014198) ,
+ url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/7/54/35"
+ },
+ {
+ bounds: new OpenLayers.Bounds(-10741909.131798,4684560.1640365, -10585366.09787,4762831.6810005),
+ url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/9/195/119"
+ },
+ {
+ bounds: new OpenLayers.Bounds(-13668958.106938,4456961.2611504, -13512415.07301,4535232.7781144),
+ url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/9/198/82"
+ }
+ ];
+
+ t.plan( urlSets.length );
+ for(var i=0;i<urlSets.length;i++)
+ {
+ var o = urlSets[i];
+ map.zoomToExtent(o.bounds, true);
+
+ var resultUrl = layer.getURL(o.bounds);
+ t.ok( resultUrl == o.url + "?foo=bar", "correct tile returned for " + o.bounds);
+ }
+ }
+
+ /**
+ * Test the formatting for the 'direct' urls, especially when not auto-configuring the layer
+ */
+ function test_Layer_ARCGISCACHE_direct(t) {
+ var roadsUrl = 'http://serverx.esri.com/arcgiscache/DG_County_roads_yesA_backgroundDark/Layers/_alllayers';
+ var urlSets = [
+ {
+ bounds: new OpenLayers.Bounds(289244.67443386,4317153.7421985, 306178.04163392,4325620.4257985),
+ url: roadsUrl + "/L00/R0000029e/C0000027f.png"
+ },
+ {
+ bounds: new OpenLayers.Bounds(308658.51534463,4303230.0164352, 325591.88254469,4311696.7000352),
+ url: roadsUrl + "/L00/R000002a0/C00000282.png"
+ },
+ {
+ bounds: new OpenLayers.Bounds(311136.39626998,4318933.8711555, 311678.26402038,4319204.8050307) ,
+ url: roadsUrl + "/L05/R000051e0/C00004e52.png"
+ }
+ ];
+ t.plan( urlSets.length );
+
+
+ //perform the exact setup from the arcgiscache_direct example
+
+ // First 4 variables extracted from conf.xml file
+ // Tile layers & map MUST have same projection
+ var proj='EPSG:26915';
+
+ // Layer can also accept serverResolutions array
+ // to deal with situation in which layer resolution array & map resolution
+ // array are out of sync
+ var mapResolutions = [33.0729828126323,16.9333672000677,8.46668360003387,4.23334180001693,2.11667090000847,1.05833545000423];
+
+ // For this example this next line is not really needed, 256x256 is default.
+ // However, you would need to change this if your layer had different tile sizes
+ var tileSize = new OpenLayers.Size(256,256);
+
+ // Tile Origin is required unless it is the same as the implicit map origin
+ // which can be effected by several variables including maxExtent for map or base layer
+ var agsTileOrigin = new OpenLayers.LonLat(-5120900,9998100);
+
+ // This can really be any valid bounds that the map would reasonably be within
+ var mapExtent = new OpenLayers.Bounds(289310.8204,4300021.937,314710.8712,4325421.988);
+
+
+ var map = new OpenLayers.Map('map', {
+ maxExtent:mapExtent,
+ controls: [
+ new OpenLayers.Control.Navigation(),
+ new OpenLayers.Control.LayerSwitcher(),
+ new OpenLayers.Control.PanZoomBar(),
+ new OpenLayers.Control.MousePosition()]
+ });
+
+ var layer = new OpenLayers.Layer.ArcGISCache('Roads', roadsUrl, {
+ tileOrigin: agsTileOrigin,
+ resolutions: mapResolutions,
+ sphericalMercator: true,
+ maxExtent: mapExtent,
+ useArcGISServer: false,
+ isBaseLayer: true,
+ projection: proj
+ });
+
+ map.addLayers([layer]);
+ map.zoomToExtent(new OpenLayers.Bounds(-8341644, 4711236, -8339198, 4712459));
+
+ for(var i=0;i<urlSets.length;i++)
+ {
+ var o = urlSets[i];
+ map.zoomToExtent(o.bounds, true);
+ var resultUrl = layer.getURL(o.bounds);
+ t.ok( resultUrl == o.url, "correct tile returned for " + o.bounds);
+ }
+ }
+
+ /**
+ * Check the utility function for generating tile indexes against a file cache
+ * This is already tested in BaseTypes test, but these are specific,
+ * common conversions that this class will rely on, so the tests are retained
+ */
+ function test_Layer_ARCGISCACHE_zeroPad(t) {
+ t.plan(4);
+
+ var layer = new OpenLayers.Layer.ArcGISCache('test', null, { });
+
+ //some tile examples
+ t.ok('00000001' == OpenLayers.Number.zeroPad(1, 8, 16), 'zeroPad should generate tile indexes properly ');
+ t.ok('00000020' == OpenLayers.Number.zeroPad(32, 8, 16), 'zeroPad should generate tile indexes properly ');
+ t.ok('00000100' == OpenLayers.Number.zeroPad(256, 8, 16), 'zeroPad should generate tile indexes properly ');
+ t.ok('00001000' == OpenLayers.Number.zeroPad(4096, 8, 16), 'zeroPad should generate tile indexes properly ');
+ }
+
+ /**
+ * Check to ensure our LOD calculation will correctly avoid returning tile indexes less than zero
+ * (see http://trac.osgeo.org/openlayers/ticket/3169)
+ */
+ function test_Layer_ARCGISCACHE_tileBounds(t) {
+ t.plan(1);
+
+ var layer = new OpenLayers.Layer.ArcGISCache('test', null, { });
+ var res = 264.583862501058;
+ layer.tileOrigin = new OpenLayers.LonLat(0.0, 650000.0);
+ layer.tileSize = new OpenLayers.Size(512, 512);
+
+ // pick a point off the left of our tile origin (would be a negative tile index)
+ var point = new OpenLayers.Geometry.Point(-123308.94829, 393128.85817);
+
+ var tile = layer.getContainingTileCoords(point, res);
+ t.ok((tile.x >= 0 && tile.y >= 0), 'layer should not generate negative tile ranges for level of detail');
+ }
+
+ /*
+ * Test that messing up the Array.prototype does not mess up the lods of the layer.
+ * This messes up zooming when resolutions are very small/scales are very large/zoomed way in.
+ */
+ function test_Layer_ARCGISCACHE_lods (t) {
+ t.plan( 2 );
+ var layerInfo = capabilitiesObject;
+
+ lods = layerInfo.tileInfo.lods.length;
+
+ // mess up the Array prototype
+ Array.prototype.foo = function() { };
+
+ t.ok( lods == layerInfo.tileInfo.lods.length, 'proper number of "Levels of Detail" before initialization' );
+
+ // initialize the layer using the JSON object from an arcgis server
+ // see: ArcGISCache.json
+ var layer = new OpenLayers.Layer.ArcGISCache(name, url, {
+ layerInfo: layerInfo
+ });
+
+ t.ok( lods == layer.lods.length, 'proper number of "Levels of Detail" after initialization.' );
+ // restore the Array prototype
+ delete Array.prototype.foo;
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px;"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/ArcGISCache.json b/misc/openlayers/tests/Layer/ArcGISCache.json
new file mode 100644
index 0000000..79dffa8
--- /dev/null
+++ b/misc/openlayers/tests/Layer/ArcGISCache.json
@@ -0,0 +1,334 @@
+var capabilitiesObject = {
+ "currentVersion" : 10.01,
+ "serviceDescription" : "This map is designed to be used as a base map by GIS professionals and as a reference map by anyone. The base map includes administrative boundaries, cities, water features, physiographic features, parks, landmarks, highways, roads, railways, airports, and buildings overlaid on land cover and shaded relief imagery for added context. The map was compiled from a variety of best available sources from several data providers, including the U.S. Geological Survey, Food and Agriculture Organization of the United Nations, National Park Service, Tele Atlas, AND, and ESRI. The base map currently provides coverage for the world down to a scale of ~1:1m and coverage for the continental United States and Hawaii to a scale of ~1:20k. The base map also includes detailed maps for selected cities in the United States including Portland, Oregon and Philadephia, Pennsylvania. The base map was designed and developed by ESRI based on the topographic map templates that are available through the ArcGIS Resource Centers. For more information on this map, visit us \u003ca href=\"http://goto.arcgisonline.com/maps/World_Topo_Map \" target=\"_new\"\u003eonline\u003c/a\u003e.",
+ "mapName" : "Layers",
+ "description" : "This map is designed to be used as a base map by GIS professionals and as a reference map by anyone. The base map includes administrative boundaries, cities, water features, physiographic features, parks, landmarks, highways, roads, railways, airports, and buildings overlaid on land cover and shaded relief imagery for added context. The map was compiled from a variety of best available sources from several data providers, including the U.S. Geological Survey, Food and Agriculture Organization of the United Nations, National Park Service, Tele Atlas, AND, and ESRI. The base map currently provides coverage for the world down to a scale of ~1:1m and coverage for the continental United States and Hawaii to a scale of ~1:20k. The base map also includes detailed maps for selected cities in the United States including Portland, Oregon and Philadephia, Pennsylvania. The base map was designed and developed by ESRI based on the topographic map templates that are available through the ArcGIS Resource Centers. For more information on this map, visit us online at http://goto.arcgisonline.com/maps/World_Topo_Map",
+ "copyrightText" : "Sources: USGS, FAO, NPS, EPA, ESRI, DeLorme, TANA, other suppliers",
+ "layers" : [
+ {
+ "id" : 0,
+ "name" : "Topographic Info",
+ "parentLayerId" : -1,
+ "defaultVisibility" : true,
+ "subLayerIds" : [1, 2, 3, 4],
+ "minScale" : 0,
+ "maxScale" : 0
+ },
+ {
+ "id" : 1,
+ "name" : "Elevation (m)",
+ "parentLayerId" : 0,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 0,
+ "maxScale" : 0
+ },
+ {
+ "id" : 2,
+ "name" : "Elevation (ft)",
+ "parentLayerId" : 0,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 0,
+ "maxScale" : 0
+ },
+ {
+ "id" : 3,
+ "name" : "Slope",
+ "parentLayerId" : 0,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 0,
+ "maxScale" : 0
+ },
+ {
+ "id" : 4,
+ "name" : "Aspect",
+ "parentLayerId" : 0,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 0,
+ "maxScale" : 0
+ },
+ {
+ "id" : 5,
+ "name" : "Places Info",
+ "parentLayerId" : -1,
+ "defaultVisibility" : true,
+ "subLayerIds" : [6, 7, 8, 9],
+ "minScale" : 0,
+ "maxScale" : 0
+ },
+ {
+ "id" : 6,
+ "name" : "Place Names (Country Level)",
+ "parentLayerId" : 5,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 0,
+ "maxScale" : 80000000
+ },
+ {
+ "id" : 7,
+ "name" : "Place Names (State Level)",
+ "parentLayerId" : 5,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 80000001,
+ "maxScale" : 1500000
+ },
+ {
+ "id" : 8,
+ "name" : "Place Names (County Level)",
+ "parentLayerId" : 5,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 1500001,
+ "maxScale" : 400000
+ },
+ {
+ "id" : 9,
+ "name" : "Place Names (City Level)",
+ "parentLayerId" : 5,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 399999,
+ "maxScale" : 0
+ },
+ {
+ "id" : 10,
+ "name" : "Scale Descriptions",
+ "parentLayerId" : -1,
+ "defaultVisibility" : true,
+ "subLayerIds" : [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26],
+ "minScale" : 0,
+ "maxScale" : 0
+ },
+ {
+ "id" : 11,
+ "name" : "Level 15 ~1:18K",
+ "parentLayerId" : 10,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 25000,
+ "maxScale" : 15001
+ },
+ {
+ "id" : 12,
+ "name" : "Level 14 ~1:36K",
+ "parentLayerId" : 10,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 50000,
+ "maxScale" : 25001
+ },
+ {
+ "id" : 13,
+ "name" : "Level 13 ~1:72K",
+ "parentLayerId" : 10,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 100000,
+ "maxScale" : 50001
+ },
+ {
+ "id" : 14,
+ "name" : "Level 12 ~1:144K",
+ "parentLayerId" : 10,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 288000,
+ "maxScale" : 100000
+ },
+ {
+ "id" : 15,
+ "name" : "Level 11 ~1:288K",
+ "parentLayerId" : 10,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 575000,
+ "maxScale" : 288000
+ },
+ {
+ "id" : 16,
+ "name" : "Level 10 ~1:577K",
+ "parentLayerId" : 10,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 1150000,
+ "maxScale" : 575000
+ },
+ {
+ "id" : 17,
+ "name" : "Level 9 ~1:1.15M",
+ "parentLayerId" : 10,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 2200000,
+ "maxScale" : 1150000
+ },
+ {
+ "id" : 18,
+ "name" : "Level 8 ~1:2.3M",
+ "parentLayerId" : 10,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 4500000,
+ "maxScale" : 2200000
+ },
+ {
+ "id" : 19,
+ "name" : "Level 7 ~1:4.5M",
+ "parentLayerId" : 10,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 9000000,
+ "maxScale" : 4500000
+ },
+ {
+ "id" : 20,
+ "name" : "Level 6 ~1:9.2M",
+ "parentLayerId" : 10,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 18000000,
+ "maxScale" : 9000000
+ },
+ {
+ "id" : 21,
+ "name" : "Level 5 ~1:18M ",
+ "parentLayerId" : 10,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 36000000,
+ "maxScale" : 18000000
+ },
+ {
+ "id" : 22,
+ "name" : "Level 4 ~1:36M",
+ "parentLayerId" : 10,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 72000000,
+ "maxScale" : 36000000
+ },
+ {
+ "id" : 23,
+ "name" : "Level 3 ~1:72M",
+ "parentLayerId" : 10,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 75500000,
+ "maxScale" : 70000000
+ },
+ {
+ "id" : 24,
+ "name" : "Level 2 ~1:147M",
+ "parentLayerId" : 10,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 290000000,
+ "maxScale" : 147000000
+ },
+ {
+ "id" : 25,
+ "name" : "Level 1 ~1:292M",
+ "parentLayerId" : 10,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 295000000,
+ "maxScale" : 150000000
+ },
+ {
+ "id" : 26,
+ "name" : "Level 0 ~1:584M",
+ "parentLayerId" : 10,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 0,
+ "maxScale" : 295000000
+ },
+ {
+ "id" : 27,
+ "name" : "Citations",
+ "parentLayerId" : -1,
+ "defaultVisibility" : true,
+ "subLayerIds" : null,
+ "minScale" : 0,
+ "maxScale" : 0
+ }
+ ],
+ "tables" : [
+
+ ],
+ "spatialReference" : {
+ "wkid" : 102100
+ },
+ "singleFusedMapCache" : true,
+ "tileInfo" : {
+ "rows" : 256,
+ "cols" : 256,
+ "dpi" : 96,
+ "format" : "JPEG",
+ "compressionQuality" : 90,
+ "origin" : {
+ "x" : -20037508.342787,
+ "y" : 20037508.342787
+ },
+ "spatialReference" : {
+ "wkid" : 102100
+ },
+ "lods" : [
+ {"level" : 0, "resolution" : 156543.033928, "scale" : 591657527.591555},
+ {"level" : 1, "resolution" : 78271.5169639999, "scale" : 295828763.795777},
+ {"level" : 2, "resolution" : 39135.7584820001, "scale" : 147914381.897889},
+ {"level" : 3, "resolution" : 19567.8792409999, "scale" : 73957190.948944},
+ {"level" : 4, "resolution" : 9783.93962049996, "scale" : 36978595.474472},
+ {"level" : 5, "resolution" : 4891.96981024998, "scale" : 18489297.737236},
+ {"level" : 6, "resolution" : 2445.98490512499, "scale" : 9244648.868618},
+ {"level" : 7, "resolution" : 1222.99245256249, "scale" : 4622324.434309},
+ {"level" : 8, "resolution" : 611.49622628138, "scale" : 2311162.217155},
+ {"level" : 9, "resolution" : 305.748113140558, "scale" : 1155581.108577},
+ {"level" : 10, "resolution" : 152.874056570411, "scale" : 577790.554289},
+ {"level" : 11, "resolution" : 76.4370282850732, "scale" : 288895.277144},
+ {"level" : 12, "resolution" : 38.2185141425366, "scale" : 144447.638572},
+ {"level" : 13, "resolution" : 19.1092570712683, "scale" : 72223.819286},
+ {"level" : 14, "resolution" : 9.55462853563415, "scale" : 36111.909643},
+ {"level" : 15, "resolution" : 4.77731426794937, "scale" : 18055.954822},
+ {"level" : 16, "resolution" : 2.38865713397468, "scale" : 9027.977411},
+ {"level" : 17, "resolution" : 1.19432856685505, "scale" : 4513.988705},
+ {"level" : 18, "resolution" : 0.597164283559817, "scale" : 2256.994353},
+ {"level" : 19, "resolution" : 0.298582141647617, "scale" : 1128.497176}
+ ]
+ },
+ "initialExtent" : {
+ "xmin" : -45223792.233066,
+ "ymin" : -22882589.2065154,
+ "xmax" : 45223792.233066,
+ "ymax" : 22882589.2065155,
+ "spatialReference" : {
+ "wkid" : 102100
+ }
+ },
+ "fullExtent" : {
+ "xmin" : -20037507.0671618,
+ "ymin" : -19971868.8804086,
+ "xmax" : 20037507.0671618,
+ "ymax" : 19971868.8804086,
+ "spatialReference" : {
+ "wkid" : 102100
+ }
+ },
+ "units" : "esriMeters",
+ "supportedImageFormatTypes" : "PNG24,PNG,JPG,DIB,TIFF,EMF,PS,PDF,GIF,SVG,SVGZ,AI,BMP",
+ "documentInfo" : {
+ "Title" : "World Topo Map",
+ "Author" : "ESRI",
+ "Comments" : "",
+ "Subject" : "",
+ "Category" : "",
+ "Keywords" : "",
+ "Credits" : ""
+ },
+ "capabilities" : "Map,Query,Data"
+}; \ No newline at end of file
diff --git a/misc/openlayers/tests/Layer/ArcIMS.html b/misc/openlayers/tests/Layer/ArcIMS.html
new file mode 100644
index 0000000..4f86227
--- /dev/null
+++ b/misc/openlayers/tests/Layer/ArcIMS.html
@@ -0,0 +1,123 @@
+<html>
+ <head>
+ <script type="text/javascript" src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ // use an arcims map service against Avencia Inc.'s global sample map services
+ var serviceName = "OpenLayers_Sample";
+ var layerName = "Global Sample Map";
+ var imsUrl = "http://sample.avencia.com/servlet/com.esri.esrimap.Esrimap";
+
+ //
+ // create an arcims layer
+ //
+ function test_Layer_ArcIMS_constructor( t ) {
+ t.plan(11);
+
+ var options = {
+ serviceName: serviceName,
+ async: false,
+ displayOutsideMaxExtent: true
+ };
+
+ var layer = new OpenLayers.Layer.ArcIMS( layerName, imsUrl, options );
+
+ // check layer & properties
+ t.ok( layer instanceof OpenLayers.Layer.ArcIMS, "new OpenLayers.Layer.ArcIMS returns object" );
+ t.eq( layer.url, imsUrl, "layer.url is correct (HTTPRequest inited)" );
+ t.eq( layer.name, layerName, "layer.name is correct" );
+ t.eq( layer.displayOutsideMaxExtent, options.displayOutsideMaxExtent,
+ "displayOutsideMaxExtent property set correctly from options" );
+
+ // check request parameters
+ t.eq( layer.params.ServiceName, serviceName, "ServiceName set properly" );
+ t.eq( layer.params.ClientVersion, "9.2", "ClientVersion set properly" );
+
+ // check request options
+ t.eq( layer.options.async, options.async, "async property set correctly from options" );
+ t.eq( layer.options.serviceName, serviceName, "serviceName property set correctly from options" );
+ t.eq( layer.options.layers.length, 0, "layers option is the correct length" );
+ t.eq( layer.options.tileSize.w, 512, "default tile width set correctly" );
+ t.eq( layer.options.tileSize.h, 512, "default tile height set correctly" );
+ }
+
+
+
+ /*
+ * how to test getURL, getURLasync, and getFeatureInfo without a proxy?
+ *
+ */
+
+
+ //
+ // Create an arcims layer, and verify that the query changes properly
+ //
+ function test_Layer_ArcIMS_setLayerQuery(t) {
+ t.plan(9);
+
+ var options = { serviceName: serviceName };
+ var layer = new OpenLayers.Layer.ArcIMS( layerName, imsUrl, options );
+ var querydef = {
+ where: "FIPS_CNTRY = 'US'"
+ };
+
+ t.eq( layer.options.layers.length, 0, "layer definitions are empty" );
+
+ layer.setLayerQuery( "layerID", querydef );
+
+ t.eq( layer.options.layers.length, 1, "layers definitions contain one layerdef" );
+ t.ok( layer.options.layers[0].query !== null, "layer query exists" );
+ t.eq( typeof layer.options.layers[0].query.where, "string", "where query is a string" );
+ t.eq( layer.options.layers[0].query.where, querydef.where, "where query matches" );
+
+ // change the definition
+ querydef = {
+ where: "FIPS_CNTRY = 'UV'",
+ spatialfilter:true
+ }
+
+ layer.setLayerQuery( "layerID", querydef );
+
+ t.eq( layer.options.layers.length, 1, "layers definitions contain one layerdef" );
+ t.ok( layer.options.layers[0].query !== null, "layer query exists" );
+ t.eq( typeof layer.options.layers[0].query.where, "string", "where query is a string" );
+ t.eq( layer.options.layers[0].query.where, querydef.where, "where query matches" );
+ }
+ function test_Layer_ArcIMS_clone (t) {
+ t.plan(5);
+
+ var url = imsUrl;
+ var options = {
+ serviceName: serviceName,
+ async: false,
+ displayOutsideMaxExtent: true
+ };
+ var map = new OpenLayers.Map('map', {controls: []});
+ var layer = new OpenLayers.Layer.ArcIMS(name, url, options);
+ map.addLayer(layer);
+
+ layer.grid = [ [6, 7],
+ [8, 9]];
+
+ var clone = layer.clone();
+
+ t.ok( clone.grid != layer.grid, "clone does not copy grid");
+
+ t.ok( clone.tileSize.equals(layer.tileSize), "tileSize correctly cloned");
+
+ t.eq( clone.params.serviceName, layer.params.serviceName, "serviceName copied correctly");
+
+ t.eq( clone.async, layer.async, "async copied correctly");
+
+ t.eq( clone.url, layer.url, "url copied correctly");
+
+ layer.grid = null;
+ map.destroy();
+ }
+
+ </script>
+ </head>
+ <body>
+ <div id="map" style="width:500px;height:550px"></div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/Layer/Bing.html b/misc/openlayers/tests/Layer/Bing.html
new file mode 100644
index 0000000..89bbba7
--- /dev/null
+++ b/misc/openlayers/tests/Layer/Bing.html
@@ -0,0 +1,200 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var map, layer;
+
+ var layerType = 'Aerial';
+ var key = "AqTGBsziZHIJYYxgivLBf0hVdrAk9mWO5cQcb8Yux8sW5M8c8opEC2lZqKR1ZZXf";
+
+ var options = {
+ type: layerType,
+ key: key
+ };
+
+ function test_constructor(t) {
+ t.plan(3);
+
+ var origProcessMetadata = OpenLayers.Layer.Bing.processMetadata;
+ var log = [];
+ OpenLayers.Layer.Bing.processMetadata = function(metadata) {
+ var script = document.getElementById(this._callbackId);
+ log.push(script.src);
+ origProcessMetadata.apply(this, arguments);
+ };
+ layer = new OpenLayers.Layer.Bing(OpenLayers.Util.extend({
+ metadataParams: {foo: "bar"}
+ }, options));
+ t.ok(layer instanceof OpenLayers.Layer.Bing, "returns OpenLayers.Layer.Bing object" );
+ t.delay_call(5, function() {
+ t.eq(log.length, 1, "processMetadata called");
+ t.eq(OpenLayers.Util.getParameters(log[0]).foo, "bar", "metadataParams passed to url correctly.");
+ OpenLayers.Layer.Bing.processMetadata = origProcessMetadata;
+ layer.destroy();
+ });
+ }
+
+ function test_initLayer(t) {
+ t.plan(2);
+
+ var meta = [];
+ var origProcessMetadata = OpenLayers.Layer.Bing.processMetadata;
+ OpenLayers.Layer.Bing.processMetadata = function(metadata) {
+ meta.push(metadata);
+ };
+ map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer.Bing(options);
+ var extent;
+ map.addLayers([layer, new OpenLayers.Layer(null, {
+ moveTo: function(bounds, changed) {
+ extent = bounds;
+ }
+ })]);
+ map.zoomToMaxExtent();
+
+ var map2 = new OpenLayers.Map("map");
+ var layer2 = new OpenLayers.Layer.Bing(OpenLayers.Util.extend({
+ initLayer: function() {
+ // pretend we have a zoomMin of 2
+ this.metadata.resourceSets[0].resources[0].zoomMin = 2;
+ OpenLayers.Layer.Bing.prototype.initLayer.apply(this, arguments);
+ }
+ }, options));
+ var extent2;
+ map2.addLayers([layer2, new OpenLayers.Layer(null, {
+ moveTo: function(bounds, changed) {
+ extent2 = bounds;
+ }
+ })]);
+ map2.zoomToMaxExtent();
+
+ t.delay_call(5, function() {
+ origProcessMetadata.call(layer, meta[0]);
+ t.eq(extent.toBBOX(), map.getExtent().toBBOX(), "layer extent correct for base layer with zoomMin == 1.");
+ map.destroy();
+ });
+
+ t.delay_call(6, function() {
+ origProcessMetadata.call(layer2, meta[1]);
+ t.eq(extent2.toBBOX(), map2.getExtent().toBBOX(), "layer extent correct for base layer with zoomMin == 2.");
+ map2.destroy();
+ OpenLayers.Layer.Bing.processMetadata = origProcessMetadata;
+ });
+ }
+
+ function test_initLayer_notempty(t) {
+ t.plan(1);
+
+ map = new OpenLayers.Map("map", {
+ projection: "EPSG:3857",
+ layers: [new OpenLayers.Layer("dummy", {isBaseLayer: true})]
+ });
+ map.zoomToExtent([-14768652, 4492113, -12263964, 5744457]);
+ var layer = new OpenLayers.Layer.Bing(OpenLayers.Util.extend({
+ isBaseLayer: false
+ }, options));
+ map.addLayer(layer);
+
+ t.delay_call(5, function() {
+ t.ok(layer.grid[0][0].url, "Tile not empty");
+ map.destroy();
+ });
+ }
+
+ function test_attribution(t) {
+ t.plan(3);
+
+ var log = [];
+ var map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer.Bing(options);
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ t.delay_call(2, function() {
+ t.ok(OpenLayers.Util.indexOf(layer.attribution, 'olBingAttribution aerial') !== -1, "Attribution has the correct css class");
+ t.ok(OpenLayers.Util.indexOf(layer.attribution, '<img src="">') == -1, "Attribution contains a logo");
+ t.ok(OpenLayers.Util.indexOf(layer.attribution, '</img></div></a><a style=') == -1 , "Attribution contains a copyright");
+ map.destroy();
+ });
+ }
+
+ function test_attribution_notempty(t) {
+ t.plan(1);
+
+ var log = [];
+ var map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer.Bing(OpenLayers.Util.applyDefaults({type: 'Road'}, options));
+ map.addLayer(layer);
+ var format = OpenLayers.String.format;
+ OpenLayers.String.format = function(tpl, options) {
+ log.push(options.copyrights);
+ }
+ map.zoomToExtent(new OpenLayers.Bounds(-14768652, 4492113, -12263964, 5744457));
+ t.delay_call(2, function() {
+ t.ok(log.join("") !== "", "Copyright not empty");
+ OpenLayers.String.format = format;
+ map.destroy();
+ });
+ }
+
+ function test_getXYZ(t) {
+ t.plan(1);
+
+ var map = new OpenLayers.Map("map", {allOverlays: true});
+ var osm = new OpenLayers.Layer.OSM();
+ map.addLayer(osm);
+ map.zoomToExtent(new OpenLayers.Bounds(11373579,-2445208,13628777,680760));
+ layer = new OpenLayers.Layer.Bing(options);
+ map.addLayer(layer);
+
+ t.delay_call(2, function() {
+ var xyz = layer.getXYZ(layer.getTileBounds(new OpenLayers.Pixel(1,1)));
+ t.eq(xyz.z, OpenLayers.Util.indexOf(layer.serverResolutions, map.getResolution()), "zoom level correct");
+ });
+ }
+
+ function test_clone(t) {
+ t.plan(1);
+
+ var clone;
+
+ layer = new OpenLayers.Layer.Bing(options);
+ clone = layer.clone();
+ t.ok(clone instanceof OpenLayers.Layer.Bing, "clone is a Layer.Bing instance");
+ }
+
+ function test_protocol(t)
+ {
+ t.plan(5);
+
+ var map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer.Bing(options);
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ t.delay_call(5, function() {
+ t.ok(OpenLayers.Util.indexOf(layer.attribution, '<img src="//') != -1, "Attribution contains a logo with protocol //");
+ t.ok(OpenLayers.Util.indexOf(layer.attribution, '<img src="http://') == -1, "Attribution logo does not have http:// protocol");
+ t.ok(layer.grid[1][1].url.indexOf('http:') == -1, "Tile url does not contain http:");
+
+ map.destroy();
+ });
+
+ var map2 = new OpenLayers.Map("map");
+ layer_https = new OpenLayers.Layer.Bing(OpenLayers.Util.applyDefaults({protocol: 'https:'}, options));
+ map2.addLayer(layer_https);
+ map2.zoomToMaxExtent();
+
+ t.delay_call(5, function() {
+ t.ok(OpenLayers.Util.indexOf(layer_https.attribution, '<img src="https://') != -1, "Attribution logo has https:// protocol");
+ t.ok(layer_https.grid[1][1].url.indexOf('https:') == 0, "Tile url contains https:");
+ map2.destroy();
+ });
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/EventPane.html b/misc/openlayers/tests/Layer/EventPane.html
new file mode 100644
index 0000000..8d8e180
--- /dev/null
+++ b/misc/openlayers/tests/Layer/EventPane.html
@@ -0,0 +1,172 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var isOpera = (navigator.userAgent.indexOf("Opera") != -1);
+ var layer;
+
+ function test_Layer_EventPane_constructor (t) {
+ t.plan( 5 );
+
+ var layer = new OpenLayers.Layer.EventPane('Test Layer');
+
+ t.ok( layer instanceof OpenLayers.Layer.EventPane, "new OpenLayers.Layer.EventPane returns object" );
+ t.eq( layer.CLASS_NAME, "OpenLayers.Layer.EventPane", "CLASS_NAME variable set correctly");
+ t.eq( layer.name, "Test Layer", "layer.name is correct" );
+ t.eq( layer.isBaseLayer, true, "EventPane layer is always base layer" );
+ if (!isMozilla) {
+ t.ok( true, "skipping element test outside of Mozilla");
+ } else {
+ t.ok( layer.pane instanceof HTMLDivElement, "layer.pane is an HTMLDivElement" );
+ }
+ }
+
+ function test_Layer_EventPane_clone (t) {
+ t.plan( 1 );
+ t.ok( true, "need to actually write some tests here" );
+ return;
+
+ /// FIX ME FIX ME: fix this later
+
+ var map = new OpenLayers.Map('map');
+ var options = { chicken: 151, foo: "bar" };
+ var layer = new OpenLayers.Layer('Test Layer', options);
+ map.addLayer(layer);
+
+ // randomly assigned property
+ layer.chocolate = 5;
+
+ var clone = layer.clone();
+
+ t.ok( clone instanceof OpenLayers.Layer, "new OpenLayers.Layer returns object" );
+ t.eq( clone.name, "Test Layer", "default clone.name is correct" );
+ t.ok( ((clone.options["chicken"] == 151) && (clone.options["foo"] == "bar")), "clone.options correctly set" );
+ t.eq(clone.chocolate, 5, "correctly copied randomly assigned property");
+
+ layer.addOptions({chicken:152});
+ t.eq(clone.options["chicken"], 151, "made a clean copy of options");
+
+
+ t.ok( clone.map == null, "cloned layer has map property set to null")
+
+ }
+
+ function test_Layer_EventPane_setMap (t) {
+
+// MOUSEMOVE test does not seem to work...
+// t.plan( 2 );
+
+ if (OpenLayers.BROWSER_NAME != "firefox" && OpenLayers.BROWSER_NAME != "mozilla") {
+ t.plan(4);
+ } else {
+ t.plan(0);
+ t.debug_print("Firefox gives different results for different browsers on setMap on EventPane, so just don't run it for now.")
+ return;
+ }
+ var map = new OpenLayers.Map('map');
+
+ layer = new OpenLayers.Layer.EventPane('Test Layer');
+
+ //give dummy function so test wont bomb on layer.setMap()
+ layer.loadMapObject = function() { };
+ layer.getWarningHTML = function() { this.warning = true; return ""; };
+ map.addLayer(layer);
+ t.eq( parseInt(layer.pane.style.zIndex) - parseInt(layer.div.style.zIndex),
+ 1, "layer pane is 1 z-level above its div" );
+
+ t.ok( layer.warning, "warning correctly registered on no mapObject load" );
+
+ layer2 = new OpenLayers.Layer.EventPane('Test Layer');
+
+ //give dummy function so test wont bomb on layer.setMap()
+ layer2.loadMapObject = function() { this.mapObject = {}; };
+ layer2.getWarningHTML = function() { this.warning = true; return ""; }
+
+ map.addLayer(layer2);
+ t.ok(!layer2.warning, "warning not registered on mapObject load");
+
+ var log = [];
+ map.events.register("mousemove", map, function(event) {
+ log.push(event);
+ });
+
+ if (document.createEvent) { // Mozilla
+ var evObj = document.createEvent('MouseEvents');
+ evObj.initEvent('mousemove', true, false);
+ map.viewPortDiv.dispatchEvent(evObj);
+ } else if(document.createEventObject) { // IE
+ map.viewPortDiv.fireEvent('onmousemove');
+ }
+
+ t.eq(log.length, 1, "got one event");
+
+ }
+
+ function test_Layer_EventPane_setVisibility (t) {
+ t.plan( 2 );
+ layer = new OpenLayers.Layer.EventPane('Test Layer');
+ layer.setVisibility(false);
+ t.eq(layer.visibility, false, "layer pane is now invisible");
+ layer.setVisibility(true);
+ t.eq(layer.visibility, true, "layer pane is now visible");
+ }
+
+
+ function test_Layer_EventPane_removeLayer(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map');
+
+ layer = new OpenLayers.Layer.EventPane('Test Layer');
+ layer.loadMapObject = function() { };
+ layer.getWarningHTML = function() { this.warning = true; return ""; };
+ map.addLayer(layer);
+ map.removeLayer(layer);
+ var parent = layer.pane.parentNode;
+ // IE creates a DOCUMENT_FRAGMENT_NODE for the parent
+ t.ok(!parent || parent.nodeType == 11, "Layer.pane removed from dom.");
+ }
+
+ function test_repeat_add(t) {
+
+ t.plan(1);
+ var map = new OpenLayers.Map("map");
+
+ layer = new OpenLayers.Layer.EventPane();
+ layer.loadMapObject = function() {};
+ layer.getWarningHTML = function() {this.warning = true; return "";};
+
+ map.addLayer(layer);
+ map.removeLayer(layer);
+
+ // try adding the layer a second time
+ var msg = "layer successfully added after being removed";
+ var pass = true;
+ try {
+ map.addLayer(layer);
+ } catch (err) {
+ msg = "couldn't add layer after removing: " + err;
+ pass = false;
+ }
+ t.ok(pass, msg);
+
+ }
+
+ function test_destroy(t) {
+
+ t.plan(2);
+ layer = new OpenLayers.Layer.EventPane();
+ t.ok(layer.pane, "pane created on initialize");
+
+ layer.destroy();
+ t.ok(!layer.pane, "pane deleted on destroy");
+
+ }
+
+
+ </script>
+</head>
+<body>
+ <div id="map" style="height:500px;width:500px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/FixedZoomLevels.html b/misc/openlayers/tests/Layer/FixedZoomLevels.html
new file mode 100644
index 0000000..133571b
--- /dev/null
+++ b/misc/openlayers/tests/Layer/FixedZoomLevels.html
@@ -0,0 +1,137 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var layer;
+
+ function test_Layer_FixedZoomLevels (t) {
+ t.plan( 39 );
+
+ var layer = { 'MIN_ZOOM_LEVEL': 5,
+ 'MAX_ZOOM_LEVEL': 10 };
+
+
+ //defaults
+
+ layer = p_createLayer(layer);
+ p_minMaxNum(t, layer, layer.MIN_ZOOM_LEVEL, layer.MAX_ZOOM_LEVEL, "nothing specified");
+
+
+ //layer.options
+
+ // min,num
+ layer = p_createLayer(layer, {}, { minZoomLevel: 3, numZoomLevels: 12});
+ p_minMaxNum(t, layer, layer.MIN_ZOOM_LEVEL, layer.MAX_ZOOM_LEVEL, "min too low num too high(layer.options)");
+
+ layer = p_createLayer(layer, {}, { minZoomLevel: 6, numZoomLevels: 3 });
+ p_minMaxNum(t, layer, 6, 8, "valid min,num(layer.options)");
+
+
+ // max
+ layer = p_createLayer(layer, {}, { maxZoomLevel: 9 });
+ p_minMaxNum(t, layer, layer.MIN_ZOOM_LEVEL, 9, "valid max(layer.options)");
+
+ layer = p_createLayer(layer, {}, { maxZoomLevel: 12 });
+ p_minMaxNum(t, layer, layer.MIN_ZOOM_LEVEL, layer.MAX_ZOOM_LEVEL, "invalid max(layer.options)");
+
+
+
+ //map
+
+ // min,num
+ layer = p_createLayer(layer, { minZoomLevel: 3, numZoomLevels: 12});
+ p_minMaxNum(t, layer, layer.MIN_ZOOM_LEVEL, layer.MAX_ZOOM_LEVEL, "min too low num too high(map)");
+
+ layer = p_createLayer(layer, { minZoomLevel: 6, numZoomLevels: 3 });
+ p_minMaxNum(t, layer, 6, 8, "valid min,num(map)");
+
+
+ // max
+ layer = p_createLayer(layer, { maxZoomLevel: 9 });
+ p_minMaxNum(t, layer, layer.MIN_ZOOM_LEVEL, 9, "valid max(map)");
+
+ layer = p_createLayer(layer, { maxZoomLevel: 12 });
+ p_minMaxNum(t, layer, layer.MIN_ZOOM_LEVEL, layer.MAX_ZOOM_LEVEL, "invalid max(map)");
+
+ //map vs. options
+
+ layer = p_createLayer(layer, {minZoomLevel: 6, numZoomLevels: 2}, { minZoomLevel: 7, numZoomLevels: 3});
+ p_minMaxNum(t, layer, 7, 9, "min,num(layer.options) wins over (map)");
+
+ layer = p_createLayer(layer, {minZoomLevel: 6, maxZoomLevel: 8}, { minZoomLevel: 7, maxZoomLevel: 9});
+ p_minMaxNum(t, layer, 7, 9, "min,max(layer.options) wins over (map)");
+
+
+ // numZoomLevels vs. maxZoomLevel
+
+ layer = p_createLayer(layer, {maxZoomLevel: 8, numZoomLevels: 6});
+ p_minMaxNum(t, layer, layer.MIN_ZOOM_LEVEL, 10, "min,max(layer.options) wins over (map)");
+
+ // resolutions array
+
+ var resolutions = Array(20);
+ for (var i = 0; i < 20; i++) {
+ resolutions[i] = Math.random();
+ }
+ OpenLayers.Util.extend(layer, {RESOLUTIONS:resolutions});
+ var minZoomLevel = 6;
+ var numZoomLevels = 2;
+ layer = p_createLayer(layer, {}, {minZoomLevel: minZoomLevel, numZoomLevels: numZoomLevels});
+ t.eq( layer.resolutions.length, numZoomLevels, "length of resolutions array ok");
+ for (var i = 0; i < numZoomLevels; i++) {
+ t.eq( layer.resolutions[i], resolutions[i + minZoomLevel], "resolutions array at index " + i + " ok");
+ }
+ }
+
+ function test_getMapObjectZoomFromOLZoom(t) {
+ t.plan(4);
+
+ var map = new OpenLayers.Map("map", {allOverlays: true});
+ var xyz = new OpenLayers.Layer.XYZ("xyz", "${x}${y}${z}", {
+ sphericalMercator: true,
+ resolutions: [39135.7584765625, 19567.87923828125, 9783.939619140625]
+ });
+ var fixed = new (OpenLayers.Class(OpenLayers.Layer, OpenLayers.Layer.FixedZoomLevels, {
+ initialize: function() {
+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);
+ }
+ }))("fixed", {
+ resolutions: [156543.03390625, 78271.516953125, 39135.7584765625, 19567.87923828125, 9783.939619140625],
+ minZoomLevel: 1
+ });
+ map.addLayers([xyz, fixed]);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 2);
+ // map.getZoom() returns 2
+ t.eq(fixed.getMapObjectZoomFromOLZoom(map.getZoom()), 4, "correct return value from getMapObjectZoomFromOLZoom");
+ t.eq(fixed.getOLZoomFromMapObjectZoom(4), map.getZoom() - fixed.minZoomLevel, "correct return value from getOLZoomFromMapObjectZoom");
+
+ map.setBaseLayer(fixed);
+ // map.getZoom() returns 4 now
+ t.eq(fixed.getMapObjectZoomFromOLZoom(map.getZoom()), 5, "correct return value from getMapObjectZoomFromOLZoom");
+ t.eq(fixed.getOLZoomFromMapObjectZoom(5), map.getZoom(), "correct return value from getOLZoomFromMapObjectZoom");
+ }
+
+ function p_createLayer(layer, mapOptions, layerOptions) {
+
+ layer.map = mapOptions || {};
+ layer.options = layerOptions || {};
+ OpenLayers.Layer.FixedZoomLevels.prototype.initResolutions.apply(layer);
+
+ return layer;
+ }
+
+ function p_minMaxNum(t, layer, min, max, msg) {
+
+ t.eq( layer.minZoomLevel, min, "min zoom level inherited from layer constant: " + msg);
+ t.eq( layer.maxZoomLevel, max, "max zoom level inherited from layer constant: " + msg);
+ t.eq( layer.numZoomLevels, max - min + 1, "num zoom levels correctly calcuated: " + msg);
+
+ }
+
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width:256px;height:256px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/GeoRSS.html b/misc/openlayers/tests/Layer/GeoRSS.html
new file mode 100644
index 0000000..a942e83
--- /dev/null
+++ b/misc/openlayers/tests/Layer/GeoRSS.html
@@ -0,0 +1,210 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var isMSIE = (navigator.userAgent.indexOf("MSIE") > -1);
+ var layer;
+
+ var georss_txt = "./georss.txt";
+ var atom_xml = "./atom-1.0.xml";
+
+ // if this test is running online, different rules apply
+ if (isMSIE) {
+ georss_txt = "." + georss_txt;
+ atom_xml = "." + atom_xml;
+ }
+
+ function test_Layer_GeoRSS_constructor (t) {
+ t.plan( 7 );
+ layer = new OpenLayers.Layer.GeoRSS('Test Layer', georss_txt );
+ t.ok( layer instanceof OpenLayers.Layer.GeoRSS, "new OpenLayers.Layer.GeoRSS returns object" );
+ t.eq( layer.location, georss_txt, "layer.location is correct" );
+ var markers;
+ layer.loadRSS();
+ t.delay_call( 1, function() {
+ t.eq( layer.markers.length, 40, "marker length is correct" );
+ var ll = new OpenLayers.LonLat(-71.142197, 42.405696);
+ var theTitle = "Knitting Room";
+ var theDescription = 'This little shop is jammed full. Yarn, yarn everywhere. They make the most of every possible nook and cranny. I like this place also because they have a lot of different kinds of knitting needles in all different sizes. Also, the people who work here are younger and hipper than in the other stores I go to. I reccomend buying supplies here and then knitting your way through a good documentary at the Capitol Theater across the street.<br/>Address: 2 lake St, Arlington, MA <br/>Tags: knitting, yarn, pins and needles, handspun, hand dyed, novelty yarn, fancy, simple, young, hip, friendly, needles, addy, cute hats<br /><br /><a href="http://platial.com/place/90306">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/90306">Grab this on Platial</a> ';
+ t.ok( layer.markers[0].lonlat.equals(ll), "lonlat on first marker is correct" );
+ t.eq( layer.name, "Crschmidt's Places At Platial", "Layer name is correct." );
+ t.eq( layer.features[0].data.title, theTitle);
+ t.eq( layer.features[0].data.description, theDescription);
+ } );
+ }
+
+ function test_Layer_GeoRSS_dontUseFeedTitle (t) {
+ t.plan( 1 );
+ layer = new OpenLayers.Layer.GeoRSS('Test Layer', georss_txt, {'useFeedTitle': false} );
+ t.delay_call( 1, function() {
+ t.eq( layer.name, "Test Layer", "Layer name is correct when not used from feed." );
+ } );
+ }
+
+ function test_Layer_GeoRSS_AtomParsing (t) {
+ t.plan( 6 );
+ layer = new OpenLayers.Layer.GeoRSS('Test Layer', atom_xml );
+ t.ok( layer instanceof OpenLayers.Layer.GeoRSS, "new OpenLayers.Layer.GeoRSS returns object" );
+ t.eq( layer.location, atom_xml, "layer.location is correct" );
+ var markers;
+ layer.loadRSS();
+ t.delay_call( 1, function() {
+ t.eq( layer.markers.length, 2, "marker length is correct" );
+ var ll = new OpenLayers.LonLat(29.9805, 36.7702);
+ t.ok( layer.markers[0].lonlat.equals(ll), "lonlat on first marker is correct" );
+ t.like( layer.features[0].data['popupContentHTML'], '<a class="link" href="http://pleiades.stoa.org/places/638896" target="_blank">Unnamed Tumulus</a>', "Link is correct.");
+ t.eq( layer.name, "tumulus", "Layer name is correct." );
+ } );
+ }
+
+ function test_Layer_GeoRSS_draw (t) {
+// t.plan(5);
+ t.plan( 2 );
+ layer = new OpenLayers.Layer.GeoRSS('Test Layer', georss_txt);
+ t.ok( layer instanceof OpenLayers.Layer.GeoRSS, "new OpenLayers.Layer.GeoRSS returns object" );
+ var map = new OpenLayers.Map('map');
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+ map.addLayer(layer);
+ t.delay_call( 1, function() {
+ map.setCenter(new OpenLayers.LonLat(0,0),0);
+ t.eq( map.layers[1].name, layer.name, "Layer name is correct" );
+
+ });;
+ }
+ function test_Layer_GeoRSS_load_events (t) {
+ t.plan( 1 );
+ layer = new OpenLayers.Layer.GeoRSS('Test Layer', georss_txt);
+ var map = new OpenLayers.Map('map');
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+ map.addLayer(layer);
+ layer.events.register("loadstart", t, function() { this.ok(true, "loadstart event triggered once (#1580)") });
+ map.setCenter(new OpenLayers.LonLat(0,0),0);
+ }
+ function test_Layer_GeoRSS_events (t) {
+ t.plan( 4 );
+ layer = new OpenLayers.Layer.GeoRSS('Test Layer', georss_txt);
+ var map = new OpenLayers.Map('map');
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0),0);
+ var event = {};
+ t.delay_call( 2, function() {
+ t.ok(layer.markers[0].events, "First marker has an events object");
+ t.eq(layer.markers[0].events.listeners['click'].length, 1, "Marker events has one object");
+ layer.markers[0].events.triggerEvent('click', event);
+ t.eq(map.popups.length, 1, "Popup opened correctly");
+ layer.markers[1].events.triggerEvent('click', event);
+ t.eq(map.popups.length, 1, "1st popup gone, 2nd Popup opened correctly");
+ });
+ }
+ function test_Layer_GeoRSS_popups (t) {
+ t.plan( 4 );
+ layer = new OpenLayers.Layer.GeoRSS('Test Layer', georss_txt);
+ var map = new OpenLayers.Map('map');
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0),0);
+ var event = {};
+ t.delay_call( 1, function() {
+ t.ok(layer.markers[0].events, "First marker has an events object");
+ t.eq(layer.markers[0].events.listeners['click'].length, 1, "Marker events has one object");
+ layer.markers[0].events.triggerEvent('click', event);
+ t.eq(map.popups.length, 1, "Popup opened correctly");
+ layer.markers[1].events.triggerEvent('click', event);
+ t.eq(map.popups.length, 1, "1st popup gone, 2nd Popup opened correctly");
+ });
+
+ }
+ function test_Layer_GeoRSS_resizedPopups(t) {
+ layer = new OpenLayers.Layer.GeoRSS('Test Layer', georss_txt, {'popupSize': new OpenLayers.Size(200,100)});
+ t.plan( 4 );
+ var map = new OpenLayers.Map('map');
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0),0);
+ var event = {};
+ t.delay_call( 1, function() {
+ t.ok(layer.markers[0].events, "First marker has an events object");
+ t.eq(layer.markers[0].events.listeners['click'].length, 1, "Marker events has one object");
+ layer.markers[0].events.triggerEvent('click', event);
+ t.eq(map.popups.length, 1, "Popup opened correctly");
+ map.popups[0].size.w=300;
+ layer.markers[1].events.triggerEvent('click', event);
+ t.eq(map.popups.length, 1, "1st popup gone, 2nd Popup opened correctly");
+ });
+ }
+
+ function test_Layer_GeoRSS_icon(t) {
+ t.plan( 3 );
+ layer = new OpenLayers.Layer.GeoRSS('Test Layer', georss_txt);
+ var the_icon = new OpenLayers.Icon('http://boston.openguides.org/markers/AQUA.png');
+ var otherLayer = new OpenLayers.Layer.GeoRSS('Test Layer', georss_txt,{icon:the_icon});
+ var map = new OpenLayers.Map('map');
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+ map.addLayers([layer,otherLayer]);
+ map.setCenter(new OpenLayers.LonLat(0,0),0);
+ var defaultIcon = OpenLayers.Marker.defaultIcon();
+ layer.loadRSS();
+ otherLayer.loadRSS();
+ t.delay_call( 2, function() {
+ t.ok(layer.markers[0].icon, "The layer has a icon");
+ t.eq(layer.markers[0].icon.url, defaultIcon.url, "The layer without icon has the default icon.");
+ t.eq(otherLayer.markers[0].icon.url, the_icon.url,"The layer with an icon has that icon.");
+ });
+ }
+ function test_Layer_GeoRSS_loadend_Event(t) {
+ var browserCode = OpenLayers.BROWSER_NAME;
+ if (browserCode == "msie") {
+ t.plan(1);
+ t.ok(true, "IE fails the GeoRSS test. This could probably be fixed by someone with enough energy to fix it.");
+ } else {
+ t.plan(2);
+ layer = new OpenLayers.Layer.GeoRSS('Test Layer', georss_txt);
+ t.delay_call(2, function() {
+ layer.events.register('loadend', layer, function() {
+ t.ok(true, "Loadend event fired");
+ });
+ layer.parseData({
+ 'responseText': '<xml xmlns="http://example.com"><title> </title></xml>'
+ });
+ t.ok(true, "Parsing data didn't fail");
+ });
+ }
+ }
+
+ function test_Layer_GeoRSS_destroy (t) {
+ t.plan( 1 );
+ layer = new OpenLayers.Layer.GeoRSS('Test Layer', georss_txt);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+ t.delay_call( 1, function() {
+ layer.destroy();
+ t.eq( layer.map, null, "layer.map is null after destroy" );
+ });
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width:500px; height:500px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/Google.html b/misc/openlayers/tests/Layer/Google.html
new file mode 100644
index 0000000..84f17c9
--- /dev/null
+++ b/misc/openlayers/tests/Layer/Google.html
@@ -0,0 +1,369 @@
+<html>
+<head>
+ <script type="text/javascript">var oldAlert = window.alert, gMess; window.alert = function(message) {gMess = message; return true;};</script>
+ <!-- this gmaps key generated for http://openlayers.org/dev/ -->
+ <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAA9XNhd8q0UdwNC7YSO4YZghSPUCi5aRYVveCcVYxzezM4iaj_gxQ9t-UajFL70jfcpquH5l1IJ-Zyyw'></script>
+ <script type="text/javascript">window.alert = oldAlert;</script>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var layer;
+ var validkey = (window.location.protocol == "file:") ||
+ (window.location.host == "localhost") ||
+ (window.location.host == "openlayers.org");
+
+ function test_Layer_Google_message(t) {
+ t.plan(0);
+ if(gMess) {
+ t.debug_print(gMess);
+ }
+ }
+
+ function test_Layer_Google_constructor (t) {
+ if(validkey) {
+ t.plan( 4 );
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.Google('Goog Layer');
+ map.addLayer(layer);
+
+
+ t.ok( layer instanceof OpenLayers.Layer.Google, "new OpenLayers.Layer.Google returns object" );
+ t.eq( layer.CLASS_NAME, "OpenLayers.Layer.Google", "CLASS_NAME variable set correctly");
+
+ t.eq( layer.name, "Goog Layer", "layer.name is correct" );
+
+ t.ok ( layer.mapObject != null, "GMap2 Object correctly loaded");
+ } else {
+ t.plan(0);
+ t.debug_print("Google tests can't be run from " +
+ window.location.host);
+ }
+ }
+
+ function test_clone(t) {
+ if (validkey) {
+ t.plan(2);
+ var layer, clone;
+
+ // test default layer
+ layer = new OpenLayers.Layer.Google();
+ clone = layer.clone();
+ t.ok(clone instanceof OpenLayers.Layer.Google, "[default] good instance");
+
+ layer.destroy();
+ clone.destroy();
+
+ // test with alt type
+ layer = new OpenLayers.Layer.Google(null, {type: G_SATELLITE_MAP});
+ clone = layer.clone();
+ t.ok(clone.type === G_SATELLITE_MAP, "[sat] correct type");
+
+ layer.destroy();
+ clone.destroy();
+ } else {
+ t.plan(0);
+ t.debug_print("Google tests can't be run from " +
+ window.location.host);
+ }
+ }
+
+ function test_Layer_Google_isBaseLayer (t) {
+ if(validkey) {
+ t.plan(1);
+
+ var layer = new OpenLayers.Layer.Google('Goog Layer');
+
+ t.ok(layer.isBaseLayer, "a default load of google layer responds as a base layer");
+ } else {
+ t.plan(0);
+ t.debug_print("Google tests can't be run from " +
+ window.location.host);
+ }
+ }
+
+ function test_Layer_Google_Translation_lonlat (t) {
+
+ if(validkey) {
+ t.plan( 4 );
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.Google('Goog Layer');
+ map.addLayer(layer);
+
+ // these two lines specify an appropriate translation.
+ // the code afterwards works by itself to test that translation
+ // works correctly both ways.
+ var gLatLng = new GLatLng(50,100);
+ var correspondingOLLonLat = new OpenLayers.LonLat(100,50);
+
+
+ olLonLat = layer.getOLLonLatFromMapObjectLonLat(gLatLng);
+ t.ok(olLonLat.equals(correspondingOLLonLat), "Translation from GLatLng to OpenLayers.LonLat works");
+
+ var transGLatLng = layer.getMapObjectLonLatFromOLLonLat(olLonLat);
+ t.ok( transGLatLng.equals(gLatLng), "Translation from OpenLayers.LonLat to GLatLng works");
+
+ t.ok( layer.getMapObjectLonLatFromOLLonLat(null) == null, "getGLatLngFromOLLonLat(null) returns null");
+ t.ok( layer.getOLLonLatFromMapObjectLonLat(null) == null, "getOLLonLatFromGLatLng(null) returns null");
+ } else {
+ t.plan(0);
+ t.debug_print("Google tests can't be run from " +
+ window.location.host);
+ }
+ }
+
+ function test_Layer_Google_Translation_pixel (t) {
+ if(validkey) {
+ t.plan( 4 );
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.Google('Goog Layer');
+ map.addLayer(layer);
+
+ // these two lines specify an appropriate translation.
+ // the code afterwards works by itself to test that translation
+ // works correctly both ways.
+ var gPoint = new GPoint(50,100);
+ var correspondingOLPixel = new OpenLayers.Pixel(50, 100);
+
+
+ olPixel = layer.getOLPixelFromMapObjectPixel(gPoint);
+ t.ok( olPixel.equals(correspondingOLPixel), "Translation from GPoint to OpenLayers.Pixel works");
+
+ var transGPoint = layer.getMapObjectPixelFromOLPixel(olPixel);
+ t.ok( transGPoint.equals(gPoint), "Translation from OpenLayers.Pixel to GPoint works");
+
+ t.ok( layer.getMapObjectPixelFromOLPixel(null) == null, "getGPointFromOLPixel(null) returns null");
+ t.ok( layer.getOLPixelFromMapObjectPixel(null) == null, "getOLPixelFromGPoint(null) returns null");
+ } else {
+ t.plan(0);
+ t.debug_print("Google tests can't be run from " +
+ window.location.host);
+ }
+ }
+
+ function test_Layer_destroy (t) {
+ if(validkey) {
+ t.plan( 5 );
+
+ var map = new OpenLayers.Map('map');
+
+ layer = new OpenLayers.Layer.Google('Test Layer');
+ map.addLayer(layer);
+
+ layer.destroy();
+
+ t.eq( layer.name, null, "layer.name is null after destroy" );
+ t.eq( layer.div, null, "layer.div is null after destroy" );
+ t.eq( layer.map, null, "layer.map is null after destroy" );
+ t.eq( layer.options, null, "layer.options is null after destroy" );
+ t.eq( layer.gmap, null, "layer.gmap is null after destroy" );
+ } else {
+ t.plan(0);
+ t.debug_print("Google tests can't be run from " +
+ window.location.host);
+ }
+ }
+
+ function test_Layer_Goole_forwardMercator(t){
+ if(validkey) {
+ t.plan(2);
+ //Just test that the fowardMercator function still exists.
+ var layer = new OpenLayers.Layer.Google('Test Layer', {'sphericalMercator': true});
+ layer.forwardMercator = function(evt) {
+ t.ok(true,
+ "GoogleMercator.forwardMercator was called and executed." );
+ return;
+ }
+ layer.forwardMercator();
+ //Now test the fowardMercator returns the expected LonLat object
+ var layer = new OpenLayers.Layer.Google('Test Layer', {'sphericalMercator': true});
+ var lonlat2 = new OpenLayers.LonLat(Math.random(),Math.random());
+ var result = layer.forwardMercator(lonlat2.lon, lonlat2.lat);
+ t.ok(result instanceof OpenLayers.LonLat, "OpenLayers.Google.fowardMercator returns LonLat object" );
+ } else {
+ t.plan(0);
+ t.debug_print("Google tests can't be run from " +
+ window.location.host);
+ }
+ }
+
+ function test_Layer_Google_overlay(t) {
+ // Test for #849.
+ if(validkey) {
+ t.plan(1);
+ var map = new OpenLayers.Map( 'map' ,
+ { controls: [] , 'numZoomLevels':20});
+
+ var satellite = new OpenLayers.Layer.Google( "Google Satellite" , {type: G_SATELLITE_MAP, 'maxZoomLevel':18} );
+ var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0", {layers: 'basic', 'transparent':true},
+ {isBaseLayer: false, singleTile: true} );
+
+ map.addLayers([satellite, layer]);
+ map.setCenter(new OpenLayers.LonLat(10.205188,48.857593), 5);
+ map.zoomIn();
+ var size = map.getSize();
+ var px = new OpenLayers.Pixel(size.w, size.h);
+ var br = map.getLonLatFromPixel(px);
+ t.ok(layer.grid[0][0].bounds.containsLonLat(br), "Bottom right pixel is covered by untiled WMS layer");
+ } else {
+ t.plan(0);
+ t.debug_print("Google tests can't be run from " +
+ window.location.host);
+ }
+ }
+ function test_Layer_Google_isBaseLayer (t) {
+ if(validkey) {
+ t.plan(3);
+ var map = new OpenLayers.Map( 'map' ,
+ { controls: [] , 'numZoomLevels':20});
+
+ var satellite = new OpenLayers.Layer.Google( "Google Satellite" , {type: G_SATELLITE_MAP, 'maxZoomLevel':18} );
+ map.addLayers([satellite]);
+ map.zoomToMaxExtent();
+
+ t.eq(satellite.div.style.display, "", "Satellite layer is visible.");
+ satellite.setVisibility(false);
+ t.eq(satellite.div.style.display, "none", "Satellite layer is not visible.");
+ satellite.setVisibility(true);
+ t.eq(satellite.div.style.display, "block", "Satellite layer is visible.");
+
+ } else {
+ t.plan(0);
+ t.debug_print("Google tests can't be run from " +
+ window.location.host);
+ }
+ }
+
+ function test_setOpacity(t) {
+ if(validkey) {
+ t.plan(6);
+
+ var map = new OpenLayers.Map("map");
+ var gmap = new OpenLayers.Layer.Google(
+ "Google Streets", // the default
+ {numZoomLevels: 20}
+ );
+ var ghyb = new OpenLayers.Layer.Google(
+ "Google Hybrid",
+ {type: G_HYBRID_MAP, numZoomLevels: 20}
+ );
+ var gsat = new OpenLayers.Layer.Google(
+ "Google Satellite",
+ {type: G_SATELLITE_MAP, numZoomLevels: 22}
+ );
+ map.addLayers([gmap, ghyb, gsat]);
+ map.zoomToMaxExtent();
+
+ var container = map.baseLayer.mapObject.getContainer();
+ var opacityCheck = function(opacity) {
+ var style = container.style;
+ var current = style.opacity === "" ? 1 : parseFloat(style.opacity);
+ if (style.filter && !style.opacity) {
+ current = Number(style.filter.replace(/alpha\(opacity=(.+?)\)/, "$1"));
+ }
+ return (current === opacity);
+ };
+
+ gmap.setOpacity(0.5);
+ t.ok(opacityCheck(0.5), "container opacity set for visible layer");
+
+ ghyb.setOpacity(0.75);
+ t.ok(opacityCheck(0.5), "container opacity not changed if layer not visible");
+ map.setBaseLayer(ghyb);
+ t.ok(opacityCheck(0.75), "container opacity changed to 0.75 when layer becomes visible");
+
+ map.setBaseLayer(gsat);
+ t.ok(opacityCheck(1), "container opacity set to 1 by default");
+ gsat.setOpacity(0.25);
+ t.ok(opacityCheck(0.25), "container opacity changed to 0.25 for visible layer");
+
+ map.setBaseLayer(gmap);
+ t.ok(opacityCheck(0.5), "container opacity set to layer opacity");
+
+ map.destroy();
+ } else {
+ t.plan(0);
+ t.debug_print("Google tests can't be run from " +
+ window.location.host);
+ }
+ }
+
+ function test_Layer_Google_setGMapVisibility(t) {
+ if(validkey) {
+ t.plan(4);
+
+ var map1 = new OpenLayers.Map('map');
+ var gmap1 = new OpenLayers.Layer.Google("Google Streets");
+ var dummy1 = new OpenLayers.Layer("Dummy", {isBaseLayer: true});
+ map1.addLayers([dummy1, gmap1]);
+ map1.zoomToMaxExtent();
+
+ t.delay_call(2, function() {
+ t.ok(gmap1.termsOfUse.style.display == "none" || gmap1.termsOfUse.style.left == "-9999px", "termsOfUse is not visible");
+ t.eq(gmap1.poweredBy.style.display, "none", "poweredBy is not visible");
+ map1.destroy();
+ });
+
+ var map2 = new OpenLayers.Map('map', {allOverlays: true});
+ var gmap2 = new OpenLayers.Layer.Google("Google Streets", {visibility: false});
+ var dummy2 = new OpenLayers.Layer("Dummy");
+ map2.addLayers([gmap2, dummy2]);
+ map2.zoomToMaxExtent();
+
+ t.delay_call(2, function() {
+ t.ok(gmap2.termsOfUse.style.display == "none" || gmap2.termsOfUse.style.left == "-9999px", "allOverlays:true - termsOfUse is not visible");
+ t.eq(gmap2.poweredBy.style.display, "none", "allOverlays:true - poweredBy is not visible");
+ map2.destroy();
+ });
+ } else {
+ t.plan(0);
+ t.debug_print("Google tests can't be run from " +
+ window.location.host);
+ }
+ }
+
+ function test_sphericalMercator(t) {
+
+ if (validkey) {
+ t.plan(4);
+ var map, layer;
+
+ map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer.Google();
+ map.addLayer(layer);
+ t.ok(!layer.sphericalMercator, "sphericalMercator false by default");
+ t.eq(map.getProjection(), "EPSG:4326", "4326 by default without sphericalMercator");
+ map.destroy();
+
+ map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer.Google(null, {
+ sphericalMercator: true
+ });
+ map.addLayer(layer);
+ t.eq(map.getProjection(), "EPSG:900913", "900913 by default with sphericalMercator");
+ map.destroy();
+
+ map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer.Google(null, {
+ sphericalMercator: true,
+ projection: "EPSG:102113"
+ });
+ map.addLayer(layer);
+ t.eq(map.getProjection(), "EPSG:102113", "custom code respected with sphericalMercator");
+ map.destroy();
+ } else {
+ t.plan(0);
+ t.debug_print("Google tests can't be run from " +
+ window.location.host);
+ }
+ }
+
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width:500px; height: 500px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/Google/v3.html b/misc/openlayers/tests/Layer/Google/v3.html
new file mode 100644
index 0000000..5f14b48
--- /dev/null
+++ b/misc/openlayers/tests/Layer/Google/v3.html
@@ -0,0 +1,337 @@
+<html>
+<head>
+ <script src="http://maps.google.com/maps/api/js?sensor=false&amp;v=3.6"></script>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var layer;
+
+ function test_Layer_Google_constructor (t) {
+ t.plan( 5 );
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.Google('Goog Layer');
+ map.addLayer(layer);
+
+
+ t.ok( layer instanceof OpenLayers.Layer.Google, "new OpenLayers.Layer.Google returns object" );
+ t.eq( layer.CLASS_NAME, "OpenLayers.Layer.Google", "CLASS_NAME variable set correctly");
+
+ t.eq( layer.name, "Goog Layer", "layer.name is correct" );
+
+ t.ok ( layer.mapObject != null, "GMap Object correctly loaded");
+
+ t.eq(layer.version, "3", "API version 3 detected.");
+ }
+
+ function test_clone(t) {
+ t.plan(2);
+ var layer, clone;
+
+ // test default layer
+ layer = new OpenLayers.Layer.Google();
+ clone = layer.clone();
+ t.ok(clone instanceof OpenLayers.Layer.Google, "[default] good instance");
+
+ layer.destroy();
+ clone.destroy();
+
+ // test with alt type
+ layer = new OpenLayers.Layer.Google(null, {type: google.maps.MapTypeId.SATELLITE});
+ clone = layer.clone();
+ t.ok(clone.type === google.maps.MapTypeId.SATELLITE, "[sat] correct type");
+
+ layer.destroy();
+ clone.destroy();
+ }
+
+ function test_Layer_Google_isBaseLayer (t) {
+ t.plan(1);
+
+ var layer = new OpenLayers.Layer.Google('Goog Layer');
+
+ t.ok(layer.isBaseLayer, "a default load of google layer responds as a base layer");
+ }
+
+ function test_Layer_Google_Translation_lonlat (t) {
+ t.plan( 4 );
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.Google('Goog Layer');
+ map.addLayer(layer);
+
+ // these two lines specify an appropriate translation.
+ // the code afterwards works by itself to test that translation
+ // works correctly both ways.
+ var gLatLng = new google.maps.LatLng(50,100);
+ // v3 uses sphericalMercator by default
+ var correspondingOLLonLat = layer.forwardMercator(100, 50);
+
+ olLonLat = layer.getOLLonLatFromMapObjectLonLat(gLatLng);
+ t.ok(olLonLat.equals(correspondingOLLonLat), "Translation from GLatLng to OpenLayers.LonLat works");
+
+ var transGLatLng = layer.getMapObjectLonLatFromOLLonLat(olLonLat);
+ t.ok( transGLatLng.equals(gLatLng), "Translation from OpenLayers.LonLat to GLatLng works");
+
+ t.ok( layer.getMapObjectLonLatFromOLLonLat(null) == null, "getGLatLngFromOLLonLat(null) returns null");
+ t.ok( layer.getOLLonLatFromMapObjectLonLat(null) == null, "getOLLonLatFromGLatLng(null) returns null");
+ }
+
+ function test_Layer_Google_Translation_pixel (t) {
+ t.plan( 4 );
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.Google('Goog Layer');
+ map.addLayer(layer);
+
+ // these two lines specify an appropriate translation.
+ // the code afterwards works by itself to test that translation
+ // works correctly both ways.
+ var gPoint = new google.maps.Point(50,100);
+ var correspondingOLPixel = new OpenLayers.Pixel(50, 100);
+
+
+ olPixel = layer.getOLPixelFromMapObjectPixel(gPoint);
+ t.ok( olPixel.equals(correspondingOLPixel), "Translation from GPoint to OpenLayers.Pixel works");
+
+ var transGPoint = layer.getMapObjectPixelFromOLPixel(olPixel);
+ t.ok( transGPoint.equals(gPoint), "Translation from OpenLayers.Pixel to GPoint works");
+
+ t.ok( layer.getMapObjectPixelFromOLPixel(null) == null, "getGPointFromOLPixel(null) returns null");
+ t.ok( layer.getOLPixelFromMapObjectPixel(null) == null, "getOLPixelFromGPoint(null) returns null");
+ }
+
+ function test_Layer_destroy (t) {
+ t.plan( 5 );
+
+ var map = new OpenLayers.Map('map');
+
+ layer = new OpenLayers.Layer.Google('Test Layer');
+ map.addLayer(layer);
+
+ layer.destroy();
+
+ t.eq( layer.name, null, "layer.name is null after destroy" );
+ t.eq( layer.div, null, "layer.div is null after destroy" );
+ t.eq( layer.map, null, "layer.map is null after destroy" );
+ t.eq( layer.options, null, "layer.options is null after destroy" );
+ t.eq( layer.gmap, null, "layer.gmap is null after destroy" );
+ }
+
+ function test_Layer_Goole_forwardMercator(t){
+ t.plan(2);
+ //Just test that the fowardMercator function still exists.
+ var layer = new OpenLayers.Layer.Google('Test Layer', {'sphericalMercator': true});
+ layer.forwardMercator = function(evt) {
+ t.ok(true,
+ "GoogleMercator.forwardMercator was called and executed." );
+ return;
+ }
+ layer.forwardMercator();
+ //Now test the fowardMercator returns the expected LonLat object
+ var layer = new OpenLayers.Layer.Google('Test Layer', {'sphericalMercator': true});
+ var lonlat2 = new OpenLayers.LonLat(Math.random(),Math.random());
+ var result = layer.forwardMercator(lonlat2.lon, lonlat2.lat);
+ t.ok(result instanceof OpenLayers.LonLat, "OpenLayers.Google.fowardMercator returns LonLat object" );
+ }
+
+ function test_Layer_Google_overlay(t) {
+ // Test for #849.
+ t.plan(1);
+ var map = new OpenLayers.Map( 'map' ,
+ { controls: [] , 'numZoomLevels':20});
+
+ var satellite = new OpenLayers.Layer.Google( "Google Satellite" , {type: google.maps.MapTypeId.SATELLITE, 'maxZoomLevel':18} );
+ var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0", {layers: 'basic', 'transparent':true},
+ {isBaseLayer: false, singleTile: true, displayOutsideMaxExtent: true} );
+
+ map.addLayers([satellite, layer]);
+ map.setCenter(new OpenLayers.LonLat(10.205188,48.857593), 5);
+ map.zoomIn();
+ var size = map.getSize();
+ var px = new OpenLayers.Pixel(size.w, size.h);
+ var br = map.getLonLatFromPixel(px);
+ t.ok(layer.grid[0][0].bounds.containsLonLat(br), "Bottom right pixel is covered by untiled WMS layer");
+ }
+ function test_Layer_Google_isBaseLayer (t) {
+ t.plan(3);
+ var map = new OpenLayers.Map( 'map' ,
+ { controls: [] , 'numZoomLevels':20});
+
+ var satellite = new OpenLayers.Layer.Google( "Google Satellite" , {type: google.maps.MapTypeId.SATELLITE, 'maxZoomLevel':18} );
+ map.addLayers([satellite]);
+ map.zoomToMaxExtent();
+
+ t.eq(satellite.div.style.display, "", "Satellite layer is visible.");
+ satellite.setVisibility(false);
+ t.eq(satellite.div.style.display, "none", "Satellite layer is not visible.");
+ satellite.setVisibility(true);
+ t.eq(satellite.div.style.display, "block", "Satellite layer is visible.");
+ }
+
+ function test_allOverlays_invisible(t) {
+
+ t.plan(1);
+
+ var map = new OpenLayers.Map('map', {allOverlays: true});
+
+ var osm = new OpenLayers.Layer.OSM();
+ var gmap = new OpenLayers.Layer.Google("Google Streets", {visibility: false});
+
+ // keep track of last argument to setGMapVisibility
+ var visible;
+ var original = gmap.setGMapVisibility;
+ gmap.setGMapVisibility = function(vis) {
+ visible = vis;
+ original.apply(gmap, arguments);
+ }
+
+ map.addLayers([osm, gmap]);
+ map.zoomToMaxExtent();
+
+ t.ok(visible === false, "setGMapVisibility last called with false");
+
+ map.destroy();
+
+ }
+
+ function test_allOverlays_pan(t) {
+
+ t.plan(8);
+
+ var origPrecision = OpenLayers.Util.DEFAULT_PRECISION;
+ // GMaps v3 seems to use a default precision of 13, which is lower
+ // than what we use in OpenLayers.
+ // See http://trac.osgeo.org/openlayers/ticket/3059
+ OpenLayers.Util.DEFAULT_PRECISION = 13;
+
+ var map = new OpenLayers.Map('map', {allOverlays: true});
+
+ var gmap = new OpenLayers.Layer.Google("Google Streets");
+ var osm = new OpenLayers.Layer.OSM();
+ map.addLayers([gmap, osm]);
+
+ var origin = new OpenLayers.LonLat(1000000, 6000000);
+ map.setCenter(origin, 4);
+ var resolution = map.getResolution();
+
+ var dx, dy, center, expected;
+
+ // confirm that panning works with Google visible
+ dx = 100, dy = -100;
+ map.pan(dx, dy, {animate: false});
+ center = map.getCenter();
+ expected = new OpenLayers.LonLat(
+ origin.lon + (resolution * dx),
+ origin.lat - (resolution * dy)
+ );
+ t.eq(center.lon, expected.lon, "x panning with Google visible " + dx + ", " + dy);
+ t.eq(center.lat, expected.lat, "y panning with Google visible " + dx + ", " + dy);
+ map.pan(-dx, -dy, {animate: false});
+ center = map.getCenter();
+ t.eq(center.lon, origin.lon, "x panning with Google visible " + (-dx) + ", " + (-dy));
+ t.eq(center.lat, origin.lat, "y panning with Google visible " + (-dx) + ", " + (-dy));
+
+ // confirm that panning works with Google invisible
+ gmap.setVisibility(false);
+ dx = 100, dy = -100;
+ map.pan(dx, dy, {animate: false});
+ center = map.getCenter();
+ expected = new OpenLayers.LonLat(
+ origin.lon + (resolution * dx),
+ origin.lat - (resolution * dy)
+ );
+ t.eq(center.lon, expected.lon, "x panning with Google invisible " + dx + ", " + dy);
+ t.eq(center.lat, expected.lat, "y panning with Google invisible " + dx + ", " + dy);
+ map.pan(-dx, -dy, {animate: false});
+ center = map.getCenter();
+ t.eq(center.lon, origin.lon, "x panning with Google invisible " + (-dx) + ", " + (-dy));
+ t.eq(center.lat, origin.lat, "y panning with Google invisible " + (-dx) + ", " + (-dy));
+
+ map.destroy();
+ OpenLayers.Util.DEFAULT_PRECISION = origPrecision;
+ }
+
+ function test_wrapDateLine(t) {
+ t.plan(2);
+
+ var origPrecision = OpenLayers.Util.DEFAULT_PRECISION;
+ // Our default precision is very high - millimeters should be enough.
+ // See http://trac.osgeo.org/openlayers/ticket/3059
+ OpenLayers.Util.DEFAULT_PRECISION = 12;
+
+ var map = new OpenLayers.Map("map");
+
+ var gmap = new OpenLayers.Layer.Google("Google Streets");
+ map.addLayer(gmap);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 1);
+
+ var center;
+
+ // pan to the edge of the world
+ map.pan(256, 0, {animate: false});
+ center = map.getCenter();
+ t.eq(center.lon, 20037508.34, "edge of the world");
+ // pan off the edge of the world
+ map.pan(100, 0, {animate: false});
+ center = map.getCenter();
+ var expect = OpenLayers.Util.toFloat(100 * map.getResolution() - 20037508.34);
+ t.eq(center.lon, expect, "magically back in the western hemisphere");
+
+ map.destroy();
+ OpenLayers.Util.DEFAULT_PRECISION = origPrecision;
+ }
+
+ function test_respectDateLine(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map("map");
+
+ var gmap = new OpenLayers.Layer.Google("Google Streets", {wrapDateLine: false});
+ map.addLayer(gmap);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 1);
+
+ var center;
+
+ // pan to the edge of the world
+ map.pan(256, 0, {animate: false});
+ center = map.getCenter();
+ t.eq(center.lon, 20037508.34, "edge of the world");
+ // pan off the edge of the world
+ map.pan(100, 0, {animate: false});
+ center = map.getCenter();
+ t.eq(center.lon, 20037508.34, "whew, still on the edge");
+
+ map.destroy();
+
+ }
+
+ function test_moveViewportDiv(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map('map', {
+ projection: 'EPSG:3857',
+ center: [0, 0],
+ zoom: 1
+ });
+ var gmap = new OpenLayers.Layer.Google();
+ map.addLayer(gmap);
+
+ t.delay_call(4, function() {
+ t.ok(map.viewPortDiv.parentNode !== map.div, 'viewport moved inside GMaps');
+
+ var osm = new OpenLayers.Layer.OSM();
+ map.addLayer(osm);
+ map.setBaseLayer(osm);
+
+ t.ok(map.viewPortDiv.parentNode === map.div, 'viewport moved back');
+ });
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width:500px; height: 500px"></div>
+</body>
+</html> \ No newline at end of file
diff --git a/misc/openlayers/tests/Layer/Grid.html b/misc/openlayers/tests/Layer/Grid.html
new file mode 100644
index 0000000..495a9ab
--- /dev/null
+++ b/misc/openlayers/tests/Layer/Grid.html
@@ -0,0 +1,1593 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var layer;
+
+ var name = 'Test Layer';
+ var url = "http://vmap0.tiles.osgeo.org/wms/vmap0";
+ var params = {layers: 'basic', format: 'image/png'};
+
+ /**
+ * NOTE TO READER:
+ *
+ * Some of the tests on the Grid class actually use the WMS class.
+ * This is because WMS is a subclass of Grid and it implements the
+ * core functions which are necessary to test the tile-generation
+ * mechanism.
+ *
+ */
+
+
+ function test_constructor (t) {
+ t.plan( 7 );
+
+ layer = new OpenLayers.Layer.Grid(name, url, params, null);
+ t.ok( layer instanceof OpenLayers.Layer.Grid, "returns OpenLayers.Layer.Grid object" );
+ t.eq( layer.buffer, 0, "buffer default is 0");
+ t.eq( layer.ratio, 1.5, "ratio default is 1.5");
+ t.eq( layer.numLoadingTiles, 0, "numLoadingTiles starts at 0");
+ t.ok( layer.tileClass === OpenLayers.Tile.Image, "tileClass default is OpenLayers.Tile.Image");
+ t.eq( layer.className, 'olLayerGrid', "className default is olLayerGrid");
+
+ var obj = {};
+ var func = function() {};
+ layer.events.register('tileloaded', obj, func);
+
+ t.ok( layer.events.listeners['tileloaded'].length == 1, "one listener for tileloaded after register");
+ }
+
+ function test_constructor_singleTile(t) {
+ t.plan(2);
+ layer = new OpenLayers.Layer.Grid(name, url, params, {singleTile: true});
+ t.eq( layer.className, 'olLayerGridSingleTile', "className default is olLayerGridSingleTile");
+ t.eq( layer.removeBackBufferDelay, 0, "removeBackBufferDelay default is 0");
+ }
+
+ function test_setMap(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map', {tileManager: null});
+ layer = new OpenLayers.Layer.Grid(name, url, params, null);
+ map.addLayer(layer);
+ t.ok(OpenLayers.Element.hasClass(layer.div, "olLayerGrid"),
+ "olLayerGrid class assigned to layer div");
+ map.destroy();
+ }
+
+ function test_setMap_singleTile(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.Grid(name, url, params, {singleTile: true});
+ map.addLayer(layer);
+ t.ok(OpenLayers.Element.hasClass(layer.div, "olLayerGridSingleTile"),
+ "olLayerGridSingleTile class assigned to layer div");
+ map.destroy();
+ }
+
+ function test_Layer_Grid_inittiles (t) {
+ t.plan( 2 );
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS(name, url, params, {buffer:2});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0),5);
+ t.eq( layer.grid.length, 8, "Grid rows is correct." );
+ t.eq( layer.grid[0].length, 7, "Grid cols is correct." );
+
+ }
+
+ function test_Layer_Grid_tileClass(t) {
+ t.plan(2);
+
+ var myTileClass = OpenLayers.Class(OpenLayers.Tile, {});
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS(name, url, params, {
+ tileClass: myTileClass
+ });
+ map.addLayer(layer);
+
+ t.ok(layer.tileClass === myTileClass, "tileClass is set");
+ var instance = layer.addTile(new OpenLayers.Bounds(-10, 10, 50, 100),
+ new OpenLayers.Pixel(10, 12));
+
+ t.ok(instance instanceof myTileClass, "addTile returns type is correct");
+
+ map.destroy();
+ }
+
+ function test_Layer_Grid_clearTiles (t) {
+ t.plan(4);
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS(name, url, params);
+ map.addLayer(layer);
+
+ map.setCenter(new OpenLayers.LonLat(0,0));
+
+ var numTiles = layer.grid.length * layer.grid[0].length;
+
+ //our count of how many times tile.destroy() is called
+ tilesDeleted = 0;
+
+ //this will get set to false if we try to destroy a tile that has
+ // not been unhookedv
+ allTilesUnhooked = true;
+
+ OpenLayers.Tile.Image.prototype._destroy =
+ OpenLayers.Tile.Image.prototype.destroy;
+
+ OpenLayers.Tile.Image.prototype.destroy = function() {
+ if (!this.unhooked) {
+ allTilesUnhooked = false;
+ }
+ tilesDeleted++;
+ }
+
+ layer.removeTileMonitoringHooks = function(tile) {
+ tile.unhooked = true;
+ }
+
+ layer.clearGrid();
+
+ t.ok( layer.grid != null, "layer.grid does not get nullified" );
+ t.eq(tilesDeleted, numTiles, "all tiles destroy()ed properly");
+ t.ok(allTilesUnhooked, "all tiles unhooked before being destroyed");
+ t.eq(layer.gridResolution, null, "gridResolution set to null");
+
+ OpenLayers.Tile.Image.prototype.destroy =
+ OpenLayers.Tile.Image.prototype._destroy;
+
+ }
+
+
+ function test_Layer_Grid_getTilesBounds(t) {
+ t.plan(4);
+
+ layer = new OpenLayers.Layer.WMS(name, url, params);
+
+
+ //normal grid
+ var bl = { bounds: new OpenLayers.Bounds(1,2,2,3)};
+ var tr = { bounds: new OpenLayers.Bounds(2,3,3,4)};
+ layer.grid = [ [6, tr],
+ [bl, 7]];
+
+ var bounds = layer.getTilesBounds();
+ var testBounds = new OpenLayers.Bounds(1,2,3,4);
+
+ t.ok( bounds.equals(testBounds), "getTilesBounds() returns correct bounds");
+
+ //no tiles
+ layer.grid = [];
+ bounds = layer.getTilesBounds();
+
+ t.ok(bounds == null, "getTilesBounds() on a tile-less grid returns null");
+
+
+ //singleTile
+ var singleTile = { bounds: new OpenLayers.Bounds(1,2,3,4)};
+ layer.grid = [ [ singleTile ] ];
+ bounds = layer.getTilesBounds();
+
+ t.ok( bounds.equals(testBounds), "getTilesBounds() returns correct bounds");
+
+ //world wrapped around the dateline
+ var bl = { bounds: new OpenLayers.Bounds(0,-90,180,90)};
+ var tr = { bounds: new OpenLayers.Bounds(-180,-90,0,90)};
+ layer.grid = [[bl, tr]];
+
+ var bounds = layer.getTilesBounds();
+ var testBounds = new OpenLayers.Bounds(0,-90,360,90);
+
+ t.ok( bounds.equals(testBounds), "getTilesBounds() returns correct bounds");
+
+ }
+
+ function test_Layer_Grid_getResolution(t) {
+ t.plan( 1 );
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS(name, url, params);
+ map.addLayer(layer);
+
+ map.zoom = 5;
+
+ t.eq( layer.getResolution(), 0.0439453125, "getResolution() returns correct value");
+ }
+
+ function test_Layer_Grid_getZoomForExtent(t) {
+ t.plan( 2 );
+ var bounds, zoom;
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS(name, url, params);
+ map.addLayer(layer);
+
+ bounds = new OpenLayers.Bounds(10,10,12,12);
+ zoom = layer.getZoomForExtent(bounds);
+
+ t.eq( zoom, 8, "getZoomForExtent() returns correct value");
+
+ bounds = new OpenLayers.Bounds(10,10,100,100);
+ zoom = layer.getZoomForExtent(bounds);
+
+ t.eq( zoom, 2, "getZoomForExtent() returns correct value");
+ }
+
+ function test_moveGriddedTiles(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS(name, url, params, {buffer: 2});
+ map.addLayer(layer);
+ map.setCenter([0, 0], 5);
+ var count = 0;
+ layer.shiftColumn = function(prepend) {
+ ++count;
+ OpenLayers.Layer.WMS.prototype.shiftColumn.apply(this, arguments);
+ }
+ map.moveTo([15, 0]);
+ t.delay_call(.5, function() {
+ t.eq(count, 1, "column shifted once");
+ });
+ }
+
+ function test_Layer_Grid_moveTo(t) {
+
+ t.plan(13);
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS(name, url, params);
+ layer.destroy = function() {}; //we're going to do funky things with the grid
+ layer.applyBackBuffer = function() {}; // backbuffering isn't under test here
+ map.addLayer(layer);
+ map.setCenter([-10, 0], 5);
+
+ var log = [];
+
+ //make sure null bounds doesnt cause script error.
+ // no test necessary, just action
+ map.getExtent = function() { return null; }
+ layer.singleTile = false;
+ layer.moveTo(); //checks to make sure null bounds doesnt break us
+
+
+ //observing globals
+ layer.initSingleTile = function(bounds) {
+ g_WhichFunc = "InitSingle";
+ g_Bounds = bounds;
+ };
+ layer.initGriddedTiles = function(bounds) {
+ g_WhichFunc = "InitGridded";
+ g_Bounds = bounds;
+ };
+ layer.moveGriddedTiles = function() {
+ g_WhichFunc = "MoveGridded";
+ g_Bounds = layer.map.getExtent();
+ };
+ var clearTestBounds = function() {
+ g_WhichFunc = null;
+ g_Bounds = null;
+ };
+
+ //default map extent (tested every time below)
+ b = new OpenLayers.Bounds(0,0,100,100);
+ map.getExtent = function() {
+ return b;
+ };
+ var tilesBounds = null;
+ layer.getTilesBounds = function() {
+ return tilesBounds;
+ }
+
+
+//FORCE
+
+ //empty grid
+ layer.grid = [];
+ //grid
+ clearTestBounds();
+ layer.singleTile = false;
+ layer.moveTo()
+ t.ok(g_Bounds.equals(b), "if grid is empty, initGridded called");
+
+ //singletile
+ clearTestBounds();
+ layer.singleTile = true;
+ layer.moveTo()
+ t.ok(g_Bounds.equals(b), "if grid is empty, initSingleTile called");
+
+ //zoomChanged
+ zoomChanged = true;
+ layer.grid = [ [ {} ] ];
+
+ //grid
+ clearTestBounds();
+ layer.singleTile = false;
+ layer.moveTo(null, zoomChanged);
+ t.ok(g_Bounds.equals(b), "if layer has grid but zoomChanged is called, initGridded called");
+
+ //singletile
+ clearTestBounds();
+ layer.singleTile = true;
+ layer.moveTo(null, zoomChanged);
+ t.ok(g_Bounds.equals(b), "if layer has grid but zoomChanged is called, initSingleTile called");
+
+
+//NO FORCE
+ zoomChanged = false;
+ layer.grid = [ [ {} ] ];
+
+ //single tile
+ layer.singleTile = true;
+
+ //DRAGGING
+ var dragging = true;
+
+ //in bounds
+ clearTestBounds();
+ tilesBounds = new OpenLayers.Bounds(-10,-10,110,110);
+ layer.moveTo(null, zoomChanged, dragging);
+ t.ok(g_Bounds == null, "if dragging and tile in bounds, no init()");
+
+ //out bounds
+ clearTestBounds();
+ tilesBounds = new OpenLayers.Bounds(10,10,120,120);
+ layer.moveTo(null, zoomChanged, dragging);
+ t.ok(g_Bounds == null, "if dragging and tile out of bounds, no init()");
+
+ //NOT DRAGGING
+ dragging = false;
+
+ //in bounds
+ clearTestBounds();
+ tilesBounds = new OpenLayers.Bounds(-10,-10,110,110);
+ layer.moveTo(null, zoomChanged, dragging);
+ t.ok(g_Bounds == null, "if dragging and tile in bounds, no init()");
+
+ //out bounds
+ clearTestBounds();
+ tilesBounds = new OpenLayers.Bounds(10,10,120,120);
+ layer.moveTo(null, zoomChanged, dragging);
+ t.ok(g_WhichFunc == "InitSingle", "if not dragging and tile out of bounds, we call initSingleTile()");
+ t.ok(g_Bounds.equals(b), "if not dragging and tile out of bounds, we call initSingleTile() with correct bounds");
+
+
+ //gridded
+ layer.grid = [ [ {position: new OpenLayers.Pixel(0,0)} ] ];
+ layer.singleTile = false;
+
+ //regular move
+ clearTestBounds();
+ tilesBounds = new OpenLayers.Bounds(10,10,120,120);
+ g_WhichFunc = null;
+ layer.moveTo(null, zoomChanged);
+ t.eq(g_WhichFunc, "MoveGridded", "if tiles not drastically out of bounds, we call moveGriddedTile()");
+ t.ok(g_Bounds.equals(b), "if tiles not drastically out of bounds, we call moveGriddedTile() with correct bounds");
+
+ // drastic pan
+ clearTestBounds();
+ tilesBounds = new OpenLayers.Bounds(-150,-150,-120,-120);
+ layer.moveTo(null, zoomChanged);
+ t.ok(g_WhichFunc == "InitGridded", "if tiles drastically out of bounds, we call initGriddedTile()");
+ t.ok(g_Bounds.equals(b), "if tiles drastically out of bounds, we call initGriddedTile() with correct bounds");
+ }
+
+ /** THIS WOULD BE WHERE THE TESTS WOULD GO FOR
+ *
+ * -insertColumn
+ * -insertRow
+ *
+
+ function 08_Layer_Grid_insertColumn(t) {
+ }
+
+ function 09_Layer_Grid_insertRow(t) {
+ }
+
+ *
+ */
+
+ function test_Layer_Grid_clone(t) {
+ t.plan(7);
+
+ var options = {tileSize: new OpenLayers.Size(500,50)};
+ var map = new OpenLayers.Map('map', options);
+ layer = new OpenLayers.Layer.Grid(name, url, params);
+ map.addLayer(layer);
+
+ layer.grid = [ [6, 7],
+ [8, 9]];
+
+ // if we clone when tiles are still loading, this should not influence the clone
+ layer.numLoadingTiles = 1;
+ var clone = layer.clone();
+ t.eq( clone.numLoadingTiles, 0, "numLoadingTiles should be reset");
+ t.ok( clone.grid != layer.grid, "clone does not copy grid");
+ t.ok( clone.grid.length == 0, "clone creates a new array instead");
+
+ t.eq(clone.backBuffer, null, "no backbuffer from original");
+
+ t.ok( clone.tileSize.equals(layer.tileSize), "tileSize correctly cloned");
+
+ layer.tileSize.w += 40;
+
+ t.eq( clone.tileSize.w, 500, "changing layer.tileSize does not change clone.tileSize -- a fresh copy was made, not just copied reference");
+
+ t.eq( clone.alpha, layer.alpha, "alpha copied correctly");
+
+ layer.grid = null;
+ }
+
+ function test_Layer_Grid_setTileSize(t) {
+ t.plan(1);
+
+ OpenLayers.Layer.HTTPRequest.prototype._setTileSize =
+ OpenLayers.Layer.HTTPRequest.prototype.setTileSize;
+
+ OpenLayers.Layer.HTTPRequest.prototype.setTileSize = function(size) {
+ g_Size = size;
+ };
+
+
+ layer = new OpenLayers.Layer.Grid(name, url, params, {
+ singleTile: true
+ });
+ mapSize = new OpenLayers.Size(100,1000);
+ layer.map = {
+ getSize: function() { return mapSize; }
+ }
+
+ g_Size = null;
+ layer.setTileSize();
+
+ var idealSize = new OpenLayers.Size(150,1500);
+ t.ok( g_Size && g_Size.equals(idealSize), "correctly calculated tile size passed to superclass setTileSize() function");
+
+ OpenLayers.Layer.HTTPRequest.prototype.setTileSize =
+ OpenLayers.Layer.HTTPRequest.prototype._setTileSize;
+ }
+
+ function test_Layer_Grid_initSingleTile(t) {
+ t.plan( 11 );
+
+ layer = new OpenLayers.Layer.Grid(name, url, params, {
+ singleTile: true,
+ ratio: 2
+ });
+
+ var bounds = new OpenLayers.Bounds(-10,10,50,100);
+
+ var desiredTileBounds = new OpenLayers.Bounds(-40,-35,80,145);
+ var desiredUL = new OpenLayers.LonLat(-40,145);
+
+ translatedPX = {};
+ layer.map = {
+ getLayerPxFromLonLat: function(ul) {
+ t.ok(ul.lon === desiredUL.lon && ul.lat === desiredUL.lat, "correct ul passed to translation");
+ return translatedPX;
+ },
+ getResolution: function() {
+ }
+ }
+
+ var newTile = {
+ draw: function() {
+ t.ok(true, "newly created tile has been drawn");
+ }
+ };
+ layer.addTile = function(tileBounds, px) {
+ t.ok(tileBounds.equals(desiredTileBounds), "correct tile bounds passed to addTile to create new tile");
+ t.ok(px == translatedPX, "correct tile px passed to addTile to create new tile");
+ return newTile;
+ };
+ layer.addTileMonitoringHooks = function(tile) {
+ t.ok(tile == newTile, "adding monitoring hooks to the newly added tile");
+ };
+ layer.removeExcessTiles = function(x,y) {
+ t.ok(x == 1 && y == 1, "removeExcessTiles called")
+ };
+
+
+ layer.grid = [];
+ layer.initSingleTile(bounds);
+
+ t.ok(layer.grid[0][0] == newTile, "grid's 0,0 is set to the newly created tile");
+
+ var tile = {
+ moveTo: function(tileBounds, px) {
+ t.ok(tileBounds.equals(desiredTileBounds), "correct tile bounds passed to tile.moveTo()");
+ t.ok(px == translatedPX, "correct tile px passed to tile.moveTo()");
+ }
+ };
+ layer.grid = [[ tile ]];
+ layer.initSingleTile(bounds);
+
+ }
+
+ function test_Layer_Grid_addTileMonitoringHooks(t) {
+ t.plan(18);
+
+ layer = new OpenLayers.Layer.Grid();
+ layer.events = {
+ 'triggerEvent': function(str, evt) {
+ g_events.push([str, evt]);
+ }
+ }
+
+ var tile = {
+ events: {
+ register: function(name, obj, func) {
+ g_registered[name] = [obj, func];
+ },
+ on: function(obj) {
+ for (var o in obj) {
+ if (obj.hasOwnProperty(o)) {
+ tile.events.register(o, obj.scope, obj[o]);
+ }
+ }
+ }
+ },
+ imgDiv: {className: ''}
+ }
+
+ g_registered = {};
+ g_events = [];
+
+ layer.addTileMonitoringHooks(tile);
+
+ //loadstart
+ t.ok(tile.onLoadStart != null, "onLoadStart function created and added to tile");
+ entry = g_registered["loadstart"];
+ t.ok( entry && entry[0] == layer && entry[1] == tile.onLoadStart, "loadstart correctly registered");
+
+ layer.numLoadingTiles = 0;
+ g_events = [];
+ tile.onLoadStart.apply(layer);
+
+ t.eq(g_events[0][0], "loadstart", "loadstart event triggered when numLoadingTiles is 0");
+ t.eq(layer.numLoadingTiles, 1, "numLoadingTiles incremented");
+ t.eq(g_events[1][0], "tileloadstart", "tileloadstart event triggered");
+
+ g_events = [];
+ tile.onLoadStart.apply(layer);
+ t.eq(g_events.length, 1, "tileloadstart, but not loadstart triggered when numLoadingTiles is not 0");
+ t.eq(layer.numLoadingTiles, 2, "numLoadingTiles incremented");
+
+
+ //loadend
+ t.ok(tile.onLoadEnd != null, "onLoadEnd function created and added to tile");
+ entry = g_registered["loadend"];
+ t.ok( entry && entry[0] == layer && entry[1] == tile.onLoadEnd, "loadend correctly registered");
+
+ g_events = [];
+ tile.onLoadError.apply(layer);
+ t.eq(g_events[0][0], "tileerror", "tileerror triggered");
+ t.ok(g_events[0][1].tile === tile, "tile passed as tile property to event object");
+
+ layer.numLoadingTiles = 2;
+ g_events = [];
+ tile.onLoadEnd.apply(layer, [{}]);
+ t.eq(g_events[0][0], "tileloaded", "tileloaded triggered when numLoadingTiles is > 0");
+ t.ok(g_events[0][1].tile === tile, "tile passed as tile property to event object");
+ t.eq(g_events.length, 1, "loadend event not triggered when numLoadingTiles is > 0");
+ t.eq(layer.numLoadingTiles, 1, "numLoadingTiles decremented");
+
+
+ g_events = [];
+ layer.grid = [[{}]]; // to prevent error in updateBackBuffer
+ tile.onLoadEnd.apply(layer, [{}]);
+ t.eq(g_events[0][0], "tileloaded", "tileloaded triggered when numLoadingTiles is 0");
+ t.eq(g_events[1][0], "loadend", "loadend event triggered when numLoadingTiles is 0");
+ t.eq(layer.numLoadingTiles, 0, "numLoadingTiles decremented");
+ }
+
+ function test_Layer_Grid_removeTileMonitoringHooks(t) {
+ t.plan(2);
+
+ layer = new OpenLayers.Layer.Grid();
+
+ var tile = {
+ onLoadStart: {},
+ onLoadEnd: {},
+ unload: function() {},
+ events: {
+ unregister: function(name, obj, func) {
+ g_unregistered[name] = [obj, func];
+ },
+ un: OpenLayers.Events.prototype.un
+ }
+ }
+
+ g_unregistered = {};
+
+ layer.removeTileMonitoringHooks(tile);
+
+ entry = g_unregistered["loadstart"];
+ t.ok( entry && entry[0] == layer && entry[1] == tile.onLoadStart, "loadstart correctly unregistered");
+
+ entry = g_unregistered["loadend"];
+ t.ok( entry && entry[0] == layer && entry[1] == tile.onLoadEnd, "loadend correctly unregistered");
+ }
+
+ function test_Layer_Grid_tileSizeIsInteger(t) {
+ t.plan(1);
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.Grid(name, url, params, {
+ singleTile: true,
+ ratio: 1.5
+ });
+ map.addLayers([layer]);
+
+ width = layer.tileSize.w;
+ height = layer.tileSize.h;
+ t.ok(width == parseInt(width) && height == parseInt(height), "calculated tileSize width/height are integer values");
+ }
+ function test_Layer_Grid_getTileBounds(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map2", {zoomMethod: null});
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.WMS(name, url, params);
+
+ var newParams = { layers: 'sooper',
+ chickpeas: 'image/png'};
+
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ map.zoomIn();
+ var bounds = layer.getTileBounds(new OpenLayers.Pixel(200,200));
+ t.eq(bounds.toBBOX(), "-180,-90,0,90", "get tile bounds returns correct bounds");
+ map.pan(200,0, {animate:false});
+ var bounds = layer.getTileBounds(new OpenLayers.Pixel(200,200));
+ t.eq(bounds.toBBOX(), "0,-90,180,90", "get tile bounds returns correct bounds after pan");
+ }
+
+ function test_Layer_Grid_moveTo_buffer_calculation (t) {
+ t.plan(6);
+
+ var map = new OpenLayers.Map( 'map3' ); // odd map size
+ var layer0 = new OpenLayers.Layer.WMS( "0 buffer: OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'}, {'buffer':0} );
+ map.addLayer(layer0);
+
+ var layer1 = new OpenLayers.Layer.WMS( "1 buffer: OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'}, {'buffer':1} );
+ map.addLayer(layer1);
+
+ var layer2 = new OpenLayers.Layer.WMS( "2 buffer: OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'}, {'buffer':2} );
+ map.addLayer(layer2);
+
+ map.setCenter(new OpenLayers.LonLat(0, 0), 4);
+ t.eq( layer0.grid.length, 3, "Grid rows with buffer:0" );
+ map.setBaseLayer(layer1);
+ t.eq( layer1.grid.length, 5, "Grid rows with buffer:1" );
+ map.setBaseLayer(layer2);
+ t.eq( layer2.grid.length, 7, "Grid rows with buffer:2" );
+
+ // zooming in on Greenland exercises the bug from pre-r4313
+ map.setCenter(new OpenLayers.LonLat(0, 90), 4);
+ t.eq( layer0.grid.length, 3, "Grid rows with buffer:0" );
+ map.setBaseLayer(layer1);
+ t.eq( layer1.grid.length, 5, "Grid rows with buffer:1" );
+ map.setBaseLayer(layer2);
+ t.eq( layer2.grid.length, 7, "Grid rows with buffer:2" );
+ }
+
+ function test_Layer_Grid_destroy (t) {
+
+ t.plan( 9 );
+
+ var map = new OpenLayers.Map('map', {tileManager: null});
+ layer = new OpenLayers.Layer.Grid(name, url, params);
+ map.addLayer(layer);
+ layer.destroy();
+ t.eq( layer.grid, null, "layer.grid is null after destroy" );
+ t.eq( layer.tileSize, null, "layer.tileSize is null after destroy" );
+
+
+ //test with tile creation
+ layer = new OpenLayers.Layer.WMS(name, url, params);
+ map.addLayer(layer);
+
+ map.setCenter(new OpenLayers.LonLat(0,0), 10);
+ map.setCenter(new OpenLayers.LonLat(1,1));
+
+ //grab a reference to one of the tiles
+ var tile = layer.grid[1][1];
+ t.eq( tile.imgDiv.className, "olTileImage", "Tile has an image" );
+
+ var removeBackBufferCalled = false;
+ layer.removeBackBuffer = function() {
+ removeBackBufferCalled = true;
+ };
+
+ layer.destroy();
+ t.eq( tile.imgDiv, null, "Tile destroyed" );
+ t.eq( layer.timerId, null, "Tile loading timeout cleared");
+ t.ok( layer.grid == null, "tiles appropriately destroyed")
+ t.ok( removeBackBufferCalled, "destroy calls removeBackBuffer");
+
+ // destroy after remove from map
+ layer = new OpenLayers.Layer.WMS(name, url, params);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 10);
+ map.removeLayer(layer);
+ layer.destroy();
+ t.eq( layer.grid, null, "layer.grid is null after destroy" );
+ t.eq( layer.tileSize, null, "layer.tileSize is null after destroy" );
+ }
+
+ function test_setOpacity(t) {
+ t.plan(5);
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS('', '', {}, {
+ isBaseLayer: true,
+ opacity: '0.6'
+ });
+ map.addLayer(layer);
+ // setCenter adds tiles to the layer's grid
+ map.setCenter(new OpenLayers.LonLat(0, 0), 5);
+
+ var tile = layer.grid[0][0], tileImg = tile.imgDiv;
+
+ tile.onImageLoad(); // simulate an image load event
+ t.eq(layer.opacity, '0.6', 'layer opacity value is correct');
+ t.eq(parseFloat(tileImg.style.opacity), 0.6, 'tile opacity is correct');
+
+ layer.setOpacity('0.2');
+ t.eq(layer.opacity, '0.2', 'layer opacity value is correct');
+ t.eq(parseFloat(tileImg.style.opacity), 0.2, 'tile opacity is correct');
+
+ tile = layer.addTile(new OpenLayers.Bounds(1, 2, 3, 4),
+ new OpenLayers.Pixel(5, 6));
+ tile.draw(); // add tile to the grid
+ tile.onImageLoad(); // simulate an image load event
+ t.eq(parseFloat(tile.imgDiv.style.opacity), 0.2, "tile opacity is correc");
+
+ map.destroy();
+ }
+
+ function test_getServerResolution(t) {
+
+ t.plan(4);
+
+ var layer = new OpenLayers.Layer.Grid('', '', {}, {});
+ var res;
+
+ res = layer.getServerResolution(1);
+ t.eq(res, 1, '[1] getServerResolution return value is correct');
+
+ layer.serverResolutions = [2, 1];
+ res = layer.getServerResolution(1);
+ t.eq(res, 1, '[2] getServerResolution return value is correct');
+
+ layer.serverResolutions = [2];
+ res = layer.getServerResolution(1);
+ t.eq(res, 2, '[3] getServerResolution return value is correct');
+
+ var exc;
+ layer.serverResolutions = [0.5];
+ res = layer.getServerResolution(1);
+ t.eq(res, 0.5, '[4] getServerResolution return value is correct');
+ }
+
+ function test_getServerZoom(t) {
+
+ t.plan(5);
+
+ var resolution, zoom;
+ var map = new OpenLayers.Map('map', {
+ resolutions: [8, 4, 2, 1, 0.5],
+ getResolution: function() {
+ return resolution;
+ }
+ });
+ var layer = new OpenLayers.Layer.WMS('', '', {}, {isBaseLayer: true});
+ map.addLayer(layer);
+
+ resolution = 8;
+ zoom = layer.getServerZoom();
+ t.eq(zoom, 0, '[1] getServerZoom return value is correct');
+
+ resolution = 4;
+ zoom = layer.getServerZoom();
+ t.eq(zoom, 1, '[2] getServerZoom return value is correct');
+
+ layer.serverResolutions = [2, 1];
+ resolution = 1;
+ zoom = layer.getServerZoom();
+ t.eq(zoom, 1, '[3] getServerZoom return value is correct');
+
+ layer.serverResolutions = [2];
+ resolution = 0.5;
+ zoom = layer.getServerZoom();
+ t.eq(zoom, 0, '[4] getServerZoom return value is correct');
+
+ var exc;
+ layer.serverResolutions = [0.5];
+ resolution = 1;
+ zoom = layer.getServerZoom();
+ t.eq(zoom, 0, '[4] getServerZoom return value is correct');
+
+ map.destroy();
+ }
+
+ function test_moveTo_scale(t) {
+
+ t.plan(11);
+
+ var map = new OpenLayers.Map('map', {
+ resolutions: [32, 16, 8, 4, 2, 1],
+ zoomMethod: null
+ });
+ var layer = new OpenLayers.Layer.WMS('', '', {}, {
+ isBaseLayer: true,
+ serverResolutions: [32, 16, 8]
+ });
+ map.addLayer(layer);
+
+ // initial resolution is 8
+ map.setCenter(new OpenLayers.LonLat(0, 0), 2);
+
+ // test initial conditions
+ t.eq(parseInt(layer.div.lastChild.style.width) / layer.tileSize.w, 1, 'layer div scale is 1');
+
+ // change from resolution 8 to 4
+ map.zoomTo(3);
+ t.eq(parseInt(layer.div.lastChild.style.width) / layer.tileSize.w, 2, '[8->4] layer div scale is 2');
+
+ // change from resolution 8 to 2
+ map.zoomTo(2); map.zoomTo(4);
+ t.eq(parseInt(layer.div.lastChild.style.width) / layer.tileSize.w, 4, '[8->2] layer div scale is 4');
+
+ // change from resolution 8 to 1
+ map.zoomTo(2); map.zoomTo(5);
+ t.eq(parseInt(layer.div.lastChild.style.width) / layer.tileSize.w, 8, '[8->1] layer div scale is 8');
+
+ // change from resolution 4 to 2
+ map.zoomTo(3); map.zoomTo(4);
+ t.eq(parseInt(layer.div.lastChild.style.width) / layer.tileSize.w, 4, '[4->2] layer div scale is 4');
+
+ // change from resolution 4 to 1
+ map.zoomTo(3); map.zoomTo(5);
+ t.eq(parseInt(layer.div.lastChild.style.width) / layer.tileSize.w, 8, '[4->1] layer div scale is 8');
+
+ // change from resolution 2 to 1
+ map.zoomTo(4); map.zoomTo(5);
+ t.eq(parseInt(layer.div.lastChild.style.width) / layer.tileSize.w, 8, '[2->1] layer div scale is 8');
+
+ // change from resolution 1 to 2
+ map.zoomTo(5); map.zoomTo(4);
+ t.eq(parseInt(layer.div.lastChild.style.width) / layer.tileSize.w, 4, '[1->2] layer div scale is 4');
+
+ // change from resolution 1 to 4
+ map.zoomTo(5); map.zoomTo(3);
+ t.eq(parseInt(layer.div.lastChild.style.width) / layer.tileSize.w, 2, '[1->4] layer div scale is 2');
+
+ // change from resolution 1 to 8
+ map.zoomTo(5); map.zoomTo(2);
+ t.eq(parseInt(layer.div.lastChild.style.width) / layer.tileSize.w, 1, '[1->8] layer div scale is 1');
+
+ // change from resolution 1 to 16
+ map.zoomTo(5); map.zoomTo(1);
+ t.eq(parseInt(layer.div.lastChild.style.width) / layer.tileSize.w, 1, '[1->16] layer div scale is 1');
+
+ map.destroy();
+ }
+
+ function test_moveTo_backbuffer_singletile(t) {
+ t.plan(4);
+
+ var map = new OpenLayers.Map('map', {
+ resolutions: [1, 0.5, 0.025],
+ zoomMethod: null
+ });
+ var resolution;
+ var layer = new OpenLayers.Layer.WMS('', '', {}, {
+ singleTile: true,
+ isBaseLayer: true,
+ transitionEffect: 'resize',
+ applyBackBuffer: function(res) {
+ resolution = res;
+ }
+ });
+ map.addLayer(layer);
+
+ // initial resolution is 0.025
+ resolution = undefined;
+ map.setCenter(new OpenLayers.LonLat(0, 0), 2);
+ t.eq(resolution, 0.025,
+ 'applyBackBuffer not called on first moveTo');
+
+ // move to (-90, 45)
+ resolution = undefined;
+ map.setCenter(new OpenLayers.LonLat(-90, 45));
+ t.eq(resolution, 0.025,
+ 'applyBackBuffer called when map is moved');
+
+ // change to resolution 1
+ resolution = undefined;
+ map.zoomTo(0);
+ t.eq(resolution, 1,
+ 'applyBackBuffer called when map is zoomed out');
+
+ // change to resolution 0.5
+ resolution = undefined;
+ map.zoomTo(1);
+ t.eq(resolution, 0.5,
+ 'applyBackBuffer called when map is zoomed out');
+
+ map.destroy();
+ }
+
+ function test_moveTo_backbuffer(t) {
+ t.plan(4);
+
+ var map = new OpenLayers.Map('map', {
+ resolutions: [1, 0.5, 0.025],
+ zoomMethod: null
+ });
+ var resolution;
+ var layer = new OpenLayers.Layer.WMS('', '', {}, {
+ isBaseLayer: true,
+ transitionEffect: 'resize',
+ applyBackBuffer: function(res) {
+ resolution = res;
+ }
+ });
+ map.addLayer(layer);
+
+ // initial resolution is 0.025
+ resolution = undefined;
+ map.setCenter(new OpenLayers.LonLat(0, 0), 2);
+ t.eq(resolution, 0.025,
+ 'applyBackBuffer not called on first moveTo');
+
+ // move to (-90, 45)
+ resolution = undefined;
+ map.setCenter(new OpenLayers.LonLat(-90, 45));
+ t.eq(resolution, undefined,
+ 'applyBackBuffer not called when map is moved');
+
+ // change to resolution 1
+ resolution = undefined;
+ map.zoomTo(0);
+ t.eq(resolution, 1,
+ 'applyBackBuffer called when map is zoomed out');
+
+ // change to resolution 0.5
+ map.zoomTo(1);
+ t.eq(resolution, 0.5,
+ 'applyBackBuffer called when map is zoomed out');
+
+ map.destroy();
+ }
+
+ function test_applyBackBuffer(t) {
+ t.plan(12);
+
+ var map = new OpenLayers.Map('map2');
+ var layer = new OpenLayers.Layer.WMS('', '', {}, {
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ var backBuffer;
+
+ // test #1
+ layer.createBackBuffer = function() {
+ return;
+ };
+ layer.applyBackBuffer(2);
+ t.eq(layer.backBuffer, undefined,
+ 'back buffer not created if createBackBuffer returns undefined');
+
+ // test #2
+ layer.createBackBuffer = function() {
+ backBuffer = document.createElement('div');
+ return backBuffer;
+ };
+ layer.gridResolution = 32;
+ layer.backBufferResolution = 2;
+ layer.grid[0][0].bounds = new OpenLayers.Bounds(0, 1, 1, 0);
+ layer.applyBackBuffer(2);
+ t.ok(layer.backBuffer === backBuffer,
+ 'back buffer set in layer');
+ t.ok(map.layerContainerDiv.firstChild === backBuffer,
+ 'back buffer inserted as first child');
+ t.eq(layer.backBuffer.style.left, '250px',
+ 'back buffer has correct left');
+ t.eq(layer.backBuffer.style.top, '275px',
+ 'back buffer has correct top');
+
+ // test #3
+ layer.createBackBuffer = function() {
+ backBuffer = document.createElement('div');
+ return backBuffer;
+ };
+ layer.gridResolution = 32;
+ layer.backBufferResolution = 2;
+ layer.grid[0][0].bounds = new OpenLayers.Bounds(0, 1, 1, 0);
+ map.layerContainerOriginPx.x = 20;
+ map.layerContainerOriginPx.y = -20;
+ layer.applyBackBuffer(2);
+ t.ok(layer.backBuffer === backBuffer,
+ 'back buffer set in layer');
+ t.ok(map.layerContainerDiv.firstChild === backBuffer,
+ 'back buffer inserted as first child');
+ t.eq(layer.backBuffer.style.left, '230px',
+ 'back buffer has correct left');
+ t.eq(layer.backBuffer.style.top, '295px',
+ 'back buffer has correct top');
+
+ // test #4
+ // and a back buffer in the layer and do as if back buffer removal
+ // has been scheduled, and test that applyBackBuffer removes the
+ // back buffer and clears the timer
+ layer.createBackBuffer = function() {
+ return;
+ };
+ backBuffer = document.createElement('div');
+ map.layerContainerDiv.insertBefore(backBuffer, map.baseLayer.div);
+ layer.backBuffer = backBuffer;
+ layer.backBufferTimerId = 'fake';
+ layer.applyBackBuffer(2);
+ t.ok(backBuffer !== map.layerContainerDiv.firstChild,
+ 'back buffer is not first child of layer container div');
+ t.eq(layer.backBuffer, null,
+ 'back buffer not set in layer');
+ t.eq(layer.backBufferTimerId, null,
+ 'back buffer timer cleared');
+ map.destroy();
+ }
+
+ function test_createBackBuffer(t) {
+ t.plan(9);
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS('', '', {}, {
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ var createBackBuffer = OpenLayers.Tile.Image.prototype.createBackBuffer;
+
+ var backBuffer;
+
+ OpenLayers.Tile.Image.prototype.createBackBuffer = function() {
+ return;
+ };
+ backBuffer = layer.createBackBuffer();
+ t.ok(backBuffer != undefined,
+ 'createBackBuffer returns a back buffer');
+ t.eq(backBuffer.childNodes.length, 0,
+ 'returned back buffer has no child nodes');
+
+ OpenLayers.Tile.Image.prototype.createBackBuffer = function() {
+ return document.createElement('div');
+ };
+
+ layer.transitionEffect = 'map-resize';
+ backBuffer = layer.createBackBuffer();
+ t.ok(backBuffer.style.zIndex == 99, 'z-index of backbuffer correct for "map-resize".');
+ layer.removeBackBuffer();
+
+ layer.transitionEffect = 'resize';
+ backBuffer = layer.createBackBuffer();
+ t.ok(backBuffer.style.zIndex == layer.getZIndex() - 1, 'z-index of backbuffer correct for "resize",');
+
+ layer.backBufferResolution = 1;
+ layer.gridResolution = 1;
+ layer.backBuffer = backBuffer;
+ layer.div.appendChild(backBuffer);
+ layer.backBufferLonLat = {lon: 0, lat: 0};
+ layer.applyBackBuffer(1);
+ t.ok(backBuffer != undefined,
+ 'createBackBuffer returns a back buffer');
+ t.eq(backBuffer.childNodes[0].style.left, '0px',
+ 'first tile has correct left');
+ t.eq(backBuffer.childNodes[0].style.top, '0px',
+ 'first tile has correct top');
+ t.eq(backBuffer.childNodes[1].style.left, '256px',
+ 'second tile has correct left');
+ t.eq(backBuffer.childNodes[1].style.top, '0px',
+ 'second tile has correct top');
+
+ map.destroy();
+ OpenLayers.Tile.Image.prototype.createBackBuffer = createBackBuffer;
+ }
+
+ function test_removeBackBuffer(t) {
+ t.plan(3);
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS('', '', {}, {isBaseLayer: true});
+ map.addLayer(layer);
+
+ // add a fake back buffer
+ var backBuffer = document.createElement('div');
+ layer.backBuffer = backBuffer;
+ layer.div.appendChild(backBuffer);
+ layer.backBufferResolution = 32;
+
+ layer.removeBackBuffer();
+ t.eq(layer.backBuffer, null, 'backBuffer set to null in layer');
+ t.eq(layer.backBufferResolution, null,
+ 'backBufferResolution set to null in layer');
+ t.ok(backBuffer.parentNode !== layer.div,
+ 'back buffer removed from layer');
+
+ map.destroy();
+ }
+
+ function test_backbuffer_replace(t) {
+ t.plan(6);
+ var map = new OpenLayers.Map('map', {tileManager: null});
+ var layer = new OpenLayers.Layer.WMS('', '../../img/blank.gif');
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ layer.grid[1][1].onImageLoad();
+ layer.mergeNewParams({foo: 'bar'});
+ var tile = layer.grid[1][1];
+ t.ok(OpenLayers.Element.hasClass(tile.imgDiv, 'olTileReplacing'), 'tile is marked for being replaced');
+ t.ok(document.getElementById(tile.id + '_bb'), 'backbuffer created for tile');
+ // simulate a css declaration where '.olTileReplacing' sets display
+ // to none.
+ tile.imgDiv.style.display = 'none';
+ tile.onImageLoad();
+ t.ok(!OpenLayers.Element.hasClass(tile.imgDiv, 'olTileReplacing'), 'tile replaced, no longer marked');
+ t.ok(!document.getElementById(tile.id + '_bb'), 'backbuffer removed for tile');
+
+ layer.mergeNewParams({foo: 'baz'});
+ tile = layer.grid[1][1];
+ // simulate a css declaration where '.olTileReplacing' does not set
+ // display to none.
+ tile.imgDiv.style.display = 'block';
+ tile.onImageLoad();
+ t.ok(!OpenLayers.Element.hasClass(tile.imgDiv, 'olTileReplacing'), 'tile replaced, no longer marked');
+ t.ok(document.getElementById(tile.id + '_bb'), 'backbuffer not removed for visible tile');
+ }
+
+ function test_backbuffer_replace_singleTile(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS('', '../../img/blank.gif', null, {
+ singleTile: true,
+ transitionEffect: 'resize'
+ });
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ t.delay_call(1, function() {
+ map.zoomIn();
+ var tile = layer.grid[0][0];
+ t.ok(!OpenLayers.Element.hasClass(tile.imgDiv, 'olTileReplacing'), 'tile is not marked for being replaced for singleTile layers');
+ });
+ }
+
+ function test_singleTile_move_and_zoom(t) {
+
+ //
+ // In single tile mode with no transition effect, we insert a non-scaled
+ // backbuffer when the layer is moved. But if a zoom occurs right after
+ // a move, i.e. before the new image is received, we need to remove the
+ // backbuffer, or an ill-positioned image will be visible during the
+ // zoom transition.
+ //
+
+ t.plan(4);
+
+ var map = new OpenLayers.Map('map', {zoomMethod: null});
+ var layer = new OpenLayers.Layer.WMS('', '', {}, {
+ isBaseLayer: true,
+ transitionEffect: null,
+ singleTile: true,
+ ratio: 1.1
+ });
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ // move
+ map.setCenter(new OpenLayers.LonLat(50, 50));
+ t.ok(layer.backBuffer && layer.backBuffer.parentNode === layer.div,
+ 'backbuffer inserted after map move');
+ t.eq(layer.backBuffer.style.left, '-25px');
+ t.eq(layer.backBuffer.style.top, '-28px');
+ // zoom
+ map.zoomTo(1);
+ t.eq(layer.backBuffer, null,
+ 'back buffer removed when zooming');
+
+ map.destroy();
+ }
+
+ function test_backbuffer_scaled_layer(t) {
+ t.plan(12);
+
+ //
+ // set up
+ //
+
+ var map = new OpenLayers.Map('map', {
+ resolutions: [32, 16, 8, 4, 2, 1],
+ zoomMethod: null,
+ tileManager: null
+ });
+ var layer = new OpenLayers.Layer.WMS(
+ "WMS",
+ window.location.href + "#",
+ null,
+ {transitionEffect: "resize"}
+ );
+
+ layer.serverResolutions = [32, 16, 8];
+
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 2);
+
+ var origCreateBackBuffer = OpenLayers.Tile.Image.prototype.createBackBuffer;
+ OpenLayers.Tile.Image.prototype.createBackBuffer = function() {
+ return document.createElement('div');
+ };
+
+ // we want to control when the back buffer is removed
+ var removeBackBuffer = OpenLayers.Function.bind(
+ layer.removeBackBuffer, layer);
+ layer.removeBackBuffer = function() {};
+
+ //
+ // test
+ //
+
+ // change resolution from 8 to 4
+ map.zoomTo(3);
+ t.eq(parseInt(layer.backBuffer.firstChild.style.width) / parseInt(layer.div.lastChild.style.width), 1,
+ '[8->4] back buffer not scaled');
+ removeBackBuffer();
+
+ // change resolution from 8 to 2
+ map.zoomTo(2); removeBackBuffer(); map.zoomTo(4);
+ t.eq(parseInt(layer.backBuffer.firstChild.style.width) / parseInt(layer.div.lastChild.style.width), 1,
+ '[8->2] back buffer not scaled');
+ removeBackBuffer();
+
+ // change resolution from 16 to 4
+ map.zoomTo(1); removeBackBuffer(); map.zoomTo(3);
+ t.eq(parseInt(layer.backBuffer.firstChild.style.width) / parseInt(layer.div.lastChild.style.width), 2,
+ '[16->4] back buffer width is as expected');
+ t.eq(parseInt(layer.backBuffer.firstChild.style.height) / parseInt(layer.div.lastChild.style.height), 2,
+ '[16->4] back buffer height is as expected');
+ removeBackBuffer();
+
+ // change resolution from 32 to 1
+ map.zoomTo(0); removeBackBuffer(); map.zoomTo(5);
+ t.eq(parseInt(layer.backBuffer.firstChild.style.width) / parseInt(layer.div.lastChild.style.width), 4,
+ '[32->1] back buffer width is as expected');
+ t.eq(parseInt(layer.backBuffer.firstChild.style.height) / parseInt(layer.div.lastChild.style.height), 4,
+ '[32->1] back buffer height is as expected');
+ removeBackBuffer();
+
+ // change resolution from 4 to 2
+ map.zoomTo(3); removeBackBuffer(); map.zoomTo(4);
+ t.eq(parseInt(layer.backBuffer.firstChild.style.width) / parseInt(layer.div.lastChild.style.width), 1,
+ '[4->2] back buffer not scaled');
+ removeBackBuffer();
+
+ // change resolution from 4 to 1
+ map.zoomTo(3); removeBackBuffer(); map.zoomTo(5);
+ t.eq(parseInt(layer.backBuffer.firstChild.style.width) / parseInt(layer.div.lastChild.style.width), 1,
+ '[4->1] back buffer not scaled');
+ removeBackBuffer();
+
+ // change resolution from 1 to 4
+ map.zoomTo(5); removeBackBuffer(); map.zoomTo(3);
+ t.eq(parseInt(layer.backBuffer.firstChild.style.width) / parseInt(layer.div.lastChild.style.width), 1,
+ '[1->4] back buffer not scaled');
+ removeBackBuffer();
+
+ // change resolution from 4 to 8
+ map.zoomTo(3); removeBackBuffer(); map.zoomTo(2);
+ t.eq(parseInt(layer.backBuffer.firstChild.style.width) / parseInt(layer.div.lastChild.style.width), 1,
+ '[4->8] back buffer not scaled');
+ removeBackBuffer();
+
+ // change resolution from 4 to 16
+ map.zoomTo(3); removeBackBuffer(); map.zoomTo(1);
+ t.eq(parseInt(layer.backBuffer.firstChild.style.width) / parseInt(layer.div.lastChild.style.width), 0.5,
+ '[4->16] back buffer width is as expected');
+ t.eq(parseInt(layer.backBuffer.firstChild.style.height) / parseInt(layer.div.lastChild.style.height), 0.5,
+ '[4->16] back buffer height is as expected');
+ removeBackBuffer();
+
+ //
+ // tear down
+ //
+
+ map.destroy();
+ OpenLayers.Tile.Image.prototype.createBackBuffer = origCreateBackBuffer
+ }
+
+
+ function test_delayed_back_buffer_removal(t) {
+ //
+ // Test that the delaying of the back buffer removal behaves
+ // as expected.
+ //
+
+ t.plan(5);
+
+ // set up
+
+ var map = new OpenLayers.Map('map', {
+ resolutions: [32, 16, 8, 4, 2, 1],
+ zoomMethod: null
+ });
+ var layer = new OpenLayers.Layer.WMS('', '', {}, {
+ isBaseLayer: true,
+ transitionEffect: 'resize'
+ });
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ map.zoomTo(1);
+
+ t.ok(layer.backBuffer === map.layerContainerDiv.firstChild,
+ '[a] back buffer is first child of layer container div');
+
+ // Mark one tile loaded and add an element to the backbuffer, to see if
+ // backbuffer removal gets scheduled.
+ layer.backBuffer.appendChild(document.createElement('img'));
+ layer.grid[1][1].onImageLoad();
+
+ t.ok(layer.backBufferTimerId !== null,
+ '[a] back buffer scheduled for removal');
+
+ var backBuffer = layer.backBuffer;
+
+ map.zoomTo(2);
+
+ t.ok(layer.backBuffer !== backBuffer,
+ '[b] a new back buffer was created');
+ t.ok(layer.backBuffer === map.layerContainerDiv.firstChild,
+ '[b] back buffer is first child of layer container div');
+ t.ok(layer.backBufferTimerId === null,
+ '[b] back buffer no longer scheduled for removal');
+
+ // tear down
+
+ map.destroy();
+ }
+
+ function test_getGridData(t) {
+ t.plan(12);
+
+ var layer = new OpenLayers.Layer.Grid(null, null, null, {
+ isBaseLayer: true, getURL: function() {
+ return "/bogus/path/to/tile";
+ }
+ });
+ var map = new OpenLayers.Map({
+ div: "map",
+ layers: [layer],
+ controls: [],
+ center: [0, 0],
+ zoom: 1
+ });
+
+ // get tile data for [0, 0]
+ var data = layer.getTileData({lon: 0, lat: 0});
+ t.ok(data && data.tile, "[0, 0]: got tile data");
+ t.eq(data.i, 0, "[0, 0]: i");
+ t.eq(data.j, 128, "[0, 0]: j");
+ t.ok(
+ data.tile.bounds.equals({left: 0, bottom: -90, right: 180, top: 90}),
+ "[0, 0]: tile bounds " + data.tile.bounds.toString()
+ );
+
+ // get tile data for [-110, 45]
+ data = layer.getTileData({lon: -110, lat: 45});
+ t.ok(data && data.tile, "[-110, 45]: got tile data");
+ t.eq(data.i, 99, "[-110, 45]: i");
+ t.eq(data.j, 64, "[-110, 45]: j");
+ t.ok(
+ data.tile.bounds.equals({left: -180, bottom: -90, right: 0, top: 90}),
+ "[-110, 45]: tile bounds " + data.tile.bounds.toString()
+ );
+
+ // get tile data for [0, 300] (north of grid)
+ data = layer.getTileData({lon: 0, lat: 300})
+ t.eq(data, null, "[0, 300]: north of grid");
+
+ // get tile data for [400, 0] (east of grid)
+ data = layer.getTileData({lon: 400, lat: 0})
+ t.eq(data, null, "[400, 0]: east of grid");
+
+ // get tile data for [0, -500] (south of grid)
+ data = layer.getTileData({lon: 0, lat: -500})
+ t.eq(data, null, "[0, -500]: south of grid");
+
+ // get tile data for [-200, 0] (west of grid)
+ data = layer.getTileData({lon: -200, lat: 0})
+ t.eq(data, null, "[-200, 0]: west of grid");
+
+ map.destroy();
+
+ }
+
+ function test_getGridData_wrapped(t) {
+ t.plan(18);
+
+ var layer = new OpenLayers.Layer.Grid(null, null, null, {
+ isBaseLayer: true, getURL: function() {
+ return "/bogus/path/to/tile";
+ },
+ wrapDateLine: true
+ });
+ var map = new OpenLayers.Map({
+ div: "map",
+ layers: [layer],
+ controls: [],
+ center: [-50, 0],
+ zoom: 1
+ });
+
+ // get tile data for [0, 0]
+ var data = layer.getTileData({lon: 0, lat: 0});
+ t.ok(data && data.tile, "[0, 0]: got tile data");
+ t.eq(data.i, 0, "[0, 0]: i");
+ t.eq(data.j, 128, "[0, 0]: j");
+ t.ok(
+ data.tile.bounds.equals({left: 0, bottom: -90, right: 180, top: 90}),
+ "[0, 0]: tile bounds " + data.tile.bounds.toString()
+ );
+
+ // get tile data for [-110, 45]
+ data = layer.getTileData({lon: -110, lat: 45});
+ t.ok(data && data.tile, "[-110, 45]: got tile data");
+ t.eq(data.i, 99, "[-110, 45]: i");
+ t.eq(data.j, 64, "[-110, 45]: j");
+ t.ok(
+ data.tile.bounds.equals({left: -180, bottom: -90, right: 0, top: 90}),
+ "[-110, 45]: tile bounds " + data.tile.bounds.toString()
+ );
+
+ // get tile data for [0, 300] (north of grid)
+ data = layer.getTileData({lon: 0, lat: 300})
+ t.eq(data, null, "[0, 300]: north of grid");
+
+ // get tile data for [400, 0] (equivalent to [40, 0] and visible on map)
+ data = layer.getTileData({lon: 400, lat: 0})
+ t.ok(data && data.tile, "[400, 0]: got tile data");
+ t.eq(data.i, 56, "[400, 0]: i");
+ t.eq(data.j, 128, "[400, 0]: j");
+ t.ok(
+ data.tile.bounds.equals({left: 0, bottom: -90, right: 180, top: 90}),
+ "[400, 0]: tile bounds " + data.tile.bounds.toString()
+ );
+
+ // get tile data for [0, -500] (south of grid)
+ data = layer.getTileData({lon: 0, lat: -500})
+ t.eq(data, null, "[0, -500]: south of grid");
+
+ // get tile data for [-200, 0] (equivalent to [160, 0] and wrapped to west side map)
+ data = layer.getTileData({lon: -200, lat: 0})
+ t.ok(data && data.tile, "[-200, 0]: got tile data");
+ t.eq(data.i, 227, "[-200, 0]: i");
+ t.eq(data.j, 128, "[-200, 0]: j");
+ t.ok(
+ data.tile.bounds.equals({left: 0, bottom: -90, right: 180, top: 90}),
+ "[-200, 0]: tile bounds " + data.tile.bounds.toString()
+ );
+
+ map.destroy();
+
+ }
+
+ function test_removeExcessTiles(t) {
+ t.plan(15);
+
+ /*
+ * Set up
+ */
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.Grid('name', '/url',
+ {}, {isBaseLayer: true});
+ map.addLayer(layer);
+
+ function newTile(id) {
+ var t = new OpenLayers.Tile(layer,
+ new OpenLayers.Pixel(1, 1),
+ new OpenLayers.Bounds(1, 1, 1, 1));
+ t._id = id;
+ return t;
+ }
+
+ layer.grid = [
+ [newTile(1), newTile(2), newTile(3)],
+ [newTile(4), newTile(5)],
+ [newTile(6), newTile(7), newTile(8)]
+ ];
+
+ // create a clone to be able to test whether
+ // tiles have been destroyed or not
+ var grid = [
+ layer.grid[0].slice(),
+ layer.grid[1].slice(),
+ layer.grid[2].slice()
+ ];
+
+ /*
+ * Test
+ */
+
+ layer.removeExcessTiles(2, 2);
+
+ t.eq(layer.grid.length, 2, 'grid has two rows');
+ t.eq(layer.grid[0].length, 2, 'row #1 has two columns');
+ t.eq(layer.grid[0][0]._id, 1, 'row #1 col #1 includes expected tile');
+ t.eq(layer.grid[0][1]._id, 2, 'row #1 col #2 includes expected tile');
+ t.eq(layer.grid[1].length, 2, 'row #2 has two columns');
+ t.eq(layer.grid[1][0]._id, 4, 'row #2 col #1 includes expected tile');
+ t.eq(layer.grid[1][1]._id, 5, 'row #2 col #2 includes expected tile');
+
+ t.ok(grid[0][0].events != null, 'tile 0,0 not destroyed');
+ t.ok(grid[0][1].events != null, 'tile 0,1 not destroyed');
+ t.ok(grid[0][2].events == null, 'tile 0,2 destroyed');
+ t.ok(grid[1][0].events != null, 'tile 1,0 not destroyed');
+ t.ok(grid[1][1].events != null, 'tile 1,1 not destroyed');
+ t.ok(grid[2][0].events == null, 'tile 2,0 destroyed');
+ t.ok(grid[2][1].events == null, 'tile 2,1 destroyed');
+ t.ok(grid[2][2].events == null, 'tile 2,2 destroyed');
+
+ /*
+ * Tear down
+ */
+
+ map.destroy();
+ }
+
+ function test_addOptions(t) {
+ t.plan(15);
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS(name, url, params, {buffer:2});
+ map.addLayer(layer);
+ t.eq(layer.tileSize, map.getTileSize(), "layer's tile size is equal to the map's tile size");
+ t.ok(layer.removeBackBufferDelay !== 0, "removeBackBufferDelay should not be 0 since we are not singleTile");
+ t.eq(layer.className, "olLayerGrid", "className correct for gridded mode");
+ map.setCenter(new OpenLayers.LonLat(0,0),5);
+ t.eq(layer.grid.length, 8, "Grid rows is correct.");
+ t.eq(layer.grid[0].length, 7, "Grid cols is correct.");
+ t.eq(layer.singleTile, false, "singleTile is false by default");
+ layer.addOptions({singleTile: true});
+ t.eq(layer.removeBackBufferDelay, 0, "removeBackBufferDelay set to 0 since singleTile is true");
+ t.eq(layer.singleTile, true, "singleTile set to true");
+ t.eq(layer.className, "olLayerGridSingleTile", "className correct for singleTile mode");
+ t.eq(layer.grid.length, 1, "Grid rows is correct.");
+ t.eq(layer.grid[0].length, 1, "Grid cols is correct.");
+ t.eq(layer.tileSize, new OpenLayers.Size(748, 823), "tile size changed");
+ layer.addOptions({singleTile: false});
+ t.eq(layer.grid.length, 8, "Grid rows is correct.");
+ t.eq(layer.grid[0].length, 7, "Grid cols is correct.");
+ t.eq(layer.tileSize, map.getTileSize(), "layer's tile size is equal to the map's tile size");
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:499px;height:549px;display:none"></div>
+<div id="map2" style="width:500px;height:550px;display:none"></div>
+<div id="map3" style="width:594px;height:464px;display:none"></div>
+<div id="map4" style="width:768px;height:512px;display:none"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/HTTPRequest.html b/misc/openlayers/tests/Layer/HTTPRequest.html
new file mode 100644
index 0000000..dcb6e23
--- /dev/null
+++ b/misc/openlayers/tests/Layer/HTTPRequest.html
@@ -0,0 +1,229 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var layer;
+
+ var name = "Test Layer";
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ var params = { map: '/mapdata/vmap_wms.map',
+ layers: 'basic',
+ format: 'image/png'};
+ var options = { chicken: 151, foo: "bar" };
+
+ function test_Layer_HTTPRequest_constructor (t) {
+ t.plan( 6 );
+
+ layer = new OpenLayers.Layer.HTTPRequest(name, url, params, options);
+
+ t.ok( layer instanceof OpenLayers.Layer.HTTPRequest, "new OpenLayers.Layer.HTTPRequest returns correctly typed object" );
+
+ // correct bubbling up to Layer.initialize()
+ t.eq( layer.name, name, "layer.name is correct" );
+ t.ok( ((layer.options["chicken"] == 151) && (layer.options["foo"] == "bar")), "layer.options correctly set" );
+
+ // HTTPRequest-specific properties
+ t.eq( layer.url, url, "layer.name is correct" );
+ t.ok( ((layer.params["map"] == '/mapdata/vmap_wms.map') &&
+ (layer.params["layers"] == "basic") &&
+ (layer.params["format"] == "image/png")), "layer.params correctly set" );
+
+ layer = new OpenLayers.Layer.HTTPRequest(name, url, null, {params: params});
+ t.ok( ((layer.params["map"] == '/mapdata/vmap_wms.map') &&
+ (layer.params["layers"] == "basic") &&
+ (layer.params["format"] == "image/png")), "layer.params correctly set from options" );
+ }
+
+ function test_Layer_HTTPRequest_clone (t) {
+ t.plan( 6 );
+
+ var toClone = new OpenLayers.Layer.HTTPRequest(name, url, params, options);
+ toClone.chocolate = 5;
+
+ var layer = toClone.clone();
+
+ t.eq(layer.chocolate, 5, "correctly copied randomly assigned property");
+
+ t.ok( layer instanceof OpenLayers.Layer.HTTPRequest, "new OpenLayers.Layer.HTTPRequest returns correctly typed object" );
+
+ // correct bubbling up to Layer.initialize()
+ t.eq( layer.name, name, "layer.name is correct" );
+ t.eq( layer.options, options, "layer.options correctly set" );
+
+ // HTTPRequest-specific properties
+ t.eq( layer.url, url, "layer.name is correct" );
+ t.ok( ((layer.params["map"] == '/mapdata/vmap_wms.map') &&
+ (layer.params["layers"] == "basic") &&
+ (layer.params["format"] == "image/png")), "layer.params correctly set" );
+
+ }
+
+ function test_Layer_HTTPRequest_setUrl (t) {
+ t.plan( 1 );
+
+ layer = new OpenLayers.Layer.HTTPRequest(name, url, params, options);
+
+ layer.setUrl("foo");
+ t.eq( layer.url, "foo", "setUrl() works");
+ }
+
+ function test_Layer_HTTPRequest_mergeNewParams (t) {
+ t.plan( 8 );
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.HTTPRequest(name, url, params, options);
+ map.addLayer(layer);
+
+ var scope = {some: "scope"}, log = [];
+ map.events.on({
+ changelayer: function(e) {
+ log.push({layer: e.layer, property: e.property, scope: this});
+ },
+ scope: scope
+ });
+
+ var newParams = { layers: 'sooper',
+ chickpeas: 'image/png'};
+
+ layer.mergeNewParams(newParams);
+
+ t.eq( layer.params.layers, "sooper", "mergeNewParams() overwrites well");
+ t.eq( layer.params.chickpeas, "image/png", "mergeNewParams() adds well");
+ t.eq( log.length, 1, "mergeNewParams() triggers changelayer once");
+ t.ok( log[0].layer == layer, "mergeNewParams() passes changelayer listener the expected layer");
+ t.ok( log[0].property == "params", "mergeNewParams() passes changelayer listener the property \"params\"");
+ t.eq( log[0].scope, scope, "mergeNewParams() executes changelayer listener with expected scope");
+
+ newParams.chickpeas = 151;
+
+ t.eq( layer.params.chickpeas, "image/png", "mergeNewParams() makes clean copy of hash");
+
+ layer.redraw = function() {
+ t.ok(true, "layer.mergeNewParams calls layer.redraw");
+ }
+ layer.mergeNewParams();
+ }
+
+ function test_Layer_HTTPRequest_getFullRequestString (t) {
+
+ tParams = { layers: 'basic',
+ format: 'image/png'};
+
+ t.plan( 12 );
+
+ // without ?
+ tUrl = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, tParams, null);
+ str = layer.getFullRequestString();
+ t.eq(str, tUrl + '?' + OpenLayers.Util.getParameterString(tParams), "getFullRequestString() works for url sans ?");
+
+
+ // with ?
+ tUrl = "http://octo.metacarta.com/cgi-bin/mapserv?";
+ layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, tParams, null);
+ str = layer.getFullRequestString();
+ t.eq(str, tUrl + OpenLayers.Util.getParameterString(tParams), "getFullRequestString() works for url with ?");
+
+ // with ?param1=5
+ tUrl = "http://octo.metacarta.com/cgi-bin/mapserv?param1=5";
+ layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, tParams, null);
+ str = layer.getFullRequestString();
+ t.eq(str, tUrl + '&' + OpenLayers.Util.getParameterString(tParams), "getFullRequestString() works for url with ?param1=5");
+
+ // with ?param1=5&
+ tUrl = "http://octo.metacarta.com/cgi-bin/mapserv?param1=5&format=image/jpeg";
+ layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, tParams, null);
+ str = layer.getFullRequestString();
+ t.eq(str, tUrl + '&' + OpenLayers.Util.getParameterString({'layers':'basic'}), "getFullRequestString() doesn't override already-existing params in URL");
+
+
+ // with ?param1=5&
+ tUrl = "http://octo.metacarta.com/cgi-bin/mapserv?param1=5&";
+ layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, tParams, null);
+ str = layer.getFullRequestString();
+ t.eq(str, tUrl + OpenLayers.Util.getParameterString(tParams), "getFullRequestString() works for url with ?param1=5&");
+
+
+
+ // passing in new params
+ layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, tParams, null);
+ str = layer.getFullRequestString( { chicken: 6,
+ layers:"road" } );
+ t.eq(str, tUrl + OpenLayers.Util.getParameterString({layers: 'road', format: "image/png", chicken: 6}), "getFullRequestString() works for passing in new params");
+
+ // layer with null params
+ layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, null, null);
+ str = layer.getFullRequestString();
+ t.eq(str, tUrl + OpenLayers.Util.getParameterString({}), "getFullRequestString() works for layer with null params");
+
+ // layer with null params passing in new params
+ layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, null, null);
+ str = layer.getFullRequestString( { chicken: 6,
+ layers:"road" } );
+ t.eq(str, tUrl + OpenLayers.Util.getParameterString({chicken: 6, layers: "road"}), "getFullRequestString() works for layer with null params passing in new params");
+
+ // with specified altUrl parameter
+ tUrl = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.HTTPRequest(name, "chicken", tParams, null);
+ str = layer.getFullRequestString(null, tUrl);
+ t.eq(str, tUrl + '?' + OpenLayers.Util.getParameterString(tParams), "getFullRequestString() works with specified altUrl parameter");
+
+ // single url object
+ tUrl = ["http://octo.metacarta.com/cgi-bin/mapserv"];
+ layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, tParams, null);
+ str = layer.getFullRequestString();
+ t.eq(str, tUrl[0] + '?' + OpenLayers.Util.getParameterString(tParams), "getFullRequestString() works for list of one url");
+
+ // two url object
+ tUrl = ["http://octo.metacarta.com/cgi-bin/mapserv","http://labs.metacarta.com/cgi-bin/mapserv"];
+ layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, tParams, null);
+ str = layer.getFullRequestString();
+ t.eq(str, tUrl[1] + '?' + OpenLayers.Util.getParameterString(tParams), "getFullRequestString() works for list of two urls");
+ str = layer.getFullRequestString({'a':'b'});
+ t.eq(str, tUrl[0] + '?' + OpenLayers.Util.getParameterString(OpenLayers.Util.extend(tParams,{'a':'b'})), "getFullRequestString() works for list of two urls and is deterministic");
+
+ }
+
+ function test_Layer_HTTPRequest_selectUrl (t) {
+ t.plan( 4 );
+
+ layer = new OpenLayers.Layer.HTTPRequest(name, url, params, options);
+
+ urls = ["wms1", "wms2", "wms3", "wms4"];
+ t.eq( layer.selectUrl("bbox=-180,0,0,90", urls), "wms3", "selectUrl(-90,-180) returns 4" );
+ t.eq( layer.selectUrl("bbox=-180,-90,0,0", urls), "wms1", "selectUrl(90,-180) returns 3" );
+ t.eq( layer.selectUrl("bbox=0,90,180,0", urls), "wms1", "selectUrl(-90,180) returns 1" );
+ t.eq( layer.selectUrl("bbox=0,0,180,90", urls), "wms4", "selectUrl(90,180) returns 2" );
+ }
+
+ function test_Layer_HTTPRequest_destroy (t) {
+ t.plan( 6 );
+
+ var map = new OpenLayers.Map('map');
+
+ layer = new OpenLayers.Layer.HTTPRequest("Test Layer",
+ "http://www.openlayers.org",
+ { foo: 2, bar: 3},
+ { opt1: 8, opt2: 9});
+
+ map.addLayer(layer);
+ layer.destroy();
+
+ // Ensure Layer.destroy() is called
+ t.eq( layer.name, null, "layer.name is null after destroy" );
+ t.eq( layer.div, null, "layer.div is null after destroy" );
+ t.eq( layer.map, null, "layer.map is null after destroy" );
+ t.eq( layer.options, null, "layer.options is null after destroy" );
+
+
+ // Specific to HTTPRequest
+ t.eq( layer.url, null, "layer.url is null after destroy" );
+ t.eq( layer.params, null, "layer.params is null after destroy" );
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/Image.html b/misc/openlayers/tests/Layer/Image.html
new file mode 100644
index 0000000..05ab5c3
--- /dev/null
+++ b/misc/openlayers/tests/Layer/Image.html
@@ -0,0 +1,164 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var layer;
+
+ function test_Layer_Image_constructor (t) {
+ t.plan( 13 );
+
+ var options = { chicken: 151, foo: "bar", projection: "EPSG:4326" };
+ var layer = new OpenLayers.Layer.Image('Test Layer',
+ 'http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif',
+ new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
+ new OpenLayers.Size(580, 288), options);
+
+ t.ok( layer instanceof OpenLayers.Layer.Image, "new OpenLayers.Layer.Image returns object" );
+ t.eq( layer.CLASS_NAME, "OpenLayers.Layer.Image", "CLASS_NAME variable set correctly");
+
+ t.eq( layer.name, "Test Layer", "layer.name is correct" );
+ t.ok( layer.id != null, "Layer is given an id");
+ t.eq( layer.projection.getCode(), "EPSG:4326", "default layer projection correctly set");
+ t.ok( ((layer.chicken == 151) && (layer.foo == "bar")), "layer.options correctly set to Layer Object" );
+ t.ok( ((layer.options["chicken"] == 151) && (layer.options["foo"] == "bar")), "layer.options correctly backed up" );
+
+ options.chicken = 552;
+
+ t.eq( layer.options["chicken"], 151 , "layer.options correctly made fresh copy" );
+
+ t.eq( layer.isBaseLayer, true, "Default img layer is base layer" );
+
+ layer = new OpenLayers.Layer.Image('Test Layer',
+ 'http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif',
+ new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
+ new OpenLayers.Size(580, 288));
+ t.ok( layer instanceof OpenLayers.Layer.Image, "new OpenLayers.Layer.Image returns object" );
+ t.eq( layer.name, "Test Layer", "layer.name is correct" );
+ t.ok( layer.projection == null, "default layer projection correctly set");
+ t.ok( layer.options instanceof Object, "layer.options correctly initialized as a non-null Object" );
+ }
+
+ function test_maxExtent(t) {
+ t.plan(2);
+ var layer;
+
+ // test that the image extent is set as the default maxExtent
+ var extent = new OpenLayers.Bounds(1, 2, 3, 4);
+ var size = new OpenLayers.Size(2, 2);
+ layer = new OpenLayers.Layer.Image("Test", "foo", extent, size);
+ t.eq(layer.maxExtent.toString(), extent.toString(), "extent set as default maxExtent");
+
+ // test that the maxExtent can be set explicitly
+ var maxExtent = new OpenLayers.Bounds(10, 20, 30, 40);
+ layer = new OpenLayers.Layer.Image("Test", "foo", extent, size, {maxExtent: maxExtent});
+ t.eq(layer.maxExtent.toString(), maxExtent.toString(), "maxExtent can be set explicitly");
+
+ }
+
+ function test_Layer_Image_tileTests (t) {
+ t.plan(7);
+ var map = new OpenLayers.Map('map');
+
+ layer = new OpenLayers.Layer.Image('Test Layer',
+ 'http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif',
+ new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
+ new OpenLayers.Size(580, 288));
+
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ // no resolution info was sent, so maxResolution should be calculated
+ // by aspectRatio*extent/size (this is the pixel aspect ratio)
+ var aspectRatio = (layer.extent.getHeight() / layer.size.h) /
+ (layer.extent.getWidth() / layer.size.w);
+ t.eq(aspectRatio, layer.aspectRatio, "aspectRatio is properly set");
+ var maxExtent = aspectRatio * layer.extent.getWidth() / layer.size.w;
+ t.eq(maxExtent, layer.maxResolution, "maxResolution is properly set");
+
+ t.eq(layer.tile.position.x,-42, "Tile x positioned correctly at maxextent");
+ t.eq(layer.tile.position.y,106, "Tile y positioned correctly at maxextent");
+ t.eq(layer.tile.url, "http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif", "URL is correct");
+ map.zoomIn();
+ t.eq(layer.tile.url, "http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif", "URL is correct");
+ layer.setUrl('http://labs.metacarta.com/wms/vmap0?LAYERS=basic&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&FORMAT=image%2Fjpeg&SRS=EPSG%3A4326&BBOX=-180,-90,0,90&WIDTH=256&HEIGHT=256');
+ t.eq(layer.tile.url, "http://labs.metacarta.com/wms/vmap0?LAYERS=basic&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&FORMAT=image%2Fjpeg&SRS=EPSG%3A4326&BBOX=-180,-90,0,90&WIDTH=256&HEIGHT=256", "URL is correct after setURL");
+ }
+/******
+ *
+ *
+ * HERE IS WHERE SOME TESTS SHOULD BE PUT TO CHECK ON THE LONLAT-PX TRANSLATION
+ * FUNCTIONS AND RESOLUTION AND GETEXTENT GETZOOMLEVEL, ETC
+ *
+ *
+ */
+
+
+ function test_Layer_Image_destroy_before_use (t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.Image('Test', 'http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif', new OpenLayers.Bounds(-180, -88.759, 180, 88.759), new OpenLayers.Size(580, 288));
+ map.addLayer(layer);
+ map.removeLayer(layer);
+ layer.destroy();
+ t.ok(true, "destroy() didn't throw an error");
+ }
+
+ function test_Layer_Image_destroy (t) {
+ t.plan( 4 );
+
+ var map = new OpenLayers.Map('map');
+
+ layer = new OpenLayers.Layer.Image('Test Layer',
+ 'http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif',
+ new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
+ new OpenLayers.Size(580, 288));
+
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ layer.destroy();
+
+ t.eq( layer.name, null, "layer.name is null after destroy" );
+ t.eq( layer.div, null, "layer.div is null after destroy" );
+ t.eq( layer.map, null, "layer.map is null after destroy" );
+ t.eq( layer.options, null, "layer.options is null after destroy" );
+
+ }
+
+ function test_loadEvents(t) {
+ t.plan(3);
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.Image(
+ 'Test', '../../img/blank.gif',
+ new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
+ new OpenLayers.Size(580, 288)
+ );
+
+ map.addLayer(layer);
+
+ layer.events.register('loadstart', null, function(obj) {
+ t.ok(obj.object.tile.isLoading, "loadstart triggered while tile is loading");
+ });
+
+ var delay = false;
+ layer.events.register('loadend', null, function(obj) {
+ delay = true;
+ });
+
+ t.delay_call(5, function() {
+ t.eq(delay, true, "registered for loadend");
+ t.eq(layer.tile.isLoading, false, "loadend triggered after tile is loaded");
+ map.destroy(); //tear down
+ return delay;
+ });
+ map.zoomToMaxExtent();
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width:500px;height:500px"></div>
+ <div id="map2" style="width:100px;height:100px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/KaMap.html b/misc/openlayers/tests/Layer/KaMap.html
new file mode 100644
index 0000000..a41e4eb
--- /dev/null
+++ b/misc/openlayers/tests/Layer/KaMap.html
@@ -0,0 +1,287 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var layer;
+
+ var name = 'Test Layer';
+ var url = "http://boston.freemap.in/tile.php?";
+ var params = {
+ 'map':'boston-new',
+ 'g':'border,water,roads,openspace',
+ 'i':'JPEG'
+ };
+ var units = "meters";
+
+
+
+ function test_Layer_KaMap_constructor (t) {
+ t.plan( 1 );
+
+ layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+ t.ok( layer instanceof OpenLayers.Layer.KaMap, "returns OpenLayers.Layer.KaMap object" );
+ }
+
+ function test_Layer_Grid_moveTo_buffer_calculation (t) {
+ t.plan(6);
+
+ var map = new OpenLayers.Map( 'map3' ); // odd map size
+ var layer0 = new OpenLayers.Layer.KaMap( "0 buffer: OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'}, {'buffer':0} );
+ map.addLayer(layer0);
+
+ var layer1 = new OpenLayers.Layer.KaMap( "1 buffer: OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'}, {'buffer':1} );
+ map.addLayer(layer1);
+
+ var layer2 = new OpenLayers.Layer.KaMap( "2 buffer: OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'}, {'buffer':2} );
+ map.addLayer(layer2);
+
+ map.setCenter(new OpenLayers.LonLat(0, 0), 4);
+ t.eq( layer0.grid.length, 3, "Grid rows with buffer:0" );
+ map.setBaseLayer(layer1);
+ t.eq( layer1.grid.length, 5, "Grid rows with buffer:1" );
+ map.setBaseLayer(layer2);
+ t.eq( layer2.grid.length, 7, "Grid rows with buffer:2" );
+
+ // zooming in on Greenland exercises the bug from pre-r4313
+ map.setCenter(new OpenLayers.LonLat(0, 90), 4);
+ t.eq( layer0.grid.length, 3, "Grid rows with buffer:0" );
+ map.setBaseLayer(layer1);
+ t.eq( layer1.grid.length, 5, "Grid rows with buffer:1" );
+ map.setBaseLayer(layer2);
+ t.eq( layer2.grid.length, 7, "Grid rows with buffer:2" );
+ map.destroy();
+ }
+
+ function test_Layer_KaMap_inittiles (t) {
+ t.plan( 2 );
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0),5);
+ t.eq( layer.grid.length, 4, "KaMap rows is correct." );
+ t.eq( layer.grid[0].length, 3, "KaMap cols is correct." );
+ map.destroy();
+
+ }
+
+ function test_Layer_KaMap_clearTiles (t) {
+ t.plan( 1 );
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+ map.addLayer(layer);
+
+ map.setCenter(new OpenLayers.LonLat(0,0));
+
+ //grab a reference to one of the tiles
+ var tile = layer.grid[0][0];
+
+ layer.clearGrid();
+
+ t.ok( layer.grid != null, "layer.grid does not get nullified" );
+ map.destroy();
+ }
+
+
+ function test_Layer_KaMap_getKaMapBounds(t) {
+ t.plan( 1 );
+
+ layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+
+ var bl = { bounds: new OpenLayers.Bounds(1,2,2,3)};
+ var tr = { bounds: new OpenLayers.Bounds(2,3,3,4)};
+ layer.grid = [ [6, tr],
+ [bl, 7]];
+
+ var bounds = layer.getTilesBounds();
+
+ var testBounds = new OpenLayers.Bounds(1,2,3,4);
+
+ t.ok( bounds.equals(testBounds), "getKaMapBounds() returns correct bounds")
+
+ layer.grid = null;
+ }
+
+ function test_Layer_KaMap_getResolution(t) {
+ t.plan( 1 );
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+ map.addLayer(layer);
+
+ map.zoom = 5;
+
+ t.eq( layer.getResolution(), 0.0439453125, "getResolution() returns correct value");
+ map.destroy();
+ }
+
+ function test_Layer_KaMap_getZoomForExtent(t) {
+ t.plan( 2 );
+ var bounds, zoom;
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+ map.addLayer(layer);
+
+ bounds = new OpenLayers.Bounds(10,10,12,12);
+ zoom = layer.getZoomForExtent(bounds);
+
+ t.eq( zoom, 8, "getZoomForExtent() returns correct value");
+
+ bounds = new OpenLayers.Bounds(10,10,100,100);
+ zoom = layer.getZoomForExtent(bounds);
+
+ t.eq( zoom, 2, "getZoomForExtent() returns correct value");
+ map.destroy();
+ }
+
+ function test_Layer_kaMap_mergeNewParams (t) {
+ t.plan( 4 );
+
+ var map = new OpenLayers.Map("map");
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.KaMap(name, url, params);
+
+ var newParams = { layers: 'sooper',
+ chickpeas: 'image/png'};
+
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ layer.redraw = function() {
+ t.ok(true, "layer is redrawn after new params merged");
+ }
+
+ layer.mergeNewParams(newParams);
+
+ t.eq( layer.params.layers, "sooper", "mergeNewParams() overwrites well");
+ t.eq( layer.params.chickpeas, "image/png", "mergeNewParams() adds well");
+
+ newParams.chickpeas = 151;
+
+ t.eq( layer.params.chickpeas, "image/png", "mergeNewParams() makes clean copy of hashtable");
+ map.destroy();
+ }
+
+
+ /** THIS WOULD BE WHERE THE TESTS WOULD GO FOR
+ *
+ * -moveTo
+ * -insertColumn
+ * -insertRow
+
+ function 07_Layer_KaMap_moveTo(t) {
+ }
+
+ function 08_Layer_KaMap_insertColumn(t) {
+ }
+
+ function 09_Layer_KaMap_insertRow(t) {
+ }
+
+ *
+ */
+
+ function test_Layer_KaMap_clone(t) {
+ t.plan(5);
+
+ var options = {tileSize: new OpenLayers.Size(500,50)};
+ var map = new OpenLayers.Map('map', options);
+ layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+ map.addLayer(layer);
+
+ layer.grid = [ [6, 7],
+ [8, 9]];
+
+ var clone = layer.clone();
+
+ t.ok( clone.grid != layer.grid, "clone does not copy grid");
+
+ t.ok( clone.tileSize.equals(layer.tileSize), "tileSize correctly cloned");
+
+ layer.tileSize.w += 40;
+
+ t.eq( clone.tileSize.w, 500, "changing layer.tileSize does not change clone.tileSize -- a fresh copy was made, not just copied reference");
+
+ t.eq( clone.alpha, layer.alpha, "alpha copied correctly");
+
+ t.eq( clone.CLASS_NAME, "OpenLayers.Layer.KaMap", "Clone is a ka-map layer");
+
+ layer.grid = null;
+ map.destroy();
+ }
+
+ function test_Layer_KaMap_setMap(t) {
+
+ t.plan(2);
+
+ var options = {tileSize: new OpenLayers.Size(500,50)};
+ var map = new OpenLayers.Map('map', options);
+ layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+
+
+ layer.setMap(map);
+
+ t.ok( layer.tileSize != null, "tileSize has been set");
+ t.ok( (layer.tileSize.h == 50) && (layer.tileSize.w == 500), "tileSize has been set correctly");
+ map.destroy();
+ }
+ function test_Layer_KaMap_getTileBounds(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map", {zoomMethod: null});
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.KaMap(name, url, params);
+
+ var newParams = { layers: 'sooper',
+ chickpeas: 'image/png'};
+
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ map.zoomIn();
+ var bounds = layer.getTileBounds(new OpenLayers.Pixel(200,200));
+ t.eq(bounds.toBBOX(), "-180,0,0,180", "get tile bounds returns correct bounds");
+ map.pan(200,0,{animate:false});
+ var bounds = layer.getTileBounds(new OpenLayers.Pixel(200,200));
+ t.eq(bounds.toBBOX(), "0,0,180,180", "get tile bounds returns correct bounds after pan");
+ map.destroy();
+ }
+
+ function test_Layer_KaMap_destroy (t) {
+
+ t.plan( 3 );
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+ map.addLayer(layer);
+ layer.destroy();
+ t.eq( layer.grid, null, "layer.grid is null after destroy" );
+ t.eq( layer.tileSize, null, "layer.tileSize is null after destroy" );
+
+
+ //test with tile creation
+ layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+ //grab a reference to one of the tiles
+ var tile = layer.grid[0][0];
+
+ layer.destroy();
+
+ t.ok( layer.grid == null, "tiles appropriately destroyed");
+ map.destroy();
+ }
+
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px;display:none"></div>
+<div id="map2" style="width:500px;height:550px;display:none"></div>
+<div id="map3" style="width:594px;height:464px;display:none"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/MapGuide.html b/misc/openlayers/tests/Layer/MapGuide.html
new file mode 100644
index 0000000..b1eb386
--- /dev/null
+++ b/misc/openlayers/tests/Layer/MapGuide.html
@@ -0,0 +1,177 @@
+<html>
+<head>
+ <script type="text/javascript">var oldAlert = window.alert, gMess; window.alert = function(message) {gMess = message; return true;};</script>
+ <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhR_wWLPFku8Ix9i2SXYRVK3e45q1BQUd_beF8dtzKET_EteAjPdGDwqpQ'></script>
+ <script type="text/javascript">window.alert = oldAlert;</script>
+<script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var layer;
+
+ var name = 'MapGuide Test Layer';
+ var url = "http://data.mapguide.com/mapguide/mapagent/mapagent.fcgi?USERNAME=Anonymous&";
+ var paramsTiled = {
+ mapdefinition: 'Library://Samples/Sheboygan/MapsTiled/Sheboygan.MapDefinition',
+ basemaplayergroupname: "Base Layer Group"
+ }
+ var paramsUntiled = {
+ mapdefinition: 'Library://Samples/Sheboygan/Maps/Sheboygan.MapDefinition'
+ };
+
+ function test_Layer_MapGuide_untiled_constructor (t) {
+ t.plan( 8 );
+
+ var trans_format = "image/png";
+ var options = {singleTile:true};
+ if (OpenLayers.Util.alphaHack()) { trans_format = "image/gif"; }
+
+ layer = new OpenLayers.Layer.MapGuide(name, url, paramsUntiled, options);
+ t.ok( layer instanceof OpenLayers.Layer.MapGuide, "new OpenLayers.Layer.MapGuide returns object" );
+ t.eq( layer.url, "http://data.mapguide.com/mapguide/mapagent/mapagent.fcgi?USERNAME=Anonymous&", "layer.url is correct (HTTPRequest inited)" );
+ t.eq( layer.params.mapdefinition, "Library://Samples/Sheboygan/Maps/Sheboygan.MapDefinition", "params passed in correctly" );
+
+ t.eq( layer.params.operation, "GETMAPIMAGE", "default params set correctly and copied");
+
+ t.eq(layer.isBaseLayer, true, "no transparency setting, layer is baselayer");
+
+ options.transparent = "true";
+ var layer2 = new OpenLayers.Layer.MapGuide(name, url, paramsUntiled, options);
+ t.eq(layer2.isBaseLayer, false, "transparency == 'true', layer is not baselayer");
+
+ options.transparent = true;
+ var layer5 = new OpenLayers.Layer.MapGuide(name, url, paramsUntiled, options);
+ t.eq(layer5.isBaseLayer, false, "transparency == true, layer is not baselayer");
+
+ options.transparent = false;
+ var layer6 = new OpenLayers.Layer.MapGuide(name, url, paramsUntiled, options);
+ t.eq(layer6.isBaseLayer, true, "transparency == false, layer is baselayer");
+ }
+
+ function test_Layer_MapGuide_tiled_constructor (t) {
+ t.plan( 5 );
+
+ var trans_format = "image/png";
+ var options = {singleTile:false};
+ if (OpenLayers.Util.alphaHack()) { trans_format = "image/gif"; }
+
+ layer = new OpenLayers.Layer.MapGuide(name, url, paramsTiled, options);
+ t.ok( layer instanceof OpenLayers.Layer.MapGuide, "new OpenLayers.Layer.MapGuide returns object" );
+ t.eq( layer.url, "http://data.mapguide.com/mapguide/mapagent/mapagent.fcgi?USERNAME=Anonymous&", "layer.url is correct (HTTPRequest inited)" );
+ t.eq( layer.params.basemaplayergroupname, "Base Layer Group", "params passed in correctly" );
+
+ t.eq( layer.params.operation, "GETTILEIMAGE", "default params correctly uppercased and copied");
+ t.eq( layer.params.version, "1.2.0", "version params set correctly set");
+ }
+
+ function test_Layer_MapGuide_inittiles (t) {
+ t.plan( 1 );
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.MapGuide(name, url, paramsTiled);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,400000),5);
+ t.eq( layer.grid.length, 3, "Grid rows is correct." );
+ // t.eq( layer.grid[0].length, 6, "Grid cols is correct." );
+ map.destroy();
+ }
+
+
+ function test_Layer_MapGuide_clone (t) {
+ t.plan(4);
+
+ var options = {tileSize: new OpenLayers.Size(500,50)};
+ var map = new OpenLayers.Map('map', options);
+ layer = new OpenLayers.Layer.MapGuide(name, url, paramsTiled);
+ map.addLayer(layer);
+
+ layer.grid = [ [6, 7],
+ [8, 9]];
+
+ var clone = layer.clone();
+
+ t.eq( layer.tileSize.w, 300, "layer.tileSize fixed to 300x300");
+ t.ok( clone.grid != layer.grid, "clone does not copy grid");
+
+ t.ok( clone.tileSize.equals(layer.tileSize), "tileSize correctly cloned");
+
+ layer.tileSize.w += 40;
+
+ t.eq( clone.alpha, layer.alpha, "alpha copied correctly");
+
+ layer.grid = null;
+ map.destroy();
+ }
+
+ function test_Layer_MapGuide_isBaseLayer(t) {
+ t.plan(3);
+
+ var options = {singleTile:true};
+ layer = new OpenLayers.Layer.MapGuide(name, url, paramsUntiled, options);
+ t.ok( layer.isBaseLayer, "baselayer is true by default");
+
+ var newParams = OpenLayers.Util.extend({}, paramsUntiled);
+ options.transparent = "true";
+ layer = new OpenLayers.Layer.MapGuide(name, url, newParams, options);
+ t.ok( !layer.isBaseLayer, "baselayer is false when transparent is set to true");
+
+ newParams = OpenLayers.Util.extend({}, paramsUntiled);
+ options.isBaseLayer = false;
+ layer = new OpenLayers.Layer.MapGuide(name, url, newParams, options);
+ t.ok( !layer.isBaseLayer, "baselayer is false when option is set to false" );
+ }
+
+ function test_Layer_MapGuide_mergeNewParams (t) {
+ t.plan( 4 );
+
+ var options = {singleTile:true};
+ var map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer.MapGuide(name, url, paramsUntiled, options);
+
+ var newParams = { mapDefinition: 'Library://Samples/Gmap/Maps/gmap.MapDefinition',
+ chickpeas: 'image/png'};
+
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ layer.redraw = function() {
+ t.ok(true, "layer is redrawn after new params merged");
+ }
+
+ layer.mergeNewParams(newParams);
+
+ t.eq( layer.params.mapDefinition, "Library://Samples/Gmap/Maps/gmap.MapDefinition", "mergeNewParams() overwrites well");
+ t.eq( layer.params.chickpeas, "image/png", "mergeNewParams() adds well");
+
+ newParams.chickpeas = 151;
+
+ t.eq( layer.params.chickpeas, "image/png", "mergeNewParams() makes clean copy of hashtable");
+ map.destroy();
+ }
+
+ function test_Layer_MapGuide_destroy (t) {
+
+ t.plan( 1 );
+
+ var options = {singleTile:true};
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.MapGuide(name, url, paramsUntiled, options);
+ map.addLayer(layer);
+
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+
+ //grab a reference to one of the tiles
+ var tile = layer.grid[0][0];
+
+ layer.destroy();
+
+ // checks to make sure superclass (grid) destroy() was called
+
+ t.ok( layer.grid == null, "grid set to null");
+ }
+
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/MapServer.html b/misc/openlayers/tests/Layer/MapServer.html
new file mode 100644
index 0000000..9ae8d01
--- /dev/null
+++ b/misc/openlayers/tests/Layer/MapServer.html
@@ -0,0 +1,238 @@
+<html>
+<head>
+ <script type="text/javascript">var oldAlert = window.alert, gMess; window.alert = function(message) {gMess = message; return true;};</script>
+ <script type="text/javascript">window.alert = oldAlert;</script>
+
+
+<script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ // turn off animation frame handling, so we can check img urls in tests
+ delete OpenLayers.Layer.Grid.prototype.queueTileDraw;
+
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var layer;
+
+ var name = 'Test Layer';
+ var url = "http://labs.metacarta.com/cgi-bin/mapserv";
+ var params = { map: '/mapdata/vmap_wms.map',
+ layers: 'basic'};
+
+ function test_Layer_MapServer_constructor (t) {
+ t.plan( 4 );
+
+ var url = "http://labs.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.MapServer(name, url, params);
+ t.ok( layer instanceof OpenLayers.Layer.MapServer, "new OpenLayers.Layer.MapServer returns object" );
+ t.eq( layer.url, "http://labs.metacarta.com/cgi-bin/mapserv", "layer.url is correct (HTTPRequest inited)" );
+
+ t.eq( layer.params.mode, "map", "default mode param correctly copied");
+ t.eq( layer.params.map_imagetype, "png", "default imagetype correctly copied");
+
+
+ }
+
+ function test_Layer_MapServer_addtile (t) {
+ t.plan( 6 );
+
+ var url = "http://labs.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.MapServer(name, url, params);
+ var map = new OpenLayers.Map('map', {tileManager: null});
+ map.addLayer(layer);
+ var pixel = new OpenLayers.Pixel(5,6);
+ var tile = layer.addTile(new OpenLayers.Bounds(1,2,3,4), pixel);
+ tile.draw();
+
+ var img = tile.imgDiv;
+ var tParams = OpenLayers.Util.extend({},params);
+ tParams = OpenLayers.Util.extend(tParams, {
+ layers: 'basic',
+ mode: 'map',
+ map_imagetype: 'png',
+ mapext:[1,2,3,4],
+ imgext:[1,2,3,4],
+ map_size:[256, 256],
+ imgx:128,
+ imgy:128,
+ imgxy:[256,256]
+ });
+ t.eq( tile.url,
+ url + "?" + OpenLayers.Util.getParameterString(tParams).replace(/,/g, "+"),
+ "image src is created correctly via addtile" );
+ t.eq( tile.getTile().style.top, "6px", "image top is set correctly via addtile" );
+ t.eq( tile.getTile().style.left, "5px", "image top is set correctly via addtile" );
+
+ var firstChild = layer.div.firstChild;
+ t.eq( firstChild.nodeName.toLowerCase(), "img", "div first child is an image object" );
+ t.ok( firstChild == img, "div first child is correct image object" );
+ t.eq( tile.position.toString(), "x=5,y=6", "Position of tile is set correctly." );
+ map.destroy();
+ }
+
+ function test_Layer_MapServer_inittiles (t) {
+ t.plan( 2 );
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.MapServer(name, url, params, {buffer: 0});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0),5);
+ t.eq( layer.grid.length, 4, "Grid rows is correct." );
+ t.eq( layer.grid[0].length, 3, "Grid cols is correct." );
+ map.destroy();
+
+ }
+
+
+ function test_Layer_MapServer_clone (t) {
+ t.plan(4);
+
+ var url = "http://labs.metacarta.com/cgi-bin/mapserv";
+ var options = {tileSize: new OpenLayers.Size(500,50)};
+ var map = new OpenLayers.Map('map', options);
+ layer = new OpenLayers.Layer.MapServer(name, url, params);
+ map.addLayer(layer);
+
+ layer.grid = [ [6, 7],
+ [8, 9]];
+
+ var clone = layer.clone();
+
+ t.ok( clone.grid != layer.grid, "clone does not copy grid");
+
+ t.ok( clone.tileSize.equals(layer.tileSize), "tileSize correctly cloned");
+
+ layer.tileSize.w += 40;
+
+ t.eq( clone.tileSize.w, 500, "changing layer.tileSize does not change clone.tileSize -- a fresh copy was made, not just copied reference");
+
+ t.eq( clone.alpha, layer.alpha, "alpha copied correctly");
+
+ layer.grid = null;
+ map.destroy();
+ }
+
+ function test_Layer_MapServer_isBaseLayer(t) {
+ t.plan(3);
+
+ var url = "http://labs.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.MapServer(name, url, params);
+ t.ok( layer.isBaseLayer, "baselayer is true by default");
+
+ var newParams = OpenLayers.Util.extend({}, params);
+ newParams.transparent = "true";
+ layer = new OpenLayers.Layer.MapServer(name, url, newParams);
+ t.ok( !layer.isBaseLayer, "baselayer is false when transparent is set to true");
+
+ layer = new OpenLayers.Layer.MapServer(name, url, params, {isBaseLayer: false});
+ t.ok( !layer.isBaseLayer, "baselayer is false when option is set to false" );
+ }
+
+ function test_Layer_MapServer_mergeNewParams (t) {
+ t.plan( 4 );
+
+ var map = new OpenLayers.Map("map");
+ var url = "http://labs.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.MapServer(name, url, params);
+
+ var newParams = { layers: 'sooper',
+ chickpeas: 'image/png'};
+
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ layer.redraw = function() {
+ t.ok(true, "layer is redrawn after new params merged");
+ }
+ layer.mergeNewParams(newParams);
+
+ t.eq( layer.params.layers, "sooper", "mergeNewParams() overwrites well");
+ t.eq( layer.params.chickpeas, "image/png", "mergeNewParams() adds well");
+
+ newParams.chickpeas = 151;
+
+ t.eq( layer.params.chickpeas, "image/png", "mergeNewParams() makes clean copy of hashtable");
+ map.destroy();
+ }
+
+ function test_Layer_MapServer_getFullRequestString (t) {
+ t.plan( 3 );
+ var map = new OpenLayers.Map('map');
+ tUrl = "http://labs.metacarta.com/cgi-bin/mapserv";
+ tParams = { layers: 'basic',
+ format: 'png'};
+ var tLayer = new OpenLayers.Layer.MapServer(name, tUrl, tParams);
+ map.addLayer(tLayer);
+ str = tLayer.getFullRequestString();
+ var tParams = {
+ layers: 'basic',
+ format: 'png',
+ mode: 'map',
+ map_imagetype: 'png'
+ };
+
+ var sStr = tUrl + "?" + OpenLayers.Util.getParameterString(tParams);
+ sStr = sStr.replace(/,/g, "+");
+
+ t.eq(str, sStr , "getFullRequestString() works");
+ map.destroy();
+
+ tUrl = ["http://octo.metacarta.com/cgi-bin/mapserv","http://labs.metacarta.com/cgi-bin/mapserv"];
+ layer = new OpenLayers.Layer.MapServer(name, tUrl, tParams, null);
+ str = layer.getFullRequestString({'c':'d'});
+ t.eq(str, tUrl[1] + '?' + OpenLayers.Util.getParameterString(OpenLayers.Util.extend(tParams,{'c':'d'})), "getFullRequestString() works for list of two urls and is deterministic");
+ layer.destroy();
+ var tParams = {
+ layers: 'basic',
+ format: 'png',
+ mode: 'map',
+ map_imagetype: 'png'
+ };
+ tUrl = ["http://octo.metacarta.com/cgi-bin/mapserv","http://labs.metacarta.com/cgi-bin/mapserv"];
+ layer = new OpenLayers.Layer.MapServer(name, tUrl, tParams, null);
+ str = layer.getFullRequestString({'a':'b'});
+ t.eq(str, tUrl[0] + '?' + OpenLayers.Util.getParameterString(OpenLayers.Util.extend(tParams,{'a':'b'})), "getFullRequestString() works for list of two urls and is deterministic");
+ layer.destroy();
+
+ }
+
+ function test_Layer_MapServer_singleTile (t) {
+ t.plan( 5 );
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.MapServer(name, url, params, {singleTile: true});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0),5);
+ t.eq( layer.singleTile, true, "layer has singleTile property, great!" );
+ t.eq( layer.grid.length, 1, "Grid has only a single row, good enough!" );
+ t.eq( layer.grid[0].length, 1, "Grid has only a single column, good enough!" );
+ t.eq( layer.tileSize.w, 750, "Image width is correct" );
+ t.eq( layer.tileSize.h, 825, "Image height is correct" );
+ map.destroy();
+ }
+
+
+
+ function test_Layer_MapServer_destroy (t) {
+
+ t.plan( 1 );
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.MapServer(name, url, params);
+ map.addLayer(layer);
+
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+
+ //grab a reference to one of the tiles
+ var tile = layer.grid[0][0];
+
+ layer.destroy();
+
+ // checks to make sure superclass (grid) destroy() was called
+
+ t.ok( layer.grid == null, "grid set to null");
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/Markers.html b/misc/openlayers/tests/Layer/Markers.html
new file mode 100644
index 0000000..07f699f
--- /dev/null
+++ b/misc/openlayers/tests/Layer/Markers.html
@@ -0,0 +1,156 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var layer;
+
+ function test_initialize(t) {
+ t.plan( 2 );
+
+ layer = new OpenLayers.Layer.Markers('Test Layer');
+ t.ok( layer instanceof OpenLayers.Layer.Markers, "new OpenLayers.Layer.Markers returns object" );
+ t.eq( layer.name, "Test Layer", "layer.name is correct" );
+ }
+ function test_addlayer (t) {
+ t.plan( 3 );
+
+ layer = new OpenLayers.Layer.Markers('Test Layer');
+ t.ok( layer instanceof OpenLayers.Layer.Markers, "new OpenLayers.Layer.Markers returns object" );
+ t.eq( layer.name, "Test Layer", "layer.name is correct" );
+ layer.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),
+ new OpenLayers.Icon())
+ );
+ t.eq( layer.markers.length, 1, "addLayer adds marker to layer." );
+ }
+ function test_addMarker_removeMarker (t) {
+ t.plan( 6 );
+
+ var map = new OpenLayers.Map('map');
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+ map.zoomToMaxExtent();
+ layer = new OpenLayers.Layer.Markers('Test Layer');
+ map.addLayer(layer);
+ var marker = new OpenLayers.Marker(new OpenLayers.LonLat(5,40));
+ layer.addMarker(marker);
+ t.ok( marker.icon.imageDiv.parentNode == layer.div, "addMarker adds marker image node into layer node." );
+ layer.removeMarker(marker);
+ t.ok( marker.icon.imageDiv.parentNode != layer.div, "removeMarker removes marker image node from layer node." );
+ layer.removeMarker(marker);
+ t.ok(true, "Removing marker twice does not fail.");
+ layer.addMarker(marker);
+ t.ok( marker.icon.imageDiv.parentNode == layer.div, "addMarker adds marker image node into layer node." );
+
+ layer.markers = null;
+ layer.removeMarker(marker);
+ t.ok(true, "removing marker when no markers present does not script error");
+
+ var l = new OpenLayers.Layer.Markers();
+ var marker = new OpenLayers.Marker(new OpenLayers.LonLat(5,40));
+ l.addMarker(marker);
+ l.removeMarker(marker);
+ t.ok(true, "Removing marker when layer not added to map does not fail.");
+
+ }
+
+ function test_markerMovement(t) {
+
+ t.plan(6);
+
+ var map = new OpenLayers.Map("map", {zoomMethod: null});
+ var layer = new OpenLayers.Layer.Markers("Base", {isBaseLayer: true});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 1);
+
+ var size = new OpenLayers.Size(10, 10);
+ var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
+ var icon = new OpenLayers.Icon("foo", size, offset);
+ var marker = new OpenLayers.Marker(new OpenLayers.LonLat(10, -10), icon)
+ layer.addMarker(marker);
+
+ t.eq(marker.icon.px.x, 554, "marker icon is placed at 554 px on x-axis");
+ t.eq(marker.icon.px.y, 314, "marker icon is placed at 314 px on y-axis");
+
+ map.zoomTo(2);
+
+ t.eq(marker.icon.px.x, 568, "marker icon moved to 568 px on x-axis");
+ t.eq(marker.icon.px.y, 328, "marker icon moved to 328 px on y-axis");
+
+ map.zoomTo(1);
+
+ t.eq(marker.icon.px.x, 554, "marker icon moved back to 554 px on x-axis");
+ t.eq(marker.icon.px.y, 314, "marker icon moved back to 314 px on y-axis");
+
+ }
+
+ function test_destroy (t) {
+ t.plan( 1 );
+ layer = new OpenLayers.Layer.Markers('Test Layer');
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+ layer.destroy();
+ t.eq( layer.map, null, "layer.map is null after destroy" );
+ }
+
+ function test_getDataExtent(t) {
+ t.plan( 4 );
+
+ var layer = {};
+ var ret = OpenLayers.Layer.Markers.prototype.getDataExtent.apply(layer, []);
+ t.eq(ret, null, "does not crash, returns null on layer with null 'this.markers'");
+
+ layer.markers = [];
+ ret = OpenLayers.Layer.Markers.prototype.getDataExtent.apply(layer, []);
+ t.eq(ret, null, "returns null on layer with empty 'this.markers'");
+
+ layer.markers.push({
+ 'lonlat': new OpenLayers.LonLat(4,5)
+ });
+ var expectedBounds = new OpenLayers.Bounds(4,5,4,5);
+ ret = OpenLayers.Layer.Markers.prototype.getDataExtent.apply(layer, []);
+ t.ok(ret.equals(expectedBounds), "returns expected bounds with only one marker");
+
+ layer.markers.push({
+ 'lonlat': new OpenLayers.LonLat(1,2)
+ });
+ var expectedBounds = new OpenLayers.Bounds(1,2,4,5);
+ ret = OpenLayers.Layer.Markers.prototype.getDataExtent.apply(layer, []);
+ t.ok(ret.equals(expectedBounds), "returns expected bounds with multiple markers");
+
+ }
+
+ function test_setOpacity(t) {
+ t.plan(1);
+
+ layer = new OpenLayers.Layer.Markers('Test Layer');
+
+ var opacity = 0.1234;
+
+ for (var i = 0; i < 12; i++) {
+ layer.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0), new OpenLayers.Icon()));
+ }
+
+ layer.setOpacity(opacity);
+
+ for (var i = 0; i < 4; i++) {
+ layer.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0), new OpenLayers.Icon()));
+ }
+
+ var itWorks = false;
+ for (var i = 0; i < layer.markers.length; i++) {
+ itWorks = parseFloat(layer.markers[i].icon.imageDiv.style.opacity) == opacity;
+ if (!itWorks) {
+ break;
+ }
+ }
+ t.ok(itWorks, "setOpacity change markers opacity");
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1080px; height: 600px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/OSM.html b/misc/openlayers/tests/Layer/OSM.html
new file mode 100644
index 0000000..fac471c
--- /dev/null
+++ b/misc/openlayers/tests/Layer/OSM.html
@@ -0,0 +1,16 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_clone(t) {
+ t.plan(1);
+ var layer = new OpenLayers.Layer.OSM();
+ var clone = layer.clone();
+ t.ok(clone instanceof OpenLayers.Layer.OSM, "clone is a Layer.OSM instance");
+ }
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/PointGrid.html b/misc/openlayers/tests/Layer/PointGrid.html
new file mode 100644
index 0000000..6fb6ae2
--- /dev/null
+++ b/misc/openlayers/tests/Layer/PointGrid.html
@@ -0,0 +1,232 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../OLLoader.js"></script>
+<script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(1);
+ var layer = new OpenLayers.Layer.PointGrid();
+ t.ok(layer instanceof OpenLayers.Layer.PointGrid, "instance created");
+ layer.destroy();
+ }
+
+ function test_name(t) {
+ t.plan(1);
+ var layer = new OpenLayers.Layer.PointGrid({name: "foo"});
+ t.eq(layer.name, "foo", "name set like every other property");
+ layer.destroy();
+ }
+
+ function test_spacing(t) {
+ t.plan(7);
+
+ var layer = new OpenLayers.Layer.PointGrid({
+ isBaseLayer: true,
+ resolutions: [1],
+ maxExtent: new OpenLayers.Bounds(-100, -50, 100, 50),
+ dx: 10,
+ dy: 10,
+ ratio: 1
+ });
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ layers: [layer],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 0
+ });
+
+ t.eq(layer.features.length, 200, "200 features");
+
+ // set dx/dy together
+ layer.setSpacing(20);
+ t.eq(layer.dx, 20, "dx 20");
+ t.eq(layer.dy, 20, "dy 20");
+ t.eq(layer.features.length, 50, "50 features");
+
+ // set dx/dy independently
+ layer.setSpacing(50, 25);
+ t.eq(layer.dx, 50, "dx 50");
+ t.eq(layer.dy, 25, "dy 25");
+ t.eq(layer.features.length, 16, "16 features");
+
+ map.destroy();
+ }
+
+ function test_ratio(t) {
+ t.plan(3);
+
+ var layer = new OpenLayers.Layer.PointGrid({
+ isBaseLayer: true,
+ resolutions: [1],
+ maxExtent: new OpenLayers.Bounds(-100, -50, 100, 50),
+ dx: 25,
+ dy: 25,
+ ratio: 1
+ });
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ layers: [layer],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 0
+ });
+
+ t.eq(layer.features.length, 32, "32 features");
+
+ // increase ratio (1.5 -> 300 x 150)
+ layer.setRatio(1.5);
+ t.eq(layer.ratio, 1.5, "ratio 1.5");
+ t.eq(layer.features.length, 72, "72 features");
+
+ map.destroy();
+ }
+
+ function test_maxFeatures(t) {
+ t.plan(3);
+
+ var layer = new OpenLayers.Layer.PointGrid({
+ isBaseLayer: true,
+ resolutions: [1],
+ maxExtent: new OpenLayers.Bounds(-100, -50, 100, 50),
+ dx: 10,
+ dy: 10,
+ ratio: 1
+ });
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ layers: [layer],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 0
+ });
+
+ t.eq(layer.features.length, 200, "200 features");
+
+ // limit maxFeatures
+ layer.setMaxFeatures(150);
+ t.eq(layer.maxFeatures, 150, "maxFeatures 150");
+ t.ok(layer.features.length <= 150, "<= 150 features");
+
+ map.destroy();
+ }
+
+ function test_rotation(t) {
+ t.plan(6);
+
+ var layer = new OpenLayers.Layer.PointGrid({
+ isBaseLayer: true,
+ resolutions: [1],
+ maxExtent: new OpenLayers.Bounds(-100, -50, 100, 50),
+ dx: 10,
+ dy: 10,
+ ratio: 1
+ });
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ layers: [layer],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 0
+ });
+
+ function getRotation(layer) {
+ // grid starts at bottom left and goes up
+ var g0 = layer.features[0].geometry;
+ var g1 = layer.features[1].geometry;
+ // subtract 90 to get rotation of grid
+ return Math.atan2(g1.y - g0.y, g1.x - g0.x) * (180 / Math.PI) - 90;
+ }
+
+ t.eq(layer.rotation, 0, "0 rotation");
+ t.eq(getRotation(layer).toFixed(3), (0).toFixed(3), "0 grid")
+
+ // rotate grid 25 degrees counter-clockwise
+ layer.setRotation(25);
+ t.eq(layer.rotation, 25, "25 rotation");
+ t.eq(getRotation(layer).toFixed(3), (25).toFixed(3), "25 grid");
+
+ // rotate grid 45 degrees clockwise
+ layer.setRotation(-45);
+ t.eq(layer.rotation, -45, "-45 rotation");
+ t.eq(getRotation(layer).toFixed(3), (-45).toFixed(3), "-45 grid");
+
+ map.destroy();
+ }
+
+ function test_origin(t) {
+ t.plan(7);
+
+ var layer = new OpenLayers.Layer.PointGrid({
+ isBaseLayer: true,
+ resolutions: [1],
+ maxExtent: new OpenLayers.Bounds(-100, -50, 100, 50),
+ dx: 10,
+ dy: 10,
+ ratio: 1
+ });
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ layers: [layer],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 0
+ });
+
+ var origin = layer.getOrigin();
+ t.ok(map.getExtent().getCenterLonLat().equals(origin), "default is center of map extent");
+
+ var g0 = layer.features[0].geometry;
+
+ t.eq((g0.x - origin.lon) % layer.dx, 0, "a) lattice aligned with origin x");
+ t.eq((g0.y - origin.lat) % layer.dy, 0, "a) lattice aligned with origin y");
+
+ // set origin
+ layer.setOrigin(new OpenLayers.LonLat(-5, 12));
+ origin = layer.getOrigin();
+ t.eq(origin.lon, -5, "-5 origin x");
+ t.eq(origin.lat, 12, "12 origin y");
+
+ g0 = layer.features[0].geometry;
+ t.eq((g0.x - origin.lon) % layer.dx, 0, "b) lattice aligned with origin x");
+ t.eq((g0.y - origin.lat) % layer.dy, 0, "b) lattice aligned with origin y");
+
+ map.destroy();
+ }
+
+ function test_zoom(t) {
+ t.plan(2);
+
+ var layer = new OpenLayers.Layer.PointGrid({
+ isBaseLayer: true,
+ resolutions: [2, 1],
+ maxExtent: new OpenLayers.Bounds(-200, -100, 200, 100),
+ dx: 20,
+ dy: 20,
+ ratio: 1
+ });
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ layers: [layer],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 1,
+ zoomMethod: null
+ });
+
+ t.eq(layer.features.length, 50, "50 features at zoom 1");
+
+ map.zoomTo(0);
+ t.eq(layer.features.length, 200, "200 features at zoom 0")
+
+ map.destroy();
+ }
+
+
+</script>
+</head>
+<body>
+<div id="map" style="width:200px;height:100px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/PointTrack.html b/misc/openlayers/tests/Layer/PointTrack.html
new file mode 100644
index 0000000..95b8ced
--- /dev/null
+++ b/misc/openlayers/tests/Layer/PointTrack.html
@@ -0,0 +1,79 @@
+<html>
+<head>
+<script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var name = "PointTrack Layer";
+
+ function test_Layer_PointTrack_constructor(t) {
+ t.plan(2);
+
+ var layer = new OpenLayers.Layer.PointTrack(name);
+ t.ok(layer instanceof OpenLayers.Layer.PointTrack, "new OpenLayers.Layer.PointTrack returns correct object" );
+ t.ok(layer.addNodes, "layer has an addNodes method");
+
+ }
+
+ function test_Layer_PointTrack_addNodes(t) {
+ t.plan(11);
+
+ var layer = new OpenLayers.Layer.PointTrack(name,
+ {dataFrom: OpenLayers.Layer.PointTrack.dataFrom.TARGET_NODE});
+
+ var point1 = new OpenLayers.Geometry.Point(-111.04, 45.68);
+ var sourceNode = new OpenLayers.Feature.Vector(point1);
+ var point2 = new OpenLayers.Geometry.Point(-112.34, 45.67);
+ var targetNode = new OpenLayers.Feature.Vector(point2, {foo: "bar"});
+ layer.addNodes([sourceNode, targetNode]);
+
+ t.eq(layer.features.length, 1, "OpenLayers.Layer.PointTrack.addNodes creates one feature from two vector point features");
+ t.eq(layer.features[0].geometry.CLASS_NAME, "OpenLayers.Geometry.LineString", "The created feature has a LineString geometry");
+ var geometry = layer.features[0].geometry;
+ t.eq(geometry.components[0].x, -111.04, "The x of the first point of the line equals the x of the first point added to the layer");
+ t.eq(geometry.components[1].y, 45.67, "The y of the second point of the line equals the y of the second point added to the layer");
+ t.eq(layer.features[0].attributes.foo, "bar", "OpenLayers.Layer.PointTrack.addNodes assigns the attributes of the target node correctly");
+
+ layer.dataFrom = OpenLayers.Layer.PointTrack.dataFrom.SOURCE_NODE;
+
+ point1 = new OpenLayers.Geometry.Point(-123.54, 45.67);
+ sourceNode = new OpenLayers.Feature.Vector(point1, {foo: "bar"});
+ point2 = new OpenLayers.Geometry.Point(-123.21, 45.32);
+ targetNode = new OpenLayers.Feature.Vector(point2);
+ layer.addNodes([sourceNode, targetNode]);
+
+ t.eq(layer.features.length, 2, "added another two points, so the layer now has two features");
+ t.eq(layer.features[1].attributes.foo, "bar", "OpenLayers.Layer.PointTrack.addNodes assigns the attributes of the source node correctly");
+
+ point1 = new OpenLayers.LonLat(-123.58, 45.69);
+ sourceNode = new OpenLayers.Feature(null, point1);
+ point2 = new OpenLayers.LonLat(-123.25, 45.37);
+ targetNode = new OpenLayers.Feature(null, point2);
+ sourceNode.data = {foo: "bar"};
+ layer.addNodes([sourceNode, targetNode]);
+
+ t.eq(layer.features.length, 3, "added another two points, this time from features, so the layer now has two features");
+ t.eq(layer.features[2].geometry.components[0].x, -123.58, "The x of the first point of the line equals the x of the first point added to the layer");
+ t.eq(layer.features[2].geometry.components[1].y, 45.37, "The y of the second point of the line equals the x of the second point added to the layer");
+ t.eq(layer.features[2].data, sourceNode.data, "OpenLayers.Layer.PointTrack.addNodes assigns the data of the source node correctly");
+
+ }
+
+ function test_Layer_PointTrack_destroy (t) {
+ t.plan(3);
+ layer = new OpenLayers.Layer.PointTrack(name);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+ t.eq(layer.map.layers.length, 1, "layer added to the map successfully");
+ layer.destroy();
+ t.eq(layer.map, null, "layer.map is null after destroy");
+ t.ok(!layer.renderer, "layer.renderer is falsey after destroy");
+ }
+
+
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/SphericalMercator.html b/misc/openlayers/tests/Layer/SphericalMercator.html
new file mode 100644
index 0000000..be94735
--- /dev/null
+++ b/misc/openlayers/tests/Layer/SphericalMercator.html
@@ -0,0 +1,126 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_SphericalMercator_forwardMercator(t) {
+ t.plan(12);
+ var arctic = OpenLayers.Layer.SphericalMercator.forwardMercator(0, 85);
+ var antarctic = OpenLayers.Layer.SphericalMercator.forwardMercator(0, -85);
+ var hawaii = OpenLayers.Layer.SphericalMercator.forwardMercator(-180, 0);
+ var phillipines = OpenLayers.Layer.SphericalMercator.forwardMercator(180, 0);
+ var ne = OpenLayers.Layer.SphericalMercator.forwardMercator(180, 90);
+ var sw = OpenLayers.Layer.SphericalMercator.forwardMercator(-180, -90);
+
+ t.eq(arctic.lon, 0, "Arctic longitude is correct");
+ t.eq(Math.round(arctic.lat), 19971869, "Arctic latitude is correct");
+
+ t.eq(antarctic.lon, 0, "Antarctic longitude is correct");
+ t.eq(Math.round(antarctic.lat), -19971869, "Antarctic latitude is correct");
+
+ t.eq(Math.round(hawaii.lat), 0, "Hawaiian lat is correct");
+ t.eq(hawaii.lon, -20037508.34, "Hawaiian lon is correct");
+
+ t.eq(Math.round(phillipines.lat), 0, "Phillipines lat is correct");
+ t.eq(phillipines.lon, 20037508.340, "Phillipines lon is correct");
+
+ // be kind and stay within the world instead of having +/- infinity lat
+ t.ok(ne.lat, 20037508.34, "NE lat is correct");
+ t.eq(ne.lon, 20037508.34, "NE lon is correct");
+
+ t.eq(sw.lat, -20037508.34, "SW lat is correct");
+ t.eq(sw.lon, -20037508.34, "SW lon is correct");
+ }
+
+ function test_sphericalMercator_inverseMercator(t) {
+ t.plan(4);
+ var sw = OpenLayers.Layer.SphericalMercator.inverseMercator(-20037508.34, -20037508.34);
+ var ne = OpenLayers.Layer.SphericalMercator.inverseMercator(20037508.34, 20037508.34);
+ t.eq(sw.lon, -180, "Southwest lon correct");
+ t.eq(ne.lon, 180, "Northeast lon correct");
+
+ t.eq(sw.lat.toFixed(10), "-85.0511287798", "Southwest lat correct");
+ t.eq(ne.lat.toFixed(10), "85.0511287798", "Northeast lat correct");
+ }
+
+ function strToFixed(str, dig) {
+ if(dig == undefined) {
+ dig = 5;
+ }
+ return str.replace(/(\d+\.\d+)/g, function(match) {
+ return parseFloat(match).toFixed(dig);
+ });
+ }
+
+ function test_SphericalMercator_to4326(t) {
+ t.plan(1);
+ var point = new OpenLayers.Geometry.Point(1113195, 2273031);
+ point.transform("EPSG:900913", "EPSG:4326");
+
+ t.eq(strToFixed(point.toString()),
+ strToFixed("POINT(10.000000828446318 20.000000618997227)"),
+ "point transforms from Spherical Mercator to EPSG:4326");
+ }
+
+ function test_SphericalMercator_addTransform(t) {
+ // this class should add two methods to the
+ // OpenLayers.Projection.transforms object
+ t.plan(4);
+ var wgs84 = OpenLayers.Projection.transforms["EPSG:4326"];
+ t.ok(wgs84 instanceof Object, "EPSG:4326 exists in table");
+
+ var smerc = OpenLayers.Projection.transforms["EPSG:900913"];
+ t.ok(smerc instanceof Object, "EPSG:900913 exists in table");
+
+ t.ok(typeof(wgs84["EPSG:900913"]) === "function",
+ "from EPSG:4326 to EPSG:900913 correctly defined");
+ t.ok(typeof(smerc["EPSG:4326"]) === "function",
+ "from EPSG:900913 to EPSG:4326 correctly defined");
+ }
+
+ function test_equivalence(t) {
+
+ // list of equivalent codes for web mercator
+ var codes = ["EPSG:900913", "EPSG:3857", "EPSG:102113", "EPSG:102100"];
+ var len = codes.length;
+
+ t.plan(len + (len * len));
+
+ var ggPoint = new OpenLayers.Geometry.Point(10, 20);
+ var smPoint = new OpenLayers.Geometry.Point(1113195, 2273031);
+
+ var gg = new OpenLayers.Projection("EPSG:4326");
+
+ var i, proj, forward, inverse, other, j, equiv;
+ for (i=0, len=codes.length; i<len; ++i) {
+ proj = new OpenLayers.Projection(codes[i]);
+
+ // confirm that forward/inverse work
+ forward = ggPoint.clone().transform(gg, proj);
+ t.eq(
+ strToFixed(forward.toString()),
+ strToFixed("POINT(1113194.9077777779 2273030.9266712805)"),
+ "transforms from EPSG:4326 to " + proj
+ );
+ inverse = smPoint.clone().transform(proj, gg);
+ t.eq(
+ strToFixed(inverse.toString()),
+ strToFixed("POINT(10.000000828446318 20.000000618997227)"),
+ "transforms from " + proj + " to EPSG:4326"
+ );
+
+ // confirm that null transform works
+ for (j=i+1; j<len; ++j) {
+ other = new OpenLayers.Projection(codes[j]);
+ equiv = ggPoint.clone().transform(proj, other);
+ t.ok(proj.equals(other), proj + " and " + other + " are equivalent");
+ t.ok(ggPoint.equals(equiv), "transform from " + proj + " to " + other + " preserves geometry");
+ }
+ }
+
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/TMS.html b/misc/openlayers/tests/Layer/TMS.html
new file mode 100644
index 0000000..4ac629f
--- /dev/null
+++ b/misc/openlayers/tests/Layer/TMS.html
@@ -0,0 +1,262 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var layer;
+
+ var name = 'Test Layer';
+ var url = "http://labs.metacarta.com/wms-c/Basic.py/";
+ var options = {'layername':'basic', 'type':'png'};
+
+
+ function test_Layer_TMS_constructor (t) {
+ t.plan( 1 );
+
+ layer = new OpenLayers.Layer.TMS(name, url, options);
+ t.ok( layer instanceof OpenLayers.Layer.TMS, "returns OpenLayers.Layer.TMS object" );
+ }
+
+
+
+ function test_Layer_TMS_clearTiles (t) {
+ t.plan( 1 );
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.TMS(name, url, options);
+ map.addLayer(layer);
+
+ map.setCenter(new OpenLayers.LonLat(0,0));
+
+ //grab a reference to one of the tiles
+ var tile = layer.grid[0][0];
+
+ layer.clearGrid();
+
+ t.ok( layer.grid != null, "layer.grid does not get nullified" );
+ map.destroy();
+ }
+
+
+ function test_Layer_TMS_getTMSBounds(t) {
+ t.plan( 1 );
+
+ layer = new OpenLayers.Layer.TMS(name, url, options);
+
+ var bl = { bounds: new OpenLayers.Bounds(1,2,2,3)};
+ var tr = { bounds: new OpenLayers.Bounds(2,3,3,4)};
+ layer.grid = [ [6, tr],
+ [bl, 7]];
+
+ var bounds = layer.getTilesBounds();
+
+ var testBounds = new OpenLayers.Bounds(1,2,3,4);
+
+ t.ok( bounds.equals(testBounds), "getTMSBounds() returns correct bounds");
+
+ layer.grid = null;
+ }
+
+ function test_Layer_TMS_getResolution(t) {
+ t.plan( 1 );
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.TMS(name, url, options);
+ map.addLayer(layer);
+
+ map.zoom = 5;
+
+ t.eq( layer.getResolution(), 0.0439453125, "getResolution() returns correct value");
+ map.destroy();
+ }
+
+ function test_Layer_TMS_getZoomForExtent(t) {
+ t.plan( 2 );
+ var bounds, zoom;
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.TMS(name, url, options);
+ map.addLayer(layer);
+
+ bounds = new OpenLayers.Bounds(10,10,12,12);
+ zoom = layer.getZoomForExtent(bounds);
+
+ t.eq( zoom, 8, "getZoomForExtent() returns correct value");
+
+ bounds = new OpenLayers.Bounds(10,10,100,100);
+ zoom = layer.getZoomForExtent(bounds);
+
+ t.eq( zoom, 2, "getZoomForExtent() returns correct value");
+ map.destroy();
+ }
+
+
+ /** THIS WOULD BE WHERE THE TESTS WOULD GO FOR
+ *
+ * -moveTo
+ * -insertColumn
+ * -insertRow
+
+ function 07_Layer_TMS_moveTo(t) {
+ }
+
+ function 08_Layer_TMS_insertColumn(t) {
+ }
+
+ function 09_Layer_TMS_insertRow(t) {
+ }
+
+ *
+ */
+ function test_Layer_TMS_getURL(t) {
+
+ t.plan(3);
+
+ var map = new OpenLayers.Map('map', options);
+ var options = {'layername':'basic', 'type':'png'};
+ layer = new OpenLayers.Layer.TMS(name, url, options);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 9);
+ var tileurl = layer.getURL(new OpenLayers.Bounds(3.515625,45,4.21875,45.703125));
+ t.eq(tileurl, "http://labs.metacarta.com/wms-c/Basic.py/1.0.0/basic/9/261/192.png", "Tile URL is correct");
+
+ var layer2 = layer.clone();
+ layer2.serviceVersion = "1.2.3";
+ map.addLayer(layer2);
+ tileurl = layer2.getURL(new OpenLayers.Bounds(3.515625,45,4.21875,45.703125));
+ t.eq(tileurl, "http://labs.metacarta.com/wms-c/Basic.py/1.2.3/basic/9/261/192.png", "TMS serviceVersion is correct");
+
+ layer.url = ["http://tilecache1/", "http://tilecache2/", "http://tilecache3/"];
+ tileurl = layer.getURL(new OpenLayers.Bounds(3.515625,45,4.21875,45.703125));
+ t.eq(tileurl, "http://tilecache1/1.0.0/basic/9/261/192.png", "Tile URL is deterministic");
+ map.destroy();
+ }
+ function test_Layer_TMS_Rounding(t) {
+ t.plan(1);
+ m = new OpenLayers.Map("map", {'maxExtent':new OpenLayers.Bounds(-122.6579,37.4901,-122.0738,37.8795)});
+ layer = new OpenLayers.Layer.TMS( "TMS",
+ "http://labs.metacarta.com/wms-c/Basic.py/", {layername: 'basic', type:'png', resolutions:[0.000634956337608418], buffer: 2} );
+ m.addLayer(layer);
+ m.zoomToMaxExtent();
+ t.eq(layer.getURL(layer.grid[3][3].bounds), "http://labs.metacarta.com/wms-c/Basic.py/1.0.0/basic/0/1/1.png", "TMS tiles around rounded properly.");
+ m.destroy();
+ }
+
+ function test_Layer_TMS_serverResolutions(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map('map', {
+ resolutions: [13,11]
+ });
+
+ var layer = new OpenLayers.Layer.TMS('tc layer', '', options);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 1);
+
+ var tileurl = layer.getURL(new OpenLayers.Bounds(0,0,0,0));
+ var level = parseInt(tileurl.split('/')[2]);
+ t.eq(map.getZoom(), level, "Tile zoom level is correct without serverResolutions");
+
+ layer.serverResolutions = [14,13,12,11,10];
+ tileurl = layer.getURL(new OpenLayers.Bounds(0,0,0,0));
+ level = parseInt(tileurl.split('/')[2]);
+ var res = map.getResolution();
+ var gotLevel = OpenLayers.Util.indexOf(layer.serverResolutions, res);
+ t.eq(gotLevel, level, "Tile zoom level is correct with serverResolutions");
+
+ map.destroy();
+ }
+
+ function test_zoomOffset(t) {
+
+ t.plan(2);
+
+ var offset, zoom;
+
+ // test offset of 2
+ offset = 2;
+ zoom = 3;
+
+ var map = new OpenLayers.Map({
+ div: "map"
+ });
+ var layer = new OpenLayers.Layer.TMS("TMS", "", {
+ layername: "basic",
+ type: "png",
+ zoomOffset: offset
+ });
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), zoom);
+
+ var tileurl = layer.getURL(new OpenLayers.Bounds(3.515625,45,4.21875,45.703125));
+ t.eq(parseInt(tileurl.split("/")[2]), zoom + offset, "correct level for offset 2");
+
+ map.destroy();
+
+ // test offset of -1
+ offset = -1;
+ zoom = 3;
+
+ var map = new OpenLayers.Map({
+ div: "map"
+ });
+ var layer = new OpenLayers.Layer.TMS("TMS", "", {
+ layername: "basic",
+ type: "png",
+ zoomOffset: offset
+ });
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), zoom);
+
+ var tileurl = layer.getURL(new OpenLayers.Bounds(3.515625,45,4.21875,45.703125));
+ t.eq(parseInt(tileurl.split("/")[2]), zoom + offset, "correct level for offset -1");
+
+ map.destroy();
+ }
+
+ function test_Layer_TMS_setMap(t) {
+
+ t.plan(3);
+
+ var map = new OpenLayers.Map('map', options);
+ layer = new OpenLayers.Layer.TMS(name, url, options);
+
+ t.eq(layer.tileOrigin, null, "Tile origin starts out null");
+ layer.setMap(map);
+
+ t.eq(layer.tileOrigin.lat, -90, "lat is -90");
+ t.eq(layer.tileOrigin.lon, -180, "lon is -180");
+ map.destroy();
+ }
+
+ function test_Layer_TMS_destroy (t) {
+
+ t.plan( 3 );
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.TMS(name, url, options);
+ map.addLayer(layer);
+ layer.destroy();
+ t.eq( layer.grid, null, "layer.grid is null after destroy" );
+ t.eq( layer.tileSize, null, "layer.tileSize is null after destroy" );
+
+
+ //test with tile creation
+ layer = new OpenLayers.Layer.TMS(name, url, options);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+ //grab a reference to one of the tiles
+ var tile = layer.grid[0][0];
+
+ layer.destroy();
+
+ t.ok( layer.grid == null, "tiles appropriately destroyed");
+ map.destroy();
+ }
+
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px;"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/Text.html b/misc/openlayers/tests/Layer/Text.html
new file mode 100644
index 0000000..3bffe4c
--- /dev/null
+++ b/misc/openlayers/tests/Layer/Text.html
@@ -0,0 +1,211 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var isMSIE = (navigator.userAgent.indexOf("MSIE") > -1);
+ var layer;
+
+ var datafile = "./data_Layer_Text_textfile.txt";
+ var datafile2 = "./data_Layer_Text_textfile_2.txt";
+ var datafile_overflow = "./data_Layer_Text_textfile_overflow.txt";
+
+ // if this test is running in IE, different rules apply
+ if (isMSIE) {
+ datafile = "." + datafile;
+ datafile2 = "." + datafile2;
+ datafile_overflow = "." + datafile_overflow;
+ }
+
+ function test_Layer_Text_constructor (t) {
+ t.plan( 5 );
+
+ layer = new OpenLayers.Layer.Text('Test Layer', { location: datafile });
+ layer.loadText();
+ t.ok( layer instanceof OpenLayers.Layer.Text, "new OpenLayers.Layer.Text returns object" );
+ t.eq( layer.location, datafile, "layer.location is correct" );
+ var markers;
+ t.delay_call( 1, function() {
+ t.eq( layer.markers.length, 2, "marker length is correct" );
+ var ll = new OpenLayers.LonLat(20, 10);
+ t.ok( layer.markers[0].lonlat.equals(ll), "first marker is correct" );
+ t.eq( layer.markers[0].icon.url, 'http://boston.openguides.org/markers/ORANGE.png', "icon" );
+ } );
+ }
+ function test_Layer_Text_draw (t) {
+// t.plan(5);
+ t.plan( 2 );
+ layer = new OpenLayers.Layer.Text('Test Layer', { location: datafile });
+ t.ok( layer instanceof OpenLayers.Layer.Text, "new OpenLayers.Layer.Text returns object" );
+ var map = new OpenLayers.Map('map');
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+ map.addLayer(layer);
+ t.eq( map.layers[1].name, layer.name, "Layer added to map okay" );
+ t.delay_call( 1, function() {
+ map.setCenter(new OpenLayers.LonLat(0,0),0);
+
+/*
+ if (!isMozilla)
+ t.ok( true, "skipping element test outside of Mozilla");
+ else
+ t.ok( map.layers[0].div.firstChild instanceof HTMLImageElement, "Marker added to div" )
+
+ t.eq( map.layers[0].div.firstChild.style.top, "219px", "Marker top set correctly" )
+ t.eq( map.layers[0].div.firstChild.style.left, "273px", "Marker left set correctly" )
+*/
+ });;
+ }
+
+ function test_Layer_Text_moveTo(t) {
+ t.plan(16);
+
+ temp = OpenLayers.Layer.Markers.prototype.moveTo;
+
+ g_Bounds = {};
+ g_ZoomChanged = {};
+ g_Minor = {};
+ var args = [g_Bounds, g_ZoomChanged, g_Minor];
+
+ OpenLayers.Layer.Markers.prototype.moveTo =
+ function(bounds, zoomChanged, minor) {
+ t.ok(bounds == g_Bounds, "correct bounds passed to Markers superclass");
+ t.ok(zoomChanged == g_ZoomChanged, "correct zoomChanged passed to Markers superclass");
+ t.ok(minor == g_Minor, "correct minor passed to Markers superclass");
+ }
+
+ var layer = {
+ 'loadText': function() { g_TextLoaded = true; }
+ };
+
+ //visibility true, loaded true
+ layer.visibility = true;
+ layer.loaded = true;
+ g_TextLoaded = false;
+ OpenLayers.Layer.Text.prototype.moveTo.apply(layer, args);
+ t.ok(g_TextLoaded == false, "text not loaded when visibility true, loaded true");
+
+ //visibility true, loaded false
+ layer.visibility = true;
+ layer.loaded = false;
+ g_TextLoaded = false;
+ OpenLayers.Layer.Text.prototype.moveTo.apply(layer, args);
+ t.ok(g_TextLoaded == true, "text is loaded when visibility true, loaded false");
+
+ //visibility false, loaded true
+ layer.visibility = false;
+ layer.loaded = true;
+ g_TextLoaded = false;
+ OpenLayers.Layer.Text.prototype.moveTo.apply(layer, args);
+ t.ok(g_TextLoaded == false, "text not loaded when visibility false, loaded true");
+
+ //visibility false, loaded false
+ layer.visibility = false;
+ layer.loaded = false;
+ g_TextLoaded = false;
+ OpenLayers.Layer.Text.prototype.moveTo.apply(layer, args);
+ t.ok(g_TextLoaded == false, "text not loaded when visibility false, loaded false");
+
+ OpenLayers.Layer.Markers.prototype.moveTo = temp;
+ }
+
+
+
+ function test_Layer_Text_events (t) {
+ t.plan( 5 );
+ layer = new OpenLayers.Layer.Text('Test Layer', { location: datafile2 });
+ var map = new OpenLayers.Map('map');
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0),0);
+ var event = {};
+ t.delay_call( 1, function() {
+ t.ok(layer.markers[0].events, "First marker has an events object");
+ t.eq(layer.markers[0].events.listeners['click'].length, 1, "Marker events has one object");
+ layer.markers[0].events.triggerEvent('click', event);
+ t.eq(map.popups.length, 1, "Popup opened correctly");
+ layer.markers[1].events.triggerEvent('click', event);
+ t.eq(map.popups.length, 1, "1st popup gone, 2nd Popup opened correctly");
+ //Safari 3 separates style overflow into overflow-x and overflow-y
+ var prop = (OpenLayers.BROWSER_NAME == 'safari') ? 'overflowX' : 'overflow';
+ t.eq(map.popups[0].contentDiv.style[prop],"auto", "default Popup overflow correct");
+ });
+ }
+ function test_Layer_Text_overflow (t) {
+ t.plan( 4 );
+ layer = new OpenLayers.Layer.Text('Test Layer', { location: datafile_overflow });
+ var map = new OpenLayers.Map('map');
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0),0);
+ var event = {};
+ t.delay_call( 1, function() {
+ layer.markers[0].events.triggerEvent('click', event);
+ t.eq(map.popups.length, 1, "Popup opened correctly");
+ //Safari 3 separates style overflow into overflow-x and overflow-y
+ var prop = (OpenLayers.BROWSER_NAME == 'safari') ? 'overflowX' : 'overflow';
+ t.eq(map.popups[0].contentDiv.style[prop],"auto", "Popup overflow read from file");
+ layer.markers[1].events.triggerEvent('click', event);
+ t.eq(map.popups.length, 1, "1st popup gone, 2nd Popup opened correctly");
+ //Safari 3 separates style overflow into overflow-x and overflow-y
+ var prop = (OpenLayers.BROWSER_NAME == 'safari') ? 'overflowX' : 'overflow';
+ t.eq(map.popups[0].contentDiv.style[prop],"hidden", "Popup overflow read from file");
+ });
+ }
+ function test_Layer_Text_events_nopopups (t) {
+ t.plan( 4 );
+ layer = new OpenLayers.Layer.Text('Test Layer', { location: datafile });
+ var map = new OpenLayers.Map('map');
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0),0);
+ var event = {};
+ t.delay_call( 1, function() {
+ t.ok(layer.markers[0].events, "First marker has an events object");
+ t.eq(layer.markers[0].events.listeners['click'], undefined, "Marker events has no object");
+ layer.markers[0].events.triggerEvent('click', event);
+ t.eq(map.popups.length, 0, "no popup on first marker");
+ layer.markers[1].events.triggerEvent('click', event);
+ t.eq(map.popups.length, 0, "no popup on second marker");
+ });
+ }
+ function test_Layer_Text_loadend_Event(t) {
+ t.plan(2);
+ layer = new OpenLayers.Layer.Text('Test Layer', {location:datafile});
+ t.delay_call(2, function() {
+ layer.events.register('loadend', layer, function() {
+ t.ok(true, "Loadend event fired");
+ });
+ layer.parseData({
+ 'responseText':''
+ });
+ t.ok(true, "Parsing data didn't fail");
+ });
+ }
+
+ function test_Layer_Text_destroy (t) {
+ t.plan( 1 );
+ layer = new OpenLayers.Layer.Text('Test Layer');
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+ layer.destroy();
+ t.eq( layer.map, null, "layer.map is null after destroy" );
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width:500px; height:500px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/TileCache.html b/misc/openlayers/tests/Layer/TileCache.html
new file mode 100644
index 0000000..2bb88f5
--- /dev/null
+++ b/misc/openlayers/tests/Layer/TileCache.html
@@ -0,0 +1,203 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+
+
+ function test_Layer_TileCache_constructor (t) {
+ t.plan( 1 );
+
+ var name = 'Test Layer';
+ var url = "http://labs.metacarta.com/wms-c/Basic.py/";
+ var layername = "basic";
+ var options = {'type':'png'};
+
+ var layer = new OpenLayers.Layer.TileCache(name, url, layername, options);
+ t.ok( layer instanceof OpenLayers.Layer.TileCache, "returns OpenLayers.Layer.TileCache object" );
+ layer.destroy();
+ }
+
+ function test_Layer_TileCache_clone(t) {
+ t.plan(3);
+
+ var name = 'Test Layer';
+ var url = "http://labs.metacarta.com/wms-c/Basic.py/";
+ var layername = "basic";
+ var options = {'type':'png'};
+ var layer = new OpenLayers.Layer.TileCache(name, url, layername, options);
+
+ var clone = layer.clone();
+ t.eq(layer.name, clone.name, "clone() correctly copy the 'name' property");
+ t.eq(layer.url, clone.url, "clone() correctly copy the 'url' property");
+ t.eq(layer.layername, clone.layername, "clone() correctly copy the 'layername' property");
+ clone.destroy();
+ layer.destroy();
+ }
+
+ function test_Layer_TileCache_clearTiles (t) {
+ t.plan( 1 );
+ var name = 'Test Layer';
+ var url = "http://labs.metacarta.com/wms-c/Basic.py/";
+ var layername = "basic";
+ var options = {'type':'png'};
+ var layer = new OpenLayers.Layer.TileCache(name, url, layername, options);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+
+ map.setCenter(new OpenLayers.LonLat(0,0));
+
+ //grab a reference to one of the tiles
+ var tile = layer.grid[0][0];
+
+ layer.clearGrid();
+
+ t.ok( layer.grid != null, "layer.grid does not get nullified" );
+ map.destroy();
+ }
+
+
+ function test_Layer_TileCache_getTileCacheBounds(t) {
+ t.plan( 1 );
+
+ var name = 'Test Layer';
+ var url = "http://labs.metacarta.com/wms-c/Basic.py/";
+ var layername = "basic";
+ var options = {'type':'png'};
+ var layer = new OpenLayers.Layer.TileCache(name, url, layername, options);
+
+ var bl = { bounds: new OpenLayers.Bounds(1,2,2,3)};
+ var tr = { bounds: new OpenLayers.Bounds(2,3,3,4)};
+ layer.grid = [ [6, tr],
+ [bl, 7]];
+
+ var bounds = layer.getTilesBounds();
+
+ var testBounds = new OpenLayers.Bounds(1,2,3,4);
+
+ t.ok( bounds.equals(testBounds), "getTileCacheBounds() returns correct bounds")
+
+ }
+
+ function test_Layer_TileCache_getResolution(t) {
+ t.plan( 1 );
+
+ var name = 'Test Layer';
+ var url = "http://labs.metacarta.com/wms-c/Basic.py/";
+ var layername = "basic";
+ var options = {'type':'png', maxResolution: 180/256};
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.TileCache(name, url, layername, options);
+ map.addLayer(layer);
+
+ map.zoom = 5;
+
+ t.eq( layer.getResolution(), 0.02197265625, "getResolution() returns correct value");
+ map.destroy();
+ }
+
+ function test_Layer_TileCache_getZoomForExtent(t) {
+ t.plan( 2 );
+ var bounds, zoom;
+
+ var name = 'Test Layer';
+ var url = "http://labs.metacarta.com/wms-c/Basic.py/";
+ var layername = "basic";
+ var options = {'type':'png', maxResolution: 180/256};
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.TileCache(name, url, layername, options);
+ map.addLayer(layer);
+
+ bounds = new OpenLayers.Bounds(10,10,12,12);
+ zoom = layer.getZoomForExtent(bounds);
+
+ t.eq( zoom, 7, "getZoomForExtent() returns correct value");
+
+ bounds = new OpenLayers.Bounds(10,10,100,100);
+ zoom = layer.getZoomForExtent(bounds);
+
+ t.eq( zoom, 1, "getZoomForExtent() returns correct value");
+ map.destroy();
+ }
+
+ function test_Layer_TileCache_getURL(t) {
+
+ t.plan(2);
+
+ var map = new OpenLayers.Map('map');
+ var name = 'Test Layer';
+ var url = "http://labs.metacarta.com/wms-c/Basic.py/";
+ var layername = "basic";
+ var options = {'layername':'basic', 'format':'image/jpg', maxResolution: 180/256};
+ var layer = new OpenLayers.Layer.TileCache(name, url, layername, options);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 9);
+ var tileurl = layer.getURL(new OpenLayers.Bounds(3.515625,45,4.21875,45.703125));
+ t.eq(tileurl, "http://labs.metacarta.com/wms-c/Basic.py/basic/09/000/000/522/000/000/384.jpeg", "Tile URL is correct");
+
+ layer.url = ["http://tilecache1/", "http://tilecache2/", "http://tilecache3/"];
+ tileurl = layer.getURL(new OpenLayers.Bounds(3.515625,45,4.21875,45.703125));
+ t.eq(tileurl, "http://tilecache2/basic/09/000/000/522/000/000/384.jpeg", "Tile URL is deterministic");
+ map.destroy();
+ }
+
+ function test_Layer_TileCache_serverResolutions(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map('map', {
+ resolutions: [13,11]
+ });
+
+ var layer = new OpenLayers.Layer.TileCache('tc layer', '', 'basic');
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 1);
+
+ var tileurl = layer.getURL(new OpenLayers.Bounds(0,0,0,0));
+ var level = parseInt(tileurl.split('/')[2]);
+ t.eq(map.getZoom(), level, "Tile zoom level is correct without serverResolutions");
+
+ layer.serverResolutions = [14,13,12,11,10];
+ tileurl = layer.getURL(new OpenLayers.Bounds(0,0,0,0));
+ level = parseInt(tileurl.split('/')[2]);
+ var gotLevel = OpenLayers.Util.indexOf(layer.serverResolutions, map.getResolution());
+ t.eq(gotLevel, level, "Tile zoom level is correct with serverResolutions");
+
+ map.destroy();
+ }
+
+ function test_Layer_TileCache_destroy (t) {
+
+ t.plan( 3 );
+
+ var name = 'Test Layer';
+ var url = "http://labs.metacarta.com/wms-c/Basic.py/";
+ var layername = "basic";
+ var options = {'layername':'basic', 'format':'image/jpg', maxResolution: 180/256};
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.TileCache(name, url, layername, options);
+ map.addLayer(layer);
+ layer.destroy();
+ t.eq( layer.grid, null, "layer.grid is null after destroy" );
+ t.eq( layer.tileSize, null, "layer.tileSize is null after destroy" );
+
+
+ //test with tile creation
+ layer = new OpenLayers.Layer.TileCache(name, url, options);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+ //grab a reference to one of the tiles
+ var tile = layer.grid[0][0];
+
+ layer.destroy();
+
+ t.ok( layer.grid == null, "tiles appropriately destroyed");
+ map.destroy();
+ }
+
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/UTFGrid.html b/misc/openlayers/tests/Layer/UTFGrid.html
new file mode 100644
index 0000000..872d796
--- /dev/null
+++ b/misc/openlayers/tests/Layer/UTFGrid.html
@@ -0,0 +1,131 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <script>
+ /**
+ * Because browsers that implement requestAnimationFrame may not execute
+ * animation functions while a window is not displayed (e.g. in a hidden
+ * iframe as in these tests), we mask the native implementations here. The
+ * native requestAnimationFrame functionality is tested in Util.html and
+ * in PanZoom.html (where a popup is opened before panning). The panTo tests
+ * here will test the fallback setTimeout implementation for animation.
+ */
+ window.requestAnimationFrame =
+ window.webkitRequestAnimationFrame =
+ window.mozRequestAnimationFrame =
+ window.oRequestAnimationFrame =
+ window.msRequestAnimationFrame = null;
+ </script>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var map, layer;
+ function setUp() {
+ layer = new OpenLayers.Layer.UTFGrid({
+ url: "../data/utfgrid/world_utfgrid/${z}/${x}/${y}.json",
+ isBaseLayer: true,
+ utfgridResolution: 4
+ });
+ map = new OpenLayers.Map({
+ div: "map",
+ projection: "EPSG:900913",
+ layers: [layer],
+ center: [0, 0],
+ zoom: 1,
+ tileManager: null
+ });
+ }
+
+ function tearDown() {
+ map.destroy();
+ map = null;
+ layer = null;
+ }
+
+ function test_constructor(t) {
+ t.plan(4);
+
+ var layer = new OpenLayers.Layer.UTFGrid({
+ name: "foo",
+ url: "path/to/tiles/${z}/${x}/${y}",
+ utfgridResolution: 8
+ });
+ t.ok(layer instanceof OpenLayers.Layer.UTFGrid, "utfgrid instance");
+ t.eq(layer.name, "foo", "layer name");
+ t.eq(layer.url, "path/to/tiles/${z}/${x}/${y}", "layer url");
+ t.eq(layer.utfgridResolution, 8, "layer utfgridResolution");
+
+ layer.destroy();
+
+ }
+
+ function test_createBackBuffer(t) {
+ t.plan(1);
+ setUp();
+
+ var got;
+ try {
+ got = layer.createBackBuffer();
+ } catch (e) {
+ got = e;
+ } finally {
+ tearDown();
+ }
+ t.eq(got, undefined, "createBackBuffer returns undefined");
+ }
+
+ function test_clone(t) {
+ t.plan(3);
+ setUp();
+
+ var clone = layer.clone();
+ t.ok(layer instanceof OpenLayers.Layer.UTFGrid, "utfgrid instance");
+ t.eq(layer.url, "../data/utfgrid/world_utfgrid/${z}/${x}/${y}.json", "layer url");
+ t.eq(layer.utfgridResolution, 4, "layer utfgridResolution");
+ clone.destroy();
+
+ tearDown();
+ }
+
+ function test_getFeatureInfo(t) {
+ t.plan(2);
+ setUp();
+
+ // wait for tile loading to finish
+ t.delay_call(0.5, function() {
+ var loc = new OpenLayers.LonLat(-110, 45).transform("EPSG:4326", "EPSG:900913");
+ var info = layer.getFeatureInfo(loc);
+
+ t.eq(info.id, "207", "feature id");
+ t.eq(info.data, {POP2005: 299846449, NAME: "United States"}, "feature data");
+
+ tearDown();
+ });
+
+ }
+
+ function test_getFeatureId(t) {
+ t.plan(2);
+ setUp();
+
+ // wait for tile loading to finish
+ t.delay_call(0.5, function() {
+ var ca = new OpenLayers.LonLat(-110, 55).transform("EPSG:4326", "EPSG:900913");
+ var ru = new OpenLayers.LonLat(90, 75).transform("EPSG:4326", "EPSG:900913");
+
+ t.eq(layer.getFeatureId(ca), "24", "feature id for ca");
+ t.eq(layer.getFeatureId(ru), "245", "feature id for ru");
+
+ tearDown();
+ });
+
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="height: 256px; width: 512px"></div>
+</body>
+</html>
+
diff --git a/misc/openlayers/tests/Layer/Vector.html b/misc/openlayers/tests/Layer/Vector.html
new file mode 100644
index 0000000..aa3e2f8
--- /dev/null
+++ b/misc/openlayers/tests/Layer/Vector.html
@@ -0,0 +1,879 @@
+<html>
+<head>
+<script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var name = "Vector Layer";
+
+ function test_Layer_Vector_constructor(t) {
+ t.plan(5);
+
+ var options = {protocol: new OpenLayers.Protocol(),
+ strategies: [new OpenLayers.Strategy(), new OpenLayers.Strategy()]}
+ var layer = new OpenLayers.Layer.Vector(name, options);
+
+ t.ok(layer instanceof OpenLayers.Layer.Vector, "new OpenLayers.Layer.Vector returns correct object" );
+ t.eq(layer.name, name, "layer name is correctly set");
+ t.ok(layer.renderer.CLASS_NAME, "layer has a renderer");
+
+ t.ok((layer.name == layer.strategies[0].layer.name) &&
+ (layer.strategies[0].layer.name == layer.strategies[1].layer.name),
+ "setLayer was called on strategies");
+
+ options.renderers = [OpenLayers.Renderer.SVG, OpenLayers.Renderer.VML, OpenLayers.Renderer.Canvas];
+ layer.destroy();
+ layer = new OpenLayers.Layer.Vector(name, options);
+ t.ok(layer.renderer.CLASS_NAME, "layer has a renderer when providing a function");
+ layer.destroy();
+ }
+
+ function test_Layer_Vector_assignRenderer(t) {
+ t.plan(2);
+
+ // create a dummy class in the global name space
+ My = {
+ Custom: {
+ Renderer: {
+ Supported: OpenLayers.Class(OpenLayers.Renderer, {
+ supported: OpenLayers.Function.True,
+ CLASS_NAME: 'My.Custom.Renderer.Supported'
+ }),
+ NotSupported: OpenLayers.Class(OpenLayers.Renderer, {
+ supported: OpenLayers.Function.False,
+ CLASS_NAME: 'My.Custom.Renderer.NotSupported'
+ })
+ }
+ }
+ };
+ var layer = new OpenLayers.Layer.Vector('vector', {
+ renderers: [My.Custom.Renderer.NotSupported,
+ My.Custom.Renderer.Supported,
+ OpenLayers.Renderer.Canvas]
+ });
+ t.eq(layer.renderer.CLASS_NAME, 'My.Custom.Renderer.Supported',
+ 'layer has a valid renderer');
+
+ var layer = new OpenLayers.Layer.Vector('vector', {
+ renderers: ['SVG', 'VML', 'Canvas', My.Custom.Renderer.Supported]
+ });
+ t.ok(layer.renderer.CLASS_NAME != 'My.Custom.Renderer.Supported',
+ 'renderers can be strings as well');
+ }
+
+ function test_Layer_Vector_refresh(t) {
+ t.plan(4);
+
+ var obj = {"an": "object"};
+
+ var log;
+ var layer = new OpenLayers.Layer.Vector(name, {
+ eventListeners: {
+ refresh: function(o) {
+ log.obj = o;
+ }
+ }
+ });
+ inRange = false;
+ layer.calculateInRange = function() {
+ return inRange;
+ };
+
+ log = {};
+ inRange = false;
+ layer.visibility = false;
+ layer.refresh(obj);
+ t.eq(log.obj, undefined, "[false, false] refresh not triggered");
+
+ log = {};
+ inRange = true;
+ layer.visibility = false;
+ layer.refresh(obj);
+ t.eq(log.obj, undefined, "[true, false] refresh not triggered");
+
+ log = {};
+ inRange = false;
+ layer.visibility = true;
+ layer.refresh(obj);
+ t.eq(log.obj, undefined, "[false, true] refresh not triggered");
+
+ log = {};
+ inRange = true;
+ layer.visibility = true;
+ layer.refresh(obj);
+ t.ok(log.obj === obj, "[true, true] refresh triggered with correct arg");
+ }
+
+ function test_Layer_Vector_addFeatures(t) {
+ t.plan(8);
+
+ var layer = new OpenLayers.Layer.Vector(name);
+
+ var point = new OpenLayers.Geometry.Point(-111.04, 45.68);
+ var pointFeature = new OpenLayers.Feature.Vector(point);
+
+ layer.preFeatureInsert = function(feature) {
+ t.ok(feature == pointFeature, "OpenLayers.Layer.Vector.addFeatures calls preFeatureInsert with the right arg");
+ };
+ layer.onFeatureInsert = function(feature) {
+ t.ok(feature == pointFeature, "OpenLayers.Layer.Vector.addFeatures calls onFeatureInsert with the right arg");
+ };
+ layer.events.register('beforefeatureadded', null, function(obj) {
+ t.ok(pointFeature == obj.feature, "OpenLayers.Layer.Vector.addFeatures triggers beforefeatureadded with correct feature passed to callback");
+ });
+ layer.events.register('featureadded', null, function(obj) {
+ t.ok(pointFeature == obj.feature, "OpenLayers.Layer.Vector.addFeatures triggers featureadded with correct feature passed to callback");
+ });
+ layer.events.register('featuresadded', null, function(obj) {
+ t.ok(pointFeature == obj.features[0], "OpenLayers.Layer.Vector.addFeatures triggers featuresadded with correct features passed to callback");
+ });
+
+ layer.addFeatures([pointFeature]);
+
+ t.eq(layer.features.length, 1, "OpenLayers.Layer.Vector.addFeatures adds something to the array");
+ t.ok(layer.features[0] == pointFeature, "OpenLayers.Layer.Vector.addFeatures returns an array of features");
+
+ layer.preFeatureInsert = function(feature) {
+ t.fail("OpenLayers.Layer.Vector.addFeatures calls preFeatureInsert while it must not");
+ };
+ layer.onFeatureInsert = function(feature) {
+ t.fail("OpenLayers.Layer.Vector.addFeatures calls onFeatureInsert while it must not");
+ };
+ layer.events.register('beforefeatureadded', null, function(obj) {
+ t.fail("OpenLayers.Layer.Vector.addFeatures triggers beforefeatureadded while it must not");
+ });
+ layer.events.register('featureadded', null, function(obj) {
+ t.fail("OpenLayers.Layer.Vector.addFeatures triggers featureadded while it must not");
+ });
+ layer.events.register('featuresadded', null, function(obj) {
+ t.fail("OpenLayers.Layer.Vector.addFeatures triggers featuresadded while it must not");
+ });
+
+ layer.addFeatures([pointFeature], {silent: true});
+
+ var extent = layer.getDataExtent();
+ t.eq(extent.toBBOX(), "-111.04,45.68,-111.04,45.68", "extent from getDataExtent is correct");
+ }
+
+ function test_Layer_Vector_getFeature(t) {
+ t.plan(13);
+
+ var layer = new OpenLayers.Layer.Vector(name);
+ var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(-111.04, 45.68));
+
+ t.ok(layer.getFeatureById(feature.id) == null,
+ "OpenLayers.Layer.Vector.getFeatureById returns null while the layer is empty");
+ t.ok(layer.getFeatureByFid('my_fid') == null,
+ "OpenLayers.Layer.Vector.getFeatureByFid returns null while the layer is empty");
+
+ layer.addFeatures([feature]);
+
+ t.ok(layer.getFeatureByFid('my_fid') == null,
+ "OpenLayers.Layer.Vector.getFeatureByFid returns null on unset feature fid");
+
+ feature.fid = 'my_fid';
+
+ t.ok(layer.getFeatureById(feature.id) == feature,
+ "OpenLayers.Layer.Vector.getFeatureById returns the correct feature");
+ t.ok(layer.getFeatureByFid(feature.fid) == feature,
+ "OpenLayers.Layer.Vector.getFeatureByFid returns the correct feature");
+ t.ok(layer.getFeatureById('some_id_that_does_not_exist') == null,
+ "OpenLayers.Layer.Vector.getFeatureById returns null on non-existing feature id");
+ t.ok(layer.getFeatureByFid('some_fid_that_does_not_exist') == null,
+ "OpenLayers.Layer.Vector.getFeatureByFid returns null on non-existing feature fid");
+ t.ok(layer.getFeatureById(feature.fid) == null,
+ "OpenLayers.Layer.Vector.getFeatureById ignores the feature fid");
+ t.ok(layer.getFeatureByFid(feature.id) == null,
+ "OpenLayers.Layer.Vector.getFeatureByFid ignores the feature id");
+
+ t.ok(layer.getFeatureBy('id', feature.id) == feature,
+ "OpenLayers.Layer.Vector.getFeatureBy('id', ...) works like getFeatureById on existing feature id");
+ t.ok(layer.getFeatureBy('id', 'some_id_that_does_not_exist') == null,
+ "OpenLayers.Layer.Vector.getFeatureBy('id', ...) works like getFeatureById on non-existing feature id");
+ t.ok(layer.getFeatureBy('fid', feature.fid) == feature,
+ "OpenLayers.Layer.Vector.getFeatureBy('fid', ...) works like getFeatureByFid on existing feature fid");
+ t.ok(layer.getFeatureBy('fid', 'some_fid_that_does_not_exist') == null,
+ "OpenLayers.Layer.Vector.getFeatureBy('fid', ...) works like getFeatureByFid on non-existing feature fid");
+ }
+
+ function test_Layer_Vector_getFeaturesByAttribute(t) {
+ t.plan( 9 );
+ // setup layer
+ var layer = new OpenLayers.Layer.Vector(name);
+
+ // feature_1
+ var geometry_1 = new OpenLayers.Geometry.Point(-28.63, 153.64);
+ var attributes_1 = {
+ humpty: 'dumpty',
+ clazz: 1
+ };
+ var feature_1 = new OpenLayers.Feature.Vector(geometry_1, attributes_1);
+ feature_1.fid = 'f_01'; // to identify later
+
+ // feature_2
+ var geometry_2 = new OpenLayers.Geometry.Point(-27.48, 153.05);
+ var attributes_2 = {
+ // this feature has attribute humpty === undefined
+ clazz: '1'
+ };
+ var feature_2 = new OpenLayers.Feature.Vector(geometry_2, attributes_2);
+ feature_2.fid = 'f_02'; // to identify later
+
+ // feature_3
+ var geometry_3 = new OpenLayers.Geometry.Point(-33.74, 150.3);
+ var attributes_3 = {
+ humpty: 'foobar',
+ clazz: 1
+ };
+ var feature_3 = new OpenLayers.Feature.Vector(geometry_3, attributes_3);
+ feature_3.fid = 'f_03'; // to identify later
+
+ // Tests
+
+ // don't find anything... no features added
+ // 1 test
+ t.ok(layer.getFeaturesByAttribute('humpty', 'dumpty').length === 0,
+ "OpenLayers.Layer.Vector.getFeaturesByAttribute returns an empty array while the layer is empty");
+
+ layer.addFeatures([feature_1, feature_2, feature_3]);
+
+ // simple use case: find 1 feature with an attribute and matching value
+ // 2 tests
+ var dumptyResults = layer.getFeaturesByAttribute('humpty', 'dumpty');
+ t.ok(dumptyResults.length === 1,
+ "OpenLayers.Layer.Vector.getFeaturesByAttribute returns an array with one feature for attribute 'humpty' with value 'dumpty'");
+ t.ok(dumptyResults[0].fid === 'f_01',
+ "OpenLayers.Layer.Vector.getFeaturesByAttribute returns the correct feature with attribute 'humpty' set to 'dumpty'");
+
+ // simple use case: find 1 feature with an attribute and matching value
+ // and respect data types
+ // 2 tests
+ var strOneResults = layer.getFeaturesByAttribute('clazz', '1');
+ t.ok(strOneResults.length === 1,
+ "OpenLayers.Layer.Vector.getFeaturesByAttribute returns an array with one feature for attribute 'clazz' with value '1' (a string)");
+ t.ok(strOneResults[0].fid === 'f_02',
+ "OpenLayers.Layer.Vector.getFeaturesByAttribute returns the correct feature with attribute 'clazz' set to the string '1'");
+
+ // simple use case: find 2 features with an attribute and matching value
+ // and respect data types
+ // 2 tests
+ var numOneResults = layer.getFeaturesByAttribute('clazz', 1);
+ t.ok(numOneResults.length === 2,
+ "OpenLayers.Layer.Vector.getFeaturesByAttribute returns an array with two features for attribute 'clazz' with value 1 (a number)");
+ var bothFound = !!((numOneResults[0].fid === 'f_01' && numOneResults[1].fid === 'f_03') || (numOneResults[0].fid === 'f_03' && numOneResults[1].fid === 'f_01'));
+ t.ok(bothFound,
+ "OpenLayers.Layer.Vector.getFeaturesByAttribute returns the correct features with attribute 'clazz' set to the number 1");
+
+ // advanced use case: find the 1 feature, that has an attribute not set
+ var undefined;
+ var humptyNotSet = layer.getFeaturesByAttribute('humpty', undefined);
+ t.ok(humptyNotSet.length === 1,
+ "OpenLayers.Layer.Vector.getFeaturesByAttribute can be used to find features that have certain attributes not set");
+ t.ok(humptyNotSet[0].fid === 'f_02',
+ "OpenLayers.Layer.Vector.getFeaturesByAttribute found the correct featuren that has a certain attribute not set");
+ }
+
+ function test_Layer_Vector_getDataExtent(t) {
+ t.plan(1);
+ var layer = new OpenLayers.Layer.Vector(name);
+
+ var point = new OpenLayers.Geometry.Point(-111.04, 45.68);
+ var pointFeature = new OpenLayers.Feature.Vector(point);
+ layer.addFeatures([pointFeature]);
+ var point = new OpenLayers.Geometry.Point(-111.04, 5.68);
+ var pointFeature = new OpenLayers.Feature.Vector(point);
+ layer.addFeatures([pointFeature]);
+ var extent = layer.getDataExtent();
+ t.ok(extent.toBBOX() != layer.features[0].geometry.getBounds().toBBOX(), "extent from getDataExtent doesn't clobber first feature");
+ }
+
+ function test_Layer_Vector_getDataExtentEmpty(t) {
+ t.plan(1);
+ var layer = new OpenLayers.Layer.Vector(name);
+ layer.addFeatures([new OpenLayers.Feature.Vector(null), new OpenLayers.Feature.Vector(null)]);
+ var extent = layer.getDataExtent();
+ t.eq(extent, null, "We expect null to be returned if there are no features with a geometry");
+ }
+
+ function test_Layer_Vector_removeFeatures(t) {
+ t.plan(17);
+
+ var layer = new OpenLayers.Layer.Vector(name);
+ var features, log;
+
+ var point1 = new OpenLayers.Geometry.Point(-111.04, 45.68);
+ var pointFeature1 = new OpenLayers.Feature.Vector(point1);
+ var point2 = new OpenLayers.Geometry.Point(-111.14, 45.78);
+ var pointFeature2 = new OpenLayers.Feature.Vector(point2);
+
+ // 1 test
+ layer.addFeatures([pointFeature1, pointFeature2]);
+ features = layer.removeFeatures([pointFeature1]);
+ t.ok(layer.features.length == 1, "OpenLayers.Layer.Vector.removeFeatures removes a feature from the features array");
+
+ // 1 test
+ layer.addFeatures([pointFeature1.clone(), pointFeature2.clone()]);
+ layer.selectedFeatures.push(layer.features[0]);
+ layer.removeFeatures(layer.features[0]);
+ t.eq(layer.selectedFeatures, [], "Remove features removes selected features");
+
+ // 1 test
+ features = layer.removeFeatures(layer.features);
+ t.ok(layer.features.length == 0,
+ "OpenLayers.Layer.Vector.removeFeatures(layer.features) removes all feature from the features array");
+
+ // 4 tests
+ log = [];
+ layer.addFeatures([pointFeature1, pointFeature2]);
+ layer.events.register("featuresremoved", null, function(obj) {
+ log.push(obj);
+ });
+ layer.removeFeatures(layer.features);
+ t.eq(log.length, 1,
+ "\"featuresremoved\" triggered once [0]");
+ t.eq(log[0].features.length, 2,
+ "\"featuresremoved\" listener is passed two features [0]");
+ t.ok(log[0].features[0] == pointFeature1,
+ "\"featuresremoved\" listener is passed the correct feature at index 0 [0]");
+ t.ok(log[0].features[1] == pointFeature2,
+ "\"featuresremoved\" listener is passed the correct feature at index 1 [0]");
+ layer.events.remove("featuresremoved");
+
+ // 4 tests
+ log = [];
+ layer.addFeatures([
+ pointFeature1, pointFeature2,
+ pointFeature1.clone(), pointFeature2.clone()
+ ]);
+ layer.selectedFeatures.push(pointFeature1);
+ layer.selectedFeatures.push(pointFeature2);
+ layer.events.register("featuresremoved", null, function(obj) {
+ log.push(obj);
+ });
+ layer.removeFeatures(layer.selectedFeatures);
+ t.eq(log.length, 1,
+ "\"featuresremoved\" triggered once [1]");
+ t.eq(log[0].features.length, 2,
+ "\"featuresremoved\" listener is passed two features [1]");
+ t.ok(log[0].features[0] == pointFeature1,
+ "\"featuresremoved\" listener is passed the correct feature at index 0 [1]");
+ t.ok(log[0].features[1] == pointFeature2,
+ "\"featuresremoved\" listener is passed the correct feature at index 1 [1]");
+ layer.events.remove("featuresremoved");
+ layer.removeFeatures(layer.features);
+
+ // 6 tests
+ layer.events.register('beforefeatureremoved', null, function(obj) {
+ t.ok(pointFeature1 == obj.feature,
+ "OpenLayers.Layer.Vector.removeFeatures triggers beforefeatureremoved with correct feature passed to callback");
+ });
+ layer.events.register('featureremoved', null, function(obj) {
+ t.ok(pointFeature1 == obj.feature,
+ "OpenLayers.Layer.Vector.removeFeatures triggers featureremoved with correct feature passed to callback");
+ });
+ layer.events.register('featuresremoved', null, function(obj) {
+ t.ok(pointFeature1 == obj.features[0],
+ "OpenLayers.Layer.Vector.removeFeatures triggers featuresremoved with correct features passed to callback");
+ });
+ layer.addFeatures([pointFeature1]);
+ layer.removeFeatures([pointFeature1]);
+ layer.addFeatures([pointFeature1]);
+ layer.removeFeatures(layer.features);
+
+ // 0 test
+ layer.events.register('beforefeatureremoved', null, function(obj) {
+ t.fail("OpenLayers.Layer.Vector.removeFeatures triggers beforefeatureremoved while it must not");
+ });
+ layer.events.register('featureremoved', null, function(obj) {
+ t.fail("OpenLayers.Layer.Vector.removeFeatures triggers featureremoved while it must not");
+ });
+ layer.events.register('featuresremoved', null, function(obj) {
+ t.fail("OpenLayers.Layer.Vector.removeFeatures triggers featuresremoved while it must not");
+ });
+ layer.addFeatures([pointFeature1]);
+ layer.removeFeatures([pointFeature1], {silent: true});
+ }
+
+ function test_Layer_Vector_drawFeature(t) {
+ t.plan(7);
+ var layer = new OpenLayers.Layer.Vector("Test Layer", {isBaseLayer: true});
+ var map = new OpenLayers.Map('map', {
+ maxExtent: new OpenLayers.Bounds(-100, -100, 100, 100)
+ });
+ map.addLayer(layer);
+ var geometry = new OpenLayers.Geometry.Point(10, 10);
+ var feature = new OpenLayers.Feature.Vector(geometry);
+
+ var f, s;
+
+ // Bogus layer renderer needs some methods
+ // for functional tests.
+ layer.drawn = true;
+ layer.renderer = {
+ drawFeature: function(feature, style) {
+ f = feature;
+ s = style;
+ },
+ root: document.createElement("div"),
+ destroy: function() { },
+ eraseFeatures: function() {},
+ setExtent: function() {}
+ };
+
+
+ layer.drawFeature(feature);
+ t.ok(geometry.equals(f.geometry),
+ "calls layer.renderer.drawFeature() with feature.geometry");
+
+ feature.style = {foo: "bar"};
+ layer.drawFeature(feature);
+ t.eq(feature.style, s,
+ "calls layer.renderer.drawFeature() with feature.style");
+
+ feature.style = null;
+ layer.style = {foo: "bar"};
+ layer.drawFeature(feature);
+ t.eq(layer.style.foo, s.foo,
+ "given null feature style, uses layer style");
+
+ feature.style = {foo1: "bar1"};
+ layer.style = {foo2: "bar2"};
+ var customStyle = {foo: "bar"};
+ layer.drawFeature(feature, customStyle);
+ t.eq(customStyle.foo, s.foo,
+ "given a custom style, renders with that");
+
+ // the real renderer's drawFeature method is tested in Renderer.html
+ layer.renderer.drawFeature = function(feature) {
+ return(feature.geometry.getBounds().intersectsBounds(map.getExtent()));
+ }
+ // reset the drawn to null as if the layer had never been rendered
+ layer.drawn = null;
+
+ layer.drawFeature(feature);
+ t.ok(true, "Trying to draw a feature on an not drawn layer doesn't throw any error.");
+
+ layer.addFeatures([feature]);
+
+ map.setCenter(new OpenLayers.Bounds(0, 0, 0, 0), 6);
+ t.ok(layer.unrenderedFeatures[feature.id], "Did not render feature outside the viewport.");
+ map.panTo(new OpenLayers.LonLat(10, 10));
+ t.ok(!layer.unrenderedFeatures[feature.id], "Rendered feature inside the viewport.");
+
+ layer.features = [];
+ }
+
+ function test_deleted_state(t) {
+ t.plan(9);
+
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector();
+ map.addLayer(layer);
+ var feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(10, 10)
+ );
+ var log;
+ layer.renderer = {
+ drawFeature: function(f, s) {
+ log = {
+ feature: f,
+ style: s
+ };
+ },
+ destroy: function() {}
+ };
+
+ // draw feature with no state
+ layer.drawn = true;
+ layer.drawFeature(feature);
+ t.ok(log.feature === feature, "[no state] drawFeature called with correct feature");
+ t.ok(log.style.display !== "none", "[no state] drawFeature called with style display not none");
+
+ // draw feature with delete style
+ feature.state = OpenLayers.State.DELETE;
+ layer.drawFeature(feature);
+ t.ok(log.feature === feature, "[delete] drawFeature called with correct feature");
+ t.eq(log.style.display, "none", "[delete] drawFeature called with style display none");
+
+ // undelete the feature and redraw
+ delete feature.state;
+ delete feature.renderIntent;
+ layer.drawFeature(feature);
+ t.ok(log.feature === feature, "[undelete] drawFeature called with correct feature");
+ t.ok(log.style.display !== "none", "[undelete] drawFeature called with style display not none");
+
+ // change deleted style
+ layer.styleMap.styles["delete"] = new OpenLayers.Style({fillOpacity: 0.1});
+
+ // draw feature with delete style
+ feature.state = OpenLayers.State.DELETE;
+ layer.drawFeature(feature);
+ t.ok(log.feature === feature, "[draw deleted] drawFeature called with correct feature");
+ t.ok(log.style.display !== "none", "[draw deleted] drawFeature called with style display not none");
+ t.eq(log.style.fillOpacity, 0.1,"[draw deleted] drawFeature called with correct fill opacity");
+
+
+ }
+
+ function test_Layer_Vector_eraseFeatures(t) {
+ t.plan(2);
+ var layer = new OpenLayers.Layer.Vector("Test Layer");
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+ var geometry = new OpenLayers.Geometry.Point(10, 10);
+ var feature = new OpenLayers.Feature.Vector(geometry);
+
+ var f;
+ layer.renderer = {
+ eraseFeatures: function(features) {
+ f = features[0];
+ },
+ destroy: function() { }
+ };
+
+ layer.eraseFeatures([feature]);
+ t.ok(f, "calls layer.renderer.eraseFeatures");
+ t.ok(geometry.equals(f.geometry),
+ "calls layer.renderer.eraseFeatures() given an array of features");
+ }
+
+ function test_Layer_Vector_destroyFeatures (t) {
+ t.plan(8);
+ var layer = new OpenLayers.Layer.Vector(name);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+ var features = [], i;
+ for (i = 0; i < 5; i++) {
+ features.push(new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(0,0)));
+ }
+ layer.addFeatures(features);
+ t.eq(layer.features.length, 5, "addFeatures adds 5 features");
+ layer.selectedFeatures.push(features[0]);
+ layer.destroyFeatures();
+ t.eq(layer.features.length, 0, "destroyFeatures triggers removal");
+ t.eq(layer.selectedFeatures, [], "Destroy features removes selected features");
+ var allDestroyed = true;
+ for (i = 0; i < 5; i++) {
+ if(features[i].geometry) {
+ allDestroyed = false;
+ }
+ }
+ t.ok(allDestroyed, "destroyFeatures actually destroys features");
+ features = [];
+ for (i = 0; i < 5; i++) {
+ features.push(new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(0,0)));
+ }
+ layer.addFeatures(features);
+ layer.selectedFeatures.push(features[0]);
+ layer.selectedFeatures.push(features[1]);
+ layer.destroyFeatures([features[0], features[1]]);
+ t.eq(layer.features.length, 3, "destroyFeatures removes appropriate features");
+ t.eq(layer.selectedFeatures, [], "destroyFeatures removes appropriate selected features");
+ t.eq(features[0].geometry, null, "destroyFeatures destroys feature 0");
+ t.eq(features[1].geometry, null, "destroyFeatures destroys feature 1");
+ }
+
+ function test_Layer_Vector_destroy (t) {
+ t.plan(6);
+
+ var options = {protocol: new OpenLayers.Protocol(),
+ strategies: [new OpenLayers.Strategy(), new OpenLayers.Strategy()]}
+ var layer = new OpenLayers.Layer.Vector(name, options);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+ layer.destroy();
+ t.eq(layer.map, null, "layer.map is null after destroy");
+ t.ok(!layer.renderer, "layer.renderer is falsey");
+ var err;
+ try {
+ layer.getFeatureFromEvent({target: "map"});
+ } catch (ex) {
+ err = ex;
+ }
+ t.ok(err, "Error thrown when calling getFeatureFromEvent on destroyed layer");
+
+ t.eq(layer.protocol, null, "layer.protocol is null after destroy");
+ t.eq(layer.strategies, null, "layer.strategies is null after destroy");
+
+ // test that we can call layer.destroy a second time without trouble
+ try {
+ layer.destroy();
+ layer.destroy();
+ t.ok(true, "layer.destroy called twice without any issues");
+ } catch(err) {
+ t.fail("calling layer.destroy twice triggers exception: " + err + " in " + err.fileName + " line " + err.lineNumber);
+ }
+
+ }
+
+ function test_Layer_Vector_clone(t) {
+ t.plan(5);
+ var original = new OpenLayers.Layer.Vector(name, {dummyOption: "foo"});
+ original.addFeatures([new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,2), {foo: "bar"})]);
+ var clone = original.clone();
+ t.ok(clone instanceof OpenLayers.Layer.Vector, "clone is an instance of OpenLayers.Layer.Vector");
+ t.ok(clone.name, original.name, "clone has the same name as the original");
+ t.ok(clone.features[0] != original.features[0], "clone's feature does not equal the original's feature");
+ t.eq(clone.features[0].attributes.foo, original.features[0].attributes.foo, "clone's feature has the same attributes as the original's feature");
+ t.eq(clone.dummyOption, original.dummyOption, "clone's dummyOption equals the original's dummy option");
+ }
+
+ function test_Layer_Vector_externalGraphic(t) {
+ t.plan(11);
+ var layer = new OpenLayers.Layer.Vector("Test Layer", {isBaseLayer: true});
+ var renderer = layer.renderer;
+ var map = new OpenLayers.Map('map');
+ map.addLayers([layer]);
+
+ var geometryX = 10;
+ var geometryY = 10;
+ var geometry = new OpenLayers.Geometry.Point(geometryX, geometryY);
+ var feature = new OpenLayers.Feature.Vector(geometry);
+
+ map.zoomToMaxExtent();
+
+ var customStyle1 = new Object({
+ externalGraphic: 'test.png',
+ pointRadius: 10
+ });
+ var customStyle2 = new Object({
+ externalGraphic: 'test.png',
+ graphicWidth: 12
+ });
+ var customStyle3 = new Object({
+ externalGraphic: 'test.png',
+ graphicHeight: 14
+ });
+ var customStyle4 = new Object({
+ externalGraphic: 'test.png',
+ graphicWidth: 24,
+ graphicHeight: 16
+ });
+ var customStyle5 = new Object({
+ externalGraphic: 'test.png',
+ graphicWidth: 24,
+ graphicOpacity: 1
+ });
+ var customStyle6 = new Object({
+ externalGraphic: 'test.png',
+ graphicWidth: 24,
+ graphicHeight: 16,
+ graphicXOffset: -24,
+ graphicYOffset: -16
+ });
+
+ var root = renderer.vectorRoot;
+ if (layer.renderer.CLASS_NAME == 'OpenLayers.Renderer.SVG') {
+ feature.style = customStyle1;
+ layer.drawFeature(feature);
+ t.eq(root.firstChild.getAttributeNS(null, 'width'),
+ (2*customStyle1.pointRadius).toString(),
+ "given a pointRadius, width equals 2*pointRadius");
+ t.eq(root.firstChild.getAttributeNS(null, 'height'),
+ (2*customStyle1.pointRadius).toString(),
+ "given a pointRadius, height equals 2*pointRadius");
+ feature.style = customStyle2;
+ layer.drawFeature(feature);
+ t.eq(root.firstChild.getAttributeNS(null, 'width'),
+ root.firstChild.getAttributeNS(null, 'height'),
+ "given a graphicWidth, width equals height");
+ t.eq(root.firstChild.getAttributeNS(null, 'width'),
+ customStyle2.graphicWidth.toString(),
+ "width is set correctly");
+ feature.style = customStyle3;
+ layer.drawFeature(feature);
+ t.eq(root.firstChild.getAttributeNS(null, 'height'),
+ root.firstChild.getAttributeNS(null, 'width'),
+ "given a graphicHeight, height equals width");
+ t.eq(root.firstChild.getAttributeNS(null, 'height'),
+ customStyle3.graphicHeight.toString(),
+ "height is set correctly");
+ feature.style = customStyle4;
+ layer.drawFeature(feature);
+ t.eq(root.firstChild.getAttributeNS(null, 'height'),
+ customStyle4.graphicHeight.toString(),
+ "given graphicHeight and graphicWidth, both are set: height");
+ t.eq(root.firstChild.getAttributeNS(null, 'width'),
+ customStyle4.graphicWidth.toString(),
+ "given graphicHeight and graphicWidth, both are set: width");
+ feature.style = customStyle5;
+ layer.drawFeature(feature);
+ // we use startsWith here as some browsers (at least Safari 3 and FireFox 4)
+ // do not append a semi-colon to the opacity string
+ t.ok(OpenLayers.String.startsWith(
+ root.firstChild.getAttributeNS(null, 'style'),
+ "opacity: " + customStyle5.graphicOpacity.toString()),
+ "graphicOpacity correctly set");
+ feature.style = customStyle6;
+ layer.drawFeature(feature);
+ var x = geometryX / renderer.getResolution() + renderer.left;
+ var y = geometryY / renderer.getResolution() - renderer.top;
+ // SVG setStyle() gets x and y using getAttributeNS(), which returns
+ // a value with only 3 decimal digits. To mimic this we use toFixed(3) here
+ x = x.toFixed(3);
+ y = y.toFixed(3);
+ // toFixed() returns a string
+ x = parseFloat(x);
+ y = parseFloat(y);
+ t.eq(root.firstChild.getAttributeNS(null, 'x'),
+ (x + customStyle6.graphicXOffset).toFixed().toString(),
+ "graphicXOffset correctly set");
+ t.eq(root.firstChild.getAttributeNS(null, 'y'),
+ (-y + customStyle6.graphicYOffset).toFixed().toString(),
+ "graphicYOffset correctly set");
+ }
+ if (layer.renderer.CLASS_NAME == 'OpenLayers.Renderer.VML') {
+ feature.style = customStyle1;
+ layer.drawFeature(feature);
+ t.eq(root.firstChild.style.width,
+ (2*customStyle1.pointRadius).toString()+'px',
+ "given a pointRadius, width equals 2*pointRadius");
+ t.eq(root.firstChild.style.height,
+ (2*customStyle1.pointRadius).toString()+'px',
+ "given a pointRadius, height equals 2*pointRadius");
+ feature.style = customStyle2;
+ layer.drawFeature(feature);
+ t.eq(root.firstChild.style.width,
+ root.firstChild.style.height,
+ "given a graphicWidth, width equals height");
+ t.eq(root.firstChild.style.width,
+ customStyle2.graphicWidth.toString()+'px',
+ "width is set correctly");
+ feature.style = customStyle3;
+ layer.drawFeature(feature);
+ t.eq(root.firstChild.style.height,
+ root.firstChild.style.width,
+ "given a graphicHeight, height equals width");
+ t.eq(root.firstChild.style.height,
+ customStyle3.graphicHeight.toString()+'px',
+ "height is set correctly");
+ feature.style = customStyle4;
+ layer.drawFeature(feature);
+ var left = parseInt(root.firstChild.style.left);
+ var top = parseInt(root.firstChild.style.top);
+ t.eq(root.firstChild.style.height,
+ customStyle4.graphicHeight.toString()+'px',
+ "given graphicHeight and graphicWidth, both are set: height");
+ t.eq(root.firstChild.style.width,
+ customStyle4.graphicWidth.toString()+'px',
+ "given graphicHeight and graphicWidth, both are set: width");
+ feature.style = customStyle5;
+ layer.renderer.clear();
+ layer.drawFeature(feature);
+ var fill = root.firstChild.getElementsByTagName("v:fill")[0];
+ var opacity;
+ if(fill) {
+ opacity = fill.getAttribute('opacity');
+ }
+ if(opacity === undefined) {
+ fill = root.firstChild.getElementsByTagName("fill")[0];
+ opacity = fill.getAttribute('opacity');
+ }
+ t.eq(opacity,
+ customStyle5.graphicOpacity,
+ "graphicOpacity correctly set");
+ feature.style = customStyle6;
+ layer.drawFeature(feature);
+ var offsetLeft = parseInt(root.firstChild.style.left);
+ var offsetTop = parseInt(root.firstChild.style.top);
+ t.eq((offsetLeft-left)*2, customStyle6.graphicXOffset, "graphicXOffset correctly set");
+ t.eq((top-offsetTop)*2, customStyle6.graphicYOffset, "graphicYOffset correctly set");
+
+ }
+ }
+
+ function test_removeLayer_drawFeature(t) {
+ // test behaviour when features are redrawn while
+ // the layer has been removed from the map
+
+ t.plan(1);
+
+ // set up
+
+ var map, layer, feature;
+
+ map = new OpenLayers.Map("map");
+ map.addLayer(new OpenLayers.Layer("base", {isBaseLayer: true}));
+
+ layer = new OpenLayers.Layer.Vector("vector");
+ map.addLayer(layer);
+
+ map.zoomToMaxExtent();
+
+ feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(1.0, 1.0));
+ layer.addFeatures(feature);
+
+ // test
+
+ map.removeLayer(layer);
+ layer.drawFeature(feature);
+ layer.drawFeature(feature);
+ map.addLayer(layer);
+
+ var count = 0, node;
+ while(node = document.getElementById(feature.geometry.id)) {
+ node.parentNode.removeChild(node);
+ count++;
+ }
+
+ t.eq(count, 1, "one geometry added, one geometry removed");
+
+ // tear down
+
+ map.destroy();
+ }
+
+ function test_vectorBeforeFeatureAddedVeto(t) {
+ t.plan( 4 );
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.Vector("");
+ map.addLayer(layer);
+ var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry(1.0, 1.0));
+ layer.addFeatures([feature]);
+
+ var addedFeatures = [];
+ var beforefeatureadded_veto = function(evt) { return false; };
+ layer.events.register("beforefeatureadded", layer, beforefeatureadded_veto);
+ layer.events.register("featuresadded", layer, function(evt) {
+ if (evt.features) {
+ for (var i = 0; i < evt.features.length; i++) {
+ addedFeatures.push(evt.features[i]);
+ }
+ }
+ });
+
+ var blankFeatures = [
+ new OpenLayers.Feature.Vector(new OpenLayers.Geometry(1.0, 1.0)),
+ new OpenLayers.Feature.Vector(new OpenLayers.Geometry(1.0, 1.0))];
+ layer.addFeatures(blankFeatures);
+
+ t.eq(layer.features.length, 1,
+ "features not added to layer after beforefeatureadded veto");
+ t.eq(addedFeatures.length, 0,
+ "no features sent to featuresadded on feature veto");
+
+ addedFeatures = [];
+
+ layer.events.unregister("beforefeatureadded", layer, beforefeatureadded_veto);
+ beforefeatureadded_veto = function(evt) { return true; };
+ layer.events.register("beforefeatureadded", layer, beforefeatureadded_veto);
+
+ layer.addFeatures(blankFeatures);
+
+ t.eq(layer.features.length, 3,
+ "features added to layer as expected");
+ t.eq(addedFeatures.length, 2,
+ "featuresadded event received expected number of features");
+ }
+
+
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/Vector/RootContainer.html b/misc/openlayers/tests/Layer/Vector/RootContainer.html
new file mode 100644
index 0000000..aa92923
--- /dev/null
+++ b/misc/openlayers/tests/Layer/Vector/RootContainer.html
@@ -0,0 +1,63 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+ var layer, map;
+
+ function test_RootContainer_collectResetRoots(t) {
+
+ map = new OpenLayers.Map("map");
+ var layer1 = new OpenLayers.Layer.Vector("layer1");
+ var layer2 = new OpenLayers.Layer.Vector("layer2");
+ layer = new OpenLayers.Layer.Vector.RootContainer("layer_1_2", {
+ layers: [layer1, layer2]
+ });
+
+ // we cannot test this with a renderer that does not hava a rendererRoot
+ var plan = layer.renderer.rendererRoot ? 4 : 0;
+ t.plan(plan);
+ if(plan == 0) {
+ return;
+ }
+
+ var numRoots = layer.renderer.rendererRoot.childNodes.length;
+
+ // addLayers will call setMap() for layer, which will call collectRoots()
+ map.addLayers([layer1, layer2, layer]);
+ t.eq(layer.renderer.rendererRoot.childNodes.length, numRoots * 3, "layer has correct number of renderer roots");
+ t.eq(layer1.renderer.rendererRoot.childNodes.length, 0, "layer1 has no own renderer root");
+
+ layer.resetRoots();
+ t.eq(layer.renderer.rendererRoot.childNodes.length, numRoots, "roots removed from container");
+ t.eq(layer1.renderer.rendererRoot.childNodes.length, numRoots, "root re-added to original layer");
+ }
+
+ function test_RootContainer_getFeatureFromEvent(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var layer1 = new OpenLayers.Layer.Vector("layer1");
+ var layer2 = new OpenLayers.Layer.Vector("layer2");
+ layer = new OpenLayers.Layer.Vector.RootContainer("layer_1_2", {
+ layers: [layer1, layer2]
+ });
+ map.addLayers([layer1, layer2, layer]);
+ var feature1 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,1));
+ var feature2 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,0));
+ layer1.addFeatures(feature1);
+ layer2.addFeatures(feature2);
+ t.eq(layer.getFeatureFromEvent({
+ srcElement: {
+ _featureId: feature1.id
+ }
+ }).id, feature1.id, "feature from layer1 found");
+ t.eq(layer.getFeatureFromEvent({srcElement: {
+ _featureId: feature2.id
+ }}).id, feature2.id, "feature from layer2 found");
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/WMS.html b/misc/openlayers/tests/Layer/WMS.html
new file mode 100644
index 0000000..b990f07
--- /dev/null
+++ b/misc/openlayers/tests/Layer/WMS.html
@@ -0,0 +1,583 @@
+<html>
+<head>
+ <script type="text/javascript">var oldAlert = window.alert, gMess; window.alert = function(message) {gMess = message; return true;};</script>
+ <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhR_wWLPFku8Ix9i2SXYRVK3e45q1BQUd_beF8dtzKET_EteAjPdGDwqpQ'></script>
+ <script type="text/javascript">window.alert = oldAlert;</script>
+<script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ // turn off animation frame handling, so we can check img urls in tests
+ delete OpenLayers.Layer.Grid.prototype.queueTileDraw;
+
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var layer;
+
+ var name = 'Test Layer';
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ var params = { map: '/mapdata/vmap_wms.map',
+ layers: 'basic',
+ format: 'image/jpeg'};
+
+ function test_Layer_WMS_constructor (t) {
+ t.plan( 15 );
+
+ var trans_format = "image/png";
+ if (OpenLayers.Util.alphaHack()) { trans_format = "image/gif"; }
+
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.WMS(name, url, params);
+ t.ok( layer instanceof OpenLayers.Layer.WMS, "new OpenLayers.Layer.WMS returns object" );
+ t.eq( layer.url, "http://octo.metacarta.com/cgi-bin/mapserv", "layer.url is correct (HTTPRequest inited)" );
+ t.eq( layer.params.MAP, "/mapdata/vmap_wms.map", "params passed in correctly uppercased" );
+
+ t.eq( layer.params.SERVICE, "WMS", "default params correclty uppercased and copied");
+
+ t.eq(layer.isBaseLayer, true, "no transparency setting, wms is baselayer");
+
+ params.TRANSPARENT = "true";
+ var layer2 = new OpenLayers.Layer.WMS(name, url, params);
+ t.eq(layer2.isBaseLayer, false, "transparency == 'true', wms is not baselayer");
+
+ params.TRANSPARENT = "TRUE";
+ var layer3 = new OpenLayers.Layer.WMS(name, url, params);
+ t.eq(layer3.isBaseLayer, false, "transparency == 'TRUE', wms is not baselayer");
+ t.eq(layer3.params.FORMAT, trans_format, "transparent = TRUE causes non-image/jpeg format");
+
+ params.TRANSPARENT = "TRuE";
+ var layer4 = new OpenLayers.Layer.WMS(name, url, params);
+ t.eq(layer4.isBaseLayer, false, "transparency == 'TRuE', wms is not baselayer");
+ t.eq(layer4.params.FORMAT, trans_format, "transparent = TRuE causes non-image/jpeg format");
+
+ params.TRANSPARENT = true;
+ var layer5 = new OpenLayers.Layer.WMS(name, url, params);
+ t.eq(layer5.isBaseLayer, false, "transparency == true, wms is not baselayer");
+ t.eq(layer5.params.FORMAT, trans_format, "transparent = true causes non-image/jpeg format");
+
+ params.TRANSPARENT = false;
+ var layer6 = new OpenLayers.Layer.WMS(name, url, params);
+ t.eq(layer6.isBaseLayer, true, "transparency == false, wms is baselayer");
+
+ params.TRANSPARENT = true;
+ var layer7 = new OpenLayers.Layer.WMS(name, url, params, {noMagic: true});
+ t.eq(layer7.params.FORMAT, "image/jpeg", "When using noMagic true image/jpeg will not be automagically switched to image/png or image/gif if transparent");
+
+ params.TRANSPARENT = true;
+ var layer8 = new OpenLayers.Layer.WMS(name, url, params, {noMagic: true});
+ t.eq(layer8.isBaseLayer, true, "When using noMagic then transparent means the wms layer is not automagically changed to not being a baselayer");
+
+ params.TRANSPARENT = false;
+
+ }
+
+ function test_Layer_WMS_addtile (t) {
+ t.plan( 6 );
+
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.WMS(name, url, params);
+ var map = new OpenLayers.Map('map', {tileManager: null});
+ map.addLayer(layer);
+ var pixel = new OpenLayers.Pixel(5,6);
+ var tile = layer.addTile(new OpenLayers.Bounds(1,2,3,4), pixel);
+ tile.draw();
+
+ var img = tile.imgDiv;
+ var tParams = OpenLayers.Util.extend({},
+ OpenLayers.Util.upperCaseObject(params));
+ tParams = OpenLayers.Util.extend(tParams, {
+ BBOX: [1,2,3,4],
+ WIDTH: "256", HEIGHT: "256"
+ });
+ t.eq( tile.url,
+ layer.getFullRequestString(tParams),
+ "image src is created correctly via addtile" );
+ t.eq( tile.getTile().style.top, "6px", "image top is set correctly via addtile" );
+ t.eq( tile.getTile().style.left, "5px", "image top is set correctly via addtile" );
+
+ var firstChild = layer.div.firstChild;
+ t.eq( firstChild.nodeName.toLowerCase(), "img", "div first child is an image object" );
+ t.ok( firstChild == img, "div first child is correct image object" );
+ t.eq( tile.position.toString(), "x=5,y=6", "Position of tile is set correctly." );
+ map.destroy();
+ }
+
+ function test_Layer_WMS_bboxEncoding (t) {
+ t.plan( 6 );
+
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.WMS(name, url, params, {encodeBBOX:true});
+ var map = new OpenLayers.Map('map', {tileManager: null});
+ map.addLayer(layer);
+ var pixel = new OpenLayers.Pixel(5,6);
+ var tile = layer.addTile(new OpenLayers.Bounds(1,2,3,4), pixel);
+ tile.draw();
+
+ var img = tile.imgDiv;
+ var tParams = OpenLayers.Util.extend({},
+ OpenLayers.Util.upperCaseObject(params));
+ tParams = OpenLayers.Util.extend(tParams, {
+ BBOX: "1,2,3,4",
+ WIDTH: "256", HEIGHT: "256"
+ });
+ t.eq( tile.url,
+ layer.getFullRequestString(tParams),
+ "image src is created correctly via addtile" );
+ t.eq( tile.getTile().style.top, "6px", "image top is set correctly via addtile" );
+ t.eq( tile.getTile().style.left, "5px", "image top is set correctly via addtile" );
+
+ var firstChild = layer.div.firstChild;
+ t.eq( firstChild.nodeName.toLowerCase(), "img", "div first child is an image object" );
+ t.ok( firstChild, img, "div first child is correct image object" );
+ t.eq( tile.position.toString(), "x=5,y=6", "Position of tile is set correctly." );
+ map.destroy();
+ }
+
+ function test_Layer_WMS_inittiles (t) {
+ t.plan( 2 );
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS(name, url, params, {buffer:2});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0),5);
+ t.eq( layer.grid.length, 8, "Grid rows is correct." );
+ t.eq( layer.grid[0].length, 7, "Grid cols is correct." );
+ map.destroy();
+ }
+
+ function test_Layer_WMS_clone (t) {
+ t.plan(4);
+
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ var options = {tileSize: new OpenLayers.Size(500,50)};
+ var map = new OpenLayers.Map('map', options);
+ layer = new OpenLayers.Layer.WMS(name, url, params);
+ map.addLayer(layer);
+
+ layer.grid = [ [6, 7],
+ [8, 9]];
+
+ var clone = layer.clone();
+
+ t.ok( clone.grid != layer.grid, "clone does not copy grid");
+
+ t.ok( clone.tileSize.equals(layer.tileSize), "tileSize correctly cloned");
+
+ layer.tileSize.w += 40;
+
+ t.eq( clone.tileSize.w, 500, "changing layer.tileSize does not change clone.tileSize -- a fresh copy was made, not just copied reference");
+
+ t.eq( clone.alpha, layer.alpha, "alpha copied correctly");
+
+ layer.grid = null;
+ map.destroy();
+ }
+
+ function test_Layer_WMS_isBaseLayer(t) {
+ t.plan(3);
+
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.WMS(name, url, params);
+ t.ok( layer.isBaseLayer, "baselayer is true by default");
+
+ var newParams = OpenLayers.Util.extend({}, params);
+ newParams.transparent = "true";
+ layer = new OpenLayers.Layer.WMS(name, url, newParams);
+ t.ok( !layer.isBaseLayer, "baselayer is false when transparent is set to true");
+
+ layer = new OpenLayers.Layer.WMS(name, url, params, {isBaseLayer: false});
+ t.ok( !layer.isBaseLayer, "baselayer is false when option is set to false" );
+ }
+
+ function test_Layer_WMS_mergeNewParams (t) {
+ t.plan( 4 );
+
+ var map = new OpenLayers.Map("map");
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.WMS(name, url, params);
+
+ var newParams = { layers: 'sooper',
+ chickpeas: 'image/png'};
+
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ layer.redraw = function() {
+ t.ok(true, "layer is redrawn after new params merged");
+ }
+
+ layer.mergeNewParams(newParams);
+
+ t.eq( layer.params.LAYERS, "sooper", "mergeNewParams() overwrites well");
+ t.eq( layer.params.CHICKPEAS, "image/png", "mergeNewParams() adds well");
+
+ newParams.CHICKPEAS = 151;
+
+ t.eq( layer.params.CHICKPEAS, "image/png", "mergeNewParams() makes clean copy of hashtable");
+ map.destroy();
+ }
+
+ function test_Layer_WMS_getFullRequestString (t) {
+
+
+ t.plan( 4 );
+ var map = new OpenLayers.Map('map');
+ map.projection = "xx";
+ var tUrl = "http://octo.metacarta.com/cgi-bin/mapserv";
+ var tParams = { layers: 'basic',
+ format: 'image/png'};
+ var tLayer = new OpenLayers.Layer.WMS(name, tUrl, tParams);
+ map.addLayer(tLayer);
+ var str = tLayer.getFullRequestString();
+ var tParams = {
+ LAYERS: "basic", FORMAT: "image/png", SERVICE: "WMS",
+ VERSION: "1.1.1", REQUEST: "GetMap", STYLES: "",
+ SRS: "xx"
+ };
+ t.eq(str,
+ tUrl + "?" + OpenLayers.Util.getParameterString(tParams),
+ "getFullRequestString() adds SRS value");
+
+ map.removeLayer(tLayer);
+ tLayer.projection = "none";
+ map.addLayer(tLayer);
+ str = tLayer.getFullRequestString();
+ delete tParams['SRS'];
+ t.eq(str,
+ tUrl + "?" + OpenLayers.Util.getParameterString(tParams),
+ "getFullRequestString() by default does *not* add SRS value if projection is 'none'");
+ map.destroy();
+
+ map = new OpenLayers.Map("map", {projection: "EPSG:4326"});
+ var layerProj = new OpenLayers.Projection("FOO", {
+ equals: function() {return true},
+ getCode: function() {return "FOO"}
+ });
+ tLayer = new OpenLayers.Layer.WMS(name, tUrl, tParams, {projection: layerProj});
+ map.addLayer(tLayer);
+ str = tLayer.getFullRequestString();
+ tParams.SRS = "FOO";
+ t.eq(str,
+ tUrl + "?" + OpenLayers.Util.getParameterString(tParams),
+ "getFullRequestString() uses the layer projection if it equals the map projection");
+ map.destroy();
+
+ map = new OpenLayers.Map("map", {projection: "EPSG:4326"});
+ map.addLayer(new OpenLayers.Layer(null, {isBaseLayer: true}));
+ tLayer = new OpenLayers.Layer.WMS(name, tUrl);
+ tLayer.map = map;
+ var error;
+ try {
+ tLayer.getFullRequestString();
+ error = false;
+ } catch(err) {
+ error = true;
+ }
+ t.ok(!error, "no error on getFullRequestString if layer has no projection");
+ map.destroy();
+
+ }
+
+ function test_setOpacity(t) {
+ t.plan(1);
+
+ var layer = new OpenLayers.Layer.WMS(
+ null, "/bogus/wms", {layers: "mylayer"}
+ );
+ var map = new OpenLayers.Map("map");
+ map.addLayer(layer);
+
+ map.zoomToMaxExtent();
+
+ layer.setOpacity(0.5);
+ t.delay_call(1, function() {
+ t.eq(parseFloat(layer.div.firstChild.style.opacity), 0.5, "opacity set");
+ map.destroy();
+ });
+ }
+
+
+ function test_Layer_WMS_noGutters (t) {
+ t.plan(2);
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS("no gutter layer", url, params, {gutter: 0});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+ var tile = layer.grid[0][0];
+ var request = layer.getURL(tile.bounds);
+ var args = OpenLayers.Util.getParameters(request);
+ t.eq(parseInt(args['WIDTH']),
+ tile.size.w,
+ "layer without gutter requests images that are as wide as the tile");
+ t.eq(parseInt(args['HEIGHT']),
+ tile.size.h,
+ "layer without gutter requests images that are as tall as the tile");
+
+ layer.destroy();
+ map.destroy();
+ }
+
+ function test_Layer_WMS_gutters (t) {
+ t.plan(2);
+ var gutter = 15;
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS("gutter layer", url, params, {gutter: gutter});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+ var tile = layer.grid[0][0];
+ var request = layer.getURL(tile.bounds);
+ var args = OpenLayers.Util.getParameters(request);
+ t.eq(parseInt(args['WIDTH']),
+ tile.size.w + (2 * gutter),
+ "layer with gutter requests images that are wider by twice the gutter");
+ t.eq(parseInt(args['HEIGHT']),
+ tile.size.h + (2 * gutter),
+ "layer with gutter requests images that are taller by twice the gutter");
+
+ layer.destroy();
+ map.destroy();
+
+ }
+
+ function test_maxExtent(t) {
+ t.plan(5);
+
+ var layer = new OpenLayers.Layer.WMS(
+ null, "http://example.com/wms",
+ {layers: "foo"},
+ {maxExtent: [-180, 0, 0, 90]}
+ );
+
+ t.ok(layer.maxExtent instanceof OpenLayers.Bounds, "(array) bounds instance");
+ t.eq(layer.maxExtent.left, -180, "(array) bounds left");
+ t.eq(layer.maxExtent.bottom, 0, "(array) bounds left");
+ t.eq(layer.maxExtent.right, 0, "(array) bounds right");
+ t.eq(layer.maxExtent.top, 90, "(array) bounds top");
+
+ layer.destroy();
+ }
+
+ function test_minExtent(t) {
+ t.plan(5);
+
+ var layer = new OpenLayers.Layer.WMS(
+ null, "http://example.com/wms",
+ {layers: "foo"},
+ {minExtent: [-180, 0, 0, 90]}
+ );
+
+ t.ok(layer.minExtent instanceof OpenLayers.Bounds, "(array) bounds instance");
+ t.eq(layer.minExtent.left, -180, "(array) bounds left");
+ t.eq(layer.minExtent.bottom, 0, "(array) bounds left");
+ t.eq(layer.minExtent.right, 0, "(array) bounds right");
+ t.eq(layer.minExtent.top, 90, "(array) bounds top");
+
+ layer.destroy();
+ }
+
+ function test_tileOrigin(t) {
+ t.plan(4);
+
+ var dummy = new OpenLayers.Layer(null, {isBaseLayer: true});
+ var unconstrained = new OpenLayers.Layer.WMS(
+ null, "http://example.com/wms",
+ {layers: "unconstrained"},
+ {isBaseLayer: false, buffer: 0}
+ );
+ var constrained = new OpenLayers.Layer.WMS(
+ null, "http://example.com/wms-c",
+ {layers: "constrained"},
+ {buffer: 0, isBaseLayer: false, tileOrigin: new OpenLayers.LonLat(-180, -90)}
+ );
+ var map = new OpenLayers.Map({
+ div: "map",
+ maxExtent: new OpenLayers.Bounds(-185, -95, 185, 95),
+ maxResolution: 1.40625,
+ layers: [dummy, unconstrained, constrained],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 1
+ });
+
+ t.eq(unconstrained.grid[1][0].bounds.bottom, -95, "unconstrained bottom correct");
+ t.eq(unconstrained.grid[1][0].bounds.left, -185, "unconstrained left correct");
+ t.eq(constrained.grid[1][0].bounds.bottom, -90, "constrained bottom correct");
+ t.eq(constrained.grid[1][0].bounds.left, -180, "constrained left correct");
+
+ map.destroy();
+
+ }
+
+ function test_Layer_WMS_destroy (t) {
+
+ t.plan( 1 );
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS(name, url, params);
+ map.addLayer(layer);
+
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+
+ //grab a reference to one of the tiles
+ var tile = layer.grid[0][0];
+
+ layer.destroy();
+
+ // checks to make sure superclass (grid) destroy() was called
+
+ t.ok( layer.grid == null, "grid set to null");
+ }
+
+ function test_customProjection(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map', {
+ units: 'm',
+ projection: new OpenLayers.Projection('EPSG:28992'),
+ maxExtent: new OpenLayers.Bounds(0, 300000, 300000, 6250000)
+ });
+ var layer = new OpenLayers.Layer.WMS(null, url, {layers: 'mylayer', version: '1.3.0'});
+ map.addLayer(layer);
+ var error = false;
+ try {
+ map.setCenter(new OpenLayers.LonLat(100000,300000), 5);
+ } catch(err) {
+ error = true;
+ }
+ t.ok(!error, "no error on getURL if layer has a custom projection and no defaults defined");
+ layer.destroy();
+ map.destroy();
+ }
+
+ function test_Layer_WMS_v13(t) {
+
+ t.plan(6);
+
+ var lon = 5;
+ var lat = 40;
+ var zoom = 5;
+ var map = new OpenLayers.Map( 'map' );
+ var layer = new OpenLayers.Layer.WMS(
+ "OpenLayers WMS",
+ "http://myserver.org/wms?",
+ {layers: 'mylayer', version: '1.3.0'},
+ {singleTile: true}
+ );
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+
+ var url = layer.getURL(map.getExtent());
+ var params = url.split("&");
+ var bbox;
+ for (var i=0, len=params.length; i<len; i++) {
+ var param = params[i];
+ var a = param.split('=');
+ if (a[0] === 'BBOX') {
+ bbox = a[1];
+ break;
+ }
+ }
+
+ t.eq(layer.params.CRS, "EPSG:4326", "In WMS 1.3 SRS is now CRS");
+ t.eq(bbox, "27.9150390625,-5.986328125,52.0849609375,15.986328125", "Axis sequence is lat lon for EPSG:4326 in WMS 1.3.0");
+
+ var layer2 = new OpenLayers.Layer.WMS(
+ "OpenLayers WMS",
+ "http://myserver.org/wms?",
+ {layers: 'mylayer', version: '1.1.1'},
+ {singleTile: true}
+ );
+ map.addLayer(layer2);
+
+ var url = layer2.getURL(map.getExtent());
+ var params = url.split("&");
+ var bbox;
+ for (var i=0, len=params.length; i<len; i++) {
+ var param = params[i];
+ var a = param.split('=');
+ if (a[0] === 'BBOX') {
+ bbox = a[1];
+ break;
+ }
+ }
+
+ t.eq(layer2.params.SRS, "EPSG:4326", "In WMS 1.1.1 parameter is called SRS");
+ t.eq(bbox, "-5.986328125,27.9150390625,15.986328125,52.0849609375", "Axis sequence is lon lat for EPSG:4326 in WMS 1.1.1");
+
+ map.destroy();
+
+ // CRS:84 has normal axis sequence (lon lat)
+ var map = new OpenLayers.Map( 'map', {projection: 'CRS:84'} );
+ var layer = new OpenLayers.Layer.WMS(
+ "OpenLayers WMS",
+ "http://myserver.org/wms?",
+ {layers: 'mylayer', version: '1.3.0'},
+ {singleTile: true}
+ );
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+
+ var url = layer.getURL(map.getExtent());
+ var params = url.split("&");
+ var bbox, exceptions;
+ for (var i=0, len=params.length; i<len; i++) {
+ var param = params[i];
+ var a = param.split('=');
+ if (a[0] === 'EXCEPTIONS') {
+ exceptions = a[1];
+ }
+ if (a[0] === 'BBOX') {
+ bbox = a[1];
+ }
+ }
+
+ t.eq(exceptions, "INIMAGE", "If not set, EXCEPTIONS should be INIMAGE for WMS 1.3");
+ t.eq(bbox, "-5.986328125,27.9150390625,15.986328125,52.0849609375", "Axis sequence for CRS:84 is lon lat");
+
+ map.destroy();
+
+ }
+
+ function test_transparent(t) {
+ t.plan(5);
+ var map = new OpenLayers.Map("map", {allOverlays: true});
+ var layer = new OpenLayers.Layer.WMS(
+ "OpenLayers WMS",
+ "http://myserver.org/wms?",
+ {layers: 'mylayer', transparent: true}
+ );
+ map.addLayer(layer);
+
+ t.eq(typeof layer.params.TRANSPARENT, "boolean", "transparent param is boolean");
+ t.ok(layer.getFullRequestString({}).indexOf("TRANSPARENT=TRUE") != -1, "Boolean transparent param value is uppercase TRUE");
+ layer.mergeNewParams({transparent: false});
+ t.ok(layer.getFullRequestString({}).indexOf("TRANSPARENT=FALSE") != -1, "Boolean transparent param value is uppercase FALSE");
+
+ layer.mergeNewParams({transparent: "true"});
+ t.eq(typeof layer.params.TRANSPARENT, "string", "transparent param is string");
+ t.ok(layer.getFullRequestString({}).indexOf("TRANSPARENT=true") != -1, "transparent param value passed as provided if String");
+
+ map.destroy();
+ }
+
+ function test_tileBounds(t) {
+ t.plan(3);
+
+ var map = new OpenLayers.Map("map", {projection: "EPSG:3857", zoomMethod: null});
+ var layer = new OpenLayers.Layer.WMS("wms", "../../img/blank.gif");
+ map.addLayer(layer);
+ map.setCenter([0, 0], 1);
+ map.pan(2, -100);
+ map.zoomIn();
+ t.eq(layer.grid[1][0].bounds, new OpenLayers.Bounds(-10018754.17, 0, 0, 10018754.17), "no floating point errors after zooming");
+ map.setCenter([0, 0], 14);
+ var bounds = layer.grid[0][0].bounds.clone();
+ map.pan(260, 520);
+ map.pan(-260, -520);
+ t.eq(layer.grid[0][0].bounds, bounds, "no floating point errors after dragging back and forth");
+ t.eq(bounds.right, 0, "0 is 0, and not some super small number");
+
+ map.destroy();
+ }
+
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/WMTS.html b/misc/openlayers/tests/Layer/WMTS.html
new file mode 100644
index 0000000..c6dcd4c
--- /dev/null
+++ b/misc/openlayers/tests/Layer/WMTS.html
@@ -0,0 +1,1491 @@
+<html>
+ <head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(6);
+ var xml = document.getElementById("capabilities").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+ var obj = new OpenLayers.Format.WMTSCapabilities().read(doc);
+
+ var layer0 = new OpenLayers.Layer.WMTS({
+ name: "GeoWebCache USA WMTS",
+ url: "http://example.com/geowebcache-1.2.2/service/wmts/",
+ layer: "arcgis-online-wms",
+ style: "",
+ matrixSet: "arcgis-online-wgs84",
+ format: "image/png",
+ isBaseLayer: false,
+ requestEncoding: "KVP",
+ maxResolution: 0.3521969032857032,
+ numZoomLevels: 7,
+ matrixIds: obj.contents.tileMatrixSets["arcgis-online-wgs84"].matrixIds
+ });
+
+ t.ok(layer0 instanceof OpenLayers.Layer.WMTS, "constructor returns instance of OpenLayers.Layer.WMTS");
+ t.eq(layer0.formatSuffix, "png", "formatSuffix is set correct based on 'format' parameter");
+
+ var layer1 = new OpenLayers.Layer.WMTS({
+ name: "Blue Marble WMTS",
+ url: "http://example.com/wmts/",
+ layer: "world",
+ style: "blue_marble",
+ matrixSet: "arcgis_online",
+ tileSize: new OpenLayers.Size(512, 512),
+ requestEncoding: "REST"
+ });
+
+ t.ok(layer1 instanceof OpenLayers.Layer.WMTS, "constructor returns instance of OpenLayers.Layer.WMTS");
+ t.eq(layer1.formatSuffix, "jpg", "formatSuffix is set correct based on default format");
+ t.eq(layer1.tileSize.w, 512.0, "tileSize w is set correctly");
+ t.eq(layer1.tileSize.h, 512.0, "tileSize h is set correctly");
+ }
+
+ function test_moveTo(t) {
+ t.plan(9);
+ var xml = document.getElementById("capabilities").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+ var obj = new OpenLayers.Format.WMTSCapabilities().read(doc);
+
+ var layer0 = new OpenLayers.Layer.WMTS({
+ name: "GeoWebCache USA WMTS",
+ url: "http://example.com/geowebcache-1.2.2/service/wmts/",
+ layer: "arcgis-online-wms",
+ style: "foo",
+ matrixSet: "arcgis-online-wgs84",
+ format: "image/png",
+ requestEncoding: "KVP",
+ maxResolution: 0.3521969032857032,
+ numZoomLevels: 7,
+ matrixIds: obj.contents.tileMatrixSets["arcgis-online-wgs84"].matrixIds
+ });
+
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer0);
+
+ map.setCenter(new OpenLayers.LonLat(-97, 38), 1);
+
+ t.ok((layer0.tileOrigin instanceof OpenLayers.LonLat), "tileOrigin is an instance of OpenLayers.LonLat");
+ t.ok((layer0.tileOrigin.lon == -180 && layer0.tileOrigin.lat == 90), "tileOrigin is set correctly");
+ t.ok((layer0.tileSize instanceof OpenLayers.Size), "tileSize is an instance of OpenLayers.Size");
+ t.eq(layer0.tileSize.w, 256.0, "tileSize w is set correctly");
+ t.eq(layer0.tileSize.h, 256.0, "tileSize h is set correctly");
+
+ map.setCenter(new OpenLayers.LonLat(-97.0, 38.0), 6);
+
+ t.eq(layer0.tileOrigin.lon, -175, "tileOrigin.lat updated correctly when zoom changed");
+ t.eq(layer0.tileOrigin.lat, 85, "tileOrigin.lat updated correctly when zoom changed");
+ t.eq(layer0.tileSize.w, 512.0, "tileSize w updated correctly when zoom changed");
+ t.eq(layer0.tileSize.h, 512.0, "tileSize h updated correctly when zoom changed");
+
+ map.destroy();
+ }
+
+ function test_clearTiles (t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map');
+
+ var layer1 = new OpenLayers.Layer.WMTS({
+ name: "Blue Marble WMTS",
+ url: "http://example.com/wmts/",
+ layer: "world",
+ style: "blue_marble",
+ matrixSet: "arcgis_online",
+ tileSize: new OpenLayers.Size(512, 512),
+ requestEncoding: "REST"
+ });
+
+ map.addLayer(layer1);
+ map.setCenter(new OpenLayers.LonLat(0,0));
+
+ //grab a reference to one of the tiles
+ var tile = layer1.grid[0][0];
+
+ layer1.clearGrid();
+
+ t.ok( layer1.grid != null, "layer.grid does not get nullified" );
+ map.destroy();
+ }
+
+ function test_getTilesBounds(t) {
+ t.plan(1);
+ var layer1 = new OpenLayers.Layer.WMTS({
+ name: "Blue Marble WMTS",
+ url: "http://example.com/wmts/",
+ layer: "world",
+ style: "blue_marble",
+ matrixSet: "arcgis_online",
+ tileSize: new OpenLayers.Size(512, 512),
+ requestEncoding: "REST"
+ });
+ var bl = {bounds: new OpenLayers.Bounds(1,2,2,3)};
+ var tr = {bounds: new OpenLayers.Bounds(2,3,3,4)};
+ layer1.grid = [[6, tr],[bl, 7]];
+ var bounds = layer1.getTilesBounds();
+ var testBounds = new OpenLayers.Bounds(1,2,3,4);
+ t.ok(bounds.equals(testBounds), "correct bounds");
+ }
+
+ function test_getResolution(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map');
+ var layer1 = new OpenLayers.Layer.WMTS({
+ name: "Blue Marble WMTS",
+ url: "http://example.com/wmts/",
+ layer: "world",
+ style: "blue_marble",
+ matrixSet: "arcgis_online",
+ tileSize: new OpenLayers.Size(512, 512),
+ maxResolution: 1.40625,
+ requestEncoding: "REST"
+ });
+ map.addLayer(layer1);
+ map.zoom = 5;
+ t.eq(layer1.getResolution(), 0.0439453125, "getResolution() returns correct value");
+ map.destroy();
+ }
+
+ function test_getZoomForExtent(t) {
+ t.plan(2);
+ var bounds, zoom;
+
+ var map = new OpenLayers.Map('map');
+ var layer1 = new OpenLayers.Layer.WMTS({
+ name: "Blue Marble WMTS",
+ url: "http://example.com/wmts/",
+ layer: "world",
+ style: "blue_marble",
+ matrixSet: "arcgis_online",
+ tileSize: new OpenLayers.Size(512, 512),
+ maxResolution: 1.40625,
+ requestEncoding: "REST"
+ });
+ map.addLayer(layer1);
+ bounds = new OpenLayers.Bounds(10,10,12,12);
+ zoom = layer1.getZoomForExtent(bounds);
+ t.eq(zoom, 8, "correct value for (10,10,12,12)");
+ bounds = new OpenLayers.Bounds(10,10,100,100);
+ zoom = layer1.getZoomForExtent(bounds);
+ t.eq(zoom, 3, "correct value (10,10,100,100)");
+ map.destroy();
+ }
+
+ function test_getURL(t) {
+ t.plan(2);
+ var xml = document.getElementById("capabilities").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+ var obj = new OpenLayers.Format.WMTSCapabilities().read(doc);
+
+ var layer0 = new OpenLayers.Layer.WMTS({
+ name: "GeoWebCache USA WMTS",
+ url: "http://example.com/geowebcache-1.2.2/service/wmts/",
+ layer: "arcgis-online-wms",
+ style: "foo",
+ matrixSet: "arcgis-online-wgs84",
+ format: "image/png",
+ requestEncoding: "KVP",
+ maxResolution: 0.3521969032857032,
+ numZoomLevels: 7,
+ matrixIds: obj.contents.tileMatrixSets["arcgis-online-wgs84"].matrixIds
+ });
+
+ var layer1 = new OpenLayers.Layer.WMTS({
+ name: "Blue Marble WMTS",
+ url: "http://example.com/wmts/",
+ layer: "world",
+ style: "blue_marble",
+ matrixSet: "arcgis_online",
+ format: "image/jpeg",
+ tileSize: new OpenLayers.Size(512, 512),
+ requestEncoding: "REST",
+ isBaseLayer: false
+ });
+
+ var options = {
+ controls: [
+ new OpenLayers.Control.LayerSwitcher(),
+ new OpenLayers.Control.Navigation(),
+ new OpenLayers.Control.PanZoom()
+ ],
+ projection: "EPSG:4326",
+ maxResolution: 0.3515625,
+ maxExtent: new OpenLayers.Bounds(-180, -90, 180, 90)
+ };
+ var map = new OpenLayers.Map('map', options);
+ map.addLayers([layer0,layer1]);
+ map.setCenter(new OpenLayers.LonLat(-97.0, 38.0), 1);
+ var tileurl0 = layer0.getURL(new OpenLayers.Bounds(-135.0, 0.0, -90.0, 45.0));
+ t.ok(OpenLayers.Util.isEquivalentUrl(tileurl0, "http://example.com/geowebcache-1.2.2/service/wmts/?LAYER=arcgis-online-wms&STYLE=foo&TILEMATRIXSET=arcgis-online-wgs84&FORMAT=image%2Fpng&SERVICE=WMTS&VERSION=1.0.0&REQUEST=GetTile&TILEMATRIX=arcgis-online-wgs84%3A1&TILEROW=1&TILECOL=1"), "layer0 getURL returns correct url");
+
+ var tileurl1 = layer1.getURL(new OpenLayers.Bounds(-180.0, 0.0, -90.0, 90.0));
+ t.eq(tileurl1, "http://example.com/wmts/1.0.0/world/blue_marble/arcgis_online/1/0/0.jpg", "layer1 getURL returns correct url");
+ map.destroy();
+ }
+
+ function test_getURL_resourceUrl(t) {
+ t.plan(2);
+
+ var xml = document.getElementById("capabilities").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+ var obj = new OpenLayers.Format.WMTSCapabilities().read(doc);
+
+ var template = "http://www.example.com/{style}/{Time}/{style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png";
+ var layer = new OpenLayers.Layer.WMTS({
+ requestEncoding: "REST",
+ url: template,
+ layer: "GeoWebCache_USA_WMTS",
+ style: "foo",
+ matrixSet: "arcgis-online",
+ params: {Time: "2011"},
+ dimensions: ["Time"]
+ });
+
+ var map = new OpenLayers.Map("map", {
+ layers: [layer],
+ projection: "EPSG:4326",
+ maxResolution: 0.3515625,
+ maxExtent: new OpenLayers.Bounds(-180, -90, 180, 90),
+ zoomMethod: null
+ });
+ map.setCenter(new OpenLayers.LonLat(-97.0, 38.0), 1);
+ t.eq(layer.getURL(new OpenLayers.Bounds(-135.0, 0.0, -90.0, 45.0)),
+ "http://www.example.com/foo/2011/foo/arcgis-online/1/1/1.png", "getURL returns correct url");
+ map.zoomIn();
+ t.eq(layer.getURL(new OpenLayers.Bounds(-180.0, 0.0, -90.0, 90.0)),
+ "http://www.example.com/foo/2011/foo/arcgis-online/2/2/2.png", "getURL returns correct url");
+ map.destroy();
+ }
+
+ function test_destroy (t) {
+ t.plan(3);
+ var map = new OpenLayers.Map('map');
+ var layer1 = new OpenLayers.Layer.WMTS({
+ name: "Blue Marble WMTS",
+ url: "http://example.com/wmts/",
+ layer: "world",
+ style: "blue_marble",
+ matrixSet: "arcgis_online",
+ tileSize: new OpenLayers.Size(512, 512),
+ requestEncoding: "REST"
+ });
+ map.addLayer(layer1);
+ layer1.destroy();
+ t.eq( layer1.grid, null, "layer.grid is null after destroy" );
+ t.eq( layer1.tileSize, null, "layer.tileSize is null after destroy" );
+
+ //test with tile creation
+ var layer2 = new OpenLayers.Layer.WMTS({
+ name: "Blue Marble WMTS",
+ url: "http://example.com/wmts/",
+ layer: "world",
+ style: "blue_marble",
+ matrixSet: "arcgis_online",
+ tileSize: new OpenLayers.Size(512, 512),
+ requestEncoding: "REST"
+ });
+ map.addLayer(layer2);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+ //grab a reference to one of the tiles
+ var tile = layer2.grid[0][0];
+
+ layer2.destroy();
+
+ t.ok( layer2.grid == null, "tiles appropriately destroyed");
+ map.destroy();
+ }
+
+ function test_getIdentifier(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map('map');
+ var layer, identifier;
+
+ layer = new OpenLayers.Layer.WMTS({
+ name: "Blue Marble WMTS",
+ url: "http://example.com/wmts/",
+ layer: "world",
+ style: "blue_marble",
+ matrixSet: "arcgis_online",
+ tileSize: new OpenLayers.Size(512, 512),
+ requestEncoding: "REST"
+ });
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+
+ layer.zoomOffset = 2;
+ identifier = layer.getIdentifier();
+ t.eq(identifier, 7, '[zoomOffset] getIdentifier return value is correct');
+
+ layer.serverResolutions = ['offset', 1.40625, 0.703125, 0.3515625, 0.17578125,
+ 0.087890625, 0.0439453125];
+ identifier = layer.getIdentifier();
+ t.eq(identifier, 6, '[serverResolutions] getIdentifier return value is correct');
+
+ map.destroy();
+ }
+
+ </script>
+ </head>
+ <body>
+ <div id="map" style="width:1024px;height:512px;"></div>
+ <div id="capabilities"><!--
+<Capabilities xmlns="http://www.opengis.net/wmts/1.0"
+xmlns:ows="http://www.opengis.net/ows/1.1"
+xmlns:xlink="http://www.w3.org/1999/xlink"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xmlns:gml="http://www.opengis.net/gml" xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://geowebcache.org/schema/opengis/wmts/1.0.0/wmtsGetCapabilities_response.xsd"
+version="1.0.0">
+<ows:ServiceIdentification>
+ <ows:Title>Web Map Tile Service - GeoWebCache</ows:Title>
+ <ows:ServiceType>OGC WMTS</ows:ServiceType>
+ <ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion>
+</ows:ServiceIdentification>
+<ows:ServiceProvider>
+ <ows:ProviderName>http://example.com/geowebcache-1.2.2/service/wmts</ows:ProviderName>
+ <ows:ProviderSite xlink:href="http://example.com/geowebcache-1.2.2/service/wmts" />
+ <ows:ServiceContact>
+ <ows:IndividualName>GeoWebCache User</ows:IndividualName>
+ </ows:ServiceContact>
+</ows:ServiceProvider>
+<ows:OperationsMetadata>
+ <ows:Operation name="GetCapabilities">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://example.com/geowebcache-1.2.2/service/wmts?">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>KVP</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ <ows:Operation name="GetTile">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://example.com/geowebcache-1.2.2/service/wmts?">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>KVP</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ <ows:Operation name="GetFeatureInfo">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://example.com/geowebcache-1.2.2/service/wmts?">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>KVP</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+</ows:OperationsMetadata>
+<Contents>
+ <Layer>
+ <ows:Title>arcgis-online-wms</ows:Title>
+ <ows:Abstract>arcgis-online-wms</ows:Abstract>
+ <ows:WGS84BoundingBox>
+ <ows:LowerCorner>-180.0 -90.0</ows:LowerCorner>
+ <ows:UpperCorner>180.0 90.0</ows:UpperCorner>
+ </ows:WGS84BoundingBox>
+ <ows:Identifier>arcgis-online-wms</ows:Identifier>
+ <Style isDefault="true">
+ <ows:Identifier>_null</ows:Identifier>
+ </Style>
+ <Format>image/png</Format>
+ <Format>image/jpeg</Format>
+ <TileMatrixSetLink> <TileMatrixSet>arcgis-online-wgs84</TileMatrixSet>
+ </TileMatrixSetLink> </Layer>
+ <TileMatrixSet>
+ <ows:Identifier>EPSG:4326</ows:Identifier>
+ <ows:SupportedCRS>urn:ogc:def:crs:EPSG::4326</ows:SupportedCRS>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:0</ows:Identifier>
+ <ScaleDenominator>2.795411320143589E8</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>2</MatrixWidth>
+ <MatrixHeight>1</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:1</ows:Identifier>
+ <ScaleDenominator>1.3977056600717944E8</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>4</MatrixWidth>
+ <MatrixHeight>2</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:2</ows:Identifier>
+ <ScaleDenominator>6.988528300358972E7</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>8</MatrixWidth>
+ <MatrixHeight>4</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:3</ows:Identifier>
+ <ScaleDenominator>3.494264150179486E7</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>16</MatrixWidth>
+ <MatrixHeight>8</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:4</ows:Identifier>
+ <ScaleDenominator>1.747132075089743E7</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>32</MatrixWidth>
+ <MatrixHeight>16</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:5</ows:Identifier>
+ <ScaleDenominator>8735660.375448715</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>64</MatrixWidth>
+ <MatrixHeight>32</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:6</ows:Identifier>
+ <ScaleDenominator>4367830.1877243575</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>128</MatrixWidth>
+ <MatrixHeight>64</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:7</ows:Identifier>
+ <ScaleDenominator>2183915.0938621787</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>256</MatrixWidth>
+ <MatrixHeight>128</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:8</ows:Identifier>
+ <ScaleDenominator>1091957.5469310894</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>512</MatrixWidth>
+ <MatrixHeight>256</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:9</ows:Identifier>
+ <ScaleDenominator>545978.7734655447</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1024</MatrixWidth>
+ <MatrixHeight>512</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:10</ows:Identifier>
+ <ScaleDenominator>272989.38673277234</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>2048</MatrixWidth>
+ <MatrixHeight>1024</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:11</ows:Identifier>
+ <ScaleDenominator>136494.69336638617</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>4096</MatrixWidth>
+ <MatrixHeight>2048</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:12</ows:Identifier>
+ <ScaleDenominator>68247.34668319309</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>8192</MatrixWidth>
+ <MatrixHeight>4096</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:13</ows:Identifier>
+ <ScaleDenominator>34123.67334159654</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>16384</MatrixWidth>
+ <MatrixHeight>8192</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:14</ows:Identifier>
+ <ScaleDenominator>17061.83667079827</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>32768</MatrixWidth>
+ <MatrixHeight>16384</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:15</ows:Identifier>
+ <ScaleDenominator>8530.918335399136</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>65536</MatrixWidth>
+ <MatrixHeight>32768</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:16</ows:Identifier>
+ <ScaleDenominator>4265.459167699568</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>131072</MatrixWidth>
+ <MatrixHeight>65536</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:17</ows:Identifier>
+ <ScaleDenominator>2132.729583849784</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>262144</MatrixWidth>
+ <MatrixHeight>131072</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:18</ows:Identifier>
+ <ScaleDenominator>1066.364791924892</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>524288</MatrixWidth>
+ <MatrixHeight>262144</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:19</ows:Identifier>
+ <ScaleDenominator>533.182395962446</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1048576</MatrixWidth>
+ <MatrixHeight>524288</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:20</ows:Identifier>
+ <ScaleDenominator>266.591197981223</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>2097152</MatrixWidth>
+ <MatrixHeight>1048576</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:21</ows:Identifier>
+ <ScaleDenominator>133.2955989906115</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>4194304</MatrixWidth>
+ <MatrixHeight>2097152</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:22</ows:Identifier>
+ <ScaleDenominator>66.64779949530575</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>8388608</MatrixWidth>
+ <MatrixHeight>4194304</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:23</ows:Identifier>
+ <ScaleDenominator>33.323899747652874</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>16777216</MatrixWidth>
+ <MatrixHeight>8388608</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:24</ows:Identifier>
+ <ScaleDenominator>16.661949873826437</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>33554432</MatrixWidth>
+ <MatrixHeight>16777216</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:25</ows:Identifier>
+ <ScaleDenominator>8.330974936913218</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>67108864</MatrixWidth>
+ <MatrixHeight>33554432</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:26</ows:Identifier>
+ <ScaleDenominator>4.165487468456609</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>134217728</MatrixWidth>
+ <MatrixHeight>67108864</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:27</ows:Identifier>
+ <ScaleDenominator>2.0827437342283046</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>268435456</MatrixWidth>
+ <MatrixHeight>134217728</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:28</ows:Identifier>
+ <ScaleDenominator>1.0413718671141523</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>536870912</MatrixWidth>
+ <MatrixHeight>268435456</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:29</ows:Identifier>
+ <ScaleDenominator>0.5206859335570762</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1073741824</MatrixWidth>
+ <MatrixHeight>536870912</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:4326:30</ows:Identifier>
+ <ScaleDenominator>0.2603429667785381</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>2147483648</MatrixWidth>
+ <MatrixHeight>1073741824</MatrixHeight>
+ </TileMatrix>
+ </TileMatrixSet>
+ <TileMatrixSet>
+ <ows:Identifier>arcgis-online-epsg102113</ows:Identifier>
+ <ows:SupportedCRS>urn:ogc:def:crs:EPSG::102113</ows:SupportedCRS>
+ <TileMatrix>
+ <ows:Abstract>The grid was not well-defined, the scale therefore assumes 1m per map unit.</ows:Abstract> <ows:Identifier>arcgis-online-epsg102113:0</ows:Identifier>
+ <ScaleDenominator>5.590822639285715E8</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.00375083392E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1</MatrixWidth>
+ <MatrixHeight>1</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Abstract>The grid was not well-defined, the scale therefore assumes 1m per map unit.</ows:Abstract> <ows:Identifier>arcgis-online-epsg102113:1</ows:Identifier>
+ <ScaleDenominator>2.7954113196428573E8</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.00375083392E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>2</MatrixWidth>
+ <MatrixHeight>2</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Abstract>The grid was not well-defined, the scale therefore assumes 1m per map unit.</ows:Abstract> <ows:Identifier>arcgis-online-epsg102113:2</ows:Identifier>
+ <ScaleDenominator>1.3977056598214287E8</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.00375083392E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>4</MatrixWidth>
+ <MatrixHeight>4</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Abstract>The grid was not well-defined, the scale therefore assumes 1m per map unit.</ows:Abstract> <ows:Identifier>arcgis-online-epsg102113:3</ows:Identifier>
+ <ScaleDenominator>6.988528299107143E7</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.00375083392E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>8</MatrixWidth>
+ <MatrixHeight>8</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Abstract>The grid was not well-defined, the scale therefore assumes 1m per map unit.</ows:Abstract> <ows:Identifier>arcgis-online-epsg102113:4</ows:Identifier>
+ <ScaleDenominator>3.494264149553572E7</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.00375083392E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>16</MatrixWidth>
+ <MatrixHeight>16</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Abstract>The grid was not well-defined, the scale therefore assumes 1m per map unit.</ows:Abstract> <ows:Identifier>arcgis-online-epsg102113:5</ows:Identifier>
+ <ScaleDenominator>1.747132074776786E7</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.00375083392E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>32</MatrixWidth>
+ <MatrixHeight>32</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Abstract>The grid was not well-defined, the scale therefore assumes 1m per map unit.</ows:Abstract> <ows:Identifier>arcgis-online-epsg102113:6</ows:Identifier>
+ <ScaleDenominator>8735660.37388393</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.00375083392E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>64</MatrixWidth>
+ <MatrixHeight>64</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Abstract>The grid was not well-defined, the scale therefore assumes 1m per map unit.</ows:Abstract> <ows:Identifier>arcgis-online-epsg102113:7</ows:Identifier>
+ <ScaleDenominator>4367830.186941965</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.00375083392E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>128</MatrixWidth>
+ <MatrixHeight>128</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Abstract>The grid was not well-defined, the scale therefore assumes 1m per map unit.</ows:Abstract> <ows:Identifier>arcgis-online-epsg102113:8</ows:Identifier>
+ <ScaleDenominator>2183915.0934709823</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.00375083392E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>256</MatrixWidth>
+ <MatrixHeight>256</MatrixHeight>
+ </TileMatrix>
+ </TileMatrixSet>
+ <TileMatrixSet>
+ <ows:Identifier>GlobalCRS84Scale</ows:Identifier>
+ <ows:SupportedCRS>urn:ogc:def:crs:EPSG::4326</ows:SupportedCRS>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:0</ows:Identifier>
+ <ScaleDenominator>5.0000000000000006E8</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>2</MatrixWidth>
+ <MatrixHeight>1</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:1</ows:Identifier>
+ <ScaleDenominator>2.5000000000000003E8</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>3</MatrixWidth>
+ <MatrixHeight>2</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:2</ows:Identifier>
+ <ScaleDenominator>1.0000000000000001E8</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>6</MatrixWidth>
+ <MatrixHeight>3</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:3</ows:Identifier>
+ <ScaleDenominator>5.000000000000001E7</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>12</MatrixWidth>
+ <MatrixHeight>6</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:4</ows:Identifier>
+ <ScaleDenominator>2.5000000000000004E7</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>23</MatrixWidth>
+ <MatrixHeight>12</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:5</ows:Identifier>
+ <ScaleDenominator>1.0E7</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>56</MatrixWidth>
+ <MatrixHeight>28</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:6</ows:Identifier>
+ <ScaleDenominator>5000000.0</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>112</MatrixWidth>
+ <MatrixHeight>56</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:7</ows:Identifier>
+ <ScaleDenominator>2500000.0</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>224</MatrixWidth>
+ <MatrixHeight>112</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:8</ows:Identifier>
+ <ScaleDenominator>1000000.0000000001</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>560</MatrixWidth>
+ <MatrixHeight>280</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:9</ows:Identifier>
+ <ScaleDenominator>500000.00000000006</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1119</MatrixWidth>
+ <MatrixHeight>560</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:10</ows:Identifier>
+ <ScaleDenominator>250000.00000000003</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>2237</MatrixWidth>
+ <MatrixHeight>1119</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:11</ows:Identifier>
+ <ScaleDenominator>100000.00000000001</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>5591</MatrixWidth>
+ <MatrixHeight>2796</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:12</ows:Identifier>
+ <ScaleDenominator>50000.00000000001</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>11182</MatrixWidth>
+ <MatrixHeight>5591</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:13</ows:Identifier>
+ <ScaleDenominator>25000.000000000004</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>22364</MatrixWidth>
+ <MatrixHeight>11182</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:14</ows:Identifier>
+ <ScaleDenominator>10000.000000000002</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>55909</MatrixWidth>
+ <MatrixHeight>27955</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:15</ows:Identifier>
+ <ScaleDenominator>5000.000000000001</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>111817</MatrixWidth>
+ <MatrixHeight>55909</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:16</ows:Identifier>
+ <ScaleDenominator>2500.0000000000005</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>223633</MatrixWidth>
+ <MatrixHeight>111817</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:17</ows:Identifier>
+ <ScaleDenominator>1000.0000000000002</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>559083</MatrixWidth>
+ <MatrixHeight>279542</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:18</ows:Identifier>
+ <ScaleDenominator>500.0000000000001</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1118165</MatrixWidth>
+ <MatrixHeight>559083</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:19</ows:Identifier>
+ <ScaleDenominator>250.00000000000006</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>2236330</MatrixWidth>
+ <MatrixHeight>1118165</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Scale:20</ows:Identifier>
+ <ScaleDenominator>100.00000000000003</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>5590823</MatrixWidth>
+ <MatrixHeight>2795412</MatrixHeight>
+ </TileMatrix>
+ </TileMatrixSet>
+ <TileMatrixSet>
+ <ows:Identifier>EPSG:900913</ows:Identifier>
+ <ows:SupportedCRS>urn:ogc:def:crs:EPSG::900913</ows:SupportedCRS>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:0</ows:Identifier>
+ <ScaleDenominator>5.590822639508929E8</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1</MatrixWidth>
+ <MatrixHeight>1</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:1</ows:Identifier>
+ <ScaleDenominator>2.7954113197544646E8</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>2</MatrixWidth>
+ <MatrixHeight>2</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:2</ows:Identifier>
+ <ScaleDenominator>1.3977056598772323E8</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>4</MatrixWidth>
+ <MatrixHeight>4</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:3</ows:Identifier>
+ <ScaleDenominator>6.988528299386162E7</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>8</MatrixWidth>
+ <MatrixHeight>8</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:4</ows:Identifier>
+ <ScaleDenominator>3.494264149693081E7</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>16</MatrixWidth>
+ <MatrixHeight>16</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:5</ows:Identifier>
+ <ScaleDenominator>1.7471320748465404E7</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>32</MatrixWidth>
+ <MatrixHeight>32</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:6</ows:Identifier>
+ <ScaleDenominator>8735660.374232702</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>64</MatrixWidth>
+ <MatrixHeight>64</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:7</ows:Identifier>
+ <ScaleDenominator>4367830.187116351</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>128</MatrixWidth>
+ <MatrixHeight>128</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:8</ows:Identifier>
+ <ScaleDenominator>2183915.0935581755</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>256</MatrixWidth>
+ <MatrixHeight>256</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:9</ows:Identifier>
+ <ScaleDenominator>1091957.5467790877</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>512</MatrixWidth>
+ <MatrixHeight>512</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:10</ows:Identifier>
+ <ScaleDenominator>545978.7733895439</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1024</MatrixWidth>
+ <MatrixHeight>1024</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:11</ows:Identifier>
+ <ScaleDenominator>272989.38669477194</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>2048</MatrixWidth>
+ <MatrixHeight>2048</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:12</ows:Identifier>
+ <ScaleDenominator>136494.69334738597</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>4096</MatrixWidth>
+ <MatrixHeight>4096</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:13</ows:Identifier>
+ <ScaleDenominator>68247.34667369298</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>8192</MatrixWidth>
+ <MatrixHeight>8192</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:14</ows:Identifier>
+ <ScaleDenominator>34123.67333684649</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>16384</MatrixWidth>
+ <MatrixHeight>16384</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:15</ows:Identifier>
+ <ScaleDenominator>17061.836668423246</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>32768</MatrixWidth>
+ <MatrixHeight>32768</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:16</ows:Identifier>
+ <ScaleDenominator>8530.918334211623</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>65536</MatrixWidth>
+ <MatrixHeight>65536</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:17</ows:Identifier>
+ <ScaleDenominator>4265.4591671058115</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>131072</MatrixWidth>
+ <MatrixHeight>131072</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:18</ows:Identifier>
+ <ScaleDenominator>2132.7295835529058</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>262144</MatrixWidth>
+ <MatrixHeight>262144</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:19</ows:Identifier>
+ <ScaleDenominator>1066.3647917764529</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>524288</MatrixWidth>
+ <MatrixHeight>524288</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:20</ows:Identifier>
+ <ScaleDenominator>533.1823958882264</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1048576</MatrixWidth>
+ <MatrixHeight>1048576</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:21</ows:Identifier>
+ <ScaleDenominator>266.5911979441132</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>2097152</MatrixWidth>
+ <MatrixHeight>2097152</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:22</ows:Identifier>
+ <ScaleDenominator>133.2955989720566</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>4194304</MatrixWidth>
+ <MatrixHeight>4194304</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:23</ows:Identifier>
+ <ScaleDenominator>66.6477994860283</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>8388608</MatrixWidth>
+ <MatrixHeight>8388608</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:24</ows:Identifier>
+ <ScaleDenominator>33.32389974301415</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>16777216</MatrixWidth>
+ <MatrixHeight>16777216</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:25</ows:Identifier>
+ <ScaleDenominator>16.661949871507076</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>33554432</MatrixWidth>
+ <MatrixHeight>33554432</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:26</ows:Identifier>
+ <ScaleDenominator>8.330974935753538</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>67108864</MatrixWidth>
+ <MatrixHeight>67108864</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:27</ows:Identifier>
+ <ScaleDenominator>4.165487467876769</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>134217728</MatrixWidth>
+ <MatrixHeight>134217728</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:28</ows:Identifier>
+ <ScaleDenominator>2.0827437339383845</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>268435456</MatrixWidth>
+ <MatrixHeight>268435456</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:29</ows:Identifier>
+ <ScaleDenominator>1.0413718669691923</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>536870912</MatrixWidth>
+ <MatrixHeight>536870912</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>EPSG:900913:30</ows:Identifier>
+ <ScaleDenominator>0.5206859334845961</ScaleDenominator>
+ <TopLeftCorner>2.0037508E7 -2.003750834E7</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1073741824</MatrixWidth>
+ <MatrixHeight>1073741824</MatrixHeight>
+ </TileMatrix>
+ </TileMatrixSet>
+ <TileMatrixSet>
+ <ows:Identifier>arcgis-online-wgs84</ows:Identifier>
+ <ows:SupportedCRS>urn:ogc:def:crs:EPSG::4326</ows:SupportedCRS>
+ <TileMatrix>
+ <ows:Identifier>arcgis-online-wgs84:0</ows:Identifier>
+ <ScaleDenominator>1.3977056600717944E8</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>4</MatrixWidth>
+ <MatrixHeight>2</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>arcgis-online-wgs84:1</ows:Identifier>
+ <ScaleDenominator>6.988528300358972E7</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>8</MatrixWidth>
+ <MatrixHeight>4</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>arcgis-online-wgs84:2</ows:Identifier>
+ <ScaleDenominator>3.494264150179486E7</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>16</MatrixWidth>
+ <MatrixHeight>8</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>arcgis-online-wgs84:3</ows:Identifier>
+ <ScaleDenominator>1.747132075089743E7</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>32</MatrixWidth>
+ <MatrixHeight>16</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>arcgis-online-wgs84:4</ows:Identifier>
+ <ScaleDenominator>8735660.375448715</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>64</MatrixWidth>
+ <MatrixHeight>32</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>arcgis-online-wgs84:5</ows:Identifier>
+ <ScaleDenominator>4367830.1877243575</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>128</MatrixWidth>
+ <MatrixHeight>64</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>arcgis-online-wgs84:6</ows:Identifier>
+ <ScaleDenominator>2183915.0938621787</ScaleDenominator>
+ <TopLeftCorner>85 -175</TopLeftCorner>
+ <TileWidth>512</TileWidth>
+ <TileHeight>512</TileHeight>
+ <MatrixWidth>256</MatrixWidth>
+ <MatrixHeight>128</MatrixHeight>
+ </TileMatrix>
+ </TileMatrixSet>
+ <TileMatrixSet>
+ <ows:Identifier>GlobalCRS84Pixel</ows:Identifier>
+ <ows:SupportedCRS>urn:ogc:def:crs:EPSG::4326</ows:SupportedCRS>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:0</ows:Identifier>
+ <ScaleDenominator>7.951392199519542E8</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1</MatrixWidth>
+ <MatrixHeight>1</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:1</ows:Identifier>
+ <ScaleDenominator>3.975696099759771E8</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>2</MatrixWidth>
+ <MatrixHeight>1</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:2</ows:Identifier>
+ <ScaleDenominator>1.9878480498798856E8</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>3</MatrixWidth>
+ <MatrixHeight>2</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:3</ows:Identifier>
+ <ScaleDenominator>1.325232033253257E8</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>5</MatrixWidth>
+ <MatrixHeight>3</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:4</ows:Identifier>
+ <ScaleDenominator>6.626160166266285E7</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>9</MatrixWidth>
+ <MatrixHeight>5</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:5</ows:Identifier>
+ <ScaleDenominator>3.3130800831331424E7</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>17</MatrixWidth>
+ <MatrixHeight>9</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:6</ows:Identifier>
+ <ScaleDenominator>1.325232033253257E7</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>43</MatrixWidth>
+ <MatrixHeight>22</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:7</ows:Identifier>
+ <ScaleDenominator>6626160.166266285</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>85</MatrixWidth>
+ <MatrixHeight>43</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:8</ows:Identifier>
+ <ScaleDenominator>3313080.0831331424</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>169</MatrixWidth>
+ <MatrixHeight>85</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:9</ows:Identifier>
+ <ScaleDenominator>1656540.0415665712</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>338</MatrixWidth>
+ <MatrixHeight>169</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:10</ows:Identifier>
+ <ScaleDenominator>552180.0138555238</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1013</MatrixWidth>
+ <MatrixHeight>507</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:11</ows:Identifier>
+ <ScaleDenominator>331308.00831331423</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1688</MatrixWidth>
+ <MatrixHeight>844</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:12</ows:Identifier>
+ <ScaleDenominator>110436.00277110476</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>5063</MatrixWidth>
+ <MatrixHeight>2532</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:13</ows:Identifier>
+ <ScaleDenominator>55218.00138555238</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>10125</MatrixWidth>
+ <MatrixHeight>5063</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:14</ows:Identifier>
+ <ScaleDenominator>33130.80083133143</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>16875</MatrixWidth>
+ <MatrixHeight>8438</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:15</ows:Identifier>
+ <ScaleDenominator>11043.600277110474</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>50625</MatrixWidth>
+ <MatrixHeight>25313</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:16</ows:Identifier>
+ <ScaleDenominator>3313.080083133142</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>168750</MatrixWidth>
+ <MatrixHeight>84375</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>GlobalCRS84Pixel:17</ows:Identifier>
+ <ScaleDenominator>1104.3600277110472</ScaleDenominator>
+ <TopLeftCorner>90.0 -180.0</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>506250</MatrixWidth>
+ <MatrixHeight>253125</MatrixHeight>
+ </TileMatrix>
+ </TileMatrixSet>
+</Contents>
+<ServiceMetadataURL xlink:href="http://example.com/geowebcache-1.2.2/service/wmts?REQUEST=getcapabilities&amp;VERSION=1.0.0"/>
+</Capabilities>
+ -->
+ </div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/Layer/WrapDateLine.html b/misc/openlayers/tests/Layer/WrapDateLine.html
new file mode 100644
index 0000000..efe8903
--- /dev/null
+++ b/misc/openlayers/tests/Layer/WrapDateLine.html
@@ -0,0 +1,188 @@
+<html>
+<head>
+<script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ // turn off animation frame handling, so we can check img urls in tests
+ delete OpenLayers.Layer.Grid.prototype.queueTileDraw;
+
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var layer;
+
+ var name = 'Test Layer';
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ var params = { map: '/mapdata/vmap_wms.map',
+ layers: 'basic',
+ format: 'image/png'};
+
+
+ function test_Layer_WrapDateLine_adjustBounds(t) {
+ t.plan(10);
+
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS(name, url, params, {'wrapDateLine':true});
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ var bounds = layer.adjustBounds(new OpenLayers.Bounds(-270,-90,-180,0));
+ t.ok( bounds.equals(new OpenLayers.Bounds(90,-90,180,0)), "-270,-90,-180,0 wraps to 90,-90,180,0");
+ bounds = layer.adjustBounds(new OpenLayers.Bounds(180,-90,270,0));
+ t.ok( bounds.equals(new OpenLayers.Bounds(-180,-90,-90,0)), "180,-90,270,0 wraps to -180,-90,-90,0");
+ bounds = layer.adjustBounds(new OpenLayers.Bounds(-180,-90,0,0));
+ t.ok( bounds.equals(new OpenLayers.Bounds(-180,-90,0,0)), "-180,-90,0,0 doesn't wrap");
+ bounds = layer.adjustBounds(new OpenLayers.Bounds(-181,-90,-179,0));
+ t.ok( bounds.equals(new OpenLayers.Bounds(-181,-90,-179,0)), "-181,-90,-179,0 doesn't wrap, because it straddles the dateline");
+ bounds = layer.adjustBounds(new OpenLayers.Bounds(-180,-180,-90,-90));
+ t.ok( bounds.equals(new OpenLayers.Bounds(-180,-180,-90,-90)), "-180,-180,-90,-90 doesn't wrap, because we don't wrap lats.");
+ layer = new OpenLayers.Layer.WMS(name, url, params);
+ map.addLayer(layer);
+ var testBounds = null;
+ var outBounds = null;
+ var testList = [
+ new OpenLayers.Bounds(-270,-90,-180,0),
+ new OpenLayers.Bounds(180,-90,270,0),
+ new OpenLayers.Bounds(-180,-90,0,0),
+ new OpenLayers.Bounds(-181,-90,-179,0),
+ new OpenLayers.Bounds(-180,-180,-90,-90)
+ ];
+ for (var i = 0; i < testList.length; i++) {
+ outBounds = layer.adjustBounds(testList[i]);
+ t.ok( outBounds.equals(testList[i]), testList[i]+" doesn't wrap in non-wrapping layer.");
+ }
+ map.destroy();
+ }
+ function test_Layer_WrapDateLine_getLonLat(t) {
+ t.plan(12);
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS(name, url, params, {'wrapDateLine':true});
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ var testLonLats = [
+ new OpenLayers.LonLat(-185,5),
+ new OpenLayers.LonLat(-180,-95),
+ new OpenLayers.LonLat(-180,95),
+ new OpenLayers.LonLat(180,-95),
+ new OpenLayers.LonLat(180,95),
+ new OpenLayers.LonLat(185,5)
+ ];
+ var outLonLats = [
+ new OpenLayers.LonLat(175,5),
+ new OpenLayers.LonLat(-180,-95),
+ new OpenLayers.LonLat(-180,95),
+ new OpenLayers.LonLat(180,-95),
+ new OpenLayers.LonLat(180,95),
+ new OpenLayers.LonLat(-175,5)
+ ];
+
+ for (var i = 0; i < testLonLats.length; i++) {
+ var pixel = layer.getViewPortPxFromLonLat(testLonLats[i]);
+ var lonlat = layer.getLonLatFromViewPortPx(pixel);
+ lonlat.lon = Math.round(lonlat.lon);
+ lonlat.lat = Math.round(lonlat.lat);
+ t.ok(outLonLats[i].equals(lonlat), testLonLats[i] + " wraps to " + outLonLats[i]+ " (what happened: " + lonlat + ")");
+ }
+
+ layer = new OpenLayers.Layer.WMS(name, url, params);
+ map.addLayer(layer);
+ var outLonLats = [
+ new OpenLayers.LonLat(-185,5),
+ new OpenLayers.LonLat(-180,-95),
+ new OpenLayers.LonLat(-180,95),
+ new OpenLayers.LonLat(180,-95),
+ new OpenLayers.LonLat(180,95),
+ new OpenLayers.LonLat(185,5)
+ ];
+ for (var i = 0; i < testLonLats.length; i++) {
+ var pixel = layer.getViewPortPxFromLonLat(testLonLats[i]);
+ var lonlat = layer.getLonLatFromViewPortPx(pixel);
+ lonlat.lon = Math.round(lonlat.lon);
+ lonlat.lat = Math.round(lonlat.lat);
+ t.ok(outLonLats[i].equals(lonlat), testLonLats[i] + " wraps to " + outLonLats[i]+ " (what happened: " + lonlat + ")");
+ }
+ map.destroy();
+
+ }
+ function test_Layer_WrapDateLine_ZoomToExtent (t) {
+ t.plan( 4 );
+
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.WMS(name, url, params, {'wrapDateLine':true});
+ var m = new OpenLayers.Map('map');
+ m.addLayer(layer);
+ m.setCenter = function(myCenter) { this.center = myCenter; }
+ var testBounds = [
+ new OpenLayers.Bounds(-185,-90,-175,-85),
+ new OpenLayers.Bounds(0,-90,-170,-85),
+ new OpenLayers.Bounds(-270,-90,-180,-85),
+ new OpenLayers.Bounds(0,0,45,45)
+ ];
+ var outCenters = [
+ new OpenLayers.LonLat(-180,-87.5),
+ new OpenLayers.LonLat(95,-87.5),
+ new OpenLayers.LonLat(135,-87.5),
+ new OpenLayers.LonLat(22.5,22.5)
+ ];
+ for (var i = 0; i < testBounds.length; i++) {
+ m.zoomToExtent(testBounds[i]);
+ t.ok(m.center.equals(outCenters[i]), "Map center from bounds " + testBounds[i] + " should be " + outCenters[i] + ", got " + m.center);
+ }
+ m.destroy();
+
+
+ }
+ function test_Layer_WrapDateLine_WMS (t) {
+ t.plan( 4 );
+
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.WMS(name, url, params, {'wrapDateLine':true,encodeBBOX:true, buffer: 2});
+ var m = new OpenLayers.Map('map', {tileManager: null, adjustZoom: function(z) {return z;}});
+ m.addLayer(layer);
+ m.zoomToMaxExtent();
+ t.eq(layer.grid[3][0].url, "http://octo.metacarta.com/cgi-bin/mapserv?MAP=%2Fmapdata%2Fvmap_wms.map&LAYERS=basic&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&SRS=EPSG%3A4326&BBOX=0%2C-90%2C180%2C90&WIDTH=256&HEIGHT=256", "cell [3][0] is wrapped around the world.");
+ t.eq(layer.grid[3][1].url, "http://octo.metacarta.com/cgi-bin/mapserv?MAP=%2Fmapdata%2Fvmap_wms.map&LAYERS=basic&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&SRS=EPSG%3A4326&BBOX=-180%2C-90%2C0%2C90&WIDTH=256&HEIGHT=256", "cell [3][1] is wrapped around the world.");
+ t.eq(layer.grid[3][2].url, "http://octo.metacarta.com/cgi-bin/mapserv?MAP=%2Fmapdata%2Fvmap_wms.map&LAYERS=basic&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&SRS=EPSG%3A4326&BBOX=0%2C-90%2C180%2C90&WIDTH=256&HEIGHT=256", "cell [3][2] is not wrapped at all.");
+ t.ok(layer.grid[0][2].url == null, "no latitudinal wrapping - tile not loaded if outside maxExtent");
+ m.destroy();
+
+ }
+ function test_Layer_WrapDateLine_KaMap (t) {
+ t.plan( 4 );
+
+ var layer = new OpenLayers.Layer.KaMap( "Blue Marble NG",
+ "http://www.openlayers.org/world/index.php",
+ {g: "satellite", map: "world"},
+ {wrapDateLine: true, buffer: 2} );
+ var m = new OpenLayers.Map('map', {tileManager: null, adjustZoom: function(z) {return z;}});
+ m.addLayer(layer);
+ m.zoomToMaxExtent();
+ t.eq(layer.grid[4][7].url, "http://www.openlayers.org/world/index.php?g=satellite&map=world&i=jpeg&t=0&l=-256&s=221471921.25", "grid[5][7] kamap is okay");
+ t.eq(layer.grid[4][6].url, "http://www.openlayers.org/world/index.php?g=satellite&map=world&i=jpeg&t=0&l=0&s=221471921.25", "grid[5][6] kamap is okay");
+ t.eq(layer.grid[4][5].url, "http://www.openlayers.org/world/index.php?g=satellite&map=world&i=jpeg&t=0&l=-256&s=221471921.25", "grid[5][5] is okay");
+ t.ok(layer.grid[7][6].url == null, "no latitudinal wrapping - tile not loaded if outside maxExtent");
+ m.destroy();
+ }
+ function test_Layer_WrapDateLine_WMS_Overlay (t) {
+ t.plan( 4 );
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ baselayer = new OpenLayers.Layer.WMS(name, url, params, {'wrapDateLine':true, buffer: 2});
+ var layer = new OpenLayers.Layer.WMS( "DM Solutions Demo",
+ "http://www2.dmsolutions.ca/cgi-bin/mswms_gmap",
+ {layers: "bathymetry,land_fn,park,drain_fn,drainage," +
+ "prov_bound,fedlimit,rail,road,popplace",
+ transparent: "true", format: "image/png"},
+ {wrapDateLine: true, encodeBBOX:true, buffer:2});
+ var m = new OpenLayers.Map('map', {tileManager: null, adjustZoom: function(z) {return z;}});
+ m.addLayers([baselayer,layer]);
+ m.zoomToMaxExtent();
+ t.eq(layer.grid[3][0].url, "http://www2.dmsolutions.ca/cgi-bin/mswms_gmap?LAYERS=bathymetry%2Cland_fn%2Cpark%2Cdrain_fn%2Cdrainage%2Cprov_bound%2Cfedlimit%2Crail%2Croad%2Cpopplace&TRANSPARENT=true&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&SRS=EPSG%3A4326&BBOX=0%2C-90%2C180%2C90&WIDTH=256&HEIGHT=256", "grid[0][0] wms overlay is okay");
+ t.eq(layer.grid[3][1].url, "http://www2.dmsolutions.ca/cgi-bin/mswms_gmap?LAYERS=bathymetry%2Cland_fn%2Cpark%2Cdrain_fn%2Cdrainage%2Cprov_bound%2Cfedlimit%2Crail%2Croad%2Cpopplace&TRANSPARENT=true&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&SRS=EPSG%3A4326&BBOX=-180%2C-90%2C0%2C90&WIDTH=256&HEIGHT=256", "grid[0][3] wms overlay is okay");
+ t.eq(layer.grid[3][2].url, "http://www2.dmsolutions.ca/cgi-bin/mswms_gmap?LAYERS=bathymetry%2Cland_fn%2Cpark%2Cdrain_fn%2Cdrainage%2Cprov_bound%2Cfedlimit%2Crail%2Croad%2Cpopplace&TRANSPARENT=true&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&SRS=EPSG%3A4326&BBOX=0%2C-90%2C180%2C90&WIDTH=256&HEIGHT=256", "grid[3][0] wms overlay okay");
+ t.ok(layer.grid[0][2].url == null, "no latitudinal wrapping - tile not loaded if outside maxExtent");
+ m.destroy();
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:1000px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/XYZ.html b/misc/openlayers/tests/Layer/XYZ.html
new file mode 100644
index 0000000..bd6d26e
--- /dev/null
+++ b/misc/openlayers/tests/Layer/XYZ.html
@@ -0,0 +1,266 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var layer;
+
+ var name = 'Test Layer';
+ var url = "http://labs.metacarta.com/wms-c/Basic.py/1.0.0/basic/${z}/${x}/${y}.png";
+ var options = {'layername':'basic', 'type':'png'};
+
+
+ function test_Layer_XYZ_constructor (t) {
+ t.plan( 1 );
+
+ layer = new OpenLayers.Layer.XYZ(name, url, options);
+ t.ok( layer instanceof OpenLayers.Layer.XYZ, "returns OpenLayers.Layer.XYZ object" );
+ }
+
+
+
+ function test_Layer_XYZ_clearTiles (t) {
+ t.plan( 1 );
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.XYZ(name, url, options);
+ map.addLayer(layer);
+
+ map.setCenter(new OpenLayers.LonLat(0,0));
+
+ //grab a reference to one of the tiles
+ var tile = layer.grid[0][0];
+
+ layer.clearGrid();
+
+ t.ok( layer.grid != null, "layer.grid does not get nullified" );
+ map.destroy();
+ }
+
+
+ function test_Layer_XYZ_getXYZBounds(t) {
+ t.plan( 1 );
+
+ layer = new OpenLayers.Layer.XYZ(name, url, options);
+
+ var bl = { bounds: new OpenLayers.Bounds(1,2,2,3)};
+ var tr = { bounds: new OpenLayers.Bounds(2,3,3,4)};
+ layer.grid = [ [6, tr],
+ [bl, 7]];
+
+ var bounds = layer.getTilesBounds();
+
+ var testBounds = new OpenLayers.Bounds(1,2,3,4);
+
+ t.ok( bounds.equals(testBounds), "getXYZBounds() returns correct bounds")
+
+ layer.grid = null;
+ }
+
+ function test_Layer_XYZ_getResolution(t) {
+ t.plan( 1 );
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.XYZ(name, url, options);
+ map.addLayer(layer);
+
+ map.zoom = 5;
+
+ t.eq( layer.getResolution(), 0.0439453125, "getResolution() returns correct value");
+ map.destroy();
+ }
+
+ function test_Layer_XYZ_getZoomForExtent(t) {
+ t.plan( 2 );
+ var bounds, zoom;
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.XYZ(name, url, options);
+ map.addLayer(layer);
+
+ bounds = new OpenLayers.Bounds(10,10,12,12);
+ zoom = layer.getZoomForExtent(bounds);
+
+ t.eq( zoom, 8, "getZoomForExtent() returns correct value");
+
+ bounds = new OpenLayers.Bounds(10,10,100,100);
+ zoom = layer.getZoomForExtent(bounds);
+
+ t.eq( zoom, 2, "getZoomForExtent() returns correct value");
+ map.destroy();
+ }
+
+
+ /** THIS WOULD BE WHERE THE TESTS WOULD GO FOR
+ *
+ * -moveTo
+ * -insertColumn
+ * -insertRow
+
+ function 07_Layer_XYZ_moveTo(t) {
+ }
+
+ function 08_Layer_XYZ_insertColumn(t) {
+ }
+
+ function 09_Layer_XYZ_insertRow(t) {
+ }
+
+ *
+ */
+ function test_Layer_XYZ_getURL(t) {
+
+ t.plan(6);
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.XYZ(name, url);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 9);
+ var tileurl = layer.getURL(new OpenLayers.Bounds(3.515625,45,4.21875,45.703125));
+ t.eq(tileurl, "http://labs.metacarta.com/wms-c/Basic.py/1.0.0/basic/9/261/63.png", "Tile URL is correct");
+
+ layer.url = ["http://tilecache1/", "http://tilecache2/", "http://tilecache3/"];
+ tileurl = layer.getURL(new OpenLayers.Bounds(3.515625,45,4.21875,45.703125));
+ t.eq(tileurl, "http://tilecache1/", "Tile URL is deterministic");
+
+ layer.url = url;
+ tileurl = layer.getURL(new OpenLayers.Bounds(180.515625,45,181.21875,45.703125));
+ t.eq(tileurl, "http://labs.metacarta.com/wms-c/Basic.py/1.0.0/basic/9/513/63.png", "Tile URL is correct");
+ tileurl = layer.getURL(new OpenLayers.Bounds(-181.515625,45,-180.21875,45.703125));
+ t.eq(tileurl, "http://labs.metacarta.com/wms-c/Basic.py/1.0.0/basic/9/-2/63.png", "Tile URL is correct");
+ layer.wrapDateLine = true;
+ tileurl = layer.getURL(new OpenLayers.Bounds(180.515625,45,181.21875,45.703125));
+ t.eq(tileurl, "http://labs.metacarta.com/wms-c/Basic.py/1.0.0/basic/9/1/63.png", "Tile URL is correct");
+ tileurl = layer.getURL(new OpenLayers.Bounds(-181.515625,45,-180.21875,45.703125));
+ t.eq(tileurl, "http://labs.metacarta.com/wms-c/Basic.py/1.0.0/basic/9/510/63.png", "Tile URL is correct");
+ map.destroy();
+ }
+ function test_Layer_XYZ_Rounding(t) {
+ t.plan(1);
+ m = new OpenLayers.Map("map", {'maxExtent':new OpenLayers.Bounds(-122.6579,37.4901,-122.0738,37.8795)});
+ layer = new OpenLayers.Layer.XYZ( "XYZ",
+ url, {layername: 'basic', type:'png', resolutions:[0.000634956337608418], buffer: 2} );
+ m.addLayer(layer);
+ m.zoomToMaxExtent()
+ t.eq(layer.getURL(layer.grid[3][3].bounds), "http://labs.metacarta.com/wms-c/Basic.py/1.0.0/basic/0/1/0.png", "XYZ tiles around rounded properly.");
+ m.destroy();
+ }
+
+ function test_Layer_XYZ_setMap(t) {
+
+ t.plan(3);
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.XYZ(name, url);
+
+ t.eq(layer.tileOrigin, null, "Tile origin starts out null");
+ layer.setMap(map);
+
+ t.eq(layer.tileOrigin.lat, -90, "lat is -90");
+ t.eq(layer.tileOrigin.lon, -180, "lon is -180");
+ map.destroy();
+ }
+
+ function test_Layer_XYZ_serverResolutions(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map('map', {
+ resolutions: [13,11]
+ });
+
+ var layer = new OpenLayers.Layer.XYZ(name, url, options);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 1);
+
+ var tileurl = layer.getURL(new OpenLayers.Bounds(0,0,0,0));
+ var level = parseInt(tileurl.split('/')[7]);
+ t.eq(map.getZoom(), level, "Tile zoom level is correct without serverResolutions");
+
+ layer.serverResolutions = [14,13,12,11,10];
+ tileurl = layer.getURL(new OpenLayers.Bounds(0,0,0,0));
+ level = parseInt(tileurl.split('/')[7]);
+ var res = map.getResolution();
+ var gotLevel = OpenLayers.Util.indexOf(layer.serverResolutions, res);
+ t.eq(gotLevel, level, "Tile zoom level is correct with serverResolutions");
+
+ map.destroy();
+ }
+
+ function test_zoomOffset(t) {
+
+ t.plan(2);
+
+ var offset;
+
+ // test offset of 2
+ offset = 2;
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ maxResolution: 1.40625 / Math.pow(2, offset)
+ });
+ var layer = new OpenLayers.Layer.XYZ(name, url, {zoomOffset: offset});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 7);
+
+ var tileurl = layer.getURL(new OpenLayers.Bounds(3.515625,45,4.21875,45.703125));
+ t.eq(tileurl, "http://labs.metacarta.com/wms-c/Basic.py/1.0.0/basic/9/261/63.png", "correct URL for offset of 2");
+
+ map.destroy();
+
+ // test offset of -1
+ offset = -1;
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ maxResolution: 1.40625 / Math.pow(2, offset)
+ });
+ var layer = new OpenLayers.Layer.XYZ(name, url, {zoomOffset: offset});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 10);
+
+ var tileurl = layer.getURL(new OpenLayers.Bounds(3.515625,45,4.21875,45.703125));
+ t.eq(tileurl, "http://labs.metacarta.com/wms-c/Basic.py/1.0.0/basic/9/261/63.png", "correct URL for offset of -1");
+
+ map.destroy();
+
+
+ }
+
+ function test_Layer_XYZ_destroy (t) {
+
+ t.plan( 3 );
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.XYZ(name, url, options);
+ map.addLayer(layer);
+ layer.destroy();
+ t.eq( layer.grid, null, "layer.grid is null after destroy" );
+ t.eq( layer.tileSize, null, "layer.tileSize is null after destroy" );
+
+
+ //test with tile creation
+ layer = new OpenLayers.Layer.XYZ(name, url, options);
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+ //grab a reference to one of the tiles
+ var tile = layer.grid[0][0];
+
+ layer.destroy();
+
+ t.ok( layer.grid == null, "tiles appropriately destroyed");
+ map.destroy();
+ }
+
+ function test_clone(t) {
+ t.plan(1);
+
+ layer = new OpenLayers.Layer.XYZ(name, url, options);
+ var clone = layer.clone();
+ t.ok(clone instanceof OpenLayers.Layer.XYZ, "clone is a Layer.XYZ instance");
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Layer/atom-1.0.xml b/misc/openlayers/tests/Layer/atom-1.0.xml
new file mode 100644
index 0000000..f0d5d6f
--- /dev/null
+++ b/misc/openlayers/tests/Layer/atom-1.0.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom"
+ xmlns:georss="http://www.georss.org/georss">
+
+ <title>tumulus</title>
+ <link rel="self"
+ href="http://pleiades.stoa.org/places/tumulus"/>
+ <updated/>
+ <author/>
+ <id>http://pleiades.stoa.org/places/tumulus</id>
+
+ <entry>
+ <title>Unnamed Tumulus</title>
+ <link rel="alternate"
+ href="http://pleiades.stoa.org/places/638896"
+ />
+ <id>http://pleiades.stoa.org/places/638896</id>
+ <updated/>
+ <summary>An ancient tumulus, attested during the Classical period (modern location: Karaburun). Its ancient name is not known.</summary>
+ <georss:point>36.7702 29.9805</georss:point>
+ </entry>
+ <entry>
+ <title>Unnamed Tumulus</title>
+ <link rel="alternate"
+ href="http://pleiades.stoa.org/places/638924"
+ />
+ <id>http://pleiades.stoa.org/places/638924</id>
+ <updated/>
+ <summary>An ancient tumulus, attested during the Classical period (modern location: Kızılbel). Its ancient name is not known.</summary>
+ <georss:point>36.7263 29.8619</georss:point>
+ </entry>
+
+</feed>
+
diff --git a/misc/openlayers/tests/Layer/data_Layer_Text_textfile.txt b/misc/openlayers/tests/Layer/data_Layer_Text_textfile.txt
new file mode 100644
index 0000000..8250988
--- /dev/null
+++ b/misc/openlayers/tests/Layer/data_Layer_Text_textfile.txt
@@ -0,0 +1,3 @@
+point image
+10,20 http://boston.openguides.org/markers/ORANGE.png
+15,25 http://boston.openguides.org/markers/ORANGE.png
diff --git a/misc/openlayers/tests/Layer/data_Layer_Text_textfile_2.txt b/misc/openlayers/tests/Layer/data_Layer_Text_textfile_2.txt
new file mode 100644
index 0000000..91a8093
--- /dev/null
+++ b/misc/openlayers/tests/Layer/data_Layer_Text_textfile_2.txt
@@ -0,0 +1,3 @@
+point title description image
+10,20 a b http://boston.openguides.org/markers/ORANGE.png
+15,25 c d http://boston.openguides.org/markers/ORANGE.png
diff --git a/misc/openlayers/tests/Layer/data_Layer_Text_textfile_overflow.txt b/misc/openlayers/tests/Layer/data_Layer_Text_textfile_overflow.txt
new file mode 100644
index 0000000..bb4768e
--- /dev/null
+++ b/misc/openlayers/tests/Layer/data_Layer_Text_textfile_overflow.txt
@@ -0,0 +1,3 @@
+overflow point title description image
+auto 10,20 a b http://boston.openguides.org/markers/ORANGE.png
+hidden 15,25 c d http://boston.openguides.org/markers/ORANGE.png
diff --git a/misc/openlayers/tests/Layer/georss.txt b/misc/openlayers/tests/Layer/georss.txt
new file mode 100644
index 0000000..053749b
--- /dev/null
+++ b/misc/openlayers/tests/Layer/georss.txt
@@ -0,0 +1,378 @@
+<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/css" href="/css/rss.css" ?>
+
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://purl.org/rss/1.0/"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:georss="http://www.georss.org/georss">
+<docs>This is an RSS file. Copy the URL into your aggregator of choice. If you don't know what this means and want to learn more, please see: <span>http://platial.typepad.com/news/2006/04/really_simple_t.html</span> for more info.</docs><channel rdf:about="http://platial.com">
+<link>http://platial.com</link>
+<title>Crschmidt's Places At Platial</title>
+<description></description>
+<items>
+<rdf:Seq>
+<rdf:li resource="http://platial.com/place/90306"/>
+<rdf:li resource="http://platial.com/place/67230"/>
+<rdf:li resource="http://platial.com/place/65645"/>
+<rdf:li resource="http://platial.com/place/62200"/>
+<rdf:li resource="http://platial.com/place/28232"/>
+<rdf:li resource="http://platial.com/place/43666"/>
+<rdf:li resource="http://platial.com/place/28394"/>
+<rdf:li resource="http://platial.com/place/28251"/>
+<rdf:li resource="http://platial.com/place/28392"/>
+<rdf:li resource="http://platial.com/place/28391"/>
+<rdf:li resource="http://platial.com/place/28231"/>
+<rdf:li resource="http://platial.com/place/28393"/>
+<rdf:li resource="http://platial.com/place/31685"/>
+<rdf:li resource="http://platial.com/place/28596"/>
+<rdf:li resource="http://platial.com/place/28595"/>
+<rdf:li resource="http://platial.com/place/28594"/>
+<rdf:li resource="http://platial.com/place/28593"/>
+<rdf:li resource="http://platial.com/place/28592"/>
+<rdf:li resource="http://platial.com/place/28591"/>
+<rdf:li resource="http://platial.com/place/28590"/>
+<rdf:li resource="http://platial.com/place/28589"/>
+<rdf:li resource="http://platial.com/place/28588"/>
+<rdf:li resource="http://platial.com/place/28587"/>
+<rdf:li resource="http://platial.com/place/28586"/>
+<rdf:li resource="http://platial.com/place/28585"/>
+<rdf:li resource="http://platial.com/place/28584"/>
+<rdf:li resource="http://platial.com/place/28583"/>
+<rdf:li resource="http://platial.com/place/28582"/>
+<rdf:li resource="http://platial.com/place/28581"/>
+<rdf:li resource="http://platial.com/place/28580"/>
+<rdf:li resource="http://platial.com/place/28579"/>
+<rdf:li resource="http://platial.com/place/28578"/>
+<rdf:li resource="http://platial.com/place/28577"/>
+<rdf:li resource="http://platial.com/place/28576"/>
+<rdf:li resource="http://platial.com/place/28575"/>
+<rdf:li resource="http://platial.com/place/28574"/>
+<rdf:li resource="http://platial.com/place/28573"/>
+<rdf:li resource="http://platial.com/place/28572"/>
+<rdf:li resource="http://platial.com/place/28571"/>
+<rdf:li resource="http://platial.com/place/28570"/>
+</rdf:Seq>
+</items>
+</channel>
+<item rdf:about="http://platial.com/place/90306">
+<link>http://platial.com/place/90306</link>
+<title>Knitting Room</title>
+<description><![CDATA[This little shop is jammed full. Yarn, yarn everywhere. They make the most of every possible nook and cranny. I like this place also because they have a lot of different kinds of knitting needles in all different sizes. Also, the people who work here are younger and hipper than in the other stores I go to. I reccomend buying supplies here and then knitting your way through a good documentary at the Capitol Theater across the street.<br/>Address: 2 lake St, Arlington, MA <br/>Tags: knitting, yarn, pins and needles, handspun, hand dyed, novelty yarn, fancy, simple, young, hip, friendly, needles, addy, cute hats<br /><br /><a href="http://platial.com/place/90306">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/90306">Grab this on Platial</a> ]]></description>
+<georss:point>42.405696 -71.142197</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-06-08T17:35:01.942452+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/67230">
+<link>http://platial.com/place/67230</link>
+<title>Knitting Room</title>
+<description><![CDATA[This little shop is jammed full. Yarn, yarn everywhere. They make the most of every possible nook and cranny. I like this place also because they have a lot of different kinds of knitting needles in all different sizes. Also, the people who work here are younger and hipper than in the other stores I go to. I reccomend buying supplies here and then knitting your way through a good documentary at the Capitol Theater across the street.<br/>Address: 2 lake St, Arlington, MA <br/>Tags: knitting, yarn, pins and needles, handspun, hand dyed, novelty yarn, fancy, simple, young, hip, friendly, needles, addy, cute hats<br /><br /><a href="http://platial.com/place/67230">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/67230">Grab this on Platial</a> ]]></description>
+<georss:point>42.405524 -71.142273</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-04-24T11:35:26.733857+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/65645">
+<link>http://platial.com/place/65645</link>
+<title>†¢¢™£ˆøœ</title>
+<description><![CDATA[ijeª£∆µˆ˚î<br/>Address: 151 Erie St., Cambridge, MA<br/>Tags: platial graffiti<br /><br /><a href="http://platial.com/place/65645">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/65645">Grab this on Platial</a> ]]></description>
+<georss:point>42.352455 -71.110210</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-04-20T08:56:12.696224+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/62200">
+<link>http://platial.com/place/62200</link>
+<title>Allen Hall</title>
+<description><![CDATA[My dorm at UIUC.<br/>Address: 1301 W Gregory Dr, Urbana, IL<br/>Tags: dorm, uiuc, college<br/><a href="http://platial.com/place/62200"><img src="http://platial.comhttp://static.flickr.com/4/8576450_0d59cc2531_s.jpg"/></a><br/><br /><br /><a href="http://platial.com/place/62200">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/62200">Grab this on Platial</a> ]]></description>
+<georss:point>40.104172 -88.220623</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-04-14T08:01:01.872873+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28232">
+<link>http://platial.com/place/28232</link>
+<title>Bagby Hot Springs, OR</title>
+<description><![CDATA[Hot spring, temperature: 136 degress F, 58 degress C. However, the area around the springs are not exactly well looked upon by people who know the place.
+
+<br/>Tags: 20s, rosalie, romance, childhood, hike, camping, soak, relax, beautiful, hot springs, bathhouse, favorite, popular, crowded, organized, honeymoon tub, plumbing made from hollowed out trees, hot springs, mt hood, notorious car break in spot, rash, bacteria<br /><br /><a href="http://platial.com/place/28232">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28232">Grab this on Platial</a> ]]></description>
+<georss:point>44.936000 -122.173000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:10:18.553063+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/43666">
+<link>http://platial.com/place/43666</link>
+<title>Shooting Location for "The Field of Dreams" Film</title>
+<description><![CDATA[1989's Field of Dreams was a Best Picture Academy Award nominee, and the baseball field in the cornfield still stands today, and has become quite a tourist destination. Games are occasionally played at the field, re-enacting professional baseball at the turn of the 20th Century.<br/>Address: Dyersville, Iowa<br/>Tags: iowa, baseball, movie locations, field of dreams, kevin costner, costner, dyersville, kinsella, james earl jones, chicago black sox, shoeless joe, joe jackson, famous farms, film, movie, cinema, shooting location<br /><br /><a href="http://platial.com/place/43666">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/43666">Grab this on Platial</a> ]]></description>
+<georss:point>42.481213 -91.111679</georss:point>
+<dc:creator>echinodermata</dc:creator>
+<dc:date>2006-03-23T11:40:17.654061+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28394">
+<link>http://platial.com/place/28394</link>
+<title>Moffetts (Bonneville) Hot Springs, WA</title>
+<description><![CDATA[Hot spring, temperature: 97 degress F, 36 degress C<br/>Tags: soak, hot springs, relax, nature<br /><br /><a href="http://platial.com/place/28394">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28394">Grab this on Platial</a> ]]></description>
+<georss:point>45.658000 -121.962000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:16:27.329816+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28251">
+<link>http://platial.com/place/28251</link>
+<title>Austin Hot Springs, OR</title>
+<description><![CDATA[Hot spring, temperature: 186 degress F, 86 degress C<br/>Tags: soak, hot springs, relax, nature, popular, crowded<br /><br /><a href="http://platial.com/place/28251">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28251">Grab this on Platial</a> ]]></description>
+<georss:point>45.021000 -122.009000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:11:04.489886+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28392">
+<link>http://platial.com/place/28392</link>
+<title>Rock Creek Hot Springs, WA</title>
+<description><![CDATA[Hot spring, temperature: Hot degress F, Hot degress C<br/>Tags: soak, hot springs, relax, nature<br /><br /><a href="http://platial.com/place/28392">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28392">Grab this on Platial</a> ]]></description>
+<georss:point>45.723000 -121.927000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:16:22.636855+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28391">
+<link>http://platial.com/place/28391</link>
+<title>St. Martins (Wind River) Hot Springs, WA</title>
+<description><![CDATA[Hot spring, temperature: 120 degress F, 49 degress C<br/>Tags: hot springs, soak, relax, nature, wonderful<br /><br /><a href="http://platial.com/place/28391">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28391">Grab this on Platial</a> ]]></description>
+<georss:point>45.728000 -121.800000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:16:20.383244+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28231">
+<link>http://platial.com/place/28231</link>
+<title>Breitenbush Hot Springs, OR</title>
+<description><![CDATA[Hot spring, temperature: 198 degress F, 92 degress C<br/>Tags: hot springs, resort, relax, nature, beautiful, http:www.breitenbush.com, soaking<br /><br /><a href="http://platial.com/place/28231">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28231">Grab this on Platial</a> ]]></description>
+<georss:point>44.782000 -121.975000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:10:16.529195+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28393">
+<link>http://platial.com/place/28393</link>
+<title>Collins Hot Springs, WA</title>
+<description><![CDATA[Hot spring, temperature: 122 degress F, 50 degress C<br/>Tags: portland, nice, hot springs, soak<br /><br /><a href="http://platial.com/place/28393">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28393">Grab this on Platial</a> ]]></description>
+<georss:point>45.701000 -121.728000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:16:24.648745+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/31685">
+<link>http://platial.com/place/31685</link>
+<title>Darwin's Ltd.</title>
+<description><![CDATA[Nice little coffee shop/cafe, free Wifi, close enough to walk from Harvard Square.<br/>Address: 148 Mount Auburn St, Cambridge, MA<br/>Tags: coffee, beer, sandwiches, freewifi<br/><a href="http://platial.com/place/31685"><img src="http://platial.comhttp://static.flickr.com/38/84885937_74fd3d1025_s.jpg"/></a><br/><br /><br /><a href="http://platial.com/place/31685">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/31685">Grab this on Platial</a> ]]></description>
+<georss:point>42.373974 -71.125053</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-10T09:24:08.152985+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28596">
+<link>http://platial.com/place/28596</link>
+<title>Huckleberry Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: Boiling degress F, Boiling degress C<br /><br /><a href="http://platial.com/place/28596">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28596">Grab this on Platial</a> ]]></description>
+<georss:point>44.115000 -110.684000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:32.283094+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28595">
+<link>http://platial.com/place/28595</link>
+<title>South Entrance Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 156 degress F, 69 degress C<br/><a href="http://platial.com/place/28595"><img src="http://platial.comhttp://static.flickr.com/52/130989872_f1457f68b5_s.jpg"/></a><br/><br /><br /><a href="http://platial.com/place/28595">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28595">Grab this on Platial</a> ]]></description>
+<georss:point>44.142000 -110.656000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:30.279497+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28594">
+<link>http://platial.com/place/28594</link>
+<title>Crawfish Creek Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 136 degress F, 58 degress C<br/><a href="http://platial.com/place/28594"><img src="http://platial.comhttp://static.flickr.com/52/128312256_d6a879924c_s.jpg"/></a><br/><br /><br /><a href="http://platial.com/place/28594">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28594">Grab this on Platial</a> ]]></description>
+<georss:point>44.157000 -110.699000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:28.280271+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28593">
+<link>http://platial.com/place/28593</link>
+<title>Crawfish Creek Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 138 degress F, 59 degress C<br /><br /><a href="http://platial.com/place/28593">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28593">Grab this on Platial</a> ]]></description>
+<georss:point>44.165000 -110.723000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:20.364077+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28592">
+<link>http://platial.com/place/28592</link>
+<title>Snake Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 136 degress F, 58 degress C<br /><br /><a href="http://platial.com/place/28592">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28592">Grab this on Platial</a> ]]></description>
+<georss:point>44.169000 -110.583000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:12.234974+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28591">
+<link>http://platial.com/place/28591</link>
+<title>Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 142 degress F, 61 degress C<br /><br /><a href="http://platial.com/place/28591">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28591">Grab this on Platial</a> ]]></description>
+<georss:point>44.187000 -110.726000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:10.027857+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28590">
+<link>http://platial.com/place/28590</link>
+<title>Hot Springs on Upper Snake River, WY</title>
+<description><![CDATA[Hot spring, temperature: 167 degress F, 75 degress C<br /><br /><a href="http://platial.com/place/28590">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28590">Grab this on Platial</a> ]]></description>
+<georss:point>44.204000 -110.486000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:07.79658+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28589">
+<link>http://platial.com/place/28589</link>
+<title>Hot Springs on lewis Lake, WY</title>
+<description><![CDATA[Hot spring, temperature: 154 degress F, 68 degress C<br /><br /><a href="http://platial.com/place/28589">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28589">Grab this on Platial</a> ]]></description>
+<georss:point>44.276000 -110.636000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:05.683418+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28588">
+<link>http://platial.com/place/28588</link>
+<title>Rustic Geyser, WY</title>
+<description><![CDATA[Hot spring, temperature: 199 degress F, 93 degress C<br /><br /><a href="http://platial.com/place/28588">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28588">Grab this on Platial</a> ]]></description>
+<georss:point>44.282000 -110.506000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:03.66329+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28587">
+<link>http://platial.com/place/28587</link>
+<title>Bechler River Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 194 degress F, 90 degress C<br /><br /><a href="http://platial.com/place/28587">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28587">Grab this on Platial</a> ]]></description>
+<georss:point>44.285000 -110.900000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:01.611442+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28586">
+<link>http://platial.com/place/28586</link>
+<title>Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: Boiling degress F, 201 degress C<br /><br /><a href="http://platial.com/place/28586">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28586">Grab this on Platial</a> ]]></description>
+<georss:point>44.290000 -110.504000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:59.658699+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28585">
+<link>http://platial.com/place/28585</link>
+<title>Heart Lake Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: Middle Group degress F, 174 degress C<br /><br /><a href="http://platial.com/place/28585">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28585">Grab this on Platial</a> ]]></description>
+<georss:point>44.299000 -110.517000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:57.181801+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28584">
+<link>http://platial.com/place/28584</link>
+<title>Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: Boiling degress F, 201 degress C<br /><br /><a href="http://platial.com/place/28584">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28584">Grab this on Platial</a> ]]></description>
+<georss:point>44.307000 -110.526000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:55.240485+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28583">
+<link>http://platial.com/place/28583</link>
+<title>Hot Springs on lewis Lake, WY</title>
+<description><![CDATA[Hot spring, temperature: 199 degress F, 93 degress C<br /><br /><a href="http://platial.com/place/28583">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28583">Grab this on Platial</a> ]]></description>
+<georss:point>44.309000 -110.654000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:53.22295+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28582">
+<link>http://platial.com/place/28582</link>
+<title>Shoshone Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: 203 degress F, 95 degress C<br /><br /><a href="http://platial.com/place/28582">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28582">Grab this on Platial</a> ]]></description>
+<georss:point>44.354000 -110.800000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:51.179049+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28581">
+<link>http://platial.com/place/28581</link>
+<title>Hot Springs on Continental Divide, WY</title>
+<description><![CDATA[Hot spring, temperature: 189 degress F, 87 degress C<br /><br /><a href="http://platial.com/place/28581">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28581">Grab this on Platial</a> ]]></description>
+<georss:point>44.401000 -110.936000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:49.077176+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28580">
+<link>http://platial.com/place/28580</link>
+<title>Hot Springs on Upper Firehole River, WY</title>
+<description><![CDATA[Hot spring, temperature: Hot degress F, Hot degress C<br /><br /><a href="http://platial.com/place/28580">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28580">Grab this on Platial</a> ]]></description>
+<georss:point>44.404000 -110.824000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:47.054664+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28579">
+<link>http://platial.com/place/28579</link>
+<title>Summit Lake Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 162 degress F, 72 degress C<br /><br /><a href="http://platial.com/place/28579">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28579">Grab this on Platial</a> ]]></description>
+<georss:point>44.410000 -110.953000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:45.039394+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28578">
+<link>http://platial.com/place/28578</link>
+<title>Lone Star Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: Footbridge degress F, 183 degress C<br /><br /><a href="http://platial.com/place/28578">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28578">Grab this on Platial</a> ]]></description>
+<georss:point>44.414000 -110.817000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:42.938808+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28577">
+<link>http://platial.com/place/28577</link>
+<title>West. Thumb Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: 203 degress F, 95 degress C<br /><br /><a href="http://platial.com/place/28577">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28577">Grab this on Platial</a> ]]></description>
+<georss:point>44.417000 -110.570000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:40.90238+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28576">
+<link>http://platial.com/place/28576</link>
+<title>Lone Star Geyser, WY</title>
+<description><![CDATA[Hot spring, temperature: 199 degress F, 93 degress C<br /><br /><a href="http://platial.com/place/28576">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28576">Grab this on Platial</a> ]]></description>
+<georss:point>44.418000 -110.805000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:38.844625+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28575">
+<link>http://platial.com/place/28575</link>
+<title>Smoke Jumper Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 198 degress F, 92 degress C<br /><br /><a href="http://platial.com/place/28575">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28575">Grab this on Platial</a> ]]></description>
+<georss:point>44.421000 -110.952000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:36.818513+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28574">
+<link>http://platial.com/place/28574</link>
+<title>West. Thumb Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: 196 degress F, 91 degress C<br /><br /><a href="http://platial.com/place/28574">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28574">Grab this on Platial</a> ]]></description>
+<georss:point>44.422000 -110.574000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:34.767729+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28573">
+<link>http://platial.com/place/28573</link>
+<title>Potts Hot Spring Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: 203 degress F, 95 degress C<br /><br /><a href="http://platial.com/place/28573">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28573">Grab this on Platial</a> ]]></description>
+<georss:point>44.433000 -110.581000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:32.749915+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28572">
+<link>http://platial.com/place/28572</link>
+<title>Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: Hot degress F, Hot degress C<br /><br /><a href="http://platial.com/place/28572">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28572">Grab this on Platial</a> ]]></description>
+<georss:point>44.433000 -110.813000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:30.829745+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28571">
+<link>http://platial.com/place/28571</link>
+<title>Hot Springs on Continental Divide, WY</title>
+<description><![CDATA[Hot spring, temperature: Hot degress F, Hot degress C<br /><br /><a href="http://platial.com/place/28571">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28571">Grab this on Platial</a> ]]></description>
+<georss:point>44.438000 -110.977000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:28.730401+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28570">
+<link>http://platial.com/place/28570</link>
+<title>SouthEastern Group, WY</title>
+<description><![CDATA[Hot spring, temperature: 198 degress F, 92 degress C<br /><br /><a href="http://platial.com/place/28570">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28570">Grab this on Platial</a> ]]></description>
+<georss:point>44.459000 -110.817000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:26.706763+00:00</dc:date>
+</item>
+</rdf:RDF> \ No newline at end of file
diff --git a/misc/openlayers/tests/Map.html b/misc/openlayers/tests/Map.html
new file mode 100644
index 0000000..9693fb1
--- /dev/null
+++ b/misc/openlayers/tests/Map.html
@@ -0,0 +1,2255 @@
+<html>
+<head>
+ <script>
+ /**
+ * Because browsers that implement requestAnimationFrame may not execute
+ * animation functions while a window is not displayed (e.g. in a hidden
+ * iframe as in these tests), we mask the native implementations here. The
+ * native requestAnimationFrame functionality is tested in Util.html and
+ * in PanZoom.html (where a popup is opened before panning). The panTo tests
+ * here will test the fallback setTimeout implementation for animation.
+ */
+ window.requestAnimationFrame =
+ window.webkitRequestAnimationFrame =
+ window.mozRequestAnimationFrame =
+ window.oRequestAnimationFrame =
+ window.msRequestAnimationFrame = null;
+ </script>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var map;
+
+ function test_Map_constructor (t) {
+ t.plan( 11 );
+
+ map = new OpenLayers.Map('map');
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+
+ t.ok( OpenLayers.Element.hasClass(map.div, "olMap"), "Map div has olMap class");
+
+ t.ok( map instanceof OpenLayers.Map, "new OpenLayers.Map returns object" );
+ if (!isMozilla) {
+ t.ok( true, "skipping element test outside of Mozilla");
+ t.ok( true, "skipping element test outside of Mozilla");
+ t.ok( true, "skipping element test outside of Mozilla");
+ } else {
+ t.ok( map.div instanceof HTMLDivElement, "map.div is an HTMLDivElement" );
+ t.ok( map.viewPortDiv instanceof HTMLDivElement, "map.viewPortDiv is an HTMLDivElement" );
+ t.ok( map.layerContainerDiv instanceof HTMLDivElement, "map.layerContainerDiv is an HTMLDivElement" );
+ }
+ t.ok( map.layers instanceof Array, "map.layers is an Array" );
+ t.ok( map.controls instanceof Array, "map.controls is an Array" );
+ t.eq( map.controls.length, 4, "Default map has 4 controls." );
+ t.ok( map.events instanceof OpenLayers.Events, "map.events is an OpenLayers.Events" );
+ t.ok( map.getMaxExtent() instanceof OpenLayers.Bounds, "map.maxExtent is an OpenLayers.Bounds" );
+ t.ok( map.getNumZoomLevels() > 0, "map has a default numZoomLevels" );
+
+ map.destroy();
+ }
+
+ function test_Map_constructor_convenience(t) {
+ t.plan(13);
+ var map = new OpenLayers.Map({
+ maxExtent: [-170, -80, 170, 80],
+ restrictedExtent: [-120, -65, 120, 65],
+ layers: [
+ new OpenLayers.Layer(null, {isBaseLayer: true})
+ ],
+ center: [-111, 45],
+ zoom: 3
+ });
+
+ // maxExtent from array
+ t.ok(map.maxExtent instanceof OpenLayers.Bounds, "maxExtent bounds");
+ t.eq(map.maxExtent.left, -170, "maxExtent left");
+ t.eq(map.maxExtent.bottom, -80, "maxExtent bottom");
+ t.eq(map.maxExtent.right, 170, "maxExtent right");
+ t.eq(map.maxExtent.top, 80, "maxExtent top");
+
+ // restrictedExtent from array
+ t.ok(map.restrictedExtent instanceof OpenLayers.Bounds, "restrictedExtent bounds");
+ t.eq(map.restrictedExtent.left, -120, "restrictedExtent left");
+ t.eq(map.restrictedExtent.bottom, -65, "restrictedExtent bottom");
+ t.eq(map.restrictedExtent.right, 120, "restrictedExtent right");
+ t.eq(map.restrictedExtent.top, 65, "restrictedExtent top");
+
+ var center = map.getCenter();
+ t.eq(center.lon, -111, "center lon");
+ t.eq(center.lat, 45, "center lat");
+
+ t.eq(map.getZoom(), 3, "zoom");
+
+ map.destroy();
+ }
+
+ function test_Map_constructor_late_rendering(t) {
+ t.plan( 4 );
+
+ map = new OpenLayers.Map();
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+
+ t.ok(map.div != null, "Map has a div even though none was specified.");
+ t.ok(map.viewPortDiv.parentNode == map.div, "Map is attached to a temporary div that holds the viewPortDiv.");
+
+ var mapDiv = document.getElementById("map");
+ // clean up the effects of other tests
+ while(OpenLayers.Element.hasClass(mapDiv, "olMap")) {
+ OpenLayers.Element.removeClass(mapDiv, "olMap");
+ }
+ map.render(mapDiv); // Can also take a string.
+
+ t.ok(map.div == mapDiv, "Map is now rendered to the 'map' div.")
+ t.ok( OpenLayers.Element.hasClass(map.div, "olMap"), "Map div has olMap class");
+
+ map.destroy();
+
+ }
+
+ function test_Map_constructor_renderTo(t) {
+ t.plan( 1 );
+
+ map = new OpenLayers.Map({
+ div: "map"
+ });
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+
+ var mapDiv = document.getElementById("map");
+ t.ok(map.div == mapDiv, "Map is rendered to the 'map' div.")
+
+ map.destroy();
+ }
+
+ function test_Map_setOptions(t) {
+ t.plan(2);
+ map = new OpenLayers.Map('map', {maxExtent: new OpenLayers.Bounds(100, 200, 300, 400)});
+ map.setOptions({theme: 'foo'});
+
+ t.eq(map.theme, 'foo', "theme is correctly set by setOptions");
+ t.ok(map.maxExtent.equals(new OpenLayers.Bounds(100, 200, 300, 400)),
+ "maxExtent is correct after calling setOptions");
+
+ map.destroy();
+ }
+
+ function test_Map_add_layers(t) {
+ t.plan(8);
+ map = new OpenLayers.Map('map');
+ var layer1 = new OpenLayers.Layer.WMS("Layer 1",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ var layer2 = new OpenLayers.Layer.WMS("Layer 2",
+ "http://wms.jpl.nasa.gov/wms.cgi", {layers: "modis,global_mosaic"});
+ // this uses map.addLayer internally
+ map.addLayers([layer1, layer2])
+ t.eq( map.layers.length, 2, "map has exactly two layers" );
+ t.ok( map.layers[0] === layer1, "1st layer is layer1" );
+ t.ok( map.layers[1] === layer2, "2nd layer is layer2" );
+ t.ok( layer1.map === map, "layer.map is map" );
+ t.eq( parseInt(layer1.div.style.zIndex), map.Z_INDEX_BASE['BaseLayer'],
+ "layer1 zIndex is set" );
+ t.eq( parseInt(layer2.div.style.zIndex), map.Z_INDEX_BASE['BaseLayer'] + 5,
+ "layer2 zIndex is set" );
+
+ map.events.register('preaddlayer', this, function(evt) {
+ return !(evt.layer.name === 'donotadd');
+ });
+ var layer3 = new OpenLayers.Layer.WMS("donotadd",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayers([layer3]);
+ t.eq(map.layers.length, 2, "layer is not added since preaddlayer returns false");
+ layer3.name = 'pleaseadd';
+ map.addLayers([layer3]);
+ t.eq(map.layers.length, 3, "layer is added since preaddlayer returns true");
+
+ map.destroy();
+ }
+
+ function test_Map_options(t) {
+ t.plan(3);
+ map = new OpenLayers.Map('map', {numZoomLevels: 6, maxResolution: 3.14159, theme: 'foo'});
+ t.eq( map.numZoomLevels, 6, "map.numZoomLevels set correctly via options hashtable" );
+ t.eq( map.maxResolution, 3.14159, "map.maxResolution set correctly via options hashtable" );
+ t.eq( map.theme, 'foo', "map theme set correctly." );
+
+ map.destroy();
+ }
+
+ function test_eventListeners(t) {
+ t.plan(1);
+
+ var method = OpenLayers.Events.prototype.on;
+ // test that events.on is called at map construction
+ var options = {
+ eventListeners: {foo: "bar"},
+ tileManager: null,
+ controls: []
+ };
+ OpenLayers.Events.prototype.on = function(obj) {
+ t.eq(obj, options.eventListeners, "events.on called with eventListeners");
+ }
+ var map = new OpenLayers.Map('map', options);
+ OpenLayers.Events.prototype.on = method;
+ map.destroy();
+
+ // if events.on is called again, this will fail due to an extra test
+ // test map without eventListeners
+ OpenLayers.Events.prototype.on = function(obj) {
+ t.fail("events.on called without eventListeners");
+ }
+ var map2 = new OpenLayers.Map("map", {tileManager: null, controls: []});
+ OpenLayers.Events.prototype.on = method;
+ map2.destroy();
+ }
+
+ function test_Map_center(t) {
+ t.plan(14);
+ var log = [];
+ map = new OpenLayers.Map('map', {
+ zoomMethod: null,
+ eventListeners: {
+ "movestart": function() {log.push("movestart");},
+ "move": function() {log.push("move");},
+ "moveend": function() {log.push("moveend");}
+ }
+ });
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"} );
+ map.addLayer(baseLayer);
+ var ll = new OpenLayers.LonLat(2,1);
+ map.setCenter(ll, 0);
+ t.ok( map.getCenter() instanceof OpenLayers.LonLat, "map.getCenter returns a LonLat");
+ t.eq( map.getZoom(), 0, "map.zoom is correct after calling setCenter");
+ t.ok( map.getCenter().equals(ll), "map center is correct after calling setCenter");
+ map.zoomIn();
+ t.eq( map.getZoom(), 1, "map.zoom is correct after calling setCenter,zoom in");
+ t.ok( map.getCenter().equals(ll), "map center is correct after calling setCenter, zoom in");
+ map.zoomOut();
+ t.eq( map.getZoom(), 0, "map.zoom is correct after calling setCenter,zoom in, zoom out");
+
+ log = [];
+ map.zoomTo(5);
+ t.eq(log[0], "movestart", "zoomTo fires movestart event");
+ t.eq(log[1], "move", "zoomTo fires move event");
+ t.eq(log[2], "moveend", "zoomTo fires moveend event");
+ t.eq( map.getZoom(), 5, "map.zoom is correct after calling zoomTo" );
+
+ /**
+ map.zoomToMaxExtent();
+ t.eq( map.getZoom(), 2, "map.zoom is correct after calling zoomToMaxExtent" );
+ var lonlat = map.getCenter();
+ var zero = new OpenLayers.LonLat(0, 0);
+ t.ok( lonlat.equals(zero), "map center is correct after calling zoomToFullExtent" );
+ */
+
+ map.getCenter().lon = 10;
+ t.ok( map.getCenter().equals(ll), "map.getCenter returns a clone of map.center");
+
+ // allow calling setCenter with an array
+ map.setCenter([4, 2]);
+ var center = map.getCenter();
+ t.ok(center instanceof OpenLayers.LonLat, "(array) center is lonlat");
+ t.eq(center.lon, 4, "(array) center lon");
+ t.eq(center.lat, 2, "(array) center lat");
+
+ map.destroy();
+ }
+
+ function test_Map_zoomend_event (t) {
+ t.plan(2);
+
+ map = new OpenLayers.Map('map', {zoomMethod: null});
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+ map.events.register("zoomend", {count: 0}, function() {
+ this.count++;
+ t.ok(true, "zoomend event was triggered " + this.count + " times");
+ });
+ map.setCenter(new OpenLayers.LonLat(2, 1), 0);
+ map.zoomIn();
+ map.zoomOut();
+
+ map.destroy();
+ }
+
+ function test_Map_add_remove_popup (t) {
+ t.plan(4);
+
+ map = new OpenLayers.Map('map');
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+
+ var popup = new OpenLayers.Popup("chicken",
+ new OpenLayers.LonLat(0,0),
+ new OpenLayers.Size(200,200));
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ map.addPopup(popup);
+ var pIndex = OpenLayers.Util.indexOf(map.popups, popup);
+ t.eq(pIndex, 0, "popup successfully added to Map's internal popups array");
+
+ var nodes = map.layerContainerDiv.childNodes;
+
+ var found = false;
+ for (var i=0; i < nodes.length; i++) {
+ if (nodes.item(i) == popup.div) {
+ found = true;
+ break;
+ }
+ }
+ t.ok(found, "popup.div successfully added to the map's viewPort");
+
+
+ map.removePopup(popup);
+ var pIndex = OpenLayers.Util.indexOf(map.popups, popup);
+ t.eq(pIndex, -1, "popup successfully removed from Map's internal popups array");
+
+ var found = false;
+ for (var i=0; i < nodes.length; i++) {
+ if (nodes.item(i) == popup.div) {
+ found = true;
+ break;
+ }
+ }
+ t.ok(!found, "popup.div successfully removed from the map's viewPort");
+
+ map.destroy();
+ }
+
+ function test_Map_add_popup_exclusive(t) {
+ t.plan(2);
+
+ map = new OpenLayers.Map('map');
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ for (var i = 0; i < 10; i++) {
+ var popup = new OpenLayers.Popup("chicken",
+ new OpenLayers.LonLat(0,0),
+ new OpenLayers.Size(200,200));
+ map.addPopup(popup);
+ }
+ t.eq(map.popups.length, 10, "addPopup non exclusive mode works");
+
+ var popup = new OpenLayers.Popup("chicken",
+ new OpenLayers.LonLat(0,0),
+ new OpenLayers.Size(200,200));
+ map.addPopup(popup, true);
+ t.eq(map.popups.length, 1, "addPopup exclusive mode works");
+
+ map.destroy();
+ }
+
+
+/*** THIS IS A GOOD TEST, BUT IT SHOULD BE MOVED TO WMS.
+ * Also, it won't work until we figure out the viewSize bug
+
+ function 08_Map_px_lonlat_translation (t) {
+ t.plan( 6 );
+ map = new OpenLayers.Map('map');
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(baseLayer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ var pixel = new OpenLayers.Pixel(50,150);
+ var lonlat = map.getLonLatFromViewPortPx(pixel);
+ t.ok( lonlat instanceof OpenLayers.LonLat, "getLonLatFromViewPortPx returns valid OpenLayers.LonLat" );
+
+ var newPixel = map.getViewPortPxFromLonLat(lonlat);
+ t.ok( newPixel instanceof OpenLayers.Pixel, "getViewPortPxFromLonLat returns valid OpenLayers.Pixel" );
+
+ // WARNING!!! I'm faily sure that the following test's validity
+ // depends highly on rounding and the resolution. For now,
+ // in the default case, it seems to work. This may not
+ // always be so.
+ t.ok( newPixel.equals(pixel), "Translation to pixel and back to lonlat is consistent");
+
+ lonlat = map.getLonLatFromPixel(pixel);
+ t.ok( lonlat instanceof OpenLayers.LonLat, "getLonLatFromPixel returns valid OpenLayers.LonLat" );
+
+ newPixel = map.getPixelFromLonLat(lonlat);
+ t.ok( newPixel instanceof OpenLayers.Pixel, "getPixelFromLonLat returns valid OpenLayers.Pixel" );
+
+ t.ok( newPixel.equals(pixel), "2nd translation to pixel and back to lonlat is consistent");
+ }
+ */
+
+ function test_Map_isValidZoomLevel(t) {
+ t.plan(5);
+ var map = new OpenLayers.Map("map");
+ map.addLayer(new OpenLayers.Layer(null, {
+ isBaseLayer: true, wrapDateLine: true, numZoomLevels: 19
+ }));
+ map.zoomToMaxExtent();
+
+ var valid;
+
+ valid = OpenLayers.Map.prototype.isValidZoomLevel.apply(map, [-1]);
+ t.eq(valid, false, "-1 is not a valid zoomLevel");
+
+ valid = OpenLayers.Map.prototype.isValidZoomLevel.apply(map, [0]);
+ t.eq(valid, true, "0 is a valid zoomLevel");
+
+ valid = OpenLayers.Map.prototype.isValidZoomLevel.apply(map, [18]);
+ t.eq(valid, true, "18 is a valid zoomLevel");
+
+ valid = OpenLayers.Map.prototype.isValidZoomLevel.apply(map, [19]);
+ t.eq(valid, false, "19 is not a valid zoomLevel");
+
+ map.moveTo([16, 48], 0);
+ t.eq(map.getCenter().toShortString(), "0, 0", "no panning when moveTo is called with invalid zoom");
+
+ map.destroy();
+ }
+
+ function test_Map_isValidLonLat(t) {
+ t.plan( 3 );
+
+ map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS('Test Layer',
+ "http://octo.metacarta.com/cgi-bin/mapserv",
+ {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'},
+ {maxExtent: new OpenLayers.Bounds(33861, 717605, 330846, 1019656), maxResolution: 296985/1024, projection:"EPSG:2805" } );
+ map.addLayer(layer);
+
+ t.ok( !map.isValidLonLat(null), "null lonlat is not valid" );
+ t.ok( map.isValidLonLat(new OpenLayers.LonLat(33862, 717606)), "lonlat outside max extent is valid" );
+ t.ok( !map.isValidLonLat(new OpenLayers.LonLat(10, 10)), "lonlat outside max extent is not valid" );
+
+ map.destroy();
+ }
+
+ function test_Map_getLayer(t) {
+ var numLayers = 3;
+ t.plan( numLayers + 1 );
+
+ var m = {
+ layers: []
+ };
+
+ for(var i = 0; i < numLayers; i++) {
+ m.layers.push( { 'id': i } );
+ }
+
+ for(var i = 0; i < numLayers; i++) {
+ var layer = OpenLayers.Map.prototype.getLayer.apply(m, [i]);
+ t.ok( layer == m.layers[i], "getLayer correctly returns layer " + i);
+ }
+
+ var gotLayer = OpenLayers.Map.prototype.getLayer.apply(m, ["chicken"]);
+ t.ok( gotLayer == null, "getLayer correctly returns null when layer not found");
+
+ map.destroy();
+ }
+
+ function test_Map_getLayersBy(t) {
+
+ var map = {
+ getBy: OpenLayers.Map.prototype.getBy,
+ getLayersBy: OpenLayers.Map.prototype.getLayersBy,
+ layers: [
+ {foo: "foo", id: Math.random()},
+ {foo: "bar", id: Math.random()},
+ {foo: "foobar", id: Math.random()},
+ {foo: "foo bar", id: Math.random()},
+ {foo: "foo", id: Math.random()}
+ ]
+ };
+
+ var cases = [
+ {
+ got: map.getLayersBy("foo", "foo"),
+ expected: [map.layers[0], map.layers[4]],
+ message: "(string literal) got two layers matching foo"
+ }, {
+ got: map.getLayersBy("foo", "bar"),
+ expected: [map.layers[1]],
+ message: "(string literal) got one layer matching foo"
+ }, {
+ got: map.getLayersBy("foo", "barfoo"),
+ expected: [],
+ message: "(string literal) got empty array for no foo match"
+ }, {
+ got: map.getLayersBy("foo", /foo/),
+ expected: [map.layers[0], map.layers[2], map.layers[3], map.layers[4]],
+ message: "(regexp literal) got three layers containing string"
+ }, {
+ got: map.getLayersBy("foo", /foo$/),
+ expected: [map.layers[0], map.layers[4]],
+ message: "(regexp literal) got three layers ending with string"
+ }, {
+ got: map.getLayersBy("foo", /\s/),
+ expected: [map.layers[3]],
+ message: "(regexp literal) got layer containing space"
+ }, {
+ got: map.getLayersBy("foo", new RegExp("BAR", "i")),
+ expected: [map.layers[1], map.layers[2], map.layers[3]],
+ message: "(regexp object) got layers ignoring case"
+ }, {
+ got: map.getLayersBy("foo", {test: function(str) {return str.length > 3;}}),
+ expected: [map.layers[2], map.layers[3]],
+ message: "(custom object) got layers with foo length greater than 3"
+ }
+ ];
+ t.plan(cases.length);
+ for(var i=0; i<cases.length; ++i) {
+ t.eq(cases[i].got, cases[i].expected, cases[i].message);
+ }
+
+ }
+
+ function test_Map_getLayersByName(t) {
+
+ var map = {
+ getBy: OpenLayers.Map.prototype.getBy,
+ getLayersBy: OpenLayers.Map.prototype.getLayersBy,
+ getLayersByName: OpenLayers.Map.prototype.getLayersByName,
+ layers: [
+ {name: "foo", id: Math.random()},
+ {name: "bar", id: Math.random()},
+ {name: "foobar", id: Math.random()},
+ {name: "foo bar", id: Math.random()},
+ {name: "foo", id: Math.random()}
+ ]
+ };
+
+ var cases = [
+ {
+ got: map.getLayersByName("foo"),
+ expected: [map.layers[0], map.layers[4]],
+ message: "(string literal) got two layers matching name"
+ }, {
+ got: map.getLayersByName("bar"),
+ expected: [map.layers[1]],
+ message: "(string literal) got one layer matching name"
+ }, {
+ got: map.getLayersByName("barfoo"),
+ expected: [],
+ message: "(string literal) got empty array for no match"
+ }, {
+ got: map.getLayersByName(/foo/),
+ expected: [map.layers[0], map.layers[2], map.layers[3], map.layers[4]],
+ message: "(regexp literal) got three layers containing string"
+ }, {
+ got: map.getLayersByName(/foo$/),
+ expected: [map.layers[0], map.layers[4]],
+ message: "(regexp literal) got three layers ending with string"
+ }, {
+ got: map.getLayersByName(/\s/),
+ expected: [map.layers[3]],
+ message: "(regexp literal) got layer containing space"
+ }, {
+ got: map.getLayersByName(new RegExp("BAR", "i")),
+ expected: [map.layers[1], map.layers[2], map.layers[3]],
+ message: "(regexp object) got layers ignoring case"
+ }, {
+ got: map.getLayersByName({test: function(str) {return str.length > 3;}}),
+ expected: [map.layers[2], map.layers[3]],
+ message: "(custom object) got layers with name length greater than 3"
+ }
+ ];
+ t.plan(cases.length);
+ for(var i=0; i<cases.length; ++i) {
+ t.eq(cases[i].got, cases[i].expected, cases[i].message);
+ }
+
+ }
+
+ function test_Map_getLayersByClass(t) {
+
+ var map = {
+ getBy: OpenLayers.Map.prototype.getBy,
+ getLayersBy: OpenLayers.Map.prototype.getLayersBy,
+ getLayersByClass: OpenLayers.Map.prototype.getLayersByClass,
+ layers: [
+ {CLASS_NAME: "foo", id: Math.random()},
+ {CLASS_NAME: "bar", id: Math.random()},
+ {CLASS_NAME: "foobar", id: Math.random()},
+ {CLASS_NAME: "foo bar", id: Math.random()},
+ {CLASS_NAME: "foo", id: Math.random()}
+ ]
+ };
+
+ var cases = [
+ {
+ got: map.getLayersByClass("foo"),
+ expected: [map.layers[0], map.layers[4]],
+ message: "(string literal) got two layers matching type"
+ }, {
+ got: map.getLayersByClass("bar"),
+ expected: [map.layers[1]],
+ message: "(string literal) got one layer matching type"
+ }, {
+ got: map.getLayersByClass("barfoo"),
+ expected: [],
+ message: "(string literal) got empty array for no match"
+ }, {
+ got: map.getLayersByClass(/foo/),
+ expected: [map.layers[0], map.layers[2], map.layers[3], map.layers[4]],
+ message: "(regexp literal) got three layers containing string"
+ }, {
+ got: map.getLayersByClass(/foo$/),
+ expected: [map.layers[0], map.layers[4]],
+ message: "(regexp literal) got three layers ending with string"
+ }, {
+ got: map.getLayersByClass(/\s/),
+ expected: [map.layers[3]],
+ message: "(regexp literal) got layer containing space"
+ }, {
+ got: map.getLayersByClass(new RegExp("BAR", "i")),
+ expected: [map.layers[1], map.layers[2], map.layers[3]],
+ message: "(regexp object) got layers ignoring case"
+ }, {
+ got: map.getLayersByClass({test: function(str) {return str.length > 3;}}),
+ expected: [map.layers[2], map.layers[3]],
+ message: "(custom object) got layers with type length greater than 3"
+ }
+ ];
+ t.plan(cases.length);
+ for(var i=0; i<cases.length; ++i) {
+ t.eq(cases[i].got, cases[i].expected, cases[i].message);
+ }
+
+ }
+
+ function test_Map_getControlsBy(t) {
+
+ var map = {
+ getBy: OpenLayers.Map.prototype.getBy,
+ getControlsBy: OpenLayers.Map.prototype.getControlsBy,
+ controls: [
+ {foo: "foo", id: Math.random()},
+ {foo: "bar", id: Math.random()},
+ {foo: "foobar", id: Math.random()},
+ {foo: "foo bar", id: Math.random()},
+ {foo: "foo", id: Math.random()}
+ ]
+ };
+
+ var cases = [
+ {
+ got: map.getControlsBy("foo", "foo"),
+ expected: [map.controls[0], map.controls[4]],
+ message: "(string literal) got two controls matching foo"
+ }, {
+ got: map.getControlsBy("foo", "bar"),
+ expected: [map.controls[1]],
+ message: "(string literal) got one control matching foo"
+ }, {
+ got: map.getControlsBy("foo", "barfoo"),
+ expected: [],
+ message: "(string literal) got empty array for no foo match"
+ }, {
+ got: map.getControlsBy("foo", /foo/),
+ expected: [map.controls[0], map.controls[2], map.controls[3], map.controls[4]],
+ message: "(regexp literal) got three controls containing string"
+ }, {
+ got: map.getControlsBy("foo", /foo$/),
+ expected: [map.controls[0], map.controls[4]],
+ message: "(regexp literal) got three controls ending with string"
+ }, {
+ got: map.getControlsBy("foo", /\s/),
+ expected: [map.controls[3]],
+ message: "(regexp literal) got control containing space"
+ }, {
+ got: map.getControlsBy("foo", new RegExp("BAR", "i")),
+ expected: [map.controls[1], map.controls[2], map.controls[3]],
+ message: "(regexp object) got layers ignoring case"
+ }, {
+ got: map.getControlsBy("foo", {test: function(str) {return str.length > 3;}}),
+ expected: [map.controls[2], map.controls[3]],
+ message: "(custom object) got controls with foo length greater than 3"
+ }
+ ];
+ t.plan(cases.length);
+ for(var i=0; i<cases.length; ++i) {
+ t.eq(cases[i].got, cases[i].expected, cases[i].message);
+ }
+
+ }
+
+ function test_Map_getControlsByClass(t) {
+
+ var map = {
+ getBy: OpenLayers.Map.prototype.getBy,
+ getControlsBy: OpenLayers.Map.prototype.getControlsBy,
+ getControlsByClass: OpenLayers.Map.prototype.getControlsByClass,
+ controls: [
+ {CLASS_NAME: "foo", id: Math.random()},
+ {CLASS_NAME: "bar", id: Math.random()},
+ {CLASS_NAME: "foobar", id: Math.random()},
+ {CLASS_NAME: "foo bar", id: Math.random()},
+ {CLASS_NAME: "foo", id: Math.random()}
+ ]
+ };
+
+ var cases = [
+ {
+ got: map.getControlsByClass("foo"),
+ expected: [map.controls[0], map.controls[4]],
+ message: "(string literal) got two controls matching type"
+ }, {
+ got: map.getControlsByClass("bar"),
+ expected: [map.controls[1]],
+ message: "(string literal) got one control matching type"
+ }, {
+ got: map.getControlsByClass("barfoo"),
+ expected: [],
+ message: "(string literal) got empty array for no match"
+ }, {
+ got: map.getControlsByClass(/foo/),
+ expected: [map.controls[0], map.controls[2], map.controls[3], map.controls[4]],
+ message: "(regexp literal) got three controls containing string"
+ }, {
+ got: map.getControlsByClass(/foo$/),
+ expected: [map.controls[0], map.controls[4]],
+ message: "(regexp literal) got three controls ending with string"
+ }, {
+ got: map.getControlsByClass(/\s/),
+ expected: [map.controls[3]],
+ message: "(regexp literal) got control containing space"
+ }, {
+ got: map.getControlsByClass(new RegExp("BAR", "i")),
+ expected: [map.controls[1], map.controls[2], map.controls[3]],
+ message: "(regexp object) got controls ignoring case"
+ }, {
+ got: map.getControlsByClass({test: function(str) {return str.length > 3;}}),
+ expected: [map.controls[2], map.controls[3]],
+ message: "(custom object) got controls with type length greater than 3"
+ }
+ ];
+ t.plan(cases.length);
+ for(var i=0; i<cases.length; ++i) {
+ t.eq(cases[i].got, cases[i].expected, cases[i].message);
+ }
+
+ }
+
+ function test_Map_double_addLayer(t) {
+ t.plan(2);
+
+ map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS('Test Layer',
+ "http://octo.metacarta.com/cgi-bin/mapserv",
+ {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'}
+ );
+
+ var added = map.addLayer(layer);
+ t.ok(added === true, "Map.addLayer returns true if the layer is added to the map.");
+ var added = map.addLayer(layer);
+ t.ok(added === false, "Map.addLayer returns false if the layer is already present.");
+
+ map.destroy();
+ }
+
+ function test_Map_setBaseLayer(t) {
+ t.plan( 6 );
+
+ map = new OpenLayers.Map('map');
+
+ var wmslayer = new OpenLayers.Layer.WMS('Test Layer',
+ "http://octo.metacarta.com/cgi-bin/mapserv",
+ {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'},
+ {maxExtent: new OpenLayers.Bounds(33861, 717605, 330846, 1019656), maxResolution: 296985/1024, projection:"EPSG:2805" } );
+
+ var wmslayer2 = new OpenLayers.Layer.WMS('Test Layer2',
+ "http://octo.metacarta.com/cgi-bin/mapserv",
+ {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'},
+ {maxExtent: new OpenLayers.Bounds(33861, 717605, 330846, 1019656), maxResolution: 296985/1024, projection:"EPSG:2805" } );
+
+ map.addLayers([wmslayer, wmslayer2]);
+
+ t.ok(map.baseLayer == wmslayer, "default base layer is first one added");
+
+ map.setBaseLayer(null);
+ t.ok(map.baseLayer == wmslayer, "setBaseLayer on null object does nothing (and does not break)");
+
+ map.setBaseLayer("chicken");
+ t.ok(map.baseLayer == wmslayer, "setBaseLayer on non-layer object does nothing (and does not break)");
+
+ map.setBaseLayer(wmslayer2);
+ t.ok(map.baseLayer == wmslayer2, "setbaselayer correctly sets 'baseLayer' property");
+
+ map.destroy();
+
+ var l1 = new OpenLayers.Layer(),
+ l2 = new OpenLayers.Layer(null, {maxResolution: 1.4});
+ map = new OpenLayers.Map({
+ div: 'map',
+ allOverlays: true,
+ layers: [l1, l2],
+ zoom: 0,
+ center: [0, 0]
+ });
+ t.eq(l2.div.style.display, "none", "Layer invisible because not in range");
+ map.raiseLayer(l1, 1);
+ t.eq(l2.div.style.display, "block", "Layer visible after base layer change because in range now");
+ map.destroy();
+ }
+
+ function test_Map_removeLayer(t) {
+ t.plan(1);
+ var f = function() {};
+ var events = {triggerEvent: f};
+ var layers = [
+ {name: "fee", removeMap: f, events: events},
+ {name: "fi", removeMap: f, events: events},
+ {name: "fo", removeMap: f, events: events},
+ {name: "fum", removeMap: f, events: events}
+ ];
+ var map = {
+ layers: layers,
+ baseLayer: layers[0],
+ layerContainerDiv: {removeChild: f},
+ events: {triggerEvent: f},
+ resetLayersZIndex: function() {}
+ };
+ OpenLayers.Map.prototype.removeLayer.apply(map, [map.baseLayer, false]);
+ t.eq(map.baseLayer, null,
+ "removing the baselayer sets baseLayer to null");
+ }
+
+ function test_Map_removeLayer_res(t) {
+ t.plan(2);
+
+ map = new OpenLayers.Map('map');
+
+ var layer0 = new OpenLayers.Layer.WMS(
+ 'Test Layer 0',
+ "http://octo.metacarta.com/cgi-bin/mapserv",
+ {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'},
+ {resolutions: [4, 2, 1]}
+ );
+
+ var layer1 = new OpenLayers.Layer.WMS(
+ 'Test Layer 1',
+ "http://octo.metacarta.com/cgi-bin/mapserv",
+ {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'},
+ {resolutions: [4, 2]}
+ );
+
+ map.addLayers([layer0, layer1]);
+ map.zoomToMaxExtent();
+ map.zoomTo(2);
+ t.eq(map.getResolution(), layer0.resolutions[2],
+ "correct resolution before removal");
+ map.removeLayer(layer0);
+ t.eq(map.getResolution(), layer0.resolutions[1],
+ "correct resolution after removal");
+
+ map.destroy();
+ }
+
+ function test_Map_removeLayer_zindex(t) {
+ t.plan(2);
+
+ map = new OpenLayers.Map('map');
+
+ var layer0 = new OpenLayers.Layer('Test Layer 0', {isBaseLayer:true});
+ var layer1 = new OpenLayers.Layer('Test Layer 1', {isBaseLayer:true});
+ var layer2 = new OpenLayers.Layer('Test Layer 2', {isBaseLayer:false});
+
+ map.addLayers([layer0, layer1, layer2]);
+ map.removeLayer(layer0);
+
+ t.eq(parseInt(layer1.div.style.zIndex), map.Z_INDEX_BASE['BaseLayer'],
+ "correct z-index after removeLayer");
+ t.eq(parseInt(layer2.div.style.zIndex), map.Z_INDEX_BASE['Overlay'] + 5,
+ "correct z-index after removeLayer");
+
+ map.destroy();
+ }
+
+ function test_Map_removeLayer_preremovelayer(t) {
+ t.plan(4);
+ map = new OpenLayers.Map('map');
+
+ map.addLayer(new OpenLayers.Layer());
+ map.removeLayer(map.layers[0]);
+
+ // one test: standard behaviour without listener
+ t.eq(map.layers.length, 0, "without registered preremovelayer-listener layers can be removed as usual");
+
+ var callCnt = 0;
+
+ map.events.register('preremovelayer', this, function(evt) {
+ callCnt++;
+ return !(evt.layer.name === 'donotremove');
+ });
+ var layer1 = new OpenLayers.Layer('donotremove');
+ var layer2 = new OpenLayers.Layer('doremove');
+
+ map.addLayers([layer1,layer2]);
+
+ // two tests: remove action can be canceled
+ map.removeLayer(layer1);
+ t.eq(map.layers.length, 2, "layer is not removed since preremovelayer returns false");
+ map.removeLayer(layer2);
+ t.eq(map.layers.length, 1, "layer is removed since preremovelayer returns true");
+
+ // one test: listener was called twice
+ t.eq(callCnt, 2, "preremovelayer-listener was called exactly twice");
+
+ map.destroy();
+ }
+
+ function test_Map_setBaseLayer_after_pan (t) {
+ t.plan(1);
+
+ map = new OpenLayers.Map('map');
+ var wmsLayer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
+ var tmsLayer = new OpenLayers.Layer.TMS("TMS",
+ "http://labs.metacarta.com/wms-c/Basic.py/",
+ {'layername':'basic', 'type':'png'});
+ map.addLayers([wmsLayer,tmsLayer]);
+ map.setBaseLayer(wmsLayer);
+ map.zoomToMaxExtent();
+ map.setBaseLayer(tmsLayer);
+ map.zoomIn();
+ map.pan(0, -200, {animate:false});
+ var log = [];
+ map.applyTransform = function(x, y, scale) {
+ log.push([x || map.layerContainerOriginPx.x, y || map.layerContainerOriginPx.y, scale]);
+ OpenLayers.Map.prototype.applyTransform.apply(this, arguments);
+ };
+ map.setBaseLayer(wmsLayer);
+ t.eq(log[0][0], 0, "layerContainer is recentered after setBaseLayer");
+
+ map.destroy();
+ }
+
+ function test_Map_moveLayer (t) {
+ t.plan(10);
+
+ var ct = 0;
+ map = new OpenLayers.Map('map');
+ var wmslayer = new OpenLayers.Layer.WMS('Test Layer',
+ "http://octo.metacarta.com/cgi-bin/mapserv",
+ {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'},
+ {maxExtent: new OpenLayers.Bounds(33861, 717605, 330846, 1019656), maxResolution: 296985/1024, projection:"EPSG:2805" } );
+
+ var wmslayer2 = new OpenLayers.Layer.WMS('Test Layer2',
+ "http://octo.metacarta.com/cgi-bin/mapserv",
+ {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'},
+ {maxExtent: new OpenLayers.Bounds(33861, 717605, 330846, 1019656), maxResolution: 296985/1024, projection:"EPSG:2805" } );
+
+ var wmslayer3 = new OpenLayers.Layer.WMS('Test Layer2',
+ "http://octo.metacarta.com/cgi-bin/mapserv",
+ {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'},
+ {maxExtent: new OpenLayers.Bounds(33861, 717605, 330846, 1019656), maxResolution: 296985/1024, projection:"EPSG:2805" } );
+
+ map.addLayers([wmslayer, wmslayer2, wmslayer3]);
+ map.events.register("changelayer", map, function (e) { ct++; });
+ t.eq( map.getNumLayers(), 3, "getNumLayers returns the number of layers" );
+ t.eq( map.getLayerIndex(wmslayer3), 2, "getLayerIndex returns the right index" );
+ map.raiseLayer(wmslayer3, 1);
+ t.eq( map.getLayerIndex(wmslayer3), 2, "can't moveLayer up past the top of the stack" );
+ map.raiseLayer(wmslayer, -1);
+ t.eq( map.getLayerIndex(wmslayer), 0, "can't moveLayer down past the bottom of the stack" );
+ map.raiseLayer(wmslayer3, -1);
+ t.eq( map.getLayerIndex(wmslayer3), 1, "can moveLayer down from the top" );
+ t.eq( parseInt(wmslayer3.div.style.zIndex), map.Z_INDEX_BASE['BaseLayer'] + 5,
+ "layer div has the right zIndex after moving down" );
+ map.raiseLayer(wmslayer, 2);
+ t.eq( map.getLayerIndex(wmslayer), 2, "can moveLayer up from the bottom" );
+ t.eq( parseInt(wmslayer.div.style.zIndex), map.Z_INDEX_BASE['BaseLayer'] + 2 * 5,
+ "layer div has the right zIndex after moving up" );
+ t.eq( map.getLayerIndex(wmslayer3), 0, "top layer is now on the bottom" );
+ t.eq( ct, 3, "raiseLayer triggered changelayer the right # of times" );
+
+ map.destroy();
+ }
+
+ function test_Map_moveTo(t) {
+ t.plan(2);
+
+ map = new OpenLayers.Map('map');
+ var baseLayer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"},
+ {maxResolution: 'auto', maxExtent: new OpenLayers.Bounds(-10,-10,10,10)});
+ baseLayer.events.on({
+ move: function() {
+ t.ok(true, "move listener called");
+ },
+ movestart: function(e) {
+ t.eq(e.zoomChanged, true, "movestart listener called with expected value");
+ }
+ });
+ baseLayer.events.on({
+ moveend: function(e) {
+ t.eq(e.zoomChanged, true, "moveend listener called with expected value");
+ }
+ });
+ map.addLayer(baseLayer);
+ var ll = new OpenLayers.LonLat(-100,-150);
+ map.moveTo(ll, 2);
+ t.ok(map.getCenter().equals(new OpenLayers.LonLat(0,0)), "safely sets out-of-bounds lonlat");
+
+ map.destroy();
+ }
+
+ function test_Map_defaultTheme(t) {
+ t.plan(5);
+
+ var links = document.getElementsByTagName('link');
+ map = new OpenLayers.Map('map');
+ var gotNodes = 0;
+ var themeNode = null;
+ for(var i=0; i<links.length; ++i) {
+ if(OpenLayers.Util.isEquivalentUrl(map.theme, links.item(i).href)) {
+ gotNodes += 1;
+ themeNode = links.item(i);
+ }
+ }
+ t.eq(gotNodes, 1, "by default, a single link node is added to document");
+ t.ok(themeNode != null, "a link node with the theme href was added");
+ t.eq(themeNode.rel, "stylesheet", "node added has rel set to stylesheet");
+ t.eq(themeNode.type, "text/css", "node added has type set to text/css");
+
+ // reconstruct the map to prove that another link is not added
+ map = new OpenLayers.Map('map');
+ t.eq(links.length, document.getElementsByTagName('link').length,
+ "calling the map constructor twice with the same theme doesn't add duplicate link nodes");
+
+ map.destroy();
+ }
+
+ function test_Map_customTheme(t) {
+ t.plan(5);
+
+ var customTheme = 'foo';
+ var options = {theme: customTheme};
+ map = new OpenLayers.Map('map', options);
+
+ var links = document.getElementsByTagName('link');
+ var gotNodes = 0;
+ var themeNode = null;
+ for(var i=0; i<links.length; ++i) {
+ if(OpenLayers.Util.isEquivalentUrl(map.theme, links.item(i).href)) {
+ gotNodes += 1;
+ themeNode = links.item(i);
+ }
+ }
+
+ t.eq(map.theme, customTheme, "map theme is properly set");
+ t.eq(gotNodes, 1, "with custom theme, a single link node is added to document");
+ t.ok(themeNode != null, "a link node with the theme href was added");
+ t.eq(themeNode.rel, "stylesheet", "node added has rel set to stylesheet");
+ t.eq(themeNode.type, "text/css", "node added has type set to text/css");
+
+ map.destroy();
+ }
+
+ function test_Map_noTheme(t) {
+ t.plan(1);
+
+ var head = document.getElementsByTagName('head')[0];
+ var nodeCount = head.childNodes.length;
+
+ var options = {theme: null};
+ map = new OpenLayers.Map('map', options);
+
+ t.eq(nodeCount, head.childNodes.length, "with no theme, a node is not added to document head" );
+
+ map.destroy();
+ }
+
+ function test_Map_addControls(t) {
+ t.plan(5);
+ var map = new OpenLayers.Map('map', {
+ controls: []
+ });
+ var controls = [
+ new OpenLayers.Control({id:'firstctrl'}),
+ new OpenLayers.Control({id:'secondctrl'})
+ ];
+ map.addControls(controls);
+ t.eq(map.controls.length, 2, "two controls were added by map.addControls without a px-array");
+ t.eq(map.controls[0].id, 'firstctrl', "control with index 0 has id 'firstctrl'");
+ t.eq(map.controls[1].id, 'secondctrl', "control with index 1 has id 'secondctrl'");
+
+ var controls2 = [
+ new OpenLayers.Control({id:'thirdctrl'}),
+ new OpenLayers.Control({id:'fourthctrl'}),
+ new OpenLayers.Control({id:'fifthctrl'})
+ ];
+ // this array is intentionally one element shorter than the above
+ var pixels2 = [
+ null,
+ new OpenLayers.Pixel(27,11)
+ ];
+ map.addControls(controls2, pixels2);
+ t.eq(map.controls.length, 5, "three additional controls were added by map.addControls with a px-array");
+ t.eq(map.controls[3].position.toString(), pixels2[1].toString(), "control 'fourthctrl' has position set to given px");
+
+ map.destroy();
+ }
+
+ function test_Map_getControl(t) {
+ t.plan(2);
+
+ var map1 = new OpenLayers.Map('map');
+
+ var control = new OpenLayers.Control();
+ map1.addControl(control);
+
+ var gotControl = map1.getControl(control.id);
+ t.ok(gotControl == control, "got right control");
+
+ gotControl = map1.getControl("bogus id");
+ t.ok(gotControl == null, "getControl() for bad id returns null");
+
+ map1.destroy();
+ }
+
+ function test_Map_removeControl(t) {
+ t.plan(6);
+
+ var oldNumControls, newNumControls;
+
+ var map1 = new OpenLayers.Map('map');
+ oldNumControls = map1.controls.length;
+
+ var control = new OpenLayers.Control();
+ map1.addControl(control);
+
+ //add control
+ newNumControls = map1.controls.length;
+ t.ok( newNumControls = oldNumControls + 1, "adding a control increases control count")
+
+ var foundDiv = false;
+ for(var i=0; i < map1.viewPortDiv.childNodes.length; i++) {
+ var childNode = map1.viewPortDiv.childNodes[i];
+ if (childNode == control.div) {
+ foundDiv = true;
+ }
+ }
+ t.ok(foundDiv, "new control's div correctly added to viewPort");
+
+ //remove control
+ map1.removeControl(control)
+ newNumControls = map1.controls.length;
+ t.ok( newNumControls == oldNumControls, "removing the control decreases control count")
+
+ var gotControl = map1.getControl(control.id);
+ t.ok( gotControl == null, "control no longer in map's controls array");
+
+ var foundDiv = false;
+ for(var i=0; i < map1.viewPortDiv.childNodes.length; i++) {
+ var childNode = map1.viewPortDiv.childNodes[i];
+ if (childNode == control.div) {
+ foundDiv = true;
+ }
+ }
+ t.ok(!foundDiv, "control no longer child of viewPort");
+
+ //remove bogus
+ control = { id: "bogus id" };
+ map1.removeControl(control);
+ newNumControls = map1.controls.length;
+ t.ok( newNumControls == oldNumControls, "removing bad controlid doesnt crash or decrease control count")
+
+ map1.destroy();
+
+ }
+
+ function test_Map_restrictedExtent(t) {
+ t.plan(25);
+ var extent = new OpenLayers.Bounds(-180, -90, 180, 90);
+ var options = {
+ maxResolution: "auto"
+ };
+ var map = new OpenLayers.Map("map", options);
+ var layer = new OpenLayers.Layer.WMS(
+ "test",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"}
+ );
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ var nw = new OpenLayers.LonLat(extent.left, extent.top);
+ var ne = new OpenLayers.LonLat(extent.right, extent.top);
+ var sw = new OpenLayers.LonLat(extent.left, extent.bottom);
+ var se = new OpenLayers.LonLat(extent.right, extent.bottom);
+
+ // try panning to northwest corner
+ map.setOptions({restrictedExtent: extent});
+ map.setCenter(nw, 0);
+ t.eq(map.getExtent().getCenterLonLat().toString(),
+ extent.getCenterLonLat().toString(),
+ "map extent properly restricted to northwest at zoom 0");
+ t.eq(map.zoom, 0, "zoom not restricted for nw, 0");
+ map.setCenter(nw, 5);
+ t.eq(map.getExtent().top, extent.top,
+ "map extent top properly restricted to northwest at zoom 5");
+ t.eq(map.getExtent().left, extent.left,
+ "map extent left properly restricted to northwest at zoom 5");
+ t.eq(map.zoom, 5, "zoom not restricted for nw, 5");
+ map.setOptions({restrictedExtent: null});
+ map.setCenter(nw, 0);
+ t.eq(map.getExtent().getCenterLonLat().toString(),
+ nw.toString(),
+ "map extent not restricted with null restrictedExtent for nw");
+
+ // try panning to northeast corner
+ map.setOptions({restrictedExtent: extent});
+ map.setCenter(ne, 0);
+ t.eq(map.getExtent().getCenterLonLat().toString(),
+ extent.getCenterLonLat().toString(),
+ "map extent properly restricted to northeast at zoom 0");
+ t.eq(map.zoom, 0, "zoom not restricted for ne, 0");
+ map.setCenter(ne, 5);
+ t.eq(map.getExtent().top, extent.top,
+ "map extent top properly restricted to northeast at zoom 5");
+ t.eq(map.getExtent().right, extent.right,
+ "map extent right properly restricted to northeast at zoom 5");
+ t.eq(map.zoom, 5, "zoom not restricted for ne, 5");
+ map.setOptions({restrictedExtent: null});
+ map.setCenter(ne, 0);
+ t.eq(map.getExtent().getCenterLonLat().toString(),
+ ne.toString(),
+ "map extent not restricted with null restrictedExtent for ne");
+
+ // try panning to southwest corner
+ map.setOptions({restrictedExtent: extent});
+ map.setCenter(sw, 0);
+ t.eq(map.getExtent().getCenterLonLat().toString(),
+ extent.getCenterLonLat().toString(),
+ "map extent properly restricted to southwest at zoom 0");
+ t.eq(map.zoom, 0, "zoom not restricted for sw, 0");
+ map.setCenter(sw, 5);
+ t.eq(map.getExtent().bottom, extent.bottom,
+ "map extent bottom properly restricted to southwest at zoom 5");
+ t.eq(map.getExtent().left, extent.left,
+ "map extent left properly restricted to southwest at zoom 5");
+ t.eq(map.zoom, 5, "zoom not restricted for sw, 5");
+ map.setOptions({restrictedExtent: null});
+ map.setCenter(sw, 0);
+ t.eq(map.getExtent().getCenterLonLat().toString(),
+ sw.toString(),
+ "map extent not restricted with null restrictedExtent for sw");
+
+ // try panning to southeast corner
+ map.setOptions({restrictedExtent: extent});
+ map.setCenter(se, 0);
+ t.eq(map.getExtent().getCenterLonLat().toString(),
+ extent.getCenterLonLat().toString(),
+ "map extent properly restricted to southeast at zoom 0");
+ t.eq(map.zoom, 0, "zoom not restricted for se, 0");
+ map.setCenter(se, 5);
+ t.eq(map.getExtent().bottom, extent.bottom,
+ "map extent bottom properly restricted to southeast at zoom 5");
+ t.eq(map.getExtent().right, extent.right,
+ "map extent right properly restricted to southeast at zoom 5");
+ t.eq(map.zoom, 5, "zoom not restricted for se, 5");
+ map.setOptions({restrictedExtent: null});
+ map.setCenter(se, 0);
+ t.eq(map.getExtent().getCenterLonLat().toString(),
+ se.toString(),
+ "map extent not restricted with null restrictedExtent for se");
+
+ map.destroy();
+
+ extent = new OpenLayers.Bounds(8, 44.5, 19, 50);
+ var options = {
+ restrictedExtent: extent,
+ zoomMethod: null
+ };
+ map = new OpenLayers.Map('map', options);
+
+ var wms = new OpenLayers.Layer.WMS(
+ "OpenLayers WMS",
+ "http://vmap0.tiles.osgeo.org/wms/vmap0?",
+ {layers: 'basic'}
+ );
+
+ map.addLayers([wms]);
+ map.zoomToExtent(extent);
+ map.zoomIn();
+ map.setOptions({restrictedExtent: null});
+ map.pan(-250, -250);
+ t.ok((map.getExtent().bottom == 48.3486328125 && map.getExtent().left == 7.45751953125), "Expected extent when toggling restrictedExtent");
+ map.destroy();
+ }
+
+ function test_Map_getResolutionForZoom(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var res = map.getResolutionForZoom();
+ t.eq(res, null, "getResolutionForZoom returns null for no base layer");
+ map.fractionalZoom = true;
+ var layer = new OpenLayers.Layer("test", {isBaseLayer: true});
+ map.addLayer(layer);
+ layer.getResolutionForZoom = function() {
+ t.ok(true, "getResolutionForZoom calls base layer getResolutionForZoom");
+ }
+ var res = map.getResolutionForZoom();
+ layer.destroy();
+ map.destroy();
+ }
+
+ function test_zoomTo(t) {
+ t.plan(8);
+
+ var map = new OpenLayers.Map("map", {zoomMethod: null});
+ map.addLayer(new OpenLayers.Layer(null, {
+ isBaseLayer: true
+ }));
+
+ map.zoomToMaxExtent();
+
+ map.zoomTo(2);
+ t.eq(map.getZoom(), 2, 'zoomTo(2)');
+
+ map.zoomTo(3.6);
+ t.eq(map.getZoom(), 4, 'zoomTo(3.6)');
+
+ map.zoomTo("4.6");
+ t.eq(map.getZoom(), 5, 'zoomTo("4.6")');
+
+ map.zoomTo("1.2");
+ t.eq(map.getZoom(), 1, 'zoomTo("1.2")');
+
+ // now allow fractional zoom
+ map.fractionalZoom = true;
+
+ map.zoomTo(2);
+ t.eq(map.getZoom(), 2, '[fractionalZoom] zoomTo(2)');
+
+ map.zoomTo(3.6);
+ t.eq(map.getZoom(), 3.6, '[fractionalZoom] zoomTo(3.6)');
+
+ map.zoomTo("4.6");
+ t.eq(map.getZoom(), 4.6, '[fractionalZoom] zoomTo("4.6")');
+
+ map.zoomTo("1.2");
+ t.eq(map.getZoom(), 1.2, '[fractionalZoom] zoomTo("1.2")');
+
+ map.destroy();
+ }
+
+ function test_zoomTo_animated(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map("map");
+ map.addLayer(new OpenLayers.Layer(null, {
+ isBaseLayer: true
+ }));
+
+ map.zoomToMaxExtent();
+
+ map.zoomTo(2);
+ map.zoomIn();
+ map.zoomOut();
+ map.zoomIn();
+ t.delay_call(2, function() {
+ t.eq(map.getZoom(), 3, '[fractionalZoom: false] zoomTo(2) - zoomIn() - zoomOut() - zoomIn()');
+
+ // now allow fractional zoom
+ map.fractionalZoom = true;
+
+ map.zoomTo(2.6);
+ map.zoomIn();
+ map.zoomOut();
+ map.zoomIn();
+ });
+ t.delay_call(4, function() {
+ t.eq(map.getZoom(), 3.6, '[fractionalZoom: true] zoomTo(2) - zoomIn() - zoomOut() - zoomIn()');
+ map.destroy();
+ });
+
+ }
+
+ function test_Map_getUnits(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map("map");
+ var units = map.getUnits();
+ t.eq(units, null, "getUnits returns null for no base layer");
+
+ var layer = new OpenLayers.Layer("test", {
+ isBaseLayer: true,
+ units: 'foo'
+ });
+ map.addLayer(layer);
+ var units = map.getUnits();
+ t.eq(units, 'foo', "getUnits returns the base layer units property");
+ layer.destroy();
+ map.destroy();
+ }
+
+ function test_Map_destroy (t) {
+ t.plan( 3 );
+ map = new OpenLayers.Map('map');
+ map.destroy();
+ t.eq( map.layers, null, "map.layers is null after destroy" );
+ t.eq( map.controls, null, "map.controls is null after destroy" );
+ t.eq( map.viewPortDiv, null, "map's viewportDiv nullified");
+ }
+
+ function test_Map_getMaxExtent(t){
+ t.plan(5);
+
+ var options = null;
+ var map = {};
+
+ //null options, no baseLayer
+ var maxExtent = OpenLayers.Map.prototype.getMaxExtent.apply(map, [options]);
+ t.eq(maxExtent, null, "null options, no baseLayer returns null");
+
+ //null options.restricted, no baseLayer
+ maxExtent = OpenLayers.Map.prototype.getMaxExtent.apply(map, [options]);
+ t.eq(maxExtent, null, "null options.restricted, no baseLayer returns null");
+
+ //true options.restricted, null map.restrictedExtent no baseLayer
+ maxExtent = OpenLayers.Map.prototype.getMaxExtent.apply(map, [options]);
+ t.eq(maxExtent, null, "true options.restricted, null map.restrictedExtent no baseLayer returns null");
+
+ //true options.restricted, valid map.restrictedExtent no baseLayer
+ options = {
+ 'restricted': true
+ };
+ map.restrictedExtent = {};
+ maxExtent = OpenLayers.Map.prototype.getMaxExtent.apply(map, [options]);
+ t.ok(maxExtent == map.restrictedExtent, "true options.restricted, valid map.restrictedExtent no baseLayer returns map.restrictedExtent");
+
+ //null options, valid baseLayer
+ options = null;
+ map.baseLayer = {
+ 'maxExtent': {}
+ };
+ var maxExtent = OpenLayers.Map.prototype.getMaxExtent.apply(map, [options]);
+ t.ok(maxExtent == map.baseLayer.maxExtent, "null options, valid baseLayer returns map.baseLayer.maxExtent");
+ }
+
+ function test_Map_zoomToMaxExtent(t){
+ t.plan(4)
+
+ gMaxExtent = {};
+
+ var map = {
+ 'getMaxExtent': function(options) {
+ gRestricted = options.restricted;
+ return gMaxExtent;
+ },
+ 'zoomToExtent': function(extent) {
+ t.ok(extent == gMaxExtent, "zoomToExtent() always called on return from map.getMaxExtent()");
+ }
+ };
+
+ //options is null
+ var options = null;
+ gRestricted = null;
+ OpenLayers.Map.prototype.zoomToMaxExtent.apply(map, [options]);
+ t.eq(gRestricted, true, "default 'restricted' passed to map.getMaxExtent() is true");
+
+ //valid options
+ options = {
+ 'restricted': {}
+ };
+ gRestricted = null;
+ OpenLayers.Map.prototype.zoomToMaxExtent.apply(map, [options]);
+ t.ok(gRestricted == options.restricted, "when valid options argument, 'options.restricted' passed to map.getMaxExtent()");
+ }
+
+ function test_Map_zoomToScale(t) {
+ t.plan(4);
+
+ var m = {
+ 'baseLayer': { 'units': {} },
+ 'size': {'w': 10, 'h': 15},
+ 'getSize': function() { return {'w': 10, 'h': 15}; },
+ 'getCachedCenter': function() { return {'lon': -5, 'lat': -25}; },
+ 'zoomToExtent': function(extent, closest) {
+ t.ok(extent.equals(g_ExpectedExtent), "extent correctly calculated for zoomToExtent()");
+ t.ok(closest == g_Closest, "closest correctly passed on to zoomToExtent()");
+ }
+ }
+
+ var temp = OpenLayers.Util.getResolutionFromScale;
+ OpenLayers.Util.getResolutionFromScale = function(scale, units) {
+ t.ok(scale == g_Scale, "scale parameter correctly passed to getResolutionFromScale");
+ t.ok(units == m.baseLayer.units, "map's baselayer's units parameter correctly passed to getResolutionFromScale");
+ return 1000;
+ };
+
+ g_ExpectedExtent = new OpenLayers.Bounds(-5005,-7525,4995,7475);
+ g_Scale = {};
+ g_Closest = {};
+ var args = [g_Scale, g_Closest];
+ OpenLayers.Map.prototype.zoomToScale.apply(m, args);
+
+ OpenLayers.Util.getResolutionFromScale = temp;
+ }
+
+ function test_Map_zoomToExtent(t) {
+ t.plan(12);
+
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer(null, {isBaseLayer: true});
+ map.addLayer(layer);
+
+ var bounds = new OpenLayers.Bounds(-160, 15, -50, 69);
+ var center;
+
+ // default for closest
+ map.zoomToExtent(bounds);
+ center = map.getCenter();
+ t.eq(center.lon, -105, "a) correct x");
+ t.eq(center.lat, 42, "a) correct y");
+ t.eq(map.getZoom(), 2, "a) correct zoom");
+
+ // false for closest
+ map.zoomToExtent(bounds, false);
+ center = map.getCenter();
+ t.eq(center.lon, -105, "b) correct x");
+ t.eq(center.lat, 42, "b) correct y");
+ t.eq(map.getZoom(), 2, "b) correct zoom");
+
+ // true for closest
+ map.zoomToExtent(bounds, true);
+ center = map.getCenter();
+ t.eq(center.lon, -105, "c) correct x");
+ t.eq(center.lat, 42, "c) correct y");
+ t.eq(map.getZoom(), 3, "c) correct zoom");
+
+ // accept array
+ map.zoomToExtent([-160, 15, -50, 69]);
+ center = map.getCenter();
+ t.eq(center.lon, -105, "(array) correct x");
+ t.eq(center.lat, 42, "(array) correct y");
+ t.eq(map.getZoom(), 2, "(array) correct zoom");
+
+ map.destroy();
+ }
+
+ function test_Map_zoomToExtent_wrapped(t) {
+ t.plan(9);
+
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer(null, {isBaseLayer: true, wrapDateLine: true});
+ map.addLayer(layer);
+
+ var bounds, center;
+
+ var cases = [{
+ // real world
+ bbox: [120, -20, 140, 0],
+ center: [130, -10]
+ }, {
+ // one world to the right
+ bbox: [220, -45, 240, 45],
+ center: [-130, 0]
+ }, {
+ // two worlds to the right
+ bbox: [550, -15, 560, 5],
+ center: [-165, -5]
+ }, {
+ // one world to the left
+ bbox: [-240, -15, -220, 5],
+ center: [130, -5]
+ }, {
+ // two worlds to the left
+ bbox: [-600, -15, -580, 5],
+ center: [130, -5]
+ }];
+
+ var num = cases.length;
+ t.plan(num * 2);
+
+ var c, bounds, center;
+ for (var i=0; i<num; ++i) {
+ c = cases[i];
+ bounds = OpenLayers.Bounds.fromArray(c.bbox);
+ map.zoomToExtent(bounds);
+ center = map.getCenter();
+ t.eq(center.lon, c.center[0], "y: " + bounds);
+ t.eq(center.lat, c.center[1], "x: " + bounds);
+ }
+
+ map.destroy();
+ }
+
+
+ function test_allOverlays(t) {
+
+ t.plan(18);
+
+ var map = new OpenLayers.Map({
+ div: "map", allOverlays: true
+ });
+
+ var a = new OpenLayers.Layer.Vector("a", {visibility: true});
+
+ var b = new OpenLayers.Layer.Image(
+ "b",
+ "http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif",
+ new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
+ new OpenLayers.Size(580, 288)
+ );
+
+ var c = new OpenLayers.Layer.WMS(
+ "c",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'}
+ );
+
+ var d = new OpenLayers.Layer.Vector("d");
+
+ map.addLayers([a, b, c, d]);
+
+ var moveCount = 0;
+ a.moveTo = function() {
+ moveCount++;
+ OpenLayers.Layer.Vector.prototype.moveTo.apply(this, arguments);
+ };
+
+ map.zoomToMaxExtent();
+ t.eq(moveCount, 1, "map.moveTo moves the base layer only once");
+ t.eq(map.getCenter().toString(), "lon=0,lat=0", "a map with all overlays can have a center");
+
+ a.setVisibility(false);
+ var moveend = 0;
+ a.events.on({"moveend": function() { moveend++; }});
+ map.zoomToMaxExtent();
+ t.eq(moveCount, 1, "map.moveTo does not move the base layer if it is invisible");
+ t.eq(moveend, 0, "map.moveTo does not trigger \"moveend\" in the layer if the layer is invisible");
+ a.setVisibility(true);
+
+ // a, b, c, d
+ t.eq(map.baseLayer.name, "a", "base layer set to first layer added");
+
+ map.removeLayer(a);
+ // b, c, d
+ t.eq(map.baseLayer.name, "b", "if base layer is removed, lowest layer becomes base");
+
+ map.addLayer(a);
+ // b, c, d, a
+ t.eq(map.baseLayer.name, "b", "adding a new layer doesn't change base layer");
+
+ map.setLayerIndex(c, 1);
+ // b, d, c, a
+ t.eq(map.baseLayer.name, "b", "changing layer order above base doesn't mess with base");
+
+ map.setLayerIndex(d, 0);
+ // d, b, c, a
+ t.eq(map.baseLayer.name, "d", "changing layer order to 0 sets base layer");
+
+ map.raiseLayer(d, 1);
+ // b, d, c, a
+ t.eq(map.baseLayer.name, "b", "raising the base layer sets a new base layer");
+
+ map.raiseLayer(d, -1);
+ // d, b, c, a
+ t.eq(map.baseLayer.name, "d", "lowering a layer to lowest index sets as base");
+
+ // all this switching of base layer didn't muck with layer visibility
+ t.eq(a.visibility, true, "a is visible");
+ t.eq(b.visibility, true, "b is visible");
+ t.eq(c.visibility, true, "c is visible");
+ t.eq(d.visibility, true, "d is visible");
+
+ // test that map can have an invisible base layer
+ b.setVisibility(false);
+ map.setLayerIndex(b, 0);
+ t.eq(b.visibility, false, "changing layer order doesn't change visibility");
+
+
+ map.destroy();
+
+ // make sure setVisibility is called when adding a single layer to the map
+ map = new OpenLayers.Map({
+ div: "map", allOverlays: true
+ });
+ var count = 0;
+ var layer = new OpenLayers.Layer(null, {
+ visibility: true,
+ maxExtent: new OpenLayers.Bounds(-180, -90, 180, 90),
+ setVisibility: function() {
+ ++count;
+ OpenLayers.Layer.prototype.setVisibility.apply(this, arguments);
+ }
+ });
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ t.eq(count, 1, "setVisibility called when visibility is true in layer config");
+ t.eq(layer.div.style.display, "", "layer is visible.");
+
+ map.destroy();
+
+ }
+
+ function test_panTo(t) {
+
+ t.plan(6);
+
+ var log = [];
+ var map = new OpenLayers.Map("map", {
+ eventListeners: {
+ "movestart": function() {log.push("movestart");},
+ "move": function() {log.push("move");},
+ "moveend": function() {log.push("moveend");}
+ }
+ });
+ map.addLayer(
+ new OpenLayers.Layer(null, {isBaseLayer: true})
+ );
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+ t.eq(log[log.length-1], "moveend", "moveend fired when map center is set");
+ log = [];
+
+ map.panTo(new OpenLayers.LonLat(1, 0));
+ t.eq(map.panTween.playing, true, "the map pan tween is playing before destroy");
+
+ t.delay_call(2, function() {
+ t.eq(log[0], "movestart", "panTo starts with movestart event");
+ t.eq(log[1], "move", "move events fired while panning");
+ t.eq(log[log.length-1], "moveend", "panTo finishes with moveend event");
+ map.destroy();
+ t.ok(!map.panTween || !map.panTween.playing, "the map pan tween is not playing after destroy");
+ });
+ }
+
+ function test_pan(t) {
+ t.plan(4);
+
+ var map = new OpenLayers.Map("map");
+ map.addLayer(
+ new OpenLayers.Layer(null, {isBaseLayer: true})
+ );
+ map.setCenter(new OpenLayers.LonLat(0, 0), 5);
+ var log = [];
+ map.events.on({
+ "movestart": function() {log.push("movestart");},
+ "move": function() {log.push("move");},
+ "moveend": function() {log.push("moveend");}
+ });
+
+ // simulate the drag sequence of the DragPan control;
+ map.pan(5,5, {animate: false, dragging: true});
+ map.pan(1,1, {animate: false, dragging: false});
+
+ t.eq(log[0], "movestart", "pan sequence starts with movestart");
+ t.eq(log[1], "move", "followed by move,");
+ t.eq(log[log.length-2], "move", "move again before we stop panning,");
+ t.eq(log[log.length-1], "moveend", "and moveend when we're done.");
+ }
+
+ function test_pan_no_anim_event_sequence(t) {
+ t.plan(4);
+
+ var log = [];
+ var map = new OpenLayers.Map("map");
+ map.addLayer(
+ new OpenLayers.Layer(null, {isBaseLayer: true})
+ );
+ map.setCenter(new OpenLayers.LonLat(0, 0), 5);
+ map.events.on({
+ "movestart": function() {
+ log.push("movestart");
+ },
+ "move": function() {
+ log.push("move");
+ },
+ "moveend": function() {
+ log.push("moveend");
+ }
+ });
+
+ map.pan(5,5, {animate: false});
+ t.eq(log.length, 3, "no more than 3 events happen.");
+ t.eq(log[0], "movestart", "pan sequence starts with movestart");
+ t.eq(log[1], "move", "followed by move,");
+ t.eq(log[2], "moveend", "and moveend when we're done.");
+
+ map.destroy();
+ }
+
+ // test if we can call updateSize before document.body is ready. updateOk
+ // is tested in the test_updateSize function below
+ var earlyMap = new OpenLayers.Map();
+ var updateOk;
+ try {
+ earlyMap.updateSize();
+ updateOk = true;
+ } catch(e) {}
+ earlyMap.destroy();
+ function test_updateSize(t) {
+ t.plan(3);
+
+ // checking updateSize from outside this test function (see above)
+ t.ok(updateOk, "updateSize works before document.body is ready");
+
+ var map, moveToCnt, size;
+
+ map = new OpenLayers.Map({div: "map"});
+ map.addLayer(new OpenLayers.Layer("layer", {isBaseLayer: true}));
+
+ map.moveTo = function() {
+ moveToCnt++;
+ OpenLayers.Map.prototype.moveTo.apply(this, arguments);
+ };
+
+ map.getCurrentSize = function() {
+ return size;
+ };
+
+ // map has no center
+ // 1 test
+ moveToCnt = 0;
+ size = new OpenLayers.Size(650, 350);
+ map.updateSize();
+ t.eq(moveToCnt, 0, "updateSize doesn't move the map if it doesn't have a center");
+
+ // map has a center
+ // 1 test
+ map.zoomToMaxExtent();
+ moveToCnt = 0;
+ size = new OpenLayers.Size(600, 300);
+ map.updateSize();
+ t.eq(moveToCnt, 1, "updateSize move the map if it has a center");
+
+ map.destroy();
+ }
+
+ function test_invisible_map(t) {
+ /**
+ * This test confirms that initializing a map using an element that is
+ * not currently displayed doesn't cause any trouble.
+ */
+ t.plan(1);
+
+ var map, msg = "initializing a map on an undisplayed element";
+ try {
+ map = new OpenLayers.Map("invisimap");
+ } catch (err) {
+ msg += ": " + err;
+ }
+ t.ok(!!map, msg);
+
+ if (map) {
+ map.destroy();
+ }
+ }
+
+ function test_layers_option(t) {
+
+ t.plan(3);
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ layers: [
+ new OpenLayers.Layer()
+ ]
+ });
+
+ t.eq(map.layers.length, 1, "single layer from options added");
+
+ map.destroy();
+
+ map = new OpenLayers.Map({
+ div: "map",
+ layers: [
+ new OpenLayers.Layer(null, {isBaseLayer: true}),
+ new OpenLayers.Layer(null, {isBaseLayer: false})
+ ]
+ });
+
+ t.eq(map.layers.length, 2, "multiple layers added from options");
+ t.ok(map.baseLayer, "map has a base layer");
+
+ map.destroy();
+
+ }
+
+ function test_center_option(t) {
+ t.plan(7);
+
+ var map, msg;
+
+
+ // try setting center without layers, this has no effect
+ var failed = false;
+ try {
+ map = new OpenLayers.Map({
+ div: "map",
+ center: new OpenLayers.LonLat(1, 2)
+ });
+ msg = "center option without layers has no effect";
+ } catch (err) {
+ failed = true;
+ msg = "center option without layers throws error";
+ }
+ t.ok(!failed, msg);
+
+ if (map) {
+ map.destroy();
+ }
+
+ var log = [];
+ var meth = OpenLayers.Layer.prototype.moveTo;
+ OpenLayers.Layer.prototype.moveTo = function() {
+ log.push(arguments);
+ meth.apply(this, arguments);
+ };
+
+ // set center without zoom
+ var center = new OpenLayers.LonLat(1, 2);
+ map = new OpenLayers.Map({
+ div: "map",
+ layers: [new OpenLayers.Layer(null, {isBaseLayer: true})],
+ center: center
+ });
+
+ t.ok(center.equals(map.getCenter()), "map center set without zoom");
+ t.eq(log.length, 1, "moveTo called once");
+
+ map.destroy();
+ OpenLayers.Layer.prototype.moveTo = meth;
+
+ // set center and zoom
+ var zoom = 3;
+ map = new OpenLayers.Map({
+ div: "map",
+ layers: [new OpenLayers.Layer(null, {isBaseLayer: true})],
+ center: center,
+ zoom: zoom
+ });
+
+ t.ok(center.equals(map.getCenter()), "map center set with center and zoom");
+ t.eq(zoom, map.getZoom(), "map zoom set with center and zoom");
+
+ map.destroy();
+
+ // set center and zoom with all overlays
+ map = new OpenLayers.Map({
+ div: "map",
+ allOverlays: true,
+ layers: [new OpenLayers.Layer()],
+ center: center,
+ zoom: zoom
+ });
+
+ t.ok(center.equals(map.getCenter()), "map center set with all overlays");
+ t.eq(zoom, map.getZoom(), "map zoom set with all overlays");
+
+ map.destroy();
+
+ }
+ function test_pixel_lonlat(t) {
+
+ t.plan(4);
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ layers: [
+ new OpenLayers.Layer("name", {isBaseLayer:true})
+ ]
+ });
+ map.zoomToMaxExtent();
+ var px = map.getPixelFromLonLat(map.getLonLatFromPixel(new OpenLayers.Pixel(100, 100)));
+ t.eq(px.x, 100, "x is the same in and ot");
+ t.eq(px.y, 100, "y is the same in and out");
+ var ll = map.getLonLatFromPixel(map.getPixelFromLonLat(new OpenLayers.LonLat(100, 100)));
+ t.ok((ll.lon > (100 -map.getResolution()) && (ll.lon < (100 + map.getResolution()))), "lon is the same in and ot");
+ t.ok((ll.lat > (100 -map.getResolution()) && (ll.lat < (100 + map.getResolution()))), "lat is the same in and ot");
+ map.destroy();
+ }
+
+ function test_moveByPx(t) {
+ t.plan(14);
+
+ var moved;
+ var Layer = OpenLayers.Class(OpenLayers.Layer, {
+ moveByPx: function(dx, dy) {
+ moved[this.name] = true;
+ }
+ });
+
+ var map = new OpenLayers.Map({
+ div: 'map',
+ maxExtent: new OpenLayers.Bounds(-50, -50, 50, 50),
+ restrictedExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+ layers: [
+ new Layer('base',
+ {isBaseLayer: true}),
+ new Layer('outofrange',
+ {isBaseLayer: false, minResolution:2})
+ ]
+ });
+ var log = [];
+ map.applyTransform = function(x, y, scale) {
+ log.push([x || map.layerContainerOriginPx.x, y || map.layerContainerOriginPx.y, scale]);
+ OpenLayers.Map.prototype.applyTransform.apply(this, arguments);
+ };
+
+ moved = {};
+ map.zoomToExtent(new OpenLayers.Bounds(-1, -1, 1, 1));
+
+ // check initial state
+ t.eq(log[0][0], 0,
+ '[initial state] layer container left correct');
+ t.eq(log[0][1], 0,
+ '[initial state] layer container top correct');
+ t.eq(moved['base'], undefined,
+ '[initial state] base layer not moved');
+ t.eq(moved['outofrange'], undefined,
+ '[initial state] out-of-range layer not moved');
+
+ // move to a valid position
+ moved = {};
+ map.moveByPx(-455, 455);
+ t.eq(log[1][0], 455,
+ '[valid position] layer container left correct');
+ t.eq(log[1][1], -455,
+ '[valid position] layer container top correct');
+ t.eq(moved['base'], true,
+ '[valid position] base layer moved');
+ t.eq(moved['outofrange'], undefined,
+ '[valid position] out-of-range layer not moved');
+
+ // move outside the max extent
+ moved = {};
+ map.moveByPx(-4500, 4500);
+ t.eq(log.length, 2,
+ '[outside max extent] layer container offset unchanged');
+ t.eq(moved['base'], undefined,
+ '[outside max extent] base layer not moved');
+ t.eq(moved['outofrange'], undefined,
+ '[outside max extent] out-of-range layer not moved');
+
+ // move outside the restricted extent
+ moved = {};
+ map.moveByPx(-500, 500);
+ t.eq(log.length, 2,
+ '[outside restricted extent] layer container offset unchanged');
+ t.eq(moved['base'], undefined,
+ '[outside restricted extent] base layer not moved');
+ t.eq(moved['outofrange'], undefined,
+ '[outside restricted extent] out-of-range layer not moved');
+
+
+ map.destroy();
+ }
+
+ // test for http://trac.osgeo.org/openlayers/ticket/3388
+ function test_moveByPx_restrictedExtent(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map({
+ div: 'map',
+ restrictedExtent: new OpenLayers.Bounds(-22.5,-11.25,22.5,11.25),
+ layers: [
+ new OpenLayers.Layer('name', {isBaseLayer: true})
+ ]
+ });
+
+ map.zoomToExtent(new OpenLayers.Bounds(-11.25, 0, 11.25, 11.25));
+
+ var log = [];
+ map.applyTransform = function(x, y, scale) {
+ log.push([x || map.layerContainerOriginPx.x, y || map.layerContainerOriginPx.y, scale]);
+ OpenLayers.Map.prototype.applyTransform.apply(this, arguments);
+ };
+
+ map.moveByPx(-10, -10);
+ t.eq(log[0][0], 10, 'layer container left correct');
+ t.eq(log[0][1], 0, 'layer container top correct');
+ }
+
+ function test_applyTransform(t) {
+ t.plan(10);
+ var origStylePrefix = OpenLayers.Util.vendorPrefix.style;
+ OpenLayers.Util.vendorPrefix.style =
+ OpenLayers.Util.vendorPrefix.css =
+ function(key) { return 'transform'; };
+
+ var map = new OpenLayers.Map('map');
+ map.layerContainerDiv = {style: {}};
+ delete map.applyTransform.transform;
+ delete map.applyTransform.template;
+ var origGetStyle = OpenLayers.Element.getStyle;
+ OpenLayers.Element.getStyle = function() { return 'foo'; }
+ map.applyTransform(1, 2, 3);
+ OpenLayers.Element.getStyle = origGetStyle;
+ t.eq(map.layerContainerDiv.style.transform, 'translate3d(1px,2px,0) scale3d(3,3,1)', '3d transform and scale used when available');
+
+ delete map.applyTransform.transform;
+ delete map.applyTransform.template;
+ var origIndexOf = String.prototype.indexOf;
+ String.prototype.indexOf = function() { return -1; };
+ map.layerContainerOriginPx = {x: -3, y: 2};
+ map.applyTransform(1, 2, 3);
+ String.prototype.indexOf = origIndexOf;
+ t.eq(map.layerContainerDiv.style.transform, 'translate(4px,0px) scale(3,3)', '2d translate and scale correct');
+ t.eq(map.layerContainerDiv.style.left, '-3px', 'container origin x set as style.left');
+ t.eq(map.layerContainerDiv.style.top, '2px', 'container origin y set as style.top');
+ map.applyTransform(1, 2);
+ t.ok(!map.layerContainerDiv.style.transform, 'no transform set when no transform needed');
+ t.eq(map.layerContainerDiv.style.left, '1px', 'style.left correct when no transform needed');
+ t.eq(map.layerContainerDiv.style.top, '2px', 'style.top correct when no transform needed');
+
+ map.applyTransform.transform = null;
+ map.applyTransform(4, 5, 6);
+ t.eq(map.layerContainerDiv.style.left, '4px', 'style.left set when transform not available')
+ t.eq(map.layerContainerDiv.style.top, '5px', 'style.top set when transform not available')
+ t.ok(!map.layerContainerDiv.style.transform, 'no transform set, because not supported');
+
+ map.destroy();
+ delete map.applyTransform.transform;
+ delete map.applyTransform.template;
+ OpenLayers.Util.vendorPrefix.style = origStylePrefix;
+ }
+
+ function test_options(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map('map');
+ t.eq(map.options, {}, 'map.options is empty with no options');
+ map.destroy();
+
+ var options = {
+ resolutions: [1,2,3,5],
+ projection: "EPSG:4326",
+ units: 'm'
+ };
+ var map = new OpenLayers.Map('map', options);
+ t.eq(map.options, options, 'map.options is a copy of the constructor option');
+ map.destroy();
+ }
+
+ function test_adjustZoom(t) {
+ t.plan(5);
+ var map = new OpenLayers.Map({
+ div: 'map',
+ layers: [
+ new OpenLayers.Layer('name', {
+ isBaseLayer: true,
+ wrapDateLine: true
+ })
+ ]
+ });
+ map.zoomToMaxExtent();
+ t.ok(map.getResolution() <= map.getMaxExtent().getWidth() / map.getSize().w, "wrapDateLine map not wider than world");
+
+ t.eq(map.adjustZoom(9), 9, "valid zoom maintained");
+ t.eq(map.adjustZoom(1), 2, "zoom adjusted to not exceed world width");
+
+ map.fractionalZoom = true;
+ t.eq(map.adjustZoom(1).toPrecision(3), "1.29", "zoom adjusted to match world width");
+
+ map.moveTo([16, 48], 0);
+ t.eq(map.getCenter().toShortString(), "0, 0", "no panning when moveTo is called with invalid zoom");
+ }
+
+ function test_correctCenterAtZoomLevel0(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map({
+ div: 'map',
+ maxExtent: new OpenLayers.Bounds(-30, 48.00, 3.50, 64.00),
+ restrictedExtent: new OpenLayers.Bounds(-30, 48.00, 3.50, 64.00),
+ projection: "EPSG:4258",
+ units: "degrees",
+ layers: [
+ new OpenLayers.Layer('name', {
+ isBaseLayer: true
+ })
+ ]
+ });
+ map.setCenter(new OpenLayers.LonLat(-1.3, 50.8), 4);
+ map.moveTo(null, 0);
+ var center = map.getCenter();
+ t.ok(center.equals(new OpenLayers.LonLat(-13.25, 56)), "Center is correct and not equal to maxExtent's center");
+ }
+
+ function test_getZoomTargetCenter(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map({
+ div: 'map',
+ layers: [
+ new OpenLayers.Layer('', {isBaseLayer: true})
+ ],
+ center: [0, 0],
+ zoom: 1
+ });
+
+ var ll = map.getZoomTargetCenter({x: 44, y: 22}, map.getMaxResolution());
+
+ t.eq(ll.toShortString(), "180, -90", "getZoomTargetCenter works.");
+
+ map.destroy();
+ }
+
+ function test_autoUpdateSize(t) {
+ t.plan(1);
+ OpenLayers.Event.unloadCache();
+ var resizeListener = false;
+ var map = new OpenLayers.Map({
+ autoUpdateSize: false,
+ div: 'map',
+ layers: [
+ new OpenLayers.Layer('name', {
+ isBaseLayer: true,
+ wrapDateLine: true
+ })
+ ]
+ });
+ map.setCenter(new OpenLayers.LonLat(-1.3, 50.8), 4);
+ for (var key in OpenLayers.Event.observers) {
+ var obj = OpenLayers.Event.observers[key];
+ for (var i=0, ii=obj.length; i<ii; ++i) {
+ var listener = obj[i];
+ if (listener.name === 'resize' && listener.element === window) {
+ resizeListener = true;
+ }
+ }
+ }
+ t.eq(resizeListener, map.autoUpdateSize, "resize listener not registered when autoUpdateSize is false");
+ map.destroy();
+ }
+
+ function test_tileManager(t) {
+ t.plan(3);
+ var map = new OpenLayers.Map('map');
+ t.ok(map.tileManager instanceof OpenLayers.TileManager, "Default tileManager created");
+ map.destroy();
+ map = new OpenLayers.Map('map', {tileManager: null});
+ t.ok(map.tileManager === null, "No tileManager created");
+ map.destroy();
+ var options = {cacheSize: 512};
+ map = new OpenLayers.Map('map', {tileManager: options});
+ t.eq(map.tileManager.cacheSize, 512, "cacheSize taken from options");
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 600px; height: 300px;"/>
+ <div style="display: none;"><div id="invisimap"></div></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Marker.html b/misc/openlayers/tests/Marker.html
new file mode 100644
index 0000000..fa9b598
--- /dev/null
+++ b/misc/openlayers/tests/Marker.html
@@ -0,0 +1,163 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var marker;
+
+ function test_Marker_constructor (t) {
+ t.plan( 4 );
+ var ll = new OpenLayers.LonLat(2,1);
+ marker = new OpenLayers.Marker(ll,new OpenLayers.Icon());
+ t.ok( marker instanceof OpenLayers.Marker, "new OpenLayers.Marker returns Marker object" );
+ t.ok( marker.icon instanceof OpenLayers.Icon, "new marker.Icon returns Icon object" );
+ t.ok( marker.lonlat instanceof OpenLayers.LonLat, "new marker.lonlat returns LonLat object" );
+ t.ok( marker.lonlat.equals(ll), "marker.lonlat returns correct" );
+ }
+
+ function test_Marker_onScreen(t) {
+ t.plan( 2 );
+
+ var map = new OpenLayers.Map("map");
+
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.WMS(name, url);
+
+ map.addLayer(layer);
+
+ mlayer = new OpenLayers.Layer.Markers('Test Layer');
+ map.addLayer(mlayer);
+
+ map.zoomToExtent(new OpenLayers.Bounds(-50,-50,50,50));
+
+ //onscreen marker
+ var ll = new OpenLayers.LonLat(0,0);
+ var marker = new OpenLayers.Marker(ll);
+ mlayer.addMarker(marker);
+
+ t.ok( marker.onScreen(), "marker knows it's onscreen" );
+
+ //offscreen marker
+ var ll = new OpenLayers.LonLat(100,100);
+ var marker2 = new OpenLayers.Marker(ll);
+ mlayer.addMarker(marker2);
+
+ t.ok( !marker2.onScreen(), "marker knows it's offscreen" );
+ map.destroy();
+ }
+
+ function test_Marker_setOpacity(t) {
+ t.plan( 2 );
+
+ var map = new OpenLayers.Map("map");
+
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.WMS(name, url);
+
+ map.addLayer(layer);
+
+ mlayer = new OpenLayers.Layer.Markers('Test Layer');
+ map.addLayer(mlayer);
+
+ map.zoomToExtent(new OpenLayers.Bounds(-50,-50,50,50));
+
+ //onscreen marker
+ var ll = new OpenLayers.LonLat(0,0);
+ var marker = new OpenLayers.Marker(ll);
+ mlayer.addMarker(marker);
+
+ t.ok(!marker.icon.imageDiv.style.opacity, "default marker has no opacity");
+
+ marker.setOpacity(0.5);
+
+ t.eq(parseFloat(marker.icon.imageDiv.style.opacity), 0.5, "marker.setOpacity() works");
+ map.destroy();
+ }
+
+ function test_Marker_setUrl(t) {
+ t.plan( 2 );
+
+ var map = new OpenLayers.Map("map");
+
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.WMS(name, url);
+
+ map.addLayer(layer);
+
+ mlayer = new OpenLayers.Layer.Markers('Test Layer');
+ map.addLayer(mlayer);
+
+ map.zoomToExtent(new OpenLayers.Bounds(-50,-50,50,50));
+
+ //onscreen marker
+ var ll = new OpenLayers.LonLat(0,0);
+ var marker = new OpenLayers.Marker(ll);
+ mlayer.addMarker(marker);
+
+ t.ok(OpenLayers.String.contains(marker.icon.imageDiv.firstChild.src, "img/marker.png"), "Marker.png is default URL");
+
+ marker.setUrl("http://example.com/broken.png");
+ t.eq(marker.icon.imageDiv.firstChild.src, "http://example.com/broken.png", "image source changes correctly.");
+
+ map.destroy();
+ }
+
+ function test_Marker_moveTo(t) {
+ t.plan( 6 );
+
+ var map = new OpenLayers.Map("map");
+
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.WMS(name, url);
+
+ map.addLayer(layer);
+
+ mlayer = new OpenLayers.Layer.Markers('Test Layer');
+ map.addLayer(mlayer);
+
+ map.zoomToExtent(new OpenLayers.Bounds(-50,-50,50,50));
+
+ //onscreen marker
+ var ll = new OpenLayers.LonLat(0,0);
+ var marker = new OpenLayers.Marker(ll);
+ mlayer.addMarker(marker);
+
+ t.eq(marker.lonlat.lon, 0, "marker lon okay");
+ t.eq(marker.lonlat.lat, 0, "marker lat okay");
+
+ marker.moveTo(new OpenLayers.Pixel(250,275));
+ t.eq(marker.lonlat.lon, 0, "marker lon no change");
+ t.eq(marker.lonlat.lat, 0, "marker lat no change");
+
+ marker.moveTo(new OpenLayers.Pixel(0,0));
+ t.eq(marker.lonlat.lon, map.getExtent().left, "on left edge of map");
+ t.eq(marker.lonlat.lat, map.getExtent().top, "on top edge of map");
+ map.destroy();
+ }
+
+ function test_Marker_isDrawn(t) {
+ t.plan(3);
+
+ var marker = {};
+
+ //no icon
+ var drawn = OpenLayers.Marker.prototype.isDrawn.apply(marker, []);
+ t.ok(!drawn, "marker with no icon not drawn");
+
+ //not drawn icon
+ marker.icon = { isDrawn: function() { return false; } };
+ drawn = OpenLayers.Marker.prototype.isDrawn.apply(marker, []);
+ t.ok(!drawn, "marker with not drawn icon not drawn");
+
+ //drawn icon
+ marker.icon.isDrawn = function() { return true; };
+ drawn = OpenLayers.Marker.prototype.isDrawn.apply(marker, []);
+ t.ok(drawn, "marker with drawn icon drawn");
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Marker/Box.html b/misc/openlayers/tests/Marker/Box.html
new file mode 100644
index 0000000..806336e
--- /dev/null
+++ b/misc/openlayers/tests/Marker/Box.html
@@ -0,0 +1,183 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var box;
+
+ function test_Box_constructor (t) {
+ t.plan( 7 );
+
+ OpenLayers.Marker.Box.prototype._setBorder =
+ OpenLayers.Marker.Box.prototype.setBorder;
+ OpenLayers.Marker.Box.prototype.setBorder = function (x,y) {
+ g_Color = x;
+ g_Width = y;
+ };
+
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var borderColor = "blue";
+ var borderWidth = 55;
+
+
+ g_Color = g_Width = null;
+ box = new OpenLayers.Marker.Box(bounds, borderColor, borderWidth);
+
+ t.ok( box instanceof OpenLayers.Marker.Box, "new OpenLayers.Marker.Box returns Box object" );
+ t.ok( box.bounds.equals(bounds), "bounds object correctly set");
+ t.ok( box.div != null, "div created");
+ //Safari 3 separates style overflow into overflow-x and overflow-y
+ var prop = (OpenLayers.BROWSER_NAME == 'safari') ? 'overflowX' : 'overflow';
+ t.eq( box.div.style[prop], "hidden", "div style overflow hidden");
+ t.ok( box.events != null, "events object created");
+ t.eq( g_Color, borderColor, "setBorder called with correct border color");
+ t.eq( g_Width, borderWidth, "setBorder called with correct border width");
+
+
+ OpenLayers.Marker.Box.prototype.setBorder =
+ OpenLayers.Marker.Box.prototype._setBorder;
+ }
+
+
+ function test_Box_setBorder(t) {
+ t.plan( 2 );
+
+ var box = {
+ div: {
+ style: {}
+ }
+ };
+
+ //defaults
+ var args = [];
+ OpenLayers.Marker.Box.prototype.setBorder.apply(box, args);
+ t.eq(box.div.style.border, "2px solid red", "style correctly set with no good values (defaults work)");
+
+ //good vals
+ var borderColor = "blue";
+ var borderWidth = 55;
+
+ args = [borderColor, borderWidth];
+ OpenLayers.Marker.Box.prototype.setBorder.apply(box, args);
+ t.eq(box.div.style.border, borderWidth + "px solid " + borderColor, "style correctly set with both good values");
+
+ }
+ function test_Box_draw(t) {
+ t.plan( 5 );
+
+ OpenLayers.Util._modifyDOMElement =
+ OpenLayers.Util.modifyDOMElement;
+ OpenLayers.Util.modifyDOMElement =
+ function (element, id, px, sz) {
+ g_Element = element;
+ g_Id = id;
+ g_Px = px;
+ g_Sz = sz;
+ };
+
+ var box = {
+ div: {}
+ };
+
+
+ var px = {};
+ var sz = {};
+ var args = [px, sz];
+
+ g_Element = g_Id = g_Px = g_Sz = null;
+ var retVal = OpenLayers.Marker.Box.prototype.draw.apply(box, args);
+
+ t.eq(g_Element, box.div, "modifyDOMElement passes box's div for element");
+ t.eq(g_Id, null, "modifyDOMElement passes null for id");
+ t.eq(g_Px, px, "modifyDOMElement passes new px value for px");
+ t.eq(g_Sz, sz, "modifyDOMElement passes new sz value for sz");
+ t.ok(retVal == box.div, "draw returns box's div");
+
+ OpenLayers.Util.modifyDOMElement =
+ OpenLayers.Util._modifyDOMElement;
+
+ }
+
+ function test_Box_onScreen(t) {
+ t.plan( 2 );
+
+ var map = new OpenLayers.Map("map");
+
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.WMS("WMS Layer", url);
+
+ map.addLayer(layer);
+
+ mlayer = new OpenLayers.Layer.Boxes('Test Layer');
+ map.addLayer(mlayer);
+
+ map.zoomToExtent(new OpenLayers.Bounds(-50,-50,50,50));
+
+ //onscreen box
+ var bounds = new OpenLayers.Bounds(-1,-1,1,1);
+ var box = new OpenLayers.Marker.Box(bounds);
+ mlayer.addMarker(box);
+
+ t.ok( box.onScreen(), "box knows it's onscreen" );
+
+ //offscreen box
+ var bounds = new OpenLayers.Bounds(100,100,150,150);
+ var box2 = new OpenLayers.Marker.Box(bounds);
+ mlayer.addMarker(box2);
+
+ t.ok( !box2.onScreen(), "box knows it's offscreen" );
+ map.destroy();
+ }
+
+ function test_Box_display(t) {
+ t.plan( 2 );
+
+ var box = {
+ div: {
+ style: {}
+ }
+ };
+
+ //display(true)
+ var args = [true];
+ OpenLayers.Marker.Box.prototype.display.apply(box, args);
+ t.eq(box.div.style.display, "", "style.display correctly set to '' when display(true)");
+
+ //display(false)
+ var args = [false];
+ OpenLayers.Marker.Box.prototype.display.apply(box, args);
+ t.eq(box.div.style.display, "none", "style.display correctly set to 'none' when display(false)");
+ }
+
+ function test_Box_destroy(t) {
+ t.plan(3);
+
+ OpenLayers.Marker.prototype._destroy =
+ OpenLayers.Marker.prototype.destroy;
+ OpenLayers.Marker.prototype.destroy = function() {
+ g_Destroy = true;
+ }
+
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var borderColor = "blue";
+ var borderWidth = 55;
+
+ g_Destroy = null;
+ box = new OpenLayers.Marker.Box(bounds, borderColor, borderWidth);
+ box.destroy();
+
+ t.eq(box.bounds, null, "bounds nullified");
+ t.eq(box.div, null, "div nullified");
+ t.ok(g_Destroy == true, "OpenLayers.Marker.destroy() called");
+
+
+ OpenLayers.Marker.prototype.destroy =
+ OpenLayers.Marker.prototype._destroy;
+ }
+
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width:500px;height:550px"></div>
+</body>
+</html> \ No newline at end of file
diff --git a/misc/openlayers/tests/OLLoader.js b/misc/openlayers/tests/OLLoader.js
new file mode 100644
index 0000000..a2311c7
--- /dev/null
+++ b/misc/openlayers/tests/OLLoader.js
@@ -0,0 +1,26 @@
+// Adding a mode parameter with "build" as value in the run-tests.html will
+// make usage of the build version of the library.
+// get the OLLoader.js script location
+(function() {
+ var r = new RegExp("(^|(.*?\\/))(" + "OLLoader.js" + ")(\\?|$)"),
+ s = document.getElementsByTagName('script'),
+ src, m, l = "";
+ for(var i=0, len=s.length; i<len; i++) {
+ src = s[i].getAttribute('src');
+ if(src) {
+ var m = src.match(r);
+ if(m) {
+ l = m[1];
+ break;
+ }
+ }
+ }
+
+ var regex = new RegExp( "[\\?&]mode=([^&#]*)" );
+ var href = window.parent.location.href;
+ var results = regex.exec( href );
+ l += (results && results[1] == 'build') ?
+ "../build/OpenLayers.js" : "../lib/OpenLayers.js";
+ scriptTag = "<script src='" + l + "'></script>";
+ document.write(scriptTag);
+})();
diff --git a/misc/openlayers/tests/OpenLayers1.html b/misc/openlayers/tests/OpenLayers1.html
new file mode 100644
index 0000000..1d96be3
--- /dev/null
+++ b/misc/openlayers/tests/OpenLayers1.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+ <script src="../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ function test_OpenLayers(t) {
+ t.plan(3);
+
+ t.eq(OpenLayers._getScriptLocation(), "../", "Script location correctly detected.");
+
+ t.ok(OpenLayers.ImgPath !== undefined, "An ImgPath property exists.");
+
+ t.eq(OpenLayers.ImgPath, '', "The default for OpenLayers.ImgPath is the empty string.");
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/OpenLayers2.html b/misc/openlayers/tests/OpenLayers2.html
new file mode 100644
index 0000000..fbdb043
--- /dev/null
+++ b/misc/openlayers/tests/OpenLayers2.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+ <script src="bogus/1/OpenLayers.js-foo"></script>
+ <script src="bogus/2/foo-OpenLayers.js"></script>
+ <script src="../lib/OpenLayers.js?foo"></script>
+ <script src="bogus/3/after-OpenLayers.js"></script>
+ <script type="text/javascript">
+ function test_OpenLayers(t) {
+ t.plan(1);
+
+ var script = document.getElementById("script");
+
+ t.eq(OpenLayers._getScriptLocation(), "../", "Script location with search string correctly detected, and not fooled by other scripts.");
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/OpenLayers3.html b/misc/openlayers/tests/OpenLayers3.html
new file mode 100644
index 0000000..c4cbb80
--- /dev/null
+++ b/misc/openlayers/tests/OpenLayers3.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+ <script>
+ var OpenLayers = {singleFile: true};
+ </script>
+ <script src="../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ function test_OpenLayers(t) {
+ t.plan(1);
+
+ var script = document.getElementById("script");
+
+ t.eq(OpenLayers._getScriptLocation(), "../lib/", "Script location for single file build correctly detected.");
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/OpenLayers4.html b/misc/openlayers/tests/OpenLayers4.html
new file mode 100644
index 0000000..7c9012c
--- /dev/null
+++ b/misc/openlayers/tests/OpenLayers4.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+ <script type="text/javascript">
+ OpenLayers = {singleFile: true}; // just to make the test run faster
+ document.write('<scr'+'ipt src="../lib/OpenLayers.js"></scr'+'ipt>');
+ document.write('<scr'+'ipt src="bogus/foo-/OpenLayers.js"></scr'+'ipt>');
+ </script>
+ <script type="text/javascript">
+ function test_OpenLayers(t) {
+ t.plan(1);
+ t.eq(OpenLayers._getScriptLocation(), "../lib/",
+ "Script location correctly detected, and not fooled by other scripts.");
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/OpenLayersJsFiles.html b/misc/openlayers/tests/OpenLayersJsFiles.html
new file mode 100644
index 0000000..8dff0ec
--- /dev/null
+++ b/misc/openlayers/tests/OpenLayersJsFiles.html
@@ -0,0 +1,27 @@
+<html>
+<head>
+ <script type="text/javascript">
+ window.OpenLayers = new Array(
+ "OpenLayers/Util.js",
+ "OpenLayers/BaseTypes.js"
+ );
+ </script>
+ <script src="../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ function test_OpenLayers(t) {
+ t.plan(1);
+ var s = document.getElementsByTagName("script");
+ var src, count = 0;
+ for(var i=0, len=s.length; i<len; i++) {
+ src = s[i].getAttribute('src');
+ if(src) {
+ count++;
+ }
+ }
+ t.eq(count, 3, "Three OpenLayers scripts loaded.");
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Popup.html b/misc/openlayers/tests/Popup.html
new file mode 100644
index 0000000..d2a9685
--- /dev/null
+++ b/misc/openlayers/tests/Popup.html
@@ -0,0 +1,219 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var popup;
+
+ function test_Popup_default_constructor(t) {
+ t.plan( 8 );
+
+ var size = new OpenLayers.Size(OpenLayers.Popup.WIDTH,
+ OpenLayers.Popup.HEIGHT);
+ popup = new OpenLayers.Popup();
+
+ t.ok( popup instanceof OpenLayers.Popup, "new OpenLayers.Popup returns Popup object" );
+ t.ok(OpenLayers.String.startsWith(popup.id, "OpenLayers_Popup"),
+ "valid default popupid");
+ var firstID = popup.id;
+ t.ok(popup.contentSize.equals(size), "good default popup.size");
+ t.eq(popup.contentHTML, null, "good default popup.contentHTML");
+ t.eq(popup.backgroundColor, OpenLayers.Popup.COLOR, "good default popup.backgroundColor");
+ t.eq(popup.opacity, OpenLayers.Popup.OPACITY, "good default popup.opacity");
+ t.eq(popup.border, OpenLayers.Popup.BORDER, "good default popup.border");
+
+
+ popup = new OpenLayers.Popup();
+ var newID = popup.id;
+ t.ok(newID != firstID, "default id generator creating unique ids");
+ }
+
+ function test_Popup_constructor (t) {
+ t.plan(9);
+
+ var id = "chicken";
+ var w = 500;
+ var h = 400;
+ var sz = new OpenLayers.Size(w,h);
+ var lon = 5;
+ var lat = 40;
+ var ll = new OpenLayers.LonLat(lon, lat);
+ var content = "foo";
+ var closePopupCallback = function(e) {
+ //this should get triggered by the "observer.observer();" call below
+ t.ok(true, "closePopupCallback called")
+ };
+
+ popup = new OpenLayers.Popup(id,
+ ll,
+ sz,
+ content,
+ true,
+ closePopupCallback);
+
+ t.ok( popup instanceof OpenLayers.Popup, "new OpenLayers.Popup returns Popup object" );
+ t.eq(popup.id, id, "popup.id set correctly");
+ t.ok(popup.lonlat.equals(ll), "popup.lonlat set correctly");
+ t.ok(popup.contentSize.equals(sz), "popup.size set correctly");
+ t.eq(popup.contentHTML, content, "contentHTML porpoerty of set correctly");
+
+ // test that a browser event is registered on click on popup closebox
+ var closeImgDiv = popup.groupDiv.childNodes[1];
+ var cacheID = closeImgDiv._eventCacheID;
+ for (var i = 0; i < OpenLayers.Event.observers[cacheID].length; i++) {
+ var observer = OpenLayers.Event.observers[cacheID][i];
+ if (observer.element == closeImgDiv) {
+ if (observer.name == "click") {
+ t.ok(true, "A click event was registered for the close box element");
+ //call the registered observer to make sure it's the right one
+ observer.observer();
+ } else if (observer.name == "touchend") {
+ t.ok(true, "A touchend event was registered for the close box element");
+ //call the registered observer to make sure it's the right one
+ observer.observer();
+ } else {
+ t.fail("A " + observer.name + " event was registered for the close box element");
+ }
+ }
+ }
+ }
+
+ function test_Popup_updatePosition(t) {
+ t.plan(1)
+ var map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer('name', {'isBaseLayer':true}));
+ map.zoomToMaxExtent();
+ var popup = new OpenLayers.Popup('id');
+ map.addPopup(popup);
+ map.getLayerPxFromLonLat = function () { return null; }
+ popup.moveTo=function() { t.fail("Shouldnt' call moveTo if layerpx is null"); }
+ popup.lonlat = true;
+ popup.updatePosition();
+ t.ok(true, "update position doesn't fail when getLayerPxFromLonLat fails.");
+ map.destroy();
+ }
+ function test_Popup_keepInMap(t) {
+
+ var bn = OpenLayers.BROWSER_NAME;
+ OpenLayers.BROWSER_NAME = "mock";
+ t.plan(3);
+ var map = new OpenLayers.Map("map");
+ map.addLayer(new OpenLayers.Layer("", {isBaseLayer: true}));
+ map.zoomToMaxExtent();
+ var longString = "<div style='width: 200px; height: 200px'>Abc def</div>";
+ popup = new OpenLayers.Popup("chicken",
+ new OpenLayers.LonLat(90, 60),
+ new OpenLayers.Size(100,100),
+ longString,
+ null, true);
+ popup.panMapIfOutOfView = false;
+ popup.keepInMap = true;
+ map.addPopup(popup);
+ var safeSize = popup.getSafeContentSize(new OpenLayers.Size(1000,1000));
+ popup = new OpenLayers.Popup("chicken",
+ new OpenLayers.LonLat(90, 60),
+ new OpenLayers.Size(100,100),
+ longString,
+ null, true);
+ popup.panMapIfOutOfView = true;
+ popup.keepInMap = true;
+ map.addPopup(popup);
+ var safeSizePanKeep = popup.getSafeContentSize(new OpenLayers.Size(1000,1000));
+ popup.keepInMap = false;
+ map.addPopup(popup);
+ map.setCenter(-180, -90);
+ var safeSizePan = popup.getSafeContentSize(new OpenLayers.Size(1000,1000));
+ t.ok(safeSizePan.equals(safeSizePanKeep), "Panning means that all sizes are equal");
+ t.ok(safeSize.w < safeSizePan.w, "Width of non-panning is less");
+ t.ok(safeSize.h < safeSizePan.h, "Height of non-panning is less");
+ OpenLayers.BROWSER_NAME = bn;
+ }
+ function test_Popup_draw(t) {
+ t.plan( 15 );
+
+ var id = "chicken";
+ var x = 50;
+ var y = 100;
+ var w = 500;
+ var h = 400;
+ var content = "charlie";
+ var color = "red";
+ var hexColor = "#ff0000";
+ var opacity = 0.5;
+ var border = "1px solid";
+ map1 = new OpenLayers.Map("map");
+ popup = new OpenLayers.Popup(id);
+ popup.setSize(new OpenLayers.Size(w, h));
+ popup.setContentHTML(content);
+ popup.setBackgroundColor(color);
+ popup.setOpacity(opacity);
+ popup.setBorder(border);
+ map1.addPopup(popup);
+ popup.moveTo(new OpenLayers.Pixel(x, y));
+
+ t.eq(popup.div.id, id, "popup.div.id set correctly");
+ t.eq(popup.div.style.left, x + "px", "left position of popup.div set correctly");
+ t.eq(popup.div.style.top, y + "px", "top position of popup.div set correctly");
+
+ var contentDiv = popup.div.childNodes[0].childNodes[0];
+
+ t.eq(contentDiv.className, "olPopupContent", "correct content div className");
+ t.eq(contentDiv.id, "chicken_contentDiv", "correct content div id");
+ t.eq(contentDiv.style.position, "relative", "correct content div position");
+ //Safari 3 separates style overflow into overflow-x and overflow-y
+ var prop = (OpenLayers.BROWSER_NAME == 'safari') ? 'overflowX' : 'overflow';
+ t.eq(contentDiv.style[prop], "", "correct content div overflow");
+ t.eq(contentDiv.innerHTML, content, "correct content div content");
+
+ var bColor = popup.div.style.backgroundColor;
+ var goodColor = ( (bColor == color) || (bColor == hexColor));
+ t.ok(goodColor, "good default popup.backgroundColor");
+ if (navigator.appName.indexOf("Microsoft") == -1 || new RegExp(/msie 10/).test(navigator.userAgent.toLowerCase())) {
+ t.eq(parseFloat(popup.div.style.opacity), opacity, "good default popup.opacity");
+ } else {
+ t.eq(popup.div.style.filter, "alpha(opacity=" + opacity*100 + ")", "good default popup.opacity");
+ }
+ //Safari 3 separates the border style into separate entities when reading it
+ if (OpenLayers.BROWSER_NAME == 'safari') {
+ var s = border.split(' ');
+ t.ok(popup.div.style.borderTopWidth == s[0] && popup.div.style.borderTopStyle == s[1], "good default popup.border")
+ } else {
+ t.ok(popup.div.style.border.indexOf(border) != -1, "good default popup.border");
+ }
+
+ x += 50;
+ popup.moveTo(new OpenLayers.Pixel(x, y));
+ t.eq(popup.div.style.left, x + "px", "moveTo updates left position of popup.div correctly");
+ t.eq(popup.div.style.top, y + "px", "moveTo updates top position of popup.div correctly");
+
+
+ //closeOnMove
+ var checkMapEvent = function(map, popup) {
+ var startListeners = map.events.listeners['movestart'];
+ if (startListeners) {
+ for (var i = 0; i < startListeners.length; i++) {
+ var listener = startListeners[i];
+ if ((listener.obj == popup) && (listener.func == popup.hide)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+ var registered = checkMapEvent(map1, popup);
+ t.ok(!registered, "when not 'closeOnMove', correctly not registered hide() on map's movestart.")
+
+ var popup2 = new OpenLayers.Popup('test');
+ popup2.closeOnMove = true;
+ map1.addPopup(popup2);
+
+ registered = checkMapEvent(map1, popup2);
+ t.ok(registered, "when 'closeOnMove', correctly registered hide() on map's movestart.")
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:512px; height:256px"> </div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Popup/Anchored.html b/misc/openlayers/tests/Popup/Anchored.html
new file mode 100644
index 0000000..3197e84
--- /dev/null
+++ b/misc/openlayers/tests/Popup/Anchored.html
@@ -0,0 +1,37 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var popup;
+
+ function test_Popup_Anchored_default_constructor(t) {
+ t.plan( 4 );
+
+ popup = new OpenLayers.Popup.Anchored();
+
+ t.ok( popup instanceof OpenLayers.Popup.Anchored, "new OpenLayers.Popup.Anchored returns Popup.Anchored object" );
+ t.ok(OpenLayers.String.startsWith(popup.id, "OpenLayers_Popup_Anchored"), "valid default popupid");
+ var firstID = popup.id;
+ t.eq(popup.contentHTML, null, "good default popup.contentHTML");
+
+
+ popup = new OpenLayers.Popup.Anchored();
+ var newID = popup.id;
+ t.ok(newID != firstID, "default id generator creating unique ids");
+ }
+ function test_Popup_Anchored_updateRelPos(t) {
+ t.plan(1);
+ var popup = new OpenLayers.Popup.Anchored();
+ popup.calculateNewPx = function () {}
+ popup.calculateRelativePosition = function() {
+ t.ok(true, "update relative position is called on moveTo");
+ }
+ popup.moveTo(new OpenLayers.Pixel(0,0));
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Popup/FramedCloud.html b/misc/openlayers/tests/Popup/FramedCloud.html
new file mode 100644
index 0000000..7da86e3
--- /dev/null
+++ b/misc/openlayers/tests/Popup/FramedCloud.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_Popup_FramedCloud_setHTML(t) {
+ t.plan(1);
+ popup = new OpenLayers.Popup.FramedCloud();
+ popup.setContentHTML("<p></p>");
+ t.ok("setHTML on popup not yet added to map doesn't fail");
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:512px; height:256px"> </div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Projection.html b/misc/openlayers/tests/Projection.html
new file mode 100644
index 0000000..5864be5
--- /dev/null
+++ b/misc/openlayers/tests/Projection.html
@@ -0,0 +1,87 @@
+<html>
+ <head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_Projection_constructor(t) {
+ t.plan(9);
+
+ var options = {'foo': 'bar'};
+ var projection = new OpenLayers.Projection("code", options);
+ t.ok(projection instanceof OpenLayers.Projection,
+ "new OpenLayers.Projection returns object" );
+ t.eq(projection.projCode, "code", "The proj code is maintained");
+ t.eq(projection.getCode(), "code", "The proj code is maintained.");
+ t.eq(projection.getUnits(), null, "Null units with no proj4js library.");
+ t.eq(projection.foo, "bar", "constructor sets options correctly");
+
+ var projection2 = new OpenLayers.Projection("epsg:4325", options);
+ out = OpenLayers.Projection.transform({'x':10,'y':12}, projection, projection2);
+ t.eq(out.x, 10, "Null transform has no effect");
+ t.eq(out.y, 12, "Null transform has no effect");
+
+ t.eq(projection.equals(null), false, "equals on null projection returns false");
+ t.eq(projection.equals({}), false, "equals on null projection object returns false (doesn't call getCode)");
+ }
+
+ function test_Projection_equals(t) {
+ t.plan(8);
+ var origTransforms = OpenLayers.Util.extend({}, OpenLayers.Projection.transforms);
+ OpenLayers.Projection.addTransform("EPSG:4326", "FOO", OpenLayers.Projection.nullTransform);
+ OpenLayers.Projection.addTransform("FOO", "EPSG:4326", OpenLayers.Projection.nullTransform);
+ var projection = new OpenLayers.Projection("FOO");
+ t.eq(projection.equals(new OpenLayers.Projection("EPSG:4326")), true, "EPSG:4326 and FOO are equal without proj4js");
+ t.eq(projection.equals(new OpenLayers.Projection("EPSG:900913")), false, "EPSG:900913 and FOO are not equal without proj4js");
+ t.eq(new OpenLayers.Projection("EPSG:4326").equals(new OpenLayers.Projection("EPSG:4326")), true, "EPSG:4326 and EPSG:4326 are equal without proj4js");
+ t.eq(new OpenLayers.Projection("BAR").equals(new OpenLayers.Projection("EPSG:4326")), false, "Projection.equals() returns false for unknown projections withoug proj4js");
+ OpenLayers.Projection.transforms = origTransforms;
+
+ var proj1 = new OpenLayers.Projection("EPSG:4326");
+ var proj2 = new OpenLayers.Projection("FOO");
+ var proj3 = new OpenLayers.Projection("EPSG:900913");
+ var proj4 = new OpenLayers.Projection("EPSG:4326");
+ var proj5 = new OpenLayers.Projection("BAR");
+
+ // conditionally mock up proj4js
+ var hasProj = !!window.Proj4js;
+ if (!hasProj) {
+ window.Proj4js = {};
+ }
+ proj1.proj = {defData: "+title= WGS84 +foo=bar +x=0"};
+ proj2.proj = {defData: "+title=FOO +foo=bar +x=0", srsCode: "FOO"};
+ proj3.proj = {defData: "+title=Web Mercator +foo=bar +x=0 +I=am-different"};
+ proj4.proj = proj1.proj;
+ proj5.proj = {srsCode: "BAR"};
+
+ t.eq(proj2.equals(proj1), true, "EPSG:4326 and FOO are equal with proj4js");
+ t.eq(proj2.equals(proj3), false, "EPSG:900913 and FOO are not equal with proj4js");
+ t.eq(proj1.equals(proj4), true, "EPSG:4326 and EPSG:4326 are equal with proj4js");
+ t.eq(proj2.equals(proj5), false, "Projection.equals() returns false for unknown projections with proj4js");
+
+ if (!hasProj) {
+ window.Proj4js = undefined;
+ }
+
+ }
+
+ function test_equals_string(t) {
+
+ t.plan(7);
+ var gg = new OpenLayers.Projection("EPSG:4326");
+ var sm = new OpenLayers.Projection("EPSG:900913");
+
+ // allow comparison with identifier
+ t.eq(gg.equals("EPSG:4326"), true, "EPSG:4326 equality with string");
+ t.eq(gg.equals("EPSG:4327"), false, "EPSG:4326 inequality with string");
+ t.eq(sm.equals("EPSG:900913"), true, "EPSG:900913 equality with string");
+ t.eq(sm.equals("EPSG:900914"), false, "EPSG:900913 inequality with string");
+ t.eq(sm.equals("EPSG:3857"), true, "EPSG:900913 equality with EPSG:3857");
+ t.eq(sm.equals("EPSG:102113"), true, "EPSG:900913 equality with EPSG:102113");
+ t.eq(sm.equals("EPSG:102100"), true, "EPSG:900913 equality with EPSG:102100");
+
+ }
+
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/Protocol.html b/misc/openlayers/tests/Protocol.html
new file mode 100644
index 0000000..7432b86
--- /dev/null
+++ b/misc/openlayers/tests/Protocol.html
@@ -0,0 +1,63 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(4);
+ var options = {};
+ var protocol = new OpenLayers.Protocol(options);
+
+ t.ok(protocol instanceof OpenLayers.Protocol,
+ "new OpenLayers.Protocol returns object" );
+ t.eq(protocol.options, options, "constructor sets this.options");
+ t.eq(protocol.options.filter, null, "constructor sets defaultFilter to null");
+ t.eq(protocol.autoDestroy, true, "constructor does not modify this.autoDestroy");
+ }
+
+ function test_read_defaultFilter(t) {
+ t.plan(4);
+
+ var protocol = new OpenLayers.Protocol({filter: "a"});
+ var options = {};
+ protocol.read(options);
+ // the line below is what happens in Protocol.WFS.v1::read
+ OpenLayers.Util.applyDefaults(options, protocol.options);
+ t.eq(options.filter, "a", "filter from protocol.options applied to options");
+ protocol.destroy();
+
+ var defaultFilter = 'a';
+ var options = {
+ defaultFilter: defaultFilter
+ };
+
+ protocol = new OpenLayers.Protocol(options);
+ var readFilter = 'b';
+ var options = { filter: readFilter };
+
+ protocol.read(options);
+
+ var filter = options.filter;
+ t.ok(filter instanceof OpenLayers.Filter.Logical, "read method merge default filter & options filter to a logical one");
+ t.eq(filter.type, OpenLayers.Filter.Logical.AND, "logical filter type is OpenLayers.Filter.Logical.AND");
+ t.eq(filter.filters, [defaultFilter, readFilter], "read method has merged filters");
+ protocol.destroy();
+ }
+
+ function test_destroy(t) {
+ t.plan(2);
+ var protocol = new OpenLayers.Protocol({
+ options: {foo: 'bar'},
+ format: 'foo'
+ });
+ protocol.destroy();
+
+ t.eq(protocol.format, null, "destroy nullify protocol.format");
+ t.eq(protocol.options, null, "destroy nullify protocol.options");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Protocol/CSW.html b/misc/openlayers/tests/Protocol/CSW.html
new file mode 100644
index 0000000..8c0847c
--- /dev/null
+++ b/misc/openlayers/tests/Protocol/CSW.html
@@ -0,0 +1,90 @@
+<html>
+<head>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(3);
+
+ var protocol = new OpenLayers.Protocol.CSW({formatOptions: {foo: "bar"}});
+ t.ok(protocol instanceof OpenLayers.Protocol.CSW.v2_0_2,
+ "initialize returns instance of default versioned protocol");
+ var format = protocol.format;
+ t.ok(format instanceof OpenLayers.Format.CSWGetRecords.v2_0_2, "Default format created");
+ t.ok(format.foo, "bar", "formatOptions set correctly");
+ protocol.destroy();
+ }
+
+ function test_read(t) {
+ t.plan(6);
+
+ var protocol = new OpenLayers.Protocol.CSW({
+ url: "http://some.url.org",
+ parseData: function(request) {
+ t.eq(request.responseText, "foo", "parseData called properly");
+ return "foo";
+ }
+ });
+
+ var _POST = OpenLayers.Request.POST;
+
+ var expected, status;
+ OpenLayers.Request.POST = function(obj) {
+ t.xml_eq(new OpenLayers.Format.XML().read(obj.data).documentElement, expected, "GetRecords request is correct");
+ obj.status = status;
+ obj.responseText = "foo";
+ obj.options = {};
+ t.delay_call(0.1, function() {obj.callback.call(this)});
+ return obj;
+ };
+
+ expected = readXML("GetRecords");
+ status = 200;
+ var data = {
+ "resultType": "results",
+ "maxRecords": 100,
+ "Query": {
+ "typeNames": "gmd:MD_Metadata",
+ "ElementSetName": {
+ "value": "full"
+ }
+ }
+ };
+ var response = protocol.read({
+ params: data,
+ callback: function(response) {
+ t.eq(response.data, "foo", "user callback properly called with data");
+ t.eq(response.code, OpenLayers.Protocol.Response.SUCCESS, "success reported properly to user callback");
+ }
+ });
+
+ var options = {
+ params: data,
+ callback: function(response) {
+ t.eq(response.code, OpenLayers.Protocol.Response.FAILURE, "failure reported properly to user callback");
+ }
+ };
+ status = 400;
+ var response = protocol.read(options);
+
+ OpenLayers.Request.POST = _POST;
+ }
+
+ function readXML(id) {
+ var xml = document.getElementById(id).firstChild.nodeValue;
+ return new OpenLayers.Format.XML().read(xml).documentElement;
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:512px; height:256px"> </div>
+<div id="GetRecords"><!--
+<csw:GetRecords xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" service="CSW" version="2.0.2" resultType="results" maxRecords="100">
+ <csw:Query typeNames="gmd:MD_Metadata">
+ <csw:ElementSetName>full</csw:ElementSetName>
+ </csw:Query>
+</csw:GetRecords>
+--></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Protocol/HTTP.html b/misc/openlayers/tests/Protocol/HTTP.html
new file mode 100644
index 0000000..fac460b
--- /dev/null
+++ b/misc/openlayers/tests/Protocol/HTTP.html
@@ -0,0 +1,842 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(8);
+ var a = new OpenLayers.Protocol.HTTP({
+ url: "foo"
+ });
+
+ // 4 tests
+ t.eq(a.url, "foo", "constructor sets url");
+ t.eq(a.options.url, a.url, "constructor copies url to options.url");
+ t.eq(a.params, {}, "constructor sets params");
+ t.eq(a.options.params, undefined, "constructor do not copy params to options.params");
+
+ var params = {hello: "world"};
+ var b = new OpenLayers.Protocol.HTTP({
+ url: "bar",
+ params: params
+ });
+
+ // 4 tests
+ t.eq(b.url, "bar", "constructor sets url");
+ t.eq(b.options.url, b.url, "constructor copies url to options.url");
+ t.eq(b.params, params, "constructor sets params");
+ t.eq(b.options.params, b.params, "constructor copies params to options.params");
+ }
+
+ function test_destroy(t) {
+ t.plan(3);
+ var protocol = new OpenLayers.Protocol.HTTP({
+ url: "bar",
+ params: {hello: "world"}
+ });
+ protocol.destroy();
+ t.eq(protocol.options, null, "destroy nullifies options");
+ t.eq(protocol.params, null, "destroy nullifies params");
+ t.eq(protocol.headers, null, "destroy nullifies headers");
+ }
+
+ function test_read(t) {
+ t.plan(10);
+ var protocol = new OpenLayers.Protocol.HTTP({
+ 'url': 'foo_url',
+ 'params': {'k': 'foo_param'}
+ });
+
+ // fake XHR request object
+ var request = {'status': 200};
+
+ // options to pass to read
+ var readOptions = {
+ 'url': 'bar_url',
+ 'params': {'k': 'bar_param'},
+ 'headers': {'k': 'bar_header'},
+ 'scope': {'hello': 'world'},
+ 'callback': function() {}
+ };
+
+ var response;
+
+ protocol.handleResponse = function(resp, opt) {
+ // 4 tests
+ var req = resp.priv;
+ t.ok(this == protocol,
+ 'handleResponse called with correct scope');
+ t.ok(opt == readOptions,
+ 'handleResponse called with correct options');
+ t.eq(resp.CLASS_NAME, 'OpenLayers.Protocol.Response',
+ 'handleResponse called with a Response object');
+ t.eq(req, request,
+ 'handleResponse called with correct request');
+
+ response = resp;
+ };
+
+ var _get = OpenLayers.Request.GET;
+
+ OpenLayers.Request.GET = function(options) {
+ // 5 tests
+ t.eq(options.url, readOptions.url,
+ 'GET called with correct url in options');
+ t.eq(options.params['k'], readOptions.params['k'],
+ 'GET called with correct params in options');
+ t.eq(options.headers['k'], readOptions.headers['k'],
+ 'GET called with correct headers in options');
+ t.eq(options.scope, undefined,
+ 'GET called with correct scope in options');
+ t.ok(typeof options.callback == 'function',
+ 'GET called with a callback in options');
+ t.delay_call(0.1, function() {
+ options.callback(request);
+ t.ok(resp == response,
+ 'read returns the expected response object');
+ // cleanup
+ protocol.destroy();
+ OpenLayers.Request.GET = _get;
+ });
+ return request;
+ };
+
+ var resp = protocol.read(readOptions);
+
+ OpenLayers.Request.GET = _get;
+ }
+
+ function test_readWithPOST(t) {
+ t.plan(10);
+ var protocol = new OpenLayers.Protocol.HTTP({
+ 'url': 'foo_url',
+ 'params': {'k': 'foo_param'}
+ });
+
+ // fake XHR request object
+ var request = {'status': 200};
+
+ // options to pass to read
+ var readOptions = {
+ 'url': 'bar_url',
+ 'params': {'k': 'bar_param'},
+ 'scope': {'hello': 'world'},
+ 'callback': function() {},
+ 'readWithPOST': true
+ };
+
+ var response;
+
+ protocol.handleResponse = function(resp, opt) {
+ // 4 tests
+ var req = resp.priv;
+ t.ok(this == protocol,
+ 'handleResponse called with correct scope');
+ t.ok(opt == readOptions,
+ 'handleResponse called with correct options');
+ t.eq(resp.CLASS_NAME, 'OpenLayers.Protocol.Response',
+ 'handleResponse called with a Response object');
+ t.eq(req, request,
+ 'handleResponse called with correct request');
+
+ response = resp;
+ };
+
+ var _post = OpenLayers.Request.POST;
+
+ OpenLayers.Request.POST = function(options) {
+ // 5 tests
+ t.eq(options.url, readOptions.url,
+ 'GET with POST called with correct url in options');
+ t.eq(options.data, OpenLayers.Util.getParameterString(readOptions.params),
+ 'GET with POST called with correct params encoded in options');
+ t.eq(options.headers, {"Content-Type": "application/x-www-form-urlencoded"},
+ 'GET with POST called with correct headers (application/x-www-form-urlencoded)');
+ t.eq(options.scope, undefined,
+ 'GET with POST called with correct scope in options');
+ t.ok(typeof options.callback == 'function',
+ 'GET with POST called with a callback in options');
+ t.delay_call(0.1, function() {
+ options.callback(request);
+ t.ok(resp == response,
+ 'read returns the expected response object');
+ // cleanup
+ protocol.destroy();
+ OpenLayers.Request.POST = _post;
+ });
+ return request;
+ };
+
+ var resp = protocol.read(readOptions);
+
+ OpenLayers.Request.POST = _post;
+ }
+
+ function test_read_method(t) {
+ t.plan(4);
+
+ var _post = OpenLayers.Request.POST;
+ OpenLayers.Request.POST = function(options) { return 'post'; }
+ var _get = OpenLayers.Request.GET;
+ OpenLayers.Request.GET = function(options) { return 'get'; }
+
+ var protocol = new OpenLayers.Protocol.HTTP({});
+
+ t.eq(protocol.read({}).priv, 'get',
+ 'readWithPOST is false by default');
+ t.eq(protocol.read({readWithPOST: true}).priv, 'post',
+ 'readWithPOST can be set in read options');
+
+ var protocol = new OpenLayers.Protocol.HTTP({readWithPOST: true});
+
+ t.eq(protocol.read({}).priv, 'post',
+ 'readWithPOST can be set in constructor');
+ t.eq(protocol.read({readWithPOST: false}).priv, 'get',
+ 'readWithPOST can be overridden in read options');
+
+ OpenLayers.Request.POST = _post;
+ OpenLayers.Request.GET = _get;
+ }
+
+ function test_read_bbox(t) {
+ t.plan(6);
+
+ var _get = OpenLayers.Request.GET;
+
+ var bounds = new OpenLayers.Bounds(1, 2, 3, 4);
+ var filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ value: bounds,
+ projection: new OpenLayers.Projection("foo")
+ });
+
+ // log requests
+ var log, exp;
+ OpenLayers.Request.GET = function(options) {
+ log.push(options.params.bbox);
+ return {status: 200};
+ };
+
+ // 1) issue request with default protocol
+ log = [];
+ new OpenLayers.Protocol.HTTP().read({filter: filter});
+
+ t.eq(log.length, 1, "1) GET called once");
+ t.ok(log[0] instanceof Array, "1) bbox param is array");
+ exp = bounds.toArray();
+ t.eq(log[0], exp, "1) bbox param doesn't include SRS id by default");
+
+ // 2) issue request with default protocol
+ log = [];
+ new OpenLayers.Protocol.HTTP({srsInBBOX: true}).read({filter: filter});
+
+ t.eq(log.length, 1, "2) GET called once");
+ t.ok(log[0] instanceof Array, "2) bbox param is array");
+ exp = bounds.toArray();
+ exp.push("foo");
+ t.eq(log[0], exp, "2) bbox param includes SRS id if srsInBBOX is true");
+
+ OpenLayers.Request.GET = _get;
+ }
+
+ function test_parseFeatures(t) {
+ t.plan(5);
+
+ var protocol = new OpenLayers.Protocol.HTTP();
+
+ // test responseXML - 2 tests
+ var request = {
+ 'responseXML': {
+ 'documentElement': 'xml'
+ }
+ };
+ protocol.format = {
+ 'read': function(doc) {
+ t.eq(doc.documentElement, 'xml',
+ 'format.read called with correct doc');
+ return doc.documentElement;
+ }
+ };
+ var ret = protocol.parseFeatures(request);
+ t.eq(ret, 'xml', 'parseFeatures returns expected value');
+
+ // test responseText - 2 tests
+ var request = {
+ 'responseText': 'text'
+ };
+ protocol.format = {
+ 'read': function(doc) {
+ t.eq(doc, 'text',
+ 'format.read called with correct doc');
+ return doc;
+ }
+ };
+ var ret = protocol.parseFeatures(request);
+ t.eq(ret, 'text', 'parseFeatures returns expected value');
+
+ // test empty responseText - 1 test
+ var request = {
+ 'responseText': ''
+ };
+ protocol.format = {
+ 'read': function(doc) {
+ t.fail('format.read should not be called');
+ }
+ };
+ var ret = protocol.parseFeatures(request);
+ t.eq(ret, null, 'parseFeatures returns expected value');
+ }
+
+ function test_create(t) {
+ t.plan(10);
+ var protocol = new OpenLayers.Protocol.HTTP({
+ 'url': 'foo_url',
+ 'format': {'write': function() {}}
+ });
+
+ // fake XHR request object
+ var request = {'status': 200};
+
+ // features to pass to create
+ var features = ['feature'];
+
+ // options to pass to create
+ var createOptions = {
+ 'url': 'bar_url',
+ 'headers': {'k': 'bar_header'},
+ 'scope': {'hello': 'world'},
+ 'callback': function() {}
+ };
+
+ var response;
+
+ protocol.handleCreate = function(resp, opt) {
+ // 5 tests
+ var req = resp.priv;
+ t.ok(this == protocol,
+ 'handleCreate called with correct scope');
+ t.ok(opt == createOptions,
+ 'handleCreate called with correct options');
+ t.eq(resp.CLASS_NAME, 'OpenLayers.Protocol.Response',
+ 'handleCreate called with a Response object');
+ t.ok(resp.reqFeatures == features,
+ 'handleCreate called with correct requested features in response');
+ t.eq(req, request,
+ 'handleCreate called with correct request');
+
+ response = resp;
+ };
+
+ var _post = OpenLayers.Request.POST;
+
+ OpenLayers.Request.POST = function(options) {
+ // 4 tests
+ t.eq(options.url, createOptions.url,
+ 'POST called with correct url in options');
+ t.eq(options.headers['k'], createOptions.headers['k'],
+ 'POST called with correct headers in options');
+ t.eq(options.scope, undefined,
+ 'POST called with correct scope in options');
+ t.ok(typeof options.callback == 'function',
+ 'POST called with a callback in options');
+ // call callback - delayed because this function has to return first
+ t.delay_call(0.1, function() {
+ options.callback(request);
+ t.ok(resp == response,
+ 'create returns the expected response object');
+ // cleanup
+ protocol.destroy();
+ OpenLayers.Request.POST = _post;
+ });
+ return request;
+ };
+
+ var resp = protocol.create(features, createOptions);
+
+ OpenLayers.Request.POST = _post;
+ }
+
+ function test_update(t) {
+ t.plan(10);
+ var protocol = new OpenLayers.Protocol.HTTP({
+ 'url': 'foo_url',
+ 'format': {'write': function() {}}
+ });
+
+ // fake XHR request object
+ var request = {'status': 200};
+
+ // feature to pass to update
+ var feature = {'feature':'feature'};
+
+ // options to pass to update
+ var updateOptions = {
+ 'url': 'bar_url',
+ 'headers': {'k': 'bar_header'},
+ 'scope': {'hello': 'world'},
+ 'callback': function() {}
+ };
+
+ var response;
+
+ protocol.handleUpdate = function(resp, opt) {
+ var req = resp.priv;
+ // 5 tests
+ t.ok(this == protocol,
+ 'handleUpdate called with correct scope');
+ t.ok(opt == updateOptions,
+ 'handleUpdate called with correct options');
+ t.eq(resp.CLASS_NAME, 'OpenLayers.Protocol.Response',
+ 'handleUpdate called with a Response object');
+ t.ok(resp.reqFeatures == feature,
+ 'handleUpdate called with correct requested feature in response');
+ t.eq(req, request,
+ 'handleUpdate called with correct request');
+
+ response = resp;
+ };
+
+ var _put = OpenLayers.Request.PUT;
+
+ OpenLayers.Request.PUT = function(options) {
+ // 4 tests
+ t.eq(options.url, updateOptions.url,
+ 'PUT called with correct url in options');
+ t.eq(options.headers['k'], updateOptions.headers['k'],
+ 'PUT called with correct headers in options');
+ t.eq(options.scope, undefined,
+ 'PUT called with correct scope in options');
+ t.ok(typeof options.callback == 'function',
+ 'PUT called with a callback in options');
+ // call callback - delayed because this function has to return first
+ t.delay_call(0.1, function() {
+ options.callback(request);
+ t.ok(resp == response,
+ 'update returns the expected response object');
+ // cleanup
+ protocol.destroy();
+ OpenLayers.Request.PUT = _put;
+ });
+ return request;
+ };
+
+ var resp = protocol.update(feature, updateOptions);
+
+ OpenLayers.Request.PUT = _put;
+ }
+
+ function test_update_featureurl(t) {
+
+ // test that OpenLayers.Request.PUT receives the URL
+ // set in the feature
+ // http://trac.openlayers.org/ticket/2393#comment:11
+
+ t.plan(1);
+
+ var protocol = new OpenLayers.Protocol.HTTP({
+ 'url': 'foo_url',
+ 'format': {'write': function() {}}
+ });
+
+ // feature to pass to update
+ var feature = {'feature':'feature', 'url': 'bar_url'};
+
+ var _put = OpenLayers.Request.PUT;
+
+ OpenLayers.Request.PUT = function(options) {
+ t.eq(options.url, feature.url,
+ 'PUT called with correct url in options');
+ };
+
+ protocol.update(feature);
+
+ OpenLayers.Request.PUT = _put;
+ }
+
+ function test_handleResponse(t) {
+ t.plan(6);
+
+ var protocol = new OpenLayers.Protocol.HTTP();
+
+ var options, response, request, features;
+
+ // test options - 2 tests
+ var scope = {'fake': 'scope'};
+ options = {
+ 'scope': scope,
+ 'callback': function(resp) {
+ t.ok(this == scope,
+ '[no status] callback called with correct scope');
+ t.ok(resp == response,
+ '[no status] callback called with correct response');
+ }
+ };
+ response = {priv: {}};
+ protocol.handleResponse(response, options);
+
+ // test failure condition - 1 test
+ options = {
+ 'callback': function(resp) {
+ t.eq(resp.code, OpenLayers.Protocol.Response.FAILURE,
+ '[status 400] callback called with correct response code');
+ }
+ };
+ response = {priv: {status: 400}};
+ protocol.handleResponse(response, options);
+
+ // test success condition - 3 tests
+ features = {'fake': 'features'};
+ options = {
+ 'callback': function(resp) {
+ t.eq(resp.code, OpenLayers.Protocol.Response.SUCCESS,
+ '[status 200] callback called with correct response code');
+ t.eq(resp.features, features,
+ '[status 200] callback called with correct features in response');
+ }
+ };
+ response = {priv: {status: 200}};
+ protocol.parseFeatures = function(request) {
+ t.ok(request == response.priv,
+ '[status 200] parseFeatures called with correct request');
+ return features;
+ }
+ protocol.handleResponse(response, options);
+
+ // cleanup
+ protocol.destroy();
+ }
+
+ function test_delete(t) {
+ t.plan(10);
+ var protocol = new OpenLayers.Protocol.HTTP({
+ 'url': 'foo_url'
+ });
+
+ // fake XHR request object
+ var request = {'status': 200};
+
+ // feature to pass to delete
+ var feature = {'url': 'bar_url'};
+
+ // options to pass to delete
+ var deleteOptions = {
+ 'url': 'bar_url',
+ 'headers': {'k': 'bar_header'},
+ 'scope': {'hello': 'world'},
+ 'callback': function() {}
+ };
+
+ var response;
+
+ protocol.handleDelete = function(resp, opt) {
+ // 5 tests
+ var req = resp.priv;
+ t.ok(this == protocol,
+ 'handleDelete called with correct scope');
+ t.ok(opt == deleteOptions,
+ 'handleDelete called with correct options');
+ t.eq(resp.CLASS_NAME, 'OpenLayers.Protocol.Response',
+ 'handleDelete called with a Response object');
+ t.ok(resp.reqFeatures == feature,
+ 'handleDelete called with correct requested feature in response');
+ t.eq(req, request,
+ 'handleDelete called with correct request');
+
+ response = resp;
+ };
+
+ var _delete = OpenLayers.Request.DELETE;
+
+ OpenLayers.Request.DELETE = function(options) {
+ // 4 tests
+ t.eq(options.url, deleteOptions.url,
+ 'DELETE called with correct url in options');
+ t.eq(options.headers['k'], deleteOptions.headers['k'],
+ 'DELETE called with correct headers in options');
+ t.eq(options.scope, undefined,
+ 'DELETE called with correct scope in options');
+ t.ok(typeof options.callback == 'function',
+ 'DELETE called with a callback in options');
+ // call callback - delayed because this function has to return first
+ t.delay_call(0.1, function() {
+ options.callback(request);
+ t.ok(resp == response,
+ 'read returns the expected response object');
+ // cleanup
+ protocol.destroy();
+ OpenLayers.Request.DELETE = _delete;
+ });
+ return request;
+ };
+
+ var resp = protocol['delete'](feature, deleteOptions);
+
+ OpenLayers.Request.DELETE = _delete;
+ }
+
+ function test_delete_featureurl(t) {
+
+ // test that OpenLayers.Request.DELETE receives the URL
+ // set in the feature
+ // http://trac.openlayers.org/ticket/2393#comment:11
+
+ t.plan(1);
+
+ var protocol = new OpenLayers.Protocol.HTTP({
+ 'url': 'foo_url',
+ 'format': {'write': function() {}}
+ });
+
+ // feature to pass to update
+ var feature = {'feature':'feature', 'url': 'bar_url'};
+
+ var _delete = OpenLayers.Request.DELETE;
+
+ OpenLayers.Request.DELETE = function(options) {
+ t.eq(options.url, feature.url,
+ 'DELETE called with correct url in options');
+ };
+
+ protocol['delete'](feature);
+
+ OpenLayers.Request.DELETE = _delete;
+ }
+
+ function test_handleDelete(t) {
+ t.plan(4);
+
+ var protocol = new OpenLayers.Protocol.HTTP();
+
+ var options, response, request, features;
+
+ // test options - 2 tests
+ var scope = {'fake': 'scope'};
+ options = {
+ 'scope': scope,
+ 'callback': function(resp) {
+ t.ok(this == scope,
+ 'callback called with correct scope');
+ t.ok(resp == response,
+ 'callback called with correct response');
+ }
+ };
+ response = {priv: {}};
+ protocol.handleDelete(response, options);
+
+ // test failure condition - 1 test
+ options = {
+ 'callback': function(resp) {
+ t.eq(resp.code, OpenLayers.Protocol.Response.FAILURE,
+ 'callback called with correct response code');
+ }
+ };
+ response = {priv: {status: 400}};
+ protocol.handleDelete(response, options);
+
+ // test success condition - 1 test
+ options = {
+ 'callback': function(resp) {
+ t.eq(resp.code, OpenLayers.Protocol.Response.SUCCESS,
+ 'callback called with correct response code');
+ }
+ };
+ response = {priv: {status: 200}};
+ protocol.handleDelete(response, options);
+
+ // cleanup
+ protocol.destroy();
+ }
+
+ function test_commit(t) {
+ t.plan(17);
+
+ var protocol = new OpenLayers.Protocol.HTTP();
+
+ // 6 features
+ var features = [
+ {'state': OpenLayers.State.INSERT},
+ {'state': OpenLayers.State.INSERT},
+ {'state': OpenLayers.State.UPDATE},
+ {'state': OpenLayers.State.UPDATE},
+ {'state': OpenLayers.State.DELETE},
+ {'state': OpenLayers.State.DELETE}
+ ];
+
+ var options = {
+ 'create': {
+ 'callback': function(resp) {
+ }
+ },
+ 'update': {
+ 'callback': function(resp) {
+ }
+ },
+ 'delete': {
+ 'callback': function(resp) {
+ }
+ }
+ };
+
+ var respCreate = new OpenLayers.Protocol.Response();
+ var respUpdate = new OpenLayers.Protocol.Response();
+ var respDelete = new OpenLayers.Protocol.Response();
+
+ // 2 tests
+ protocol['create'] = function(feature, options) {
+ t.ok(options.scope == protocol,
+ 'create called with correct scope');
+ t.ok(typeof options.callback == 'function',
+ 'create called with a callback in options');
+ options.callback.call(options.scope, respCreate);
+ return respCreate;
+ };
+ // 4 tests
+ protocol['update'] = function(feature, options) {
+ t.ok(options.scope == protocol,
+ 'update called with correct scope');
+ t.ok(typeof options.callback == 'function',
+ 'update called with a callback in options');
+ options.callback.call(options.scope, respUpdate);
+ return respUpdate;
+ };
+ // 4 tests
+ protocol['delete'] = function(feature, options) {
+ t.ok(options.scope == protocol,
+ 'delete called with correct scope');
+ t.ok(typeof options.callback == 'function',
+ 'delete called with a callback in options');
+ options.callback.call(options.scope, respDelete);
+ return respDelete;
+ };
+
+ var count = 0;
+
+ // 5 tests
+ protocol.callUserCallback = function(resp, opt) {
+ t.ok(opt == options,
+ 'callUserCallback called with correction options map');
+ count++;
+ };
+
+ var resp = protocol.commit(features, options);
+
+ // 2 tests
+ t.eq(count, 5, 'callUserCallback called for each request');
+ t.eq(resp.length, 5, 'commit returns array with correct length');
+
+ // cleanup
+ protocol.destroy();
+ }
+
+ function test_callUserCallback(t) {
+ t.plan(1);
+
+ var protocol = new OpenLayers.Protocol.HTTP();
+
+ var scope = {'fake': 'scope'};
+
+ // test commit callback
+ var log = {};
+ var options = {
+ foo: {
+ callback: function() {
+ log.scope = this;
+ },
+ scope: scope
+ }
+ };
+ var resp = {requestType: 'foo'};
+ protocol.callUserCallback(resp, options);
+ t.ok(log.scope, scope, 'correct callback called with correct scope');
+
+ }
+
+ function test_options(t) {
+ t.plan(6);
+
+ var log1 = {};
+
+ // test that read with no options uses protocol options - 5 tests
+ var url = ".";
+ var headers = {};
+ var params = {};
+ var scope = {};
+ var protocol = new OpenLayers.Protocol.HTTP({
+ format: new OpenLayers.Format({
+ read: function() {},
+ write: function() {}
+ }),
+ url: url,
+ headers: headers,
+ params: params,
+ callback: function(resp) {
+ log1.callbackCalled = true;
+ log1.callbackScope = this;
+ log1.request = resp && resp.priv;
+ log1.requestType = resp && resp.requestType;
+ },
+ scope: scope
+ });
+ protocol.read();
+
+ t.delay_call(2, function() {
+ t.eq(log1.callbackCalled, true, "[read] callback called");
+ t.eq(log1.callbackScope, scope, "[read] correct scope");
+ t.ok(log1.request instanceof OpenLayers.Request.XMLHttpRequest, "[read] correct priv type");
+ t.eq(log1.requestType, "read", "[read] correct request type");
+ });
+
+
+ // test that commit with no options uses protocol options - 2 tests
+ var log2 = {called: 0};
+ protocol.options.callback = function() {
+ log2.called++;
+ log2.scope = this;
+ };
+ protocol.commit([
+ {state: OpenLayers.State.INSERT},
+ {state: OpenLayers.State.INSERT},
+ {state: OpenLayers.State.UPDATE, url: "./1"},
+ {state: OpenLayers.State.UPDATE, url: "./2"},
+ {state: OpenLayers.State.DELETE, url: "./3"},
+ {state: OpenLayers.State.DELETE, url: "./4"}
+ ]);
+ t.delay_call(2, function() {
+ t.eq(log2.called, 1, "[commit] Callback called once.");
+ t.eq(log2.scope, scope, "[commit] Correct scope.");
+ });
+ }
+
+ function test_read_global_options(t) {
+
+ // test that calling read doesn't write params into the protocol's
+ // options object, see ticket #3237
+
+ t.plan(2);
+
+ var protocol = new OpenLayers.Protocol.HTTP({
+ url: '.',
+ callback: function() {},
+ params: {'a': 'a'}
+ });
+
+ // check initial state first
+ t.eq(protocol.options.params, {'a': 'a'},
+ 'protocol params are ok at initial state');
+
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: 'b',
+ value: 'b'
+ });
+ protocol.read({filter: filter});
+ t.eq(protocol.options.params, {'a': 'a'},
+ "protocol params are ok after read");
+ }
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Protocol/SOS.html b/misc/openlayers/tests/Protocol/SOS.html
new file mode 100644
index 0000000..58e6607
--- /dev/null
+++ b/misc/openlayers/tests/Protocol/SOS.html
@@ -0,0 +1,57 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(4);
+ var a = new OpenLayers.Protocol.SOS({
+ url: "foo",
+ fois: ["a", "b", "c"]
+ });
+
+ t.eq(a.url, "foo", "constructor sets url");
+ t.eq(a.options.url, a.url, "constructor copies url to options.url");
+ t.eq(a.fois[0], "a", "constructor sets the fois correctly");
+ t.eq((a.format instanceof OpenLayers.Format.SOSGetFeatureOfInterest), true, "Constructor sets format correctly");
+ }
+
+ function test_read(t) {
+ t.plan(4);
+
+ var protocol = new OpenLayers.Protocol.SOS({
+ url: "http://some.url.org/sos?",
+ fois: ["foi1", "foi2"],
+ parseFeatures: function(request) {
+ t.eq(request.responseText, "foo", "parseFeatures called properly");
+ return "foo";
+ }
+ });
+
+ var _POST = OpenLayers.Request.POST;
+
+ var expected, status;
+ OpenLayers.Request.POST = function(obj) {
+ t.xml_eq(new OpenLayers.Format.XML().read(obj.data).documentElement, expected, "GetFeatureOfInterest request is correct");
+ obj.status = status;
+ obj.responseText = "foo";
+ obj.options = {};
+ t.delay_call(0.1, function() {obj.callback.call(this)});
+ return obj;
+ };
+
+ var xml = '<GetFeatureOfInterest xmlns="http://www.opengis.net/sos/1.0" version="1.0.0" service="SOS" xsi:schemaLocation="http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosAll.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FeatureOfInterestId>foi1</FeatureOfInterestId><FeatureOfInterestId>foi2</FeatureOfInterestId></GetFeatureOfInterest>';
+ expected = new OpenLayers.Format.XML().read(xml).documentElement;
+ status = 200;
+ var response = protocol.read({callback: function(response) {
+ t.eq(response.features, "foo", "user callback properly called with features");
+ t.eq(response.code, OpenLayers.Protocol.Response.SUCCESS, "success reported properly");
+ }});
+
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Protocol/Script.html b/misc/openlayers/tests/Protocol/Script.html
new file mode 100644
index 0000000..894427a
--- /dev/null
+++ b/misc/openlayers/tests/Protocol/Script.html
@@ -0,0 +1,282 @@
+<html>
+<head>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(11);
+ var a = new OpenLayers.Protocol.Script({
+ url: "foo"
+ });
+
+ // 7 tests
+ t.eq(a.url, "foo", "constructor sets url");
+ t.eq(a.options.url, a.url, "constructor copies url to options.url");
+ t.eq(a.params, {}, "constructor sets params");
+ t.eq(a.options.params, undefined, "constructor does not copy params to options.params");
+ t.ok(a.format instanceof OpenLayers.Format.GeoJSON,
+ "constructor sets a GeoJSON format by default");
+ t.eq(a.callbackKey, 'callback',
+ "callbackKey is set to 'callback' by default");
+ t.eq(a.callbackPrefix, '',
+ "callbackPrefix is set to '' by default");
+
+ var params = {hello: "world"};
+ var b = new OpenLayers.Protocol.Script({
+ url: "bar",
+ params: params,
+ callbackKey: 'cb_key',
+ callbackPrefix: 'cb_prefix'
+ });
+
+ // 6 tests
+ t.eq(b.params, params, "constructor sets params");
+ t.eq(b.options.params, b.params, "constructor copies params to options.params");
+ t.eq(b.callbackKey, 'cb_key',
+ "callbackKey is set to 'cb_key'");
+ t.eq(b.callbackPrefix, 'cb_prefix',
+ "callbackPrefix is set to 'cb_prefix'");
+ }
+
+ function test_destroy(t) {
+ t.plan(3);
+ var aborted = false;
+ var protocol = new OpenLayers.Protocol.Script({
+ url: "bar",
+ params: {hello: "world"},
+ abort: function() {
+ aborted = true;
+ }
+ });
+ protocol.destroy();
+ t.ok(aborted, "destroy aborts request");
+ t.eq(protocol.params, null, "destroy nullifies params");
+ t.eq(protocol.format, null, "destroy nullifies format");
+ }
+
+ function test_read(t) {
+ t.plan(5);
+ var protocol = new OpenLayers.Protocol.Script({
+ 'url': 'foo_url',
+ 'params': {'k': 'foo_param'}
+ });
+
+ // fake XHR request object
+ var request = {'status': 200};
+
+ // options to pass to read
+ var readOptions = {
+ 'url': 'bar_url',
+ 'params': {'k': 'bar_param'}
+ };
+
+ var response;
+
+ protocol.createRequest = function(url, params, callback) {
+ // 4 tests
+ t.ok(this == protocol,
+ 'createRequest called with correct scope');
+ t.ok(url == readOptions.url,
+ 'createRequest called with correct url');
+ t.ok(params == readOptions.params,
+ 'createRequest called with correct params');
+ t.ok(callback instanceof Function,
+ 'createRequest called with a function as callback');
+
+ return 'foo_request';
+ };
+
+ var resp = protocol.read(readOptions);
+
+ t.eq(resp.priv, 'foo_request',
+ 'response priv property set to what the createRequest method returns');
+ }
+
+ function test_read_bbox(t) {
+ t.plan(6);
+
+ var _createRequest = OpenLayers.Protocol.Script.prototype.createRequest;
+
+ var bounds = new OpenLayers.Bounds(1, 2, 3, 4);
+ var filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ value: bounds,
+ projection: new OpenLayers.Projection("foo")
+ });
+
+ // log requests
+ var log, exp;
+ OpenLayers.Protocol.Script.prototype.createRequest = function(url, params,
+ callback) {
+ log.push(params.bbox);
+ return null;
+ };
+
+ // 1) issue request with default protocol
+ log = [];
+ new OpenLayers.Protocol.Script().read({filter: filter});
+
+ t.eq(log.length, 1, "1) createRequest called once");
+ t.ok(log[0] instanceof Array, "1) bbox param is array");
+ exp = bounds.toArray();
+ t.eq(log[0], exp, "1) bbox param doesn't include SRS id by default");
+
+ // 2) issue request with default protocol
+ log = [];
+ new OpenLayers.Protocol.Script({srsInBBOX: true}).read({filter: filter});
+
+ t.eq(log.length, 1, "2) createRequest called once");
+ t.ok(log[0] instanceof Array, "2) bbox param is array");
+ exp = bounds.toArray();
+ exp.push("foo");
+ t.eq(log[0], exp, "2) bbox param includes SRS id if srsInBBOX is true");
+
+ OpenLayers.Protocol.Script.prototype.createRequest = _createRequest;
+ }
+
+ function test_createRequest(t) {
+ t.plan(6);
+ var protocol = new OpenLayers.Protocol.Script({
+ callbackKey: 'cb_key',
+ callbackPrefix: 'cb_prefix:'
+ });
+
+ var _register = OpenLayers.Protocol.Script.register;
+ OpenLayers.Protocol.Script.register = function() {
+ return 'bar';
+ };
+
+ var script = protocol.createRequest('http://bar_url/', {'k': 'bar_param'}, 'bar_callback');
+
+ t.eq(script.type, 'text/javascript',
+ 'created script has a correct type');
+
+ var params = OpenLayers.Util.getParameters(script.src);
+ t.eq(params.k, "bar_param", "custom query string param");
+ t.eq(params.cb_key, "cb_prefix:OpenLayers.Protocol.Script.registry.bar", "callback with prefix");
+
+ t.eq(script.id, 'OpenLayers_Protocol_Script_bar',
+ 'created script has a correct id');
+
+ protocol.callbackTemplate = "customCallback(${id})";
+ script = protocol.createRequest('http://bar_url/', {'k': 'bar_param2'}, 'bar_callback');
+
+ params = OpenLayers.Util.getParameters(script.src);
+ t.eq(params.k, "bar_param2", "custom query string param");
+ t.eq(params.cb_key, "cb_prefix:customCallback(bar)", "custom callback with prefix");
+
+ OpenLayers.Protocol.Script.register = _register;
+
+ }
+
+ function test_destroyRequest(t) {
+ t.plan(2);
+
+ var protocol = new OpenLayers.Protocol.Script({});
+
+ var _unregister = OpenLayers.Protocol.Script.unregister;
+ OpenLayers.Protocol.Script.unregister = function(id) {
+ t.eq(id, 'foo', "destroyRequest calls unregister with correct id");
+ };
+ var script = {
+ id: 'script_foo'
+ };
+ protocol.destroyRequest(script);
+ t.eq(protocol.pendingRequests[script.id], null,
+ "destroyRequest nullifies the pending request");
+
+ OpenLayers.Protocol.Script.unregister = _unregister;
+ }
+
+ function test_handleResponse(t) {
+ t.plan(8);
+
+ var protocol = new OpenLayers.Protocol.Script();
+
+ // 2 tests (should be called only twive)
+ protocol.destroyRequest = function(priv) {
+ t.eq(priv, 'foo_priv', 'destroyRequest called with correct argument');
+ }
+
+ // 1 test (should be called only once)
+ protocol.parseFeatures = function(data) {
+ t.eq(data, 'foo_data', 'parseFeatures called with correct argument');
+ return 'foo_features';
+ }
+
+ var response = {
+ priv: 'foo_priv',
+ data: 'foo_data'
+ }
+ var options = {
+ // 2 tests (should be called twice)
+ scope: 'foo_scope',
+ callback: function(resp) {
+ t.eq(this, 'foo_scope', 'callback called with correct scope');
+ }
+ }
+ protocol.handleResponse(response, options);
+ // 2 tests
+ t.eq(response.code, OpenLayers.Protocol.Response.SUCCESS,
+ 'response code correctly set');
+ t.eq(response.features, 'foo_features',
+ 'response features takes a correct value');
+
+ response = {
+ priv: 'foo_priv'
+ }
+ protocol.handleResponse(response, options);
+ // 1 test
+ t.eq(response.code, OpenLayers.Protocol.Response.FAILURE,
+ 'response code correctly set');
+ }
+
+ function test_parseFeatures(t) {
+ t.plan(1);
+
+ var protocol = new OpenLayers.Protocol.Script();
+
+ protocol.format = {
+ 'read': function(data) {
+ t.ok(true, 'format.read called');
+ }
+ };
+
+ var ret = protocol.parseFeatures({foo: 'bar'});
+ }
+
+ function test_abort(t) {
+ t.plan(2);
+
+ var protocol = new OpenLayers.Protocol.Script();
+
+ // 1 test
+ protocol.destroyRequest = function(priv) {
+ t.eq(priv, 'foo_priv', 'destroyRequest called with correct argument');
+ }
+
+ var response = {
+ priv: 'foo_priv'
+ }
+
+ protocol.abort(response);
+
+ var calls = [];
+ protocol.pendingRequests = {
+ 'foo': 'foo_request',
+ 'bar': 'bar_request'
+ }
+ protocol.destroyRequest = function(priv) {
+ calls.push(priv);
+ }
+ protocol.abort();
+ // 1 test
+ t.eq(calls, ['foo_request', 'bar_request'],
+ 'destroyRequest called for each pending requests');
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Protocol/WFS.html b/misc/openlayers/tests/Protocol/WFS.html
new file mode 100644
index 0000000..24e775d
--- /dev/null
+++ b/misc/openlayers/tests/Protocol/WFS.html
@@ -0,0 +1,471 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(2);
+
+ var protocol = new OpenLayers.Protocol.WFS({
+ url: "http://some.url.org",
+ featureNS: "http://namespace.org",
+ featureType: "type"
+ });
+ t.ok(protocol instanceof OpenLayers.Protocol.WFS.v1_0_0,
+ "initialize returns instance of default versioned protocol")
+
+ var protocol = new OpenLayers.Protocol.WFS({
+ url: "http://some.url.org",
+ featureNS: "http://namespace.org",
+ featureType: "type",
+ version: "1.1.0"
+ });
+ t.ok(protocol instanceof OpenLayers.Protocol.WFS.v1_1_0,
+ "initialize returns instance of custom versioned protocol")
+ }
+
+ function test_setGeometryName(t) {
+ t.plan(4);
+ var protocol = new OpenLayers.Protocol.WFS({
+ url: "http://some.url.org",
+ featureNS: "http://namespace.org",
+ featureType: "type",
+ geometryName: "geom"
+ });
+ t.eq(protocol.geometryName, "geom", "geometryName set correctly by constructor");
+ t.eq(protocol.format.geometryName, "geom", "geometryName correctly set on format by constructor");
+ // change the geometryName on the fly
+ protocol.setGeometryName("SHAPE");
+ t.eq(protocol.geometryName, "SHAPE", "geometryName changed correctly by setGeometryName");
+ t.eq(protocol.format.geometryName, "SHAPE", "geometryName correctly changed on format by setGeometryName");
+ protocol.destroy();
+ }
+
+ function test_setFeatureType(t) {
+ t.plan(4);
+ var protocol = new OpenLayers.Protocol.WFS({
+ url: "http://some.url.org",
+ featureNS: "http://namespace.org",
+ featureType: "type"
+ });
+ t.eq(protocol.featureType, "type", "featureType set correctly by constructor");
+ t.eq(protocol.format.featureType, "type", "featureType correctly set on format by constructor");
+ // change the feature type on the fly
+ protocol.setFeatureType("foo");
+ t.eq(protocol.featureType, "foo", "featureType changed correctly by setFeatureType");
+ t.eq(protocol.format.featureType, "foo", "featureType correctly changed on format by setFeatureType");
+ protocol.destroy();
+ }
+
+ function test_read(t) {
+ t.plan(7);
+
+ var protocol = new OpenLayers.Protocol.WFS({
+ url: "http://some.url.org",
+ featureNS: "http://namespace.org",
+ featureType: "type",
+ parseResponse: function(request, options) {
+ t.eq(request.responseText, "foo", "parseResponse called properly");
+ t.eq(options, {foo: "bar"}, "parseResponse receives readOptions");
+ return "foo";
+ }
+ });
+
+ var _POST = OpenLayers.Request.POST;
+
+ var expected, status;
+ OpenLayers.Request.POST = function(obj) {
+ t.xml_eq(new OpenLayers.Format.XML().read(obj.data).documentElement, expected, "GetFeature request is correct");
+ obj.status = status;
+ obj.responseText = "foo";
+ t.delay_call(0.1, function() {obj.callback.call(this)});
+ return obj;
+ };
+
+ expected = readXML("GetFeature_1");
+ status = 200;
+ var response = protocol.read({readOptions: {foo: "bar"}, callback: function(response) {
+ t.eq(response.features, "foo", "user callback properly called with features");
+ t.eq(response.code, OpenLayers.Protocol.Response.SUCCESS, "success reported properly");
+ }});
+
+ options = {
+ maxFeatures: 10,
+ featureType: 'type2',
+ srsName: 'EPSG:900913',
+ featureNS: 'htttp://alternative.namespace.org',
+ callback: function(response) {
+ t.eq(response.code, OpenLayers.Protocol.Response.FAILURE, "failure reported properly to user callback");
+ }
+ };
+ expected = readXML("GetFeature_2");
+ status = 400;
+ var response = protocol.read(options);
+
+ OpenLayers.Request.POST = _POST;
+ }
+
+ function test_parseResponse_poorconfig(t) {
+ t.plan(2);
+
+ var protocol = new OpenLayers.Protocol.WFS({
+ url: "http://some.url.org",
+ featurePrefix: "topp",
+ featureType: "tasmania_roads",
+ geometryName: null
+ });
+
+ protocol.parseResponse({responseText: document.getElementById("query_response").firstChild.nodeValue});
+ t.eq(protocol.geometryName, "geom", "geometryName configured correctly");
+ t.eq(protocol.featureNS, "http://www.openplans.org/topp", "featureNS configured correctly");
+ }
+
+ function test_exception(t) {
+ t.plan(8);
+ var url = "http://some.url.org";
+ var protocol = new OpenLayers.Protocol.WFS({
+ url: url,
+ version: "1.1.0",
+ featureNS: "http://namespace.org",
+ featureType: "type"
+ });
+ // mock up a response
+ var response = {
+ priv: {
+ status: 200,
+ responseText: '<?xml version="1.0" encoding="UTF-8"?><ows:ExceptionReport language="en" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/ows http://schemas.opengis.net/ows/1.0.0/owsExceptionReport.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ows="http://www.opengis.net/ows"><ows:Exception locator="foo" exceptionCode="InvalidParameterValue"><ows:ExceptionText>Update error: Error occurred updating features</ows:ExceptionText><ows:ExceptionText>Second exception line</ows:ExceptionText></ows:Exception></ows:ExceptionReport>'
+ }
+ };
+ var log, entry, expected;
+
+ // test GetFeature
+ log = [];
+ protocol.handleRead(OpenLayers.Util.extend({}, response), {
+ callback: function(resp) {
+ log.push(resp);
+ }
+ });
+ expected = {
+ exceptionReport: {
+ version: "1.0.0",
+ language: "en",
+ exceptions: [{
+ code: "InvalidParameterValue",
+ locator: "foo",
+ texts: [
+ "Update error: Error occurred updating features",
+ "Second exception line"
+ ]
+ }]
+ },
+ success: false
+ };
+
+ t.eq(log.length, 1, "GetFeature handled");
+ entry = log[0];
+ t.eq(entry.code, OpenLayers.Protocol.Response.FAILURE, "GetFeature failure reported");
+ t.ok(!!entry.error, "GetFeature got error");
+ t.eq(entry.error, expected, "GetFeature error matches expected");
+
+ // test a commit
+ log = [];
+ protocol.handleCommit(response, {
+ callback: function(resp) {
+ log.push(resp);
+ }
+ });
+ t.eq(log.length, 1, "commit handled");
+ entry = log[0];
+ t.eq(entry.code, OpenLayers.Protocol.Response.FAILURE, "commit failure reported");
+ t.ok(!!entry.error, "commit got error");
+ t.eq(entry.error, expected, "GetFeature error matches expected");
+
+ }
+
+ function test_commit(t){
+ t.plan(5);
+
+ var url = "http://some.url.org";
+ var protocol = new OpenLayers.Protocol.WFS({
+ url: url,
+ featureNS: "http://namespace.org",
+ featureType: "type"
+ });
+ protocol.format.read = function(data) {
+ t.eq(data, "foo", "callback called with correct argument");
+ return {
+ insertIds: new Array(3),
+ success: true
+ }
+ };
+
+ var _POST = OpenLayers.Request.POST;
+
+ var expected;
+ OpenLayers.Request.POST = function(obj) {
+ t.xml_eq(new OpenLayers.Format.XML().read(obj.data).documentElement, expected, "Transaction XML with Insert, Update and Delete created correctly");
+ t.eq(obj.headers, {foo: 'bar'}, "HTTP headers passed from commit to Request.POST");
+ obj.responseText = "foo";
+ t.delay_call(0.1, function() {obj.callback.call(this)});
+ return obj;
+ };
+
+ var featureDelete = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(42, 7), {has : "cheeseburger"});
+ featureDelete.fid = "fid.37";
+ featureDelete.state = OpenLayers.State.DELETE;
+ featureDelete.layer = {
+ projection: {
+ getCode : function(){
+ return "EPSG:4326";
+ }
+ }
+ }
+ var featureInsert = featureDelete.clone();
+ featureInsert.state = OpenLayers.State.INSERT;
+ var featureModify = featureDelete.clone();
+ featureModify.fid = "fid.37";
+ featureModify.state = OpenLayers.State.UPDATE;
+
+ options = {
+ featureNS: "http://some.namespace.org",
+ featureType: "type",
+ headers: {foo: 'bar'},
+ callback: function(response) {
+ t.eq(response.insertIds.length, 3, "correct response passed to user callback");
+ t.eq(response.code, OpenLayers.Protocol.Response.SUCCESS, "success properly reported to user callback");
+ }
+ }
+
+ expected = readXML("commit");
+ var response = protocol.commit([featureInsert, featureModify, featureDelete], options);
+
+ OpenLayers.Request.POST = _POST;
+
+ }
+
+ function test_filterDelete(t) {
+ t.plan(2)
+
+ var url = "http://some.url.org";
+ var protocol = new OpenLayers.Protocol.WFS({
+ url: url,
+ featureNS: "http://namespace.org",
+ featureType: "type"
+ });
+
+ var filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ value: new OpenLayers.Bounds(-5, -5, 5, 5)
+ });
+
+ var _POST = OpenLayers.Request.POST;
+
+ var expected = readXML("filter_delete");
+ OpenLayers.Request.POST = function(obj) {
+ t.xml_eq(new OpenLayers.Format.XML().read(obj.data).documentElement, expected, "request data correct");
+ t.delay_call(0.1, function() {obj.callback.call(this)});
+ return obj;
+ };
+
+ var response = protocol.filterDelete(filter, {
+ callback: function() {
+ t.ok("user callback function called");
+ }
+ });
+
+ OpenLayers.Request.POST = _POST;
+ }
+
+ function test_abort(t) {
+ t.plan(1);
+ var protocol = new OpenLayers.Protocol.WFS({
+ url: "http://example.com",
+ featureNS: "http://example.com#namespace",
+ featureType: "type"
+ });
+
+ var response = {
+ priv: {
+ abort: function() {
+ aborted = true;
+ }
+ }
+ };
+
+ // call abort with mocked response
+ var aborted = false;
+ protocol.abort(response);
+ t.eq(aborted, true, "abort called on response.priv");
+
+ }
+
+ function test_fromWMSLayer(t) {
+ t.plan(9);
+ var map = new OpenLayers.Map("map", {
+ projection: "CRS:84"
+ });
+ var layer = new OpenLayers.Layer.WMS("foo", "htttp://foo/ows",
+ {layers: "topp:states"}
+ );
+ map.addLayer(layer);
+ var protocol = OpenLayers.Protocol.WFS.fromWMSLayer(layer);
+ t.eq(protocol.url, "htttp://foo/ows", "url taken from wms layer");
+ t.eq(protocol.featurePrefix, "topp", "feature prefix correctly extracted");
+ t.eq(protocol.featureType, "states", "typeName correctly extracted");
+ t.eq(protocol.srsName, "CRS:84", "srsName set correctly");
+ t.eq(protocol.version, "1.1.0", "version set correctly");
+ t.eq(protocol.format.geometryName, null, "format's geometryName set to null");
+
+ layer.params["LAYERS"] = ["topp:street_centerline", "topp:states"];
+ layer.projection = new OpenLayers.Projection("EPSG:900913");
+ protocol = OpenLayers.Protocol.WFS.fromWMSLayer(layer);
+ t.eq(protocol.featurePrefix, "topp", "featurePrefix from layer param array");
+ t.eq(protocol.featureType, "street_centerline", "first layer from layer param array as featureType");
+ t.eq(protocol.srsName, "EPSG:900913", "projection from layer preferred");
+ }
+
+ function test_readFormat(t) {
+ t.plan(1);
+
+ var protocol = new OpenLayers.Protocol.WFS({
+ url: "http://some.url.org",
+ featureNS: "http://namespace.org",
+ featureType: "type",
+ formatOptions: {outputFormat: 'json'},
+ readFormat: new OpenLayers.Format.GeoJSON()
+ });
+
+ var request = {};
+ request.responseText = '{"type":"FeatureCollection","features":[{"type":"Feature","id":"V_HECTOPUNTEN.108411","geometry":{"type":"MultiPoint","coordinates":[[190659.467,349576.19]]},"geometry_name":"ORA_GEOMETRY","properties":{"WEGNUMMER":"002","HECTOMTRNG_ORG":2200,"HECTOMTRNG":"220.00","bbox":[190659.467,349576.19,190659.467,349576.19]}}]}';
+ var features = protocol.parseResponse(request);
+ t.eq(features.length, 1, "the right format is used to read the request (GeoJSON)");
+ }
+
+ function test_outputFormat(t) {
+ t.plan(2);
+
+ var protocol = new OpenLayers.Protocol.WFS({
+ version: "1.1.0",
+ url: "http://some.url.org",
+ featureNS: "http://namespace.org",
+ featureType: "type",
+ outputFormat: 'json'
+ });
+
+ t.ok(protocol.readFormat instanceof OpenLayers.Format.GeoJSON, "the correct readFormat is used for outputFormat JSON");
+
+ protocol = new OpenLayers.Protocol.WFS({
+ version: "1.1.0",
+ url: "http://some.url.org",
+ featureNS: "http://namespace.org",
+ featureType: "type",
+ outputFormat: 'GML2'
+ });
+
+ t.ok(protocol.readFormat instanceof OpenLayers.Format.GML.v2, "the correct readFormat is used for outputFormat GML2");
+ }
+
+ function test_readOptions(t) {
+ t.plan(1);
+
+ var protocol = new OpenLayers.Protocol.WFS({
+ url: "http://some.url.org",
+ version: "1.1.0",
+ featureNS: "http://namespace.org",
+ featureType: "type",
+ readOptions: {'output': 'object'},
+ parseResponse: function(request, options) {
+ t.eq(options.output, "object", "Options object correctly set to pass on to Format's read");
+ }
+ });
+
+ var _POST = OpenLayers.Request.POST;
+
+ OpenLayers.Request.POST = function(obj) {
+ obj.status = 200;
+ obj.responseText = "foo";
+ t.delay_call(0.1, function() {obj.callback.call(this)});
+ return obj;
+ };
+
+ protocol.read({
+ callback: function() {}
+ });
+
+ OpenLayers.Request.POST = _POST;
+ }
+
+ function readXML(id) {
+ var xml = document.getElementById(id).firstChild.nodeValue;
+ return new OpenLayers.Format.XML().read(xml).documentElement;
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:512px; height:256px"> </div>
+<div id="GetFeature_1"><!--
+<wfs:GetFeature xmlns:wfs="http://www.opengis.net/wfs" service="WFS" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <wfs:Query typeName="feature:type" xmlns:feature="http://namespace.org"/>
+</wfs:GetFeature>
+--></div>
+<div id="GetFeature_2"><!--
+<wfs:GetFeature xmlns:wfs="http://www.opengis.net/wfs" service="WFS" version="1.0.0" maxFeatures="10" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <wfs:Query typeName="feature:type2" xmlns:feature="htttp://alternative.namespace.org"/>
+</wfs:GetFeature>
+--></div>
+<div id="commit"><!--
+<wfs:Transaction xmlns:wfs="http://www.opengis.net/wfs" service="WFS" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <wfs:Insert>
+ <feature:type xmlns:feature="http://namespace.org">
+ <feature:the_geom>
+ <gml:Point xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326">
+ <gml:coordinates decimal="." cs="," ts=" ">42,7</gml:coordinates>
+ </gml:Point>
+ </feature:the_geom>
+ <feature:has>cheeseburger</feature:has>
+ </feature:type>
+ </wfs:Insert>
+ <wfs:Update typeName="feature:type" xmlns:feature="http://namespace.org">
+ <wfs:Property>
+ <wfs:Name>the_geom</wfs:Name>
+ <wfs:Value>
+ <gml:Point xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326">
+ <gml:coordinates decimal="." cs="," ts=" ">42,7</gml:coordinates>
+ </gml:Point>
+ </wfs:Value>
+ </wfs:Property>
+ <wfs:Property>
+ <wfs:Name>has</wfs:Name>
+ <wfs:Value>cheeseburger</wfs:Value>
+ </wfs:Property>
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:FeatureId fid="fid.37"/>
+ </ogc:Filter>
+ </wfs:Update>
+ <wfs:Delete typeName="feature:type" xmlns:feature="http://namespace.org">
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:FeatureId fid="fid.37"/>
+ </ogc:Filter>
+ </wfs:Delete>
+</wfs:Transaction>
+--></div>
+<div id="filter_delete"><!--
+<wfs:Transaction xmlns:wfs="http://www.opengis.net/wfs" service="WFS" version="1.0.0">
+ <wfs:Delete typeName="feature:type" xmlns:feature="http://namespace.org">
+ <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+ <ogc:BBOX>
+ <gml:Box xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326">
+ <gml:coordinates decimal="." cs="," ts=" ">-5,-5 5,5</gml:coordinates>
+ </gml:Box>
+ </ogc:BBOX>
+ </ogc:Filter>
+ </wfs:Delete>
+</wfs:Transaction>
+--></div>
+<div id="query_response"><!--
+<?xml version="1.0" encoding="UTF-8"?>
+<wfs:FeatureCollection xmlns:ogc="http://www.opengis.net/ogc" xmlns:wfs="http://www.opengis.net/wfs" xmlns:topp="http://www.openplans.org/topp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ows="http://www.opengis.net/ows" xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>5450000.0 500000.0</gml:lowerCorner><gml:upperCorner>5450000.0 540000.0</gml:upperCorner></gml:Envelope></gml:boundedBy><gml:featureMembers><topp:tasmania_roads gml:id="tasmania_roads.1"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>5450000.0 500000.0</gml:lowerCorner><gml:upperCorner>5450000.0 540000.0</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:geom><gml:MultiLineString srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lineStringMember><gml:LineString><gml:posList>5450000.0 500000.0 5450000.0 540000.0</gml:posList></gml:LineString></gml:lineStringMember></gml:MultiLineString></topp:geom><topp:TYPE>street</topp:TYPE></topp:tasmania_roads></gml:featureMembers></wfs:FeatureCollection>
+--></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/README.txt b/misc/openlayers/tests/README.txt
new file mode 100644
index 0000000..dc1f192
--- /dev/null
+++ b/misc/openlayers/tests/README.txt
@@ -0,0 +1,16 @@
+This directory contains unit tests for the OpenLayers library.
+
+Tests use the Test.AnotherWay library from <http://openjsan.org>. The test
+runner is 'run-tests.html' and new test files need to be added to
+'list-tests.html'.
+
+The following file naming conventions are used:
+
+ * A filename that starts with `test_` and has an `.html` extension
+ contains tests. These should contain tests for a specific class.
+
+ * A filename starting with `page_` and has an `.html` extension is a
+ supporting HTML file used in one or more tests.
+
+ * A filename starting with 'data_` is a supporting data file used in one
+ or more tests.
diff --git a/misc/openlayers/tests/Renderer.html b/misc/openlayers/tests/Renderer.html
new file mode 100644
index 0000000..4ec44f6
--- /dev/null
+++ b/misc/openlayers/tests/Renderer.html
@@ -0,0 +1,96 @@
+<html>
+<head>
+<script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_Renderer_constructor(t) {
+ t.plan(2);
+ var el = document.body;
+ el.id = "foo";
+ var r = new OpenLayers.Renderer(el.id);
+
+ t.ok(r instanceof OpenLayers.Renderer, "new OpenLayers.Renderer returns Renderer object" );
+ t.ok(r.container == el, "renderer container is correctly set");
+ }
+
+ function test_Renderer_supported(t) {
+ t.plan(1);
+
+ var r = new OpenLayers.Renderer();
+ t.eq(r.supported(), false, "supported returns false by default");
+ }
+
+ function test_Renderer_setextent(t) {
+ t.plan(2);
+
+ var r = new OpenLayers.Renderer();
+ r.map = {};
+ var extent = new OpenLayers.Bounds(1,2,3,4);
+ r.resolution = 1;
+ r.setExtent(extent, true);
+ t.ok(r.extent.equals(extent), "extent is correctly set");
+ t.eq(r.resolution, null, "resolution nullified");
+ }
+
+ function test_Renderer_setsize(t) {
+ t.plan(2);
+
+ var r = new OpenLayers.Renderer();
+ var size = new OpenLayers.Size(1,2);
+ r.resolution = 1;
+ r.setSize(size);
+ t.ok(r.size.equals(size), "size is correctly set");
+ t.eq(r.resolution, null, "resolution nullified");
+ }
+
+ function test_Renderer_getresolution(t) {
+ t.plan(2);
+
+ var r = new OpenLayers.Renderer();
+ var map = new OpenLayers.Map("map");
+ r.map = map;
+ var resolution = r.getResolution();
+ t.eq(resolution, map.getResolution(), "resolution matches the map resolution");
+ t.eq(r.resolution, resolution, "resolution is correctly set");
+ }
+
+ function test_calculateFeatureDx(t) {
+ t.plan(4);
+ var r = new OpenLayers.Renderer();
+ r.extent = new OpenLayers.Bounds(177, -2, 183, 2);
+ var worldBounds = new OpenLayers.Bounds(-180,-90,180,90);
+ r.calculateFeatureDx(new OpenLayers.Bounds(179,-1,181,1), worldBounds);
+ t.eq(r.featureDx, 0, "no offset for feature inside extent");
+ r.calculateFeatureDx(new OpenLayers.Bounds(-181,-1,-179,1), worldBounds);
+ t.eq(r.featureDx, -360, "negative offset for feature on other end of world");
+ r.calculateFeatureDx(new OpenLayers.Bounds(359,-1,361,1), worldBounds);
+ t.eq(r.featureDx, 360, "positive offset for feature that is one world away");
+ r.calculateFeatureDx(new OpenLayers.Bounds(719,-1,721,1), worldBounds);
+ t.eq(r.featureDx, 720, "correct offset for feature that is two worlds away");
+ }
+
+ function test_Renderer_destroy(t) {
+ t.plan(5);
+
+ var r = new OpenLayers.Renderer();
+ r.container = document.createElement("div");
+ r.extent = new OpenLayers.Bounds(1,2,3,4);
+ r.size = new OpenLayers.Size(1,2);
+ r.resolution = 1;
+ r.map = {};
+
+ r.destroy();
+
+ t.eq(r.container, null, "container nullified");
+ t.eq(r.extent, null, "extent nullified");
+ t.eq(r.size, null, "size nullified");
+ t.eq(r.resolution, null, "resolution nullified");
+ t.eq(r.map, null, "map nullified");
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Renderer/Canvas.html b/misc/openlayers/tests/Renderer/Canvas.html
new file mode 100644
index 0000000..f9a4c31
--- /dev/null
+++ b/misc/openlayers/tests/Renderer/Canvas.html
@@ -0,0 +1,501 @@
+<html>
+<head>
+<script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ var supported = OpenLayers.Renderer.Canvas.prototype.supported();
+
+ var map, layer;
+ function setUp() {
+ map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer.Vector(null, {
+ isBaseLayer: true,
+ renderers: ["Canvas"]
+ });
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0));
+ }
+
+ function tearDown() {
+ map.destroy();
+ map = null;
+ layer = null;
+ }
+
+ function test_Renderer_Canvas_constructor(t) {
+ if (!supported) { t.plan(0); return; }
+ t.plan(2);
+ var el = document.body;
+ el.id = "foo";
+ var r = new OpenLayers.Renderer.Canvas(el.id);
+
+ t.ok(r instanceof OpenLayers.Renderer.Canvas, "new OpenLayers.Renderer.Canvas returns Renderer.Canvas object" );
+ t.ok(r.container == el, "renderer container is correctly set");
+ r.destroy();
+ }
+
+ function test_Renderer_Canvas_setextent(t) {
+ if (!supported) { t.plan(0); return; }
+ t.plan(2);
+
+ setUp();
+
+ var r = layer.renderer;
+ var extent = new OpenLayers.Bounds(1,2,3,4);
+ r.resolution = 1;
+ r.setExtent(extent, true);
+ t.ok(r.extent.equals(extent), "extent is correctly set");
+ t.eq(r.resolution, null, "resolution nullified");
+
+ tearDown();
+ }
+
+ function test_Renderer_Canvas_setsize(t) {
+ if (!supported) { t.plan(0); return; }
+ t.plan(2);
+
+ var el = document.body;
+ el.id = "foo";
+ var r = new OpenLayers.Renderer.Canvas(el.id);
+ var size = new OpenLayers.Size(1,2);
+ r.resolution = 1;
+ r.setSize(size);
+ t.ok(r.size.equals(size), "size is correctly set");
+ t.eq(r.resolution, null, "resolution nullified");
+ r.destroy();
+ }
+
+ function test_Renderer_Canvas_getresolution(t) {
+ if (!supported) { t.plan(0); return; }
+ t.plan(2);
+
+ var el = document.body;
+ el.id = "foo";
+ var r = new OpenLayers.Renderer.Canvas(el.id);
+ var map = new OpenLayers.Map("map");
+ r.map = map;
+ var resolution = r.getResolution();
+ t.eq(resolution, map.getResolution(), "resolution matches the map resolution");
+ t.eq(r.resolution, resolution, "resolution is correctly set");
+ map.destroy();
+ }
+
+ function test_featureIdToHex(t) {
+ if (!supported) {
+ t.plan(0);
+ return;
+ }
+ t.plan(2);
+ var el = document.body;
+ el.id = "foo";
+ var renderer = new OpenLayers.Renderer.Canvas(el.id);
+
+ var cases = [{
+ id: "foo_0", hex: "#000001"
+ }, {
+ id: "foo_10", hex: "#00000b"
+ }, {
+ id: "foo_100", hex: "#000065"
+ }, {
+ id: "foo_1000000", hex: "#0f4241"
+ }, {
+ id: "foo_16777214", hex: "#ffffff"
+ }, {
+ id: "foo_16777215", hex: "#000001"
+ }];
+ t.plan(cases.length);
+
+ var c;
+ for (var i=0; i<cases.length; ++i) {
+ c = cases[i];
+ t.eq(renderer.featureIdToHex(c.id), c.hex, c.id);
+ }
+
+ renderer.destroy();
+ }
+
+
+ function test_Renderer_Canvas_destroy(t) {
+ if (!supported) { t.plan(0); return; }
+ t.plan(5);
+
+ var el = document.body;
+ el.id = "foo";
+ var r = new OpenLayers.Renderer.Canvas(el.id);
+ r.container = document.createElement("div");
+ r.extent = new OpenLayers.Bounds(1,2,3,4);
+ r.size = new OpenLayers.Size(1,2);
+ r.resolution = 1;
+ r.map = {};
+
+ r.destroy();
+
+ t.eq(r.container, null, "container nullified");
+ t.eq(r.extent, null, "extent nullified");
+ t.eq(r.size, null, "size nullified");
+ t.eq(r.resolution, null, "resolution nullified");
+ t.eq(r.map, null, "map nullified");
+ }
+
+ function test_drawFeature(t) {
+ if (!supported) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(10);
+
+ setUp();
+
+ var renderer = layer.renderer;
+ var count = 0;
+ var redraw = layer.renderer.redraw;
+ renderer.redraw = function() {
+ ++count;
+ redraw.apply(this, arguments);
+ }
+ var exp;
+
+ // a) draw a point feature
+ count = 0;
+ exp = renderer.drawFeature(
+ new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0, 0)), {}
+ );
+ t.eq(exp, true, "a) drawFeature returns true");
+ t.eq(count, 1, "a) redraw called once after drawing a point feature");
+ renderer.clear();
+
+ // b) draw one feature with no geometry
+ count = 0;
+ exp = renderer.drawFeature(
+ new OpenLayers.Feature.Vector(), {}
+ );
+ t.eq(exp, undefined, "b) drawFeature returns undefined");
+ t.eq(count, 0, "b) redraw is not called when drawing a feature with no geometry");
+ renderer.clear();
+
+ // c) draw a point feature with display "none"
+ count = 0;
+ exp = renderer.drawFeature(
+ new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1, 0)),
+ {display: "none"}
+ );
+ t.eq(exp, false, "c) drawFeature returns false");
+ t.eq(count, 1, "c) redraw is called when drawing a feature with display 'none'");
+ renderer.clear();
+
+ // d) draw a point feature outside renderer extent
+ count = 0;
+ exp = renderer.drawFeature(
+ new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(-1000, 0)), {}
+ );
+ t.eq(exp, false, "d) drawFeature returns false");
+ t.eq(count, 1, "d) redraw is called when drawing a feature outside renderer extent");
+ renderer.clear();
+
+ // e) draw a polygon feature without bounds
+ count = 0;
+ exp = renderer.drawFeature(
+ new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon()), {}
+ );
+ t.eq(exp, false, "d) drawFeature returns false");
+ t.eq(count, 1, "d) redraw is called when drawing a feature without bounds");
+ renderer.clear();
+
+ tearDown();
+ }
+
+
+ function test_pendingRedraw(t) {
+ if (!supported) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(4);
+ var layer = new OpenLayers.Layer.Vector(null, {
+ isBaseLayer: true,
+ renderers: ["Canvas"]
+ });
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ controls: [],
+ layers: [layer],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 0
+ });
+
+ var count = 0;
+ var redraw = layer.renderer.redraw;
+ layer.renderer.redraw = function() {
+ ++count;
+ redraw.apply(this, arguments);
+ }
+
+ // add one point feature and confirm redraw is called once
+ count = 0;
+ layer.addFeatures([
+ new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0, 0))
+ ]);
+ t.eq(count, 1, "redraw called once after adding one point feature");
+
+ // add one feature with no geometry and confirm redraw is not called
+ count = 0;
+ layer.addFeatures([
+ new OpenLayers.Feature.Vector()
+ ]);
+ t.eq(count, 0, "redraw is not called when adding a feature with no geometry");
+
+ // add one point feature, one feature with no geom, and one point feature and confirm redraw is called once
+ count = 0;
+ layer.addFeatures([
+ new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1, 0)),
+ new OpenLayers.Feature.Vector(),
+ new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0, 1))
+ ]);
+ t.eq(count, 1, "redraw called once after adding three features where middle one has no geometry");
+
+ // add two point features and one feature with no geom, and confirm redraw is called once
+ count = 0;
+ layer.addFeatures([
+ new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1, 0)),
+ new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0, 1)),
+ new OpenLayers.Feature.Vector()
+ ]);
+ t.eq(count, 1, "redraw called once after adding three features where last one has no geometry");
+
+ map.destroy();
+ }
+
+ function test_hitDetection(t) {
+ if (!supported) {
+ t.plan(0);
+ return;
+ }
+
+ var layer = new OpenLayers.Layer.Vector(null, {
+ isBaseLayer: true,
+ resolutions: [1],
+ styleMap: new OpenLayers.StyleMap({
+ pointRadius: 5,
+ strokeWidth: 3,
+ fillColor: "red",
+ fillOpacity: 0.5,
+ strokeColor: "blue",
+ strokeOpacity: 0.75
+ }),
+ renderers: ["Canvas"]
+ });
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ controls: [],
+ layers: [layer],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 0
+ });
+
+ layer.addFeatures([
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(-100, 0)
+ ),
+ new OpenLayers.Feature.Vector(
+ OpenLayers.Geometry.fromWKT("LINESTRING(-50 0, 50 0)")
+ ),
+ new OpenLayers.Feature.Vector(
+ OpenLayers.Geometry.fromWKT("POLYGON((100 -25, 150 -25, 150 25, 100 25, 100 -25), (120 -5, 130 -5, 130 5, 120 5, 120 -5))")
+ ),
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(80, 0), {}, {
+ graphicName: "square",
+ pointRadius: 8,
+ strokeWidth: 4,
+ fillColor: "red",
+ fillOpacity: 0.5,
+ strokeColor: "blue",
+ strokeOpacity: 0.75
+ }
+ )
+ ]);
+
+ var cases = [{
+ msg: "center of point", x: -100, y: 0, id: layer.features[0].id
+ }, {
+ msg: "edge of point", x: -106, y: 0, id: layer.features[0].id
+ }, {
+ msg: "outside point", x: -110, y: 0, id: null
+ }, {
+ msg: "center of line", x: 0, y: 0, id: layer.features[1].id
+ }, {
+ msg: "edge of line", x: 0, y: 1, id: layer.features[1].id
+ }, {
+ msg: "outside line", x: 0, y: 5, id: null
+ }, {
+ msg: "inside polygon", x: 110, y: 0, id: layer.features[2].id
+ }, {
+ msg: "edge of polygon", x: 99, y: 0, id: layer.features[2].id
+ }, {
+ msg: "inside polygon hole", x: 125, y: 0, id: null
+ }, {
+ msg: "outside polygon", x: 155, y: 0, id: null
+ }, {
+ msg: "inside symbol", x: 80, y: 0, id: layer.features[3].id
+ }, {
+ msg: "outside symbol interior, inside symbol edge", x: 90, y: 8, id: layer.features[3].id
+ }, {
+ msg: "outside symbol", x: 94, y: 0, id: null
+ }];
+
+ function px(x, y) {
+ return map.getPixelFromLonLat(
+ new OpenLayers.LonLat(x, y)
+ );
+ }
+
+ var num = cases.length;
+ t.plan(2 * num);
+ var c, feature;
+ for (var i=0; i<num; ++i) {
+ c = cases[i];
+ feature = layer.renderer.getFeatureIdFromEvent({xy: px(c.x, c.y)});
+ t.eq(feature && feature.id, c.id, c.msg);
+
+ // Extra test: hit detection on an invisible canvas should return undefined
+ layer.setVisibility(false);
+ feature = layer.renderer.getFeatureIdFromEvent({xy: px(c.x, c.y)});
+ t.eq(feature, undefined, c.msg + ' (invisible)');
+ layer.setVisibility(true);
+ }
+
+ map.destroy();
+
+ }
+
+ // see http://trac.osgeo.org/openlayers/ticket/3264
+ function test_externalGraphic_destroyFeatures(t) {
+ if (!supported) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+
+ // set up
+
+ var layer = new OpenLayers.Layer.Vector(null, {
+ isBaseLayer: true,
+ renderers: ["Canvas"]
+ });
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ controls: [],
+ layers: [layer],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 0
+ });
+
+ layer.addFeatures([
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(0, 0),
+ null,
+ {
+ externalGraphic: '../../img/marker.png',
+ graphicHeight: 20,
+ graphicWidth: 20
+ }
+ )
+ ]);
+
+ var called = false;
+ layer.renderer.canvas.drawImage = function(img, x, y, w, h) {
+ called = true;
+ };
+
+ // test
+
+ // schedule a canvas.drawImage
+ layer.renderer.redraw();
+
+ // destroy the feature before drawImage gets called
+ layer.destroyFeatures();
+
+ t.delay_call(0.1, function() {
+ t.ok(!called,
+ 'canvas.drawImage not called if feature is destroyed');
+
+ // tear down
+ map.destroy();
+ });
+ }
+
+ // see http://trac.osgeo.org/openlayers/ticket/3264
+ function test_externalGraphic_moveTo(t) {
+ if (!supported) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(2);
+
+ // set up
+
+ var layer = new OpenLayers.Layer.Vector(null, {
+ isBaseLayer: true,
+ renderers: ["Canvas"]
+ });
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ controls: [],
+ layers: [layer],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 0
+ });
+
+ var feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(0, 0),
+ null,
+ {
+ externalGraphic: '../../img/marker.png',
+ graphicHeight: 20,
+ graphicWidth: 20,
+ graphicXOffset: 0,
+ graphicYOffset: 0
+ }
+ );
+
+ layer.addFeatures([feature]);
+
+ // test
+
+ // delay_call to let the first drawImage (the one
+ // resulting from addFeatures) run
+ t.delay_call(0.1, function() {
+
+ var log = [];
+ layer.renderer.canvas.drawImage = function(img, x, y, w, h) {
+ log.push({x: x, y: y});
+ };
+
+ layer.renderer.redraw();
+ map.setCenter(new OpenLayers.LonLat(45, 0), 0);
+
+ t.delay_call(0.1, function() {
+ t.eq(log.length, 2,
+ "canvas.drawImage called twice");
+ t.ok(log[0].x == log[1].x && log[0].y == log[1].y,
+ "image drawn at the same location");
+
+ // tear down
+ map.destroy();
+ });
+ });
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Renderer/Elements.html b/misc/openlayers/tests/Renderer/Elements.html
new file mode 100644
index 0000000..53590e2
--- /dev/null
+++ b/misc/openlayers/tests/Renderer/Elements.html
@@ -0,0 +1,651 @@
+<html>
+<head>
+<script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function setUp() {
+ // Stub out functions that are meant to be overridden by
+ // subclasses.
+ OpenLayers.Renderer.Elements.prototype._createRenderRoot =
+ OpenLayers.Renderer.Elements.prototype.createRenderRoot;
+
+ var rendererRoot = document.createElement("div");
+ OpenLayers.Renderer.Elements.prototype.createRenderRoot = function() {
+ return rendererRoot;
+ };
+
+ OpenLayers.Renderer.Elements.prototype._createRoot =
+ OpenLayers.Renderer.Elements.prototype.createRoot;
+
+ OpenLayers.Renderer.Elements.prototype.createRoot = function() {
+ return document.createElement("div");
+ };
+
+ OpenLayers.Renderer.Elements.prototype._createNode =
+ OpenLayers.Renderer.Elements.prototype.createNode;
+
+ OpenLayers.Renderer.Elements.prototype.createNode = function() {
+ return document.createElement("div");
+ };
+ }
+
+ // Create a new Elements renderer based on an id and an ordering
+ // type. For these tests, both of these parameters are optional.
+ function create_renderer(id, options) {
+
+ rendererRoot = null;
+
+ if (id == null) {
+ var el = document.createElement('div');
+ document.body.appendChild(el);
+ el.id = OpenLayers.Util.createUniqueID();
+ id = el.id;
+ }
+
+ return new OpenLayers.Renderer.Elements(id, options);
+ }
+
+ // Cleanup stubs made in the function above.
+ function tearDown() {
+ OpenLayers.Renderer.Elements.prototype.createRenderRoot =
+ OpenLayers.Renderer.Elements.prototype._createRenderRoot;
+ OpenLayers.Renderer.Elements.prototype.createRoot =
+ OpenLayers.Renderer.Elements.prototype._createRoot;
+ OpenLayers.Renderer.Elements.prototype.createNode =
+ OpenLayers.Renderer.Elements.prototype._createNode;
+ }
+
+ function test_Elements_constructor(t) {
+ t.plan(6);
+
+ setUp();
+
+ var r = create_renderer();
+
+ t.ok(r instanceof OpenLayers.Renderer.Elements, "new OpenLayers.Renderer.Elements returns Elements object" );
+ t.ok(r.rendererRoot != null, "elements rendererRoot is not null");
+ t.ok(r.root != null, "elements root is not null");
+ t.ok(r.indexer == null, "indexer is null if unused.");
+
+ t.ok(r.root.parentNode == r.rendererRoot, "elements root is correctly appended to rendererRoot");
+ t.ok(r.rendererRoot.parentNode == r.container, "elements rendererRoot is correctly appended to container");
+
+ tearDown();
+ }
+
+ function test_Elements_destroy(t) {
+ t.plan(5);
+
+ var elems = {
+ 'clear': function() {
+ t.ok(true, "clear called");
+ },
+ 'rendererRoot': {},
+ 'root': {},
+ 'xmlns': {}
+ };
+
+ OpenLayers.Renderer.prototype._destroy =
+ OpenLayers.Renderer.prototype.destroy;
+
+ var args = [{}, {}, {}];
+ OpenLayers.Renderer.prototype.destroy = function() {
+ t.ok((arguments[0] == args[0]) &&
+ (arguments[1] == args[1]) &&
+ (arguments[2] == args[2]), "correct arguments passed to OpenLayers.Renderer.destroy()");
+ };
+
+ OpenLayers.Renderer.Elements.prototype.destroy.apply(elems, args);
+
+ t.ok(elems.rendererRoot == null, "rendererRoot nullified");
+ t.ok(elems.root == null, "root nullified");
+ t.ok(elems.xmlns == null, "xmlns nullified");
+
+ OpenLayers.Renderer.prototype.destroy =
+ OpenLayers.Renderer.prototype._destroy;
+
+ }
+
+ function test_Elements_clear(t) {
+ t.plan(2);
+
+ setUp();
+
+ var r = create_renderer();
+ var element = document.createElement("div");
+ r.root = element;
+
+ var node = document.createElement("div");
+ element.appendChild(node);
+
+ r.clear();
+
+ t.ok(r.vectorRoot.childNodes.length == 0, "vector root is correctly cleared");
+ t.ok(r.textRoot.childNodes.length == 0, "text root is correctly cleared");
+
+ tearDown();
+ }
+
+ function test_Elements_drawGeometry(t) {
+ t.plan(7);
+
+ setUp();
+
+ var r = create_renderer();
+
+ var element = document.createElement("div");
+ r.vectorRoot = element;
+
+ r.nodeFactory = function(id, type) {
+ var element = document.createElement("div");
+ return element;
+ };
+ var g_Node = null;
+ var b_Node = null;
+ r.drawGeometryNode = function(node, geometry, style) {
+ g_Node = node;
+ return {node: node, complete: true};
+ };
+ r.redrawBackgroundNode = function(id, geometry, style, featureId) {
+ b_Node = r.nodeFactory();
+ b_Node.id = "foo_background";
+ element.appendChild(b_Node);
+ };
+
+ r.getNodeType = function(geometry, style) {
+ return "div";
+ };
+ var geometry = {
+ id: 'foo',
+ CLASS_NAME: 'bar',
+ getBounds: function() {return {bottom: 0}}
+ };
+ var style = {'backgroundGraphic': 'foo'};
+ var featureId = 'dude';
+ r.drawGeometry(geometry, style, featureId);
+ t.ok(g_Node.parentNode == element, "node is correctly appended to root");
+ t.ok(b_Node.parentNode == element, "redrawBackgroundNode appended background node");
+ t.eq(g_Node._featureId, 'dude', "_featureId is correct");
+ t.eq(g_Node._style.backgroundGraphic, "foo", "_style is correct");
+ t.eq(g_Node._geometryClass, 'bar', "_geometryClass is correct");
+
+ var returnNode = function(id) {
+ return id == "foo_background" ? b_Node : g_Node;
+ }
+
+ var _getElement = document.getElementById;
+ document.getElementById = returnNode;
+ OpenLayers.Util.getElement = returnNode;
+
+ style = {'display':'none'};
+ r.drawGeometry(geometry, style, featureId);
+ t.ok(g_Node.parentNode != element, "node is correctly removed");
+ t.ok(b_Node.parentNode != element, "background node correctly removed")
+
+ document.getElementById = _getElement;
+
+ tearDown();
+ }
+
+ function test_Elements_drawGeometry_2(t) {
+ t.plan(8);
+
+ setUp();
+
+ var geometry = {
+ getBounds: function() {return {bottom: 0}}
+ }
+
+ var r = create_renderer();
+
+ var element = document.createElement("div");
+ r.root = element;
+
+ r.nodeFactory = function(id, type) {
+ var element = document.createElement("div");
+ return element;
+ };
+ r.setStyle = function(node, style, options, geometry) {
+ return node;
+ };
+
+ // point
+ var properDraw = false;
+ r.drawPoint = function(node, geometry) {
+ properDraw = true;
+ return {};
+ };
+ var point = OpenLayers.Util.applyDefaults({CLASS_NAME: 'OpenLayers.Geometry.Point'}, geometry);
+ style = true;
+ r.drawGeometry(point, style);
+ t.ok(properDraw, "drawGeometry called drawPoint when passed a point");
+
+ // line string
+ var properDraw = false;
+ r.drawLineString = function(g) {
+ properDraw = true;
+ return {};
+ };
+ var linestring = OpenLayers.Util.applyDefaults({id: "foo", CLASS_NAME: 'OpenLayers.Geometry.LineString'}, geometry);
+ style = true;
+ r.drawGeometry(linestring, style);
+ t.ok(properDraw, "drawGeometry called drawLineString when passed a line string");
+
+ // linear ring
+ var properDraw = false;
+ r.drawLinearRing = function(g) {
+ properDraw = true;
+ return {};
+ };
+ var linearring = OpenLayers.Util.applyDefaults({CLASS_NAME: 'OpenLayers.Geometry.LinearRing'}, geometry);
+ style = true;
+ r.drawGeometry(linearring, style);
+ t.ok(properDraw, "drawGeometry called drawLinearRing when passed a linear ring");
+
+ // polygon
+ var properDraw = false;
+ r.drawPolygon = function(g) {
+ properDraw = true;
+ return {};
+ };
+ var polygon = OpenLayers.Util.applyDefaults({CLASS_NAME: 'OpenLayers.Geometry.Polygon'}, geometry);
+ style = true;
+ r.drawGeometry(polygon, style);
+ t.ok(properDraw, "drawGeometry called drawPolygon when passed a polygon");
+
+ // rectangle
+ var properDraw = false;
+ r.drawRectangle = function(g) {
+ properDraw = true;
+ return {};
+ };
+ var rectangle = OpenLayers.Util.applyDefaults({CLASS_NAME: 'OpenLayers.Geometry.Rectangle'}, geometry);
+ style = true;
+ r.drawGeometry(rectangle, style);
+ t.ok(properDraw, "drawGeometry called drawRectangle when passed a rectangle");
+
+ // multi-point
+ var properDraw = false;
+ r.drawPoint = function(g) {
+ properDraw = true;
+ return {};
+ };
+ var multipoint = OpenLayers.Util.applyDefaults({
+ CLASS_NAME: 'OpenLayers.Geometry.MultiPoint',
+ components: [point]
+ }, geometry);
+ style = true;
+ r.drawGeometry(multipoint, style);
+ t.ok(properDraw, "drawGeometry called drawPoint when passed a multi-point");
+
+ // multi-linestring
+ var properDraw = false;
+ r.drawLineString = function(g) {
+ properDraw = true;
+ return {};
+ };
+ var multilinestring = OpenLayers.Util.applyDefaults({
+ CLASS_NAME: 'OpenLayers.Geometry.MultiLineString',
+ components: [linestring]
+ }, geometry);
+ style = true;
+ r.drawGeometry(multilinestring, style);
+ t.ok(properDraw, "drawGeometry called drawLineString when passed a multi-linestring");
+
+ // multi-polygon
+ var properDraw = false;
+ r.drawPolygon = function(g) {
+ properDraw = true;
+ return {};
+ };
+ var multipolygon = OpenLayers.Util.applyDefaults({
+ CLASS_NAME: 'OpenLayers.Geometry.MultiPolygon',
+ components: [polygon]
+ }, geometry);
+ style = true;
+ r.drawGeometry(multipolygon, style);
+ t.ok(properDraw, "drawGeometry called drawPolygon when passed a multi-polygon");
+
+ tearDown();
+ }
+
+ function test_Elements_getfeatureidfromevent(t) {
+ t.plan(2);
+
+ var node = {
+ _featureId: 'foo'
+ };
+ var event = {
+ target: node
+ };
+
+ var id = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent(event);
+ t.eq(id, 'foo', "returned id is correct when event with target is passed");
+
+ var event = {
+ srcElement: node
+ };
+
+ var id = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent(event);
+ t.eq(id, 'foo', "returned id is correct when event with srcElement is passed");
+ }
+
+ function test_Elements_erasegeometry(t) {
+ t.plan(15);
+
+ var elements = {
+ 'eraseGeometry': function(geometry) {
+ gErased.push(geometry);
+ }
+ };
+
+ var geometry = {
+ 'components': [{}, {}, {}]
+ };
+
+ //multipoint
+ geometry.CLASS_NAME = "OpenLayers.Geometry.MultiPoint";
+ gErased = [];
+ OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
+ t.ok( (gErased[0] == geometry.components[0]) &&
+ (gErased[1] == geometry.components[1]) &&
+ (gErased[2] == geometry.components[2]), "multipoint all components of geometry correctly erased.");
+
+ //multilinestring
+ geometry.CLASS_NAME = "OpenLayers.Geometry.MultiLineString";
+ gErased = [];
+ OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
+ t.ok( (gErased[0] == geometry.components[0]) &&
+ (gErased[1] == geometry.components[1]) &&
+ (gErased[2] == geometry.components[2]), "multilinestring all components of geometry correctly erased.");
+
+ //multipolygon
+ geometry.CLASS_NAME = "OpenLayers.Geometry.MultiPolygon";
+ gErased = [];
+ OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
+ t.ok( (gErased[0] == geometry.components[0]) &&
+ (gErased[1] == geometry.components[1]) &&
+ (gErased[2] == geometry.components[2]), "multipolygon all components of geometry correctly erased.");
+
+ //collection
+ geometry.CLASS_NAME = "OpenLayers.Geometry.Collection";
+ gErased = [];
+ OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
+ t.ok( (gErased[0] == geometry.components[0]) &&
+ (gErased[1] == geometry.components[1]) &&
+ (gErased[2] == geometry.components[2]), "collection all components of geometry correctly erased.");
+
+
+ // OTHERS
+ //
+ geometry.CLASS_NAME = {};
+
+ gElement = null;
+ gBackElement = null;
+
+ OpenLayers.Util._getElement = OpenLayers.Util.getElement;
+ OpenLayers.Util.getElement = function(id) {
+ var retVal = null;
+ if (id != null) {
+ var hasBack = (id.indexOf(elements.BACKGROUND_ID_SUFFIX) != -1);
+ retVal = hasBack ? gBackElement : gElement;
+ }
+ return retVal;
+ };
+
+ //element null
+ geometry.id = null;
+ OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
+ // (no tests here, just make sure it doesn't bomb)
+
+ //element.parentNode null
+ elements.BACKGROUND_ID_SUFFIX = 'BLAHBLAHBLAH';
+ geometry.id = "foo";
+ gElement = {};
+ OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
+ // (no tests here, just make sure it doesn't bomb)
+
+ //valid element.parentNode, element.geometry
+ elements.indexer = {
+ 'remove': function(elem) {
+ gIndexerRemoved = elem;
+ }
+ };
+
+ gElement = {
+ 'geometry': {
+ 'destroy': function() {
+ t.ok(true, "geometry destroyed");
+ }
+ },
+ 'parentNode': {
+ 'removeChild': function(elem) {
+ gElemRemoved = elem;
+ }
+ },
+ '_style' : {backgroundGraphic: "foo"}
+ };
+ gBackElement = {
+ 'parentNode': {
+ 'removeChild': function(elem) {
+ gBackRemoved = elem;
+ }
+ }
+ };
+
+ gElemRemoved = gBackRemoved = gIndexerRemoved = null;
+ OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
+ t.ok( (gElement.geometry == null), "all normal: element's 'geometry' property nullified");
+ t.ok( (gElemRemoved == gElement), "all normal: main element properly removed from parent node");
+ t.ok( (gBackRemoved == gBackElement), "all normal: back element properly removed from parent node");
+ t.ok( (gIndexerRemoved == gElement), "all normal: main element properly removed from the indexer");
+
+ //valid element.parentNode, no element.geometry, no bElem
+ gBackElement = null;
+ gElement.geometry = null;
+ gElemRemoved = gBackRemoved = gIndexerRemoved = null;
+ OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
+ t.ok( (gElemRemoved == gElement), "no bElem: main element properly removed from parent node");
+ t.ok( (gBackRemoved == null), "no bElem: back element not tried to remove from parent node when it doesn't exist");
+ t.ok( (gIndexerRemoved == gElement), "no bElem: main element properly removed from the indexer");
+
+ //valid element.parentNode, no element.geometry, valid bElem, no bElem.parentNode
+ gBackElement = {};
+ gElemRemoved = gBackRemoved = gIndexerRemoved = null;
+ OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
+ t.ok( (gElemRemoved == gElement), "no bElem.parentNode: main element properly removed from parent node");
+ t.ok( (gBackRemoved == null), "no bElem.parentNode: back element not tried to remove from parent node when it has no parent node");
+ t.ok( (gIndexerRemoved == gElement), "no bElem.parentNode: main element properly removed from the indexer");
+
+
+ OpenLayers.Util.getElement = OpenLayers.Util._getElement;
+ }
+
+ function test_Elements_drawAndErase(t) {
+ t.plan(20);
+
+ setUp();
+
+ var r = create_renderer(null, {zIndexing: true});
+ var element = document.createElement("div");
+ r.vectorRoot = element;
+ document.body.appendChild(element);
+
+ r.createNode = function(type, id) {
+ var element = document.createElement("div");
+ element.id = id;
+ return element;
+ };
+ r.nodeTypeCompare = function() {return true};
+ r.setStyle = function(node, style, options, geometry) {
+ return node;
+ };
+
+ var geometry = {
+ id: 'foo',
+ CLASS_NAME: 'bar',
+ getBounds: function() {return {bottom: 0}}
+ };
+ var style = {
+ graphicZIndex: 10
+ };
+ var featureId = 'foo';
+ r.drawGeometry(geometry, style, featureId);
+
+ function count(obj) {
+ var result = 0;
+ for (var i in obj) {
+ result++;
+ }
+ return result;
+ }
+
+ t.eq(element.childNodes.length, 1, "root is correctly filled");
+ t.eq(r.indexer.maxZIndex, 10, "indexer.maxZIndex is correctly filled");
+ t.eq(r.indexer.order.length, 1, "indexer.order is correctly filled");
+ t.eq(count(r.indexer.indices), 1, "indexer.indices is correctly filled");
+
+ r.eraseGeometry(geometry);
+
+ t.eq(element.childNodes.length, 0, "root is correctly cleared");
+ t.eq(r.indexer.maxZIndex, 0, "indexer.maxZIndex is correctly reset");
+ t.eq(r.indexer.order.length, 0, "indexer.order is correctly reset");
+ t.eq(count(r.indexer.indices), 0, "indexer.indices is correctly reset");
+
+ delete(style.graphicZIndex);
+ r.drawGeometry(geometry, style, featureId);
+
+ t.eq(element.childNodes.length, 1, "root is correctly filled");
+ t.eq(r.indexer.maxZIndex, 0, "indexer.maxZIndex is correctly filled");
+ t.eq(r.indexer.order.length, 1, "indexer.order is correctly filled");
+ t.eq(count(r.indexer.indices), 1, "indexer.indices is correctly filled");
+
+ r.clear();
+
+ t.eq(element.childNodes.length, 0, "root is correctly cleared");
+ t.eq(r.indexer.maxZIndex, 0, "indexer.maxZIndex is correctly reset");
+ t.eq(r.indexer.order.length, 0, "indexer.order is correctly reset");
+ t.eq(count(r.indexer.indices), 0, "indexer.indices is correctly reset");
+
+ style.graphicZIndex = 12;
+ r.drawGeometry(geometry, style, featureId);
+
+ t.eq(element.childNodes.length, 1, "root is correctly filled");
+ t.eq(r.indexer.maxZIndex, 12, "indexer.maxZIndex is correctly filled");
+ t.eq(r.indexer.order.length, 1, "indexer.order is correctly filled");
+ t.eq(count(r.indexer.indices), 1, "indexer.indices is correctly filled");
+
+ tearDown();
+ }
+
+ function test_Elements_moveRoot(t) {
+ t.plan(2);
+ setUp();
+ var r1 = create_renderer();
+ var r2 = create_renderer();
+ r1.moveRoot(r2);
+ t.xml_eq(r1.root.parentNode, r2.root.parentNode, "root moved successfully");
+ r1.moveRoot(r1);
+ t.xml_eq(r1.root.parentNode, r1.rendererRoot, "root moved back successfully");
+ tearDown();
+ }
+
+ function test_Elements_drawGeometry_3(t) {
+ t.plan(2);
+
+ setUp();
+
+ var r = create_renderer();
+
+ var element = document.createElement("div");
+ r.vectorRoot = element;
+
+ r.nodeFactory = function(id, type) {
+ return document.createElement("div");
+ };
+ var g_Node = null;
+ var b_Node = null;
+ r.drawGeometryNode = function(node, geometry, style) {
+ g_Node = node;
+ return {node: node, complete: true};
+ };
+ r.redrawBackgroundNode = function(id, geometry, style, featureId) {
+ b_Node = r.nodeFactory();
+ b_Node.id = "foo_background";
+ element.appendChild(b_Node);
+ };
+
+ r.getNodeType = function(geometry, style) {
+ return "div";
+ };
+ var geometry = {
+ id: 'foo',
+ CLASS_NAME: 'bar',
+ getBounds: function() {return {bottom: 0}}
+ };
+ var style = {'backgroundGraphic': 'foo'};
+ var featureId = 'dude';
+ r.drawGeometry(geometry, style, featureId);
+ t.ok(b_Node.parentNode == element, "redrawBackgroundNode appended background node");
+
+ var returnNode = function(id) {
+ return id == "foo_background" ? b_Node : g_Node;
+ }
+
+ var _getElement = document.getElementById;
+ document.getElementById = returnNode;
+ OpenLayers.Util.getElement = returnNode;
+
+ style = {};
+ r.drawGeometry(geometry, style, featureId);
+ t.ok(b_Node.parentNode != element, "background node correctly removed")
+
+ document.getElementById = _getElement;
+
+ tearDown();
+ }
+
+ function test_setExtent(t) {
+ t.plan(10);
+ setUp();
+ var resolution = 1;
+ var r = create_renderer();
+ r.map = {
+ getMaxExtent: function() {
+ return new OpenLayers.Bounds(-180,-90,180,90);
+ },
+ getExtent: function() {
+ return r.extent;
+ },
+ getResolution: function() {
+ return resolution;
+ },
+ baseLayer: {wrapDateLine: true}
+ }
+
+ r.setExtent(new OpenLayers.Bounds(179, -1, 182, 1), true);
+ t.eq(r.rightOfDateLine, true, "on the right side of the dateline");
+ t.eq(r.xOffset, r.map.getMaxExtent().getWidth(), "correct xOffset");
+ r.setExtent(new OpenLayers.Bounds(179.5, -1, 182.5, 1), false);
+ t.eq(r.rightOfDateLine, true, "still on the right side of the dateline");
+ t.eq(r.xOffset, r.map.getMaxExtent().getWidth(), "still correct xOffset");
+ resolution = 2;
+ r.setExtent(new OpenLayers.Bounds(178, -2, 184, 2), true);
+ t.eq(r.rightOfDateLine, true, "still on the right side of the dateline");
+ t.eq(r.xOffset, r.map.getMaxExtent().getWidth() / resolution, "xOffset adjusted for new resolution");
+ r.setExtent(new OpenLayers.Bounds(-184, -2, 178, 2), false);
+ t.eq(r.rightOfDateLine, false, "on the left side of the dateline");
+ t.eq(r.xOffset, 0, "no xOffset");
+ r.setExtent(new OpenLayers.Bounds(178, -2, 184, 2), true);
+ t.eq(r.rightOfDateLine, true, "back on the right side of the dateline");
+ t.eq(r.xOffset, r.map.getMaxExtent().getWidth() / resolution, "correct xOffset");
+
+ tearDown();
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Renderer/SVG.html b/misc/openlayers/tests/Renderer/SVG.html
new file mode 100644
index 0000000..31eb058
--- /dev/null
+++ b/misc/openlayers/tests/Renderer/SVG.html
@@ -0,0 +1,441 @@
+<html>
+<head>
+<script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var geometry = null, node = null;
+
+ function test_SVG_constructor(t) {
+ if (!OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+ var r = new OpenLayers.Renderer.SVG(document.body);
+ t.ok(r instanceof OpenLayers.Renderer.SVG, "new OpenLayers.Renderer.SVG returns SVG object" );
+ }
+
+ function test_SVG_destroy(t) {
+ if (!OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+
+ var g_Destroy = false;
+
+ OpenLayers.Renderer.Elements.prototype._destroy =
+ OpenLayers.Renderer.Elements.prototype.destroy;
+
+ OpenLayers.Renderer.prototype.destroy = function() {
+ g_Destroy = true;
+ }
+
+ var r = new OpenLayers.Renderer.SVG(document.body);
+ r.destroy();
+
+ t.eq(g_Destroy, true, "OpenLayers.Renderer.Elements.destroy() called");
+
+ OpenLayers.Renderer.prototype.destroy =
+ OpenLayers.Renderer.prototype._destroy;
+ }
+
+ function test_SVG_setextent(t) {
+ if (!OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(5);
+
+ OpenLayers.Renderer.Elements.prototype._setExtent =
+ OpenLayers.Renderer.Elements.prototype.setExtent;
+
+ var g_SetExtent = false;
+ OpenLayers.Renderer.Elements.prototype.setExtent = function() {
+ g_SetExtent = true;
+ }
+
+ var r = new OpenLayers.Renderer.SVG(document.body);
+ r.setSize(new OpenLayers.Size(4,4));
+ r.map = {
+ getResolution: function() {
+ return 0.5;
+ }
+ }
+
+ var extent = new OpenLayers.Bounds(1,2,3,4);
+ r.setExtent(extent);
+
+ t.eq(g_SetExtent, true, "Elements.setExtent() called");
+
+ t.eq(r.left, -2, "left is correct");
+ t.eq(r.top, 8, "top is correct");
+
+ t.eq(r.rendererRoot.getAttributeNS(null, "viewBox"), "0 0 4 4", "rendererRoot viewBox is correct");
+
+ // test extent changes
+ var extent = new OpenLayers.Bounds(4,3,2,1);
+ r.setExtent(extent);
+ var el = r.createNode("g");
+ el.setAttributeNS(null, "transform", "translate(-6,-6)");
+ t.eq(r.root.getAttributeNS(null, "transform"), el.getAttributeNS(null, "transform"), "rendererRoot viewBox is correct after a new setExtent");
+
+ OpenLayers.Renderer.Elements.prototype.setExtent =
+ OpenLayers.Renderer.Elements.prototype._setExtent;
+ }
+
+ function test_SVG_setsize(t) {
+ if (!OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(2);
+
+ var r = new OpenLayers.Renderer.SVG(document.body);
+
+ var size = new OpenLayers.Size(1,2);
+ r.setSize(size);
+ t.eq(r.rendererRoot.getAttributeNS(null, "width"), size.w.toString(), "width is correct");
+ t.eq(r.rendererRoot.getAttributeNS(null, "height"), size.h.toString(), "height is correct");
+ }
+
+ function test_SVG_drawpoint(t) {
+ if (!OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+
+ var r = new OpenLayers.Renderer.SVG(document.body);
+
+ var properDraw = false;
+ var g_Radius = null;
+ r.drawCircle = function(n, g, r) {
+ properDraw = true;
+ g_Radius = 1;
+ }
+ r.drawPoint();
+
+ t.ok(properDraw && g_Radius == 1, "drawPoint called drawCircle with radius set to 1");
+ }
+
+ function test_SVG_drawcircle(t) {
+ if (!OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(5);
+
+ var r = new OpenLayers.Renderer.SVG(document.body);
+ r.resolution = 0.5;
+ r.left = 0;
+ r.top = 0;
+
+ var node = document.createElement('div');
+
+ var geometry = {
+ x: 1,
+ y: 2
+ }
+
+ r.drawCircle(node, geometry, 3);
+
+ t.eq(node.getAttributeNS(null, 'cx'), '2', "cx is correct");
+ t.eq(node.getAttributeNS(null, 'cy'), '-4', "cy is correct");
+ t.eq(node.getAttributeNS(null, 'r'), '3', "r is correct");
+
+ // #1274: out of bound node fails when first added
+ var geometry = {
+ x: 10000000,
+ y: 200000000,
+ CLASS_NAME: "OpenLayers.Geometry.Point",
+ id: "foo",
+ getBounds: function() {return {bottom: 0}}
+ }
+ node.id = geometry.id;
+ r.root.appendChild(node);
+
+ var drawCircleCalled = false;
+ r.drawCircle = function() {
+ drawCircleCalled = true;
+ return OpenLayers.Renderer.SVG.prototype.drawCircle.apply(r, arguments);
+ }
+
+ r.drawGeometry(geometry, {pointRadius: 3}, "blah_4000");
+ t.eq(drawCircleCalled, true, "drawCircle called on drawGeometry for a point geometry.")
+ t.ok(node.parentNode != r.root, "circle will not be drawn when coordinates are outside the valid range");
+ }
+
+ function test_SVG_drawlinestring(t) {
+ if (!OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(2);
+
+ var r = new OpenLayers.Renderer.SVG(document.body);
+
+ var node = document.createElement('div');
+
+ var geometry = {
+ components: "foo"
+ }
+ g_GetString = false;
+ g_Components = null;
+ r.getComponentsString = function(c) {
+ g_GetString = true;
+ g_Components = c;
+ return {path: "bar", complete: true};
+ }
+
+ r.drawLineString(node, geometry);
+
+ t.ok(g_GetString && g_Components == "foo", "getComponentString is called with valid arguments");
+ t.eq(node.getAttributeNS(null, "points"), "bar", "points attribute is correct");
+ }
+
+ function test_SVG_drawlinearring(t) {
+ if (!OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(2);
+
+ var r = new OpenLayers.Renderer.SVG(document.body);
+
+ var node = document.createElement('div');
+
+ var geometry = {
+ components: "foo"
+ }
+ g_GetString = false;
+ g_Components = null;
+ r.getComponentsString = function(c) {
+ g_GetString = true;
+ g_Components = c;
+ return {path: "bar", complete: true};
+ }
+
+ r.drawLinearRing(node, geometry);
+
+ t.ok(g_GetString, "getComponentString is called with valid arguments");
+ t.eq(node.getAttributeNS(null, "points"), "bar", "points attribute is correct");
+ }
+
+ function test_SVG_drawpolygon(t) {
+ if (!OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(4);
+
+ var r = new OpenLayers.Renderer.SVG(document.body);
+
+ var node = document.createElement('div');
+
+ var linearRings = [{
+ components: ["foo"]
+ },{
+ components: ["bar"]
+ }]
+
+ var geometry = {
+ components: linearRings
+ }
+ g_GetString = false;
+ r.getShortString = function(c) {
+ g_GetString = true;
+ return c;
+ }
+
+ r.drawPolygon(node, geometry);
+
+ t.ok(g_GetString, "getShortString is called");
+ t.eq(node.getAttributeNS(null, "d"), " M foo M bar z", "d attribute is correctly set");
+ t.eq(node.getAttributeNS(null, "fill-rule"), "evenodd", "fill-rule attribute is correctly set");
+
+ r.getShortString = function(c) {
+ return false;
+ }
+ t.eq(r.drawPolygon(node, geometry), false, "drawPolygon returns false if one linearRing cannot be drawn");
+ }
+
+ function test_SVG_drawrectangle(t) {
+ if (!OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(4);
+
+ var r = new OpenLayers.Renderer.SVG(document.body);
+ r.resolution = 0.5;
+ r.left = 0;
+ r.top = 0;
+
+ var node = document.createElement('div');
+
+ var geometry = {
+ x: 1,
+ y: 2,
+ width: 3,
+ height: 4
+ }
+
+ r.drawRectangle(node, geometry);
+
+ t.eq(node.getAttributeNS(null, "x"), "2", "x attribute is correctly set");
+ t.eq(node.getAttributeNS(null, "y"), "-4", "y attribute is correctly set");
+ t.eq(node.getAttributeNS(null, "width"), "6", "width attribute is correctly set");
+ t.eq(node.getAttributeNS(null, "height"), "8", "height attribute is correctly set");
+ }
+
+ function test_SVG_getcomponentsstring(t) {
+ if (!OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+
+ var components = ['foo', 'bar'];
+
+ OpenLayers.Renderer.SVG.prototype._getShortString =
+ OpenLayers.Renderer.SVG.prototype.getShortString;
+
+ OpenLayers.Renderer.SVG.prototype.getShortString = function(p) {
+ return p;
+ };
+
+ var string = OpenLayers.Renderer.SVG.prototype.getComponentsString(components).path;
+ t.eq(string, "foo,bar", "returned string is correct");
+
+ OpenLayers.Renderer.SVG.prototype.getShortString =
+ OpenLayers.Renderer.SVG.prototype._getShortString;
+ }
+
+
+
+ function test_SVG_getshortstring(t) {
+ if (!OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+
+ var r = new OpenLayers.Renderer.SVG(document.body);
+ r.resolution = 0.5;
+ r.left = 0;
+ r.top = 0;
+
+ var point = {
+ x: 1,
+ y: 2
+ };
+
+ var string = r.getShortString(point);
+ t.eq(string, "2,-4", "returned string is correct");
+ }
+
+ function test_svg_importsymbol(t) {
+ if (!OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(2);
+
+ var r = new OpenLayers.Renderer.SVG(document.body);
+
+ r.importSymbol("square");
+
+ var polygon = document.getElementById(r.container.id + "_defs").firstChild.firstChild;
+
+ var pass = false;
+ for (var i = 0; i < polygon.points.numberOfItems; i++) {
+ var p = polygon.points.getItem(i);
+ pass = p.x === OpenLayers.Renderer.symbol.square[2*i] &&
+ p.y === OpenLayers.Renderer.symbol.square[2*i+1];
+ if (!pass) {
+ break;
+ }
+ }
+ t.ok(pass, "Square symbol rendered correctly");
+ t.ok(r.symbolMetrics["-square"], "Symbol metrics cached correctly.");
+ }
+
+ function test_svg_dashstyle(t) {
+ if (!OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(5);
+
+ var r = new OpenLayers.Renderer.SVG(document.body);
+
+ t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "dot"}, 1), "1,4", "dot dasharray created correctly");
+ t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "dash"}, 1), "4,4", "dash dasharray created correctly");
+ t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "longdash"}, 1), "8,4", "longdash dasharray created correctly");
+ t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "dashdot"}, 1), "4,4,1,4", "dashdot dasharray created correctly");
+ t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "longdashdot"}, 1), "8,4,1,4", "dashdot dasharray created correctly");
+ }
+
+ function test_svg_clipline(t) {
+ if (!OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(3);
+
+ var r = new OpenLayers.Renderer.SVG(document.body);
+ r.setSize(new OpenLayers.Size(0, 0));
+ r.map = {
+ getResolution: function() {
+ return 0.5;
+ }
+ }
+ r.setExtent(new OpenLayers.Bounds(0, 0, 0, 0));
+
+ var node = document.createElement('div');
+
+ var geometry = new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(0, -5000),
+ new OpenLayers.Geometry.Point(10000, 0),
+ new OpenLayers.Geometry.Point(0, 5000)
+ ]);
+ r.drawLineString(node, geometry);
+ t.eq(node.getAttribute("points"), "0,10000,15000,2500,15000,-2500,0,-10000", "Line with 3 points correctly clipped at inValidRange bounds");
+
+ geometry = new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(0, -5000),
+ new OpenLayers.Geometry.Point(10000, 0)
+ ]);
+ r.drawLineString(node, geometry);
+ t.eq(node.getAttribute("points"), "0,10000,15000,2500", "2-point line with 2nd point outside range correctly clipped at inValidRange bounds");
+
+ var geometry = new OpenLayers.Geometry.LineString([
+ new OpenLayers.Geometry.Point(10000, 0),
+ new OpenLayers.Geometry.Point(0, 5000)
+ ]);
+ r.drawLineString(node, geometry);
+ t.eq(node.getAttribute("points"), "15000,-2500,0,-10000", "2-point line with 1st point outside range correctly clipped at inValidRange bounds");
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Renderer/VML.html b/misc/openlayers/tests/Renderer/VML.html
new file mode 100644
index 0000000..2bdc876
--- /dev/null
+++ b/misc/openlayers/tests/Renderer/VML.html
@@ -0,0 +1,454 @@
+<html>
+<head>
+<script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var geometry = null, node = null;
+
+ function test_VML_constructor(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+ var r = new OpenLayers.Renderer.VML(document.body);
+ t.ok(r instanceof OpenLayers.Renderer.VML, "new OpenLayers.Renderer.VML returns VML object" );
+ }
+
+ function test_VML_destroy(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+
+ var g_Destroy = false;
+
+ OpenLayers.Renderer.Elements.prototype._destroy =
+ OpenLayers.Renderer.Elements.prototype.destroy;
+
+ OpenLayers.Renderer.prototype.destroy = function() {
+ g_Destroy = true;
+ }
+
+ var r = new OpenLayers.Renderer.VML(document.body);
+ r.destroy();
+
+ t.eq(g_Destroy, true, "OpenLayers.Renderer.Elements.destroy() called");
+
+ OpenLayers.Renderer.prototype.destroy =
+ OpenLayers.Renderer.prototype._destroy;
+ }
+
+ function test_VML_setextent(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(4);
+
+ OpenLayers.Renderer.Elements.prototype._setExtent =
+ OpenLayers.Renderer.Elements.prototype.setExtent;
+
+ var g_SetExtent = false;
+ OpenLayers.Renderer.Elements.prototype.setExtent = function() {
+ g_SetExtent = true;
+ }
+
+ var r = new OpenLayers.Renderer.VML(document.body);
+ r.setSize(new OpenLayers.Size(4,4));
+ r.map = {
+ getResolution: function() {
+ return 0.5;
+ }
+ }
+
+ var extent = new OpenLayers.Bounds(1,2,3,4);
+ r.setExtent(extent);
+
+ t.eq(g_SetExtent, true, "Elements.setExtent() called");
+
+ t.ok(r.root.coordorigin == "0,0", "coordorigin is correct");
+ t.ok(r.root.coordsize == "4,4", "coordsize is correct");
+ t.eq(r.offset, {x:2, y:4}, "offset is correct");
+
+ OpenLayers.Renderer.Elements.prototype.setExtent =
+ OpenLayers.Renderer.Elements.prototype._setExtent;
+ }
+
+ function test_VML_setsize(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(4);
+
+ var r = new OpenLayers.Renderer.VML(document.body);
+
+ var size = new OpenLayers.Size(1,2);
+ r.setSize(size);
+ t.eq(r.rendererRoot.style.width, "1px", "rendererRoot width is correct");
+ t.eq(r.rendererRoot.style.height, "2px", "rendererRoot height is correct");
+
+ t.eq(r.root.style.width, "1px", "root width is correct");
+ t.eq(r.root.style.height, "2px", "root height is correct");
+ }
+
+ function test_VML_drawText(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(2);
+
+ var r = new OpenLayers.Renderer.VML(document.body);
+ r.offset = {x: 0, y: 0};
+
+ r.LABEL_ID_SUFFIX = "";
+ r.getResolution = function() {
+ return 1;
+ };
+
+ var style = {
+ label: "myText",
+ fontColor: "blue"
+ }
+
+ r.drawText("feature1", style, new OpenLayers.Geometry.Point(1,1));
+
+ var textbox = document.getElementById("feature1_textbox");
+
+ t.eq(textbox.innerText, style.label, "label set correctly");
+ t.eq(textbox.style.color, style.fontColor, "font color of label set correctly");
+ }
+
+ function test_VML_drawpoint(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+
+ var r = new OpenLayers.Renderer.VML(document.body);
+
+ var properDraw = false;
+ var g_Radius = null;
+ r.drawCircle = function(n, g, r) {
+ properDraw = true;
+ g_Radius = 1;
+ }
+ r.drawPoint();
+
+ t.ok(properDraw && g_Radius == 1, "drawPoint called drawCircle with radius set to 1");
+ }
+
+ function test_VML_drawcircle(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(4);
+
+ var r = new OpenLayers.Renderer.VML(document.body);
+ r.offset = {x: 0, y: 0};
+ r.resolution = 0.5;
+
+ var node = document.createElement('div');
+
+ var geometry = {
+ x: 1,
+ y: 2
+ }
+
+ var radius = 3;
+ r.drawCircle(node, geometry, radius);
+
+ t.eq(node.style.left, '-1px', "left is correct");
+ t.eq(node.style.top, '1px', "top is correct");
+ t.eq(node.style.width, (2 * radius) + "px", "width is correct");
+ t.eq(node.style.height, (2 * radius) + "px", "height is correct");
+ }
+
+ function test_VML_drawGraphic(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(6);
+
+ var r = new OpenLayers.Renderer.VML(document.body);
+ r.offset = {x: 0, y: 0};
+ r.resolution = 1;
+
+ var node = document.createElement('div');
+ node.id = "test"
+ node._geometryClass = "OpenLayers.Geometry.Point";
+
+ var geometry = {
+ x: 1,
+ y: 2
+ }
+
+ var style = {
+ externalGraphic: "foo.png",
+ graphicWidth: 7,
+ graphicHeight: 10
+ }
+
+ r.drawGeometryNode(node, geometry, style);
+
+ t.eq(node.childNodes[0].id, "test_fill", "fill child node correctly created");
+ t.eq(node.style.left, "-2px", "x of insertion point with calculated xOffset correct");
+ t.eq(node.style.top, "-3px", "y of insertion point with calculated yOffset correct");
+
+ style.rotation = 90;
+
+ r.drawGeometryNode(node, geometry, style);
+
+ t.eq(node.childNodes[1].id, "test_image", "image child node correctly created");
+ t.eq(node.style.left, "-3px", "x of insertion point of rotated image correct");
+ t.eq(node.style.top, "-4px", "y of insertion point of rotated image correct");
+ }
+
+ function test_VML_drawlinestring(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+
+ var r = new OpenLayers.Renderer.VML(document.body);
+
+ g_DrawLine = false;
+ r.drawLine = function(c) {
+ g_DrawLine = true;
+ }
+
+ r.drawLineString(node, geometry);
+
+ t.ok(g_DrawLine, "drawLine is called");
+ }
+
+ function test_VML_drawlinearring(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+
+ var r = new OpenLayers.Renderer.VML(document.body);
+
+ g_DrawLine = false;
+ r.drawLine = function(c) {
+ g_DrawLine = true;
+ }
+
+ r.drawLinearRing(node, geometry);
+
+ t.ok(g_DrawLine, "drawLine is called");
+ }
+
+ function test_VML_drawline(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(8);
+
+ var r = new OpenLayers.Renderer.VML(document.body);
+ r.offset = {x: 0, y: 0};
+ r.resolution = 0.5;
+
+ var node = document.createElement('div');
+
+ var geometry = {
+ components: [{
+ x: 1,
+ y: 2
+ },{
+ x: 3,
+ y: 4
+ }],
+ getBounds: function() {
+ return new OpenLayers.Bounds(5,6,7,8);
+ }
+ };
+
+ r.drawLine(node, geometry, true);
+ t.ok(node.path.indexOf("x") != -1, "path attribute is correct when passed closeLine = true");
+
+
+ r.drawLine(node, geometry, false);
+ t.eq(node.path, "m 2,4 l 6,8 l e", "path attribute is correct");
+ t.eq(node.style.left, "10px", "node.style.left is correct");
+ t.eq(node.style.top, "16px", "node.style.top is correct");
+ t.eq(node.style.width, "4px", "node.style.width is correct");
+ t.eq(node.style.height, "4px", "node.style.height is correct");
+ t.eq(node.coordorigin, "10 16", "node.coordorigin is correct");
+ t.eq(node.coordsize, "4 4", "node.coordsize is correct");
+ }
+
+ function test_VML_drawpolygon(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(3);
+
+ var r = new OpenLayers.Renderer.VML(document.body);
+ r.offset = {x: 0, y: 0};
+ r.resolution = 0.5;
+
+ g_SetNodeDimension = false;
+ r.setNodeDimension = function(){
+ g_SetNodeDimension = true;
+ };
+
+ var node = document.createElement('div');
+
+ var geometry = OpenLayers.Geometry.fromWKT(
+ "POLYGON((1 2, 3 4), (5 6, 7 8))"
+ );
+ r.drawPolygon(node, geometry, true);
+ t.ok(g_SetNodeDimension, "setNodeDimension is called");
+ t.eq(node.path, "m 2,4 l 6,8 2,4 x m 10,12 l 14,16 10,12 e", "path attribute is correct - inner ring has no area and is not closed");
+
+ geometry.components[1].addComponent(new OpenLayers.Geometry.Point(8, 7));
+ r.drawPolygon(node, geometry, true);
+ t.eq(node.path, "m 2,4 l 6,8 2,4 x m 10,12 l 14,16 16,14 10,12 x e", "path attribute is correct - inner ring has an area and is closed");
+ }
+
+ function test_VML_drawrectangle(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(4);
+
+ var r = new OpenLayers.Renderer.VML(document.body);
+ r.offset = {x: 0, y: 0};
+ r.resolution = 0.5;
+
+ var node = document.createElement('div');
+
+ var geometry = {
+ x: 1,
+ y: 2,
+ width: 3,
+ height: 4
+ }
+
+ r.drawRectangle(node, geometry);
+
+ t.eq(node.style.left, "2px", "node.style.left is correct");
+ t.eq(node.style.top, "4px", "node.style.top is correct");
+ t.eq(node.style.width, "6px", "node.style.width is correct");
+ t.eq(node.style.height, "8px", "node.style.height is correct");
+ }
+
+ function test_vml_getnodetype(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+
+ var r = new OpenLayers.Renderer.VML(document.body);
+
+ var g = {CLASS_NAME: "OpenLayers.Geometry.Point"}
+ var s = {graphicName: "square"};
+
+ t.eq(r.getNodeType(g, s), "olv:shape", "Correct node type for well known symbols");
+ }
+
+ function test_vml_importsymbol(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(4);
+
+ OpenLayers.Renderer.symbol.rect1 = [0,0, 10,0, 10,4, 0,4, 0,0];
+ OpenLayers.Renderer.symbol.rect2 = [0,0, 4,0, 4,10, 0,10, 0,0];
+
+ var r = new OpenLayers.Renderer.VML(document.body);
+
+ var cache = r.importSymbol("square");
+
+ t.eq(cache.path, "m 0 0 l 0 1 1 1 1 0 0 0 x e", "Square symbol rendered correctly");
+ t.ok(r.symbolCache["-square"], "Symbol has been cached correctly.");
+
+ cache = r.importSymbol("rect1");
+ t.eq(cache.bottom, -3, "coordorigin bottom of landscape symbol set to -3 to move topmost part to the bottom (we are flipping y!)");
+
+ cache = r.importSymbol("rect2");
+ t.eq(cache.left, -3, "coordorigin left of portrait symbol set to -3 to move leftmost part to the right");
+
+ delete OpenLayers.Renderer.symbol.rect1;
+ delete OpenLayers.Renderer.symbol.rect2;
+
+ }
+
+ function test_vml_dashstyle(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(5);
+
+ var r = new OpenLayers.Renderer.VML(document.body);
+
+ t.eq(r.dashStyle({strokeDashstyle: "1 4"}), "dot", "dot pattern recognized correctly.");
+ t.eq(r.dashStyle({strokeDashstyle: "4 4"}), "dash", "dash pattern recognized correctly.");
+ t.eq(r.dashStyle({strokeDashstyle: "8 4"}), "longdash", "longdash pattern recognized correctly.");
+ t.eq(r.dashStyle({strokeDashstyle: "4 4 1 4"}), "dashdot", "dashdot pattern recognized correctly.");
+ t.eq(r.dashStyle({strokeDashstyle: "8 4 1 4"}), "longdashdot", "longdashdot pattern recognized correctly.");
+ }
+
+ function test_vml_moveRoot(t) {
+ if (!OpenLayers.Renderer.VML.prototype.supported() || OpenLayers.Renderer.SVG.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+ t.plan(2);
+
+ var map = new OpenLayers.Map("map");
+ var l1 = new OpenLayers.Layer.Vector("vector");
+ map.addLayer(l1);
+ var l2 = new OpenLayers.Layer.Vector.RootContainer("rootcontainer", {layers: [l1]});
+
+ var clear = l1.renderer.clear;
+ l1.renderer.clear = function() {
+ // this should be called twice, once when l2 is added to the map,
+ // and once when removed from the map.
+ t.ok(true, "Clearing original layer");
+ };
+ map.addLayer(l2);
+ map.removeLayer(l2);
+ l1.renderer.clear = clear;
+
+ map.removeLayer(l1);
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Request.html b/misc/openlayers/tests/Request.html
new file mode 100644
index 0000000..cd679c6
--- /dev/null
+++ b/misc/openlayers/tests/Request.html
@@ -0,0 +1,524 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+ function setup() {
+ window._xhr = OpenLayers.Request.XMLHttpRequest;
+ var anon = new Function();
+ OpenLayers.Request.XMLHttpRequest = function() {};
+ OpenLayers.Request.XMLHttpRequest.prototype = {
+ open: anon,
+ setRequestHeader: anon,
+ send: anon
+ };
+ OpenLayers.Request.XMLHttpRequest.DONE = 4;
+ }
+ function teardown() {
+ OpenLayers.Request.XMLHttpRequest = window._xhr;
+ }
+
+ function test_defaultHeaders(t) {
+ setup();
+ t.plan(2);
+ var config = {
+ headers: {
+ x: 'y'
+ }
+ };
+ OpenLayers.Request.DEFAULT_CONFIG.headers = {
+ foo: 'bar'
+ };
+ var proto = OpenLayers.Request.XMLHttpRequest.prototype;
+ var issue = OpenLayers.Function.bind(OpenLayers.Request.issue,
+ OpenLayers.Request);
+
+ var headers = {};
+ var _setRequestHeader = proto.setRequestHeader;
+ proto.setRequestHeader = function(key, value) {
+ headers[key] = value;
+ };
+ request = issue(config);
+ t.eq(headers.foo, 'bar', "Header from DEFAULT_CONFIG set correctly");
+ t.eq(headers.x, 'y', "Header from config set correctly");
+ proto.setRequestHeader = _setRequestHeader;
+ OpenLayers.Request.DEFAULT_CONFIG.headers = {};
+ teardown();
+ }
+
+ function test_issue(t) {
+ setup();
+
+ t.plan(25);
+ var request, config;
+ var proto = OpenLayers.Request.XMLHttpRequest.prototype;
+ var issue = OpenLayers.Function.bind(OpenLayers.Request.issue,
+ OpenLayers.Request);
+
+ // test that issue returns a new XMLHttpRequest - 1 test
+ request = issue();
+ t.ok(request instanceof OpenLayers.Request.XMLHttpRequest,
+ "returns an XMLHttpRequest instance");
+
+ // test that issue calls xhr.open with correct args from config - 5 tests
+ var _open = proto.open;
+ config = {
+ method: "foo",
+ url: "http://nowhere",
+ async: "bar",
+ user: "uncle",
+ password: "sam"
+ };
+ proto.open = function(method, url, async, user, password) {
+ t.eq(method, config.method, "open called with correct method");
+ t.eq(url, config.url, "open called with correct url");
+ t.eq(async, config.async, "open called with correct async");
+ t.eq(user, config.user, "open called with correct user");
+ t.eq(password, config.password, "open called with correct password");
+ };
+ request = issue(config);
+
+ // test that params are serialized as query string - 1 test
+ config = {
+ method: "GET",
+ url: "http://example.com/",
+ params: {"foo": "bar"}
+ };
+ proto.open = function(method, url, async, user, password) {
+ t.eq(url, config.url + "?foo=bar", "params serialized as query string");
+ };
+ request = issue(config);
+
+ // test that empty params object doesn't produce query string - 1 test
+ config = {
+ method: "GET",
+ url: "http://example.com/",
+ params: {}
+ };
+ proto.open = function(method, url, async, user, password) {
+ t.eq(url, config.url, "empty params doesn't produce query string");
+ }
+ request = issue(config);
+
+ // test that query string doesn't get two ? separators
+ config = {
+ method: "GET",
+ url: "http://example.com/?existing=query",
+ params: {"foo": "bar"}
+ };
+ proto.open = function(method, url, async, user, password) {
+ t.eq(url, config.url + "&foo=bar", "existing query string gets extended with &");
+ }
+ request = issue(config);
+
+ // test that query string doesn't get ? followed by &
+ config = {
+ method: "GET",
+ url: "http://example.com/service?",
+ params: {"foo": "bar"}
+ };
+ proto.open = function(method, url, async, user, password) {
+ t.eq(url, config.url + "foo=bar", "existing query string ending with ? gets extended without &");
+ }
+ request = issue(config);
+
+ // reset open method
+ proto.open = _open;
+
+ // test that headers are correctly set - 6 tests
+ var _setRequestHeader = proto.setRequestHeader;
+ config = {
+ headers: {
+ foo: "bar",
+ chicken: "soup",
+ // This checks whether the autoadded 'X-Requested-With'-header
+ // can be overridden, even though the given key here is spelled
+ // in lowercase.
+ 'x-requested-with': 'humpty'
+ }
+ };
+ // we also track how often setRequestHeader is being called, it should
+ // be called once for every header, even with the above defined
+ // custom 'x-requested-with' header which we usually autoadd.
+ // If the numbers match, we make sure to not send duplicate headers like
+ // x-requested-with: humpty AND
+ // X-Requested-With: XMLHttpRequest
+ var actualSetHeaderCnt = 0;
+ var expectedSetHeaderCnt = 3; // and not four!
+ proto.setRequestHeader = function(key, value) {
+ actualSetHeaderCnt++;
+ t.ok(key in config.headers, "setRequestHeader called with key: " + key);
+ t.eq(value, config.headers[key], "setRequestHeader called with correct value: " + value);
+ };
+ request = issue(config);
+
+ t.eq(actualSetHeaderCnt, expectedSetHeaderCnt, 'A custom "x-requested-with" header overrides the default "X-Requested-With" header.');
+
+ proto.setRequestHeader = _setRequestHeader;
+
+ // test that callback is called (no scope) - 1 test
+ var unbound = function(request) {
+ t.ok(request instanceof OpenLayers.Request.XMLHttpRequest,
+ "unbound callback called with xhr instance");
+ }
+ config = {
+ callback: unbound
+ };
+ request = issue(config);
+ request.readyState = OpenLayers.Request.XMLHttpRequest.DONE;
+ request.onreadystatechange();
+
+ // test that callback is called (with scope) - 2 tests
+ var obj = {};
+ var bound = function(request) {
+ t.ok(this === obj, "bound callback has correct scope");
+ t.ok(request instanceof OpenLayers.Request.XMLHttpRequest,
+ "bound callback called with xhr instance");
+ }
+ config = {
+ callback: bound,
+ scope: obj
+ };
+ request = issue(config);
+ request.readyState = 4;
+ request.onreadystatechange();
+
+ // test that optional success callback is only called with 200s and
+ // failure is only called with non-200s
+ var _send = proto.send;
+ proto.send = function() {};
+
+ config = {
+ success: function(req) {
+ t.ok(!req.status || (req.status >= 200 && req.status < 300),
+ "success callback called with " + req.status + " status");
+ },
+ failure: function(req) {
+ t.ok(req.status && (req.status < 200 || req.status >= 300),
+ "failure callback called with " + req.status + " status");
+ }
+ };
+ request = issue(config);
+ request.readyState = 4;
+
+ // mock up status 200 (1 test)
+ request.status = 200;
+ request.onreadystatechange();
+
+ // mock up status 299 (1 test)
+ request.status = 299;
+ request.onreadystatechange();
+
+ // mock up status 100 (1 test)
+ request.status = 100;
+ request.onreadystatechange();
+
+ // mock up status 300 (1 test)
+ request.status = 300;
+ request.onreadystatechange();
+
+ // mock up a status null (1 test)
+ request.status = null;
+ request.onreadystatechange();
+
+ proto.send = _send;
+
+ teardown();
+ }
+
+ function test_delayed_send(t) {
+ t.plan(1);
+ var proto = OpenLayers.Request.XMLHttpRequest.prototype;
+ var _send = proto.send;
+
+ // test that send is called with data - 1 test
+ var config = {
+ method: "PUT",
+ data: "bling"
+ };
+ var got = {};
+ proto.send = function(data) {
+ got.data = data;
+ }
+ OpenLayers.Request.issue(config);
+
+ t.delay_call(1, function() {
+ t.eq(got.data, config.data, "proper data sent");
+ proto.send = _send;
+ });
+
+ }
+
+ function test_GET(t) {
+ t.plan(1);
+ var _issue = OpenLayers.Request.issue;
+ OpenLayers.Request.issue = function(config) {
+ t.eq(config.method, "GET", "calls issue with correct method");
+ }
+ OpenLayers.Request.GET();
+ OpenLayers.Request.issue = _issue;
+ }
+ function test_POST(t) {
+ t.plan(1);
+ var _issue = OpenLayers.Request.issue;
+ OpenLayers.Request.issue = function(config) {
+ t.eq(config.method, "POST", "calls issue with correct method");
+ }
+ OpenLayers.Request.POST();
+ OpenLayers.Request.issue = _issue;
+ }
+ function test_PUT(t) {
+ t.plan(1);
+ var _issue = OpenLayers.Request.issue;
+ OpenLayers.Request.issue = function(config) {
+ t.eq(config.method, "PUT", "calls issue with correct method");
+ }
+ OpenLayers.Request.PUT();
+ OpenLayers.Request.issue = _issue;
+ }
+ function test_DELETE(t) {
+ t.plan(1);
+ var _issue = OpenLayers.Request.issue;
+ OpenLayers.Request.issue = function(config) {
+ t.eq(config.method, "DELETE", "calls issue with correct method");
+ }
+ OpenLayers.Request.DELETE();
+ OpenLayers.Request.issue = _issue;
+ }
+ function test_HEAD(t) {
+ t.plan(1);
+ var _issue = OpenLayers.Request.issue;
+ OpenLayers.Request.issue = function(config) {
+ t.eq(config.method, "HEAD", "calls issue with correct method");
+ }
+ OpenLayers.Request.HEAD();
+ OpenLayers.Request.issue = _issue;
+ }
+ function test_OPTIONS(t) {
+ t.plan(1);
+ var _issue = OpenLayers.Request.issue;
+ OpenLayers.Request.issue = function(config) {
+ t.eq(config.method, "OPTIONS", "calls issue with correct method");
+ }
+ OpenLayers.Request.OPTIONS();
+ OpenLayers.Request.issue = _issue;
+ }
+
+ function test_events_success(t) {
+
+ t.plan(5);
+
+ var events = [];
+ function listener(event) {
+ events.push(event);
+ }
+
+ // set up event listeners
+ OpenLayers.Request.events.on({
+ complete: listener,
+ success: listener,
+ failure: listener
+ });
+
+ // issue a request that succeeds
+ OpenLayers.Request.GET({
+ url: ".", params: {bar: "baz"}, async: false
+ });
+ t.eq(events.length, 2, "two events logged");
+ t.eq(events[0].type, "complete", "first event is complete");
+ t.eq(events[1].type, "success", "second event is success");
+ t.ok(events[1].config, "success listener sent config");
+ t.eq(events[1].requestUrl, ".?bar=baz", "success listener sent config.url");
+
+ // remove event listeners
+ OpenLayers.Request.events.un({
+ complete: listener,
+ success: listener,
+ failure: listener
+ });
+
+ }
+
+ function test_events_failure(t) {
+
+ t.plan(5);
+
+ var events = [];
+ function listener(event) {
+ events.push(event);
+ }
+
+ // set up event listeners
+ OpenLayers.Request.events.on({
+ complete: listener,
+ success: listener,
+ failure: listener
+ });
+
+ // issue a request that succeeds
+ OpenLayers.Request.GET({
+ url: "foo", params: {bar: "baz"}, async: false
+ });
+ t.eq(events.length, 2, "two events logged");
+ t.eq(events[0].type, "complete", "first event is complete");
+ t.eq(events[1].type, "failure", "second event is failure");
+ t.ok(events[1].config, "failure listener sent config");
+ t.eq(events[1].requestUrl, "foo?bar=baz", "failure listener sent requestUrl");
+
+ // remove event listeners
+ OpenLayers.Request.events.un({
+ complete: listener,
+ success: listener,
+ failure: listener
+ });
+
+ }
+
+ function test_ProxyHost(t) {
+ t.plan(5);
+
+ /*
+ * Setup
+ */
+
+ setup();
+
+ var expectedURL;
+
+ var _ProxyHost = OpenLayers.ProxyHost;
+
+ var proto = OpenLayers.Request.XMLHttpRequest.prototype;
+ var _open = proto.open;
+ var log = [];
+ var port;
+ proto.open = function(method, url, async, user, password) {
+ log.push(url);
+ };
+
+ /*
+ * Test
+ */
+
+ // 2 tests
+ log = [];
+ OpenLayers.ProxyHost = "http://fooproxy/?url=";
+ expectedURL = "http://fooproxy/?url=http%3A%2F%2Fbar%3Fk1%3Dv1%26k2%3Dv2";
+ OpenLayers.Request.GET({url: "http://bar?k1=v1&k2=v2"});
+ t.eq(log.length, 1, "[1] XHR.open called once");
+ t.eq(log[0], expectedURL, "[1] the URL used for XHR is correct (" + log[0] + ")");
+
+ // 1 test
+ log = [];
+ OpenLayers.ProxyHost = "http://fooproxy/?url=";
+ port = window.location.port ? ':'+window.location.port : '';
+ expectedURL = window.location.protocol+"//"+window.location.hostname+port+"/service";
+ OpenLayers.Request.GET({url: expectedURL});
+ t.eq(log[0], expectedURL, "[2] proxy is not used when requesting the same server");
+
+ // 2 tests
+ log = [];
+ OpenLayers.ProxyHost = function(url) {
+ var p = OpenLayers.Util.getParameters(url);
+ var p = OpenLayers.Util.getParameterString(p);
+ return "http://barproxy/?" + p;
+ };
+ expectedURL = "http://barproxy/?k1=v1&k2=v2";
+ OpenLayers.Request.GET({url: "http://bar?k1=v1&k2=v2"});
+ t.eq(log.length, 1, "[3] XHR.open called once");
+ t.eq(log[0], expectedURL, "[3] the URL used for XHR is correct (" + log[0] + ")");
+
+ /*
+ * Teardown
+ */
+
+ OpenLayers.Request.XMLHttpRequest.prototype.open = _open;
+ OpenLayers.ProxyHost = _ProxyHost;
+ teardown();
+ }
+
+ function test_abort(t) {
+
+ t.plan(0);
+
+ var sendCalled;
+
+ // set up
+
+ var _open = OpenLayers.Request.XMLHttpRequest.prototype.open;
+ OpenLayers.Request.XMLHttpRequest.prototype.open = function() {
+ this.readyState = OpenLayers.Request.XMLHttpRequest.OPENED;
+ };
+
+ var _setRequestHeader = OpenLayers.Request.XMLHttpRequest.prototype.setRequestHeader;
+ OpenLayers.Request.XMLHttpRequest.prototype.setRequestHeader = function() {};
+
+ var _send = OpenLayers.Request.XMLHttpRequest.prototype.send;
+ OpenLayers.Request.XMLHttpRequest.prototype.send = function() {
+ sendCalled = true;
+ };
+
+ // test
+
+ sendCalled = false;
+ OpenLayers.Request.issue().abort();
+
+ t.delay_call(0.5, function() {
+ if (sendCalled) {
+ t.fail("Send should not be called because request is aborted");
+ }
+
+ // tear down
+ OpenLayers.Request.XMLHttpRequest.prototype.open = _open;
+ OpenLayers.Request.XMLHttpRequest.prototype.setRequestHeader = _setRequestHeader;
+ OpenLayers.Request.XMLHttpRequest.prototype.send = _send;
+ });
+ }
+
+ function test_abort2(t) {
+ t.plan(0);
+ var fail = false;
+ OpenLayers.Request.XMLHttpRequest.onsend = function(args) {
+ fail = true;
+ }
+ t.delay_call(0.5, function() {
+ if (fail === true) {
+ t.fail("Send should not be called because request is aborted");
+ }
+ OpenLayers.Request.XMLHttpRequest.onsend = null;
+ });
+ var req = OpenLayers.Request.GET();
+ req.abort();
+ }
+
+ function test_XRequestedWithHeaderAutoadded(t) {
+ t.plan( 2 );
+
+ var headerSet = false;
+ var headerGot = '';
+ var headerExpected = 'XMLHttpRequest';
+
+ // save to be able to restore later
+ var _setRequestHeader = OpenLayers.Request.XMLHttpRequest.prototype.setRequestHeader;
+
+ OpenLayers.Request.XMLHttpRequest.prototype.setRequestHeader = function(field, value) {
+ if (field === 'X-Requested-With') {
+ headerSet = true;
+ headerGot = value;
+ }
+ };
+
+ var req = OpenLayers.Request.issue({
+ url: location.href,
+ async: false
+ });
+
+ t.ok( headerSet, 'We call the method "setRequestHeader" to set a "X-Requested-With"-header' );
+ t.eq( headerGot, headerExpected, 'The "X-Requested-With"-header is set to "' + headerExpected + '" as expected.' );
+
+ // restore old setRequestHeader
+ OpenLayers.Request.XMLHttpRequest.prototype.setRequestHeader = _setRequestHeader;
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Request/XMLHttpRequest.html b/misc/openlayers/tests/Request/XMLHttpRequest.html
new file mode 100644
index 0000000..fa62807
--- /dev/null
+++ b/misc/openlayers/tests/Request/XMLHttpRequest.html
@@ -0,0 +1,59 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ function test_constructor(t) {
+ t.plan(1);
+ t.ok(new OpenLayers.Request.XMLHttpRequest(),
+ "constructor didn't fail and we trust the code is well tested in OpenLayers.Request methods");
+ }
+ function test_readyState(t) {
+ // Verify compliance of the standard (a part) See: http://www.w3.org/TR/XMLHttpRequest/
+ t.plan(9);
+ // Case 1: Request-A: open & abort
+ var requestA = new OpenLayers.Request.XMLHttpRequest();
+ //requestA.onreadystatechange = function() {};
+ t.eq(requestA.readyState, 0, "Request-A: readyState after new is 0-UNSENT");
+ requestA.open("GET", ".", true);
+ t.eq(requestA.readyState, 1, "Request-A: readyState after open is 1-OPENED");
+ requestA.abort();
+ t.eq(requestA.readyState, 0, "Request-A: readyState after abort is 0-UNSENT");
+
+ // Case 2: Request-B: open & send
+ var requestB = new OpenLayers.Request.XMLHttpRequest();
+ requestB.onreadystatechange = function() {
+ if (requestB.readyState == 4) {
+ t.ok(true, "Request-B: triggered the event onreadystatechange when 4-DONE");
+ }
+ };
+ t.eq(requestB.readyState, 0, "Request-B: readyState after new is 0-UNSENT");
+ requestB.open("GET", ".", true);
+ t.eq(requestB.readyState, 1, "Request-B: readyState after open is 1-OPENED");
+ requestB.send();
+
+ // Case 3: Request-C: open, send & abort
+ var requestC = new OpenLayers.Request.XMLHttpRequest();
+ requestC.onreadystatechange = function() {
+ if (requestC.readyState == 4) {
+ t.fail("Request-C: triggered the event onreadystatechange when 4-DONE after abort");
+ }
+ };
+ t.eq(requestC.readyState, 0, "Request-C: readyState after new is 0-UNSENT");
+ requestC.open("GET", ".", true);
+ t.eq(requestC.readyState, 1, "Request-C: readyState after open is 1-OPENED");
+ requestC.send();
+ requestC.abort();
+ t.eq(requestC.readyState, 0, "Request-C: readyState after abort is 0-UNSENT");
+
+ // delay destroy
+ t.delay_call(
+ 2, function() {
+ // to await the end of requestB and requestC
+ }
+ );
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Rule.html b/misc/openlayers/tests/Rule.html
new file mode 100644
index 0000000..56e3483
--- /dev/null
+++ b/misc/openlayers/tests/Rule.html
@@ -0,0 +1,123 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_Rule_constructor(t) {
+ t.plan(3);
+
+ var options = {'foo': 'bar'};
+ var rule = new OpenLayers.Rule(options);
+ t.ok(rule instanceof OpenLayers.Rule,
+ "new OpenLayers.Rule returns object" );
+ t.eq(rule.foo, "bar", "constructor sets options correctly");
+ t.eq(typeof rule.evaluate, "function", "rule has an evaluate function");
+ }
+
+ function test_Rule_getContext(t) {
+ t.plan(2);
+ var rule, options;
+
+ var feature = {
+ attributes: {
+ 'dude': 'hello'
+ },
+ 'foobar': 'world'
+ }
+
+ rule = new OpenLayers.Rule();
+ var context = rule.getContext(feature);
+ t.eq(context.dude, "hello", "value returned by getContext is correct"
+ + " if no context is specified");
+
+ var options = {
+ context: function(feature){
+ return feature;
+ }
+ };
+ rule = new OpenLayers.Rule(options);
+ var context = rule.getContext(feature);
+ t.eq(context.foobar, "world", "value returned by getContext is correct"
+ + " if a context is given in constructor options");
+ }
+
+ function test_clone(t) {
+
+ t.plan(9);
+
+ var rule = new OpenLayers.Rule({
+ name: "test rule",
+ minScaleDenominator: 10,
+ maxScaleDenominator: 20,
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "prop",
+ value: "value"
+ }),
+ symbolizer: {
+ fillColor: "black"
+ },
+ context: {
+ foo: "bar"
+ }
+ });
+ var clone = rule.clone();
+ t.eq(clone.name, "test rule", "name copied");
+ t.eq(clone.minScaleDenominator, 10, "minScaleDenominator copied");
+ t.eq(clone.filter.type, OpenLayers.Filter.Comparison.EQUAL_TO, "clone has correct filter type");
+
+ // modify original
+ rule.filter.property = "new";
+ rule.symbolizer.fillColor = "white";
+ rule.context.foo = "baz";
+
+ // confirm that clone didn't change
+ t.eq(clone.filter.property, "prop", "clone has clone of filter");
+ t.eq(clone.symbolizer.fillColor, "black", "clone has clone of symbolizer");
+ t.eq(clone.context.foo, "bar", "clone has clone of context");
+
+ // confirm that ids are different
+ t.ok(clone.id !== rule.id, "clone has different id");
+
+ rule.destroy();
+ clone.destroy();
+
+ // test multiple symbolizers
+ rule = new OpenLayers.Rule({
+ name: "test rule",
+ minScaleDenominator: 10,
+ maxScaleDenominator: 20,
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "prop",
+ value: "value"
+ }),
+ symbolizers: [
+ new OpenLayers.Symbolizer.Line({
+ strokeColor: "black"
+ })
+ ]
+ });
+ clone = rule.clone();
+
+ t.eq(clone.symbolizers.length, 1, "clone has one symbolizer");
+ t.ok(clone.symbolizers[0] !== rule.symbolizers[0], "clone has different symbolizers than original");
+
+ clone.destroy();
+ rule.destroy();
+
+ }
+
+ function test_Rule_destroy(t) {
+ t.plan(1);
+
+ var rule = new OpenLayers.Rule();
+ rule.destroy();
+ t.eq(rule.symbolizer, null, "symbolizer hash nulled properly");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/SingleFile1.html b/misc/openlayers/tests/SingleFile1.html
new file mode 100644
index 0000000..836a1a5
--- /dev/null
+++ b/misc/openlayers/tests/SingleFile1.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+ <script src="some/OpenLayers/path/OpenLayers.js"></script>
+ <script src="../lib/OpenLayers/SingleFile.js"></script>
+ <script type="text/javascript">
+ function test_OpenLayers(t) {
+ t.plan(1);
+ t.eq(OpenLayers._getScriptLocation(), "some/OpenLayers/path/",
+ "Script location correctly detected (OpenLayers.js).");
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/SingleFile2.html b/misc/openlayers/tests/SingleFile2.html
new file mode 100644
index 0000000..68b47a3
--- /dev/null
+++ b/misc/openlayers/tests/SingleFile2.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+ <script src="some/OpenLayers/path/OpenLayers.light.js"></script>
+ <script src="../lib/OpenLayers/SingleFile.js"></script>
+ <script type="text/javascript">
+ function test_OpenLayers(t) {
+ t.plan(1);
+ t.eq(OpenLayers._getScriptLocation(), "some/OpenLayers/path/",
+ "Script location correctly detected (OpenLayers.light.js)�.");
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/SingleFile3.html b/misc/openlayers/tests/SingleFile3.html
new file mode 100644
index 0000000..bb58fcb
--- /dev/null
+++ b/misc/openlayers/tests/SingleFile3.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+ <script src="some/OpenLayers/path/OpenLayers.light.debug.js"></script>
+ <script src="../lib/OpenLayers/SingleFile.js"></script>
+ <script type="text/javascript">
+ function test_OpenLayers(t) {
+ t.plan(1);
+ t.eq(OpenLayers._getScriptLocation(), "some/OpenLayers/path/",
+ "Script location correctly detected (OpenLayers.light.debug.js).");
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Strategy.html b/misc/openlayers/tests/Strategy.html
new file mode 100644
index 0000000..5ecdef6
--- /dev/null
+++ b/misc/openlayers/tests/Strategy.html
@@ -0,0 +1,94 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(5);
+ var options = {};
+ var strategy = new OpenLayers.Strategy(options);
+
+ t.ok(strategy instanceof OpenLayers.Strategy,
+ "new OpenLayers.Strategy returns object" );
+ t.eq(strategy.options, options, "constructor sets this.options");
+ t.eq(strategy.active, false, "constructor sets this.active to false");
+ t.eq(strategy.autoActivate, true, "constructor does not modify this.autoActivate");
+ t.eq(strategy.autoDestroy, true, "constructor does not modify this.autoDestroy");
+ }
+
+ function test_activate(t) {
+ t.plan(1);
+ var options = {
+ activate: function() {
+ t.ok(true, "OpenLayer.Map.addLayer calls activate");
+ }
+ };
+
+ var layer = new OpenLayers.Layer.Vector("Vector Layer", {
+ strategies: [new OpenLayers.Strategy(options)]
+ });
+
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+ }
+
+ function test_destroy(t) {
+ t.plan(3);
+
+ var strategy = new OpenLayers.Strategy({
+ deactivate: function() {
+ t.ok(true, "destroy calls deactivate");
+ },
+
+ options: {foo: 'bar'},
+ layer: 'foo'
+ });
+ strategy.destroy();
+
+ t.eq(strategy.layer, null, "destroy nullify protocol.layer");
+ t.eq(strategy.options, null, "destroy nullify protocol.options");
+ }
+
+ function test_activate(t) {
+ t.plan(4);
+ var strategy = new OpenLayers.Strategy({
+ layer: 'foo'
+ });
+
+ var ret;
+ ret = strategy.activate();
+
+ t.eq(strategy.active, true, "activate sets this.active to true on first call");
+ t.eq(ret, true, "activate returns true on first call");
+
+ ret = strategy.activate();
+
+ t.eq(strategy.active, true, "activate does not modify this.active on second call");
+ t.eq(ret, false, "activate returns false on second call");
+ }
+
+ function test_deactivate(t) {
+ t.plan(4);
+ var strategy = new OpenLayers.Strategy({
+ layer: 'foo'
+ });
+ strategy.activate();
+
+ var ret;
+ ret = strategy.deactivate();
+
+ t.eq(strategy.active, false, "deactivate sets this.active to false on first call");
+ t.eq(ret, true, "deactivate returns true on first call");
+
+ ret = strategy.deactivate();
+
+ t.eq(strategy.active, false, "deactivate does not modify this.active on second call");
+ t.eq(ret, false, "deactivate returns false on second call");
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Strategy/BBOX.html b/misc/openlayers/tests/Strategy/BBOX.html
new file mode 100644
index 0000000..6e409e6
--- /dev/null
+++ b/misc/openlayers/tests/Strategy/BBOX.html
@@ -0,0 +1,361 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(1);
+
+ var ratio = 4;
+
+ var s = new OpenLayers.Strategy.BBOX({ratio: ratio});
+ t.eq(s.ratio, ratio, "ctor sets ratio");
+ }
+
+ function test_activate(t) {
+ t.plan(5);
+
+ var l = new OpenLayers.Layer.Vector();
+ var s = new OpenLayers.Strategy.BBOX();
+ s.setLayer(l);
+
+ t.eq(s.active, false, "not active after construction");
+
+ var activated = s.activate();
+ t.eq(activated, true, "activate returns true");
+ t.eq(s.active, true, "activated after activate");
+ t.ok(l.events.listeners["moveend"][0].obj == s &&
+ l.events.listeners["moveend"][0].func == s.update,
+ "activates registers moveend listener");
+ t.ok(l.events.listeners["refresh"][0].obj == s &&
+ l.events.listeners["refresh"][0].func == s.update,
+ "activates registers refresh listener");
+ }
+
+ function test_update(t) {
+ t.plan(7);
+
+ // Create a dummy layer that can act as the map base layer.
+ // This will be unnecessary if #1921 is addressed (allowing
+ // map to have different projection than base layer).
+ var dummy = new OpenLayers.Layer(null, {isBaseLayer: true});
+
+ var strategy = new OpenLayers.Strategy.BBOX({
+ ratio: 1 // makes for easier comparison to map bounds
+ });
+ var log = [];
+ var layer = new OpenLayers.Layer.Vector(null, {
+ isBaseLayer: true,
+ protocol: new OpenLayers.Protocol({abort: function(response) { log.push(response); }}),
+ strategies: [strategy]
+ });
+
+ // create a map with the layers and a center
+ var map = new OpenLayers.Map("map", {zoomMethod: null});
+ map.addLayers([dummy, layer]);
+ map.zoomToMaxExtent();
+
+ /**
+ * The setCenter call above should set strategy bounds. I *think* this
+ * issue is captured in http://trac.openlayers.org/ticket/1835.
+ * For now, I'm going to force an update on the strategy. This line
+ * should be removed when the issue(s) described in #1835 are addressed.
+ */
+ strategy.update({force: true});
+ strategy.response = {};
+ strategy.update({force: true});
+ t.eq(log.length, 1, "Response aborted");
+ log = [];
+ strategy.update({force: true});
+ strategy.update({force: true, noAbort: true});
+ t.eq(log.length, 0, "Response not aborted when noAbort is true");
+
+ // test that the strategy bounds were set
+ t.ok(map.getExtent().equals(strategy.bounds), "[set center] bounds set to map extent");
+
+ // zoom and test that bounds are not reset
+ var old = strategy.bounds.clone();
+ map.zoomIn();
+ t.ok(strategy.bounds.equals(old), "[zoom in] bounds not reset");
+
+ // force update and check that bounds change
+ strategy.update({force: true});
+ t.ok(!strategy.bounds.equals(old), "[force update] bounds changed");
+ t.ok(strategy.bounds.equals(map.getExtent()), "[force update] bounds set to map extent");
+
+ // change the layer projection to confirm strategy uses same
+ layer.projection = new OpenLayers.Projection("EPSG:900913");
+ strategy.update({force: true});
+ var from = map.getProjectionObject();
+ var to = layer.projection;
+
+ var strategyBounds = strategy.bounds,
+ mapExtent = map.getExtent().transform(from, to),
+ // we don't use bounds::toString because we might run into
+ // precision issues
+ precision = 7,
+ strategyBoundsGot = [
+ strategyBounds.left.toFixed( precision ),
+ strategyBounds.bottom.toFixed( precision ),
+ strategyBounds.right.toFixed( precision ),
+ strategyBounds.top.toFixed( precision )
+ ].join(','),
+ mapExtentExpected = [
+ mapExtent.left.toFixed( precision ),
+ mapExtent.bottom.toFixed( precision ),
+ mapExtent.right.toFixed( precision ),
+ mapExtent.top.toFixed( precision )
+ ].join(',');
+ t.eq(strategyBoundsGot, mapExtentExpected,
+ "[force update different proj] bounds transformed");
+ }
+
+ function test_events(t) {
+
+ t.plan(7);
+
+ var log = [];
+
+ var response = new OpenLayers.Protocol.Response();
+
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector(null, {
+ strategies: [new OpenLayers.Strategy.BBOX()],
+ protocol: new OpenLayers.Protocol({
+ read: function(config) {
+ config.callback.call(config.scope, response);
+ }
+ }),
+ isBaseLayer: true,
+ eventListeners: {
+ loadstart: function(event) {
+ log.push(event);
+ },
+ loadend: function(event) {
+ log.push(event);
+ }
+ }
+ });
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ t.eq(log.length, 2, "2 events logged");
+ t.eq(log[0].type, "loadstart", "loadstart first");
+ t.ok(log[0].filter.type === OpenLayers.Filter.Spatial.BBOX, "loadstart includes filter used");
+ t.eq(log[1].type, "loadend", "loadend second");
+ t.ok(log[1].response == response, "loadend includes response");
+
+ var calls = [];
+ layer.protocol.read = function(obj) {
+ calls.push(obj);
+ }
+ layer.refresh({force: true, whee: 'chicken'});
+
+ t.eq(calls.length, 1, "1 call to read");
+ t.eq(calls[0].whee, "chicken", "properties passed to read");
+
+ map.destroy();
+
+ }
+
+ function test_triggerRead(t) {
+ t.plan(4);
+
+ var s = new OpenLayers.Strategy.BBOX();
+
+ var filter = {"fake": "filter"};
+
+ s.createFilter = function() {
+ return filter;
+ };
+ s.response = {"fake": "response"};
+
+ var log = {};
+
+ var protocol = new OpenLayers.Protocol({
+ read: function(options) {
+ log.options = options;
+ },
+ abort: function(response) {
+ log.abort = response.fake;
+ }
+ });
+
+ var layer = new OpenLayers.Layer.Vector(null, {
+ strategies: [s],
+ protocol: protocol,
+ isBaseLayer: true
+ });
+ var map = new OpenLayers.Map("map");
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ t.ok(log.options.filter == filter,
+ "protocol read called with correct filter");
+ t.ok(log.options.callback == s.merge,
+ "protocol read called with correct callback");
+ t.ok(log.options.scope == s,
+ "protocol read called with correct scope");
+ t.eq(log.abort, "response",
+ "protocol abort called with correct response");
+
+ map.destroy();
+
+ }
+
+ function test_resFactor(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map("map", {zoomMethod: null});
+ var bbox = new OpenLayers.Strategy.BBOX();
+ var fakeProtocol = new OpenLayers.Protocol({
+ 'read': function() {
+ t.ok(true, "read called once without resfactor");
+ }
+ });
+ var layer = new OpenLayers.Layer.Vector("test", {
+ strategies: [bbox],
+ protocol: fakeProtocol,
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+ map.zoomIn();
+
+ fakeProtocol.read = function() {
+ t.ok("read called again on zooming with resFactor: 1");
+ }
+ bbox.resFactor = 1;
+ map.zoomIn();
+
+ }
+
+ function test_createFilter(t) {
+ t.plan(3);
+
+ var s = new OpenLayers.Strategy.BBOX();
+
+ var f;
+
+ // 2 test
+ s.setLayer({});
+ f = s.createFilter();
+ t.ok(f.CLASS_NAME.search(/^OpenLayers.Filter.Spatial/) != -1,
+ "createFilter returns a spatial filter object");
+ t.eq(f.type, OpenLayers.Filter.Spatial.BBOX,
+ "createFilter returns a BBOX-typed filter");
+
+ // 1 test
+ s.setLayer({filter: {fake: "filter"}});
+ f = s.createFilter();
+ t.ok(f.CLASS_NAME.search(/^OpenLayers.Filter.Logical/) != -1,
+ "createFilter returns a logical filter object");
+ }
+
+ function test_merge(t) {
+ t.plan(4);
+
+ var strategy = new OpenLayers.Strategy.BBOX();
+
+ // create map with default projection
+ var map = new OpenLayers.Map("map");
+
+ // create layer with custom projection
+ var layer = new OpenLayers.Layer.Vector(null, {
+ isBaseLayer: true,
+ strategies: [strategy],
+ protocol: new OpenLayers.Protocol(),
+ projection: new OpenLayers.Projection("EPSG:900913")
+ });
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ // create some features
+ var geometries = [
+ new OpenLayers.Geometry.Point(100, 200),
+ new OpenLayers.Geometry.Point(1000, 2000)
+ ];
+ var features = [
+ new OpenLayers.Feature.Vector(geometries[0].clone()),
+ new OpenLayers.Feature.Vector(geometries[1].clone())
+ ];
+
+ // call merge with a mocked up response
+ strategy.merge({features: features, success: OpenLayers.Function.True});
+
+ // test that feature geometries have been transformed to map projection
+ var from = layer.projection;
+ var to = map.getProjectionObject();
+ t.geom_eq(layer.features[0].geometry, features[0].geometry.transform(from, to), "[different proj] feature 0 geometry transformed");
+ t.geom_eq(layer.features[1].geometry, features[1].geometry.transform(from, to), "[different proj] feature 1 geometry transformed");
+
+ // same as above but with same map/layer projection
+ layer.destroyFeatures();
+ layer.projection = map.getProjectionObject();
+
+ features = [
+ new OpenLayers.Feature.Vector(geometries[0].clone()),
+ new OpenLayers.Feature.Vector(geometries[1].clone())
+ ];
+
+ // call merge again with mocked up response
+ strategy.merge({features: features, success: OpenLayers.Function.True});
+
+ // test that feature geometries have not been transformed
+ t.geom_eq(layer.features[0].geometry, features[0].geometry, "[same proj] feature 0 geometry not transformed");
+ t.geom_eq(layer.features[1].geometry, features[1].geometry, "[same proj] feature 1 geometry not transformed");
+
+ }
+
+ // Test fix for Ticket #3142
+ function test_layerLoadedAfterBeingAdded(t) {
+ t.plan(3);
+
+ var dummy = new OpenLayers.Layer(null, {isBaseLayer: true});
+
+ var strategy = new OpenLayers.Strategy.BBOX({
+ ratio: 1 // makes for easier comparison to map bounds
+ });
+ var layer = new OpenLayers.Layer.Vector(null, {
+ protocol: new OpenLayers.Protocol(),
+ strategies: [strategy]
+ });
+
+ // Make sure to test the case of a vector layer needing to be
+ // reprojected while the map is not yet centered
+ var layerReproject = new OpenLayers.Layer.Vector(null, {
+ protocol: new OpenLayers.Protocol(),
+ strategies: [new OpenLayers.Strategy.BBOX()],
+ projection: 'EPSG:900913'
+ });
+
+ // Make sure that layers that are not in range don't request data
+ var layerOutOfRange = new OpenLayers.Layer.Vector(null, {
+ maxResolution: 1,
+ protocol: new OpenLayers.Protocol(),
+ strategies: [new OpenLayers.Strategy.BBOX()]
+ });
+
+ var map = new OpenLayers.Map("map");
+ map.addLayer(dummy);
+ map.addLayer(layerReproject);
+ map.setCenter(new OpenLayers.LonLat(0, 0));
+ map.addLayer(layer);
+ map.addLayer(layerOutOfRange);
+ // test that the strategy bounds were set
+ t.ok(map.getExtent().equals(strategy.bounds), "[set center] bounds set to map extent");
+ t.eq(layerOutOfRange.strategies[0].bounds, null, "Data not requested if layer is out of range");
+
+ layerOutOfRange.setVisibility(false);
+ layerOutOfRange.setVisibility(true);
+ t.eq(layerOutOfRange.strategies[0].bounds, null, "Data not requested if layer is out of range when switching visibility");
+
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 200px" />
+</body>
+</html>
diff --git a/misc/openlayers/tests/Strategy/Cluster.html b/misc/openlayers/tests/Strategy/Cluster.html
new file mode 100644
index 0000000..3358ff9
--- /dev/null
+++ b/misc/openlayers/tests/Strategy/Cluster.html
@@ -0,0 +1,148 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_activate(t) {
+ t.plan(2);
+
+ var strategy = new OpenLayers.Strategy.Cluster();
+ t.eq(strategy.active, false, "not active after construction");
+
+ var layer = new OpenLayers.Layer.Vector("Vector Layer", {
+ strategies: [strategy]
+ });
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+
+ t.eq(strategy.active, true, "active after adding to map");
+ }
+
+ function test_clusters(t) {
+ t.plan(22);
+
+ function featuresEq(got, exp) {
+ var eq = false;
+ if(got instanceof Array && exp instanceof Array) {
+ if(got.length === exp.length) {
+ for(var i=0; i<got.length; ++i) {
+ if(got[i] !== exp[i]) {
+ console.log(got[i], exp[i]);
+ break;
+ }
+ }
+ eq = (i == got.length);
+ }
+ }
+ return eq;
+ }
+
+ var strategy = new OpenLayers.Strategy.Cluster();
+ var layer = new OpenLayers.Layer.Vector("Vector Layer", {
+ strategies: [strategy],
+ isBaseLayer: true
+ });
+ var map = new OpenLayers.Map('map', {
+ resolutions: [4, 2, 1],
+ maxExtent: new OpenLayers.Bounds(-40, -40, 40, 40),
+ zoomMethod: null
+ });
+ map.addLayer(layer);
+
+ // create features in a line, 1 unit apart
+ var features = new Array(80);
+ for(var i=0; i<80; ++i) {
+ features[i] = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(-40 + i, 0)
+ );
+ }
+
+ // add one additional feature, with no geometry - just to confirm it doesn't break things
+ features.push(new OpenLayers.Feature.Vector());
+
+ layer.addFeatures(features);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ // resolution 4
+ // threshold: 4 * 20 = 80 units
+ // one cluster
+ t.eq(layer.features.length, 1, "[4] layer has one cluster");
+ t.ok(featuresEq(layer.features[0].cluster, features.slice(0, 80)), "[4] cluster includes all features with geometries");
+
+ // resolution 2
+ // threshold: 2 * 20 = 40 units
+ // two clusters (41 and 39) - first cluster includes all features within 40 units of the first (0-40 or 41 features)
+ map.zoomIn();
+ t.eq(layer.features.length, 2, "[2] layer has two clusters");
+ t.ok(featuresEq(layer.features[0].cluster, features.slice(0, 41)), "[2] first cluster includes first 41 features");
+ t.ok(featuresEq(layer.features[1].cluster, features.slice(41, 80)), "[2] second cluster includes last 39 features");
+
+ // resolution 1
+ // threshold: 1 * 20 = 20 units
+ // four clusters (21, 21, 21, and 17)
+ map.zoomIn();
+ t.eq(layer.features.length, 4, "[1] layer has four clusters");
+ t.ok(featuresEq(layer.features[0].cluster, features.slice(0, 21)), "[1] first cluster includes first 21 features");
+ t.ok(featuresEq(layer.features[1].cluster, features.slice(21, 42)), "[1] second cluster includes second 21 features");
+ t.ok(featuresEq(layer.features[2].cluster, features.slice(42, 63)), "[1] third cluster includes third 21 features");
+ t.ok(featuresEq(layer.features[3].cluster, features.slice(63, 80)), "[1] fourth cluster includes last 17 features");
+
+ // zoom out and back in to test threshold property (21)
+ map.zoomOut();
+ strategy.threshold = 21;
+ map.zoomIn();
+ t.eq(layer.features.length, 20, "[1-threshold 21] layer has 20 clusters");
+ t.ok(featuresEq(layer.features[0].cluster, features.slice(0, 21)), "[1-threshold 21] first cluster includes first 21 features");
+ t.ok(featuresEq(layer.features[1].cluster, features.slice(21, 42)), "[1-threshold 21] second cluster includes second 21 features");
+ t.ok(featuresEq(layer.features[2].cluster, features.slice(42, 63)), "[1-threshold 21] third cluster includes third 21 features");
+ t.ok(featuresEq(layer.features.slice(3, 20), features.slice(63, 80)), "[1-threshold 21] last 17 features are not clustered");
+
+ // zoom out and back in to test high threshold
+ map.zoomOut();
+ strategy.threshold = 100; // clusters must contain 100 features or more
+ map.zoomIn();
+ // the one feature with no geometry is not added to the layer
+ t.eq(layer.features.length, features.length-1, "[1-threshold 100] layer has " + (features.length-1) + " clusters");
+ t.ok(featuresEq(layer.features, features.slice(0, 80)), "[1-threshold 100] layer has all features with geometry");
+
+ // remove features and zoom
+ strategy.threshold = 1;
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+ t.eq(strategy.features.length, 81,
+ "[remove features] cluster has cache");
+ layer.removeAllFeatures();
+ t.eq(layer.features.length, 0,
+ "[remove features] layer has no features after remove");
+ t.ok(!strategy.features,
+ "[remove features] cluster has no cache after remove");
+ map.zoomIn();
+ t.eq(layer.features.length, 0,
+ "[remove features] layer has no features after zoom");
+ t.ok(!strategy.features,
+ "[remove features] cluster has no cache after zoom");
+
+ map.destroy();
+ }
+
+ function test_deactivate(t) {
+ t.plan(2);
+
+ var strategy = new OpenLayers.Strategy.Cluster();
+ var layer = new OpenLayers.Layer.Vector("Vector Layer", {
+ strategies: [strategy]
+ });
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+
+ t.eq(strategy.active, true, "active after adding to map");
+
+ map.removeLayer(layer);
+ t.eq(strategy.active, false, "not active after removing from map");
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 200px" />
+</body>
+</html>
diff --git a/misc/openlayers/tests/Strategy/Filter.html b/misc/openlayers/tests/Strategy/Filter.html
new file mode 100644
index 0000000..7889d1e
--- /dev/null
+++ b/misc/openlayers/tests/Strategy/Filter.html
@@ -0,0 +1,135 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../OLLoader.js"></script>
+<script>
+
+var features = [];
+for (var i=0; i<20; ++i) {
+ features.push(
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(0, 0), {index: i}
+ )
+ );
+}
+
+var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LESS_THAN,
+ property: "index",
+ value: 10
+});
+
+function test_initialize(t) {
+
+ t.plan(4);
+
+ var strategy = new OpenLayers.Strategy.Filter({filter: filter});
+
+ t.ok(strategy instanceof OpenLayers.Strategy, "is strategy");
+ t.ok(strategy instanceof OpenLayers.Strategy.Filter, "is filter strategy");
+
+ t.ok(strategy.filter === filter, "has filter");
+
+ strategy.destroy();
+
+ try {
+ strategy = new OpenLayers.Strategy.Filter();
+ t.ok(true, "strategy without filter works");
+ } catch (err) {
+ t.fail("strategy without filter should not throw");
+ }
+
+
+}
+
+function test_autoActivate(t) {
+
+ t.plan(2);
+
+ var strategy = new OpenLayers.Strategy.Filter({filter: filter});
+
+ var layer = new OpenLayers.Layer.Vector(null, {
+ strategies: [strategy]
+ });
+
+ t.ok(!strategy.active, "strategy not active before adding to map");
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ allOverlays: true,
+ layers: [layer],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 1
+ });
+
+ t.ok(strategy.active, "strategy active after adding to map");
+
+ map.destroy();
+
+}
+
+function test_setFilter(t) {
+
+ t.plan(13);
+
+ var strategy = new OpenLayers.Strategy.Filter({filter: filter});
+ var layer = new OpenLayers.Layer.Vector(null, {
+ strategies: [strategy]
+ });
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ allOverlays: true,
+ layers: [layer],
+ center: new OpenLayers.LonLat(0, 0),
+ zoom: 1
+ });
+
+ var log = [];
+ layer.events.on({
+ beforefeaturesadded: function(event) {
+ log.push(event.type);
+ },
+ beforefeaturesremoved: function(event) {
+ log.push(event.type);
+ }
+ })
+
+ // a) add all features
+ log = [];
+ layer.addFeatures(features);
+ t.eq(features.length, 20, "a) collection of 20 features")
+ t.eq(layer.features.length, 10, "a) layer got 10 with filter 'index < 10'");
+ t.eq(strategy.cache.length, 10, "a) strategy cached 10 with filter 'index < 10'");
+ t.eq(log.length, 1, "a) one event logged");
+ t.eq(log[0], "beforefeaturesadded", "a) beforefeaturesadded fired");
+
+ // b) update filter
+ log = [];
+ filter.value = 5;
+ strategy.setFilter(filter);
+ t.eq(layer.features.length, 5, "b) layer got 5 with filter 'index < 5'");
+ t.eq(strategy.cache.length, 15, "b) strategy cached 15 with filter 'index < 5'");
+ t.eq(log.length, 1, "b) one event logged");
+ t.eq(log[0], "beforefeaturesremoved", "b) beforefeaturesremoved fired");
+
+ // c) update filter
+ log = [];
+ filter.value = 15;
+ strategy.setFilter(filter);
+ t.eq(layer.features.length, 15, "c) layer got 15 with filter 'index < 15'");
+ t.eq(strategy.cache.length, 5, "c) strategy cached 5 with filter 'index < 15'");
+ t.eq(log.length, 1, "c) one event logged");
+ t.eq(log[0], "beforefeaturesadded", "c) beforefeaturesadded fired");
+
+ map.destroy();
+
+}
+
+
+
+</script></head>
+<body>
+ <div id="map" style="width: 512px; height: 256px" />
+</body>
+</html> \ No newline at end of file
diff --git a/misc/openlayers/tests/Strategy/Fixed.html b/misc/openlayers/tests/Strategy/Fixed.html
new file mode 100644
index 0000000..a9bf608
--- /dev/null
+++ b/misc/openlayers/tests/Strategy/Fixed.html
@@ -0,0 +1,253 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_activate(t) {
+ t.plan(5);
+
+ var featureList = ['foo', 'bar'];
+ // a fake protocol
+ var protocol = {
+ read: function(options) {
+ options.callback.call(options.scope, {
+ features: featureList,
+ success: OpenLayers.Function.True
+ });
+ }
+ };
+
+ // Create a dummy layer that can act as the map base layer.
+ // This will be unnecessary if #1920 is addressed or if base layer
+ // handling is changed.
+ var dummy = new OpenLayers.Layer(null, {isBaseLayer: true});
+
+ var layer = new OpenLayers.Layer.Vector("Vector Layer", {
+ strategies: [new OpenLayers.Strategy.Fixed()],
+ protocol: protocol,
+ addFeatures: function(features) {
+ t.eq(features, featureList, "Features added to the layer");
+ }
+ });
+
+ var layerp = new OpenLayers.Layer.Vector("Hidden preload Layer", {
+ strategies: [new OpenLayers.Strategy.Fixed({preload:true})],
+ protocol: protocol,
+ visibility: false,
+ addFeatures: function(features) {
+ t.ok(!this.visibility, "Features preloaded before visible");
+ }
+ });
+
+ var s = new OpenLayers.Strategy.Fixed();
+ var layer2 = new OpenLayers.Layer.Vector("Hidden lazyload Layer", {
+ strategies: [s],
+ protocol: protocol,
+ visibility: false,
+ addFeatures: function(features) {
+ t.ok(this.visibility, "Layer visible when features added");
+ }
+ });
+
+ var map = new OpenLayers.Map('map');
+ map.addLayers([dummy, layer, layerp, layer2]);
+
+ t.ok(layer2.events.listeners["visibilitychanged"][0].obj == s &&
+ layer2.events.listeners["visibilitychanged"][0].func == s.load,
+ "activate registers visibilitychanged listener if layer hidden"+
+ " and is lazyloading");
+
+ layer2.setVisibility(true);
+
+ t.ok(layer2.events.listeners["visibilitychanged"] == false,
+ "visibilitychanged listener unregistered");
+ }
+
+ function test_events(t) {
+
+ t.plan(7);
+
+ var log = [];
+
+ var response = new OpenLayers.Protocol.Response();
+
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector(null, {
+ filter: new OpenLayers.Filter.Comparison({
+ type: '==',
+ property: 'foo',
+ value: 'bar'
+ }),
+ strategies: [new OpenLayers.Strategy.Fixed()],
+ protocol: new OpenLayers.Protocol({
+ read: function(config) {
+ config.callback.call(config.scope, response);
+ }
+ }),
+ isBaseLayer: true,
+ eventListeners: {
+ loadstart: function(event) {
+ log.push(event);
+ },
+ loadend: function(event) {
+ log.push(event);
+ }
+ }
+ });
+
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ t.eq(log.length, 2, "2 events logged");
+ t.eq(log[0].type, "loadstart", "loadstart first");
+ t.eq(log[0].filter, layer.filter, "filter passed on as argument to loadstart");
+ t.eq(log[1].type, "loadend", "loadend second");
+ t.ok(log[1].response == response, "loadend includes response");
+
+ var calls = [];
+ layer.protocol.read = function(obj) {
+ calls.push(obj);
+ }
+ layer.refresh({whee: 'chicken'});
+
+ t.eq(calls.length, 1, "1 call to read");
+ t.eq(calls[0].whee, "chicken", "properties passed to read");
+
+ map.destroy();
+
+ }
+
+
+ function test_merge(t) {
+
+ t.plan(6);
+
+ var strategy = new OpenLayers.Strategy.Fixed();
+
+ // create map with default projection
+ var map = new OpenLayers.Map("map");
+
+ var log = {
+ loadend: 0
+ };
+
+ // create layer with custom projection
+ var layer = new OpenLayers.Layer.Vector(null, {
+ isBaseLayer: true,
+ strategies: [strategy],
+ protocol: new OpenLayers.Protocol(),
+ projection: new OpenLayers.Projection("EPSG:900913"),
+ eventListeners: {
+ loadend: function() {
+ ++log.loadend;
+ }
+ }
+ });
+
+ // give the layer some existing features (one)
+ layer.addFeatures([
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(0, 0)
+ )
+ ]);
+
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ // create some features
+ var geometries = [
+ new OpenLayers.Geometry.Point(100, 200),
+ new OpenLayers.Geometry.Point(1000, 2000)
+ ];
+ var features = [
+ new OpenLayers.Feature.Vector(geometries[0].clone()),
+ new OpenLayers.Feature.Vector(geometries[1].clone())
+ ];
+
+ // call merge with a mocked up response
+ strategy.merge({features: features, success: OpenLayers.Function.True});
+
+ // confirm that the original features were destroyed
+ t.eq(layer.features.length, 2, "old features destroyed");
+
+ // confirm that loadend was called
+ t.eq(log.loadend, 1, "merge triggers loadend");
+
+ // test that feature geometries have been transformed to map projection
+ var from = layer.projection;
+ var to = map.getProjectionObject();
+ t.geom_eq(layer.features[0].geometry, features[0].geometry.transform(from, to), "[different proj] feature 0 geometry transformed");
+ t.geom_eq(layer.features[1].geometry, features[1].geometry.transform(from, to), "[different proj] feature 1 geometry transformed");
+
+ // same as above but with same map/layer projection
+ layer.destroyFeatures();
+ layer.projection = map.getProjectionObject();
+
+ features = [
+ new OpenLayers.Feature.Vector(geometries[0].clone()),
+ new OpenLayers.Feature.Vector(geometries[1].clone())
+ ];
+
+ // call merge again with mocked up response
+ strategy.merge({features: features, success: OpenLayers.Function.True});
+
+ // test that feature geometries have not been transformed
+ t.geom_eq(layer.features[0].geometry, features[0].geometry, "[same proj] feature 0 geometry not transformed");
+ t.geom_eq(layer.features[1].geometry, features[1].geometry, "[same proj] feature 1 geometry not transformed");
+
+ }
+
+ function test_load(t) {
+ t.plan(3);
+
+ // set up
+
+ var log;
+
+ var map = new OpenLayers.Map({
+ div: "map",
+ projection: new OpenLayers.Projection("EPSG:900913"),
+ layers: [new OpenLayers.Layer("", {isBaseLayer: true})]
+ });
+
+ var response = new OpenLayers.Protocol.Response();
+
+ var strategy = new OpenLayers.Strategy.Fixed({
+ merge: function(r) {
+ log = {scope: this, response: r};
+ }
+ });
+
+ var layer = new OpenLayers.Layer.Vector("vector", {
+ strategies: [strategy],
+ protocol: {
+ read: function(o) {
+ o.callback.call(o.scope, response);
+ }
+ }
+ });
+
+ map.addLayer(layer);
+
+ // test
+
+ strategy.load();
+
+ // verify that the callback is correctly bound
+ t.ok(log !== undefined,
+ "merge was called");
+ t.ok(log.scope == strategy,
+ "merge called with expected scope");
+ t.ok(log.response == response,
+ "merge called with response as the first arg");
+
+ // tear down
+
+ map.destroy();
+ }
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 200px" />
+</body>
+</html>
diff --git a/misc/openlayers/tests/Strategy/Paging.html b/misc/openlayers/tests/Strategy/Paging.html
new file mode 100644
index 0000000..a85167e
--- /dev/null
+++ b/misc/openlayers/tests/Strategy/Paging.html
@@ -0,0 +1,113 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_activate(t) {
+ t.plan(2);
+
+ var strategy = new OpenLayers.Strategy.Paging();
+ t.eq(strategy.active, false, "not active after construction");
+
+ var layer = new OpenLayers.Layer.Vector("Vector Layer", {
+ strategies: [strategy]
+ });
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+
+ t.eq(strategy.active, true, "active after adding to map");
+ }
+
+ function test_paging(t) {
+ t.plan(18);
+
+ var strategy = new OpenLayers.Strategy.Paging();
+ var layer = new OpenLayers.Layer.Vector("Vector Layer", {
+ strategies: [strategy],
+ drawFeature: function() {}
+ });
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+
+ var features = new Array(25);
+ for(var i=0; i<features.length; ++i) {
+ features[i] = {destroy: function() {}};
+ }
+
+ function featuresEq(got, exp) {
+ var eq = false;
+ if(got instanceof Array && exp instanceof Array) {
+ if(got.length === exp.length) {
+ for(var i=0; i<got.length; ++i) {
+ if(got[i] !== exp[i]) {
+ console.log(got[i], exp[i]);
+ break;
+ }
+ }
+ eq = (i == got.length);
+ }
+ }
+ return eq;
+ }
+
+ var len = strategy.pageLength();
+ t.eq(len, 10, "page length defaults to 10");
+
+ // add 25 features to the layer
+ layer.addFeatures(features);
+ t.eq(strategy.features.length, features.length, "strategy caches all features");
+ t.eq(layer.features.length, len, "layer gets one page of features");
+ t.ok(featuresEq(layer.features, features.slice(0, len)), "layer gets first page initially");
+ t.eq(strategy.pageNum(), 0, "strategy reports 0 based page number");
+ t.eq(strategy.pageCount(), Math.ceil(features.length / len), "strategy reports correct number of pages");
+
+ // load next page of features
+ var changed = strategy.pageNext();
+ t.eq(changed, true, "(1) strategy reports change");
+ t.eq(strategy.pageNum(), 1, "second page");
+ t.ok(featuresEq(layer.features, features.slice(len, 2*len)), "layer has second page of features");
+
+ // load next page of features (half page)
+ changed = strategy.pageNext();
+ t.eq(changed, true, "(2) strategy reports change");
+ t.eq(strategy.pageNum(), 2, "third page");
+
+ // try to change forward again
+ changed = strategy.pageNext();
+ t.eq(changed, false, "strategy reports no change");
+ t.eq(layer.features.length, features.length % len, "layer has partial page");
+ t.ok(featuresEq(layer.features, features.slice(2*len, 3*len)), "layer has third page of features");
+ t.eq(strategy.pageNum(), 2, "still on third page");
+
+ // change back a page
+ changed = strategy.pagePrevious();
+ t.eq(changed, true, "(3) strategy reports change");
+ t.eq(strategy.pageNum(), 1, "back on second page");
+ t.ok(featuresEq(layer.features, features.slice(len, 2*len)), "layer has second page of features again");
+
+ layer.destroy();
+
+ }
+
+ function test_deactivate(t) {
+ t.plan(2);
+
+ var strategy = new OpenLayers.Strategy.Paging();
+ var layer = new OpenLayers.Layer.Vector("Vector Layer", {
+ strategies: [strategy]
+ });
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+
+ t.eq(strategy.active, true, "active after adding to map");
+
+ map.removeLayer(layer);
+ t.eq(strategy.active, false, "not active after removing from map");
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 200px" />
+</body>
+</html>
diff --git a/misc/openlayers/tests/Strategy/Refresh.html b/misc/openlayers/tests/Strategy/Refresh.html
new file mode 100644
index 0000000..054f028
--- /dev/null
+++ b/misc/openlayers/tests/Strategy/Refresh.html
@@ -0,0 +1,177 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var interval = 5000;
+
+ function test_initialize(t) {
+ t.plan(2);
+
+ var s = new OpenLayers.Strategy.Refresh({interval: interval});
+ t.ok(typeof s.interval === "number", "interval must be a number");
+ t.eq(s.interval, interval, "ctor sets interval");
+ }
+
+ function test_activate(t) {
+ t.plan(4);
+
+ var l = new OpenLayers.Layer.Vector();
+ l.setVisibility(false);
+ var s = new OpenLayers.Strategy.Refresh();
+ s.setLayer(l);
+
+ t.eq(s.active, false, "not active after construction");
+
+ var activated = s.activate();
+ t.eq(activated, true, "activate returns true");
+ t.eq(s.active, true, "activated after activate");
+ t.ok(l.events.listeners.visibilitychanged[0].obj == s &&
+ l.events.listeners.visibilitychanged[0].func == s.reset,
+ "activates registers visibilitychanged listener");
+ }
+
+ function test_deactivate(t) {
+ t.plan(3);
+
+ var l = new OpenLayers.Layer.Vector();
+ l.setVisibility(false);
+ var s = new OpenLayers.Strategy.Refresh();
+ s.setLayer(l);
+ s.activate();
+ var deactivated = s.deactivate();
+ t.eq(deactivated, true, "deactivate returns true");
+ t.eq(s.active, false, "deactivated after activate");
+ t.ok(l.events.listeners.visibilitychanged.length == 0,
+ "deactivate unregisters visibilitychanged listener");
+ }
+
+ function test_activateWithVisibleLayer(t) {
+ t.plan(5);
+
+ var l = new OpenLayers.Layer.Vector();
+ l.setVisibility(true);
+ var s = new OpenLayers.Strategy.Refresh({interval: interval});
+ s.setLayer(l);
+
+ t.eq(s.active, false, "not active after construction");
+
+ var activated = s.activate();
+ t.eq(activated, true, "activate returns true");
+ t.eq(s.active, true, "activated after activate");
+ t.ok(l.events.listeners.visibilitychanged[0].obj == s &&
+ l.events.listeners.visibilitychanged[0].func == s.reset,
+ "activates registers visibilitychanged listener");
+ t.ok(s.timer !== null, "timer should be set on activate if layer is visible");
+
+ // reset the timer!!
+ s.stop();
+ }
+
+ function test_events(t) {
+
+ t.plan(1);
+ var log = {
+ visibilitychanged: 0
+ };
+
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector(null, {
+ strategies: [new OpenLayers.Strategy.Refresh({interval: interval})],
+ protocol: new OpenLayers.Protocol({
+ read: function(config) {
+ config.callback.call(config.scope, {});
+ }
+ }),
+ isBaseLayer: true,
+ eventListeners: {
+ visibilitychanged: function() {
+ ++log.visibilitychanged;
+ }
+ }
+ });
+ map.addLayer(layer);
+
+ layer.setVisibility(false);
+ t.eq(log.visibilitychanged, 1, "visibilitychanged triggered");
+
+ map.destroy();
+
+ }
+
+ function test_refreshWithNormalProgress(t) {
+
+ t.plan(1);
+ var log = {
+ refreshcalled: 0
+ };
+
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector(null, {
+ strategies: [new OpenLayers.Strategy.Refresh({
+ interval: interval,
+ refresh: function() {
+ if (this.layer && this.layer.refresh) {
+ ++log.refreshcalled;
+ }
+ }
+ })],
+ protocol: new OpenLayers.Protocol({
+ read: function(config) {
+ config.callback.call(config.scope, {});
+ }
+ }),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+
+ t.delay_call((5 * (interval / 1000)) + 0.5, function() {
+ t.eq(log.refreshcalled, 5, "number of refreshes");
+ map.destroy();
+ });
+
+ }
+
+ function test_refreshWithSwitchingVisibility(t) {
+
+ t.plan(1);
+ var log = {
+ refreshcalled: 0
+ };
+
+ var map = new OpenLayers.Map("map");
+ var layer = new OpenLayers.Layer.Vector(null, {
+ strategies: [new OpenLayers.Strategy.Refresh({
+ interval: interval,
+ refresh: function() {
+ if (this.layer && this.layer.refresh) {
+ ++log.refreshcalled;
+ }
+ }
+ })],
+ protocol: new OpenLayers.Protocol({
+ read: function(config) {
+ config.callback.call(config.scope, {});
+ }
+ }),
+ isBaseLayer: true
+ });
+ map.addLayer(layer);
+
+ window.setTimeout(function() {
+ layer.setVisibility(false);
+ }, 2.5 * interval);
+
+ t.delay_call((5 * (interval / 1000)) + 0.5, function() {
+ t.eq(log.refreshcalled, 2, "number of refreshes");
+ map.destroy();
+ });
+
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 200px;"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Strategy/Save.html b/misc/openlayers/tests/Strategy/Save.html
new file mode 100644
index 0000000..1290485
--- /dev/null
+++ b/misc/openlayers/tests/Strategy/Save.html
@@ -0,0 +1,127 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(1);
+ var strategy = new OpenLayers.Strategy.Save();
+ t.eq(strategy.auto, false, "auto is false by default");
+ }
+
+ function test_activate(t) {
+
+ t.plan(3);
+
+ var strategy = new OpenLayers.Strategy.Save();
+ var layer = new OpenLayers.Layer.Vector(null, {
+ isBaseLayer: true,
+ protocol: new OpenLayers.Protocol(),
+ strategies: [strategy]
+ });
+ var map = new OpenLayers.Map("map");
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ // check that auto true registers listeners
+ strategy.deactivate();
+ strategy.auto = true;
+ strategy.activate();
+ t.ok(layer.events.listeners["featureadded"][0].func === strategy.triggerSave,
+ "[auto true] triggerSave registered as listener for featureadded");
+ t.ok(layer.events.listeners["afterfeaturemodified"][0].func === strategy.triggerSave,
+ "[auto true] triggerSave registered as listener for afterfeaturemodified");
+
+ // check that auto can be set to interval
+ strategy.deactivate();
+ strategy.auto = 1;
+ strategy.activate();
+ t.ok(strategy.timer != null, "[auto number] timer set")
+
+ map.destroy();
+
+ }
+
+ function test_save(t) {
+ t.plan(9);
+
+ var strategy = new OpenLayers.Strategy.Save();
+
+ // mock up a protocol for synchronous and successful commits
+ var protocol = new OpenLayers.Protocol({
+ commit: function(features, options) {
+ var response = new OpenLayers.Protocol.Response();
+ response.reqFeatures = features;
+ response.insertIds = [];
+ for(var i=0; i<features.length; ++i) {
+ // test feature.url first
+ t.eq(features[i].url, "url" + i,
+ "feature.url correctly set (url" + i + ")");
+ if(features[i].state == OpenLayers.State.INSERT) {
+ response.insertIds.push("new_" + i);
+ }
+ }
+ response.code = OpenLayers.Protocol.Response.SUCCESS;
+ options.callback.call(options.scope, response);
+ }
+ });
+
+ var layer = new OpenLayers.Layer.Vector(null, {
+ isBaseLayer: true,
+ protocol: protocol,
+ strategies: [strategy],
+ projection: "EPSG:4326"
+ });
+ var map = new OpenLayers.Map("map", {
+ getProjectionObject: function() {
+ return new OpenLayers.Projection("EPSG:900913");
+ }
+ })
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+
+ // give the layer some features
+ var features = [
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(5, 45)
+ ), // insert
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(6, 46)
+ ), // delete
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(7, 47)
+ ), // update
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(8, 48)
+ ) // nothing
+ ];
+ features[0].state = OpenLayers.State.INSERT;
+ features[0].url = "url0";
+ features[1].state = OpenLayers.State.DELETE;
+ features[1].url = "url1";
+ features[2].state = OpenLayers.State.UPDATE;
+ features[2].url = "url2";
+ features[3].url = "url3";
+ layer.addFeatures(features);
+
+ // save feature modifications
+ strategy.save(features);
+
+ // confirm that newly created feature has an id and no longer has insert state
+ t.eq(features[0].fid, "new_0", "newly created feature gets fid");
+ t.ok(features[0].state == null, "newly created feature no longer insert state");
+
+ // confirm that deleted features are not on layer
+ t.eq(layer.features.length, 3, "layer only has three features");
+ t.ok(features[1].layer == null, "deleted feature has no layer");
+
+ // confirm that updated feature no longer has update state
+ t.ok(features[2].state == null, "updated feature no longer update state");
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 400px; height: 200px" />
+</body>
+</html>
diff --git a/misc/openlayers/tests/Style.html b/misc/openlayers/tests/Style.html
new file mode 100644
index 0000000..0b8b33b
--- /dev/null
+++ b/misc/openlayers/tests/Style.html
@@ -0,0 +1,426 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_Style_constructor(t) {
+ t.plan(6);
+
+ var rules = [
+ new OpenLayers.Rule({
+ symbolizer: {fillColor: "red"},
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "type",
+ value: "fire engine"
+ })
+ }),
+ new OpenLayers.Rule({
+ symbolizer: {fillColor: "yellow"},
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "type",
+ value: "sports car"
+ })
+ })
+ ];
+ var style = new OpenLayers.Style(null, {
+ foo: "bar",
+ rules: rules
+ });
+ t.ok(style instanceof OpenLayers.Style,
+ "new OpenLayers.Style returns object" );
+ t.eq(style.foo, "bar", "constructor sets options correctly");
+ t.eq(style.rules.length, 2, "correct number of rules added");
+ t.ok(style.rules[0] === rules[0], "correct first rule added");
+ t.ok(style.rules[1] === rules[1], "correct second rule added");
+ t.eq(typeof style.createSymbolizer, "function", "style has a createSymbolizer function");
+ }
+
+ function test_Style_create(t) {
+ t.plan(10);
+
+ var map = new OpenLayers.Map("map");
+
+ var layer = new OpenLayers.Layer.Vector("layer");
+
+ var baseStyle = OpenLayers.Util.extend(
+ OpenLayers.Feature.Vector.style["default"],
+ {externalGraphic: "bar${foo}.png"}
+ );
+
+ var style = new OpenLayers.Style(baseStyle);
+
+ var rule1 = new OpenLayers.Rule({
+ symbolizer: {"Point": {fillColor: "green"}},
+ maxScaleDenominator: 500000,
+ filter: new OpenLayers.Filter.FeatureId({
+ fids: ["1"]
+ })
+ });
+ var rule2 = new OpenLayers.Rule({
+ symbolizer: {"Point": {fillColor: "yellow"}},
+ minScaleDenominator: 500000,
+ maxScaleDenominator: 1000000,
+ filter: new OpenLayers.Filter.FeatureId({
+ fids: ["1"]
+ })
+ });
+ var rule3 = new OpenLayers.Rule({
+ symbolizer: {"Point": {fillColor: "red"}},
+ minScaleDenominator: 1000000,
+ maxScaleDenominator: 2500000,
+ filter: new OpenLayers.Filter.FeatureId({
+ fids: ["1"]
+ })
+ });
+
+ var feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(3,5),
+ {"foo": "bar"}
+ );
+
+ feature.fid = "1";
+ // for this fid, the above rule should apply
+
+ layer.styleMap = new OpenLayers.StyleMap(style);
+
+ layer.addFeatures([feature]);
+ map.addLayer(layer);
+ map.setBaseLayer(layer);
+
+ map.setCenter(new OpenLayers.LonLat(3,5), 10);
+
+ var createdStyle = style.createSymbolizer(feature);
+ t.eq(createdStyle.externalGraphic, "barbar.png", "Calculated property style for default symbolizer correctly.");
+
+ style.addRules([rule1, rule2, rule3]);
+ createdStyle = style.createSymbolizer(feature);
+
+ // at this scale, the feature should be green
+ t.eq(createdStyle.display, undefined, "Feature is visible at scale "+map.getScale());
+ t.eq(createdStyle.fillColor, "green", "Point symbolizer from rule applied correctly.");
+
+ map.setCenter(new OpenLayers.LonLat(3,5), 9);
+ // at this scale, the feature should be red
+ createdStyle = style.createSymbolizer(feature);
+ t.eq(createdStyle.display, undefined, "Feature is visible at scale "+map.getScale());
+ t.eq(createdStyle.fillColor, "yellow", "Point symbolizer from rule applied correctly.");
+
+ map.setCenter(new OpenLayers.LonLat(3,5), 8);
+ // at this scale, the feature should be yellow
+ createdStyle = style.createSymbolizer(feature);
+ t.eq(createdStyle.display, undefined, "Feature is visible at scale "+map.getScale());
+ t.eq(createdStyle.fillColor, "red", "Point symbolizer from rule applied correctly.");
+
+ map.setCenter(new OpenLayers.LonLat(3,5), 7);
+ // at this scale, the feature should be invisible
+ createdStyle = style.createSymbolizer(feature);
+ t.eq(createdStyle.display, "none", "Feature is invisible at scale "+map.getScale());
+ t.eq(createdStyle.fillColor, baseStyle.fillColor, "Point symbolizer from base style applied correctly.");
+
+ feature.fid = "2";
+ // now the rule should not apply
+
+ createdStyle = style.createSymbolizer(feature);
+ t.eq(createdStyle.fillColor, baseStyle.fillColor, "Correct style for rule that does not apply to fid=\"2\".");
+ }
+
+ function test_Style_createSymbolizer(t) {
+ t.plan(5);
+ var style = new OpenLayers.Style();
+
+ // override applySymbolizer to log arguments
+ var log = [];
+ style.applySymbolizer = function(r) {
+ log.push(r);
+ OpenLayers.Style.prototype.applySymbolizer.apply(this, arguments);
+ };
+
+ // rules for the style
+ var rule = new OpenLayers.Rule({
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "foo",
+ value: "bar"
+ }),
+ symbolizer: {
+ label: "${labelValue}"
+ }
+ });
+ rule.id = "foo=bar rule";
+ var elseRule = new OpenLayers.Rule({
+ elseFilter: true,
+ symbolizer: {
+ label: "${labelValue}"
+ }
+ });
+ elseRule.id = "else rule";
+ style.addRules([rule, elseRule]);
+
+ // a) test that applySymbolizer is only called with rule
+ log = [];
+ style.createSymbolizer(
+ new OpenLayers.Feature.Vector(null, {foo: "bar"})
+ );
+ t.eq(log.length, 1, "a) applySymbolizer called once");
+ t.eq(log[0] && log[0].id, rule.id, "a) applySymbolizer called with correct rule");
+
+ // b) test that applySymbolizer is only called with elseRule
+ log = [];
+ style.createSymbolizer(
+ new OpenLayers.Feature.Vector(null, {foo: "baz"})
+ );
+ t.eq(log.length, 1, "b) applySymbolizer called once");
+ t.eq(log[0] && log[0].id, elseRule.id, "b) applySymbolizer called with correct rule");
+
+ // c) test that label in returned symbolizer is a string even if property value is a number
+ var symbolizer = style.createSymbolizer(
+ new OpenLayers.Feature.Vector(null, {foo: "bar", labelValue: 0})
+ );
+ t.eq(symbolizer.label, "0", "c) feature property cast to string when used as symbolizer label");
+
+ }
+
+ function test_Style_applySymbolizer(t) {
+ t.plan(15);
+
+ var feature = new OpenLayers.Feature.Vector();
+ var defaults = OpenLayers.Feature.Vector.style["default"];
+ var style, symbolizer;
+
+ style = new OpenLayers.Style();
+ symbolizer = style.createSymbolizer(feature);
+ t.eq(symbolizer.pointRadius, defaults.pointRadius, "symbolizer has the correct pointRadius");
+ t.eq(symbolizer.strokeWidth, defaults.strokeWidth, "symbolizer has the correct strokeWidth");
+ t.eq(symbolizer.fillColor, defaults.fillColor, "symbolizer has the correct fillColor");
+ t.eq(symbolizer.graphicName, defaults.graphicName, "symbolizer has the correct graphicName");
+
+ style = new OpenLayers.Style(null, {
+ defaultsPerSymbolizer: true,
+ rules: [
+ new OpenLayers.Rule({
+ symbolizer: {
+ stroke: true
+ }
+ })
+ ]
+ });
+ symbolizer = style.createSymbolizer(feature);
+ t.eq(symbolizer.strokeWidth, defaults.strokeWidth, "symbolizer has the correct strokeWidth");
+ t.ok(symbolizer.fillColor == undefined, "fillColor is undefined");
+
+ style = new OpenLayers.Style(null, {
+ defaultsPerSymbolizer: true,
+ rules: [
+ new OpenLayers.Rule({
+ symbolizer: {
+ }
+ })
+ ]
+ });
+ symbolizer = style.createSymbolizer(feature);
+ t.eq(symbolizer.pointRadius, defaults.pointRadius, "symbolizer has the correct pointRadius");
+ t.ok(symbolizer.strokeWidth == undefined, "strokeWidth is undefined");
+ t.ok(symbolizer.fillColor == undefined, "fillColor is undefined");
+ t.ok(symbolizer.graphicName == undefined, "graphicName is undefined");
+
+ style = new OpenLayers.Style(null, {
+ defaultsPerSymbolizer: true,
+ rules: [
+ new OpenLayers.Rule({
+ symbolizer: {
+ stroke: true
+ }
+ })
+ ]
+ });
+ symbolizer = style.createSymbolizer(feature);
+ t.eq(symbolizer.strokeWidth, defaults.strokeWidth, "symbolizer has the correct strokeWidth");
+ t.ok(symbolizer.fillColor == undefined, "fillColor is undefined");
+
+ style = new OpenLayers.Style(null, {
+ defaultsPerSymbolizer: true,
+ rules: [
+ new OpenLayers.Rule({
+ symbolizer: {
+ fill: true
+ }
+ })
+ ]
+ });
+ symbolizer = style.createSymbolizer(feature);
+ t.eq(symbolizer.fillColor, defaults.fillColor, "symbolizer has the correct fillColor");
+ t.ok(symbolizer.strokeWidth == undefined, "strokeWidth is undefined");
+
+ style = new OpenLayers.Style(null, {
+ defaultsPerSymbolizer: true,
+ rules: [
+ new OpenLayers.Rule({
+ symbolizer: {
+ graphic: true
+ }
+ })
+ ]
+ });
+ symbolizer = style.createSymbolizer(feature);
+ t.eq(symbolizer.graphicName, defaults.graphicName, "symbolizer has the correct graphicName");
+ }
+
+ function test_Style_context(t) {
+ t.plan(4);
+ var rule = new OpenLayers.Rule({
+ symbolizer: {"Point": {externalGraphic: "${img1}"}},
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LESS_THAN,
+ property: "size",
+ value: 11
+ })
+ });
+ var style = new OpenLayers.Style();
+ style.context = {
+ "img1": "myImage.png"
+ };
+ style.addRules([rule]);
+ var feature = new OpenLayers.Feature.Vector();
+ feature.attributes = {size: 10};
+ var styleHash = style.createSymbolizer(feature);
+ t.eq(styleHash.externalGraphic, "myImage.png", "correctly evaluated rule and calculated property styles from a custom context");
+
+ // same as above, but without rule (#1526)
+ style = new OpenLayers.Style(
+ {externalGraphic: "${getExternalGraphic}"},
+ {context: {
+ getExternalGraphic: function(feature) {
+ return "foo" + feature.attributes.size + ".png";
+ }
+ }});
+ t.eq(style.createSymbolizer(feature).externalGraphic, "foo10.png", "correctly evaluated symbolizer without rule");
+
+ style = new OpenLayers.Style(
+ {externalGraphic: "${getExternalGraphic}",
+ pointRadius: "${size}"},
+ {context: {
+ getExternalGraphic: function(feature) {
+ return "foo" + feature.attributes.size + ".png";
+ }
+ }});
+ t.eq(style.createSymbolizer(feature).externalGraphic, "foo10.png", "correctly evaluated symbolizer from context");
+ t.eq(style.createSymbolizer(feature).pointRadius, 10, "correctly evaluated symbolizer from attributes");
+
+ };
+
+ function test_Style_findPropertyStyles(t) {
+ t.plan(4);
+ var rule1 = new OpenLayers.Rule({symbolizer: {
+ pointRadius: 3,
+ externalGraphic: "${foo}.bar"
+ }});
+ var rule2 = new OpenLayers.Rule({symbolizer: {"Point": {
+ strokeWidth: "${foo}"
+ }}});
+ var style = new OpenLayers.Style({
+ strokeOpacity: 1,
+ strokeColor: "${foo}"
+ });
+ style.addRules([rule1, rule2]);
+ var propertyStyles = style.findPropertyStyles();
+ t.ok(propertyStyles.externalGraphic, "detected externalGraphic from rule correctly");
+ t.ok(propertyStyles.strokeWidth, "detected strokeWidth from Point symbolizer correctly");
+ t.ok(propertyStyles.strokeColor, "detected strokeColor from style correctly");
+ t.eq(typeof propertyStyles.pointRadius, "undefined", "correctly detected pointRadius as non-property style");
+ }
+
+ function test_createLiteral(t) {
+ t.plan(6);
+
+ var value, context, feature, result, expected;
+ var func = OpenLayers.Style.createLiteral;
+
+ // without templates
+ value = "foo";
+ expected = value;
+ result = func(value);
+ t.eq(result, expected, "(no template) preserves literal");
+
+ // with templates
+ value = "${foo}"
+ expected = "bar";
+ context = {foo: expected};
+ result = func(value, context);
+ t.eq(result, expected, "(template) preserves literal");
+
+ expected = "";
+ context = {foo: expected};
+ result = func(value, context);
+ t.eq(result, expected, "(template) preserves empty string");
+
+ expected = "16/03/2008";
+ context = {foo: expected};
+ result = func(value, context);
+ t.eq(result, expected, "(template) preserves string with numbers");
+
+ expected = 16;
+ context = {foo: expected + ""};
+ result = func(value, context);
+ t.eq(result, expected, "(template) casts integer in a string");
+
+ expected = 16;
+ context = {foo: " " + expected + " "};
+ result = func(value, context);
+ t.eq(result, expected, "(template) casts integer in a space padded string");
+
+ }
+
+ function test_clone(t) {
+
+ t.plan(7);
+
+ var style = new OpenLayers.Style({bar: "baz"}, {
+ name: "test style",
+ rules: [new OpenLayers.Rule({
+ name: "test rule"
+ })],
+ context: {
+ foo: "bar"
+ }
+ });
+ var clone = style.clone();
+ t.eq(clone.name, "test style", "name copied");
+ t.eq(clone.rules[0].name, "test rule", "clone has correct rule");
+
+ // modify original
+ style.name = "new";
+ style.addRules([new OpenLayers.Rule({
+ name: "new rule"
+ })]);
+ style.context.foo = "baz";
+
+ // confirm that clone didn't change
+ t.eq(clone.defaultStyle.bar, "baz", "clone has clone of defaultStyle");
+ t.eq(clone.name, "test style", "clone has clone of name");
+ t.eq(clone.rules.length, 1, "clone has clone of rules");
+ t.eq(clone.context.foo, "bar", "clone has clone of context");
+
+ // confirm that ids are different
+ t.ok(clone.id !== style.id, "clone has different id");
+
+ style.destroy();
+ clone.destroy();
+
+ }
+
+ function test_Style_destroy(t) {
+ t.plan(1);
+
+ var style = new OpenLayers.Style();
+ style.destroy();
+ t.eq(style.rules, null, "rules array nulled properly");
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width:500px;height:500px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Style2.html b/misc/openlayers/tests/Style2.html
new file mode 100644
index 0000000..87ab584
--- /dev/null
+++ b/misc/openlayers/tests/Style2.html
@@ -0,0 +1,56 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(4);
+
+ var rules = [
+ new OpenLayers.Rule({
+ symbolizer: {fillColor: "red"},
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "type",
+ value: "fire engine"
+ })
+ }),
+ new OpenLayers.Rule({
+ symbolizer: {fillColor: "yellow"},
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: "type",
+ value: "sports car"
+ })
+ })
+ ];
+ var style = new OpenLayers.Style2({rules: rules});
+ t.ok(style instanceof OpenLayers.Style2, "correct type");
+ t.eq(style.rules.length, 2, "correct number of rules added");
+ t.ok(style.rules[0] === rules[0], "correct first rule added");
+ t.ok(style.rules[1] === rules[1], "correct second rule added");
+ }
+
+ function test_destroy(t) {
+ t.plan(1);
+
+ var style = new OpenLayers.Style2({
+ rules: [
+ new OpenLayers.Rule({
+ symbolizers: [
+ new OpenLayers.Symbolizer.Point({
+ fillColor: "fuchsia"
+ })
+ ]
+ })
+ ]
+ });
+ style.destroy();
+ t.ok(!style.rules, "rules array gone");
+ }
+
+
+ </script>
+</head>
+<body></body>
+</html>
diff --git a/misc/openlayers/tests/StyleMap.html b/misc/openlayers/tests/StyleMap.html
new file mode 100644
index 0000000..6c633c3
--- /dev/null
+++ b/misc/openlayers/tests/StyleMap.html
@@ -0,0 +1,44 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_StyleMap_constructor(t) {
+ t.plan(6);
+
+ var options = {'foo': 'bar'};
+ var styleMap = new OpenLayers.StyleMap(null, options);
+ t.ok(styleMap instanceof OpenLayers.StyleMap,
+ "new OpenLayers.StyleMap returns object" );
+ t.eq(styleMap.foo, "bar", "constructor sets options correctly");
+
+ var style = new OpenLayers.Style();
+ var styleMap = new OpenLayers.StyleMap(style);
+ t.eq(styleMap.styles["default"].defaultStyle.strokeColor, style.defaultStyle.strokeColor, "default style set correctly from style object");
+
+ var style = {strokeColor: "blue"};
+ var styleMap = new OpenLayers.StyleMap(style);
+ t.eq(styleMap.styles["default"].defaultStyle.strokeColor, "blue", "default style set correctly from style hash");
+
+ var style = {
+ "default": new OpenLayers.Style({strokeColor: "yellow"}),
+ "select": {strokeColor: "blue"}};
+ var styleMap = new OpenLayers.StyleMap(style);
+ t.eq(styleMap.styles["default"].defaultStyle.strokeColor, "yellow", "default style set correctly from a mixed hash of renderIntents");
+ t.eq(styleMap.styles["select"].defaultStyle.strokeColor, "blue", "select style set correctly from a mixed hash of renderIntents");
+ }
+
+ function test_StyleMap_destroy(t) {
+ t.plan(2);
+ var styleMap = new OpenLayers.StyleMap();
+ t.ok(styleMap.styles["default"], "Got a default style after initialisation");
+ styleMap.destroy();
+ t.ok(!styleMap.styles, "StyleMap styles successfully destroyed");
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width:500px;height:500px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Symbolizer.html b/misc/openlayers/tests/Symbolizer.html
new file mode 100644
index 0000000..be24e9c
--- /dev/null
+++ b/misc/openlayers/tests/Symbolizer.html
@@ -0,0 +1,31 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(2);
+
+ var symbolizer = new OpenLayers.Symbolizer({foo: "bar"});
+
+ t.ok(symbolizer instanceof OpenLayers.Symbolizer, "correct type");
+ t.eq(symbolizer.foo, "bar", "constructor applies config properties");
+
+ }
+
+ function test_clone(t) {
+ t.plan(2);
+
+ var symbolizer = new OpenLayers.Symbolizer({foo: "bar"});
+ var clone = symbolizer.clone();
+
+ t.ok(clone instanceof OpenLayers.Symbolizer, "correct type");
+ t.eq(clone.foo, "bar", "clone copies properties");
+
+ }
+
+
+ </script>
+</head>
+<body></body>
+</html>
diff --git a/misc/openlayers/tests/Symbolizer/Line.html b/misc/openlayers/tests/Symbolizer/Line.html
new file mode 100644
index 0000000..5396f3b
--- /dev/null
+++ b/misc/openlayers/tests/Symbolizer/Line.html
@@ -0,0 +1,42 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(3);
+
+ var symbolizer = new OpenLayers.Symbolizer.Line({foo: "bar"});
+
+ t.ok(symbolizer instanceof OpenLayers.Symbolizer, "instance of OpenLayers.Symbolizer");
+ t.ok(symbolizer instanceof OpenLayers.Symbolizer.Line, "instance of OpenLayers.Symbolizer.Line");
+ t.eq(symbolizer.foo, "bar", "constructor applies config properties");
+
+ }
+
+ function test_clone(t) {
+ t.plan(2);
+
+ var symbolizer = new OpenLayers.Symbolizer.Line({foo: "bar"});
+ var clone = symbolizer.clone();
+
+ t.ok(clone instanceof OpenLayers.Symbolizer.Line, "correct type");
+ t.eq(clone.foo, "bar", "clone copies properties");
+
+ }
+
+ function test_defaults(t) {
+ t.plan(5);
+ var symbolizer = new OpenLayers.Symbolizer.Line();
+ t.ok(symbolizer.strokeColor === undefined, "no default strokeColor");
+ t.ok(symbolizer.strokeOpacity === undefined, "no default strokeOpacity");
+ t.ok(symbolizer.strokeWidth === undefined, "no default strokeWidth");
+ t.ok(symbolizer.strokeLinecap === undefined, "no default strokeLinecap");
+ t.ok(symbolizer.strokeDashstyle === undefined, "no default strokeDashstyle");
+ }
+
+
+ </script>
+</head>
+<body></body>
+</html>
diff --git a/misc/openlayers/tests/Symbolizer/Point.html b/misc/openlayers/tests/Symbolizer/Point.html
new file mode 100644
index 0000000..b1311c0
--- /dev/null
+++ b/misc/openlayers/tests/Symbolizer/Point.html
@@ -0,0 +1,52 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(3);
+
+ var symbolizer = new OpenLayers.Symbolizer.Point({foo: "bar"});
+
+ t.ok(symbolizer instanceof OpenLayers.Symbolizer, "instance of OpenLayers.Symbolizer");
+ t.ok(symbolizer instanceof OpenLayers.Symbolizer.Point, "instance of OpenLayers.Symbolizer.Point");
+ t.eq(symbolizer.foo, "bar", "constructor applies config properties");
+
+ }
+
+ function test_clone(t) {
+ t.plan(2);
+
+ var symbolizer = new OpenLayers.Symbolizer.Point({foo: "bar"});
+ var clone = symbolizer.clone();
+
+ t.ok(clone instanceof OpenLayers.Symbolizer.Point, "correct type");
+ t.eq(clone.foo, "bar", "clone copies properties");
+
+ }
+
+ function test_defaults(t) {
+ t.plan(16);
+ var symbolizer = new OpenLayers.Symbolizer.Point();
+ t.ok(symbolizer.strokeColor === undefined, "no default strokeColor");
+ t.ok(symbolizer.strokeOpacity === undefined, "no default strokeOpacity");
+ t.ok(symbolizer.strokeWidth === undefined, "no default strokeWidth");
+ t.ok(symbolizer.strokeLinecap === undefined, "no default strokeLinecap");
+ t.ok(symbolizer.strokeDashstyle === undefined, "no default strokeDashstyle");
+ t.ok(symbolizer.fillColor === undefined, "no default fillColor");
+ t.ok(symbolizer.fillOpacity === undefined, "no default fillOpacity");
+ t.ok(symbolizer.pointRadius === undefined, "no default pointRadius");
+ t.ok(symbolizer.externalGraphic === undefined, "no default externalGraphic");
+ t.ok(symbolizer.graphicWidth === undefined, "no default graphicWidth");
+ t.ok(symbolizer.graphicHeight === undefined, "no default graphicHeight");
+ t.ok(symbolizer.graphicOpacity === undefined, "no default graphicOpacity");
+ t.ok(symbolizer.graphicXOffset === undefined, "no default graphicXOffset");
+ t.ok(symbolizer.graphicYOffset === undefined, "no default graphicYOffset");
+ t.ok(symbolizer.rotation === undefined, "no default rotation");
+ t.ok(symbolizer.graphicName === undefined, "no default graphicName");
+ }
+
+ </script>
+</head>
+<body></body>
+</html>
diff --git a/misc/openlayers/tests/Symbolizer/Polygon.html b/misc/openlayers/tests/Symbolizer/Polygon.html
new file mode 100644
index 0000000..ebea5ea
--- /dev/null
+++ b/misc/openlayers/tests/Symbolizer/Polygon.html
@@ -0,0 +1,44 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(3);
+
+ var symbolizer = new OpenLayers.Symbolizer.Polygon({foo: "bar"});
+
+ t.ok(symbolizer instanceof OpenLayers.Symbolizer, "instance of OpenLayers.Symbolizer");
+ t.ok(symbolizer instanceof OpenLayers.Symbolizer.Polygon, "instance of OpenLayers.Symbolizer.Polygon");
+ t.eq(symbolizer.foo, "bar", "constructor applies config properties");
+
+ }
+
+ function test_clone(t) {
+ t.plan(2);
+
+ var symbolizer = new OpenLayers.Symbolizer.Polygon({foo: "bar"});
+ var clone = symbolizer.clone();
+
+ t.ok(clone instanceof OpenLayers.Symbolizer.Polygon, "correct type");
+ t.eq(clone.foo, "bar", "clone copies properties");
+
+ }
+
+ function test_defaults(t) {
+ t.plan(7);
+ var symbolizer = new OpenLayers.Symbolizer.Polygon();
+ t.ok(symbolizer.strokeColor === undefined, "no default strokeColor");
+ t.ok(symbolizer.strokeOpacity === undefined, "no default strokeOpacity");
+ t.ok(symbolizer.strokeWidth === undefined, "no default strokeWidth");
+ t.ok(symbolizer.strokeLinecap === undefined, "no default strokeLinecap");
+ t.ok(symbolizer.strokeDashstyle === undefined, "no default strokeDashstyle");
+ t.ok(symbolizer.fillColor === undefined, "no default fillColor");
+ t.ok(symbolizer.fillOpacity === undefined, "no default fillOpacity");
+ }
+
+
+ </script>
+</head>
+<body></body>
+</html>
diff --git a/misc/openlayers/tests/Symbolizer/Raster.html b/misc/openlayers/tests/Symbolizer/Raster.html
new file mode 100644
index 0000000..8dd9cb9
--- /dev/null
+++ b/misc/openlayers/tests/Symbolizer/Raster.html
@@ -0,0 +1,32 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(3);
+
+ var symbolizer = new OpenLayers.Symbolizer.Raster({foo: "bar"});
+
+ t.ok(symbolizer instanceof OpenLayers.Symbolizer, "instance of OpenLayers.Symbolizer");
+ t.ok(symbolizer instanceof OpenLayers.Symbolizer.Raster, "instance of OpenLayers.Symbolizer.Raster");
+ t.eq(symbolizer.foo, "bar", "constructor applies config properties");
+
+ }
+
+ function test_clone(t) {
+ t.plan(2);
+
+ var symbolizer = new OpenLayers.Symbolizer.Raster({foo: "bar"});
+ var clone = symbolizer.clone();
+
+ t.ok(clone instanceof OpenLayers.Symbolizer.Raster, "correct type");
+ t.eq(clone.foo, "bar", "clone copies properties");
+
+ }
+
+
+ </script>
+</head>
+<body></body>
+</html>
diff --git a/misc/openlayers/tests/Symbolizer/Text.html b/misc/openlayers/tests/Symbolizer/Text.html
new file mode 100644
index 0000000..a849f20
--- /dev/null
+++ b/misc/openlayers/tests/Symbolizer/Text.html
@@ -0,0 +1,42 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_constructor(t) {
+ t.plan(3);
+
+ var symbolizer = new OpenLayers.Symbolizer.Text({foo: "bar"});
+
+ t.ok(symbolizer instanceof OpenLayers.Symbolizer, "instance of OpenLayers.Symbolizer");
+ t.ok(symbolizer instanceof OpenLayers.Symbolizer.Text, "instance of OpenLayers.Symbolizer.Text");
+ t.eq(symbolizer.foo, "bar", "constructor applies config properties");
+
+ }
+
+ function test_clone(t) {
+ t.plan(2);
+
+ var symbolizer = new OpenLayers.Symbolizer.Text({foo: "bar"});
+ var clone = symbolizer.clone();
+
+ t.ok(clone instanceof OpenLayers.Symbolizer.Text, "correct type");
+ t.eq(clone.foo, "bar", "clone copies properties");
+
+ }
+
+ function test_defaults(t) {
+ t.plan(5);
+ var symbolizer = new OpenLayers.Symbolizer.Point();
+ t.ok(symbolizer.label === undefined, "no default label");
+ t.ok(symbolizer.fontFamily === undefined, "no default fontFamily");
+ t.ok(symbolizer.fontSize === undefined, "no default fontSize");
+ t.ok(symbolizer.fontWeight === undefined, "no default fontWeight");
+ t.ok(symbolizer.fontStyle === undefined, "no default fontStyle");
+ }
+
+
+ </script>
+</head>
+<body></body>
+</html>
diff --git a/misc/openlayers/tests/Test.AnotherWay.baseadditions.js b/misc/openlayers/tests/Test.AnotherWay.baseadditions.js
new file mode 100644
index 0000000..338bf82
--- /dev/null
+++ b/misc/openlayers/tests/Test.AnotherWay.baseadditions.js
@@ -0,0 +1,191 @@
+// total counters
+Test.AnotherWay._openlayers_sum_total_detail_ok=0;
+Test.AnotherWay._openlayers_sum_total_detail_fail=0;
+Test.AnotherWay._startTime = null;
+
+// method overwrites
+//
+// behaviour (timing)
+Test.AnotherWay._old_run_all_onclick = Test.AnotherWay._run_all_onclick;
+Test.AnotherWay._run_all_onclick = function(){
+ Test.AnotherWay._startTime = (new Date()).getTime();
+ Test.AnotherWay.reset_running_time();
+ Test.AnotherWay._old_run_all_onclick.apply(this, arguments);
+};
+
+Test.AnotherWay._old_run_selected_onclick = Test.AnotherWay._run_selected_onclick;
+Test.AnotherWay._run_selected_onclick = function(){
+ Test.AnotherWay._startTime = (new Date()).getTime();
+ Test.AnotherWay.reset_running_time();
+ Test.AnotherWay._old_run_selected_onclick.apply(this, arguments);
+};
+
+Test.AnotherWay._old_run_one_onclick = Test.AnotherWay._run_one_onclick;
+Test.AnotherWay._run_one_onclick = function(){
+ Test.AnotherWay._startTime = (new Date()).getTime();
+ Test.AnotherWay.reset_running_time();
+ Test.AnotherWay._old_run_one_onclick.apply(this, arguments);
+};
+
+// test page loading
+Test.AnotherWay.old_load_next_page = Test.AnotherWay._load_next_page;
+Test.AnotherWay._load_next_page = function(){
+ document.getElementById("test_iframe_el").style.display = "none";
+ Test.AnotherWay.update_running_time();
+ Test.AnotherWay.old_load_next_page.apply(this, arguments);
+};
+
+
+Test.AnotherWay._add_test_page_url = function(test_url, convention){
+ var table = document.getElementById("testtable");
+ var record_select = document.getElementById("record_select");
+ var index = Test.AnotherWay._g_test_page_urls.length;
+
+ // trim spaces.
+ if (test_url.match("^(\\s*)(.*\\S)(\\s*)$")) {
+ test_url = RegExp.$2;
+ }
+
+ Test.AnotherWay._g_test_page_urls[index] = {
+ url: test_url,
+ convention: convention
+ };
+ var row = table.insertRow(-1);
+
+ var cell;
+ var cell_child;
+ var link;
+
+ cell = row.insertCell(-1);
+ cell_child = document.createElement("input");
+ cell_child.type = "checkbox";
+ cell_child.id = "checkbox" + index;
+ cell_child.checked = 'checked';
+ cell_child.defaultChecked = 'checked';
+ cell.appendChild(cell_child);
+
+ cell = row.insertCell(-1);
+ cell.setAttribute("width", "75%");
+
+ // make the URL a clickable link that opens in a new window
+ // start changes
+ link = document.createElement("a");
+ link.href=test_url;
+ link.target='_blank';
+ link.title='Opens testfile in a new window.';
+ link.appendChild(document.createTextNode(test_url));
+ cell.appendChild(link);
+ // end changes
+
+ cell = row.insertCell(-1);
+ cell_child = document.createElement("input");
+ cell_child.type = "button";
+ cell_child.id = "test" + index;
+ cell_child.value = " run ";
+ cell_child.onclick = Test.AnotherWay._run_one_onclick;
+ cell.appendChild(cell_child);
+
+ cell = row.insertCell(-1);
+ cell.setAttribute("width", "8em");
+ cell_child = document.createElement("span");
+ cell.appendChild(cell_child);
+
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(test_url));
+ record_select.appendChild(option);
+};
+
+Test.AnotherWay.old_set_iframe_location = Test.AnotherWay._set_iframe_location;
+Test.AnotherWay._set_iframe_location = function(iframe, loc, outside_path_correction){
+ var optionPos = loc.indexOf( "?" ),
+ option;
+ if (optionPos != -1) {
+ option = loc.substring(optionPos+1);
+ loc = loc.substring(0, optionPos);
+ }
+ if (option === "visible") {
+ document.getElementById("test_iframe_el").style.display = "";
+ }
+ return Test.AnotherWay.old_set_iframe_location.call(this, iframe, loc, outside_path_correction);
+};
+
+// new methods
+Test.AnotherWay.update_running_time = function() {
+ var now = (new Date()).getTime();
+ var floor = Math.floor;
+ var elapsed = now - Test.AnotherWay._startTime;
+ var zeroPad = function(num, length){
+ var len = -1 * (length || 2);
+ return ('00000' + num).slice(len);
+ };
+ var ms = zeroPad(elapsed%1000, 3);
+ var seconds=zeroPad(floor((elapsed/1000)%60));
+ var minutes=zeroPad(floor((elapsed/60000)%60));
+
+ document.getElementById('running-time').innerHTML = 'Elapsed time ' + minutes + ':' + seconds + ':' + ms +' (m:s:ms).';
+};
+
+Test.AnotherWay.reset_running_time = function(){
+ document.getElementById('running-time').innerHTML = '';
+};
+
+// quickfilter
+Test.AnotherWay.bindQuicksearchListener = function(){
+ var input = document.getElementById('quickfilter');
+ if (input.addEventListener) {
+ input.addEventListener('keyup', Test.AnotherWay.quicksearch);
+ } else if (input.attachEvent) {
+ input.attachEvent('onkeyup', Test.AnotherWay.quicksearch);
+ } else {
+ // remove the input field
+ input.parentNode.removeChild(input);
+ }
+};
+Test.AnotherWay.quicksearchThrottleTimeOut = null;
+Test.AnotherWay.quicksearch = function(){
+ if (Test.AnotherWay.quicksearchThrottleTimeOut) {
+ window.clearTimeout(Test.AnotherWay.quicksearchThrottleTimeOut);
+ }
+ Test.AnotherWay.quicksearchThrottleTimeOut = window.setTimeout(function(){
+ var input = document.getElementById('quickfilter');
+ Test.AnotherWay.filterTestList(input.value);
+ }, 300);
+};
+
+Test.AnotherWay.filterTestList = function(str){
+ Test.AnotherWay.unfilterTestList();
+ var re = new RegExp(str, 'i');
+ var candidates = document.querySelectorAll('#testtable tr a');
+ for (var idx = 0, len = candidates.length; idx<len; idx++) {
+ var tr = candidates[idx].parentNode.parentNode;
+ var html = candidates[idx].innerHTML;
+ if (re.test(html)) {
+ tr.className = 'isShown';
+ } else {
+ tr.className = 'isHidden';
+ }
+ }
+
+};
+
+Test.AnotherWay.unfilterTestList = function() {
+ if ( document.querySelectorAll ) {
+ var hidden = document.querySelectorAll('.isHidden');
+ for (var idx = 0, len = hidden.length; idx < len; idx++) {
+ hidden[idx].className = 'isShown';
+ }
+ }
+};
+
+// bind our quicksearch init method to body onload.
+(function(win) {
+ if (win.addEventListener) {
+ win.addEventListener('load', Test.AnotherWay.bindQuicksearchListener);
+ } else if (win.attachEvent) {
+ win.attachEvent('onload', Test.AnotherWay.bindQuicksearchListener);
+ } else {
+ win.onload = function(){
+ Test.AnotherWay.bindQuicksearchListener();
+ };
+ }
+})(window);
diff --git a/misc/openlayers/tests/Test.AnotherWay.css b/misc/openlayers/tests/Test.AnotherWay.css
new file mode 100644
index 0000000..5bb1181
--- /dev/null
+++ b/misc/openlayers/tests/Test.AnotherWay.css
@@ -0,0 +1,243 @@
+/**
+ * Test.AnotherWay version 0.5
+ *
+ * Copyright (c) 2005 Artem Khodush, http://straytree.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+* {
+ padding: 0;
+ margin: 0;
+}
+
+html {
+ height: 99%;
+}
+
+body {
+ height: 98%;
+ font: normal normal 10pt sans-serif
+}
+
+#col1 {
+ float: left;
+ width: 27em;
+ margin: 0 0 0 1em;
+ overflow: visible;
+}
+
+#col2 {
+ position: relative;
+ height: 98%;
+ margin: 0 0.5em 0 28em;
+}
+
+#col1_header {
+ margin-top: 0.5em;
+}
+
+#scroller {
+ height: 400px;
+ overflow: auto;
+}
+
+#testtable {
+ margin: 0 0 2em 0;
+ width: 97%;
+ font-size: 1em;
+ border-collapse: collapse;
+}
+#testtable input {
+ cursor: pointer;
+}
+#testtable td {
+ line-height: 2em;
+ padding: 0;
+ margin: 0;
+ border-top: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+}
+#testtable tr:hover td {
+ background-color: #ededed;
+}
+#testtable tr.isHidden {
+ display: none;
+}
+#testtable tr.isShown {
+ display: table-row;
+}
+
+#run_buttons, #running-time {
+ margin-bottom: 1em;
+}
+
+#right_header {
+ padding-top: 0.8em;
+}
+
+#results_count {
+ float: left;
+}
+
+#results > p:hover {
+ background-color: #ededed;
+}
+
+.active_tab {
+ float: right;
+ padding: 0 1em 0.2em 1em;
+ background: #0af;
+ border: 1px solid #048;
+ border-bottom: none;
+ cursor: pointer;
+ cursor: hand;
+ position: relative;
+ top: -0.2em;
+}
+
+.inactive_tab {
+ float: right;
+ padding: 0 1em 0 1em;
+ background: #9bb;
+ color: #444;
+ border: 1px solid #9bb;
+ border-bottom: none;
+ cursor: pointer;
+ cursor: hand;
+}
+
+.inactive_mouseover_tab {
+ float: right;
+ padding: 0 1em 0 1em;
+ background: #9bb;
+ color: #062;
+ border: 1px solid #062;
+ border-bottom: none;
+ cursor: pointer;
+ cursor: hand;
+}
+
+#right_frame {
+ overflow: auto;
+ position: relative;
+ top: -0.2em;
+ clear: right;
+ height: 95%;
+ border: 1px solid #048;
+}
+
+#debug {
+ display: none;
+}
+
+#debug p {
+ margin: 2px 0 0 5em;
+ text-indent: -4.8em;
+}
+
+#error {
+ display: none;
+ color: #c22;
+}
+
+#results p {
+ margin: 0 0 2px 0;
+}
+
+/* cursor indicating that detailed results may be expanded/contracted */
+#results p.badtest {
+ cursor: text;
+}
+
+#results p.ok, #results p.fail {
+ cursor: pointer;
+ cursor: hand;
+}
+
+/* colored squares in the results window at the left of test page names */
+#results p.ok .bullet {
+ background: #6d6;
+}
+
+#results p.fail .bullet {
+ background: #d46;
+}
+
+#results p.badtest .bullet {
+ background: #ea3;
+}
+
+#results p.loading .bullet {
+ background: #48f;
+}
+
+#results p.running .bullet {
+ background: #26e;
+}
+
+#results p.waiting .bullet {
+ background: #04d;
+}
+
+/* highlight in the results line */
+#results p .warning {
+ background: #ffc;
+}
+
+/* layout of the detailed results */
+.result_detail {
+ padding-left: 3em;
+}
+
+.result_exception_detail {
+ padding-left: 4em;
+}
+
+.result_exception_stack_detail {
+ padding-left: 5em;
+}
+
+.result_micro_detail {
+ padding-left: 6em;
+}
+
+/* colouring in the detailed results */
+.result_detail .fail, .result_exception_detail .fail, .result_micro_detail .fail {
+ background: #ffd8d8;
+}
+
+/* "start recording" controls*/
+#record_div {
+ margin-top: 3em;
+}
+
+#record_div p {
+ margin-bottom: 0.5em;
+}
+
+#record_select {
+ width: 88%;
+}
+
+#record_input {
+ width: 53%;
+} \ No newline at end of file
diff --git a/misc/openlayers/tests/Test.AnotherWay.geom_eq.js b/misc/openlayers/tests/Test.AnotherWay.geom_eq.js
new file mode 100644
index 0000000..893c5b5
--- /dev/null
+++ b/misc/openlayers/tests/Test.AnotherWay.geom_eq.js
@@ -0,0 +1,139 @@
+/**
+ * File: Test.AnotherWay.geom_eq.js
+ * Adds a geom_eq method to AnotherWay test objects.
+ *
+ */
+
+(function() {
+
+ /**
+ * Function assertEqual
+ * Test two objects for equivalence (based on ==). Throw an exception
+ * if not equivalent.
+ *
+ * Parameters:
+ * got - {Object}
+ * expected - {Object}
+ * msg - {String} The message to be thrown. This message will be appended
+ * with ": got {got} but expected {expected}" where got and expected are
+ * replaced with string representations of the above arguments.
+ */
+ function assertEqual(got, expected, msg) {
+ if(got === undefined) {
+ got = "undefined";
+ } else if (got === null) {
+ got = "null";
+ }
+ if(expected === undefined) {
+ expected = "undefined";
+ } else if (expected === null) {
+ expected = "null";
+ }
+ if(got != expected) {
+ throw msg + ": got '" + got + "' but expected '" + expected + "'";
+ }
+ }
+
+ /**
+ * Function assertFloatEqual
+ * Test two objects for floating point equivalence. Throw an exception
+ * if not equivalent.
+ *
+ * Parameters:
+ * got - {Object}
+ * expected - {Object}
+ * msg - {String} The message to be thrown. This message will be appended
+ * with ": got {got} but expected {expected}" where got and expected are
+ * replaced with string representations of the above arguments.
+ */
+ function assertFloatEqual(got, expected, msg) {
+ var OpenLayers = Test.AnotherWay._g_test_iframe.OpenLayers;
+ if(got === undefined) {
+ got = "undefined";
+ } else if (got === null) {
+ got = "null";
+ }
+ if(expected === undefined) {
+ expected = "undefined";
+ } else if (expected === null) {
+ expected = "null";
+ }
+ if(Math.abs(got - expected) > Math.pow(10, -OpenLayers.Util.DEFAULT_PRECISION)) {
+ throw msg + ": got '" + got + "' but expected '" + expected + "'";
+ }
+ }
+
+ /**
+ * Function assertGeometryEqual
+ * Test two geometries for equivalence. Geometries are considered
+ * equivalent if they are of the same class, and given component
+ * geometries, if all components are equivalent. Throws a message as
+ * exception if not equivalent.
+ *
+ * Parameters:
+ * got - {OpenLayers.Geometry}
+ * expected - {OpenLayers.Geometry}
+ * options - {Object} Optional object for configuring test options.
+ */
+ function assertGeometryEqual(got, expected, options) {
+
+ var OpenLayers = Test.AnotherWay._g_test_iframe.OpenLayers;
+
+ // compare types
+ assertEqual(typeof got, typeof expected, "Object types mismatch");
+
+ // compare classes
+ assertEqual(got.CLASS_NAME, expected.CLASS_NAME, "Object class mismatch");
+
+ if(got instanceof OpenLayers.Geometry.Point) {
+ // compare points
+ assertFloatEqual(got.x, expected.x, "x mismatch");
+ assertFloatEqual(got.y, expected.y, "y mismatch");
+ assertFloatEqual(got.z, expected.z, "z mismatch");
+ } else {
+ // compare components
+ assertEqual(
+ got.components.length, expected.components.length,
+ "Component length mismatch for " + got.CLASS_NAME
+ );
+ for(var i=0; i<got.components.length; ++i) {
+ try {
+ assertGeometryEqual(
+ got.components[i], expected.components[i], options
+ );
+ } catch(err) {
+ throw "Bad component " + i + " for " + got.CLASS_NAME + ": " + err;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Function: Test.AnotherWay._test_object_t.geom_eq
+ * Test if two geometry objects are equivalent. Tests for same geometry
+ * class, same number of components (if any), equivalent component
+ * geometries, and same coordinates.
+ *
+ * (code)
+ * t.geom_eq(got, expected, message);
+ * (end)
+ *
+ * Parameters:
+ * got - {OpenLayers.Geometry} Any geometry instance.
+ * expected - {OpenLayers.Geometry} The expected geometry.
+ * msg - {String} A message to print with test output.
+ * options - {Object} Optional object for configuring test options.
+ */
+ var proto = Test.AnotherWay._test_object_t.prototype;
+ proto.geom_eq = function(got, expected, msg, options) {
+ // test geometries for equivalence
+ try {
+ assertGeometryEqual(got, expected, options);
+ this.ok(true, msg);
+ } catch(err) {
+ this.fail(msg + ": " + err);
+ }
+ }
+
+})();
diff --git a/misc/openlayers/tests/Test.AnotherWay.js b/misc/openlayers/tests/Test.AnotherWay.js
new file mode 100644
index 0000000..8500f13
--- /dev/null
+++ b/misc/openlayers/tests/Test.AnotherWay.js
@@ -0,0 +1,2498 @@
+/**
+ * Test.AnotherWay version 0.5
+ *
+ * Copyright (c) 2005 Artem Khodush, http://straytree.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+if (typeof(Test) == "undefined") {
+ Test = {};
+}
+Test.AnotherWay = {};
+
+Test.AnotherWay._g_test_iframe = null; // frame where to load test pages
+Test.AnotherWay._g_test_frame_no_clear = false; // true - leave last page displayed after tests end
+Test.AnotherWay._g_test_page_urls = []; // array of: { url: url, convention: "anotherway" or "jsan" }
+Test.AnotherWay._g_test_object_for_jsan = null; // test object for filling by tests that adhere to jsan Test.Simple calling convention
+Test.AnotherWay._g_pages_to_run = null; // list of pages to run automatically after loading
+Test.AnotherWay._g_run_on_main_load = false; // special handling for run_pages_to_run when it might be called before onload or before list of test pages is known.
+Test.AnotherWay._g_run_on_list_load = false;
+Test.AnotherWay._g_main_loaded = false;
+
+Test.AnotherWay._run_pages_to_run = function(called_from_outside){
+ if (!Test.AnotherWay._g_main_loaded) {
+ Test.AnotherWay._g_run_on_main_load = true;
+ }
+ else {
+ var a_pages = Test.AnotherWay._g_pages_to_run;
+ if (a_pages == "all") {
+ for (var i = 0; i < Test.AnotherWay._g_test_page_urls.length; ++i) {
+ Test.AnotherWay._run_test_page("test" + i);
+ }
+ }
+ else
+ if (a_pages != null) {
+ for (var run_i = 0; run_i < a_pages.length; ++run_i) {
+ var run_page = a_pages[run_i];
+ var found = false;
+ for (var all_i = 0; all_i < Test.AnotherWay._g_test_page_urls.length; ++all_i) {
+ if (run_page == Test.AnotherWay._g_test_page_urls[all_i].url) {
+ Test.AnotherWay._run_test_page("test" + all_i, called_from_outside);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ Test.AnotherWay._show_error("page specified to run is not found in the page list: " + run_page);
+ break;
+ }
+ }
+ }
+ }
+};
+
+Test.AnotherWay._add_test_page_url = function(test_url, convention){
+ var table = document.getElementById("testtable");
+ var record_select = document.getElementById("record_select");
+ var index = Test.AnotherWay._g_test_page_urls.length;
+
+ // trim spaces.
+ if (test_url.match("^(\\s*)(.*\\S)(\\s*)$")) {
+ test_url = RegExp.$2;
+ }
+
+ Test.AnotherWay._g_test_page_urls[index] = {
+ url: test_url,
+ convention: convention
+ };
+ var row = table.insertRow(-1);
+
+ var cell;
+ var cell_child;
+ var link;
+
+ cell = row.insertCell(-1);
+ cell_child = document.createElement("input");
+ cell_child.type = "checkbox";
+ cell_child.id = "checkbox" + index;
+ cell_child.checked = 'checked';
+ cell_child.defaultChecked = 'checked';
+ cell.appendChild(cell_child);
+
+ cell = row.insertCell(-1);
+ cell.setAttribute("width", "75%");
+
+ cell.appendChild(document.createTextNode(test_url));
+
+ cell = row.insertCell(-1);
+ cell_child = document.createElement("input");
+ cell_child.type = "button";
+ cell_child.id = "test" + index;
+ cell_child.value = " run ";
+ cell_child.onclick = Test.AnotherWay._run_one_onclick;
+ cell.appendChild(cell_child);
+
+ cell = row.insertCell(-1);
+ cell.setAttribute("width", "8em");
+ cell_child = document.createElement("span");
+ cell.appendChild(cell_child);
+
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(test_url));
+ record_select.appendChild(option);
+};
+Test.AnotherWay._show_error = function(msg){
+ var error_div = document.getElementById("error");
+ error_div.innerHTML = "";
+ error_div.appendChild(document.createTextNode(msg));
+ error_div.style.display = "block";
+};
+
+// read urls from the list in the html file inside the list_iframe
+// fill on-screen list with urls and "run" buttons, and fill the g_test_page_urls object.
+Test.AnotherWay._list_iframe_onload = function(){
+ if (window.frames.list_iframe != null && window.frames.list_iframe.location != "" && window.frames.list_iframe.location != "about:blank") {
+ var list_doc = window.frames.list_iframe.document;
+ var list = list_doc.getElementById("testlist");
+ if (list != null) {
+ for (var i = 0; i < list.childNodes.length; ++i) {
+ var item = list.childNodes[i];
+ if (item.nodeName == "LI" || item.nodeName == "li") {
+ var convention = "anotherway";
+ if (Test.AnotherWay._get_css_class(item) == "jsan") {
+ convention = "jsan";
+ }
+ Test.AnotherWay._add_test_page_url(item.innerHTML, convention);
+ }
+ }
+ if (Test.AnotherWay._g_run_on_list_load) {
+ Test.AnotherWay._g_run_on_list_load = false;
+ Test.AnotherWay._run_pages_to_run();
+ }
+ }
+ else {
+ Test.AnotherWay._show_error("no list with id 'testlist' in a list file " + window.frames.list_iframe.location);
+ }
+ }
+};
+
+Test.AnotherWay._map_checkboxes = function(f){
+ var table = document.getElementById("testtable");
+ var checks = table.getElementsByTagName("INPUT");
+ for (var i = 0; i < checks.length; ++i) {
+ if (checks[i].type == "checkbox" && checks[i].id.match(/^checkbox(\d+)$/)) {
+ f(checks[i], RegExp.$1);
+ }
+ }
+};
+
+Test.AnotherWay._run_all_onclick = function(){
+ Test.AnotherWay._map_checkboxes(function(c, id){
+ Test.AnotherWay._run_test_page("test" + id);
+ });
+};
+Test.AnotherWay._run_selected_onclick = function(){
+ Test.AnotherWay._map_checkboxes(function(c, id){
+ if (c.checked) {
+ Test.AnotherWay._run_test_page("test" + id);
+ }
+ });
+};
+
+Test.AnotherWay._unselect_all_onclick = function(){
+ Test.AnotherWay._map_checkboxes(function(c, id){
+ c.checked = false;
+ });
+};
+
+Test.AnotherWay._run_one_onclick = function(){
+ Test.AnotherWay._run_test_page(this.id);
+};
+
+// construct an object that will gather results of running one test function
+Test.AnotherWay._test_object_t = function(fun_name){
+ this.name = fun_name; // name of the test function
+ this.n_plan = null; // planned number of assertions
+ this.n_ok = 0; // # of ok assertions
+ this.n_fail = 0; // # of failed assertions
+ this.exception = ""; // if the function throwed an exception, it's its message
+ this.exception_stack = []; // strings: function call stack from the exception
+ this.assertions = []; // assertion results: array of { ok: 1 or 0, name: string }
+ this.wait_result_milliseconds = 0; // how long to wait before collecting results from the test
+ this.second_wait_msg = null; // <p> status message (in addition to the page wait_msg)
+ this.delay_actions = []; // array of actions to be perfomed after the test function returns
+ // action : { acton_kind: "call" | "window" | "replay"
+ // when "call": { call_fn call_delay_milliseconds } call_fn takes nothing
+ // when "window" : { wnd_url wnd_wnd wnd_fn wnd_timeout_milliseconds wnd_dont_close } wnd_fn takes wnd
+ // wnen "replay" : { replay_wnd replay_events replay_event_i replay_checkpoints } checkpoint_fn takes this, wnd
+ // }
+ this.delay_action_i = null; // index of delay action currently being performed
+ this.delay_prev_timer_time = 0; // for counting time while performing delay_actions
+ this.delay_current_milliseconds_left = 0; // time left before the next action, runs down
+ this.delay_total_milliseconds_left = 0; // for indication: total estimated time for all actions, runs up and down
+};
+
+Test.AnotherWay._test_object_t.prototype.ok = function(cond, name){
+ if (cond) {
+ ++this.n_ok;
+ cond = 1;
+ }
+ else {
+ ++this.n_fail;
+ cond = 0;
+ }
+ this.assertions.push({
+ ok: cond,
+ name: name
+ });
+};
+Test.AnotherWay._test_object_t.prototype.fail = function(name){
+ this.ok(false, name);
+};
+Test.AnotherWay._test_object_t.prototype.plan = function(n){
+ this.n_plan = n;
+};
+Test.AnotherWay._test_object_t.prototype.wait_result = function(seconds){
+ this.wait_result_milliseconds = 1000 * seconds;
+};
+Test.AnotherWay._eq_fail_msg = function(path, what, expected, got){
+ return "eq: " + path + " " + what + " differ: got " + got + ", but expected " + expected;
+};
+Test.AnotherWay._array_eq = function(expected, got, path, msg){
+ if (expected.length != got.length) {
+ msg.msg = Test.AnotherWay._eq_fail_msg(path, "array length", expected.length, got.length);
+ return false;
+ }
+ for (var i = 0; i < expected.length; ++i) {
+ if (!Test.AnotherWay._thing_eq(expected[i], got[i], path + "[" + i + "]", msg)) {
+ return false;
+ }
+ }
+ return true;
+};
+Test.AnotherWay._object_eq = function(expected, got, path, msg){
+ var v;
+ for (v in expected) {
+ if (!(v in got)) {
+ msg.msg = Test.AnotherWay._eq_fail_msg(path + "." + v, "properties", expected[v], "undefined");
+ return false;
+ }
+ if (!Test.AnotherWay._thing_eq(expected[v], got[v], path + "." + v, msg)) {
+ return false;
+ }
+ }
+ for (v in got) {
+ if (!(v in expected)) {
+ msg.msg = Test.AnotherWay._eq_fail_msg(path + "." + v, "properties", "undefined", got[v]);
+ return false;
+ }
+ }
+ return true;
+};
+
+Test.AnotherWay._constructor_name = function(x){
+ if (x == null) {
+ return "";
+ }
+ var s = "unknown";
+ try {
+ s = typeof(x.constructor);
+ if (s != "unknown") {
+ s = x.constructor.toString();
+ }
+ }
+ catch (e) {
+ s = "unknown";
+ }
+ if (s == "unknown") {
+ // hackish attempt to guess a type
+ var is_array = true;
+ var index = 0;
+ for (i in x) {
+ if (i != index) {
+ is_array = false;
+ }
+ ++index;
+ }
+ return is_array ? "Array" : "Object"; // for empty arrays/objects, this will be wrong half the time
+ }
+ else
+ if (s.match(/^\s*function\s+(\w+)\s*\(/)) {
+ return RegExp.$1;
+ }
+ else {
+ var c = '';
+ switch (typeof x) {
+ case 'string':
+ c = 'String';
+ break;
+ case 'object':
+ c = 'Object';
+ break;
+ default:
+ c = '';
+ }
+ return c;
+ }
+};
+Test.AnotherWay._is_array = function(x){
+ return Test.AnotherWay._constructor_name(x) == "Array";
+};
+
+Test.AnotherWay._is_value_type = function(x){
+ cn = Test.AnotherWay._constructor_name(x);
+ return cn == "Number" || cn == "String" || cn == "Boolean" || cn == "Date";
+};
+
+Test.AnotherWay._thing_eq = function(expected, got, path, msg){
+ if (expected == null && got == null) {
+ return true;
+ }
+ else
+ if ((expected == null && got != null) || (expected != null && got == null)) {
+ msg.msg = Test.AnotherWay._eq_fail_msg(path, "values", expected, got);
+ return false;
+ }
+ else {
+ var expected_cn = Test.AnotherWay._constructor_name(expected);
+ var got_cn = Test.AnotherWay._constructor_name(got);
+ if (expected_cn != got_cn) {
+ msg.msg = Test.AnotherWay._eq_fail_msg(path, "types", expected_cn, got_cn);
+ return false;
+ }
+ else {
+ if (Test.AnotherWay._is_array(expected)) {
+ return Test.AnotherWay._array_eq(expected, got, path, msg);
+ }
+ else
+ if (Test.AnotherWay._is_value_type(expected)) {
+ if (expected != got) {
+ msg.msg = Test.AnotherWay._eq_fail_msg(path, "values", expected, got);
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+ else { // just a plain object
+ return Test.AnotherWay._object_eq(expected, got, path, msg);
+ }
+ }
+ }
+};
+
+Test.AnotherWay._test_object_t.prototype.eq = function(got, expected, name){
+ var msg = {};
+ if (Test.AnotherWay._thing_eq(expected, got, "", msg)) {
+ this.ok(1, name);
+ }
+ else {
+ this.fail(name + ". " + msg.msg);
+ }
+};
+
+Test.AnotherWay._test_object_t.prototype.like = function(got, expected, name){
+ if (got.match(expected) != null) {
+ this.ok(1, name);
+ }
+ else {
+ this.fail(name + ": got " + got + ", but expected it to match: " + expected);
+ }
+};
+
+Test.AnotherWay._g_html_eq_span = null;
+Test.AnotherWay._html_eq_string_to_node = function(string_or_node, what, msg){
+ if (string_or_node.nodeType != null) {
+ string_or_node = Test.AnotherWay._html_eq_node_to_string(string_or_node); // double trip - to make properties assigned in scripts available as html node attributes
+ }
+ if (Test.AnotherWay._g_html_eq_span == null) {
+ Test.AnotherWay._g_html_eq_span = document.createElement("span");
+ }
+ Test.AnotherWay._g_html_eq_span.innerHTML = string_or_node;
+ if (Test.AnotherWay._g_html_eq_span.childNodes.length != 1) {
+ msg.msg = "bad " + what + " html string given (should contain exactly one outermost element): " + string_or_node;
+ }
+ return Test.AnotherWay._g_html_eq_span.childNodes[0].cloneNode(true);
+};
+
+Test.AnotherWay._html_eq_node_to_string = function(node){
+ if (Test.AnotherWay._g_html_eq_span == null) {
+ Test.AnotherWay._g_html_eq_span = document.createElement("span");
+ }
+ Test.AnotherWay._g_html_eq_span.innerHTML = "";
+ if (node.outerHTML != null) {
+ Test.AnotherWay._g_html_eq_span.innerHTML = node.outerHTML;
+ }
+ else {
+ var clone = node.cloneNode(true);
+ var node = Test.AnotherWay._g_html_eq_span;
+ if (node.ownerDocument && node.ownerDocument.importNode) {
+ if (node.ownerDocument != clone.ownerDocument) {
+ clone = node.ownerDocument.importNode(clone, true);
+ }
+ }
+ node.appendChild(clone);
+ }
+ return Test.AnotherWay._g_html_eq_span.innerHTML;
+};
+
+Test.AnotherWay._html_eq_path_msg = function(path){
+ var msg = "";
+ for (var i = 0; i < path.length; ++i) {
+ msg += " [node " + path[i].node;
+ if (path[i].id != null && path[i].id != "") {
+ msg += " id " + path[i].id;
+ }
+ else
+ if (path[i].index != null) {
+ msg += " at index " + path[i].index;
+ }
+ msg += "] ";
+ }
+ return msg;
+};
+
+Test.AnotherWay._html_eq_fail_msg = function(path, what, expected, got){
+ return Test.AnotherWay._html_eq_path_msg(path) + ": " + what + " differ: got " + got + ", but expected " + expected;
+};
+
+Test.AnotherWay._html_eq_remove_blank = function(text){
+ if (text == null) {
+ return "";
+ }
+ else
+ if (text.match("^(\\s*)(.*\\S)(\\s*)$")) {
+ return RegExp.$2;
+ }
+ else
+ if (text.match("\s*")) {
+ return "";
+ }
+ return text;
+};
+
+Test.AnotherWay._html_eq_remove_blank_nodes = function(node){
+ var to_remove = [];
+ for (var child = node.firstChild; child != null; child = child.nextSibling) {
+ if (child.nodeType == 3) {
+ var value = Test.AnotherWay._html_eq_remove_blank(child.nodeValue);
+ if (value == "") {
+ to_remove.push(child);
+ }
+ else {
+ child.nodeValue = value;
+ }
+ }
+ }
+ for (var i = 0; i < to_remove.length; ++i) {
+ node.removeChild(to_remove[i]);
+ }
+};
+
+Test.AnotherWay._html_node_type_text = function(node_type){
+ if (node_type == 1) {
+ return "1 (html element)";
+ }
+ else
+ if (node_type == 3) {
+ return "3 (text)";
+ }
+ else {
+ return node_type;
+ }
+};
+
+Test.AnotherWay._html_eq_node = function(expected, got, path, msg, expected_loc_base, got_loc_base){
+ if (expected.nodeType != got.nodeType) {
+ msg.msg = Test.AnotherWay._html_eq_fail_msg(path, "node types", Test.AnotherWay._html_node_type_text(expected.nodeType), Test.AnotherWay._html_node_type_text(got.nodeType));
+ return false;
+ }
+ else
+ if (expected.nodeType == 3) {
+ if (expected.nodeValue != got.nodeValue) {
+ msg.msg = Test.AnotherWay._html_eq_fail_msg(path, "text", expected.nodeValue, got.nodeValue);
+ return false;
+ }
+ }
+ else
+ if (expected.nodeType == 1) {
+ if (expected.nodeName != got.nodeName) {
+ msg.msg = Test.AnotherWay._html_eq_fail_msg(path, "node names", expected.nodeName, got.nodeName);
+ return false;
+ }
+ // compare attributes
+ var expected_attrs = {};
+ var got_attrs = {};
+ var i;
+ var a;
+ for (i = 0; i < expected.attributes.length; ++i) {
+ a = expected.attributes[i];
+ if (a.specified) {
+ expected_attrs[a.name] = 1;
+ }
+ }
+ for (i = 0; i < got.attributes.length; ++i) {
+ a = got.attributes[i];
+ if (a.specified) {
+ got_attrs[a.name] = 1;
+ }
+ }
+ for (a in expected_attrs) {
+ if (!(a in got_attrs)) {
+ msg.msg = Test.AnotherWay._html_eq_path_msg(path) + ": attribute sets differ: expected attribute " + a + " is missing";
+ return false;
+ }
+ }
+ for (a in got_attrs) {
+ if (!(a in expected_attrs)) {
+ msg.msg = Test.AnotherWay._html_eq_path_msg(path) + ": attribute sets differ: got extra attribute " + a;
+ return false;
+ }
+ }
+ for (a in expected_attrs) {
+ var expected_value = expected.getAttribute(a);
+ var got_value = got.getAttribute(a);
+ if (typeof(expected_value) == "string" && typeof(got_value) == "string") {
+ expected_value = Test.AnotherWay._html_eq_remove_blank(expected_value);
+ got_value = Test.AnotherWay._html_eq_remove_blank(got_value);
+ var ok = expected_value == got_value;
+ if (!ok && (a == "href" || a == "HREF")) { // try relative hrefs
+ var expected_relative_value = expected_value;
+ if (expected_loc_base != null && expected_value.substring(0, expected_loc_base.length) == expected_loc_base) {
+ expected_relative_value = expected_value.substring(expected_loc_base.length);
+ }
+ var got_relative_value = got_value;
+ if (got_loc_base != null && got_value.substring(0, got_loc_base.length) == got_loc_base) {
+ got_relative_value = got_value.substring(got_loc_base.length);
+ }
+ ok = expected_relative_value == got_relative_value;
+ }
+ if (!ok) {
+ msg.msg = Test.AnotherWay._html_eq_fail_msg(path, "attribute " + a + " values", expected_value, got_value);
+ return false;
+ }
+ }
+ else
+ if (typeof(expected_value) == "function" && typeof(got_value) == "function") {
+ expected_value = expected_value.toString();
+ got_value = got_value.toString();
+ if (expected_value != got_value) {
+ msg.msg = Test.AnotherWay._html_eq_fail_msg(path, "attribute " + a + " values", expected_value, got_value);
+ return false;
+ }
+ }
+ else {
+ var value_msg = {};
+ if (!Test.AnotherWay._thing_eq(expected_value, got_value, "", value_msg)) {
+ msg.msg = Test.AnotherWay._html_eq_path_msg(path) + ": attribute " + a + " values differ: " + value_msg.msg;
+ return false;
+ }
+ }
+ }
+ // compare child nodes
+ Test.AnotherWay._html_eq_remove_blank_nodes(expected);
+ Test.AnotherWay._html_eq_remove_blank_nodes(got);
+ var expected_length = expected.childNodes.length;
+ var got_length = got.childNodes.length;
+ if (expected_length < got_length) {
+ msg.msg = Test.AnotherWay._html_eq_path_msg(path) + ": got " + (got_length - expected_length) + " extra child nodes";
+ return false;
+ }
+ else
+ if (expected_length > got_length) {
+ msg.msg = Test.AnotherWay._html_eq_path_msg(path) + ": expected " + (expected_length - got_length) + " more child nodes";
+ return false;
+ }
+ else {
+ for (i = 0; i < expected_length; ++i) {
+ var expected_node = expected.childNodes[i];
+ path.push({
+ node: expected_node.nodeName,
+ id: expected_node.id,
+ index: i
+ });
+ var eq = Test.AnotherWay._html_eq_node(expected_node, got.childNodes[i], path, msg, expected_loc_base, got_loc_base);
+ path.pop();
+ if (!eq) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+};
+
+Test.AnotherWay._html_eq_get_loc_base = function(node){
+ var loc_base = document.location;
+ if (node.ownerDocument != null) {
+ loc_base = node.ownerDocument.location;
+ }
+ if (loc_base != null) {
+ loc_base = loc_base.href;
+ var slash_pos = loc_base.lastIndexOf("/");
+ if (slash_pos != -1) {
+ loc_base = loc_base.substring(0, slash_pos + 1);
+ }
+ }
+ return loc_base;
+};
+
+Test.AnotherWay._test_object_t.prototype.html_eq = function(got, expected, name){
+ var msg = {};
+ var expected_node = Test.AnotherWay._html_eq_string_to_node(expected, "expected", msg);
+ if (msg.msg != null) {
+ this.fail(name + " html_eq: " + msg.msg);
+ }
+ else {
+ var got_node = Test.AnotherWay._html_eq_string_to_node(got, "got", msg);
+ if (msg.msg != null) {
+ this.fail(name + " html_eq: " + msg.msg);
+ }
+ else {
+ var expected_loc_base = Test.AnotherWay._html_eq_get_loc_base(expected);
+ var got_loc_base = Test.AnotherWay._html_eq_get_loc_base(got);
+ if (Test.AnotherWay._html_eq_node(expected_node, got_node, [], msg, expected_loc_base, got_loc_base)) {
+ this.ok(1, name);
+ }
+ else {
+ var msg = name + " html_eq " + msg.msg;
+ var expected_str = Test.AnotherWay._html_eq_node_to_string(expected_node);
+ var got_str = Test.AnotherWay._html_eq_node_to_string(got_node);
+ msg += ".\n got html: " + got_str;
+ msg += ".\n expected html: " + expected_str;
+ this.fail(msg);
+ }
+ }
+ }
+};
+
+Test.AnotherWay._debug_pane_print = function(msg){
+ var d = new Date();
+ var p = document.createElement("p");
+ p.appendChild(document.createTextNode(d.toLocaleTimeString() + " " + msg));
+ var debug_pane = document.getElementById("debug");
+ debug_pane.appendChild(p);
+ var debug_tab = document.getElementById("debug_tab");
+ var results_tab = document.getElementById("results_tab");
+ debug_tab.style.visibility = "visible";
+ results_tab.style.visibility = "visible";
+};
+
+Test.AnotherWay._test_object_t.prototype.debug_print = function(msg){
+ Test.AnotherWay._debug_pane_print(this.name + ": " + msg);
+};
+
+Test.AnotherWay._test_object_t.prototype.delay_call = function(){
+ var timeout_ms = 200;
+ for (var i = 0; i < arguments.length; ++i) {
+ if (typeof(arguments[i]) != "function") {
+ timeout_ms = 1000 * arguments[i];
+ }
+ else {
+ var action = {
+ action_kind: "call",
+ call_delay_milliseconds: timeout_ms,
+ call_fn: arguments[i]
+ };
+ this.delay_total_milliseconds_left += Test.AnotherWay._action_estimate_milliseconds(action);
+ this.delay_actions.push(action);
+ }
+ }
+};
+
+Test.AnotherWay._test_object_t.prototype.open_window = function(url, fn, timeout_seconds){
+ if (timeout_seconds == null) {
+ timeout_seconds = 4;
+ }
+ var no_close = document.getElementById("dont_close_test_windows");
+ var action = {
+ action_kind: "window",
+ wnd_url: url.toString() + (window.location.search || ""),
+ wnd_wnd: null,
+ wnd_fn: fn,
+ wnd_timeout_milliseconds: timeout_seconds * 1000,
+ wnd_no_close: no_close.checked
+ };
+ this.delay_total_milliseconds_left += Test.AnotherWay._action_estimate_milliseconds(action);
+ this.delay_actions.push(action);
+};
+
+Test.AnotherWay._test_object_t.prototype.replay_events = function(wnd, events){
+ if (Test.AnotherWay._g_no_record_msg != null) {
+ this.fail("replay_events: " + Test.AnotherWay._g_no_record_msg);
+ }
+ else {
+ var action = {
+ action_kind: "replay",
+ replay_wnd: wnd,
+ replay_events: events.events,
+ replay_event_i: null,
+ replay_checkpoints: events.checkpoints
+ };
+ this.delay_total_milliseconds_left += Test.AnotherWay._action_estimate_milliseconds(action);
+ this.delay_actions.push(action);
+ }
+};
+
+Test.AnotherWay._action_estimate_milliseconds = function(action){
+ var ms = 0;
+ if (action.action_kind == "call") {
+ ms = action.call_delay_milliseconds;
+ }
+ else
+ if (action.action_kind == "window") {
+ ms = 0;
+ }
+ else
+ if (action.action_kind == "replay") {
+ ms = 0;
+ for (var i = 0; i < action.replay_events.length; ++i) {
+ ms += action.replay_events[i]["time"] - 0;
+ }
+ }
+ return ms;
+};
+
+Test.AnotherWay._g_timeout_granularity = 200;
+Test.AnotherWay._g_tests_queue = []; // vector of { url: string, test_objects : array of test_object_t, test_object_i: int, wait_msg: <p> object, loading_timeout_milliseconds: int, timeout_id: id }
+// load one html page, schedule further processing
+Test.AnotherWay._run_test_page = function(id, called_from_outside){
+ if (id.match(/^test(\d+)/)) {
+ id = RegExp.$1;
+ Test.AnotherWay._g_tests_queue.push({
+ url: Test.AnotherWay._g_test_page_urls[id].url,
+ convention: Test.AnotherWay._g_test_page_urls[id].convention,
+ test_objects: []
+ });
+ if (Test.AnotherWay._g_tests_queue.length == 1) {
+ if (!called_from_outside) {
+ // Crap. Be careful stepping around.
+ // For Mozilla and Opera, when this file is included into the frameset page that is in another directory (and _g_outside_path_correction!=null)
+ // but the test pages are started from within it (by "run" buttons), then:
+ // depending on whether the page is the first one loaded into the test frame or not,
+ // the base url for relative test pages differs.
+ // Crap, like I said.
+ Test.AnotherWay._g_tests_queue[0].suppress_outside_path_correction = true;
+ }
+ Test.AnotherWay._start_loading_page();
+ }
+ }
+};
+
+Test.AnotherWay._load_next_page = function(){
+ Test.AnotherWay._g_tests_queue.splice(0, 1);
+ if (Test.AnotherWay._g_tests_queue.length > 0) {
+ Test.AnotherWay._start_loading_page();
+ }
+ else {
+ if (!Test.AnotherWay._g_test_frame_no_clear) {
+ Test.AnotherWay._g_test_iframe.location.replace("about:blank");
+ }
+ }
+};
+
+Test.AnotherWay._g_opera_path_correction = null; // ugly wart to support opera
+Test.AnotherWay._g_outside_path_correction = null; // ugly wart to accomodate Opera and Mozilla, where relative url relates to the directory where the page that calls this function is located
+Test.AnotherWay._set_iframe_location = function(iframe, loc, outside_path_correction){
+ // allow to load only locations with the same origin
+ var proto_end = loc.indexOf("://");
+ if (proto_end != -1) { // otherwise, it's safe to assume (for Opera, Mozilla and IE ) that loc will be treated as relative
+ var main_loc = window.location.href;
+ var host_end = loc.substring(proto_end + 3).indexOf("/");
+ var ok = false;
+ if (host_end != -1) {
+ var loc_origin = loc.substring(0, proto_end + 3 + host_end + 1);
+ if (main_loc.length >= loc_origin.length && main_loc.substring(0, loc_origin.length) == loc_origin) {
+ ok = true;
+ }
+ }
+ if (!ok) {
+ return {
+ msg: "test pages may have only urls with the same origin as " + main_loc
+ };
+ }
+ }
+ // opera cannot handle urls relative to file:// without assistance
+ if (window.opera != null && window.location.protocol == "file:" && loc.indexOf(":") == -1) {
+ var base = window.location.href;
+ var q_pos = base.indexOf("?");
+ if (q_pos != -1) {
+ base = base.substring(0, q_pos);
+ }
+ var slash_pos = base.lastIndexOf("/");
+ if (slash_pos != -1) {
+ base = base.substring(0, slash_pos + 1);
+ Test.AnotherWay._g_opera_path_correction = base;
+ loc = base + loc;
+ }
+ }
+ // if this function is called from another page, and if that page is in another directory, correction is needed
+ if (outside_path_correction != null) {
+ var pos = loc.indexOf(outside_path_correction);
+ if (pos == 0) {
+ loc = loc.substring(outside_path_correction.length + 1);
+ }
+ }
+ if (iframe.location != null) {
+ iframe.location.replace(loc);
+ }
+ else {
+ iframe.src = loc;
+ }
+ return {};
+};
+
+Test.AnotherWay._start_loading_page = function(){
+ var test_page = Test.AnotherWay._g_tests_queue[0];
+ test_page.loading_timeout_milliseconds = 12000;
+ test_page.timeout_id = setTimeout(Test.AnotherWay._loading_timeout, Test.AnotherWay._g_timeout_granularity);
+ test_page.wait_msg = Test.AnotherWay._print_counter_result(test_page.url, "loading...", test_page.loading_timeout_milliseconds, "loading");
+ if (test_page.convention == "jsan") {
+ // the tests in that page will run when it's loading, so the test object must be ready
+ Test.AnotherWay._g_test_object_for_jsan = new Test.AnotherWay._test_object_t(test_page.url);
+ }
+ var outside_path_correction = null;
+ if (Test.AnotherWay._g_outside_path_correction != null && !test_page.suppress_outside_path_correction) {
+ outside_path_correction = Test.AnotherWay._g_outside_path_correction;
+ }
+ var result = Test.AnotherWay._set_iframe_location(Test.AnotherWay._g_test_iframe, test_page.url, outside_path_correction);
+ if (result.msg != null) {
+ Test.AnotherWay._unprint_result(test_page.wait_msg);
+ Test.AnotherWay._print_result(test_page.url, result.msg, "badtest", null);
+ Test.AnotherWay._load_next_page();
+ }
+};
+
+Test.AnotherWay._loading_timeout = function(){
+ var test_page = Test.AnotherWay._g_tests_queue[0];
+ test_page.loading_timeout_milliseconds -= Test.AnotherWay._g_timeout_granularity;
+ if (test_page.loading_timeout_milliseconds > 0) {
+ Test.AnotherWay._update_msg_counter(test_page.wait_msg, (test_page.loading_timeout_milliseconds / 1000).toFixed());
+ test_page.timeout_id = setTimeout(Test.AnotherWay._loading_timeout, Test.AnotherWay._g_timeout_granularity);
+ }
+ else {
+ Test.AnotherWay._unprint_result(test_page.wait_msg);
+ Test.AnotherWay._print_result(test_page.url, "Unable to load test page. Timeout expired", "badtest", null);
+ Test.AnotherWay._load_next_page();
+ }
+};
+
+Test.AnotherWay._strip_query_and_hash = function(s){
+ var i = s.lastIndexOf("#");
+ if (i != -1) {
+ s = s.substring(0, i);
+ }
+ i = s.lastIndexOf("?");
+ if (i != -1) {
+ s = s.substring(0, i);
+ }
+ return s;
+};
+
+Test.AnotherWay._is_url_loaded = function(url, wnd){
+ var loaded = false;
+ if (wnd != null && wnd.location != null) {
+ // after some popup blocker interference, location may behave strange..
+ var location_s = "";
+ location_s += wnd.location;
+ if (location_s != "") {
+ var pathname = wnd.location.pathname;
+ var expected_url = url;
+ var i = expected_url.lastIndexOf("#");
+ if (i != -1) {
+ expected_url = expected_url.substring(0, i);
+ }
+ i = expected_url.lastIndexOf("?");
+ if (i != -1) {
+ expected_url = expected_url.substring(0, i);
+ }
+ i = expected_url.lastIndexOf("/");
+ if (i != -1 && i != expected_url.length - 1) {
+ expected_url = expected_url.substring(i + 1);
+ }
+ i = pathname.indexOf(expected_url);
+ if (wnd.location.href == url || (i != -1 && i == pathname.length - expected_url.length)) {
+ if ( /*window.opera==null*/wnd.document.readyState == null || wnd.document.readyState == "complete") { // for opera (and IE?), getElementById does not work until..
+ loaded = true;
+ }
+ }
+ }
+ }
+ return loaded;
+};
+// find and run all test functions in the g_cur_page html page.
+Test.AnotherWay._test_page_onload = function(){
+ if (Test.AnotherWay._g_tests_queue.length == 0) {
+ return;
+ }
+ var test_page = Test.AnotherWay._g_tests_queue[0];
+ if (!Test.AnotherWay._is_url_loaded(test_page.url, Test.AnotherWay._g_test_iframe)) {
+ return;
+ }
+ clearTimeout(test_page.timeout_id);
+ Test.AnotherWay._unprint_result(test_page.wait_msg);
+
+ if (test_page.convention == "anotherway") {
+ // get test function names (those beginning with "test")
+ if (typeof(Test.AnotherWay._g_test_iframe.document.scripts) != 'undefined') { // IE
+ for (var i = 0; i < Test.AnotherWay._g_test_iframe.document.scripts.length; ++i) {
+ var script_text = Test.AnotherWay._g_test_iframe.document.scripts[i].text;
+ var fun_sig = "function test";
+ var fun_start = script_text.indexOf(fun_sig);
+
+ while (fun_start != -1) {
+ script_text = script_text.substring(fun_start, script_text.length);
+ var fun_end = script_text.indexOf('(');
+ var fun_name = script_text.substring("function ".length, fun_end);
+ var whitespace = fun_name.indexOf(' ');
+ if (whitespace >= 0) {
+ fun_name = fun_name.substring(0, whitespace);
+ }
+ test_page.test_objects.push(new Test.AnotherWay._test_object_t(fun_name));
+ script_text = script_text.substring(fun_end, script_text.length);
+ fun_start = script_text.indexOf(fun_sig);
+ }
+ }
+ }
+ else { // otherwise (not IE) it ought to work like this
+ for (var i in Test.AnotherWay._g_test_iframe) {
+ // Hack to prevent failure in FF3.0b1 (innerWidth/innerHeight) and FF>=3.5 (sessionStorage)
+ if (i == "innerWidth" || i == "innerHeight" || i == "sessionStorage") {
+ continue;
+ }
+ if (typeof(Test.AnotherWay._g_test_iframe[i]) == 'function') {
+ if (i.substring(0, 4) == "test") {
+ test_page.test_objects.push(new Test.AnotherWay._test_object_t(i));
+ }
+ }
+ }
+ }
+ }
+ else
+ if (test_page.convention == "jsan") {
+ // the test object is already filled with results
+ test_page.test_objects.push(Test.AnotherWay._g_test_object_for_jsan);
+ }
+
+ if (test_page.test_objects.length == 0) {
+ Test.AnotherWay._print_result(test_page.url, "No test functions defined in the page", "badtest", null);
+ Test.AnotherWay._load_next_page();
+ return;
+ }
+
+ test_page.wait_msg = Test.AnotherWay._print_result(test_page.url, "running tests..<span class=\"counter\">" + test_page.test_objects.length + "</span>", "running", null);
+
+ test_page.test_object_i = 0;
+ Test.AnotherWay._run_more_tests();
+};
+
+Test.AnotherWay._handle_exception = function(o, e, title){
+ var s = title + ": " + typeof(e) + ": ";
+ if (e.message != null) {
+ s += e.message;
+ }
+ else
+ if (e.description != null) {
+ s += e.description;
+ }
+ else {
+ s += e.toString();
+ }
+ // if( e.location!=null ) { // XXX figure out how to display exception location if it's present (like in mozilla)
+ // s+=" location: "+e.location.toString();
+ // }
+ o.exception = s;
+ s = [];
+ if (e.stack) {
+ var lines = e.stack.split("\n");
+ for (var i = 0; i < lines.length; ++i) {
+ // format of the line: func_name(args)@file_name:line_no
+ if (lines[i].match(/(\w*)\(([^\)]*)\)@(.*):([^:]*)$/)) {
+ var func_name = RegExp.$1;
+ if (func_name.length == 0) {
+ func_name = "<anonymous>";
+ }
+ s.push("in " + func_name + "( " + RegExp.$2 + ") at " + RegExp.$3 + " line " + RegExp.$4 + "\n");
+ }
+ }
+ }
+ o.exception_stack = s;
+};
+
+Test.AnotherWay._run_more_tests = function(){
+ var test_page = Test.AnotherWay._g_tests_queue[0];
+ while (test_page.test_object_i < test_page.test_objects.length) {
+ Test.AnotherWay._update_msg_counter(test_page.wait_msg, (1 + test_page.test_object_i) + "/" + test_page.test_objects.length);
+ var o = test_page.test_objects[test_page.test_object_i];
+ if (test_page.convention == "anotherway") {
+ try {
+ Test.AnotherWay._g_test_iframe[o.name](o);
+ }
+ catch (e) {
+ Test.AnotherWay._handle_exception(o, e, "");
+ }
+ } // for "jsan" convention, test has run already
+ if (o.delay_actions.length > 0 || o.wait_result_milliseconds > 0) {
+ o.delay_total_milliseconds_left += o.wait_result_milliseconds;
+ Test.AnotherWay._delay_actions_timeout();
+ return;
+ }
+ ++test_page.test_object_i;
+ }
+ Test.AnotherWay._unprint_result(test_page.wait_msg);
+ Test.AnotherWay._print_result(test_page.url, null, null, test_page.test_objects);
+ Test.AnotherWay._load_next_page();
+};
+
+Test.AnotherWay._delay_actions_timeout = function(){
+ var test_page = Test.AnotherWay._g_tests_queue[0];
+ var test_object = test_page.test_objects[test_page.test_object_i];
+ var finished = true;
+ if (test_object.delay_action_i == null) {
+ // set up to start first action
+ test_object.delay_action_i = -1;
+ }
+ else {
+ // perform current action
+ var milliseconds_passed = (new Date()).getTime() - test_object.delay_prev_timer_time;
+ test_object.delay_current_milliseconds_left -= milliseconds_passed;
+ test_object.delay_total_milliseconds_left -= milliseconds_passed;
+ finished = Test.AnotherWay._delay_continue_action(test_object, milliseconds_passed);
+ }
+ while (finished && test_object.delay_action_i < test_object.delay_actions.length) {
+ ++test_object.delay_action_i; // start next action
+ finished = Test.AnotherWay._delay_start_action(test_object);
+ }
+ if (test_object.delay_action_i <= test_object.delay_actions.length) { // any more actions left ?
+ test_object.delay_prev_timer_time = (new Date()).getTime();
+ var next_timeout = Test.AnotherWay._g_timeout_granularity;
+ if (test_object.delay_current_milliseconds_left < next_timeout) {
+ next_timeout = test_object.delay_current_milliseconds_left;
+ }
+ if (test_object.second_wait_msg != null) {
+ Test.AnotherWay._update_msg_counter(test_object.second_wait_msg, (test_object.delay_total_milliseconds_left / 1000).toFixed());
+ }
+ setTimeout(Test.AnotherWay._delay_actions_timeout, next_timeout);
+ }
+ else { // no more actions left. run the next test.
+ if (test_object.second_wait_msg != null) {
+ Test.AnotherWay._unprint_result(test_object.second_wait_msg);
+ test_object.second_wait_msg = null;
+ }
+ ++test_page.test_object_i;
+ Test.AnotherWay._run_more_tests();
+ }
+};
+
+Test.AnotherWay._delay_start_action = function(test_object){
+ var finished = false;
+ var wait_msg = "";
+ if (test_object.delay_action_i == test_object.delay_actions.length) {
+ if (test_object.wait_result_milliseconds > 0) {
+ test_object.delay_current_milliseconds_left = test_object.wait_result_milliseconds; // wait for result
+ wait_msg = "waiting for results..";
+ }
+ else {
+ ++test_object.delay_action_i; // dont wait for result
+ }
+ }
+ else {
+ var action = test_object.delay_actions[test_object.delay_action_i];
+ if (action.action_kind == "call") {
+ test_object.delay_current_milliseconds_left = action.call_delay_milliseconds;
+ wait_msg = "performing delayed calls..";
+ }
+ else
+ if (action.action_kind == "window") {
+ if (Test.AnotherWay._g_opera_path_correction != null && action.wnd_url.indexOf(":") == -1) {
+ action.wnd_url = Test.AnotherWay._g_opera_path_correction + action.wnd_url;
+ }
+ action.wnd_wnd = window.open(action.wnd_url, "_blank");
+ if (action.wnd_wnd == null) {
+ finished = true;
+ test_object.fail("unable to open window for " + action.wnd_url);
+ }
+ else {
+ test_object.delay_current_milliseconds_left = action.wnd_timeout_milliseconds;
+ wait_msg = "opening window..";
+ }
+ }
+ else
+ if (action.action_kind == "replay") {
+ if (action.replay_events.length == 0) {
+ finished = true;
+ }
+ else {
+ action.replay_event_i = 0;
+ test_object.delay_current_milliseconds_left = action.replay_events[0]["time"];
+ wait_msg = "replaying events..";
+ }
+ }
+ }
+ if (test_object.second_wait_msg != null) {
+ Test.AnotherWay._unprint_result(test_object.second_wait_msg);
+ }
+ if (wait_msg != "") {
+ var test_page = Test.AnotherWay._g_tests_queue[0];
+ test_object.second_wait_msg = Test.AnotherWay._print_counter_result(test_page.url, wait_msg, test_object.delay_total_milliseconds_left, "waiting");
+ }
+ else {
+ test_object.second_wait_msg = null;
+ }
+ return finished;
+};
+Test.AnotherWay._delay_continue_action = function(test_object, milliseconds_passed){
+ var finished = test_object.delay_current_milliseconds_left <= 0;
+ if (test_object.delay_action_i == test_object.delay_actions.length) { // action is "waiting for results"
+ if (test_object.n_plan != null && test_object.n_plan == test_object.n_ok + test_object.n_fail) {
+ finished = true; // if all assertions results are recorded, don't wait any more
+ }
+ if (finished) {
+ ++test_object.delay_action_i; // move on to the next test
+ }
+ }
+ else {
+ var action = test_object.delay_actions[test_object.delay_action_i];
+ if (action.action_kind == "call") {
+ if (finished) {
+ try {
+ action.call_fn();
+ }
+ catch (e) {
+ Test.AnotherWay._handle_exception(test_object, e, "in delay_call");
+ }
+ }
+ }
+ else
+ if (action.action_kind == "window") {
+ test_object.delay_total_milliseconds_left += milliseconds_passed; // for "window", the countdown is suspended since it's unknown how long it will take
+ if (Test.AnotherWay._is_url_loaded(action.wnd_url, action.wnd_wnd)) {
+ try {
+ action.wnd_fn(action.wnd_wnd);
+ }
+ catch (e) {
+ Test.AnotherWay._handle_exception(test_object, e, "in open_window function call");
+ }
+ finished = true;
+ }
+ else
+ if (finished) {
+ test_object.fail("unable to open window for url '" + action.wnd_url + "'. timeout expired");
+ }
+ }
+ else
+ if (action.action_kind == "replay") {
+ if (finished) {
+ // try {
+ Test.AnotherWay._delay_replay_event(test_object, action.replay_wnd, action.replay_events[action.replay_event_i], action.replay_checkpoints);
+ // }catch( e ) { // disabled, until I know how to gel location info from an exception
+ // Test.AnotherWay._handle_exception( test_object, e, "while replaying event" );
+ // }
+ ++action.replay_event_i;
+ finished = action.replay_event_i == action.replay_events.length;
+ if (!finished) {
+ test_object.delay_current_milliseconds_left = action.replay_events[action.replay_event_i]["time"];
+ }
+ }
+ }
+ }
+ return finished;
+};
+
+Test.AnotherWay._delay_replay_event = function(test_object, wnd, event, checkpoints){
+ if (event.type == "_checkpoint") {
+ var checkpoint_n = event.which;
+ var prev_n_fail = test_object.n_fail;
+ checkpoints[checkpoint_n](test_object, wnd);
+ var flash_color = prev_n_fail == test_object.n_fail ? "#2f2" : "#f22";
+ Test.AnotherWay._record_flash_border(flash_color);
+ }
+ else
+ if (event.type == "click" || event.type == "mouseover" || event.type == "mouseout" || event.type == "mousemove" || event.type == "mousedown" || event.type == "mouseup") {
+ var target = Test.AnotherWay._record_node_path_to_node(event["target"], wnd.document);
+ if (target != null) {
+ Test.AnotherWay._record_control_update_highlight(target, "ball", event);
+ var e = wnd.document.createEvent("MouseEvents");
+ var related_target = Test.AnotherWay._record_node_path_to_node(event["relatedTarget"], wnd.document);
+ e.initMouseEvent(event["type"], event["cancelable"], event["bubbles"], wnd.document.defaultView, event["detail"], event["screenX"], event["screenY"], event["clientX"], event["clientY"], event["ctrlKey"], event["altKey"], event["shiftKey"], event["metaKey"], event["button"], Test.AnotherWay._record_node_path_to_node(event["relatedTarget"], wnd.document));
+ // Firefox 1.0.6 somehow loses relatedTarget somewhere on the way. Pass through our own, for those who choose to care.
+ e.passThroughRelatedTarget = related_target;
+ target.dispatchEvent(e);
+ }
+ }
+ else
+ if (event.type == "keyup" || event.type == "keydown" || event.type == "keypress") {
+ var e = wnd.document.createEvent("KeyboardEvents"); // forget it. Apparently it's not supported neither by mozilla nor by opera.
+ e.initKeyboardEvent(event["type"], event["cancelable"], event["bubbles"], wnd.document.defaultView, event["which"], event["which"], event["ctrlKey"], event["altKey"], event["shiftKey"], event["metaKey"], false);
+ wnd.document.dispatchEvent(e);
+ }
+};
+
+Test.AnotherWay._print_counter_result = function(url, msg, milliseconds, style){
+ return Test.AnotherWay._print_result(url, msg + "<span class=\"counter\">" + (milliseconds / 1000).toFixed() + "</span>", style, null);
+};
+
+Test.AnotherWay._g_result_count = 0; // for assigning unique ids to result paragraphs
+// number of pages tested
+Test.AnotherWay._g_ok_pages = 0;
+Test.AnotherWay._g_fail_pages = 0;
+
+Test.AnotherWay._print_result = function(url, msg, style, test_objects){
+ var results = document.getElementById("results");
+ var r = results.appendChild(document.createElement("p"));
+ r.id = "result" + Test.AnotherWay._g_result_count;
+ ++Test.AnotherWay._g_result_count;
+ r.onclick = Test.AnotherWay._toggle_detail;
+ var text = "<span class=\"bullet\">&nbsp;&nbsp;&nbsp;</span>&nbsp;";
+ if (url != "") {
+ text += url + ": ";
+ }
+ if (msg != null) {
+ text += msg;
+ }
+ if (test_objects != null) {
+ // compose summary and detail texts
+ var total_ok = 0;
+ var total_detail_ok = 0;
+ var total_fail = 0;
+ var total_detail_fail = 0;
+ var no_plan = 0;
+
+ var detail = results.appendChild(document.createElement("div"));
+
+ if (r.id.match(/^result(\d+)$/)) {
+ detail.id = "result_detail" + RegExp.$1;
+ }
+
+ for (var i = 0; i < test_objects.length; ++i) {
+ var o = test_objects[i];
+ var p;
+ var p_text;
+ p = document.createElement("P");
+ Test.AnotherWay._set_css_class(p, "result_detail");
+ p_text = o.name;
+ if (o.n_fail > 0 || o.exception || (o.n_plan != null && o.n_plan != o.n_ok + o.n_fail) || (o.n_plan == null && o.n_ok == 0 && o.n_fail == 0)) {
+ ++total_fail;
+ p_text += " <span class=\"fail\">";
+ if (o.n_plan != null && o.n_plan != o.n_ok + o.n_fail) {
+ p_text += "planned " + o.n_plan + " assertions but got " + (o.n_ok + o.n_fail) + "; ";
+ }
+ if (o.n_plan == null && o.n_ok == 0 && o.n_fail == 0) {
+ p_text += "test did not output anything";
+ }
+ else {
+ p_text += " fail " + o.n_fail;
+ }
+ p_text += "</span>";
+ }
+ else {
+ ++total_ok;
+ }
+ p_text += " ok " + o.n_ok;
+ if (o.n_plan == null) {
+ no_plan = 1;
+ p_text += " <span class=\"warning\">no plan</span>";
+ }
+ p.innerHTML = p_text;
+ detail.appendChild(p);
+ if (o.exception) {
+ p = document.createElement("P");
+ Test.AnotherWay._set_css_class(p, "result_exception_detail");
+ p.innerHTML = "<span class=\"fail\">exception:</span> " + o.exception;
+ detail.appendChild(p);
+ p = document.createElement("P");
+ Test.AnotherWay._set_css_class(p, "result_exception_stack_detail");
+ p.innerHTML = o.exception_stack.join("<br/>");
+ detail.appendChild(p);
+ }
+ for (var ii = 0; ii < o.assertions.length; ++ii) {
+ var oo = o.assertions[ii];
+ var status = oo.ok ? "ok" : "<span class=\"fail\">fail</span>";
+ p = document.createElement("P");
+ Test.AnotherWay._set_css_class(p, "result_micro_detail");
+ p.innerHTML = status;
+ p.appendChild(document.createTextNode(" " + oo.name));
+ detail.appendChild(p);
+ }
+ total_detail_ok += o.n_ok;
+ total_detail_fail += o.n_fail;
+ }
+ if (total_fail || total_detail_fail) {
+ text += " fail " + total_fail;
+ }
+ text += " ok " + total_ok + " (detailed:";
+ if (total_fail || total_detail_fail) {
+ text += " fail " + total_detail_fail;
+ }
+ text += " ok " + total_detail_ok + ")";
+ if (no_plan) {
+ text += " <span class=\"warning\">no plan</span>";
+ }
+ style = total_fail == 0 ? "ok" : "fail";
+ detail.style.display = style == "fail" ? "block" : "none";
+ detail.style.cursor = "text";
+ }
+ if (style != null) {
+ Test.AnotherWay._set_css_class(r, style);
+ if (style == "ok") {
+ ++Test.AnotherWay._g_ok_pages;
+ }
+ else
+ if (style == "fail" || style == "badtest") {
+ ++Test.AnotherWay._g_fail_pages;
+ }
+ var pages_total = "";
+ if (Test.AnotherWay._g_fail_pages > 0) {
+ pages_total += " fail " + Test.AnotherWay._g_fail_pages;
+ }
+ pages_total += " ok " + Test.AnotherWay._g_ok_pages;
+
+ // also count out the total number of tests in fail and ok
+ Test.AnotherWay._openlayers_sum_total_detail_ok = Test.AnotherWay._openlayers_sum_total_detail_ok || 0;
+ Test.AnotherWay._openlayers_sum_total_detail_ok += (total_detail_ok||0);
+
+ Test.AnotherWay._openlayers_sum_total_detail_fail = Test.AnotherWay._openlayers_sum_total_detail_fail || 0;
+ Test.AnotherWay._openlayers_sum_total_detail_fail += (total_detail_fail||0);
+
+ pages_total+=" (detailed: fail " + Test.AnotherWay._openlayers_sum_total_detail_fail + " | ok " + Test.AnotherWay._openlayers_sum_total_detail_ok + ")";
+
+ Test.AnotherWay._update_results_total(pages_total);
+ }
+ r.innerHTML = text;
+ if (results.scrollHeight != null && results.scrollTop != null && results.offsetHeight != null) {
+ results.scrollTop = results.scrollHeight - results.offsetHeight;
+ }
+ // when test_objects is not null, the results are final - good time to clean up
+ if (test_objects != null) {
+ for (var i = 0; i < test_objects.length; ++i) {
+ var actions = test_objects[i].delay_actions;
+ for (var action_i = 0; action_i < actions.length; ++action_i) {
+ var action = actions[action_i];
+ if (action.action_kind == "window" && action.wnd_wnd != null && !action.wnd_no_close) {
+ action.wnd_wnd.close();
+ action.wnd_wnd = null;
+ }
+ }
+ }
+ }
+ return r;
+};
+
+Test.AnotherWay._unprint_result = function(child){
+ var results = document.getElementById("results");
+ results.removeChild(child);
+};
+
+Test.AnotherWay._toggle_detail = function(){
+ if (this.id.match(/^result(\d+)$/)) {
+ var detail = document.getElementById("result_detail" + RegExp.$1);
+ if (detail != null) {
+ if (detail.style.display == "none") {
+ detail.style.display = "block";
+ }
+ else
+ if (detail.style.display == "block") {
+ detail.style.display = "none";
+ }
+ }
+ }
+};
+
+Test.AnotherWay._update_msg_counter = function(msg, text){
+ for (var i = 0; i < msg.childNodes.length; ++i) {
+ var item = msg.childNodes[i];
+ if (item.nodeName == "SPAN" && Test.AnotherWay._get_css_class(item) == "counter") {
+ item.innerHTML = text;
+ }
+ }
+};
+
+Test.AnotherWay._update_results_total = function(msg){
+ var total = document.getElementById("total");
+ if (total) {
+ total.innerHTML = msg;
+ }
+};
+
+Test.AnotherWay._results_clear_onclick = function(){
+ var results = document.getElementById("results");
+ results.innerHTML = "";
+ Test.AnotherWay._update_results_total("");
+ Test.AnotherWay._g_ok_pages = 0;
+ Test.AnotherWay._g_fail_pages = 0;
+ Test.AnotherWay._openlayers_sum_total_detail_ok=0;
+ Test.AnotherWay._openlayers_sum_total_detail_fail=0;
+ var debug = document.getElementById("debug");
+ debug.innerHTML = "";
+ Test.AnotherWay.reset_running_time();
+};
+
+Test.AnotherWay._get_css_class = function(o){
+ var c = o.getAttribute("className");
+ if (c == null || c == "") {
+ c = o.getAttribute("class");
+ }
+ return c;
+};
+
+Test.AnotherWay._set_css_class = function(o, css_class){
+ o.setAttribute("className", css_class);
+ o.setAttribute("class", css_class);
+};
+
+Test.AnotherWay._tab_onclick = function(){
+ var tab = this;
+ var tabs = [document.getElementById("debug_tab"), document.getElementById("results_tab")];
+ var panes = [document.getElementById("debug"), document.getElementById("results")];
+ for (var i = 0; i < tabs.length; ++i) {
+ if (tab == tabs[i]) {
+ Test.AnotherWay._set_css_class(tabs[i], "active_tab");
+ panes[i].style.display = "block";
+ }
+ else {
+ Test.AnotherWay._set_css_class(tabs[i], "inactive_tab");
+ panes[i].style.display = "none";
+ }
+ }
+};
+Test.AnotherWay._tab_mouseover = function(){
+ if (Test.AnotherWay._get_css_class(this) == "inactive_tab") {
+ Test.AnotherWay._set_css_class(this, "inactive_mouseover_tab");
+ }
+};
+Test.AnotherWay._tab_mouseout = function(){
+ if (Test.AnotherWay._get_css_class(this) == "inactive_mouseover_tab") {
+ Test.AnotherWay._set_css_class(this, "inactive_tab");
+ }
+};
+
+// recording mouse input
+Test.AnotherWay._record_check_onfocus = function(){
+ var o = this;
+ var check_select = o.type != "text";
+ var div = document.getElementById("record_div");
+ var inputs = div.getElementsByTagName("input");
+ for (var i = 0; i < inputs.length; ++i) {
+ var input = inputs[i];
+ if (input.type == "radio") {
+ if (input.value == "select") {
+ input.checked = check_select;
+ }
+ else
+ if (input.value == "input") {
+ input.checked = !check_select;
+ }
+ }
+ }
+};
+
+Test.AnotherWay._g_no_record_msg = null; // not null - recording is unavailable
+Test.AnotherWay._g_record_timeout_cnt = 0; // opening window for a page for recording
+Test.AnotherWay._g_record_url = null;
+Test.AnotherWay._g_record_wnd = null;
+Test.AnotherWay._g_record_random_id = null; // added to element ids of record_control div so that they do not clash with ids already in the page for which input is recorded
+Test.AnotherWay._g_record_keydown = null; // recording control - which key is down
+Test.AnotherWay._g_record_ctrl_keydown = false;
+Test.AnotherWay._g_record_shift_keydown = false;
+Test.AnotherWay._g_record_control_visible = true; // recording control ui state
+Test.AnotherWay._g_record_started;
+Test.AnotherWay._g_record_paused;
+Test.AnotherWay._g_record_include_mousemove = false;
+Test.AnotherWay._g_record_start_time; // for time references
+Test.AnotherWay._g_record_pause_start_time;
+Test.AnotherWay._g_record_update_time_interval; // showing time in the control ui
+Test.AnotherWay._g_record_waiting_for_results = false; // waiting for results window to open
+Test.AnotherWay._g_record_events; // recorded events
+Test.AnotherWay._g_record_under_cursor; // track element under cursor
+Test.AnotherWay._g_record_checkpoint_count; // for checkpoint numbering
+Test.AnotherWay._g_record_mouse_over_record_control; // for avoiding record control highlight on mouseover
+Test.AnotherWay._g_record_highlighted_element = {
+ element: null,
+ x: null,
+ y: null
+};
+
+Test.AnotherWay._record_control_get_element = function(id){
+ if (Test.AnotherWay._g_record_wnd != null && Test.AnotherWay._g_record_wnd.document != null) {
+ return Test.AnotherWay._g_record_wnd.document.getElementById(id + Test.AnotherWay._g_record_random_id);
+ }
+ else {
+ return null;
+ }
+};
+Test.AnotherWay._record_start_onclick = function() // "record" button on the run_tests.html: open a window for a page for which input is recorded
+{
+ if (Test.AnotherWay._g_no_record_msg != null) {
+ alert(Test.AnotherWay._g_no_record_msg);
+ return;
+ }
+ if (Test.AnotherWay._g_record_timeout_cnt > 0 ||
+ (Test.AnotherWay._g_record_wnd != null && (Test.AnotherWay._g_record_wnd.closed != null && !Test.AnotherWay._g_record_wnd.closed))) { // in opera, closed is null.
+ alert("there is already window opened for recording input for a page " + Test.AnotherWay._g_record_url);
+ return;
+ }
+ var div = document.getElementById("record_div");
+ var inputs = div.getElementsByTagName("input");
+ var url = null;
+ for (var i = 0; i < inputs.length; ++i) {
+ var input = inputs[i];
+ if (input.type == "radio") {
+ if (input.value == "select" && input.checked) {
+ var index = document.getElementById("record_select").selectedIndex;
+ if (index > 0) {
+ url = Test.AnotherWay._g_test_page_urls[index - 1].url;
+ }
+ }
+ else
+ if (input.value == "input" && input.checked) {
+ url = document.getElementById("record_input").value;
+ }
+ }
+ }
+ if (url != null) {
+ Test.AnotherWay._g_record_url = url;
+ Test.AnotherWay._g_record_wnd = window.open(url, "_blank");
+ if (Test.AnotherWay._g_record_wnd == null) {
+ alert("unable to open new window for a page: " + url);
+ }
+ else {
+ Test.AnotherWay._g_record_timeout_cnt = 50;
+ setTimeout(Test.AnotherWay._record_window_timeout, 100);
+ }
+ }
+};
+Test.AnotherWay._record_window_timeout = function(){
+ if (Test.AnotherWay._is_url_loaded(Test.AnotherWay._g_record_url, Test.AnotherWay._g_record_wnd)) {
+ Test.AnotherWay._record_window_setup(Test.AnotherWay._g_record_wnd);
+ }
+ else {
+ if (--Test.AnotherWay._g_record_timeout_cnt > 0) {
+ setTimeout(Test.AnotherWay._record_window_timeout, 100);
+ }
+ else {
+ alert("timeout expired while opening new window for a page: " + Test.AnotherWay._g_record_url);
+ Test.AnotherWay._g_record_wnd = null;
+ Test.AnotherWay._g_record_url = null;
+ Test.AnotherWay._g_record_timeout_cnt = 0;
+ }
+ }
+};
+Test.AnotherWay._record_control_randomize_id = function(e, r){
+ if (e.id != "") {
+ e.id = e.id + r;
+ }
+ for (var c = e.firstChild; c != null; c = c.nextSibling) {
+ Test.AnotherWay._record_control_randomize_id(c, r);
+ }
+};
+Test.AnotherWay._record_window_setup = function(wnd) // insert recording control into the page for which input is recorded
+{
+ Test.AnotherWay._g_record_timeout_cnt = 0;
+ var this_div = document.getElementById("record_control");
+ var record_control = wnd.document.importNode(this_div, true);
+ Test.AnotherWay._g_record_random_id = (1000 * Math.random()).toFixed();
+ Test.AnotherWay._record_control_randomize_id(record_control, Test.AnotherWay._g_record_random_id);
+ Test.AnotherWay._g_record_control_visible = true;
+ Test.AnotherWay._g_record_started = false;
+ Test.AnotherWay._g_record_paused = false;
+ Test.AnotherWay._g_record_checkpoint_count = 0;
+ Test.AnotherWay._g_record_mouse_over_record_control = false;
+ var doc = wnd.document;
+ doc.body.appendChild(record_control);
+ // opera sans-serif font is different
+ if (window.opera) {
+ cursor_over_indicator = Test.AnotherWay._record_control_get_element("record_cursor_over");
+ cursor_over_indicator.style.width = "18em";
+ cursor_over_indicator.style.height = "2em";
+ cursor_over_indicator.style.fontSize = "7pt";
+ }
+ doc.addEventListener("keydown", Test.AnotherWay._record_control_keydown, true);
+ doc.addEventListener("keyup", Test.AnotherWay._record_control_keyup, true);
+ // doc.addEventListener( "keypress", Test.AnotherWay._record_event, true ); // replaying is not supported by any known browser
+
+ doc.body.addEventListener("mousemove", Test.AnotherWay._record_on_mousemove, true);
+ doc.body.addEventListener("click", Test.AnotherWay._record_event, true);
+ doc.body.addEventListener("mouseover", Test.AnotherWay._record_event, true);
+ doc.body.addEventListener("mouseout", Test.AnotherWay._record_event, true);
+ doc.body.addEventListener("mousedown", Test.AnotherWay._record_event, true);
+ doc.body.addEventListener("mouseup", Test.AnotherWay._record_event, true);
+};
+Test.AnotherWay._record_control_key_disabled = function(k){
+ if (k == "c") {
+ return !Test.AnotherWay._g_record_started;
+ }
+ else
+ if (k == "p") {
+ return !Test.AnotherWay._g_record_started;
+ }
+ else
+ if (k == "s") {
+ return Test.AnotherWay._g_record_waiting_for_results;
+ }
+ else {
+ return false;
+ }
+};
+
+Test.AnotherWay._record_control_update_ui = function(){
+ var keydown_color = "#fff";
+ var disabled_color = "#aaa";
+ var button_color = "#adf";
+ var active_color = "#fdf";
+
+ var display = {};
+ display[false] = "none";
+ display[true] = "inline";
+
+ var s_button = Test.AnotherWay._record_control_get_element("record_s");
+ var record_on = Test.AnotherWay._record_control_get_element("record_on");
+ var record_off = Test.AnotherWay._record_control_get_element("record_off");
+
+ s_button.style.backgroundColor = Test.AnotherWay._record_control_key_disabled("s") ? disabled_color : Test.AnotherWay._g_record_keydown == "s" ? keydown_color : Test.AnotherWay._g_record_started ? active_color : button_color;
+ record_on.style.display = display[!Test.AnotherWay._g_record_started];
+ record_off.style.display = display[Test.AnotherWay._g_record_started];
+
+ var h_button = Test.AnotherWay._record_control_get_element("record_h");
+ h_button.style.backgroundColor = Test.AnotherWay._g_record_keydown == "h" ? keydown_color : button_color;
+
+ var p_button = Test.AnotherWay._record_control_get_element("record_p");
+ var record_pause_on = Test.AnotherWay._record_control_get_element("record_pause_on");
+ var record_pause_off = Test.AnotherWay._record_control_get_element("record_pause_off");
+ p_button.style.backgroundColor = Test.AnotherWay._record_control_key_disabled("p") ? disabled_color : Test.AnotherWay._g_record_keydown == "p" ? keydown_color : Test.AnotherWay._g_record_paused ? active_color : button_color;
+ record_pause_on.style.display = display[!Test.AnotherWay._g_record_paused];
+ record_pause_off.style.display = display[Test.AnotherWay._g_record_paused];
+
+ var m_button = Test.AnotherWay._record_control_get_element("record_m");
+ var record_include_mousemove = Test.AnotherWay._record_control_get_element("record_include_mousemove");
+ var record_omit_mousemove = Test.AnotherWay._record_control_get_element("record_omit_mousemove");
+ m_button.style.backgroundColor = Test.AnotherWay._g_record_keydown == "m" ? keydown_color : Test.AnotherWay._g_record_include_mousemove ? active_color : button_color;
+ record_include_mousemove.style.display = display[!Test.AnotherWay._g_record_include_mousemove];
+ record_omit_mousemove.style.display = display[Test.AnotherWay._g_record_include_mousemove];
+
+ var c_button = Test.AnotherWay._record_control_get_element("record_c");
+ c_button.style.backgroundColor = Test.AnotherWay._record_control_key_disabled("c") ? disabled_color : Test.AnotherWay._g_record_keydown == "c" ? keydown_color : button_color;
+
+ var record_indicator = Test.AnotherWay._record_control_get_element("record_indicator");
+ record_indicator.style.display = display[Test.AnotherWay._g_record_started];
+
+ var pause_indicator = Test.AnotherWay._record_control_get_element("record_pause_indicator");
+ pause_indicator.style.display = display[Test.AnotherWay._g_record_paused];
+
+ var record_control = Test.AnotherWay._record_control_get_element("record_control");
+ record_control.style.display = Test.AnotherWay._g_record_control_visible ? "block" : "none";
+
+ var shift_button = Test.AnotherWay._record_control_get_element("record_shift_key");
+ shift_button.style.backgroundColor = Test.AnotherWay._g_record_shift_keydown ? keydown_color : button_color;
+
+ var ctrl_button = Test.AnotherWay._record_control_get_element("record_ctrl_key");
+ ctrl_button.style.backgroundColor = Test.AnotherWay._g_record_ctrl_keydown ? keydown_color : button_color;
+};
+Test.AnotherWay._record_format_time = function(t){
+ t = new Date(t);
+ var m = t.getMinutes();
+ var s = t.getSeconds();
+ var str = m == 0 ? "" : m + "m ";
+ str += s + "s.";
+ return str;
+};
+Test.AnotherWay._record_control_update_time = function(){
+ var time_display = Test.AnotherWay._record_control_get_element("record_time");
+ if (time_display != null) {
+ time_display.innerHTML = Test.AnotherWay._record_format_time((new Date()).getTime() - Test.AnotherWay._g_record_start_time);
+ }
+};
+Test.AnotherWay._record_control_update_highlight = function(elem, style, event){
+ if (elem == null) {
+ Test.AnotherWay._record_highlight_border(null);
+ }
+ else {
+ var pos = Test.AnotherWay._get_page_coords(elem);
+ if (style == "ball" || elem != Test.AnotherWay._g_record_highlighted_element.element || pos.x != Test.AnotherWay._g_record_highlighted_element.x || pos.y != Test.AnotherWay._g_record_highlighted_element.y) {
+ Test.AnotherWay._g_record_highlighted_element = {
+ element: elem,
+ x: pos.x,
+ y: pos.y
+ };
+ Test.AnotherWay._record_highlight_border(elem, style, event);
+ }
+ }
+};
+Test.AnotherWay._record_decode_key = function(event){
+ var k = null;
+ if (event == null) {
+ k = Test.AnotherWay._g_record_wnd.event.keyCode;
+ }
+ else {
+ k = event.which;
+ }
+ if (k == 83) {
+ return "s";
+ }
+ else
+ if (k == 72) {
+ return "h";
+ }
+ else
+ if (k == 73) {
+ return "i";
+ }
+ else
+ if (k == 80) {
+ return "p";
+ }
+ else
+ if (k == 67) {
+ return "c";
+ }
+ else
+ if (k == 77) {
+ return "m";
+ }
+ else
+ if (k == 16) {
+ return "shift";
+ }
+ else
+ if (k == 17) {
+ return "ctrl";
+ }
+ else
+ if (k == 18) {
+ return "alt";
+ }
+ else
+ if (k == 19) {
+ return "pause";
+ }
+ else
+ if (k == 123) {
+ return "f12";
+ }
+ return "";
+};
+Test.AnotherWay._record_control_keydown = function(event){
+ var handled = false;
+ var k = Test.AnotherWay._record_decode_key(event);
+ if (k == "shift") {
+ Test.AnotherWay._g_record_shift_keydown = true;
+ }
+ else
+ if (k == "ctrl") {
+ Test.AnotherWay._g_record_ctrl_keydown = true;
+ }
+ else
+ if (k != "" && (Test.AnotherWay._g_record_keydown == null || Test.AnotherWay._g_record_keydown == k)) {
+ if (Test.AnotherWay._g_record_ctrl_keydown && Test.AnotherWay._g_record_shift_keydown && !Test.AnotherWay._record_control_key_disabled(k)) {
+ Test.AnotherWay._g_record_keydown = k;
+ handled = true;
+ }
+ }
+ else {
+ Test.AnotherWay._g_record_keydown = "";
+ }
+ Test.AnotherWay._record_control_update_ui();
+ if (!handled) {
+ // Test.AnotherWay._record_event( event ); // replaying is not supported in any known browser
+ }
+ return;
+};
+Test.AnotherWay._record_control_keyup = function(event){
+ var handled = false;
+ var k = Test.AnotherWay._record_decode_key(event);
+ if (k == "shift") {
+ Test.AnotherWay._g_record_shift_keydown = false;
+ }
+ else
+ if (k == "ctrl") {
+ Test.AnotherWay._g_record_ctrl_keydown = false;
+ }
+ else
+ if (k != "" && k == Test.AnotherWay._g_record_keydown && Test.AnotherWay._g_record_ctrl_keydown && Test.AnotherWay._g_record_shift_keydown) {
+ if (k == "s") {
+ Test.AnotherWay._g_record_started = !Test.AnotherWay._g_record_started;
+ if (Test.AnotherWay._g_record_started) {
+ Test.AnotherWay._g_record_events = [];
+ Test.AnotherWay._g_record_start_time = (new Date()).getTime();
+ Test.AnotherWay._record_control_update_time();
+ Test.AnotherWay._g_record_update_time_interval = window.setInterval(Test.AnotherWay._record_control_update_time, 200);
+ }
+ else {
+ Test.AnotherWay._record_control_update_highlight(null);
+ if (!Test.AnotherWay._g_record_paused) {
+ window.clearInterval(Test.AnotherWay._g_record_update_time_interval);
+ }
+ Test.AnotherWay._g_record_waiting_for_results = true;
+ // open a new window for self, pass a parameter to dump recorded events as javascript code there
+ // (the easiest way to obtain a document from the same origin, so it's writable, is to open this same page again)
+ Test.AnotherWay._g_record_paused = false;
+ var loc = window.location;
+ loc = loc.protocol + "//" + loc.host + loc.pathname + "?recording_results=" + Test.AnotherWay._g_record_random_id;
+ if (window.open(loc, "_blank") == null) {
+ alert("unable to open new window for results");
+ }
+ }
+ handled = true;
+ }
+ else
+ if (k == "h") {
+ Test.AnotherWay._g_record_control_visible = !Test.AnotherWay._g_record_control_visible;
+ handled = true;
+ }
+ else
+ if (k == "p") {
+ Test.AnotherWay._g_record_paused = !Test.AnotherWay._g_record_paused;
+ if (Test.AnotherWay._g_record_paused) {
+ Test.AnotherWay._g_record_pause_start_time = (new Date()).getTime();
+ if (Test.AnotherWay._g_record_started) {
+ window.clearInterval(Test.AnotherWay._g_record_update_time_interval);
+ }
+ Test.AnotherWay._record_control_update_highlight(null);
+ }
+ else {
+ var pause_duration = (new Date()).getTime() - Test.AnotherWay._g_record_pause_start_time;
+ Test.AnotherWay._g_record_start_time += pause_duration;
+ Test.AnotherWay._g_record_update_time_interval = window.setInterval(Test.AnotherWay._record_control_update_time, 200);
+ }
+ handled = true;
+ }
+ else
+ if (k == "m") {
+ Test.AnotherWay._g_record_include_mousemove = !Test.AnotherWay._g_record_include_mousemove;
+ handled = true;
+ }
+ else
+ if (k == "c") {
+ var o = Test.AnotherWay._record_checkpoint();
+ Test.AnotherWay._record_display_checkpoint(o);
+ Test.AnotherWay._record_flash_border("#24d");
+ handled = true;
+ }
+ }
+ Test.AnotherWay._g_record_keydown = null;
+ Test.AnotherWay._record_control_update_ui();
+ if (!handled) {
+ // Test.AnotherWay._record_event( event ); // replaying is not supported in any known browser
+ }
+ return;
+};
+Test.AnotherWay._record_html_node_path = function(node){
+ if (node == null) {
+ return null;
+ }
+ var path = [];
+ while (true) {
+ if (node.id != null && node.id != "") {
+ path.unshift("#" + node.id + " " + node.nodeName);
+ break;
+ }
+ else {
+ var parent_node = node.parentNode;
+ if (parent_node == null) {
+ return []; // no BODY up the path - this node is screwed (browsers differ in what's above the body), discard
+ }
+ else {
+ var i = 0;
+ var found = false;
+ for (var child = parent_node.firstChild; child != null; child = child.nextSibling) {
+ if (child == node) {
+ found = true;
+ break;
+ }
+ if (child.nodeType == 1) { // count only HTML element nodes
+ ++i;
+ }
+ }
+ if (!found) {
+ i = -1;
+ }
+ path.unshift(i + " " + node.nodeName);
+ if (parent_node.nodeName == "BODY" || parent_node.nodeName == "body") {
+ break;
+ }
+ node = parent_node;
+ }
+ }
+ }
+ return path;
+};
+Test.AnotherWay._record_node_path_to_string = function(path){
+ var s = "";
+ if (path != null) {
+ for (var i = 0; i < path.length; ++i) {
+ s += i == 0 ? "" : ", ";
+ var elem = path[i].split(" ");
+ if (elem[0].charAt(0) == "#") {
+ s += elem[1] + " " + elem[0];
+ }
+ else {
+ s += elem[1] + " [" + elem[0] + "]";
+ }
+ }
+ }
+ return s;
+};
+Test.AnotherWay._record_node_path_to_node = function(path_str, doc){
+ if (path_str == null) {
+ return null;
+ }
+ var path = path_str.split(",");
+ var node = doc.body;
+ for (var i = 0; i < path.length; ++i) {
+ var node_i = path[i].split(" ")[0];
+ if (node_i.charAt(0) == "#") {
+ node = doc.getElementById(node_i.substring(1));
+ }
+ else {
+ if (node_i < 0 || node_i >= node.childNodes.length) {
+ node = null;
+ }
+ else {
+ node = node.firstChild;
+ while (node != null) {
+ if (node.nodeType == 1) { // count only HTML element nodes
+ if (node_i == 0) {
+ break;
+ }
+ --node_i;
+ }
+ node = node.nextSibling;
+ }
+ }
+ }
+ if (node == null) {
+ return null;
+ }
+ }
+ return node;
+};
+Test.AnotherWay._record_control_contains_id = function(s){
+ return s.match(/^#record_[\w_]+/) && s.match(Test.AnotherWay._g_record_random_id);
+};
+Test.AnotherWay._record_checkpoint = function(){
+ var o = {
+ type: "_checkpoint",
+ time: (new Date()).getTime() - Test.AnotherWay._g_record_start_time,
+ which: Test.AnotherWay._g_record_checkpoint_count++,
+ target: Test.AnotherWay._record_html_node_path(Test.AnotherWay._g_record_under_cursor)
+ };
+ Test.AnotherWay._g_record_events.push(o);
+ return o;
+};
+Test.AnotherWay._record_event = function(event){
+ var unneeded = ["rangeOffset", "eventPhase", "timeStamp", "isTrusted", "popupWindowFeatures", "rangeOffset"];
+ if (Test.AnotherWay._g_record_started && !Test.AnotherWay._g_record_paused) {
+ var o = {};
+ for (var n in event) {
+ var needed = !n.match(/^[A-Z0-9_]+$/);
+ if (needed) {
+ for (var ui = 0; ui < unneeded.length; ++ui) {
+ if (unneeded[ui] == n) {
+ needed = false;
+ break;
+ }
+ }
+ if (needed) {
+ var value = event[n];
+ if (typeof(value) != "object" && typeof(value) != "function") {
+ o[n] = value;
+ }
+ else
+ if (n == "target" || n == "relatedTarget") {
+ o[n] = Test.AnotherWay._record_html_node_path(value);
+ }
+ }
+ }
+ }
+ o["time"] = (new Date()).getTime() - Test.AnotherWay._g_record_start_time;
+ var over_record_control = o["target"] != null && o["target"][0] != null && Test.AnotherWay._record_control_contains_id(o["target"][0]);
+ if (!over_record_control) {
+ Test.AnotherWay._g_record_events.push(o);
+ }
+ }
+ return true;
+};
+Test.AnotherWay._record_on_mousemove = function(event){
+ var path = Test.AnotherWay._record_html_node_path(event.target);
+ var new_mouse_over_record_control = path != null && path[0] != null && Test.AnotherWay._record_control_contains_id(path[0]);
+ if (new_mouse_over_record_control != Test.AnotherWay._g_record_mouse_over_record_control) {
+ Test.AnotherWay._g_record_mouse_over_record_control = new_mouse_over_record_control;
+ Test.AnotherWay._record_control_update_ui();
+ }
+ if (event.target != null && event.target != Test.AnotherWay._g_record_under_cursor) {
+ Test.AnotherWay._g_record_under_cursor = event.target;
+ var s = "";
+ if (path == null || path[0] == null || !Test.AnotherWay._record_control_contains_id(path[0])) {
+ s = Test.AnotherWay._record_node_path_to_string(path);
+ }
+ if (s == "") {
+ s = "&nbsp;";
+ }
+ var cursor_over_indicator = Test.AnotherWay._record_control_get_element("record_cursor_over");
+ cursor_over_indicator.innerHTML = s;
+ }
+
+ var highlight_element = null;
+ if (!Test.AnotherWay._g_record_mouse_over_record_control && Test.AnotherWay._g_record_started && !Test.AnotherWay._g_record_paused) {
+ highlight_element = event.target;
+ }
+ // highlight border disabled on recording - it causes page to scroll, issuing spurious mouseover/mouseout event
+ //Test.AnotherWay._record_control_update_highlight( highlight_element, "border" );
+
+ if (Test.AnotherWay._g_record_include_mousemove) {
+ Test.AnotherWay._record_event(event);
+ }
+ return true;
+};
+Test.AnotherWay._record_display_checkpoint = function(o){
+ var checkpoints_div = Test.AnotherWay._record_control_get_element("record_checkpoints");
+ var p = checkpoints_div.appendChild(checkpoints_div.ownerDocument.createElement("div"));
+ p.style.marginTop = "3px";
+ p.style.font = "normal normal 8pt sans-serif";
+ p.style.color = "#000";
+ p.style.textAligh = "left";
+ p.style.position = "relative";
+ p.style.width = "100%";
+ var checkpoint_text = "";
+ checkpoint_text += "#" + (o.which + 1);
+ checkpoint_text += " " + Test.AnotherWay._record_format_time(o.time);
+ if (o.target != null) {
+ checkpoint_text += Test.AnotherWay._record_node_path_to_string(o.target);
+ }
+ p.appendChild(p.ownerDocument.createTextNode(checkpoint_text));
+};
+Test.AnotherWay._record_save_results = function(doc){
+ // strange, but DOM-style append does not work here in opera 8.
+ var append = function(s){
+ doc.write("<div>" + s + "</div>");
+ };
+ append("/* paste this data into your javascript and pass it as an argument to replay_events method */");
+ append("{ checkpoints: [");
+ var first_checkpoint = true;
+ for (var i = 0; i < Test.AnotherWay._g_record_events.length; ++i) {
+ var o = Test.AnotherWay._g_record_events[i];
+ if (o.type == "_checkpoint") {
+ var str = first_checkpoint ? "" : "}, ";
+ str += "function( tst, wnd ) { // #" + o.which + " time " + Test.AnotherWay._record_format_time(o.time) + " cursor was over " + Test.AnotherWay._record_node_path_to_string(o.target);
+ append(str);
+ first_checkpoint = false;
+ }
+ }
+ if (!first_checkpoint) {
+ append("}");
+ }
+ append("], events: [ ");
+ var prev_time = 0;
+ for (var i = 0; i < Test.AnotherWay._g_record_events.length; ++i) {
+ var o = Test.AnotherWay._g_record_events[i];
+ var s = "";
+ s += "{";
+ var n_first = true;
+ for (var n in o) {
+ if (n == "time") { // convert to relative time
+ var cur_time = o[n] - 0;
+ o[n] = cur_time - prev_time;
+ prev_time = cur_time;
+ }
+ s += n_first ? n : ", " + n;
+ s += ":";
+ if (o[n] == null) {
+ s += "null";
+ }
+ else {
+ s += "\"" + o[n] + "\"";
+ }
+ n_first = false;
+ }
+ s += i == Test.AnotherWay._g_record_events.length - 1 ? "}" : "},";
+ append(s);
+ }
+ append("] }");
+ append(";");
+};
+
+Test.AnotherWay._g_record_border; // border highlighting element under cursor
+Test.AnotherWay._g_record_border_flashes = []; // array of { color: color, timeout: milliseconds }
+Test.AnotherWay._g_record_border_flashing = false;
+Test.AnotherWay._g_record_border_normal_color = "#d4b";
+Test.AnotherWay._record_flash_border_timeout = function(){
+ var color = Test.AnotherWay._g_record_border_normal_color;
+ var timeout = null;
+ if (Test.AnotherWay._g_record_border_flashes.length != 0) {
+ color = Test.AnotherWay._g_record_border_flashes[0].color;
+ timeout = Test.AnotherWay._g_record_border_flashes[0].timeout;
+ Test.AnotherWay._g_record_border_flashes.splice(0, 1);
+ }
+ if (Test.AnotherWay._g_record_border != null) {
+ for (var i = 0; i < Test.AnotherWay._g_record_border.length; ++i) {
+ Test.AnotherWay._g_record_border[i].style.backgroundColor = color;
+ }
+ }
+ if (timeout != null) {
+ setTimeout(Test.AnotherWay._record_flash_border_timeout, timeout);
+ }
+ else {
+ Test.AnotherWay._g_record_border_flashing = false;
+ }
+};
+Test.AnotherWay._get_page_coords = function(elm){
+ var point = {
+ x: 0,
+ y: 0
+ };
+ while (elm) {
+ point.x += elm.offsetLeft;
+ point.y += elm.offsetTop;
+ elm = elm.offsetParent;
+ }
+ return point;
+};
+Test.AnotherWay._set_page_coords = function(elm, x, y){
+ var parent_coords = {
+ x: 0,
+ y: 0
+ };
+ if (elm.offsetParent) {
+ parent_coords = Test.AnotherWay._get_page_coords(elm.offsetParent);
+ }
+ var new_x = x - parent_coords.x;
+ if (new_x < 0) {
+ new_x = 0;
+ }
+ elm.style.left = new_x + 'px';
+ var new_y = y - parent_coords.y;
+ if (new_y < 0) {
+ new_y = 0;
+ }
+ elm.style.top = new_y + 'px';
+};
+Test.AnotherWay._record_setup_highlight_positions = function(element, style, coords, positions){
+ if (style == "border") {
+ var width = element.clientWidth;
+ var height = element.clientHeight;
+ var step = 0;
+ var thickness = 2;
+ var fudge_expand = 4;
+ positions.push({
+ x: coords.x - step - thickness,
+ y: coords.y - step - thickness,
+ width: width + 2 * step + 2 * thickness + fudge_expand,
+ height: thickness
+ });
+ positions.push({
+ x: coords.x + width + step + fudge_expand,
+ y: coords.y - step - thickness,
+ width: thickness,
+ height: height + 2 * step + 2 * thickness + fudge_expand
+ });
+ positions.push({
+ x: positions[0].x,
+ y: positions[0].y,
+ width: positions[0].width,
+ height: positions[0].height
+ });
+ positions.push({
+ x: positions[1].x,
+ y: positions[1].y,
+ width: positions[1].width,
+ height: positions[1].height
+ });
+ positions[2].y += height + thickness + 2 * step + fudge_expand;
+ positions[3].x -= width + thickness + 2 * step + fudge_expand;
+ }
+ else
+ if (style == "ball") {
+ positions.push({
+ x: coords.x + 2,
+ y: coords.y,
+ width: 2,
+ height: 6
+ });
+ positions.push({
+ x: coords.x,
+ y: coords.y + 2,
+ width: 6,
+ height: 2
+ });
+ positions.push({
+ x: coords.x + 1,
+ y: coords.y + 1,
+ width: 4,
+ height: 4
+ });
+ }
+};
+Test.AnotherWay._record_highlight_border = function(element, style, event) // null - hide border
+{
+ if (element != null) {
+ if (Test.AnotherWay._g_record_border == null || Test.AnotherWay._g_record_border[0].ownerDocument != element.ownerDocument) {
+ Test.AnotherWay._g_record_border = [];
+ var n = style == "border" ? 4 : style == "ball" ? 3 : 0;
+ for (var i = 0; i < 4; ++i) {
+ var b = element.ownerDocument.createElement("div");
+ b.style.position = "absolute";
+ b.style.zIndex = "1";
+ b.style.backgroundColor = Test.AnotherWay._g_record_border_normal_color;
+ element.ownerDocument.body.appendChild(b);
+ Test.AnotherWay._g_record_border.push(b);
+ }
+ }
+ var coords = null;
+ if (style == "border") {
+ coords = Test.AnotherWay._get_page_coords(element);
+ }
+ else
+ if (style == "ball") {
+ if (event != null) {
+ if (event.pageX != null && event.pageY != null) {
+ coords = {
+ x: event.pageX - 0,
+ y: event.pageY - 0
+ };
+ }
+ else
+ if (event.clientX != null && event.clientY != null) {
+ var doc = element.ownerDocument;
+ if (doc != null) {
+ coords = {
+ x: (event.clientX - 0) + doc.body.scrollLeft,
+ y: (event.clientY - 0) + doc.body.scrollTop
+ };
+ }
+ }
+ }
+ }
+ if (coords != null && element.clientWidth != null && element.clientHeight != null) {
+ var positions = [];
+ Test.AnotherWay._record_setup_highlight_positions(element, style, coords, positions);
+ for (var i = 0; i < positions.length; ++i) {
+ var b = Test.AnotherWay._g_record_border[i];
+ var p = positions[i];
+ Test.AnotherWay._set_page_coords(b, p.x, p.y);
+ b.style.width = p.width + "px";
+ b.style.height = p.height + "px";
+ b.style.display = "block";
+ }
+ }
+ }
+ else {
+ if (Test.AnotherWay._g_record_border != null) {
+ for (var i = 0; i < Test.AnotherWay._g_record_border.length; ++i) {
+ Test.AnotherWay._g_record_border[i].style.display = "none";
+ }
+ }
+ }
+};
+Test.AnotherWay._record_flash_border = function(color){
+ if (Test.AnotherWay._g_record_border_flashing) { //already
+ Test.AnotherWay._g_record_border_flashes.push({
+ color: Test.AnotherWay._g_record_border_normal_color,
+ timeout: 300
+ });
+ Test.AnotherWay._g_record_border_flashes.push({
+ color: color,
+ timeout: 600
+ });
+ }
+ else {
+ Test.AnotherWay._g_record_border_flashing = true;
+ Test.AnotherWay._g_record_border_flashes.push({
+ color: color,
+ timeout: 600
+ });
+ Test.AnotherWay._record_flash_border_timeout();
+ }
+};
+Test.AnotherWay._record_prepare_doc_for_results = function(){
+ document.open();
+ document.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">");
+ document.write("<html><head><title> Input recording results</title>");
+ document.write("<style type=\"text/css\">");
+ document.write("body { font: normal normal smaller sans-serif; }");
+ document.write("div { margin-top: 3px; }");
+ document.write("</style></head><body>");
+ // opera and mozilla disagree over who the opener is.
+ if (typeof(window.opener.Test) != "undefined" && typeof(window.opener.Test.AnotherWay) != "undefined") {
+ window.opener.Test.AnotherWay._record_save_results(document);
+ window.opener.Test.AnotherWay._g_record_waiting_for_results = false;
+ window.opener.Test.AnotherWay._record_control_update_ui();
+ }
+ else
+ if (typeof(window.opener.opener.Test) != "undefined" && typeof(window.opener.opener.Test.AnotherWay) != "undefined") {
+ window.opener.opener.Test.AnotherWay._record_save_results(document);
+ window.opener.opener.Test.AnotherWay._g_record_waiting_for_results = false;
+ window.opener.opener.Test.AnotherWay._record_control_update_ui();
+ }
+ document.write("</body>");
+ document.close();
+};
+
+// global initialization
+onload = function(){
+ if (window.opera) {
+ var good_opera = typeof(window.opera.version) == "function";
+ good_opera = good_opera && window.opera.version().match(/^\s*(\d+)/);
+ good_opera = good_opera && RegExp.$1 >= 8;
+ }
+ var span = document.createElement("SPAN");
+ span.innerHTML = "<!--[if IE]><br /><![endif]-" + "->";
+ var is_ie = span.getElementsByTagName("BR").length > 0;
+
+ Test.AnotherWay._g_test_iframe = window.frames.test_iframe;
+
+ var query_str = window.location.search;
+ if (query_str.charAt(0) == "?") {
+ query_str = query_str.substring(1);
+ }
+ var testlist_page = "list-tests.html";
+ var auto_run = false;
+ if (query_str != "") {
+ var params = [query_str];
+ if (query_str.indexOf(";") != -1) {
+ params = query_str.split(";");
+ }
+ else
+ if (query_str.indexOf("&") != -1) {
+ params = query_str.split("&");
+ }
+ for (var param_i = 0; param_i < params.length; ++param_i) {
+ var param = params[param_i].split("=");
+ if (param[0] == "recording_results") {
+ if (window.opener != null) {
+ // we were told to show recording results - replace everything in the document with the results
+ Test.AnotherWay._record_prepare_doc_for_results();
+ return;
+ }
+ }
+ else
+ if (param[0] == "testpage") {
+ Test.AnotherWay._add_test_page_url(decodeURIComponent(param[1]), "anotherway");
+ }
+ else
+ if (param[0] == "jsantestpage") {
+ Test.AnotherWay._add_test_page_url(decodeURIComponent(param[1]), "jsan");
+ }
+ else
+ if (param[0] == "testlist") {
+ testlist_page = decodeURIComponent(param[1]);
+ }
+ else
+ if (param[0] == "testframe") {
+ if (window.opera && !good_opera) {
+ Test.AnotherWay._show_error("testframe parameter does not work in versions of Opera prior to 8.0. Sorry (pathches are welcome).");
+ // Opera 7 barfs on attempt to access frame.frameElement.
+ // if someone knows a way to assign onload handler to that iframe in Opera 7
+ // without disrupting code that works in other browsers, patches are welcome.
+ }
+ else {
+ var frame_path = param[1].split(".");
+ var frame = top;
+ for (var frame_path_i = 0; frame_path_i < frame_path.length; ++frame_path_i) {
+ frame = frame[frame_path[frame_path_i]];
+ }
+ if (frame == null) {
+ Test.AnotherWay._show_error("unable to find frame specified for loading test pages: " + param[1]);
+ }
+ else {
+ if (frame.frameElement != null) { // for the following assignement to onload to work, frameElement is required
+ frame = frame.frameElement;
+ }
+ Test.AnotherWay._g_test_iframe = frame;
+ }
+ }
+ }
+ else
+ if (param[0] == "testframe_no_clear") {
+ Test.AnotherWay._g_test_frame_no_clear = true;
+ }
+ else
+ if (param[0] == "windows") {
+ if (param[1] == "none") {
+ Test.AnotherWay._test_object_t.prototype.open_window = null;
+ }
+ }
+ else
+ if (param[0] == "run") {
+ auto_run = true;
+ if (param[1] == "all") {
+ Test.AnotherWay._g_pages_to_run = "all";
+ }
+ else {
+ if (Test.AnotherWay._g_pages_to_run == null || Test.AnotherWay._g_pages_to_run == "all") {
+ Test.AnotherWay._g_pages_to_run = [];
+ }
+ var pages = param[1].split(",");
+ for (var i = 0; i < pages.length; ++i) {
+ Test.AnotherWay._g_pages_to_run.push(pages[i]);
+ }
+ }
+ }
+ }
+ }
+ if (Test.AnotherWay._g_test_page_urls.length == 0) { // if no individual pages were given on the command line, load the list
+ var result = Test.AnotherWay._set_iframe_location(window.frames["list_iframe"], testlist_page);
+ if (result.msg != null) {
+ Test.AnotherWay._show_error(result.msg);
+ }
+ Test.AnotherWay._g_run_on_list_load = auto_run;
+ }
+ else {
+ Test.AnotherWay._g_run_on_main_load = auto_run;
+ }
+
+ var f = Test.AnotherWay._g_test_iframe;
+ try {
+ if (f.attachEvent != null) {
+ f.attachEvent("onload", Test.AnotherWay._test_page_onload);
+ }
+ else {
+ f.onload = Test.AnotherWay._test_page_onload;
+ }
+ if (Test.AnotherWay._g_test_iframe.nodeType != null && Test.AnotherWay._g_test_iframe.contentWindow != null) { // it's iframe element, not the iframe. we need iframe.
+ Test.AnotherWay._g_test_iframe = Test.AnotherWay._g_test_iframe.contentWindow;
+ }
+ }
+ catch (e) {
+ // ignore stupid opera error if the frame has onload handler assigned in the inline html
+ }
+ var handlers = {
+ "run_all": {
+ "onclick": Test.AnotherWay._run_all_onclick
+ },
+ "run_selected": {
+ "onclick": Test.AnotherWay._run_selected_onclick
+ },
+ "unselect_all": {
+ "onclick": Test.AnotherWay._unselect_all_onclick
+ },
+ "record_select": {
+ "onfocus": Test.AnotherWay._record_check_onfocus
+ },
+ "record_input": {
+ "onfocus": Test.AnotherWay._record_check_onfocus
+ },
+ "record_start": {
+ "onclick": Test.AnotherWay._record_start_onclick
+ },
+ "clear_btn": {
+ "onclick": Test.AnotherWay._results_clear_onclick
+ },
+ "results_tab": {
+ "onclick": Test.AnotherWay._tab_onclick,
+ "onmouseover": Test.AnotherWay._tab_mouseover,
+ "onmouseout": Test.AnotherWay._tab_mouseout
+ },
+ "debug_tab": {
+ "onclick": Test.AnotherWay._tab_onclick,
+ "onmouseover": Test.AnotherWay._tab_mouseover,
+ "onmouseout": Test.AnotherWay._tab_mouseout
+ }
+ };
+ for (var hs in handlers) {
+ var o = document.getElementById(hs);
+ if (o != null) {
+ for (var h in handlers[hs]) {
+ o[h] = handlers[hs][h];
+ }
+ }
+ else {
+ Test.AnotherWay._show_error("unable to set " + h + " handler: id " + hs + " not found");
+ }
+ }
+
+ if (window.opera && !good_opera) {
+ Test.AnotherWay._g_no_record_msg = "Input events recording and replaying is not available in opera versions prior to 8.0.";
+ }
+ if (is_ie) {
+ Test.AnotherWay._g_no_record_msg = "Input events recording and replaying is not available in internet explorer.";
+ }
+ if (Test.AnotherWay._g_no_record_msg != null) {
+ var no_record_p = document.getElementById("record_not_supported");
+ no_record_p.style.display = "block";
+ no_record_p.appendChild(document.createTextNode(Test.AnotherWay._g_no_record_msg));
+ }
+
+ Test.AnotherWay._g_main_loaded = true;
+ if (Test.AnotherWay._g_run_on_main_load) {
+ Test.AnotherWay._g_run_on_main_load = false;
+ Test.AnotherWay._run_pages_to_run();
+ }
+};
diff --git a/misc/openlayers/tests/Test.AnotherWay.xml_eq.js b/misc/openlayers/tests/Test.AnotherWay.xml_eq.js
new file mode 100644
index 0000000..8c24566
--- /dev/null
+++ b/misc/openlayers/tests/Test.AnotherWay.xml_eq.js
@@ -0,0 +1,311 @@
+/**
+ * File: Test.AnotherWay.xml_eq.js
+ * Adds a xml_eq method to AnotherWay test objects.
+ *
+ */
+
+(function() {
+
+ /**
+ * Function: createNode
+ * Given a string, try to create an XML DOM node. Throws string messages
+ * on failure.
+ *
+ * Parameters:
+ * text - {String} An XML string.
+ *
+ * Returns:
+ * {DOMElement} An element node.
+ */
+ function createNode(text) {
+
+ var index = text.indexOf('<');
+ if(index > 0) {
+ text = text.substring(index);
+ }
+
+ var doc;
+ if(window.ActiveXObject && !this.xmldom) {
+ doc = new ActiveXObject("Microsoft.XMLDOM");
+ try {
+ doc.loadXML(text);
+ } catch(err) {
+ throw "ActiveXObject loadXML failed: " + err;
+ }
+ } else if(window.DOMParser) {
+ try {
+ doc = new DOMParser().parseFromString(text, 'text/xml');
+ } catch(err) {
+ throw "DOMParser.parseFromString failed";
+ }
+ if(doc.documentElement && doc.documentElement.nodeName == "parsererror") {
+ throw "DOMParser.parseFromString returned parsererror";
+ }
+ } else {
+ var req = new XMLHttpRequest();
+ req.open("GET", "data:text/xml;charset=utf-8," +
+ encodeURIComponent(text), false);
+ if(req.overrideMimeType) {
+ req.overrideMimeType("text/xml");
+ }
+ req.send(null);
+ doc = req.responseXML;
+ }
+
+ var root = doc.documentElement;
+ if(!root) {
+ throw "no documentElement";
+ }
+ return root;
+ }
+
+ /**
+ * Function assertEqual
+ * Test two objects for equivalence (based on ==). Throw an exception
+ * if not equivalent.
+ *
+ * Parameters:
+ * got - {Object}
+ * expected - {Object}
+ * msg - {String} The message to be thrown. This message will be appended
+ * with ": got {got} but expected {expected}" where got and expected are
+ * replaced with string representations of the above arguments.
+ */
+ function assertEqual(got, expected, msg) {
+ if(got === undefined) {
+ got = "undefined";
+ } else if (got === null) {
+ got = "null";
+ }
+ if(expected === undefined) {
+ expected = "undefined";
+ } else if (expected === null) {
+ expected = "null";
+ }
+ if(got != expected) {
+ throw msg + ": got '" + got + "' but expected '" + expected + "'";
+ }
+ }
+
+ /**
+ * Function assertElementNodesEqual
+ * Test two element nodes for equivalence. Nodes are considered equivalent
+ * if they are of the same type, have the same name, have the same
+ * namespace prefix and uri, and if all child nodes are equivalent.
+ * Throws a message as exception if not equivalent.
+ *
+ * Parameters:
+ * got - {DOMElement}
+ * expected - {DOMElement}
+ * options - {Object} Optional object for configuring test options.
+ *
+ * Valid options:
+ * prefix - {Boolean} Compare element and attribute
+ * prefixes (namespace uri always tested). Default is false.
+ * includeWhiteSpace - {Boolean} Include whitespace only nodes when
+ * comparing child nodes. Default is false.
+ */
+ function assertElementNodesEqual(got, expected, options) {
+ var testPrefix = (options && options.prefix === true);
+
+ // compare types
+ assertEqual(got.nodeType, expected.nodeType, "Node type mismatch");
+
+ // compare names
+ var gotName = testPrefix ?
+ got.nodeName : got.nodeName.split(":").pop();
+ var expName = testPrefix ?
+ expected.nodeName : expected.nodeName.split(":").pop();
+ assertEqual(gotName, expName, "Node name mismatch");
+
+ // for text nodes compare value
+ if(got.nodeType == 3) {
+ assertEqual(
+ got.nodeValue, expected.nodeValue, "Node value mismatch"
+ );
+ }
+ // for element type nodes compare namespace, attributes, and children
+ else if(got.nodeType == 1) {
+
+ // test namespace alias and uri
+ if(got.prefix || expected.prefix) {
+ if(testPrefix) {
+ assertEqual(
+ got.prefix, expected.prefix,
+ "Bad prefix for " + got.nodeName
+ );
+ }
+ }
+ if(got.namespaceURI || expected.namespaceURI) {
+ assertEqual(
+ got.namespaceURI, expected.namespaceURI,
+ "Bad namespaceURI for " + got.nodeName
+ );
+ }
+
+ // compare attributes - disregard xmlns given namespace handling above
+ var gotAttrLen = 0;
+ var gotAttr = {};
+ var expAttrLen = 0;
+ var expAttr = {};
+ var ga, ea, gn, en;
+ for(var i=0; i<got.attributes.length; ++i) {
+ ga = got.attributes[i];
+ if(ga.specified === undefined || ga.specified === true) {
+ if(ga.name.split(":").shift() != "xmlns") {
+ gn = testPrefix ? ga.name : ga.name.split(":").pop();
+ gotAttr[gn] = ga;
+ ++gotAttrLen;
+ }
+ }
+ }
+ for(var i=0; i<expected.attributes.length; ++i) {
+ ea = expected.attributes[i];
+ if(ea.specified === undefined || ea.specified === true) {
+ if(ea.name.split(":").shift() != "xmlns") {
+ en = testPrefix ? ea.name : ea.name.split(":").pop();
+ expAttr[en] = ea;
+ ++expAttrLen;
+ }
+ }
+ }
+ assertEqual(
+ gotAttrLen, expAttrLen,
+ "Attributes length mismatch for " + got.nodeName
+ );
+ var gv, ev;
+ for(var name in gotAttr) {
+ if(expAttr[name] == undefined) {
+ throw "Attribute name " + gotAttr[name].name + " expected for element " + got.nodeName;
+ }
+ // test attribute namespace
+ assertEqual(
+ gotAttr[name].namespaceURI, expAttr[name].namespaceURI,
+ "Attribute namespace mismatch for element " +
+ got.nodeName + " attribute name " + gotAttr[name].name
+ );
+ // test attribute value
+ assertEqual(
+ gotAttr[name].value, expAttr[name].value,
+ "Attribute value mismatch for element " + got.nodeName +
+ " attribute name " + gotAttr[name].name
+ );
+ }
+
+ // compare children
+ var gotChildNodes = getChildNodes(got, options);
+ var expChildNodes = getChildNodes(expected, options);
+
+ assertEqual(
+ gotChildNodes.length, expChildNodes.length,
+ "Children length mismatch for " + got.nodeName
+ );
+ for(var j=0; j<gotChildNodes.length; ++j) {
+ try {
+ assertElementNodesEqual(
+ gotChildNodes[j], expChildNodes[j], options
+ );
+ } catch(err) {
+ throw "Bad child " + j + " for element " + got.nodeName + ": " + err;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Function getChildNodes
+ * Returns the child nodes of the specified nodes. By default this method
+ * will ignore child text nodes which are made up of whitespace content.
+ * The 'includeWhiteSpace' option is used to control this behaviour.
+ *
+ * Parameters:
+ * node - {DOMElement}
+ * options - {Object} Optional object for test configuration.
+ *
+ * Valid options:
+ * includeWhiteSpace - {Boolean} Include whitespace only nodes when
+ * comparing child nodes. Default is false.
+ *
+ * Returns:
+ * {Array} of {DOMElement}
+ */
+ function getChildNodes(node, options) {
+ //check whitespace
+ if (options && options.includeWhiteSpace) {
+ return node.childNodes;
+ }
+ else {
+ nodes = [];
+ for (var i = 0; i < node.childNodes.length; i++ ) {
+ var child = node.childNodes[i];
+ if (child.nodeType == 1) {
+ //element node, add it
+ nodes.push(child);
+ }
+ else if (child.nodeType == 3) {
+ //text node, add if non empty
+ if (child.nodeValue &&
+ child.nodeValue.replace(/^\s*(.*?)\s*$/, "$1") != "" ) {
+
+ nodes.push(child);
+ }
+ }
+ }
+
+ return nodes;
+ }
+ }
+
+ /**
+ * Function: Test.AnotherWay._test_object_t.xml_eq
+ * Test if two XML nodes are equivalent. Tests for same node types, same
+ * node names, same namespace URI, same attributes, and recursively
+ * tests child nodes for same criteria.
+ *
+ * (code)
+ * t.xml_eq(got, expected, message);
+ * (end)
+ *
+ * Parameters:
+ * got - {DOMElement | String} A DOM node or XML string to test.
+ * expected - {DOMElement | String} The expected DOM node or XML string.
+ * msg - {String} A message to print with test output.
+ * options - {Object} Optional object for configuring test.
+ *
+ * Valid options:
+ * prefix - {Boolean} Compare element and attribute
+ * prefixes (namespace uri always tested). Default is false.
+ * includeWhiteSpace - {Boolean} Include whitespace only nodes when
+ * comparing child nodes. Default is false.
+ */
+ var proto = Test.AnotherWay._test_object_t.prototype;
+ proto.xml_eq = function(got, expected, msg, options) {
+ // convert arguments to nodes if string
+ if(typeof got == "string") {
+ try {
+ got = createNode(got);
+ } catch(err) {
+ this.fail(msg + ": got argument could not be converted to an XML node: " + err);
+ return;
+ }
+ }
+ if(typeof expected == "string") {
+ try {
+ expected = createNode(expected);
+ } catch(err) {
+ this.fail(msg + ": expected argument could not be converted to an XML node: " + err);
+ return;
+ }
+ }
+
+ // test nodes for equivalence
+ try {
+ assertElementNodesEqual(got, expected, options);
+ this.ok(true, msg);
+ } catch(err) {
+ this.fail(msg + ": " + err);
+ }
+ }
+
+})();
diff --git a/misc/openlayers/tests/Tile.html b/misc/openlayers/tests/Tile.html
new file mode 100644
index 0000000..087b320
--- /dev/null
+++ b/misc/openlayers/tests/Tile.html
@@ -0,0 +1,130 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+ var tile;
+
+ var map, layer;
+ function setUp() {
+ map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer(null, {
+ isBaseLayer: true
+ });
+ map.addLayer(layer)
+ map.setCenter(new OpenLayers.LonLat(0, 0));
+ }
+
+ function tearDown() {
+ map.destroy();
+ map = null;
+ layer = null;
+ }
+
+
+ function test_Tile_constructor (t) {
+ t.plan( 13 );
+
+ setUp();
+
+ var dummy = {};
+
+ var position = new OpenLayers.Pixel(10,20);
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var url = "bobob";
+ var size = new OpenLayers.Size(5,6);
+
+ tile = new OpenLayers.Tile(layer, position, bounds, url, size, {
+ eventListeners: {
+ loadstart: OpenLayers.Function.False
+ }
+ });
+
+ t.ok(tile instanceof OpenLayers.Tile, "new OpenLayers.Tile returns Tile object");
+ t.ok(tile.layer === layer, "tile.layer set correctly");
+ t.ok(tile.position.equals(position), "tile.position set correctly");
+ t.ok(tile.position != position, "tile.position set not by reference");
+ t.ok(tile.bounds.equals(bounds), "tile.bounds set correctly");
+ t.ok(tile.bounds != bounds, "tile.bounds set not by reference");
+ t.eq(tile.url, url, "tile.url set correctly");
+ t.ok(tile.size.equals(size), "tile.size is set correctly");
+ t.ok(tile.size != size, "tile.size set not by reference");
+
+ t.ok(tile.id != null, "tile is given an id");
+ t.ok(OpenLayers.String.startsWith(tile.id, "Tile_"),
+ "tile's id starts correctly");
+ t.ok(tile.events != null, "tile's events initialized");
+ t.ok(tile.events.listeners.loadstart.length == 1,
+ "tile's events initialized from eventListeners option");
+
+ tearDown();
+
+ }
+
+ function test_Tile_draw(t) {
+ t.plan(6);
+ setUp();
+
+ var position = new OpenLayers.Pixel(10,20);
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var url = "bobob";
+ var size = new OpenLayers.Size(5,6);
+
+ tile = new OpenLayers.Tile(layer, position, bounds, url, size);
+ var log = [];
+ tile.clear = function() {
+ log.push("clear");
+ }
+ tile.draw();
+ t.eq(log.length, 1, "Tile cleared before drawing");
+
+ log = [];
+ tile.events.register("beforedraw", this, function() {
+ log.push("beforedraw");
+ return false;
+ });
+ var drawn = tile.draw();
+ t.eq(log[0], "clear", "tile cleared");
+ t.eq(log[1], "beforedraw", "beforedraw event fired");
+ t.eq(drawn, null, "tile not drawn when beforedraw listener returns false");
+ drawn = tile.draw(true);
+ t.eq(log.length, 2, "no beforedraw event fired and tile not cleared when draw called with 'deferred' argument set to true");
+ t.eq(drawn, true, "tile drawn when draw called with 'deferred' argument set to true");
+
+ tearDown();
+ }
+
+ function test_Tile_destroy(t) {
+ t.plan( 6 );
+
+ setUp();
+
+ var position = new OpenLayers.Pixel(10,20);
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var url = "bobob";
+ var size = new OpenLayers.Size(5,6);
+
+ tile = new OpenLayers.Tile(layer, position, bounds, url, size);
+ tile.events.destroy = function() {
+ t.ok(true, "tile events destroy() called");
+ };
+
+
+ tile.destroy();
+
+ t.ok(tile.layer == null, "tile.layer set to null");
+ t.ok(tile.bounds == null, "tile.bounds set to null");
+ t.ok(tile.size == null, "tile.size set to null");
+ t.ok(tile.position == null, "tile.position set to null");
+
+ t.ok(tile.events == null, "tile.events set to null");
+
+ tearDown();
+
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
+
diff --git a/misc/openlayers/tests/Tile/Image.html b/misc/openlayers/tests/Tile/Image.html
new file mode 100644
index 0000000..825c187
--- /dev/null
+++ b/misc/openlayers/tests/Tile/Image.html
@@ -0,0 +1,490 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+ // turn off tile queue, so we can check img urls in tests
+ delete OpenLayers.Layer.Grid.prototype.queueTileDraw;
+
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var tile;
+
+ var map, layer;
+ function setUp() {
+ map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer(null, {
+ isBaseLayer: true
+ });
+ map.addLayer(layer)
+ map.setCenter(new OpenLayers.LonLat(0, 0));
+ }
+
+ function tearDown() {
+ map.destroy();
+ map = null;
+ layer = null;
+ }
+
+ function test_Tile_Image_constructor (t) {
+ t.plan( 6 );
+
+ setUp();
+
+ var position = new OpenLayers.Pixel(20,30);
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var url = "http://www.openlayers.org/dev/tests/tileimage";
+ var size = new OpenLayers.Size(5,6);
+ tile = new OpenLayers.Tile.Image(layer, position, bounds, url, size);
+
+ t.ok( tile instanceof OpenLayers.Tile.Image, "new OpenLayers.Tile returns Tile object" );
+ t.ok( tile.layer == layer, "tile.layer is set correctly");
+ t.ok( tile.position.equals(position), "tile.position is set correctly");
+ t.ok( tile.bounds.equals(bounds), "tile.bounds is set correctly");
+ t.eq( tile.url, url, "tile.url is set correctly");
+ t.ok( tile.size.equals(size), "tile.size is set correctly");
+
+ tearDown();
+ }
+
+ function test_destroy_observers(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "../../img/blank.gif", {layers: 'basic'});
+ map.addLayer(layer);
+
+ var position = new OpenLayers.Pixel(20,30);
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var size = new OpenLayers.Size(5,6);
+
+ // with alpha hack
+ var withAlpha = new OpenLayers.Tile.Image(layer, position, bounds, null, size);
+ withAlpha.layerAlphaHack = true;
+
+ withAlpha.draw();
+ var cacheID = withAlpha.imgDiv._eventCacheID;
+ withAlpha.destroy();
+
+ t.eq(OpenLayers.Event.observers[cacheID], undefined,
+ "With alpha hack: imgDiv observers are cleared in destroy");
+
+ // without alpha hack
+ var withoutAlpha = new OpenLayers.Tile.Image(layer, position, bounds, null, size);
+ withoutAlpha.layerAlphaHack = false;
+
+ withoutAlpha.draw();
+ var cacheID = withoutAlpha.imgDiv._eventCacheID;
+ withoutAlpha.destroy();
+
+ t.eq(OpenLayers.Event.observers[cacheID], undefined,
+ "Without alpha hack: imgDiv observers are cleared in destroy");
+
+ map.destroy();
+ }
+
+ function test_Tile_Image_async (t) {
+ t.plan( 3 );
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS(
+ "Name",
+ "../../img/blank.gif",
+ {layers: 'basic'}, {async: true, getURLasync: function(bounds, callback, scope) {
+ callback.call(scope, this.getURL(bounds));
+ }}
+ );
+ map.addLayer(layer);
+
+ var position = new OpenLayers.Pixel(20,30);
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ tile = layer.addTile(bounds, position);
+ tile.renderTile();
+ t.delay_call(0.1, function() {
+ var expected = new Image();
+ expected.src = layer.getURL(bounds);
+ t.eq(tile.imgDiv.src, expected.src, "image src correct for async request");
+ t.eq(tile.asyncRequestId, 1, "asyncRequestId correct after renderTile");
+ tile.renderTile();
+ });
+ t.delay_call(0.2, function() {
+ t.eq(tile.asyncRequestId, 2, "asyncRequestId correct after subsequent renderTile");
+ tile.destroy();
+ layer.destroy();
+ map.destroy();
+ });
+ }
+
+ function test_Tile_Image_draw (t) {
+ t.plan(7);
+
+ var map = new OpenLayers.Map('map');
+
+ var size = new OpenLayers.Size(5,6);
+ layer = new OpenLayers.Layer.WMS("Name",
+ "../../img/blank.gif",
+ null,
+ {tileSize: size});
+ map.addLayer(layer);
+ var position = new OpenLayers.Pixel(20,30);
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var url = "http://www.openlayers.org/dev/tests/tileimage";
+ tile = new OpenLayers.Tile.Image(layer, position, bounds, url, size);
+
+ tile.events.register("loadstart", this, function() {
+ t.ok(true, "loadstart triggered");
+ });
+ tile.events.register("reload", this, function() {
+ t.ok(true, "reload triggered");
+ });
+
+ //this should trigger a "loadstart" event
+ tile.draw();
+
+ if (!isMozilla)
+ t.ok( true, "skipping element test outside of Mozilla");
+ else
+ t.ok( tile.imgDiv instanceof HTMLElement, "tile.draw creates an image");
+ var tParams = {
+ SERVICE: "WMS", VERSION: "1.1.1",
+ REQUEST: "GetMap", STYLES: "",
+ FORMAT: "image/jpeg",
+ SRS: "EPSG:4326", BBOX: [1,2,3,4],
+ WIDTH: String(size.w), HEIGHT: String(size.h)
+ };
+ var expected = new Image();
+ expected.src = "../../img/blank.gif?" + OpenLayers.Util.getParameterString(tParams)
+ t.delay_call(0.1, function() {
+ t.eq( tile.imgDiv.src, expected.src, "tile.draw creates an image");
+ });
+ t.eq( tile.imgDiv.style.width, "5px", "Image width is correct" );
+ t.eq( tile.imgDiv.style.height, "6px", "Image height is correct" );
+ t.ok( tile.imgDiv.parentNode === layer.div, "Image is directly appended to the layer div" );
+
+ // this should trigger a "reload" event (since the image never actually
+ // loads in tests)
+ tile.draw();
+
+ }
+ function test_Tile_Image_OutsideMaxExtent(t) {
+ t.plan( 11 );
+ var position = new OpenLayers.Pixel(20,30);
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var url = "http://www.openlayers.org/dev/tests/tileimage";
+ var size = new OpenLayers.Size(5,6);
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "../../img/blank.gif", {layers: 'basic'}, {encodeBBOX: true});
+ map.addLayer(layer);
+ tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-185,-90,-180,90), url, size);
+ tile.draw()
+ t.eq(tile.imgDiv, null, "Images against side of maxextent don't load");
+ tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-181,-91,180,90), url, size);
+ tile.draw()
+ var tParams = {
+ LAYERS: "basic", SERVICE: "WMS", VERSION: "1.1.1",
+ REQUEST: "GetMap", STYLES: "",
+ FORMAT: "image/jpeg",
+ SRS: "EPSG:4326", BBOX: "-181,-91,180,90",
+ WIDTH: "256", HEIGHT: "256"
+ };
+ t.eq(tile.url,
+ "../../img/blank.gif?" + OpenLayers.Util.getParameterString(tParams),
+ "Images over edges of maxextent do load");
+ tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-181,-90,180,90), url, size);
+ tile.draw()
+ tParams = OpenLayers.Util.extend(tParams, {BBOX: "-181,-90,180,90"});
+ t.eq(tile.url,
+ "../../img/blank.gif?" + OpenLayers.Util.getParameterString(tParams),
+ "Images over edges of maxextent do load");
+ tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-180,-90,180,90), url, size);
+ tile.draw()
+ tParams = OpenLayers.Util.extend(tParams, {BBOX: "-180,-90,180,90"});
+ t.eq(tile.url,
+ "../../img/blank.gif?" + OpenLayers.Util.getParameterString(tParams),
+ "Image covering all of extent loads");
+ tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-80,-45,80,45), url, size);
+ tile.draw()
+ tParams = OpenLayers.Util.extend(tParams, {BBOX: "-80,-45,80,45"});
+ t.eq(tile.url,
+ "../../img/blank.gif?" + OpenLayers.Util.getParameterString(tParams),
+ "Image covering small part of extent loads");
+ tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-185,-95,185,95), url, size);
+ tile.draw()
+ tParams = OpenLayers.Util.extend(tParams, {BBOX: "-185,-95,185,95"});
+ t.eq(tile.url,
+ "../../img/blank.gif?" + OpenLayers.Util.getParameterString(tParams),
+ "Image covering more than all of extent loads");
+
+ layer.displayOutsideMaxExtent=1;
+ tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-185,-90,-180,90), url, size);
+ tile.draw()
+ tParams = OpenLayers.Util.extend(tParams, {BBOX: "-185,-90,-180,90"});
+ t.eq(tile.url,
+ "../../img/blank.gif?" + OpenLayers.Util.getParameterString(tParams),
+ "Images against side of maxextent do load with displayOutsideMaxExtent");
+ tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-181,-90,180,90), url, size);
+ tile.draw()
+ tParams = OpenLayers.Util.extend(tParams, {BBOX: "-181,-90,180,90"});
+ t.eq(tile.url,
+ "../../img/blank.gif?" + OpenLayers.Util.getParameterString(tParams),
+ "Images over edges of maxextent do load with displayOutsideMaxExtent set");
+ tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-180,-90,180,90), url, size);
+ tile.draw()
+ tParams = OpenLayers.Util.extend(tParams, {BBOX: "-180,-90,180,90"});
+ t.eq(tile.url,
+ "../../img/blank.gif?" + OpenLayers.Util.getParameterString(tParams),
+ "Image covering all of extent loads with display outside max extent");
+ tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-80,-45,80,45), url, size);
+ tile.draw()
+ tParams = OpenLayers.Util.extend(tParams, {BBOX: "-80,-45,80,45"});
+ t.eq(tile.url,
+ "../../img/blank.gif?" + OpenLayers.Util.getParameterString(tParams),
+ "Image covering small part of extent loads with display outside max extent");
+ tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-185,-95,185,95), url, size);
+ tile.draw()
+ tParams = OpenLayers.Util.extend(tParams, {BBOX: "-185,-95,185,95"});
+ t.eq(tile.url,
+ "../../img/blank.gif?" + OpenLayers.Util.getParameterString(tParams),
+ "Image covering more than all of extent loads");
+ }
+ function test_Tile_Image_Display_After_Move(t) {
+ t.plan(2);
+ var position = new OpenLayers.Pixel(20,30);
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var url = "http://www.openlayers.org/dev/tests/tileimage";
+ var size = new OpenLayers.Size(5,6);
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "../../img/blank.gif", {layers: 'basic'});
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-90,-85,-90,85), url, size);
+ tile.draw();
+ tile.moveTo(new OpenLayers.Bounds(-185,-90,-180,-80), new OpenLayers.Pixel(-180,-85), true);
+ t.delay_call( 1, function() { t.ok(!tile.imgDiv, "Reference to tile image removed.") } );
+ var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "../../img/blank.gif", {layers: 'basic'}, {'alpha':true});
+ map.addLayer(layer);
+ tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-90,-85,-90,85), url, size);
+ tile.draw();
+ tile.moveTo(new OpenLayers.Bounds(-185,-90,-180,-80), new OpenLayers.Pixel(-180,-85), true)
+ t.delay_call( 1, function() { t.ok(!tile.imgDiv, "Reference to alpha tile image removed.") } );
+
+ }
+
+ function test_Tile_Image_gutters(t) {
+ t.plan(5);
+
+ var gutter = 0;
+ var name = 'Test Layer';
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ var params = { map: '/mapdata/vmap_wms.map',
+ layers: 'basic',
+ format: 'image/png'};
+
+
+ var map = new OpenLayers.Map('map', {tileManager: null});
+ var layer = new OpenLayers.Layer.WMS(name, url, params, {gutter: gutter});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+
+ var tile = layer.grid[0][0];
+ t.ok(tile.layer.imageSize == null,
+ "zero size gutter doesn't set image size");
+
+ var zero_gutter_bounds = tile.bounds;
+
+ map.destroy();
+
+ var gutter = 15;
+ var map = new OpenLayers.Map('map', {tileManager: null});
+ var layer = new OpenLayers.Layer.WMS(name, url, params, {gutter: gutter});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+ var tile = layer.grid[0][0];
+ t.ok(tile.layer.imageSize.equals(new OpenLayers.Size(tile.size.w + (2 * gutter),
+ tile.size.h + (2 * gutter))),
+ "gutter properly changes image size");
+
+ var offsetLeft = -(gutter / layer.tileSize.w * 100) | 0;
+ var offsetTop = -(gutter / layer.tileSize.h * 100) | 0;
+ t.eq(parseInt(tile.imgDiv.style.left, 10), offsetLeft,
+ "gutter properly sets image left style");
+ t.eq(parseInt(tile.imgDiv.style.top, 10), offsetTop,
+ "gutter properly sets image top style");
+ t.ok(tile.bounds.equals(zero_gutter_bounds),
+ "gutter doesn't affect tile bounds");
+
+ map.destroy();
+ }
+
+ function test_createBackBuffer(t) {
+ t.plan(3);
+
+ var map = new OpenLayers.Map('map', {tileManager: null});
+ var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "../../img/blank.gif", {layers: 'basic'});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+ var tile = layer.grid[0][0];
+
+ // we're going to create a back buffer while the image
+ // is actually loading, so we call stopObservingElement
+ // to avoid any unexpected behavior
+ tile.isLoading = false;
+ OpenLayers.Event.stopObservingElement(tile.imgDiv);
+
+ var img = tile.imgDiv;
+ var left = img.style.left;
+ var bb = tile.createBackBuffer();
+ t.eq(bb.style.left, left, "backbuffer has same left style as frame");
+ t.ok(bb === img, "image appended to bb");
+ t.ok(tile.imgDiv == null, "image reference removed from tile");
+ map.destroy();
+ }
+
+ function test_onImageLoad(t) {
+ t.plan(3);
+
+ var map = new OpenLayers.Map('map', {tileManager: null});
+ var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "../../img/blank.gif", {layers: 'basic'}, {opacity: 0.5});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+
+ var tile = layer.grid[0][0];
+
+ var log;
+ tile.events.on({loadend: function() { log++; }});
+
+ log = 0;
+ tile.onImageLoad();
+ t.eq(tile.imgDiv.style.visibility, 'inherit',
+ 'onImageLoad makes the image visible');
+ t.eq(parseFloat(tile.imgDiv.style.opacity), 0.5,
+ 'onImageLoad sets the expected opacity for the image');
+ t.eq(log, 1,
+ 'onImageLoad does trigger loadend');
+
+ map.destroy();
+ }
+
+ function test_getCanvasContext(t) {
+ if (!OpenLayers.CANVAS_SUPPORTED) {
+ t.plan(0);
+ } else {
+ t.plan(1);
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS("blank",
+ "../../img/blank.gif", {layers: 'fake'});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 5);
+
+ t.delay_call(5, function() {
+ var tile = layer.grid[0][0];
+ tile.onImageLoad();
+ t.ok(tile.getCanvasContext() instanceof CanvasRenderingContext2D,
+ "getCanvasContext() returns CanvasRenderingContext2D instance");
+ map.destroy();
+ });
+ }
+ }
+
+ /*
+ * A series of tests to verify the dimensions and positions
+ * of the tile frame and img after draw.
+
+ * Written for https://github.com/openlayers/openlayers/issues/441
+ */
+ function test_draw_without_gutter_without_frame(t) {
+ t.plan(5);
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS('blank',
+ '../../img/blank.gif',
+ {layers: 'fake'},
+ {isBaseLayer: true});
+ map.addLayer(layer);
+ var tile = new OpenLayers.Tile.Image(
+ layer,
+ new OpenLayers.Pixel(6, 6),
+ new OpenLayers.Bounds(5, 45, 6, 46),
+ null,
+ new OpenLayers.Size(256, 256));
+
+ tile.draw();
+ t.eq(tile.frame, null, 'no frame');
+ t.eq(parseInt(tile.imgDiv.style.left, 10), 6, 'correct tile img left');
+ t.eq(parseInt(tile.imgDiv.style.top, 10), 6, 'correct tile img top');
+ t.eq(parseInt(tile.imgDiv.style.width, 10), 256, 'correct tile img width');
+ t.eq(parseInt(tile.imgDiv.style.height, 10), 256, 'correct tile img height');
+
+ map.destroy();
+ }
+ function test_draw_without_gutter_with_frame(t) {
+ t.plan(8);
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS('blank',
+ '../../img/blank.gif',
+ {layers: 'fake'},
+ {isBaseLayer: true});
+ map.addLayer(layer);
+ layer.gutter = 1; // this is just for a frame to be created for
+ // the tile
+ var tile = new OpenLayers.Tile.Image(
+ layer,
+ new OpenLayers.Pixel(6, 6),
+ new OpenLayers.Bounds(5, 45, 6, 46),
+ null,
+ new OpenLayers.Size(256, 256));
+ layer.gutter = null;
+
+ tile.draw();
+ t.eq(parseInt(tile.frame.style.left, 10), 6, 'correct tile frame left');
+ t.eq(parseInt(tile.frame.style.top, 10), 6, 'correct tile frame top');
+ t.eq(parseInt(tile.frame.style.width, 10), 256, 'correct tile frame width');
+ t.eq(parseInt(tile.frame.style.height, 10), 256, 'correct tile frame height');
+ t.eq(parseInt(tile.imgDiv.style.left, 10), 0, 'correct tile img left');
+ t.eq(parseInt(tile.imgDiv.style.top, 10), 0, 'correct tile img top');
+ t.eq(parseInt(tile.imgDiv.style.width, 10), 100, 'correct tile img width');
+ t.eq(parseInt(tile.imgDiv.style.height, 10), 100, 'correct tile img height');
+
+ map.destroy();
+ }
+ function test_draw_with_gutter(t) {
+ t.plan(8);
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS('blank',
+ '../../img/blank.gif',
+ {layers: 'fake'},
+ {isBaseLayer: true, gutter: 15});
+ map.addLayer(layer);
+ var tile = new OpenLayers.Tile.Image(
+ layer,
+ new OpenLayers.Pixel(6, 6),
+ new OpenLayers.Bounds(5, 45, 6, 46),
+ null,
+ new OpenLayers.Size(256, 256));
+
+ tile.draw();
+ t.eq(parseInt(tile.frame.style.left, 10), 6, 'correct tile frame left');
+ t.eq(parseInt(tile.frame.style.top, 10), 6, 'correct tile frame top');
+ t.eq(parseInt(tile.frame.style.width, 10), 256, 'correct tile frame width');
+ t.eq(parseInt(tile.frame.style.height, 10), 256, 'correct tile frame height');
+ t.eq(parseInt(tile.imgDiv.style.left, 10), -5, 'correct tile img left');
+ t.eq(parseInt(tile.imgDiv.style.top, 10), -5, 'correct tile img top');
+ t.eq(parseInt(tile.imgDiv.style.width, 10), 111, 'correct tile img width');
+ t.eq(parseInt(tile.imgDiv.style.height, 10), 111, 'correct tile img height');
+
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="height:550px;width:500px"></div>
+</body>
+</html>
+
diff --git a/misc/openlayers/tests/Tile/Image/IFrame.html b/misc/openlayers/tests/Tile/Image/IFrame.html
new file mode 100644
index 0000000..192c7c6
--- /dev/null
+++ b/misc/openlayers/tests/Tile/Image/IFrame.html
@@ -0,0 +1,183 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script type="text/javascript">
+ // turn off animation frame handling, so we can check img urls in tests
+ delete OpenLayers.Layer.Grid.prototype.queueTileDraw;
+
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var isOpera = (navigator.userAgent.indexOf("Opera") != -1);
+ var isIElt9 = (parseFloat(navigator.appVersion.split("MSIE")[1]) < 9);
+
+ var map, layer;
+ var position = new OpenLayers.Pixel(20,30);
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var url = "http://www.openlayers.org/dev/tests/tileimage";
+ var size = new OpenLayers.Size(5,6);
+ var name = "OpenaLayers WMS";
+ var wmsUrl = "http://labs.metacarta.com/wms/vmap0?";
+
+ function test_Tile_Image_IFrame_create (t) {
+ t.plan( 3 );
+ map = new OpenLayers.Map('map', {tileManager: null});
+ var bar = new Array(205).join("1234567890");
+ layer = new OpenLayers.Layer.WMS(name, wmsUrl,
+ {layers: 'basic', foo: bar},
+ {tileOptions: {maxGetUrlLength: 2048},
+ transitionEffect: 'resize'});
+ map.addLayer(layer);
+
+ var tile = layer.addTile(bounds, position);
+
+ tile.draw();
+ t.eq(tile.imgDiv.nodeName.toLowerCase(), "iframe", "IFrame used for long URL");
+
+ layer.mergeNewParams({foo: null});
+ tile.draw();
+ t.eq(tile.imgDiv.nodeName.toLowerCase(), "img", "IMG used for short URL");
+
+ tile.maxGetUrlLength = 0;
+ tile.draw();
+ t.eq(tile.imgDiv.nodeName.toLowerCase(), "iframe", "IFrame used when maxGetUrlLength is 0");
+
+ tile.destroy();
+ layer.destroy();
+ map.destroy();
+ }
+
+ function test_Tile_Image_IFrame_clear (t) {
+ t.plan( 1 );
+
+ map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS(name, wmsUrl, {layers: 'basic'}, {tileOptions: {maxGetUrlLength: 0}});
+ map.addLayer(layer);
+ tile = layer.addTile(bounds, position);
+ tile.draw();
+ tile.clear();
+
+ t.eq(
+ tile.frame.getElementsByTagName("iframe").length, 0,
+ "IFrame removed on clear()");
+ tile.destroy();
+ layer.destroy();
+ map.destroy();
+ }
+
+ function test_Tile_Image_IFrame_initImage (t) {
+ t.plan( 2 );
+
+ map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS(name, wmsUrl, {layers: 'basic'}, {tileOptions: {maxGetUrlLength: 0}});
+ map.addLayer(layer);
+ tile = layer.addTile(bounds, position);
+ tile.url = layer.getURL(bounds);
+ tile.initImage();
+
+ if(isMozilla) {
+ t.ok( tile.imgDiv instanceof HTMLElement, "tile.iFrame successfully created.");
+ }
+ else {
+ t.ok( tile.imgDiv != null, "tile.iFrame successfully created.");
+ }
+ t.eq( tile.imgDiv.className, "olTileImage", "iFrame's className correctly set.");
+
+ map.destroy();
+ }
+
+ function test_Tile_Image_IFrame_createImage (t) {
+ t.plan( 9 );
+
+ map = new OpenLayers.Map('map', {tileManager: null});
+ layer = new OpenLayers.Layer.WMS(name, wmsUrl, {layers: 'basic'}, {tileOptions: {maxGetUrlLength: 0}});
+ map.addLayer(layer);
+ var tile = layer.addTile(bounds, position);
+ tile.draw();
+ var iFrame = tile.imgDiv;
+ var eventPane = tile.frame.childNodes[0];
+
+ t.ok(OpenLayers.String.contains(eventPane.style.backgroundImage,
+ tile.blankImageUrl),
+ "backgroundImage of eventPane is set.");
+ t.eq(parseInt(eventPane.style.zIndex, 10), 1, "zIndex of eventPane is set.");
+ if(isIElt9) {
+ t.ok(iFrame != null, "IFrame successfully created.");
+ t.eq(iFrame.style.backgroundColor, '#ffffff', "backgroundColor correctly set.");
+ t.eq(iFrame.style.filter, 'chroma(color=#FFFFFF)', "filter correctly set.");
+ } else {
+ t.ok(iFrame instanceof HTMLElement, "IFrame successfully created.");
+ t.ok(true, 'Skip IFrame backgroundColor test outside IE < 9');
+ t.ok(true, 'Skip IFrame filter test outside IE < 9');
+ }
+ t.eq( iFrame.scrolling, 'no', "no scrolling");
+ t.eq( parseFloat(iFrame.marginWidth), 0, "no margin width");
+ t.eq( parseFloat(iFrame.marginHeight), 0, "no margin height");
+ t.eq( parseFloat(iFrame.frameBorder), 0, "no iframe border");
+
+ map.destroy();
+ }
+
+ function test_Tile_Image_IFrame_createRequestForm (t) {
+ t.plan( 6 );
+
+ var tParams = {
+ SERVICE: "WMS", VERSION: "1.1.1",
+ REQUEST: "GetMap", STYLES: "",
+ FORMAT: "image/jpeg",
+ SRS: "EPSG:4326", BBOX: [1,2,3,4],
+ WIDTH: String(size.w), HEIGHT: String(size.h)
+ };
+ var newLayer = new OpenLayers.Layer.WMS("Name",
+ "http://labs.metacarta.com/TESTURL",
+ tParams,
+ {tileSize: size, tileOptions: {maxGetUrlLength: 0}});
+ map = new OpenLayers.Map('map');
+ map.addLayer(newLayer);
+ tile = newLayer.addTile(bounds, position);
+ tile.url = newLayer.getURL(bounds);
+ tile.initImage();
+
+ tile.url = newLayer.getURL(bounds);
+ var form = tile.createRequestForm();
+ if(isMozilla) {
+ t.ok( form instanceof HTMLElement, "created html form successfully.");
+ }
+ else {
+ t.ok( form != null, "created html form successfully.");
+ }
+
+
+ var cacheId = newLayer.params["_OLSALT"];
+ cacheId = (cacheId ? cacheId + "_" : "") + tile.bounds.toBBOX();
+ var url = OpenLayers.Util.urlAppend(newLayer.url, cacheId);
+
+ t.eq( form.method.toLowerCase(), 'post', "form method correctly set.");
+ t.eq( form.target, tile.id+'_iFrame', "form target correctly set.");
+ t.eq( form.action, url, "form action correctly set.");
+
+ var formParams = {};
+ var children = form.childNodes;
+ for(var i=0; i<form.childNodes.length; i++) {
+ formParams[children[i].name] = children[i].value
+ }
+ newLayer.params.BBOX = newLayer.params.BBOX.join(",");
+ t.eq(newLayer.params, formParams, "html form elements equal layer's parameters.");
+
+ tile.draw();
+ tile.clear();
+ tile.initImage();
+ tile.createRequestForm();
+ t.ok(
+ tile.imgDiv.nodeName == "IFRAME",
+ "Iframe has been reinserted properly"
+ );
+
+ tile.destroy();
+ newLayer.destroy();
+ map.destroy();
+ }
+</script>
+</head>
+<body>
+<div id="map" style="height:550px;width:500px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Tile/UTFGrid.html b/misc/openlayers/tests/Tile/UTFGrid.html
new file mode 100644
index 0000000..4998adc
--- /dev/null
+++ b/misc/openlayers/tests/Tile/UTFGrid.html
@@ -0,0 +1,306 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <script>
+ /**
+ * Because browsers that implement requestAnimationFrame may not execute
+ * animation functions while a window is not displayed (e.g. in a hidden
+ * iframe as in these tests), we mask the native implementations here. The
+ * native requestAnimationFrame functionality is tested in Util.html and
+ * in PanZoom.html (where a popup is opened before panning). The panTo tests
+ * here will test the fallback setTimeout implementation for animation.
+ */
+ window.requestAnimationFrame =
+ window.webkitRequestAnimationFrame =
+ window.mozRequestAnimationFrame =
+ window.oRequestAnimationFrame =
+ window.msRequestAnimationFrame = null;
+ </script>
+ <script src="../OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var map, layer;
+ function setUp() {
+ layer = new OpenLayers.Layer.UTFGrid({
+ url: "../data/utfgrid/world_utfgrid/${z}/${x}/${y}.json",
+ isBaseLayer: true,
+ utfgridResolution: 4
+ });
+ map = new OpenLayers.Map({
+ div: "map",
+ projection: "EPSG:900913",
+ layers: [layer],
+ center: [0, 0],
+ zoom: 1,
+ tileManager: null
+ });
+ }
+
+ function tearDown() {
+ map.destroy();
+ map = null;
+ layer = null;
+ }
+
+ function test_constructor(t) {
+ t.plan(7);
+
+ setUp();
+
+ var position = new OpenLayers.Pixel(20, 30);
+ var bounds = new OpenLayers.Bounds(1, 2, 3, 4);
+ var url = "http://example.com/";
+ var size = new OpenLayers.Size(5, 6);
+ var tile = new OpenLayers.Tile.UTFGrid(layer, position, bounds, url, size);
+
+ t.ok(tile instanceof OpenLayers.Tile, "tile instance");
+ t.ok(tile instanceof OpenLayers.Tile.UTFGrid, "UTFGrid tile instance");
+ t.ok(tile.layer === layer, "layer set");
+ t.ok(tile.position.equals(position), "position set");
+ t.ok(tile.bounds.equals(bounds), "bounds set");
+ t.eq(tile.url, url, "url set");
+ t.ok(tile.size.equals(size), "size set");
+
+ tearDown();
+ }
+
+ function test_parseData(t) {
+ t.plan(2);
+ setUp();
+
+ var tile = layer.grid[0][0];
+
+ tile.parseData('{"foo": "bar"}');
+ t.eq(tile.json, {foo: "bar"}, "valid json parsed");
+
+ var err, obj;
+ try {
+ obj = tile.parseData('foo bar');
+ } catch (e) {
+ err = e;
+ }
+ // The JSON format doesn't actually throw on IE6, so we also check
+ // for undefined here.
+ t.ok(err instanceof Error || obj === undefined, "throws on invalid json");
+
+ tearDown();
+ }
+
+ function test_draw(t) {
+ t.plan(7);
+ setUp();
+
+ var position = new OpenLayers.Pixel(20, 30);
+ var bounds = new OpenLayers.Bounds(1, 2, 3, 4);
+ var url = "../data/utfgrid/world_utfgrid/${z}/${x}/${y}.json";
+ var size = new OpenLayers.Size(256, 256);
+ var tile = new OpenLayers.Tile.UTFGrid(layer, position, bounds, url, size);
+
+ var log = [];
+ function logger(event) {
+ log.push(event);
+ }
+ tile.events.on({
+ loadstart: logger,
+ reload: logger,
+ loadend: logger
+ });
+
+ t.eq(log.length, 0, "no events logged");
+
+ // start tile loading
+ tile.draw();
+ t.eq(log.length, 1, "[first draw] one event");
+ t.eq(log[0].type, "loadstart", "[first draw] loadstart");
+
+ // restart tile loading
+ log.length = 0;
+ tile.draw();
+ t.eq(log.length, 1, "[second draw] first event");
+ t.eq(log[0].type, "reload", "[second draw] reload");
+
+ // wait for tile loading to finish
+ t.delay_call(1, function() {
+ t.eq(log.length, 2, "[second draw] second event");
+ t.eq(log[1].type, "loadend", "[second draw] loadend");
+ tearDown();
+ });
+
+ }
+
+ function test_abortLoading(t) {
+ t.plan(7);
+ setUp();
+
+ var position = new OpenLayers.Pixel(20, 30);
+ var bounds = new OpenLayers.Bounds(1, 2, 3, 4);
+ var url = "../data/utfgrid/world_utfgrid/${z}/${x}/${y}.json";
+ var size = new OpenLayers.Size(256, 256);
+ var tile = new OpenLayers.Tile.UTFGrid(layer, position, bounds, url, size);
+
+ var log = [];
+ function logger(event) {
+ log.push(event);
+ }
+ tile.events.on({
+ loadstart: logger,
+ reload: logger,
+ loadend: logger
+ });
+
+ t.eq(log.length, 0, "no events logged");
+
+ // start tile loading
+ tile.draw();
+ t.eq(log.length, 1, "[first draw] one event");
+ t.eq(log[0].type, "loadstart", "[first draw] loadstart");
+
+ // abort tile loading
+ log.length = 0;
+ tile.abortLoading();
+ t.eq(log.length, 0, "[first abort] no events logged"); // TODO: does anybody need an abort event?
+
+ // abort again for the heck of it
+ var err;
+ try {
+ tile.abortLoading();
+ } catch (e) {
+ err = e;
+ }
+ t.ok(!err, "[second abort] no trouble");
+ t.eq(log.length, 0, "[second abort] no events");
+
+ // wait to confirm tile loading doesn't happen after abort
+ t.delay_call(1, function() {
+ t.eq(log.length, 0, "[wait] no events");
+ tearDown();
+ });
+
+ }
+
+ function test_getFeatureId(t) {
+ t.plan(3);
+ setUp();
+
+ var tile = layer.grid[1][1];
+ t.delay_call(0.5, function() {
+ var id = tile.getFeatureId(16, 60);
+ t.eq(id, "238", "feature 238 at 16, 60");
+ t.eq(tile.getFeatureId(18, 63), id, "same feature at 18, 63");
+
+ t.eq(tile.getFeatureId(300, 10), null, "null id outside tile");
+
+ tearDown();
+ });
+ }
+
+ function test_getFeatureInfo(t) {
+ t.plan(3);
+ setUp();
+
+ var tile = layer.grid[1][1];
+ t.delay_call(0.5, function() {
+ var info = tile.getFeatureInfo(16, 60);
+ var exp = {
+ id: "238",
+ data: {
+ NAME: "Svalbard",
+ POP2005: 0
+ }
+ };
+ t.eq(info, exp, "feature info at 16, 60");
+ t.eq(tile.getFeatureInfo(17, 62), exp, "same feature at 17, 62");
+
+ t.eq(tile.getFeatureInfo(300, 10), null, "undefined outside tile");
+
+ tearDown();
+ });
+ }
+
+ // While I dislike committing tests that aren't run, I'd like to make an
+ // exception here. This test (or something like it) should pass. When
+ // https://github.com/mapbox/utfgrid-spec/issues/1 is resolved, we should
+ // either modify this or update demo.json and enable the test.
+ function xtest_getFeatureId_demo(t) {
+ /**
+ * The UTFGrid 1.2 spec (https://github.com/mapbox/utfgrid-spec/blob/master/1.2/utfgrid.md)
+ * links to a demo.json to be used for testing implementations. This
+ * file is constructed with 256x256 data points. Each data point maps
+ * to a "feature id" using this heuristic:
+ *
+ * // x and y are pixel offsets from top left of 256x256 tile
+ * if (y < 255 || x < 222) {
+ * id = (y * 256) + x
+ * } else {
+ * id = 65501; // max number of ids that can be encoded
+ * }
+ */
+ t.plan(1);
+ setUp();
+
+ // look at this beauty of a constructor
+ var tile = new OpenLayers.Tile.UTFGrid(
+ layer, // layer
+ new OpenLayers.Pixel(0, 0), // position
+ new OpenLayers.Bounds(0, 0, 256, 256), // bounds
+ "../data/utfgrid/demo-1.1.json", // url
+ new OpenLayers.Size(256, 256), // size
+ {utfgridResolution: 1} // options
+ );
+
+ var err;
+ var request = new OpenLayers.Request.GET({
+ url: tile.url,
+ success: function(req) {
+ try {
+ tile.parseData(req.responseText);
+ } catch (e) {
+ err = e;
+ }
+ },
+ failure: function(req) {
+ err = new Error("Failed to fetch json. Status: " + req.status);
+ }
+ });
+
+ // wait for response and parsing, then make assertions
+ t.delay_call(1, function() {
+ if (err) {
+ t.fail(err);
+ } else {
+ var got, exp, failure;
+ outer: for (var y=0; y<256; ++y) {
+ for (var x=0; x<256; ++x) {
+ if (y<255 || x<222) {
+ exp = String((y * 256) + x);
+ } else {
+ exp = "65501";
+ }
+ got = tile.getFeatureId(x, y);
+ if (got !== exp) {
+ failure = "Failed to get id for (" + x + ", " + y + "): " +
+ "got " + got + " but expected " + exp;
+
+ break outer;
+ }
+ }
+ }
+ if (!failure) {
+ t.ok(true, "resolved feature ids for all data points");
+ } else {
+ t.fail(failure);
+ }
+ }
+ tearDown();
+ });
+
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="height:550px;width:500px"></div>
+</body>
+</html>
+
diff --git a/misc/openlayers/tests/TileManager.html b/misc/openlayers/tests/TileManager.html
new file mode 100644
index 0000000..23398be
--- /dev/null
+++ b/misc/openlayers/tests/TileManager.html
@@ -0,0 +1,137 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(4);
+
+ var tileManager = new OpenLayers.TileManager();
+ var map = new OpenLayers.Map('map', {
+ zoomMethod: null,
+ tileManager: tileManager
+ });
+ var layer = new OpenLayers.Layer.WMS('WMS1', '../img/blank.gif');
+ map.addLayer(layer);
+ map.setCenter([16, 48], 9);
+ t.ok(tileManager.tileQueue[map.id].length, "Tiles queued from layer");
+ map.removeLayer(layer);
+ t.eq(tileManager.tileQueue[map.id].length, 0, "Tiles unqueued when layer is removed");
+ map.addLayer(new OpenLayers.Layer.WMS('WMS2', '../img/blank.gif'));
+ map.zoomIn();
+ t.ok(tileManager.tileQueue[map.id].length, "Tiles queued from added layer");
+ map.destroy();
+ t.eq(tileManager.tileQueue[map.id], undefined, "Tile queue removed when map was destroyed");
+ }
+
+ function test_destroy(t) {
+ t.plan(3);
+
+ var tileManager = new OpenLayers.TileManager();
+ var map = new OpenLayers.Map('map', {tileManager: tileManager});
+ var layer = new OpenLayers.Layer.WMS('WMS', '../img/blank.gif');
+ map.addLayer(layer);
+ map.setCenter([16, 48], 9);
+ var numTileListeners = layer.grid[0][0].events.listeners.beforeload.length;
+ var numLayerListeners = layer.events.listeners.retile.length;
+ var numMapListeners = map.events.listeners.preremovelayer.length;
+ tileManager.destroy();
+ t.eq(layer.grid[0][0].events.listeners.beforeload.length, numTileListeners - 1, "no listener on tile after destroy");
+ t.eq(layer.events.listeners.retile.length, numLayerListeners - 1, "no listeners on layer after destroy");
+ t.eq(map.events.listeners.preremovelayer.length, numMapListeners - 1, "no listeners on map after destroy");
+ map.destroy();
+ }
+
+ function test_manageTileCache(t) {
+ t.plan(10);
+
+ var tileManager = new OpenLayers.TileManager({
+ cacheSize: 12
+ });
+ var map = new OpenLayers.Map('map', {tileManager: tileManager});
+ layer = new OpenLayers.Layer.WMS('WMS', '../img/blank.gif');
+ map.addLayer(layer);
+ map.setCenter([16, 48], 9);
+ var gridSize;
+
+ var firstInCache, sharedTile;
+ t.delay_call(2, function() {
+ t.eq(tileManager.tileCacheIndex.length, 12, "tiles cached");
+ t.ok(~OpenLayers.Util.indexOf(tileManager.tileCacheIndex, layer.grid[1][2].url), "tile found in cache");
+ t.ok(tileManager.tileCache[layer.grid[1][2].url] === layer.grid[1][2].imgDiv, "correct object cached");
+ firstInCache = tileManager.tileCache[tileManager.tileCacheIndex[0]];
+ sharedTile = tileManager.tileCache[tileManager.tileCacheIndex[11]];
+ gridSize = layer.div.childNodes.length;
+ map.setCenter([17, 47]);
+ });
+
+ function inCache(img) {
+ var search = img.src.split('?')[1];
+ for (var s in tileManager.tileCache) {
+ if (s.split('?')[1] == search) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ t.delay_call(4, function() {
+ t.eq(tileManager.tileCacheIndex.length, 12, "tiles cached");
+ t.ok(tileManager.tileCache[layer.grid[1][2].url] === layer.grid[1][2].imgDiv, "correct object cached");
+ t.ok(!inCache(firstInCache), "old tile discarded");
+ t.ok(inCache(sharedTile), "shared tile still in cache");
+ firstInCache = tileManager.tileCache[tileManager.tileCacheIndex[0]];
+ map.setCenter([16, 48]);
+ });
+ t.delay_call(6, function() {
+ t.ok(!inCache(firstInCache), "old tile discarded");
+ t.ok(inCache(sharedTile), "shared tile still in cache");
+ t.eq(layer.div.childNodes.length, gridSize, 'no unused images left in dom');
+ map.destroy();
+ });
+ }
+
+ function test_queueTileDraw(t) {
+ t.plan(3);
+
+ var tileManager = new OpenLayers.TileManager();
+ var map = new OpenLayers.Map('map', {tileManager: tileManager});
+ layer = new OpenLayers.Layer.WMS('WMS', '../img/blank.gif');
+ map.addLayer(layer);
+ map.setCenter([0, 0], 3);
+ var queued = tileManager.tileQueue[map.id].length;
+ t.ok(tileManager.tileQueue[map.id].length, "Tiles queued for drawing");
+ map.zoomIn();
+ t.eq(tileManager.tileQueue[map.id].length, queued, "Tile queue has same length after immediate zoom change");
+ t.delay_call(1, function() {
+ t.eq(tileManager.tileQueue[map.id].length, 0, "Tiles from queue processed");
+ map.destroy();
+ });
+ }
+
+ function test_deferTileDraw(t) {
+
+ t.plan(3);
+
+ var tileManager = new OpenLayers.TileManager();
+ var map = new OpenLayers.Map('map', {tileManager: tileManager});
+ layer = new OpenLayers.Layer.WMS('WMS', '../img/blank.gif');
+ layer.destroy = function() {}; //we're going to do funky things with the grid
+ layer.applyBackBuffer = function() {}; // backbuffering isn't under test here
+ map.addLayer(layer);
+ map.setCenter([-10, 0], 5);
+
+ map.moveTo([5, 0]);
+ t.ok(tileManager.tileQueue[map.id].length, "tile loading deferred after moveTo");
+ map.moveTo([0, 0]);
+ t.ok(tileManager.tileQueue[map.id].length, "deferred again after another moveTo");
+ t.delay_call(1, function() {
+ t.eq(tileManager.tileQueue[map.id].length, 0, "tiles loaded after moveDelay");
+ });
+ }
+ </script>
+</head>
+<body>
+<div id="map" style="width:499px;height:549px;display:none"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Tween.html b/misc/openlayers/tests/Tween.html
new file mode 100644
index 0000000..1fbfa3c
--- /dev/null
+++ b/misc/openlayers/tests/Tween.html
@@ -0,0 +1,116 @@
+<html>
+<head>
+ <script>
+ /**
+ * Because browsers that implement requestAnimationFrame may not execute
+ * animation functions while a window is not displayed (e.g. in a hidden
+ * iframe as in these tests), we mask the native implementations here. The
+ * native requestAnimationFrame functionality is tested in Util.html and
+ * in PanZoom.html (where a popup is opened before panning). The panTo tests
+ * here will test the fallback setTimeout implementation for animation.
+ */
+ window.requestAnimationFrame =
+ window.webkitRequestAnimationFrame =
+ window.mozRequestAnimationFrame =
+ window.oRequestAnimationFrame =
+ window.msRequestAnimationFrame = null;
+ </script>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_Tween_constructor(t) {
+ t.plan(3);
+
+ var tween = new OpenLayers.Tween();
+ t.ok(tween instanceof OpenLayers.Tween,
+ "new OpenLayers.Tween returns object" );
+ t.eq(typeof tween.easing, "function",
+ "constructor sets easing correctly");
+ t.eq(typeof tween.start, "function", "tween has a start function");
+ }
+
+ function test_Tween_start(t) {
+ t.plan(5);
+
+ var tween = new OpenLayers.Tween();
+
+ var start = {foo: 0, bar: 10};
+ var finish = {foo: 10, bar: 0};
+ var _start = false;
+ var _done = false;
+ var _eachStep = false;
+ var callbacks = {
+ start: function() {
+ _start = true;
+ },
+ done: function() {
+ _done = true;
+ },
+ eachStep: function() {
+ _eachStep = true;
+ }
+ }
+ tween.start(start, finish, 10, {callbacks: callbacks});
+ t.ok(tween.animationId != null, "animationId correctly set");
+ t.delay_call(0.8, function() {
+ t.eq(_start, true, "start callback called");
+ t.eq(_done, true, "finish callback called");
+ t.eq(_eachStep, true, "eachStep callback called");
+ t.eq(tween.time, 11, "Number of steps reached is correct");
+ });
+ }
+
+ function test_Tween_stop(t) {
+ t.plan(2);
+
+ var tween = new OpenLayers.Tween();
+ tween.animationId = OpenLayers.Animation.start(function() {});
+ tween.playing = true;
+ tween.stop();
+ t.eq(tween.animationId, null, "tween correctly stopped");
+
+ tween.animationId = OpenLayers.Animation.start(function() {});
+ tween.playing = false;
+ tween.stop();
+ t.ok(tween.animationId != null, "stop method doesn't do anything if tween isn't running");
+ }
+
+ function test_Tween_skip(t) {
+ t.plan(2);
+
+ var tween = new OpenLayers.Tween();
+ var log = 0;
+ tween.start({count: 0}, {count: 10}, 10, {
+ callbacks: {
+ eachStep: function() {
+ log++;
+ }
+ },
+ minFrameRate: 10000
+ });
+
+ t.delay_call(0.8, function() {
+ t.eq(log, 0, 'all frames skipped at a frame rate of 10000');
+
+ log = 0;
+ tween.start({count: 0}, {count: 10}, 10, {
+ callbacks: {
+ eachStep: function() {
+ log++;
+ }
+ },
+ minFrameRate: 1
+ });
+ });
+
+ t.delay_call(1.6, function() {
+ t.eq(log, 11, 'no frames skipped at a frame rate of 1');
+ });
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width:500px;height:500px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Util.html b/misc/openlayers/tests/Util.html
new file mode 100644
index 0000000..07fa5ed
--- /dev/null
+++ b/misc/openlayers/tests/Util.html
@@ -0,0 +1,1180 @@
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+ <style>
+ body {
+ margin: 0;
+ padding: 0;
+ }
+ #map {
+ position: absolute;
+ top: 1234px;
+ left: 123px;
+ }
+ .test_getRenderedDimensions p{
+ padding: 20px;
+ }
+ </style>
+ <script>
+ var OpenLayers = [
+ "OpenLayers/BaseTypes/Class.js",
+ "OpenLayers/Util.js",
+ "OpenLayers/BaseTypes.js",
+ "OpenLayers/BaseTypes/Element.js",
+ "OpenLayers/BaseTypes/LonLat.js",
+ "OpenLayers/BaseTypes/Pixel.js",
+ "OpenLayers/BaseTypes/Size.js",
+ "OpenLayers/Lang.js",
+ "OpenLayers/Console.js"
+ ];
+ </script>
+ <script src="OLLoader.js"></script>
+ <script src="Util_common.js"></script>
+ <script type="text/javascript">
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var map;
+
+ function test_isElement(t) {
+ t.plan(3);
+
+ // set up
+ var o;
+
+ // tests
+ o = {};
+ t.eq(OpenLayers.Util.isElement(o), false,
+ "isElement reports that {} isn't an Element");
+ o = document.createElement("div");
+ t.eq(OpenLayers.Util.isElement(o), true,
+ "isElement reports that object returned by createElement is an Element");
+ o = OpenLayers.Util.getElement("map");
+ t.eq(OpenLayers.Util.isElement(o), true,
+ "isElement reports that object returned by getElement is an Element");
+ }
+
+ function test_isArray(t) {
+ t.plan(5);
+
+ var a;
+
+ a = null;
+ t.eq(OpenLayers.Util.isArray(a), false,
+ "isArray reports 'null' isn't an Array");
+ a = "Array";
+ t.eq(OpenLayers.Util.isArray(a), false,
+ "isArray reports \"Array\" isn't an Array");
+ a = {};
+ t.eq(OpenLayers.Util.isArray(a), false,
+ "isArray reports {} isn't an Array");
+ a = [];
+ t.eq(OpenLayers.Util.isArray(a), true,
+ "isArray reports [] is an Array");
+ a = new Array();
+ t.eq(OpenLayers.Util.isArray(a), true,
+ "isArray reports new Array() is an Array");
+ }
+
+ function test_iframe_isArray(t) {
+ t.plan(3);
+ // create an array in an iframe
+ var iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ frames[frames.length-1].document.write(
+ "<script>parent.testArray = [];<\/script>"
+ );
+
+ t.ok(!!testArray, "testArray created");
+ t.ok(!(testArray instanceof Array), "instanceof check doesn't work");
+ t.eq(OpenLayers.Util.isArray(testArray), true, "isArray works");
+ }
+
+ function test_Util_getImagesLocation (t) {
+ t.plan( 1 );
+ t.ok( OpenLayers.Util.getImagesLocation(), "../img/",
+ "getImagesLocation()" );
+ }
+
+ function test_Util_IndexOf(t) {
+ t.plan( 3 );
+ var array = new Array(1, "bar");
+ t.eq(OpenLayers.Util.indexOf(array, 1), 0);
+ t.eq(OpenLayers.Util.indexOf(array, "bar"), 1);
+ t.eq(OpenLayers.Util.indexOf(array, "foo"), -1);
+ }
+
+ function test_Util_Array(t) {
+ t.plan( 2 );
+
+ var array = new Array(1,2,3,4,4,5);
+
+ OpenLayers.Util.removeItem(array, 3);
+ t.eq( array.toString(), "1,2,4,4,5", "Util.removeItem works on one element");
+ OpenLayers.Util.removeItem(array, 4);
+ t.eq( array.toString(), "1,2,5", "Util.removeItem works on more than one element ");
+ }
+
+ function test_Util_pagePosition(t) {
+ t.plan( 2 );
+
+ // making sure that the test iframe is visible
+ var origDisplay;
+ var parents = window.parent.document.getElementsByTagName('iframe');
+ if (parents.length) {
+ origDisplay = parents[1].parentNode.style.display;
+ // span containing the test iframe is the invisible element
+ parents[1].parentNode.style.display = "";
+ }
+
+ var pp = OpenLayers.Util.pagePosition(window);
+ t.eq( pp.toString(), "0,0", "Page position doesn't bail if passed 'window'");
+
+ var mapDiv = document.getElementById("map");
+ var beforeScrollPp = OpenLayers.Util.pagePosition(mapDiv);
+ window.scrollTo(100, 1200);
+ pp = OpenLayers.Util.pagePosition(mapDiv);
+ t.eq(pp, beforeScrollPp, "Page position should work after page has been scrolled");
+
+ // reset test iframe visibility
+ if (parents.length) {
+ parents[1].parentNode.style.display = origDisplay;
+ }
+ }
+
+ function test_Util_createDiv(t) {
+ t.plan( 24 );
+
+ var id = "boo";
+ var px = new OpenLayers.Pixel(5,5);
+ var sz = new OpenLayers.Size(10,10);
+ var img = "http://www.openlayers.org/images/OpenLayers.trac.png";
+ var position = "absolute";
+ var border = "13px solid";
+ var overflow = "hidden";
+ var opacity = 0.5;
+
+ var div = OpenLayers.Util.createDiv(id, px, sz, img, position, border, overflow, opacity);
+
+ if (!isMozilla)
+ t.ok( true, "skipping element test outside of Mozilla");
+ else
+ t.ok( div instanceof HTMLDivElement, "createDiv creates a valid HTMLDivElement" );
+ t.eq( div.id, id, "div.id set correctly");
+ t.eq( div.style.left, px.x + "px", "div.style.left set correctly");
+ t.eq( div.style.top, px.y + "px", "div.style.top set correctly");
+
+ t.eq( div.style.width, sz.w + "px", "div.style.width set correctly");
+ t.eq( div.style.height, sz.h + "px", "div.style.height set correctly");
+
+ bImg = div.style.backgroundImage;
+ imgCorrect = ( (bImg == "url(" + img + ")") ||
+ (bImg == "url(\"" + img + "\")") );
+ t.ok(imgCorrect, "div.style.backgroundImage correctly");
+
+ t.eq( div.style.position, position, "div.style.positionset correctly");
+ //Safari 3 separates the border style into separate entities when reading it
+ if (OpenLayers.BROWSER_NAME == 'safari') {
+ var s = border.split(' ');
+ t.ok(div.style.borderTopWidth == s[0] && div.style.borderTopStyle == s[1], "good default popup.border")
+ } else {
+ t.ok( (div.style.border.indexOf(border) != -1), "div.style.border set correctly");
+ }
+
+ //Safari 3 separates style overflow into overflow-x and overflow-y
+ var prop = (OpenLayers.BROWSER_NAME == 'safari') ? 'overflowX' : 'overflow';
+ t.eq( div.style[prop], overflow, "div.style.overflow set correctly");
+ t.eq( parseFloat(div.style.opacity), opacity, "element.style.opacity set correctly");
+ //Some non-IE browsers don't return the alpha string for this value, which is okay
+ var filterString = div.style.filter.match(/^alpha/) != null ?
+ 'alpha(opacity=' + (opacity * 100) + ')' : div.style.filter;
+ t.eq( div.style.filter, filterString, "element.style.filter set correctly");
+
+ //test defaults
+ var div = OpenLayers.Util.createDiv();
+
+ if (!isMozilla)
+ t.ok( true, "skipping element test outside of Mozilla");
+ else
+ t.ok( div instanceof HTMLDivElement, "createDiv creates a valid HTMLDivElement" );
+ t.ok( (div.id != ""), "div.id set correctly");
+ t.eq(div.style.left, "", "div.style.left set correctly");
+ t.eq(div.style.top, "", "div.style.top set correctly");
+
+ t.eq( div.style.width, "", "div.style.width set correctly");
+ t.eq( div.style.height, "", "div.style.height set correctly");
+
+ t.eq(div.style.backgroundImage, "", "div.style.backgroundImage correctly");
+
+ t.eq( div.style.position, "absolute", "div.style.positionset correctly");
+ //Safari 3 separates the border style into separate entities when reading it
+ if (OpenLayers.BROWSER_NAME == 'safari') {
+ t.ok(div.style.borderTopWidth == '' && div.style.borderTopStyle == '', "good default popup.border")
+ } else {
+ t.eq( div.style.border, "", "div.style.border set correctly");
+ }
+ //Safari 3 separates style overflow into overflow-x and overflow-y
+ var prop = (OpenLayers.BROWSER_NAME == 'safari') ? 'overflowX' : 'overflow';
+ t.eq(div.style[prop], "", "div.style.overflow set correctly");
+ t.ok( !div.style.opacity, "element.style.opacity set correctly");
+ t.ok( !div.style.filter, "element.style.filter set correctly");
+
+ }
+
+ function test_Util_createImage(t) {
+ t.plan( 22 );
+
+ var img = "http://www.openlayers.org/images/OpenLayers.trac.png";
+ var sz = new OpenLayers.Size(10,10);
+ var xy = new OpenLayers.Pixel(5,5);
+ var position = "absolute";
+ var id = "boo";
+ var border = "1px solid";
+ var opacity = 0.5;
+
+ var image = OpenLayers.Util.createImage(id, xy, sz, img, position, border, opacity);
+
+ if (!isMozilla)
+ t.ok( true, "skipping element test outside of Mozilla");
+ else
+ t.ok( image.nodeName == "IMG", "createImage creates a valid HTMLImageElement" );
+ t.eq( image.id, id, "image.id set correctly");
+ t.eq( image.style.left, xy.x + "px", "image.style.left set correctly");
+ t.eq( image.style.top, xy.y + "px", "image.style.top set correctly");
+
+ t.eq( image.style.width, sz.w + "px", "image.style.width set correctly");
+ t.eq( image.style.height, sz.h + "px", "image.style.height set correctly");
+
+ //Safari 3 separates the border style into separate entities when reading it
+ if (OpenLayers.BROWSER_NAME == 'safari') {
+ var s = border.split(' ');
+ t.ok(image.style.borderTopWidth == s[0] && image.style.borderTopStyle == s[1], "good default popup.border")
+ } else {
+ t.ok( (image.style.border.indexOf(border) != -1), "image.style.border set correctly");
+ }
+ t.eq( image.src, img, "image.style.backgroundImage correctly");
+ t.eq( image.style.position, position, "image.style.position set correctly");
+ t.eq( parseFloat(image.style.opacity), opacity, "image.style.opacity set correctly");
+ //Some non-IE browsers don't return the alpha string for this value, which is okay
+ var filterString = image.style.filter.match(/^alpha/) != null ?
+ 'alpha(opacity=' + (opacity * 100) + ')' : image.style.filter;
+ t.eq( image.style.filter, filterString, "element.style.filter set correctly");
+
+ //test defaults
+ var image = OpenLayers.Util.createImage();
+
+ if (!isMozilla)
+ t.ok( true, "skipping element test outside of Mozilla");
+ else
+ t.ok( image.nodeName == "IMG", "createDiv creates a valid HTMLDivElement" );
+ t.ok( (image.id != ""), "image.id set to something");
+ t.eq( image.style.left, "", "image.style.left set correctly");
+ t.eq( image.style.top, "", "image.style.top set correctly");
+
+ t.eq( image.style.width, "", "image.style.width set correctly");
+ t.eq( image.style.height, "", "image.style.height set correctly");
+
+ t.ok((image.style.border == ""), "image.style.border set correctly");
+ t.eq(image.src, "", "image.style.backgroundImage correctly");
+ t.eq( image.style.position, "relative", "image.style.positionset correctly");
+ t.ok( !image.style.opacity, "element.style.opacity default unset");
+ t.ok( !image.style.filter, "element.style.filter default unset");
+
+ }
+
+ function test_Util_applyDefaults(t) {
+
+ t.plan(12);
+
+ var to = {
+ 'a': "abra",
+ 'b': "blorg",
+ 'n': null
+ };
+
+ var from = {
+ 'b': "zoink",
+ 'c': "press",
+ 'toString': function() {return 'works'},
+ 'n': "broken"
+ };
+
+ OpenLayers.Util.applyDefaults(to, from);
+
+ t.ok( to instanceof Object, " applyDefaults returns an object");
+ t.eq( to["a"], "abra", "key present in to but not from maintained");
+ t.eq( to["b"], "blorg", "key present in to and from, maintained in to");
+ t.eq( to["c"], "press", "key present in from and not to successfully copied to to");
+
+ var ret = OpenLayers.Util.applyDefaults({'a': "abra",'b': "blorg"}, from);
+ t.ok( ret instanceof Object, " applyDefaults returns an object");
+ t.eq( ret["a"], "abra", "key present in ret but not from maintained");
+ t.eq( ret["b"], "blorg", "key present in ret and from, maintained in ret");
+ t.eq( ret["c"], "press", "key present in from and not ret successfully copied to ret");
+ t.eq(to.toString(), "works", "correctly applies custom toString");
+ t.eq(to.n, null, "correctly preserves null");
+
+ var to;
+ var from = {rand: Math.random()};
+
+ var ret = OpenLayers.Util.applyDefaults(to, from);
+ t.eq(ret.rand, from.rand, "works with undefined to");
+
+ //regression test for #1716 -- allow undefined from
+ try {
+ OpenLayers.Util.applyDefaults({}, undefined);
+ t.ok(true, "no exception thrown when from is undefined");
+ } catch(err) {
+ t.fail("exception thrown when from is undefined:" + err);
+ }
+
+ }
+
+ function test_Util_getParameterString(t) {
+ t.plan(6);
+
+ var params = {
+ 'foo': "bar",
+ 'chicken': 1.5
+ };
+
+ t.eq( OpenLayers.Util.getParameterString(params), "foo=bar&chicken=1.5", "getParameterString returns correctly");
+ t.eq( OpenLayers.Util.getParameterString({'a:':'b='}), "a%3A=b%3D", "getParameterString returns correctly with non-ascii keys/values");
+
+ t.eq(OpenLayers.Util.getParameterString({chars: "~!*()'"}), "chars=~!*()'", "~!*()' are unreserved or have no reserved purpose in a URI component");
+
+
+ // Parameters which are a list should end up being a comma-seperated
+ // list of the URL encoded strings
+ var params = { foo: ["bar,baz"] };
+ t.eq( OpenLayers.Util.getParameterString(params), "foo=bar%2Cbaz", "getParameterString encodes , correctly in arrays");
+
+ var params = { foo: ["bar","baz,"] };
+ t.eq( OpenLayers.Util.getParameterString(params), "foo=bar,baz%2C", "getParameterString returns with list of CSVs when given a list. ");
+
+ var params = { foo: [null, undefined, 0, "", "bar"] }
+ t.eq( OpenLayers.Util.getParameterString(params), "foo=,,0,,bar", "getParameterString works fine with null values in array.");
+ }
+
+ function test_Util_urlAppend(t) {
+
+ var params = "foo=bar";
+
+ t.plan( 7 );
+
+ // without ?
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ var str = OpenLayers.Util.urlAppend(url, params);
+ t.eq(str, url + '?' + params, "urlAppend() works for url sans ?");
+
+
+ // with ?
+ url = "http://octo.metacarta.com/cgi-bin/mapserv?";
+ str = OpenLayers.Util.urlAppend(url, params);
+ t.eq(str, url + params, "urlAppend() works for url with ?");
+
+ // with ?param1=5
+ url = "http://octo.metacarta.com/cgi-bin/mapserv?param1=5";
+ str = OpenLayers.Util.urlAppend(url, params);
+ t.eq(str, url + '&' + params, "urlAppend() works for url with ?param1=5");
+
+ // with ?param1=5&
+ url = "http://octo.metacarta.com/cgi-bin/mapserv?param1=5&";
+ str = OpenLayers.Util.urlAppend(url, params);
+ t.eq(str, url + params, "urlAppend() works for url with ?param1=5&");
+
+ // with ?param1=5&param2=6
+ url = "http://octo.metacarta.com/cgi-bin/mapserv?param1=5&param2=6";
+ str = OpenLayers.Util.urlAppend(url, params);
+ t.eq(str, url + "&" + params, "urlAppend() works for url with ?param1=5&param2=6");
+
+ // with empty paramStr
+ url = "http://octo.metacarta.com/cgi-bin/mapserv?param1=5"
+ str = OpenLayers.Util.urlAppend(url, "");
+ t.eq(str, url, "urlAppend() works with empty paramStr")
+
+ // with null paramStr
+ url = "http://octo.metacarta.com/cgi-bin/mapserv?param1=5"
+ str = OpenLayers.Util.urlAppend(url, null);
+ t.eq(str, url, "urlAppend() works with null paramStr")
+ }
+
+ function test_Util_createAlphaImageDiv(t) {
+ t.plan( 19 );
+
+ var img = "http://www.openlayers.org/images/OpenLayers.trac.png";
+ var sz = new OpenLayers.Size(10,10);
+ var xy = new OpenLayers.Pixel(5,5);
+ var position = "absolute";
+ var id = "boo";
+ var border = "1px solid";
+ var sizing = "crop";
+ var opacity = 0.5;
+
+ var imageDiv = OpenLayers.Util.createAlphaImageDiv(id, xy, sz, img, position, border, sizing, opacity);
+
+ if (!isMozilla)
+ t.ok( true, "skipping element test outside of Mozilla");
+ else
+ t.ok( imageDiv instanceof HTMLDivElement, "createDiv creates a valid HTMLDivElement" );
+
+ t.eq( imageDiv.id, id, "image.id set correctly");
+ t.eq( imageDiv.style.left, xy.x + "px", "image.style.left set correctly");
+ t.eq( imageDiv.style.top, xy.y + "px", "image.style.top set correctly");
+
+ t.eq( imageDiv.style.width, sz.w + "px", "image.style.width set correctly");
+ t.eq( imageDiv.style.height, sz.h + "px", "image.style.height set correctly");
+
+ t.eq( imageDiv.style.position, position, "image.style.positionset correctly");
+ t.eq( parseFloat(imageDiv.style.opacity), opacity, "element.style.opacity set correctly");
+
+ var filterString;
+ if (OpenLayers.Util.alphaHack()) {
+ filterString = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='http://www.openlayers.org/images/OpenLayers.trac.png', sizingMethod='crop') alpha(opacity=50)";
+ } else {
+ //Some non-IE browsers don't return the alpha string for this value, which is okay
+ var filterString = imageDiv.style.filter.match(/^alpha/) != null ?
+ 'alpha(opacity=' + (opacity * 100) + ')' : imageDiv.style.filter;
+ }
+ t.eq( imageDiv.style.filter, filterString, "element.style.filter set correctly");
+
+
+ image = imageDiv.firstChild;
+ if (!isMozilla)
+ t.ok( true, "skipping element test outside of Mozilla");
+ else
+ t.ok( image.nodeName == "IMG", "createImage creates a valid HTMLImageElement" );
+ t.eq( image.id, id + "_innerImage", "image.id set correctly");
+
+ t.eq( image.style.width, sz.w + "px", "image.style.width set correctly");
+ t.eq( image.style.height, sz.h + "px", "image.style.height set correctly");
+
+ //Safari 3 separates the border style into separate entities when reading it
+ if (OpenLayers.BROWSER_NAME == 'safari') {
+ var s = border.split(' ');
+ t.ok(image.style.borderTopWidth == s[0] && image.style.borderTopStyle == s[1], "good default popup.border")
+ } else {
+ t.ok( (image.style.border.indexOf(border) != -1), "image.style.border set correctly");
+ }
+
+ t.eq( image.style.position, "relative", "image.style.positionset correctly");
+
+ if (OpenLayers.Util.alphaHack()) {
+
+ t.eq(imageDiv.style.display, "inline-block", "imageDiv.style.display set correctly");
+
+ var filter = "progid:DXImageTransform.Microsoft" +
+ ".AlphaImageLoader(src='" + img + "', " +
+ "sizingMethod='" + sizing + "') alpha(opacity=50)";
+ t.eq(imageDiv.style.filter, filter, "div filter value correctly set");
+
+ filter = "alpha(opacity=0)";
+ t.eq(image.style.filter, filter, "image filter set correctly");
+
+ } else {
+ t.eq( image.src, img, "image.style.backgroundImage correctly");
+ t.ok(true, "div filter value not set (not in IE)");
+ t.ok(true, "image filter value not set (not in IE)");
+ }
+
+ var imageDiv = OpenLayers.Util.createAlphaImageDiv(id, xy, sz, img, position, border);
+ if (OpenLayers.Util.alphaHack()) {
+ var filter = "progid:DXImageTransform.Microsoft" +
+ ".AlphaImageLoader(src='" + img + "', " +
+ "sizingMethod='scale')";
+ t.eq(imageDiv.style.filter, filter, "sizingMethod default correctly set to scale");
+ } else {
+ t.ok(true);
+ }
+
+ }
+
+ function test_Util_modifyDOMElement_opacity(t) {
+ t.plan(8);
+
+ var opacity = 0.2;
+
+ var element = document.createElement("div");
+
+ OpenLayers.Util.modifyDOMElement(element, null, null, null, null,
+ null, null, opacity);
+
+ t.eq(parseFloat(element.style.opacity), opacity,
+ "element.style.opacity set correctly when opacity = " + opacity);
+ //Some non-IE browsers don't return the alpha string for this value, which is okay
+ var filterString = element.style.filter.match(/^alpha/) != null ?
+ 'alpha(opacity=' + (opacity * 100) + ')' : element.style.filter;
+ t.eq(element.style.filter, filterString,
+ "element.style.filter set correctly when opacity = " + opacity);
+
+ OpenLayers.Util.modifyDOMElement(element, null, null, null, null,
+ null, null, "5");
+
+ t.eq(parseFloat(element.style.opacity), opacity,
+ "element.style.opacity not changed if the value is incorrect");
+ //Some non-IE browsers don't return the alpha string for this value, which is okay
+ var filterString = element.style.filter.match(/^alpha/) != null ?
+ 'alpha(opacity=' + (opacity * 100) + ')' : element.style.filter;
+ t.eq(element.style.filter, filterString,
+ "element.style.filter not changed if the value is incorrect");
+
+ OpenLayers.Util.modifyDOMElement(element, null, null, null, null,
+ null, null, "hello");
+
+ t.eq(parseFloat(element.style.opacity), opacity,
+ "element.style.opacity not changed if the value is incorrect");
+ //Some non-IE browsers don't return the alpha string for this value, which is okay
+ var filterString = element.style.filter.match(/^alpha/) != null ?
+ 'alpha(opacity=' + (opacity * 100) + ')' : element.style.filter;
+ t.eq(element.style.filter, filterString,
+ "element.style.filter not changed if the value is incorrect");
+
+ opacity = 1.00;
+ OpenLayers.Util.modifyDOMElement(element, null, null, null, null,
+ null, null, opacity);
+
+ t.eq(element.style.opacity, '',
+ "element.style.opacity is removed when opacity = " + opacity);
+ // Some browser returns null instead of '', which is okay
+ t.ok(element.style.filter == '' || element.style.filter == null,
+ "element.style.filter is removed when opacity = " + opacity);
+ }
+
+ function test_Util_modifyDOMElement(t) {
+ t.plan( 10 );
+
+ var id = "boo";
+ var px = new OpenLayers.Pixel(5,5);
+ var sz = new OpenLayers.Size(10,10);
+ var position = "absolute";
+ var border = "1px solid";
+ var overflow = "hidden";
+ var opacity = 1/2;
+
+ var element = document.createElement("div");
+
+ OpenLayers.Util.modifyDOMElement(element, id, px, sz, position,
+ border, overflow, opacity);
+
+ t.eq( element.id, id, "element.id set correctly");
+ t.eq( element.style.left, px.x + "px", "element.style.left set correctly");
+ t.eq( element.style.top, px.y + "px", "element.style.top set correctly");
+
+ t.eq( element.style.width, sz.w + "px", "element.style.width set correctly");
+ t.eq( element.style.height, sz.h + "px", "element.style.height set correctly");
+
+ t.eq( element.style.position, position, "element.style.position set correctly");
+ //Safari 3 separates the border style into separate entities when reading it
+ if (OpenLayers.BROWSER_NAME == 'safari') {
+ var s = border.split(' ');
+ t.ok(element.style.borderTopWidth == s[0] && element.style.borderTopStyle == s[1], "good default popup.border")
+ } else {
+ t.ok( (element.style.border.indexOf(border) != -1), "element.style.border set correctly");
+ }
+ //Safari 3 separates style overflow into overflow-x and overflow-y
+ var prop = (OpenLayers.BROWSER_NAME == 'safari') ? 'overflowX' : 'overflow';
+ t.eq( element.style[prop], overflow, "element.style.overflow set correctly");
+ t.eq( parseFloat(element.style.opacity), opacity, "element.style.opacity set correctly");
+ //Some non-IE browsers don't return the alpha string for this value, which is okay
+ var filterString = element.style.filter.match(/^alpha/) != null ?
+ 'alpha(opacity=' + (opacity * 100) + ')' : element.style.filter;
+ t.eq( element.style.filter, filterString, "element.style.filter set correctly");
+ }
+
+ function test_Util_modifyAlphaImageDiv(t) {
+ t.plan( 21 );
+
+ var imageDiv = OpenLayers.Util.createAlphaImageDiv();
+
+ var img = "http://www.openlayers.org/images/OpenLayers.trac.png";
+ var sz = new OpenLayers.Size(10,10);
+ var xy = new OpenLayers.Pixel(5,5);
+ var position = "absolute";
+ var id = "boo";
+ var border = "1px solid";
+ var sizing = "crop";
+ var opacity = 0.5;
+
+ OpenLayers.Util.modifyAlphaImageDiv(imageDiv, id, xy, sz, img, position, border, sizing, opacity);
+ if (OpenLayers.Util.alphaHack())
+ t.ok( true, "skipping element test outside of Mozilla");
+ else
+ t.ok( imageDiv.nodeName == "DIV", "createDiv creates a valid HTMLDivElement" );
+
+ t.eq( imageDiv.id, id, "image.id set correctly");
+ t.eq( imageDiv.style.left, xy.x + "px", "image.style.left set correctly");
+ t.eq( imageDiv.style.top, xy.y + "px", "image.style.top set correctly");
+
+ t.eq( imageDiv.style.width, sz.w + "px", "image.style.width set correctly");
+ t.eq( imageDiv.style.height, sz.h + "px", "image.style.height set correctly");
+
+ t.eq( imageDiv.style.position, position, "image.style.position set correctly");
+ t.eq( parseFloat(imageDiv.style.opacity), opacity, "element.style.opacity set correctly");
+
+
+
+ image = imageDiv.firstChild;
+
+ var filterString;
+ if (OpenLayers.Util.alphaHack()) {
+ filterString = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='http://www.openlayers.org/images/OpenLayers.trac.png', sizingMethod='crop') alpha(opacity=50)";
+ t.ok( true, "skipping element test outside of Mozilla");
+ } else {
+ //Some non-IE browsers don't return the alpha string for this value, which is okay
+ var filterString = imageDiv.style.filter.match(/^alpha/) != null ?
+ 'alpha(opacity=' + (opacity * 100) + ')' : imageDiv.style.filter;
+ t.ok( image.nodeName == "IMG", "createImage creates a valid HTMLImageElement" );
+ }
+ t.eq( imageDiv.style.filter, filterString, "element.style.filter set correctly");
+ t.eq( image.id, id + "_innerImage", "image.id set correctly");
+
+ t.eq( image.style.width, sz.w + "px", "image.style.width set correctly");
+ t.eq( image.style.height, sz.h + "px", "image.style.height set correctly");
+
+ //Safari 3 separates the border style into separate entities when reading it
+ if (OpenLayers.BROWSER_NAME == 'safari') {
+ var s = border.split(' ');
+ t.ok(image.style.borderTopWidth == s[0] && image.style.borderTopStyle == s[1], "good default popup.border")
+ } else {
+ t.ok( (image.style.border.indexOf(border) != -1), "image.style.border set correctly");
+ }
+
+ t.eq( image.style.position, "relative", "image.style.positionset correctly");
+ t.eq( image.src, img, "image.style.backgroundImage correctly");
+
+ if (OpenLayers.Util.alphaHack()) {
+
+ var filter = "progid:DXImageTransform.Microsoft" +
+ ".AlphaImageLoader(src='" + img + "', " +
+ "sizingMethod='" + sizing + "') alpha(opacity=" + opacity *100 + ")";
+ t.eq(imageDiv.style.filter, filter, "div filter value correctly set");
+
+ filter = "alpha(opacity=0)";
+ t.eq(image.style.filter, filter, "image filter set correctly");
+
+ } else {
+ t.ok(true, "div filter value not set (not in IE)");
+ t.ok(true, "image filter value not set (not in IE)");
+ }
+
+ var imageDiv = OpenLayers.Util.createAlphaImageDiv();
+ var display = "none";
+ imageDiv.style.display = display;
+ OpenLayers.Util.modifyAlphaImageDiv(imageDiv, id, xy, sz, img, position, border, sizing, opacity);
+ t.eq(imageDiv.style.display, display, "imageDiv.style.display set correctly, if 'none'");
+
+ var imageDiv = OpenLayers.Util.createAlphaImageDiv();
+ var display = "block";
+ imageDiv.style.display = display;
+ OpenLayers.Util.modifyAlphaImageDiv(imageDiv, id, xy, sz, img, position, border, sizing, opacity);
+ if(OpenLayers.Util.alphaHack()) {
+ t.eq(imageDiv.style.display, "inline-block", "imageDiv.style.display set correctly, if not 'none'");
+ } else {
+ t.ok(true, "inline-block is not part of CSS2 and is not supported by Firefox 2");
+ }
+
+
+
+ var imageDiv = OpenLayers.Util.createAlphaImageDiv(id, xy, sz, img, position, border, "scale", opacity);
+ if (OpenLayers.Util.alphaHack()) {
+ var filter = "progid:DXImageTransform.Microsoft" +
+ ".AlphaImageLoader(src='" + img + "', " +
+ "sizingMethod='scale') alpha(opacity=" + opacity *100 + ")";
+ t.eq(imageDiv.style.filter, filter, "sizingMethod default correctly set to scale");
+ } else {
+ t.ok(true);
+ }
+
+ }
+
+ function test_Util_upperCaseObject(t) {
+ t.plan(8);
+
+ var aKey = "chicken";
+ var aValue = "pot pie";
+
+ var bKey = "blorg";
+ var bValue = "us maximus";
+
+ var obj = {};
+ obj[aKey] = aValue;
+ obj[bKey] = bValue;
+
+ var uObj = OpenLayers.Util.upperCaseObject(obj);
+
+ //make sure old object not modified
+ t.eq(obj[aKey], aValue, "old lowercase value still present in old obj");
+ t.eq(obj[bKey], bValue, "old lowercase value still present in old obj");
+
+ t.eq(obj[aKey.toUpperCase()], null, "new uppercase value not present in old obj");
+ t.eq(obj[bKey.toUpperCase()], null, "new uppercase value not present in old obj");
+
+ //make sure new object modified
+ t.eq(uObj[aKey], null, "old lowercase value not present");
+ t.eq(uObj[bKey], null, "old lowercase value not present");
+
+ t.eq(uObj[aKey.toUpperCase()], aValue, "new uppercase value present");
+ t.eq(uObj[bKey.toUpperCase()], bValue, "new uppercase value present");
+ }
+
+ function test_Util_createUniqueID(t) {
+ t.plan(2);
+
+ var id = OpenLayers.Util.createUniqueID();
+ t.ok(OpenLayers.String.startsWith(id, "id_"),
+ "default OpenLayers.Util.createUniqueID starts id correctly");
+
+ var id = OpenLayers.Util.createUniqueID("chicken");
+ t.ok(OpenLayers.String.startsWith(id, "chicken"),
+ "OpenLayers.Util.createUniqueID starts id correctly");
+ }
+
+ function test_units(t) {
+ t.plan(2);
+ t.eq(OpenLayers.INCHES_PER_UNIT.m, OpenLayers.INCHES_PER_UNIT.Meter, 'Same inches per m and Meters');
+ t.eq(OpenLayers.INCHES_PER_UNIT.km, OpenLayers.INCHES_PER_UNIT.Kilometer, 'Same inches per km and Kilometers');
+ }
+
+ function test_Util_normalizeScale(t) {
+ t.plan(2);
+
+ //normal scale
+ var scale = 1/5;
+ t.eq( OpenLayers.Util.normalizeScale(scale), scale, "normalizing a normal scale does nothing");
+
+ //funky scale
+ var scale = 5;
+ t.eq( OpenLayers.Util.normalizeScale(scale), 1/5, "normalizing a wrong scale works!");
+ }
+
+ function test_Util_getScaleResolutionTranslation(t) {
+ t.plan(5);
+
+ var scale = 1/150000000;
+ var resolution = OpenLayers.Util.getResolutionFromScale(scale);
+ t.eq(resolution.toFixed(6), "0.476217", "Calculated correct resolution for " + scale);
+
+ var scale = 1/150000000;
+ var resolution = OpenLayers.Util.getResolutionFromScale(scale, 'm');
+ t.eq(resolution.toFixed(6), "52916.772500", "Calculated correct resolution for " + scale);
+
+ scale = 150000000;
+ resolution = OpenLayers.Util.getResolutionFromScale(scale);
+ t.eq(resolution.toFixed(6), "0.476217", "Calculated correct resolution for " + scale);
+
+ scale = 150000000;
+ resolution = OpenLayers.Util.getResolutionFromScale(scale);
+ t.eq(OpenLayers.Util.getScaleFromResolution(resolution), scale, "scale->resolution->scale works");
+
+ scale = null;
+ resolution = OpenLayers.Util.getResolutionFromScale(scale);
+ t.eq(resolution, undefined, "falsey scale results in undefined resolution");
+
+ }
+
+ function test_Util_getImgLocation(t) {
+ t.plan(3);
+
+ OpenLayers.ImgPath = "foo/";
+ t.eq(OpenLayers.Util.getImagesLocation(), "foo/", "ImgPath works as expected.");
+ OpenLayers.ImgPath = null;
+ t.eq(OpenLayers.Util.getImagesLocation().substr(OpenLayers.Util.getImagesLocation().length-4,4), "img/", "ImgPath works as expected when not set.");
+
+ OpenLayers.ImgPath = '';
+ t.eq(OpenLayers.Util.getImagesLocation().substr(OpenLayers.Util.getImagesLocation().length-4,4), "img/", "ImgPath works as expected when set to ''.");
+ }
+
+ function test_Util_isEquivalentUrl(t) {
+ t.plan(10);
+
+ var url1, url2, options;
+
+ //CASE
+
+ url1 = "http://www.openlayers.org";
+ url2 = "HTTP://WWW.OPENLAYERS.ORG";
+
+ t.ok(OpenLayers.Util.isEquivalentUrl(url1, url2), "default ignoreCase works");
+
+ //ARGS
+
+ url1 = "http://www.openlayers.org?foo=5;bar=6";
+ url2 = "http://www.openlayers.org?bar=6;foo=5";
+
+ t.ok(OpenLayers.Util.isEquivalentUrl(url1, url2), "shuffled arguments works");
+
+ //PORT
+
+ url1 = "http://www.openlayers.org:80";
+ url2 = "http://www.openlayers.org";
+
+ t.ok(OpenLayers.Util.isEquivalentUrl(url1, url2), "default ignorePort80 works");
+
+ options = {
+ 'ignorePort80': false
+ }
+ url1 = "http://www.openlayers.org:80";
+ url2 = "http://www.openlayers.org:50";
+
+ t.ok(!OpenLayers.Util.isEquivalentUrl(url1, url2, options), "port check works");
+
+
+ //HASH
+
+ url1 = "http://www.openlayers.org#barf";
+ url2 = "http://www.openlayers.org";
+
+ t.ok(OpenLayers.Util.isEquivalentUrl(url1, url2), "default ignoreHash works");
+ options = {
+ 'ignoreHash': false
+ }
+ t.ok(!OpenLayers.Util.isEquivalentUrl(url1, url2, options), "ignoreHash FALSE works");
+
+ //PROTOCOL
+
+ url1 = "http://www.openlayers.org";
+ url2 = "ftp://www.openlayers.org";
+
+ t.ok(!OpenLayers.Util.isEquivalentUrl(url1, url2), "default ignoreHash works");
+
+
+ //PATHNAME
+ url1 = "foo.html?bar=now#go";
+ url2 = "../tests/../tests/foo.html?bar=now#go";
+
+ t.ok(OpenLayers.Util.isEquivalentUrl(url1, url2), "relative vs. absolute paths works");
+
+ url1 = "/foo/bar";
+ url2 = new Array(window.location.pathname.split("/").length-1).join("../")+"foo/bar";
+
+ t.ok(OpenLayers.Util.isEquivalentUrl(url1, url2), "absolute and relative path without host works for "+url2)
+
+ //ARGS
+ url1 = "foo.html?bbox=1,2,3,4",
+ url2 = url1;
+ t.ok(OpenLayers.Util.isEquivalentUrl(url1, url2), "equal urls with comma delimited params are equal");
+ }
+
+ function test_createUrlObject(t) {
+
+ var cases = [{
+ url: "http://example.com/",
+ exp: {
+ protocol: "http:",
+ host: "example.com",
+ port: "80",
+ pathname: "/",
+ args: {},
+ hash: ""
+ }
+ }, {
+ url: "http://example.com:80/",
+ opt: {ignorePort80: true},
+ exp: {
+ protocol: "http:",
+ host: "example.com",
+ port: "",
+ pathname: "/",
+ args: {},
+ hash: ""
+ }
+ }, {
+ url: "http://example.com/",
+ opt: {ignorePort80: true},
+ exp: {
+ protocol: "http:",
+ host: "example.com",
+ port: "",
+ pathname: "/",
+ args: {},
+ hash: ""
+ }
+ }, {
+ url: "http://example.com:88/",
+ exp: {
+ protocol: "http:",
+ host: "example.com",
+ port: "88",
+ pathname: "/",
+ args: {},
+ hash: ""
+ }
+ }, {
+ url: "http://example.com:88/foo#bar",
+ exp: {
+ protocol: "http:",
+ host: "example.com",
+ port: "88",
+ pathname: "/foo",
+ args: {},
+ hash: "#bar"
+ }
+ }, {
+ url: "http://example.com:88/?foo=bar",
+ exp: {
+ protocol: "http:",
+ host: "example.com",
+ port: "88",
+ pathname: "/",
+ args: {foo: "bar"},
+ hash: ""
+ }
+ }, {
+ url: "http://example.com/bogus/../bogus/../path",
+ exp: {
+ protocol: "http:",
+ host: "example.com",
+ port: "80",
+ pathname: "/path",
+ args: {},
+ hash: ""
+ }
+ }, {
+ url: "/relative#foo",
+ exp: {
+ protocol: window.location.protocol,
+ host: window.location.hostname,
+ port: window.location.port || "80",
+ pathname: "/relative",
+ args: {},
+ hash: "#foo"
+ }
+ }, {
+ url: "../foo",
+ exp: {
+ protocol: window.location.protocol,
+ host: window.location.hostname,
+ port: window.location.port || "80",
+ pathname: (function() {
+ var parts = window.location.pathname.split("/");
+ return parts.slice(0, parts.length -2).join("/") + "/foo";
+ })(),
+ args: {},
+ hash: ""
+ }
+ }];
+
+ t.plan(cases.length);
+
+ var c, obj;
+ for(var i=0; i<cases.length; ++i) {
+ c = cases[i];
+ obj = OpenLayers.Util.createUrlObject(c.url, c.opt);
+ t.eq(obj, c.exp, i + ": '" + c.url + "'");
+ }
+
+ }
+
+ function test_Util_createUniqueIDSeq(t) {
+ t.plan(1);
+
+ OpenLayers.Util.lastSeqID = 0;
+ OpenLayers.Util.createDiv();
+ OpenLayers.Util.createDiv();
+ t.eq(OpenLayers.Util.createDiv().id, "OpenLayersDiv3", "Div created is sequential, starting at lastSeqID in Util.");
+ }
+
+ function test_Util_getParameters(t) {
+ t.plan(20);
+
+ t.eq(OpenLayers.Util.getParameters(''), {},
+ "getParameters works when the given argument is empty string");
+
+ t.eq(OpenLayers.Util.getParameters(), {},
+ "getParameters works with optional argument");
+
+ t.eq(OpenLayers.Util.getParameters(null), {},
+ "getParameters works with optional argument");
+
+ t.eq(OpenLayers.Util.getParameters('http://www.example.com'), {},
+ "getParameters works when args = ''");
+ t.eq(OpenLayers.Util.getParameters('http://www.example.com?'), {},
+ "getParameters works when args = '?'");
+ t.eq(OpenLayers.Util.getParameters('http://www.example.com?hello=world&foo=bar'),
+ {'hello' : 'world', 'foo': 'bar'},
+ "getParameters works when args = '?hello=world&foo=bar'");
+ t.eq(OpenLayers.Util.getParameters('http://www.example.com?hello=&foo=bar'),
+ {'hello' : '', 'foo': 'bar'},
+ "getParameters works when args = '?hello=&foo=bar'");
+ t.eq(OpenLayers.Util.getParameters('http://www.example.com?foo=bar#bugssucks'),
+ {'foo': 'bar'},
+ "getParameters works when using a fragment identifier");
+ t.eq(OpenLayers.Util.getParameters('http://www.example.com?foo=bar%3Aone'),
+ {'foo': 'bar:one'},
+ "getParameters works with percent encoded values");
+ t.eq(OpenLayers.Util.getParameters('http://www.example.com?foo=bar:one,pub,disco'),
+ {'foo': ['bar:one', 'pub', 'disco']},
+ "getParameters works with a comma-separated value (parses into array)");
+ t.eq(OpenLayers.Util.getParameters('http://www.example.com?foo=bar%3Aone%2Cpub%2Cdisco'),
+ {'foo': ['bar:one', 'pub', 'disco']},
+ "getParameters works with a URL encoded comma-separated values (parses into array)");
+
+ var value = "%20"; // say you wanted to have a query string parameter value be literal "%20"
+ var encoded = encodeURIComponent(value); // this is the proper URI component encoding
+ var url = "http://example.com/path?key=" + encoded; // this is a properly encoded URL
+ var params = OpenLayers.Util.getParameters(url);
+ t.eq(params.key, value, "a properly encoded value of '%20' is properly decoded");
+
+ /**
+ * IETF RFC 2396 (http://www.ietf.org/rfc/rfc2396.txt) says spaces
+ * should be encoded as "%20". However, the "+" is used widely to
+ * indicate a space in a URL.
+ */
+ t.eq(OpenLayers.Util.getParameters('http://www.example.com?foo=bar+one'),
+ {'foo': 'bar one'},
+ "getParameters works with + instead of %20 in values");
+ t.eq(OpenLayers.Util.getParameters('http://www.example.com?foo=bar%20one'),
+ {'foo': 'bar one'},
+ "getParameters works with properly encoded space character");
+ t.eq(OpenLayers.Util.getParameters('http://www.example.com?foo=bar%2Bone'),
+ {'foo': 'bar+one'},
+ "getParameters works with properly encoded + character");
+
+ // Let's do some round tripping to make it harder to introduce regressions
+ var obj = {
+ "a key": "a value with spaces (and +)",
+ "see%2B%2B": "C++",
+ "C++": "see%2B%2B",
+ "~%257E": "+%252B",
+ "who?": "me?",
+ "#yes": "#you",
+ "url": "http://example.com:80/?question=%3F&hash=%23&amp=&26#id"
+ };
+ var str = OpenLayers.Util.getParameterString(obj);
+ t.eq(OpenLayers.Util.getParameters("?" + str), obj, "round tripped parameters");
+
+ // try some oddly encoded strings
+ var url = "http://example.com/?C%E9sar=C%E9sar+Ch%E1vez";
+ var obj = OpenLayers.Util.getParameters(url);
+ t.ok("César" in obj, "got proper key from C%E9sar");
+ t.eq(obj["César"], "César Chávez", "got proper value from C%E9sar+Ch%E1vez");
+
+ // try some properly encoded strings
+ var url = "http://example.com/?C%C3%A9sar=C%C3%A9sar+Ch%C3%A1vez";
+ var obj = OpenLayers.Util.getParameters(url);
+ t.ok("César" in obj, "got proper key from C%C3%A9sar");
+ t.eq(obj["César"], "César Chávez", "got proper value from C%E9sar+Ch%E1vez");
+
+ }
+
+ function tests_Util_extend(t) {
+ t.plan(7);
+
+ var source = {
+ num: Math.random(),
+ obj: {
+ foo: "bar"
+ },
+ method: function() {
+ return "method";
+ },
+ toString: function() {
+ return "source";
+ },
+ nada: undefined
+ };
+ var destination = OpenLayers.Util.extend({nada: "untouched"}, source);
+ t.eq(destination.num, source.num,
+ "extend properly sets primitive property on destination");
+ t.eq(destination.obj, source.obj,
+ "extend properly sets object property on destination");
+ t.eq(destination.method(), "method",
+ "extend properly sets function property on destination");
+ t.eq(destination.toString(), "source",
+ "extend properly sets custom toString method");
+ t.eq(destination.nada, "untouched",
+ "undefined source properties don't clobber existing properties");
+ t.eq(window.property, undefined, "Property variable not clobbered.");
+
+ var destination;
+ var source = {rand: Math.random()};
+ var ret = OpenLayers.Util.extend(destination, source);
+ t.eq(destination.rand, source.rand, "works with undefined destination");
+
+ }
+
+ function test_XX_Util_Try(t) {
+ t.plan(7);
+
+ var func1 = function() {
+ t.ok(true, "func1 executed");
+ throw "error";
+ };
+
+ var func2 = function() {
+ t.ok(true, "func2 executed");
+ throw "error";
+ };
+
+ g_TestVal3 = {};
+ var func3 = function() {
+ t.ok(true, "func3 executed");
+ return g_TestVal3;
+ };
+
+ g_TestVal4 = {};
+ var func4 = function() {
+ t.fail("func4 should *not* be executed");
+ return g_TestVal4;
+ };
+
+ var ret = OpenLayers.Util.Try(func1, func2);
+ t.ok(ret == null, "if all functions throw exceptions, null returned");
+
+ var ret = OpenLayers.Util.Try(func1, func2, func3, func4);
+ t.ok(ret == g_TestVal3, "try returns first sucessfully executed function's return");
+
+ }
+
+ function test_getRenderedDimensions(t) {
+ // from <script src="Util_common.js"> and shared by Util_w3c.html
+ com_test_getRenderedDimensions(t);
+ }
+
+ function test_toFloat(t) {
+ t.plan(2);
+ // actual possible computed Mercator tile coordinates, more or less
+ var a1=40075016.67999999, b1=-20037508.33999999,
+ a2=40075016.68, b2=-20037508.34;
+ t.eq(OpenLayers.Util.toFloat(a1), OpenLayers.Util.toFloat(a2),
+ "toFloat rounds large floats correctly #1");
+ t.eq(OpenLayers.Util.toFloat(b1), OpenLayers.Util.toFloat(b2),
+ "toFloat rounds large floats correctly #2");
+ }
+ function test_getFormattedLonLat(t) {
+ t.plan(3);
+ var z = 2 + (4/60) - 0.000002 ;
+ t.eq(OpenLayers.Util.getFormattedLonLat(z,"lon"), "02°04'00\"E",
+ "LonLat does not show 60 seconds.");
+ t.eq(OpenLayers.Util.getFormattedLonLat(-181, "lon"), "179°00'00\"E", "crossing dateline from the west results in correct east coordinate");
+ t.eq(OpenLayers.Util.getFormattedLonLat(181, "lon"), "179°00'00\"W", "crossing dateline from the east results in correct west coordinate");
+ }
+
+ /**
+ * To test that we can safely call OpenLayers.Util.extend with an Event
+ * instance, we need to capture a real event.
+ */
+ var loadEvent;
+ window.onload = function(evt) {
+ loadEvent = evt || window.event;
+ }
+ function test_extend_event(t) {
+ t.plan(2);
+ t.ok(loadEvent, "loadEvent recorded");
+ var extended, err;
+ try {
+ extended = OpenLayers.Util.extend({foo: "bar"}, loadEvent);
+ } catch (e) {
+ err = e;
+ }
+ if (err) {
+ t.fail("Failed to extend with an event: " + err.message);
+ } else {
+ t.eq(extended && extended.foo, "bar", "extended with event");
+ }
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/Util/vendorPrefix.html b/misc/openlayers/tests/Util/vendorPrefix.html
new file mode 100644
index 0000000..924ae09
--- /dev/null
+++ b/misc/openlayers/tests/Util/vendorPrefix.html
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>vendorPrefix.js Tests</title>
+ <script>
+ var div = document.createElement("div");
+ var style = div.style,
+ orgCreateElement = document.createElement;
+
+ // wrap document.createElement to control property values
+ document.createElement = function(type) {
+ return div;
+ };
+
+ // dependencies for tests
+ var OpenLayers = [
+ "OpenLayers/Util/vendorPrefix.js"
+ ];
+
+ </script>
+ <script src="../OLLoader.js"></script>
+
+ <script>
+
+ /**
+ * Test vendor prefixing
+ */
+ function test_vendor_prefixes(t) {
+ t.plan(20);
+ var err;
+
+ function clearCache(type) {
+ var cache = OpenLayers.Util.vendorPrefix[type.replace("style", "js") + "Cache"];
+ for (var key in cache) {
+ delete cache[key];
+ }
+ }
+
+ function setStyleMockProp(prop, value) {
+ if (prop && value === undefined) {
+ delete style[prop];
+ } else if (prop) {
+ style[prop] = value;
+ }
+ }
+
+ function curryTestPrefix(type) {
+ return function(standardProp, expectedPrefix, msg) {
+ var prefixedProp, err;
+ try {
+ clearCache(type);
+ setStyleMockProp(expectedPrefix, "");
+ prefixedProp = OpenLayers.Util.vendorPrefix[type](standardProp);
+ } catch(e) {
+ err = e;
+ } finally {
+ setStyleMockProp(expectedPrefix, undefined);
+ }
+
+ if(!err) {
+ t.eq(prefixedProp, expectedPrefix, msg);
+ } else {
+ t.fail("Error when testing " + type.toUpperCase() + " vendor prefix: " + err.message);
+ }
+ };
+ }
+ var testDomPrefix = curryTestPrefix("style"),
+ testCssPrefix = curryTestPrefix("css");
+
+ testDomPrefix("unsupported", null, "DOM vendor prefix - unsupported");
+ testCssPrefix("unsupported", null, "CSS vendor prefix - unsupported");
+
+ testDomPrefix("test", "test", "DOM vendor prefix - single word");
+ testCssPrefix("test", "test", "CSS vendor prefix - single word");
+
+ testDomPrefix("testMultiWord", "testMultiWord", "DOM vendor prefix - multiple words");
+ testCssPrefix("test-multi-word", "test-multi-word", "CSS vendor prefix - multiple words");
+
+ testDomPrefix("multiWord", "WebkitMultiWord", "DOM vendor prefix - multiple words for WebKit");
+ testCssPrefix("multi-word", "-webkit-multi-word", "CSS vendor prefix - multiple words for WebKit");
+
+ testDomPrefix("multiWord", "MozMultiWord", "DOM vendor prefix - multiple words for Mozilla");
+ testCssPrefix("multi-word", "-moz-multi-word", "CSS vendor prefix - multiple words for Mozilla");
+
+ testDomPrefix("multiWord", "OMultiWord", "DOM vendor prefix - multiple words for Opera");
+ testCssPrefix("multi-word", "-o-multi-word", "CSS vendor prefix - multiple words for Opera");
+
+ testDomPrefix("multiWord", "msMultiWord", "DOM vendor prefix - multiple words for Internet Explorer");
+ testCssPrefix("multi-word", "-ms-multi-word", "CSS vendor prefix - multiple words for Internet Explorer");
+
+ // test vendor prefix on object
+ clearCache("js");
+ t.eq( OpenLayers.Util.vendorPrefix.js( {}, "unsupported" ), null, "Standard object property - unsupported");
+
+ clearCache("js");
+ t.eq( OpenLayers.Util.vendorPrefix.js( { "test": true }, "test" ), "test", "Standard object property");
+
+ clearCache("js");
+ t.eq( OpenLayers.Util.vendorPrefix.js( { "oTest": true }, "test" ), "oTest", "Standard object property");
+
+ clearCache("js");
+ t.eq( OpenLayers.Util.vendorPrefix.js( { "msTest": true }, "test" ), "msTest", "Standard object property");
+
+ clearCache("js");
+ t.eq( OpenLayers.Util.vendorPrefix.js( { "mozTest": true }, "test" ), "mozTest", "Standard object property");
+
+ clearCache("js");
+ t.eq( OpenLayers.Util.vendorPrefix.js( { "webkitTest": true }, "test" ), "webkitTest", "Standard object property");
+
+ // unwrap document.createElement
+ document.createElement = orgCreateElement;
+ }
+
+ </script>
+ </head>
+ <body></body>
+</html> \ No newline at end of file
diff --git a/misc/openlayers/tests/Util_common.js b/misc/openlayers/tests/Util_common.js
new file mode 100644
index 0000000..471b0d6
--- /dev/null
+++ b/misc/openlayers/tests/Util_common.js
@@ -0,0 +1,64 @@
+function com_test_getRenderedDimensions(t) {
+ t.plan(17);
+ var content = (new Array(100)).join("foo ");
+
+ // test with fixed width
+ var fw = OpenLayers.Util.getRenderedDimensions(content, {w: 20});
+ t.eq(fw.w, 20, "got the fixed width");
+
+ // test with fixed height
+ var fh = OpenLayers.Util.getRenderedDimensions(content, {h: 15});
+ t.eq(fh.h, 15, "got the fixed height");
+
+ var size = OpenLayers.Util.getRenderedDimensions("<p>Content</p>");
+ var bigger = OpenLayers.Util.getRenderedDimensions("<p>Content</p>", null, {displayClass: 'test_getRenderedDimensions'});
+ var overflow = OpenLayers.Util.getRenderedDimensions("<p style='overflow:auto'>Content</p>");
+ var width = OpenLayers.Util.getRenderedDimensions("<p>Content</p>", new OpenLayers.Size(250, null));
+ var height = OpenLayers.Util.getRenderedDimensions("<p>Content</p>", new OpenLayers.Size(null, 40));
+ t.ok((size.w + 40) == bigger.w && (size.h + 40) == bigger.h, "bigger Pass: " + size + ", " + bigger);
+ t.ok(size.w == overflow.w && size.h == overflow.h, "overflow Pass: " + size + ", " + overflow);
+ t.ok(width.w == 250 && width.h == size.h, "width Pass: " + size + ", " + width);
+ t.ok(height.h == 40 && height.w == size.w, "height Pass: " + size + ", " + height);
+
+ content = (new Array(10)).join("foo foo foo <br>");
+ var testName,
+ finalSize,
+ initialSize = OpenLayers.Util.getRenderedDimensions(content, null);
+ // containerElement option on absolute position with width and height
+ testName = "Absolute with w&h: ";
+ var optionAbsDiv ={
+ containerElement: document.getElementById("absoluteDiv")
+ };
+ finalSize = OpenLayers.Util.getRenderedDimensions(content, null, optionAbsDiv);
+ t.ok(initialSize.w > 0 && initialSize.h > 0, "Has initial size (requires visible test_iframe)");
+ t.eq(finalSize.w, initialSize.w,
+ testName + "initial width " + initialSize.w + "px is maintained");
+ t.eq(finalSize.h, initialSize.h,
+ testName + "initial height " + initialSize.h + "px is maintained");
+ testName = "Absolute with w&h (set height): ";
+ finalSize = OpenLayers.Util.getRenderedDimensions(content, {h: 15}, optionAbsDiv);
+ t.eq(finalSize.h, 15, testName + "got the fixed height to 15px");
+ t.eq(finalSize.w, initialSize.w,
+ testName + "initial width " + initialSize.w + "px is maintained");
+ testName = "Absolute with w&h (set width): ";
+ finalSize = OpenLayers.Util.getRenderedDimensions(content, {w: 20}, optionAbsDiv);
+ t.eq(finalSize.w, 20, testName + "got the fixed width to 20px");
+ // containerElement option on absolute position without width and height
+ testName = "Absolute without w&h: ";
+ var optionAbsDiv00 ={
+ containerElement: document.getElementById("absoluteDiv00")
+ };
+ finalSize = OpenLayers.Util.getRenderedDimensions(content, null, optionAbsDiv00);
+ t.eq(finalSize.w, initialSize.w,
+ testName + "initial width " + initialSize.w + "px is maintained");
+ t.eq(finalSize.h, initialSize.h,
+ testName + "initial height " + initialSize.h + "px is maintained");
+ testName = "Absolute without w&h (set height): ";
+ finalSize = OpenLayers.Util.getRenderedDimensions(content, {h: 15}, optionAbsDiv00);
+ t.eq(finalSize.h, 15, testName + "got the fixed height to 15px");
+ t.eq(finalSize.w, initialSize.w,
+ testName + "initial width " + initialSize.w + "px is maintained");
+ testName = "Absolute without w&h (set width): ";
+ finalSize = OpenLayers.Util.getRenderedDimensions(content, {w: 20}, optionAbsDiv00);
+ t.eq(finalSize.w, 20, testName + "got the fixed width to 20px");
+}
diff --git a/misc/openlayers/tests/Util_w3c.html b/misc/openlayers/tests/Util_w3c.html
new file mode 100644
index 0000000..457341f
--- /dev/null
+++ b/misc/openlayers/tests/Util_w3c.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+ <style type="text/css">
+ .test_getRenderedDimensions p{
+ padding: 20px;
+ }
+ </style>
+ <script>
+ var OpenLayers = [
+ "OpenLayers/BaseTypes/Class.js",
+ "OpenLayers/Util.js",
+ "OpenLayers/BaseTypes.js",
+ "OpenLayers/BaseTypes/Element.js",
+ "OpenLayers/BaseTypes/LonLat.js",
+ "OpenLayers/BaseTypes/Pixel.js",
+ "OpenLayers/BaseTypes/Size.js",
+ "OpenLayers/Lang.js",
+ "OpenLayers/Console.js"
+ ];
+ </script>
+ <script src="OLLoader.js"></script>
+ <script src="Util_common.js"></script>
+ <script type="text/javascript">
+ function test_getRenderedDimensions(t) {
+ // from <script src="Util_common.js"> and shared by Util.html
+ com_test_getRenderedDimensions(t);
+ }
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/WPSClient.html b/misc/openlayers/tests/WPSClient.html
new file mode 100644
index 0000000..34b21f9
--- /dev/null
+++ b/misc/openlayers/tests/WPSClient.html
@@ -0,0 +1,108 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var client;
+
+ function test_initialize(t) {
+ t.plan(3);
+
+ client = new OpenLayers.WPSClient({
+ servers: {
+ local: "/geoserver/wps"
+ }
+ });
+
+ t.ok(client instanceof OpenLayers.WPSClient, 'creates an instance');
+ t.ok(client.events, 'has an events instance');
+ t.eq(client.servers.local.url, '/geoserver/wps', 'servers stored on instance');
+ }
+
+ function test_getProcess(t) {
+ t.plan(4);
+
+ client = new OpenLayers.WPSClient({
+ servers: {
+ local: "/geoserver/wps"
+ },
+ lazy: true
+ });
+
+ var process = client.getProcess('local', 'gs:splitPolygon');
+ t.ok(process instanceof OpenLayers.WPSProcess, 'creates a process');
+ t.ok(process.client === client, 'process knows about client');
+ t.eq(process.server, 'local', 'process created with correct server');
+ t.eq(process.identifier, 'gs:splitPolygon', 'process created with correct identifier');
+
+ }
+
+ function test_describeProcess(t) {
+ t.plan(6);
+ var log = {request: [], event: []};
+ var originalGET = OpenLayers.Request.GET;
+ OpenLayers.Request.GET = function(cfg) {
+ log.request.push(cfg);
+ }
+ function describe(evt) {
+ log.event.push(evt);
+ }
+ client.events.register('describeprocess', this, describe);
+
+ process = client.getProcess('local', 'gs:splitPolygon');
+ t.eq(client.servers.local.processDescription['gs:splitPolyon'], null, 'describeProcess pending');
+ process.describe();
+ t.eq(log.request.length, 1, 'describeProcess request only sent once');
+ log.request[0].success.call(client, {
+ responseText: '<?xml version="1.0" encoding="UTF-8"?><wps:ProcessDescriptions xmlns:wps="http://www.opengis.net/wps/1.0.0"></wps:ProcessDescriptions>'
+ });
+ t.eq(log.event[0].type, 'describeprocess', 'describeprocess event triggered');
+ t.ok(client.servers.local.processDescription['gs:splitPolygon'], 'We have a process description!');
+ process.describe();
+ t.eq(log.request.length, 1, 'describeProcess request only sent once');
+ t.eq(log.event.length, 1, 'describeprocess event only triggered once');
+
+ OpenLayers.Request.GET = originalGET;
+ client.events.unregister('describeprocess', this, describe);
+ }
+
+ function test_execute(t) {
+ t.plan(1);
+
+ client = new OpenLayers.WPSClient({
+ servers: {
+ local: "/geoserver/wps"
+ },
+ lazy: true
+ });
+ var log = [];
+ client.getProcess = function() {
+ return {
+ execute: function(options) {
+ log.push(options);
+ }
+ }
+ }
+
+ client.execute({inputs: 'a', success: 'b', scope: 'c'});
+ t.eq(log[0], {inputs: 'a', success: 'b', scope: 'c'}, "process executed with correct options");
+ }
+
+ function test_destroy(t) {
+ t.plan(2);
+ client = new OpenLayers.WPSClient({
+ servers: {
+ local: "/geoserver/wps"
+ },
+ lazy: true
+ });
+ client.destroy();
+ t.eq(client.events, null, "Events nullified");
+ t.eq(client.servers, null, "Servers nullified");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/WPSProcess.html b/misc/openlayers/tests/WPSProcess.html
new file mode 100644
index 0000000..f668ca3
--- /dev/null
+++ b/misc/openlayers/tests/WPSProcess.html
@@ -0,0 +1,188 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ var wkt = new OpenLayers.Format.WKT();
+ var process;
+ var client = new OpenLayers.WPSClient({
+ servers: {
+ local: 'geoserver/wps'
+ }
+ });
+ client.servers.local.processDescription = {
+ 'JTS:intersection': '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<wps:ProcessDescriptions xml:lang="en" service="WPS" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink"><ProcessDescription wps:processVersion="1.0.0" statusSupported="true" storeSupported="true"><ows:Identifier>JTS:intersection</ows:Identifier><ows:Title>Returns the intersectoin between a and b (eventually an empty collection if there is no intersection)</ows:Title><ows:Abstract>Returns the intersectoin between a and b (eventually an empty collection if there is no intersection)</ows:Abstract><DataInputs><Input maxOccurs="1" minOccurs="1"><ows:Identifier>a</ows:Identifier><ows:Title>a</ows:Title><ows:Abstract>[undescribed]</ows:Abstract><ComplexData><Default><Format><MimeType>text/xml; subtype=gml/3.1.1</MimeType></Format></Default><Supported><Format><MimeType>text/xml; subtype=gml/3.1.1</MimeType></Format><Format><MimeType>text/xml; subtype=gml/2.1.2</MimeType></Format><Format><MimeType>application/wkt</MimeType></Format><Format><MimeType>application/gml-3.1.1</MimeType></Format><Format><MimeType>application/gml-2.1.2</MimeType></Format></Supported></ComplexData></Input><Input maxOccurs="1" minOccurs="1"><ows:Identifier>b</ows:Identifier><ows:Title>b</ows:Title><ows:Abstract>[undescribed]</ows:Abstract><ComplexData><Default><Format><MimeType>text/xml; subtype=gml/3.1.1</MimeType></Format></Default><Supported><Format><MimeType>text/xml; subtype=gml/3.1.1</MimeType></Format><Format><MimeType>text/xml; subtype=gml/2.1.2</MimeType></Format><Format><MimeType>application/wkt</MimeType></Format><Format><MimeType>application/gml-3.1.1</MimeType></Format><Format><MimeType>application/gml-2.1.2</MimeType></Format></Supported></ComplexData></Input></DataInputs><ProcessOutputs><Output><ows:Identifier>result</ows:Identifier><ows:Title>Process result</ows:Title><ComplexOutput><Default><Format><MimeType>text/xml; subtype=gml/3.1.1</MimeType></Format></Default><Supported><Format><MimeType>text/xml; subtype=gml/3.1.1</MimeType></Format><Format><MimeType>text/xml; subtype=gml/2.1.2</MimeType></Format><Format><MimeType>application/wkt</MimeType></Format><Format><MimeType>application/gml-3.1.1</MimeType></Format><Format><MimeType>application/gml-2.1.2</MimeType></Format></Supported></ComplexOutput></Output></ProcessOutputs></ProcessDescription></wps:ProcessDescriptions>'
+ };
+
+ function test_initialize(t) {
+ t.plan(1);
+ process = new OpenLayers.WPSProcess();
+ t.ok(process instanceof OpenLayers.WPSProcess, 'creates an instance');
+ }
+
+ function test_describe(t) {
+ t.plan(2);
+ process = client.getProcess('local', 'JTS:intersection');
+ var log = [];
+ process.describe({
+ callback: function(description) { log.push(description); }
+ });
+ t.delay_call(0.1, function() {
+ t.eq(log.length, 1, 'callback called');
+ t.eq(log[0].identifier, 'JTS:intersection', 'callback called with correct description');
+ });
+ }
+
+ function test_execute(t) {
+ t.plan(7);
+
+ var log = [];
+ var originalPOST = OpenLayers.Request.POST;
+ OpenLayers.Request.POST = function(cfg) {
+ log.push(cfg);
+ cfg.success.call(cfg.scope, {responseText: ''});
+ }
+
+ process = new OpenLayers.WPSProcess({
+ client: client,
+ server: 'local',
+ identifier: 'gs:splitPolygon'
+ });
+ process.description = {
+ dataInputs: [{
+ identifier: 'line',
+ complexData: {
+ supported: {
+ formats: {'application/wkt': true}
+ }
+ }
+ }, {
+ identifier: 'polygon',
+ complexData: {
+ supported: {
+ formats: {'application/wkt': true}
+ }
+ }
+ }],
+ processOutputs: [{
+ identifier: 'foo',
+ complexOutput: {
+ supported: {
+ formats: {'application/wkt': true}
+ }
+ }
+ }]
+ };
+ var line = 'LINESTRING(117 22,112 18,118 13,115 8)';
+ var polygon = 'POLYGON((110 20,120 20,120 10,110 10,110 20),(112 17,118 18,118 16,112 15,112 17))';
+ var output = [];
+ function success(result) {
+ output.push(result);
+ }
+ // configured with output identifier
+ process.execute({
+ inputs: {
+ line: wkt.read(line),
+ polygon: wkt.read(polygon)
+ },
+ output: 'foo',
+ success: success
+ });
+ // configured without output identifier
+ process.execute({
+ inputs: {
+ line: wkt.read(line),
+ polygon: wkt.read(polygon)
+ },
+ success: success
+ });
+
+ t.delay_call(0.1, function() {
+ t.eq(log.length, 2, 'Two execute requests sent');
+ t.eq(process.description.dataInputs[0].data.complexData.value, line, 'data for first input correct');
+ t.eq(process.description.dataInputs[0].data.complexData.mimeType, 'application/wkt', 'format for first input correct');
+ t.eq(process.description.responseForm.rawDataOutput.identifier, 'foo', 'correct identifier for responseForm');
+ t.eq(process.description.responseForm.rawDataOutput.mimeType, 'application/wkt', 'correct format for responseForm');
+ t.ok('foo' in output[0], 'process result contains output with correct identifier when configured with output');
+ t.ok('result' in output[1], 'process result contains output with correct identifier when configured without output');
+
+ OpenLayers.Request.POST = originalPOST;
+ });
+ }
+
+ function test_chainProcess(t) {
+ t.plan(5);
+
+ var originalGET = OpenLayers.Request.GET;
+ OpenLayers.Request.GET = function(cfg) {
+ window.setTimeout(function() {
+ cfg.success.call(cfg.scope, {
+ responseText: '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<wps:ProcessDescriptions xml:lang="en" service="WPS" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink"><ProcessDescription wps:processVersion="1.0.0" statusSupported="true" storeSupported="true"><ows:Identifier>JTS:buffer</ows:Identifier><ows:Title>Buffers a geometry using a certain distance</ows:Title><ows:Abstract>Buffers a geometry using a certain distance</ows:Abstract><DataInputs><Input maxOccurs="1" minOccurs="1"><ows:Identifier>geom</ows:Identifier><ows:Title>geom</ows:Title><ows:Abstract>The geometry to be buffered</ows:Abstract><ComplexData><Default><Format><MimeType>text/xml; subtype=gml/3.1.1</MimeType></Format></Default><Supported><Format><MimeType>text/xml; subtype=gml/3.1.1</MimeType></Format><Format><MimeType>text/xml; subtype=gml/2.1.2</MimeType></Format><Format><MimeType>application/wkt</MimeType></Format><Format><MimeType>application/gml-3.1.1</MimeType></Format><Format><MimeType>application/gml-2.1.2</MimeType></Format></Supported></ComplexData></Input><Input maxOccurs="1" minOccurs="1"><ows:Identifier>distance</ows:Identifier><ows:Title>distance</ows:Title><ows:Abstract>The distance (same unit of measure as the geometry)</ows:Abstract><LiteralData><ows:DataType>xs:double</ows:DataType><ows:AnyValue/></LiteralData></Input><Input maxOccurs="1" minOccurs="0"><ows:Identifier>quadrantSegments</ows:Identifier><ows:Title>quadrantSegments</ows:Title><ows:Abstract>Number of quadrant segments. Use &gt; 0 for round joins, 0 for flat joins, &lt; 0 for mitred joins</ows:Abstract><LiteralData><ows:DataType>xs:int</ows:DataType><ows:AnyValue/></LiteralData></Input><Input maxOccurs="1" minOccurs="0"><ows:Identifier>capStyle</ows:Identifier><ows:Title>capStyle</ows:Title><ows:Abstract>The buffer cap style, round, flat, square</ows:Abstract><LiteralData><ows:AllowedValues><ows:Value>Round</ows:Value><ows:Value>Flat</ows:Value><ows:Value>Square</ows:Value></ows:AllowedValues></LiteralData></Input></DataInputs><ProcessOutputs><Output><ows:Identifier>result</ows:Identifier><ows:Title>result</ows:Title><ComplexOutput><Default><Format><MimeType>text/xml; subtype=gml/3.1.1</MimeType></Format></Default><Supported><Format><MimeType>text/xml; subtype=gml/3.1.1</MimeType></Format><Format><MimeType>text/xml; subtype=gml/2.1.2</MimeType></Format><Format><MimeType>application/wkt</MimeType></Format><Format><MimeType>application/gml-3.1.1</MimeType></Format><Format><MimeType>application/gml-2.1.2</MimeType></Format></Supported></ComplexOutput></Output></ProcessOutputs></ProcessDescription></wps:ProcessDescriptions>'
+ });
+ }, 100);
+ }
+ var originalPOST = OpenLayers.Request.POST;
+ OpenLayers.Request.POST = function(cfg) {
+ cfg.success.call(cfg.scope, {responseText: ''});
+ };
+
+ var intersect = client.getProcess('local', 'JTS:intersection');
+ intersect.configure({
+ inputs: {
+ a: wkt.read(
+ 'LINESTRING(117 22,112 18,118 13,115 8)'
+ ),
+ b: wkt.read(
+ 'POLYGON((110 20,120 20,120 10,110 10,110 20),(112 17,118 18,118 16,112 15,112 17))'
+ )
+ }
+ });
+
+ // one buffer process to make sure chaining works
+ var buffer1 = client.getProcess('local', 'JTS:buffer');
+ // another buffer process to make sure that things work asynchronously
+ var buffer2 = client.getProcess('local', 'JTS:buffer');
+ var log = [];
+ buffer1.chainProcess = buffer2.chainProcess = function() {
+ log.push(this.executeCallbacks.length);
+ OpenLayers.WPSProcess.prototype.chainProcess.apply(this, arguments);
+ };
+ var done1 = done2 = false;
+ buffer1.execute({
+ inputs: {
+ geom: intersect.output(),
+ distance: 1
+ },
+ success: function(outputs) {
+ done1 = true;
+ }
+ });
+ buffer2.execute({
+ inputs: {
+ geom: intersect.output(),
+ distance: 2
+ },
+ success: function(outputs) {
+ done2 = true;
+ }
+ });
+
+ t.delay_call(0.5, function() {
+ t.eq(log.length, 2, 'chainProcess called once for each process');
+ t.eq(log[0], 1, 'executeCallback queued to wait for 1 chained process');
+ t.eq(log[1], 1, 'executeCallback queued to wait for 1 chained process');
+ t.eq(done1, true, 'execute for buffer1 process successfully completed');
+ t.eq(done2, true, 'execute for buffer2 process successfully completed');
+
+ OpenLayers.Request.GET = originalGET;
+ OpenLayers.Request.POST = originalPOST;
+ });
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/atom-1.0.xml b/misc/openlayers/tests/atom-1.0.xml
new file mode 100644
index 0000000..f0d5d6f
--- /dev/null
+++ b/misc/openlayers/tests/atom-1.0.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom"
+ xmlns:georss="http://www.georss.org/georss">
+
+ <title>tumulus</title>
+ <link rel="self"
+ href="http://pleiades.stoa.org/places/tumulus"/>
+ <updated/>
+ <author/>
+ <id>http://pleiades.stoa.org/places/tumulus</id>
+
+ <entry>
+ <title>Unnamed Tumulus</title>
+ <link rel="alternate"
+ href="http://pleiades.stoa.org/places/638896"
+ />
+ <id>http://pleiades.stoa.org/places/638896</id>
+ <updated/>
+ <summary>An ancient tumulus, attested during the Classical period (modern location: Karaburun). Its ancient name is not known.</summary>
+ <georss:point>36.7702 29.9805</georss:point>
+ </entry>
+ <entry>
+ <title>Unnamed Tumulus</title>
+ <link rel="alternate"
+ href="http://pleiades.stoa.org/places/638924"
+ />
+ <id>http://pleiades.stoa.org/places/638924</id>
+ <updated/>
+ <summary>An ancient tumulus, attested during the Classical period (modern location: Kızılbel). Its ancient name is not known.</summary>
+ <georss:point>36.7263 29.8619</georss:point>
+ </entry>
+
+</feed>
+
diff --git a/misc/openlayers/tests/auto-tests.html b/misc/openlayers/tests/auto-tests.html
new file mode 100644
index 0000000..f467e41
--- /dev/null
+++ b/misc/openlayers/tests/auto-tests.html
@@ -0,0 +1,2447 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html><head>
+<meta http-equiv="refresh" content="1200" />
+<title> Run the testsuite</title>
+<noscript>Javascript is disabled in your browser. This page cannot be displayed correctly without Javascript. Sorry. <br/> If you want to view this page, please change your browser settings so that Javascript is enabled.</noscript>
+<!--
+Test.AnotherWay version 0.5
+
+Copyright (c) 2005 Artem Khodush, http://straytree.org
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+-->
+<style type="text/css">
+* { padding: 0; margin: 0; }
+html { height: 99%; }
+body { height: 98%; font: normal normal 10pt sans-serif }
+#col1 { float: left; width: 27em; margin: 0 0 0 1em; overflow: visible; }
+#col2 { position: relative; height: 98%; margin: 0 0.5em 0 28em; }
+#col1_header { margin-top: 0.5em; }
+#scroller { height: 400px; overflow: auto;}
+#testtable { margin: 0 0 2em 0; width: 97%; }
+#run_buttons { margin-bottom: 4em; }
+
+#right_header { padding-top: 0.8em; }
+#results_count { float: left; }
+.active_tab { float: right; padding: 0 1em 0.2em 1em; background: #0af; border: 1px solid #048; border-bottom: none; cursor: pointer; cursor: hand;
+ position: relative; top: -0.2em; }
+.inactive_tab { float: right; padding: 0 1em 0 1em; background: #9bb; color: #444; border: 1px solid #9bb; border-bottom: none; cursor: pointer; cursor: hand; }
+.inactive_mouseover_tab { float: right; padding: 0 1em 0 1em; background: #9bb; color: #062; border: 1px solid #062; border-bottom: none; cursor: pointer; cursor: hand; }
+
+#right_frame { overflow: auto; position: relative; top: -0.2em; clear: right; height: 95%; border: 1px solid #048; }
+
+#debug { display: none; }
+#debug p { margin: 2px 0 0 5em; text-indent: -4.8em; }
+
+#error { display: none; color: #c22; }
+
+#results p { margin: 0 0 2px 0; }
+/* cursor indicating that detailed results may be expanded/contracted */
+#results p.badtest { cursor: text; }
+#results p.ok, #results p.fail { cursor: pointer; cursor: hand; }
+
+/* colored squares in the results window at the left of test page names */
+#results p.ok .bullet { background: #6d6; }
+#results p.fail .bullet { background: #d46; }
+#results p.badtest .bullet { background: #ea3; }
+#results p.loading .bullet { background: #48f; }
+#results p.running .bullet { background: #26e; }
+#results p.waiting .bullet { background: #04d; }
+/* highlight in the results line */
+#results p .warning { background: #ffc; }
+
+/* layout of the detailed results */
+.result_detail { padding-left: 3em; }
+.result_exception_detail { padding-left: 4em; }
+.result_exception_stack_detail { padding-left: 5em; }
+.result_micro_detail { padding-left: 6em; }
+/* colouring in the detailed results */
+.result_detail .fail, .result_exception_detail .fail, .result_micro_detail .fail { background: #ffd8d8; }
+
+/* "start recording" controls*/
+#record_div { margin-top: 3em; }
+#record_div p { margin-bottom: 0.5em; }
+#record_select { width: 88%; }
+#record_input { width: 53%; }
+</style>
+<script type="text/javascript">
+<!--
+
+function report_results() {
+ req = false;
+ // branch for native XMLHttpRequest object
+ if(window.XMLHttpRequest && !(window.ActiveXObject)) {
+ try {
+ req = new XMLHttpRequest();
+ } catch(e) {
+ req = false;
+ }
+ // branch for IE/Windows ActiveX version
+ } else if(window.ActiveXObject) {
+ try {
+ req = new ActiveXObject("Msxml2.XMLHTTP");
+ } catch(e) {
+ try {
+ req = new ActiveXObject("Microsoft.XMLHTTP");
+ } catch(e) {
+ req = false;
+ }
+ }
+ }
+ req.open("POST", "/test/results.cgi");
+ req.setRequestHeader("Content-Type", 'application/x-www-form-urlencoded');
+ var results = document.getElementById('total').innerHTML;
+ var test_text = "";
+ if (results.match("fail")) {
+ test_text = document.getElementById("results").innerHTML;
+ }
+ req.send("results="+escape(results)+"&test_text="+escape(test_text));
+}
+
+if( typeof( Test )=="undefined" ) {
+ Test={};
+}
+Test.AnotherWay={};
+
+Test.AnotherWay._g_test_iframe=null; // frame where to load test pages
+Test.AnotherWay._g_test_frame_no_clear=false; // true - leave last page displayed after tests end
+Test.AnotherWay._g_test_page_urls=[]; // array of: { url: url, convention: "anotherway" or "jsan" }
+Test.AnotherWay._g_test_object_for_jsan=null; // test object for filling by tests that adhere to jsan Test.Simple calling convention
+Test.AnotherWay._g_pages_to_run=null; // list of pages to run automatically after loading
+Test.AnotherWay._g_run_on_main_load=false; // special handling for run_pages_to_run when it might be called before onload or before list of test pages is known.
+Test.AnotherWay._g_run_on_list_load=false;
+Test.AnotherWay._g_main_loaded=false;
+
+Test.AnotherWay._run_pages_to_run=function( called_from_outside )
+{
+ if( !Test.AnotherWay._g_main_loaded ) {
+ Test.AnotherWay._g_run_on_main_load=true;
+ }else {
+ var a_pages=Test.AnotherWay._g_pages_to_run;
+ if( a_pages=="all" ) {
+ for( var i=0; i<Test.AnotherWay._g_test_page_urls.length; ++i ) {
+ Test.AnotherWay._run_test_page( "test"+i );
+ }
+ }else if( a_pages!=null ) {
+ for( var run_i=0; run_i<a_pages.length; ++run_i ) {
+ var run_page=a_pages[run_i];
+ var found=false;
+ for( var all_i=0; all_i<Test.AnotherWay._g_test_page_urls.length; ++all_i ) {
+ if( run_page==Test.AnotherWay._g_test_page_urls[all_i].url ) {
+ Test.AnotherWay._run_test_page( "test"+all_i, called_from_outside );
+ found=true;
+ break;
+ }
+ }
+ if( !found ) {
+ Test.AnotherWay._show_error( "page specified to run is not found in the page list: "+run_page );
+ break;
+ }
+ }
+ }
+ }
+}
+
+Test.AnotherWay._add_test_page_url=function( test_url, convention )
+{
+ var table=document.getElementById( "testtable" );
+ var record_select=document.getElementById( "record_select" );
+ var index=Test.AnotherWay._g_test_page_urls.length;
+
+ // trim spaces.
+ if( test_url.match( "^(\\s*)(.*\\S)(\\s*)$" ) ) {
+ test_url=RegExp.$2;
+ }
+
+ Test.AnotherWay._g_test_page_urls[index]={ url: test_url, convention: convention };
+ var row=table.insertRow( -1 );
+
+ var cell;
+ var cell_child;
+ cell=row.insertCell( -1 );
+ cell_child=document.createElement( "input" );
+ cell_child.type="checkbox";
+ cell_child.id="checkbox"+index;
+ cell_child.checked='checked';
+ cell_child.defaultChecked='checked';
+ cell.appendChild( cell_child );
+
+ cell=row.insertCell( -1 );
+ cell.setAttribute( "width", "75%" );
+ cell.appendChild( document.createTextNode( test_url ) );
+
+ cell=row.insertCell( -1 );
+ cell_child=document.createElement( "input" );
+ cell_child.type="button";
+ cell_child.id="test"+index;
+ cell_child.value=" run ";
+ cell_child.onclick=Test.AnotherWay._run_one_onclick;
+ cell.appendChild( cell_child );
+
+ cell=row.insertCell( -1 );
+ cell.setAttribute( "width", "8em" );
+ cell_child=document.createElement( "span" );
+ cell.appendChild( cell_child );
+
+ var option=document.createElement( "option" );
+ option.appendChild( document.createTextNode( test_url ) );
+ record_select.appendChild( option );
+}
+Test.AnotherWay._show_error=function( msg )
+{
+ var error_div=document.getElementById( "error" );
+ error_div.innerHTML="";
+ error_div.appendChild( document.createTextNode( msg ) );
+ error_div.style.display="block";
+}
+
+// read urls from the list in the html file inside the list_iframe
+// fill on-screen list with urls and "run" buttons, and fill the g_test_page_urls object.
+Test.AnotherWay._list_iframe_onload=function()
+{
+ if( window.frames.list_iframe!=null && window.frames.list_iframe.location!="" && window.frames.list_iframe.location!="about:blank" ) {
+ var list_doc=window.frames.list_iframe.document;
+ var list=list_doc.getElementById( "testlist" );
+ if( list!=null ) {
+ for( var i=0; i<list.childNodes.length; ++i ) {
+ var item=list.childNodes[i];
+ if( item.nodeName=="LI" || item.nodeName=="li" ) {
+ var convention="anotherway";
+ if( Test.AnotherWay._get_css_class( item )=="jsan" ) {
+ convention="jsan";
+ }
+ Test.AnotherWay._add_test_page_url( item.innerHTML, convention );
+ }
+ }
+ if( Test.AnotherWay._g_run_on_list_load ) {
+ Test.AnotherWay._g_run_on_list_load=false;
+ Test.AnotherWay._run_pages_to_run();
+ }
+ }else {
+ Test.AnotherWay._show_error( "no list with id 'testlist' in a list file "+window.frames.list_iframe.location );
+ }
+ }
+}
+
+Test.AnotherWay._map_checkboxes=function( f )
+{
+ var table=document.getElementById( "testtable" );
+ var checks=table.getElementsByTagName( "INPUT" );
+ for( var i=0; i<checks.length; ++i ) {
+ if( checks[i].type=="checkbox" && checks[i].id.match( /^checkbox(\d+)$/ ) ) {
+ f( checks[i], RegExp.$1 );
+ }
+ }
+}
+Test.AnotherWay._run_all_onclick=function()
+{
+ Test.AnotherWay._map_checkboxes( function( c, id ) { Test.AnotherWay._run_test_page( "test"+id ); } );
+}
+Test.AnotherWay._run_selected_onclick=function()
+{
+ Test.AnotherWay._map_checkboxes( function( c, id ) { if( c.checked ) Test.AnotherWay._run_test_page( "test"+id ); } );
+}
+Test.AnotherWay._unselect_all_onclick=function()
+{
+ Test.AnotherWay._map_checkboxes( function( c, id ) { c.checked=false; } );
+}
+Test.AnotherWay._run_one_onclick=function()
+{
+ Test.AnotherWay._run_test_page( this.id );
+}
+
+// construct an object that will gather results of running one test function
+Test.AnotherWay._test_object_t=function( fun_name )
+{
+ this.name=fun_name; // name of the test function
+ this.n_plan=null; // planned number of assertions
+ this.n_ok=0; // # of ok assertions
+ this.n_fail=0; // # of failed assertions
+ this.exception=""; // if the function throwed an exception, it's its message
+ this.exception_stack=[]; // strings: function call stack from the exception
+ this.assertions=[]; // assertion results: array of { ok: 1 or 0, name: string }
+ this.wait_result_milliseconds=0; // how long to wait before collecting results from the test
+ this.second_wait_msg=null; // <p> status message (in addition to the page wait_msg)
+ this.delay_actions=[]; // array of actions to be perfomed after the test function returns
+ // action : { acton_kind: "call" | "window" | "replay"
+ // when "call": { call_fn call_delay_milliseconds } call_fn takes nothing
+ // when "window" : { wnd_url wnd_wnd wnd_fn wnd_timeout_milliseconds wnd_dont_close } wnd_fn takes wnd
+ // wnen "replay" : { replay_wnd replay_events replay_event_i replay_checkpoints } checkpoint_fn takes this, wnd
+ // }
+ this.delay_action_i=null; // index of delay action currently being performed
+ this.delay_prev_timer_time=0; // for counting time while performing delay_actions
+ this.delay_current_milliseconds_left=0; // time left before the next action, runs down
+ this.delay_total_milliseconds_left=0; // for indication: total estimated time for all actions, runs up and down
+}
+
+Test.AnotherWay._test_object_t.prototype.ok=function( cond, name )
+{
+ if( cond ) {
+ ++this.n_ok;
+ cond=1;
+ }else {
+ ++this.n_fail;
+ cond=0;
+ }
+ this.assertions.push( { ok: cond, name: name } );
+}
+Test.AnotherWay._test_object_t.prototype.fail=function( name )
+{
+ this.ok( false, name );
+}
+Test.AnotherWay._test_object_t.prototype.plan=function( n )
+{
+ this.n_plan=n;
+}
+Test.AnotherWay._test_object_t.prototype.wait_result=function( seconds )
+{
+ this.wait_result_milliseconds=1000*seconds;
+}
+Test.AnotherWay._eq_fail_msg=function( path, what, expected, got )
+{
+ return "eq: "+path+" "+what+" differ: got "+got+", but expected "+expected;
+}
+Test.AnotherWay._array_eq=function( expected, got, path, msg )
+{
+ if( expected.length!=got.length ) {
+ msg.msg=Test.AnotherWay._eq_fail_msg( path, "array length", expected.length, got.length );
+ return false;
+ }
+ for( var i=0; i<expected.length; ++i ) {
+ if( !Test.AnotherWay._thing_eq( expected[i], got[i], path+"["+i+"]", msg ) ) {
+ return false;
+ }
+ }
+ return true;
+}
+Test.AnotherWay._object_eq=function( expected, got, path, msg )
+{
+ var v;
+ for( v in expected ) {
+ if( ! (v in got) ) {
+ msg.msg=Test.AnotherWay._eq_fail_msg( path+"."+v, "properties", expected[v], "undefined" );
+ return false;
+ }
+ if( !Test.AnotherWay._thing_eq( expected[v], got[v], path+"."+v, msg ) ) {
+ return false;
+ }
+ }
+ for( v in got ) {
+ if( ! (v in expected) ) {
+ msg.msg=Test.AnotherWay._eq_fail_msg( path+"."+v, "properties", "undefined", got[v] );
+ return false;
+ }
+ }
+ return true;
+}
+Test.AnotherWay._constructor_name=function( x )
+{
+ if( x==null ) {
+ return "";
+ }
+ var s="unknown";
+ try {
+ s=typeof( x.constructor );
+ if( s!="unknown" ) {
+ s=x.constructor.toString();
+ }
+ }catch( e ) {
+ s="unknown";
+ }
+ if( s=="unknown" ) {
+ // hackish attempt to guess a type
+ var is_array=true;
+ var index=0;
+ for( i in x ) {
+ if( i!=index ) {
+ is_array=false;
+ }
+ ++index;
+ }
+ return is_array ? "Array" : "Object"; // for empty arrays/objects, this will be wrong half the time
+ }else if( s.match( /^\s*function\s+(\w+)\s*\(/ ) ) {
+ return RegExp.$1;
+ }else {
+ var c = '';
+ switch(typeof x) {
+ case 'string':
+ c = 'String';
+ break;
+ case 'object':
+ c = 'Object';
+ break;
+ default:
+ c = '';
+ }
+ return c; }
+}
+Test.AnotherWay._is_array=function( x )
+{
+ return Test.AnotherWay._constructor_name( x )=="Array";
+}
+Test.AnotherWay._is_value_type=function( x )
+{
+ cn=Test.AnotherWay._constructor_name( x );
+ return cn=="Number" || cn=="String" || cn=="Boolean" || cn=="Date";
+}
+Test.AnotherWay._thing_eq=function( expected, got, path, msg )
+{
+ if( expected==null && got==null ) {
+ return true;
+ }else if( (expected==null && got!=null) || (expected!=null && got==null) ) {
+ msg.msg=Test.AnotherWay._eq_fail_msg( path, "values", expected, got );
+ return false;
+ }else {
+ var expected_cn=Test.AnotherWay._constructor_name( expected );
+ var got_cn=Test.AnotherWay._constructor_name( got );
+ if( expected_cn!=got_cn ) {
+ msg.msg=Test.AnotherWay._eq_fail_msg( path, "types", expected_cn, got_cn );
+ return false;
+ }else {
+ if( Test.AnotherWay._is_array( expected ) ) {
+ return Test.AnotherWay._array_eq( expected, got, path, msg );
+ }else if( Test.AnotherWay._is_value_type( expected ) ) {
+ if( expected!=got ) {
+ msg.msg=Test.AnotherWay._eq_fail_msg( path, "values", expected, got );
+ return false;
+ }else {
+ return true;
+ }
+ }else { // just a plain object
+ return Test.AnotherWay._object_eq( expected, got, path, msg );
+ }
+ }
+ }
+}
+Test.AnotherWay._test_object_t.prototype.eq=function( got, expected, name )
+{
+ var msg={};
+ if( Test.AnotherWay._thing_eq( expected, got, "", msg ) ) {
+ this.ok( 1, name );
+ }else {
+ this.fail( name+". "+msg.msg );
+ }
+}
+Test.AnotherWay._test_object_t.prototype.like=function( got, expected, name )
+{
+ if( got.match( expected )!=null ) {
+ this.ok( 1, name );
+ }else {
+ this.fail( name+": got "+got+", but expected it to match: "+expected );
+ }
+}
+Test.AnotherWay._g_html_eq_span=null;
+Test.AnotherWay._html_eq_string_to_node=function( string_or_node, what, msg )
+{
+ if( string_or_node.nodeType!=null ) {
+ string_or_node=Test.AnotherWay._html_eq_node_to_string( string_or_node ); // double trip - to make properties assigned in scripts available as html node attributes
+ }
+ if( Test.AnotherWay._g_html_eq_span==null ) {
+ Test.AnotherWay._g_html_eq_span=document.createElement( "span" );
+ }
+ Test.AnotherWay._g_html_eq_span.innerHTML=string_or_node;
+ if( Test.AnotherWay._g_html_eq_span.childNodes.length!=1 ) {
+ msg.msg="bad "+what+" html string given (should contain exactly one outermost element): "+string_or_node;
+ }
+ return Test.AnotherWay._g_html_eq_span.childNodes[0].cloneNode( true );
+}
+Test.AnotherWay._html_eq_node_to_string=function( node ) {
+ if( Test.AnotherWay._g_html_eq_span==null ) {
+ Test.AnotherWay._g_html_eq_span=document.createElement( "span" );
+ }
+ Test.AnotherWay._g_html_eq_span.innerHTML="";
+ if( node.outerHTML!=null ) {
+ Test.AnotherWay._g_html_eq_span.innerHTML=node.outerHTML;
+ }else {
+ var clone = node.cloneNode(true);
+ var node = Test.AnotherWay._g_html_eq_span;
+ if(node.ownerDocument && node.ownerDocument.importNode) {
+ if(node.ownerDocument != clone.ownerDocument) {
+ clone = node.ownerDocument.importNode(clone, true);
+ }
+ }
+ node.appendChild(clone);
+ }
+ return Test.AnotherWay._g_html_eq_span.innerHTML;
+}
+Test.AnotherWay._html_eq_path_msg=function( path )
+{
+ var msg="";
+ for( var i=0; i<path.length; ++i ) {
+ msg+=" [node "+path[i].node;
+ if( path[i].id!=null && path[i].id!="" ) {
+ msg+=" id "+path[i].id;
+ }else if( path[i].index!=null ) {
+ msg+=" at index "+path[i].index;
+ }
+ msg+="] "
+ }
+ return msg;
+}
+Test.AnotherWay._html_eq_fail_msg=function( path, what, expected, got )
+{
+ return Test.AnotherWay._html_eq_path_msg( path )+": "+what+" differ: got "+got+", but expected "+expected;
+}
+Test.AnotherWay._html_eq_remove_blank=function( text )
+{
+ if( text==null ) {
+ return "";
+ }else if( text.match( "^(\\s*)(.*\\S)(\\s*)$" ) ) {
+ return RegExp.$2;
+ }else if( text.match( "\s*" ) ) {
+ return "";
+ }
+ return text;
+}
+Test.AnotherWay._html_eq_remove_blank_nodes=function( node )
+{
+ var to_remove=[];
+ for( var child=node.firstChild; child!=null; child=child.nextSibling ) {
+ if( child.nodeType==3 ) {
+ var value=Test.AnotherWay._html_eq_remove_blank( child.nodeValue );
+ if( value=="" ) {
+ to_remove.push( child );
+ }else {
+ child.nodeValue=value;
+ }
+ }
+ }
+ for( var i=0; i<to_remove.length; ++i ) {
+ node.removeChild( to_remove[i] );
+ }
+}
+Test.AnotherWay._html_node_type_text=function( node_type )
+{
+ if( node_type==1 ) {
+ return "1 (html element)";
+ }else if( node_type==3 ) {
+ return "3 (text)";
+ }else {
+ return node_type;
+ }
+}
+Test.AnotherWay._html_eq_node=function( expected, got, path, msg, expected_loc_base, got_loc_base )
+{
+ if( expected.nodeType!=got.nodeType ) {
+ msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "node types", Test.AnotherWay._html_node_type_text( expected.nodeType ), Test.AnotherWay._html_node_type_text( got.nodeType ) );
+ return false;
+ }else if( expected.nodeType==3 ) {
+ if( expected.nodeValue!=got.nodeValue ) {
+ msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "text", expected.nodeValue, got.nodeValue );
+ return false;
+ }
+ }else if( expected.nodeType==1 ) {
+ if( expected.nodeName!=got.nodeName ) {
+ msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "node names", expected.nodeName, got.nodeName );
+ return false;
+ }
+ // compare attributes
+ var expected_attrs={};
+ var got_attrs={};
+ var i;
+ var a;
+ for( i=0; i<expected.attributes.length; ++i ) {
+ a=expected.attributes[i];
+ if( a.specified ) {
+ expected_attrs[a.name]=1;
+ }
+ }
+ for( i=0; i<got.attributes.length; ++i ) {
+ a=got.attributes[i];
+ if( a.specified ) {
+ got_attrs[a.name]=1;
+ }
+ }
+ for( a in expected_attrs ) {
+ if( ! (a in got_attrs) ) {
+ msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": attribute sets differ: expected attribute "+a+" is missing";
+ return false;
+ }
+ }
+ for( a in got_attrs ) {
+ if( ! (a in expected_attrs) ) {
+ msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": attribute sets differ: got extra attribute "+a;
+ return false;
+ }
+ }
+ for( a in expected_attrs ) {
+ var expected_value=expected.getAttribute( a );
+ var got_value=got.getAttribute( a );
+ if( typeof( expected_value )=="string" && typeof( got_value )=="string" ) {
+ expected_value=Test.AnotherWay._html_eq_remove_blank( expected_value );
+ got_value=Test.AnotherWay._html_eq_remove_blank( got_value );
+ var ok=expected_value==got_value;
+ if( !ok && (a=="href" || a=="HREF" ) ) { // try relative hrefs
+ var expected_relative_value=expected_value;
+ if( expected_loc_base!=null && expected_value.substring( 0, expected_loc_base.length )==expected_loc_base ) {
+ expected_relative_value=expected_value.substring( expected_loc_base.length );
+ }
+ var got_relative_value=got_value;
+ if( got_loc_base!=null && got_value.substring( 0, got_loc_base.length )==got_loc_base ) {
+ got_relative_value=got_value.substring( got_loc_base.length );
+ }
+ ok=expected_relative_value==got_relative_value;
+ }
+ if( !ok ) {
+ msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "attribute "+a+" values", expected_value, got_value );
+ return false;
+ }
+ }else if( typeof( expected_value )=="function" && typeof( got_value )=="function" ) {
+ expected_value=expected_value.toString();
+ got_value=got_value.toString();
+ if( expected_value!=got_value ) {
+ msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "attribute "+a+" values", expected_value, got_value );
+ return false;
+ }
+ }else {
+ var value_msg={};
+ if( !Test.AnotherWay._thing_eq( expected_value, got_value, "", value_msg ) ) {
+ msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": attribute "+a+" values differ: "+value_msg.msg;
+ return false;
+ }
+ }
+ }
+ // compare child nodes
+ Test.AnotherWay._html_eq_remove_blank_nodes( expected );
+ Test.AnotherWay._html_eq_remove_blank_nodes( got );
+ var expected_length=expected.childNodes.length;
+ var got_length=got.childNodes.length;
+ if( expected_length<got_length ) {
+ msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": got "+(got_length-expected_length)+" extra child nodes";
+ return false;
+ }else if( expected_length>got_length ) {
+ msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": expected "+(expected_length-got_length)+" more child nodes";
+ return false;
+ }else {
+ for( i=0; i<expected_length; ++i ) {
+ var expected_node=expected.childNodes[i];
+ path.push( { node: expected_node.nodeName, id: expected_node.id, index: i } );
+ var eq=Test.AnotherWay._html_eq_node( expected_node, got.childNodes[i], path, msg, expected_loc_base, got_loc_base );
+ path.pop();
+ if( !eq ) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+Test.AnotherWay._html_eq_get_loc_base=function( node )
+{
+ var loc_base=document.location;
+ if( node.ownerDocument!=null ) {
+ loc_base=node.ownerDocument.location;
+ }
+ if( loc_base!=null ) {
+ loc_base=loc_base.href;
+ var slash_pos=loc_base.lastIndexOf( "/" );
+ if( slash_pos!=-1 ) {
+ loc_base=loc_base.substring( 0, slash_pos+1 );
+ }
+ }
+ return loc_base;
+}
+Test.AnotherWay._test_object_t.prototype.html_eq=function( got, expected, name )
+{
+ var msg={};
+ var expected_node=Test.AnotherWay._html_eq_string_to_node( expected, "expected", msg );
+ if( msg.msg!=null ) {
+ this.fail( name+" html_eq: "+msg.msg );
+ }else {
+ var got_node=Test.AnotherWay._html_eq_string_to_node( got, "got", msg );
+ if( msg.msg!=null ) {
+ this.fail( name+" html_eq: "+msg.msg );
+ }else {
+ var expected_loc_base=Test.AnotherWay._html_eq_get_loc_base( expected );
+ var got_loc_base=Test.AnotherWay._html_eq_get_loc_base( got );
+ if( Test.AnotherWay._html_eq_node( expected_node, got_node, [], msg, expected_loc_base, got_loc_base ) ) {
+ this.ok( 1, name );
+ }else {
+ var msg=name+" html_eq "+msg.msg;
+ var expected_str=Test.AnotherWay._html_eq_node_to_string( expected_node );
+ var got_str=Test.AnotherWay._html_eq_node_to_string( got_node );
+ msg+=".\n got html: "+got_str;
+ msg+=".\n expected html: "+expected_str;
+ this.fail( msg );
+ }
+ }
+ }
+}
+Test.AnotherWay._debug_pane_print=function( msg )
+{
+ var d=new Date();
+ var p=document.createElement( "p" );
+ p.appendChild( document.createTextNode( d.toLocaleTimeString()+" "+msg ) );
+ var debug_pane=document.getElementById( "debug" );
+ debug_pane.appendChild( p );
+ var debug_tab=document.getElementById( "debug_tab" );
+ var results_tab=document.getElementById( "results_tab" );
+ debug_tab.style.visibility="visible";
+ results_tab.style.visibility="visible";
+}
+Test.AnotherWay._test_object_t.prototype.debug_print=function( msg )
+{
+ Test.AnotherWay._debug_pane_print( this.name+": "+msg );
+}
+Test.AnotherWay._test_object_t.prototype.delay_call=function()
+{
+ var timeout_ms=200;
+ for( var i=0; i<arguments.length; ++i ) {
+ if( typeof( arguments[i] )!="function" ) {
+ timeout_ms=3000*arguments[i];
+ }else {
+ var action={ action_kind: "call", call_delay_milliseconds: timeout_ms, call_fn: arguments[i] };
+ this.delay_total_milliseconds_left+=Test.AnotherWay._action_estimate_milliseconds( action );
+ this.delay_actions.push( action );
+ }
+ }
+}
+Test.AnotherWay._test_object_t.prototype.open_window=function( url, fn, timeout_seconds )
+{
+ if( timeout_seconds==null ) {
+ timeout_seconds=4;
+ }
+ var no_close=document.getElementById( "dont_close_test_windows" );
+ var action={ action_kind: "window", wnd_url: url.toString(), wnd_wnd: null, wnd_fn: fn, wnd_timeout_milliseconds: timeout_seconds*1000, wnd_no_close: no_close.checked };
+ this.delay_total_milliseconds_left+=Test.AnotherWay._action_estimate_milliseconds( action );
+ this.delay_actions.push( action );
+}
+Test.AnotherWay._test_object_t.prototype.replay_events=function( wnd, events )
+{
+ if( Test.AnotherWay._g_no_record_msg!=null ) {
+ this.fail( "replay_events: "+Test.AnotherWay._g_no_record_msg );
+ }else {
+ var action={ action_kind: "replay", replay_wnd: wnd, replay_events: events.events, replay_event_i: null, replay_checkpoints: events.checkpoints };
+ this.delay_total_milliseconds_left+=Test.AnotherWay._action_estimate_milliseconds( action );
+ this.delay_actions.push( action );
+ }
+}
+Test.AnotherWay._action_estimate_milliseconds=function( action )
+{
+ var ms=0;
+ if( action.action_kind=="call" ) {
+ ms=action.call_delay_milliseconds;
+ }else if( action.action_kind=="window" ) {
+ ms=0;
+ }else if( action.action_kind=="replay" ) {
+ ms=0;
+ for( var i=0; i<action.replay_events.length; ++i ) {
+ ms+=action.replay_events[i]["time"]-0;
+ }
+ }
+ return ms;
+}
+
+Test.AnotherWay._g_timeout_granularity=200;
+Test.AnotherWay._g_tests_queue=[]; // vector of { url: string, test_objects : array of test_object_t, test_object_i: int, wait_msg: <p> object, loading_timeout_milliseconds: int, timeout_id: id }
+
+// load one html page, schedule further processing
+Test.AnotherWay._run_test_page=function( id, called_from_outside )
+{
+ if( id.match( /^test(\d+)/ ) ) {
+ id=RegExp.$1;
+ Test.AnotherWay._g_tests_queue.push( {
+ url: Test.AnotherWay._g_test_page_urls[id].url,
+ convention: Test.AnotherWay._g_test_page_urls[id].convention,
+ test_objects: []
+ } );
+ if( Test.AnotherWay._g_tests_queue.length==1 ) {
+ if( !called_from_outside ) {
+ // Crap. Be careful stepping around.
+ // For Mozilla and Opera, when this file is included into the frameset page that is in another directory (and _g_outside_path_correction!=null)
+ // but the test pages are started from within it (by "run" buttons), then:
+ // depending on whether the page is the first one loaded into the test frame or not,
+ // the base url for relative test pages differs.
+ // Crap, like I said.
+ Test.AnotherWay._g_tests_queue[0].suppress_outside_path_correction=true;
+ }
+ Test.AnotherWay._start_loading_page();
+ }
+ }
+}
+Test.AnotherWay._load_next_page=function()
+{
+ Test.AnotherWay._g_tests_queue.splice( 0, 1 );
+ if( Test.AnotherWay._g_tests_queue.length>0 ) {
+ Test.AnotherWay._start_loading_page();
+ }else {
+ if( !Test.AnotherWay._g_test_frame_no_clear ) {
+ Test.AnotherWay._g_test_iframe.location.replace( "about:blank" );
+ }
+ report_results();
+ }
+}
+Test.AnotherWay._g_opera_path_correction=null; // ugly wart to support opera
+Test.AnotherWay._g_outside_path_correction=null; // ugly wart to accomodate Opera and Mozilla, where relative url relates to the directory where the page that calls this function is located
+Test.AnotherWay._set_iframe_location=function( iframe, loc, outside_path_correction )
+{
+ // allow to load only locations with the same origin
+ var proto_end=loc.indexOf( "://" );
+ if( proto_end!=-1 ) { // otherwise, it's safe to assume (for Opera, Mozilla and IE ) that loc will be treated as relative
+ var main_loc=window.location.href;
+ var host_end=loc.substring( proto_end+3 ).indexOf( "/" );
+ var ok=false;
+ if( host_end!=-1 ) {
+ var loc_origin=loc.substring( 0, proto_end+3+host_end+1 );
+ if( main_loc.length>=loc_origin.length && main_loc.substring( 0, loc_origin.length )==loc_origin ) {
+ ok=true;
+ }
+ }
+ if( !ok ) {
+ return { msg: "test pages may have only urls with the same origin as "+main_loc };
+ }
+ }
+ // opera cannot handle urls relative to file:// without assistance
+ if( window.opera!=null && window.location.protocol=="file:" && loc.indexOf( ":" )==-1 ) {
+ var base=window.location.href;
+ var q_pos=base.indexOf( "?" );
+ if( q_pos!=-1 ) {
+ base=base.substring( 0, q_pos );
+ }
+ var slash_pos=base.lastIndexOf( "/" );
+ if( slash_pos!=-1 ) {
+ base=base.substring( 0, slash_pos+1 );
+ Test.AnotherWay._g_opera_path_correction=base;
+ loc=base+loc;
+ }
+ }
+ // if this function is called from another page, and if that page is in another directory, correction is needed
+ if( outside_path_correction!=null ) {
+ var pos=loc.indexOf( outside_path_correction );
+ if( pos==0 ) {
+ loc=loc.substring( outside_path_correction.length+1 );
+ }
+ }
+ if( iframe.location!=null ) {
+ iframe.location.replace( loc );
+ }else {
+ iframe.src=loc;
+ }
+ return {};
+}
+Test.AnotherWay._start_loading_page=function()
+{
+ var test_page=Test.AnotherWay._g_tests_queue[0];
+ test_page.loading_timeout_milliseconds=20000;
+ test_page.timeout_id=setTimeout( Test.AnotherWay._loading_timeout, Test.AnotherWay._g_timeout_granularity );
+ test_page.wait_msg=Test.AnotherWay._print_counter_result( test_page.url, "loading...", test_page.loading_timeout_milliseconds, "loading" );
+ if( test_page.convention=="jsan" ) {
+ // the tests in that page will run when it's loading, so the test object must be ready
+ Test.AnotherWay._g_test_object_for_jsan=new Test.AnotherWay._test_object_t( test_page.url );
+ }
+ var outside_path_correction=null;
+ if( Test.AnotherWay._g_outside_path_correction!=null && !test_page.suppress_outside_path_correction ) {
+ outside_path_correction=Test.AnotherWay._g_outside_path_correction;
+ }
+ var result=Test.AnotherWay._set_iframe_location( Test.AnotherWay._g_test_iframe, test_page.url, outside_path_correction );
+ if( result.msg!=null ) {
+ Test.AnotherWay._unprint_result( test_page.wait_msg );
+ Test.AnotherWay._print_result( test_page.url, result.msg, "badtest", null );
+ Test.AnotherWay._load_next_page();
+ }
+}
+
+Test.AnotherWay._loading_timeout=function()
+{
+ var test_page=Test.AnotherWay._g_tests_queue[0];
+ test_page.loading_timeout_milliseconds-=Test.AnotherWay._g_timeout_granularity;
+ if( test_page.loading_timeout_milliseconds>0 ) {
+ Test.AnotherWay._update_msg_counter( test_page.wait_msg, (test_page.loading_timeout_milliseconds/1000).toFixed() );
+ test_page.timeout_id=setTimeout( Test.AnotherWay._loading_timeout, Test.AnotherWay._g_timeout_granularity );
+ }else {
+ Test.AnotherWay._unprint_result( test_page.wait_msg );
+ Test.AnotherWay._print_result( test_page.url, "Unable to load test page. Timeout expired", "badtest", null );
+ Test.AnotherWay._load_next_page();
+ }
+}
+
+Test.AnotherWay._strip_query_and_hash=function( s )
+{
+ var i=s.lastIndexOf( "#" );
+ if( i!=-1 ) {
+ s=s.substring( 0, i );
+ }
+ i=s.lastIndexOf( "?" );
+ if( i!=-1 ) {
+ s=s.substring( 0, i );
+ }
+ return s;
+}
+Test.AnotherWay._is_url_loaded=function( url, wnd )
+{
+ var loaded=false;
+ if( wnd!=null && wnd.location!=null ) {
+ // after some popup blocker interference, location may behave strange..
+ var location_s="";
+ location_s+=wnd.location;
+ if( location_s!="" ) {
+ var pathname=wnd.location.pathname;
+ var expected_url=url;
+ var i=expected_url.lastIndexOf( "#" );
+ if( i!=-1 ) {
+ expected_url=expected_url.substring( 0, i );
+ }
+ i=expected_url.lastIndexOf( "?" );
+ if( i!=-1 ) {
+ expected_url=expected_url.substring( 0, i );
+ }
+ i=expected_url.lastIndexOf( "/" );
+ if( i!=-1 && i!=expected_url.length-1 ) {
+ expected_url=expected_url.substring( i+1 );
+ }
+ i=pathname.indexOf( expected_url )
+ if( wnd.location.href==url || (i!=-1 && i==pathname.length-expected_url.length) ) {
+ if( /*window.opera==null*/wnd.document.readyState==null || wnd.document.readyState=="complete" ) { // for opera (and IE?), getElementById does not work until..
+ loaded=true;
+ }
+ }
+ }
+ }
+ return loaded;
+}
+// find and run all test functions in the g_cur_page html page.
+Test.AnotherWay._test_page_onload=function()
+{
+ if( Test.AnotherWay._g_tests_queue.length==0 ) {
+ return;
+ }
+ var test_page=Test.AnotherWay._g_tests_queue[0];
+ if( !Test.AnotherWay._is_url_loaded( test_page.url, Test.AnotherWay._g_test_iframe ) ) {
+ return;
+ }
+ clearTimeout( test_page.timeout_id );
+ Test.AnotherWay._unprint_result( test_page.wait_msg );
+
+ if( test_page.convention=="anotherway" ) {
+ // get test function names (those beginning with "test")
+ if( typeof( Test.AnotherWay._g_test_iframe.document.scripts )!='undefined' ) { // IE
+ for( var i=0; i<Test.AnotherWay._g_test_iframe.document.scripts.length; ++i ) {
+ var script_text=Test.AnotherWay._g_test_iframe.document.scripts[i].text;
+ var fun_sig="function test";
+ var fun_start=script_text.indexOf( fun_sig );
+
+ while( fun_start!=-1 ) {
+ script_text=script_text.substring( fun_start, script_text.length );
+ var fun_end=script_text.indexOf( '(' );
+ var fun_name=script_text.substring( "function ".length, fun_end );
+ var whitespace = fun_name.indexOf( ' ' );
+ if (whitespace >= 0)
+ fun_name = fun_name.substring( 0, whitespace );
+ test_page.test_objects.push( new Test.AnotherWay._test_object_t( fun_name ) );
+ script_text=script_text.substring( fun_end, script_text.length );
+ fun_start=script_text.indexOf( fun_sig );
+ }
+ }
+ }else { // otherwise (not IE) it ought to work like this
+ for( var i in Test.AnotherWay._g_test_iframe) {
+ // Hack to prevent failure in FF3.0b1
+ if (i == "innerWidth" || i == "innerHeight") { continue; }
+ if( typeof( Test.AnotherWay._g_test_iframe[i] )=='function' ) {
+ if( i.substring( 0, 4 )=="test" ) {
+ test_page.test_objects.push( new Test.AnotherWay._test_object_t( i ) );
+ }
+ }
+ }
+ }
+ }else if( test_page.convention=="jsan" ) {
+ // the test object is already filled with results
+ test_page.test_objects.push( Test.AnotherWay._g_test_object_for_jsan );
+ }
+
+ if( test_page.test_objects.length==0 ) {
+ Test.AnotherWay._print_result( test_page.url, "No test functions defined in the page", "badtest", null );
+ Test.AnotherWay._load_next_page();
+ return;
+ }
+
+ test_page.wait_msg=Test.AnotherWay._print_result( test_page.url, "running tests..<span class=\"counter\">"+test_page.test_objects.length+"</span>", "running", null );
+
+ test_page.test_object_i=0;
+ Test.AnotherWay._run_more_tests();
+}
+
+Test.AnotherWay._handle_exception=function( o, e, title )
+{
+ var s=title+": "+typeof( e )+": ";
+ if( e.message!=null ) {
+ s+=e.message;
+ }else if( e.description!=null ) {
+ s+=e.description;
+ }else {
+ s+=e.toString();
+ }
+// if( e.location!=null ) { // XXX figure out how to display exception location if it's present (like in mozilla)
+// s+=" location: "+e.location.toString();
+// }
+ o.exception=s;
+ s=[];
+ if( e.stack ) {
+ var lines=e.stack.split( "\n" );
+ for( var i=0; i<lines.length; ++i ) {
+ // format of the line: func_name(args)@file_name:line_no
+ if( lines[i].match( /(\w*)\(([^\)]*)\)@(.*):([^:]*)$/ ) ) {
+ var func_name=RegExp.$1;
+ if( func_name.length==0 ) {
+ func_name="<anonymous>";
+ }
+ s.push( "in "+func_name+"( "+RegExp.$2+") at "+RegExp.$3+" line "+RegExp.$4+"\n" );
+ }
+ }
+ }
+ o.exception_stack=s;
+}
+
+Test.AnotherWay._run_more_tests=function()
+{
+ var test_page=Test.AnotherWay._g_tests_queue[0];
+ while( test_page.test_object_i<test_page.test_objects.length ) {
+ Test.AnotherWay._update_msg_counter( test_page.wait_msg, (1+test_page.test_object_i)+"/"+test_page.test_objects.length );
+ var o=test_page.test_objects[test_page.test_object_i];
+ if( test_page.convention=="anotherway" ) {
+ try {
+ Test.AnotherWay._g_test_iframe[o.name]( o );
+ }catch( e ) {
+ Test.AnotherWay._handle_exception( o, e, "" );
+ }
+ } // for "jsan" convention, test has run already
+ if( o.delay_actions.length>0 || o.wait_result_milliseconds>0 ) {
+ o.delay_total_milliseconds_left+=o.wait_result_milliseconds;
+ Test.AnotherWay._delay_actions_timeout();
+ return;
+ }
+ ++test_page.test_object_i;
+ }
+ Test.AnotherWay._unprint_result( test_page.wait_msg );
+ Test.AnotherWay._print_result( test_page.url, null, null, test_page.test_objects );
+ Test.AnotherWay._load_next_page();
+}
+
+Test.AnotherWay._delay_actions_timeout=function()
+{
+ var test_page=Test.AnotherWay._g_tests_queue[0];
+ var test_object=test_page.test_objects[test_page.test_object_i];
+ var finished=true;
+ if( test_object.delay_action_i==null ) {
+ // set up to start first action
+ test_object.delay_action_i=-1;
+ }else {
+ // perform current action
+ var milliseconds_passed=(new Date()).getTime()-test_object.delay_prev_timer_time;
+ test_object.delay_current_milliseconds_left-=milliseconds_passed;
+ test_object.delay_total_milliseconds_left-=milliseconds_passed;
+ finished=Test.AnotherWay._delay_continue_action( test_object, milliseconds_passed );
+ }
+ while( finished && test_object.delay_action_i<test_object.delay_actions.length ) {
+ ++test_object.delay_action_i; // start next action
+ finished=Test.AnotherWay._delay_start_action( test_object );
+ }
+ if( test_object.delay_action_i<=test_object.delay_actions.length ) { // any more actions left ?
+ test_object.delay_prev_timer_time=(new Date()).getTime();
+ var next_timeout=Test.AnotherWay._g_timeout_granularity;
+ if( test_object.delay_current_milliseconds_left<next_timeout ) {
+ next_timeout=test_object.delay_current_milliseconds_left;
+ }
+ if( test_object.second_wait_msg!=null ) {
+ Test.AnotherWay._update_msg_counter( test_object.second_wait_msg, (test_object.delay_total_milliseconds_left/1000).toFixed() );
+ }
+ setTimeout( Test.AnotherWay._delay_actions_timeout, next_timeout );
+ }else { // no more actions left. run the next test.
+ if( test_object.second_wait_msg!=null ) {
+ Test.AnotherWay._unprint_result( test_object.second_wait_msg );
+ test_object.second_wait_msg=null;
+ }
+ ++test_page.test_object_i;
+ Test.AnotherWay._run_more_tests();
+ }
+}
+Test.AnotherWay._delay_start_action=function( test_object )
+{
+ var finished=false;
+ var wait_msg="";
+ if( test_object.delay_action_i==test_object.delay_actions.length ) {
+ if( test_object.wait_result_milliseconds>0 ) {
+ test_object.delay_current_milliseconds_left=test_object.wait_result_milliseconds; // wait for result
+ wait_msg="waiting for results..";
+ }else {
+ ++test_object.delay_action_i; // dont wait for result
+ }
+ }else {
+ var action=test_object.delay_actions[test_object.delay_action_i];
+ if( action.action_kind=="call" ) {
+ test_object.delay_current_milliseconds_left=action.call_delay_milliseconds;
+ wait_msg="performing delayed calls..";
+ }else if( action.action_kind=="window" ) {
+ if( Test.AnotherWay._g_opera_path_correction!=null && action.wnd_url.indexOf( ":" )==-1 ) {
+ action.wnd_url=Test.AnotherWay._g_opera_path_correction+action.wnd_url;
+ }
+ action.wnd_wnd=window.open( action.wnd_url, "_blank" );
+ if( action.wnd_wnd==null ) {
+ finished=true;
+ test_object.fail( "unable to open window for "+action.wnd_url );
+ }else {
+ test_object.delay_current_milliseconds_left=action.wnd_timeout_milliseconds;
+ wait_msg="opening window..";
+ }
+ }else if( action.action_kind=="replay" ) {
+ if( action.replay_events.length==0 ) {
+ finished=true;
+ }else {
+ action.replay_event_i=0;
+ test_object.delay_current_milliseconds_left=action.replay_events[0]["time"];
+ wait_msg="replaying events..";
+ }
+ }
+ }
+ if( test_object.second_wait_msg!=null ) {
+ Test.AnotherWay._unprint_result( test_object.second_wait_msg );
+ }
+ if( wait_msg!="" ) {
+ var test_page=Test.AnotherWay._g_tests_queue[0];
+ test_object.second_wait_msg=Test.AnotherWay._print_counter_result( test_page.url, wait_msg, test_object.delay_total_milliseconds_left, "waiting" );
+ }else {
+ test_object.second_wait_msg=null;
+ }
+ return finished;
+}
+Test.AnotherWay._delay_continue_action=function( test_object, milliseconds_passed )
+{
+ var finished=test_object.delay_current_milliseconds_left<=0;
+ if( test_object.delay_action_i==test_object.delay_actions.length ) { // action is "waiting for results"
+ if( test_object.n_plan!=null && test_object.n_plan==test_object.n_ok+test_object.n_fail ) {
+ finished=true; // if all assertions results are recorded, don't wait any more
+ }
+ if( finished ) {
+ ++test_object.delay_action_i; // move on to the next test
+ }
+ }else {
+ var action=test_object.delay_actions[test_object.delay_action_i];
+ if( action.action_kind=="call" ) {
+ if( finished ) {
+ try {
+ action.call_fn();
+ }catch( e ) {
+ Test.AnotherWay._handle_exception( test_object, e, "in delay_call" );
+ }
+ }
+ }else if( action.action_kind=="window" ) {
+ test_object.delay_total_milliseconds_left+=milliseconds_passed; // for "window", the countdown is suspended since it's unknown how long it will take
+ if( Test.AnotherWay._is_url_loaded( action.wnd_url, action.wnd_wnd ) ) {
+ try {
+ action.wnd_fn( action.wnd_wnd );
+ }catch( e ) {
+ Test.AnotherWay._handle_exception( test_object, e, "in open_window function call" );
+ }
+ finished=true;
+ }else if( finished ) {
+ test_object.fail( "unable to open window for url '"+action.wnd_url+"'. timeout expired" );
+ }
+ }else if( action.action_kind=="replay" ) {
+ if( finished ) {
+// try {
+ Test.AnotherWay._delay_replay_event( test_object, action.replay_wnd, action.replay_events[action.replay_event_i], action.replay_checkpoints );
+// }catch( e ) { // disabled, until I know how to gel location info from an exception
+// Test.AnotherWay._handle_exception( test_object, e, "while replaying event" );
+// }
+ ++action.replay_event_i;
+ finished=action.replay_event_i==action.replay_events.length;
+ if( !finished ) {
+ test_object.delay_current_milliseconds_left=action.replay_events[action.replay_event_i]["time"];
+ }
+ }
+ }
+ }
+ return finished;
+}
+Test.AnotherWay._delay_replay_event=function( test_object, wnd, event, checkpoints )
+{
+ if( event.type=="_checkpoint" ) {
+ var checkpoint_n=event.which;
+ var prev_n_fail=test_object.n_fail;
+ checkpoints[checkpoint_n]( test_object, wnd );
+ var flash_color= prev_n_fail==test_object.n_fail ? "#2f2" : "#f22" ;
+ Test.AnotherWay._record_flash_border( flash_color );
+ }else if( event.type=="click" || event.type=="mouseover" || event.type=="mouseout" || event.type=="mousemove" || event.type=="mousedown" || event.type=="mouseup" ) {
+ var target=Test.AnotherWay._record_node_path_to_node( event["target"], wnd.document );
+ if( target!=null ) {
+ Test.AnotherWay._record_control_update_highlight( target, "ball", event );
+ var e=wnd.document.createEvent( "MouseEvents" );
+ var related_target=Test.AnotherWay._record_node_path_to_node( event["relatedTarget"], wnd.document );
+ e.initMouseEvent(
+ event["type"],
+ event["cancelable"],
+ event["bubbles"],
+ wnd.document.defaultView,
+ event["detail"],
+ event["screenX"],
+ event["screenY"],
+ event["clientX"],
+ event["clientY"],
+ event["ctrlKey"],
+ event["altKey"],
+ event["shiftKey"],
+ event["metaKey"],
+ event["button"],
+ Test.AnotherWay._record_node_path_to_node( event["relatedTarget"], wnd.document )
+ );
+ // Firefox 1.0.6 somehow loses relatedTarget somewhere on the way. Pass through our own, for those who choose to care.
+ e.passThroughRelatedTarget=related_target;
+ target.dispatchEvent( e );
+ }
+ }else if( event.type=="keyup" || event.type=="keydown" || event.type=="keypress" ) {
+ var e=wnd.document.createEvent( "KeyboardEvents" ); // forget it. Apparently it's not supported neither by mozilla nor by opera.
+ e.initKeyboardEvent(
+ event["type"],
+ event["cancelable"],
+ event["bubbles"],
+ wnd.document.defaultView,
+ event["which"],
+ event["which"],
+ event["ctrlKey"],
+ event["altKey"],
+ event["shiftKey"],
+ event["metaKey"],
+ false
+ );
+ wnd.document.dispatchEvent( e );
+ }
+}
+
+Test.AnotherWay._print_counter_result=function( url, msg, milliseconds, style )
+{
+ return Test.AnotherWay._print_result( url, msg+"<span class=\"counter\">"+(milliseconds/1000).toFixed()+"</span>", style, null );
+}
+
+Test.AnotherWay._g_result_count=0; // for assigning unique ids to result paragraphs
+
+// number of pages tested
+Test.AnotherWay._g_ok_pages=0;
+Test.AnotherWay._g_fail_pages=0;
+
+Test.AnotherWay._print_result=function( url, msg, style, test_objects )
+{
+ var results=document.getElementById( "results" );
+ var r=results.appendChild( document.createElement( "p" ) );
+ r.id="result"+Test.AnotherWay._g_result_count;
+ ++Test.AnotherWay._g_result_count;
+ r.onclick=Test.AnotherWay._toggle_detail;
+ var text="<span class=\"bullet\">&nbsp;&nbsp;&nbsp;</span>&nbsp;";
+ if( url!="" ) {
+ text+=url+": ";
+ }
+ if( msg!=null ) {
+ text+=msg;
+ }
+ if( test_objects!=null ) {
+ // compose summary and detail texts
+ var total_ok=0;
+ var total_detail_ok=0;
+ var total_fail=0;
+ var total_detail_fail=0;
+ var no_plan=0;
+
+ var detail=results.appendChild( document.createElement( "div" ) );
+
+ if( r.id.match( /^result(\d+)$/ ) ) {
+ detail.id="result_detail"+RegExp.$1;
+ }
+
+ for( var i=0; i<test_objects.length; ++i ) {
+ var o=test_objects[i];
+ var p;
+ var p_text;
+ p=document.createElement( "P" );
+ Test.AnotherWay._set_css_class( p, "result_detail" );
+ p_text=o.name;
+ if( o.n_fail>0 || o.exception || (o.n_plan!=null && o.n_plan!=o.n_ok+o.n_fail) || (o.n_plan==null && o.n_ok==0 && o.n_fail==0)) {
+ ++total_fail;
+ p_text+=" <span class=\"fail\">";
+ if( o.n_plan!=null && o.n_plan!=o.n_ok+o.n_fail) {
+ p_text+="planned "+o.n_plan+" assertions but got "+(o.n_ok+o.n_fail)+"; ";
+ }
+ if(o.n_plan==null && o.n_ok==0 && o.n_fail==0) {
+ p_text+="test did not output anything";
+ }else {
+ p_text+=" fail "+o.n_fail;
+ }
+ p_text+="</span>";
+ }else {
+ ++total_ok;
+ }
+ p_text+=" ok "+o.n_ok;
+ if( o.n_plan==null ) {
+ no_plan=1;
+ p_text+=" <span class=\"warning\">no plan</span>";
+ }
+ p.innerHTML=p_text;
+ detail.appendChild( p );
+ if( o.exception ) {
+ p=document.createElement( "P" );
+ Test.AnotherWay._set_css_class( p, "result_exception_detail" );
+ p.innerHTML="<span class=\"fail\">exception:</span> "+o.exception;
+ detail.appendChild( p );
+ p=document.createElement( "P" );
+ Test.AnotherWay._set_css_class( p, "result_exception_stack_detail" );
+ p.innerHTML=o.exception_stack.join( "<br/>" );
+ detail.appendChild( p );
+ }
+ for( var ii=0; ii<o.assertions.length; ++ii ) {
+ var oo=o.assertions[ii];
+ var status=oo.ok ? "ok" : "<span class=\"fail\">fail</span>";
+ p=document.createElement( "P" );
+ Test.AnotherWay._set_css_class( p, "result_micro_detail" );
+ p.innerHTML=status;
+ p.appendChild( document.createTextNode( " "+oo.name ) );
+ detail.appendChild( p );
+ }
+ total_detail_ok+=o.n_ok;
+ total_detail_fail+=o.n_fail;
+ }
+ if( total_fail || total_detail_fail ) {
+ text+=" fail "+total_fail;
+ }
+ text+=" ok "+total_ok+" (detailed:";
+ if( total_fail || total_detail_fail ) {
+ text+=" fail "+total_detail_fail;
+ }
+ text+=" ok "+total_detail_ok+")";
+ if( no_plan ) {
+ text+=" <span class=\"warning\">no plan</span>";
+ }
+ style= total_fail==0 ? "ok" : "fail";
+ detail.style.display= style=="fail" ? "block" : "none";
+ detail.style.cursor="text";
+ }
+ if( style!=null ) {
+ Test.AnotherWay._set_css_class( r, style );
+ if( style=="ok" ) {
+ ++Test.AnotherWay._g_ok_pages;
+ }else if( style=="fail" || style=="badtest" ) {
+ ++Test.AnotherWay._g_fail_pages;
+ }
+ var pages_total="";
+ if( Test.AnotherWay._g_fail_pages>0 ) {
+ pages_total+=" fail "+Test.AnotherWay._g_fail_pages;
+ }
+ pages_total+=" ok "+Test.AnotherWay._g_ok_pages;
+ Test.AnotherWay._update_results_total( pages_total );
+ }
+ r.innerHTML=text;
+ if( results.scrollHeight!=null && results.scrollTop!=null && results.offsetHeight!=null ) {
+ results.scrollTop=results.scrollHeight-results.offsetHeight;
+ }
+ // when test_objects is not null, the results are final - good time to clean up
+ if( test_objects!=null ) {
+ for( var i=0; i<test_objects.length; ++i ) {
+ var actions=test_objects[i].delay_actions;
+ for( var action_i=0; action_i<actions.length; ++action_i ) {
+ var action=actions[action_i];
+ if( action.action_kind=="window" && action.wnd_wnd!=null && !action.wnd_no_close ) {
+ action.wnd_wnd.close();
+ action.wnd_wnd=null;
+ }
+ }
+ }
+ }
+ return r;
+}
+Test.AnotherWay._unprint_result=function( child )
+{
+ var results=document.getElementById( "results" );
+ results.removeChild( child );
+}
+Test.AnotherWay._toggle_detail=function()
+{
+ if( this.id.match( /^result(\d+)$/ ) ) {
+ var detail=document.getElementById( "result_detail"+RegExp.$1 );
+ if( detail!=null ) {
+ if( detail.style.display=="none" ) {
+ detail.style.display="block";
+ }else if( detail.style.display=="block" ) {
+ detail.style.display="none";
+ }
+ }
+ }
+}
+Test.AnotherWay._update_msg_counter=function( msg, text )
+{
+ for( var i=0; i<msg.childNodes.length; ++i ) {
+ var item=msg.childNodes[i];
+ if( item.nodeName=="SPAN" && Test.AnotherWay._get_css_class( item )=="counter" ) {
+ item.innerHTML=text;
+ }
+ }
+}
+Test.AnotherWay._update_results_total=function( msg )
+{
+ var total=document.getElementById( "total" );
+ if( total ) {
+ total.innerHTML=msg;
+ }
+}
+Test.AnotherWay._results_clear_onclick=function()
+{
+ var results=document.getElementById( "results" );
+ results.innerHTML="";
+ Test.AnotherWay._update_results_total( "" );
+ Test.AnotherWay._g_ok_pages=0;
+ Test.AnotherWay._g_fail_pages=0;
+ var debug=document.getElementById( "debug" );
+ debug.innerHTML="";
+}
+
+Test.AnotherWay._get_css_class=function( o )
+{
+ var c=o.getAttribute( "className" );
+ if( c==null || c=="" ) {
+ c=o.getAttribute( "class" );
+ }
+ return c;
+}
+Test.AnotherWay._set_css_class=function( o, css_class )
+{
+ o.setAttribute( "className", css_class );
+ o.setAttribute( "class", css_class );
+}
+
+Test.AnotherWay._tab_onclick=function()
+{
+ var tab=this;
+ var tabs=[ document.getElementById( "debug_tab" ), document.getElementById( "results_tab" ) ];
+ var panes=[ document.getElementById( "debug" ), document.getElementById( "results" ) ];
+ for( var i=0; i<tabs.length; ++i ) {
+ if( tab==tabs[i] ) {
+ Test.AnotherWay._set_css_class( tabs[i], "active_tab" );
+ panes[i].style.display="block";
+ }else {
+ Test.AnotherWay._set_css_class( tabs[i], "inactive_tab" );
+ panes[i].style.display="none";
+ }
+ }
+}
+Test.AnotherWay._tab_mouseover=function()
+{
+ if( Test.AnotherWay._get_css_class( this )=="inactive_tab" ) {
+ Test.AnotherWay._set_css_class( this, "inactive_mouseover_tab" );
+ }
+}
+Test.AnotherWay._tab_mouseout=function()
+{
+ if( Test.AnotherWay._get_css_class( this )=="inactive_mouseover_tab" ) {
+ Test.AnotherWay._set_css_class( this, "inactive_tab" );
+ }
+}
+
+// recording mouse input
+Test.AnotherWay._record_check_onfocus=function()
+{
+ var o=this;
+ var check_select=o.type!="text";
+ var div=document.getElementById( "record_div" );
+ var inputs=div.getElementsByTagName( "input" );
+ for( var i=0; i<inputs.length; ++i ) {
+ var input=inputs[i];
+ if( input.type=="radio" ) {
+ if( input.value=="select" ) {
+ input.checked=check_select;
+ }else if( input.value=="input" ) {
+ input.checked=!check_select;
+ }
+ }
+ }
+}
+
+Test.AnotherWay._g_no_record_msg=null; // not null - recording is unavailable
+Test.AnotherWay._g_record_timeout_cnt=0; // opening window for a page for recording
+Test.AnotherWay._g_record_url=null;
+Test.AnotherWay._g_record_wnd=null;
+Test.AnotherWay._g_record_random_id=null; // added to element ids of record_control div so that they do not clash with ids already in the page for which input is recorded
+Test.AnotherWay._g_record_keydown=null; // recording control - which key is down
+Test.AnotherWay._g_record_ctrl_keydown=false;
+Test.AnotherWay._g_record_shift_keydown=false;
+Test.AnotherWay._g_record_control_visible=true; // recording control ui state
+Test.AnotherWay._g_record_started;
+Test.AnotherWay._g_record_paused;
+Test.AnotherWay._g_record_include_mousemove=false;
+Test.AnotherWay._g_record_start_time; // for time references
+Test.AnotherWay._g_record_pause_start_time;
+Test.AnotherWay._g_record_update_time_interval; // showing time in the control ui
+Test.AnotherWay._g_record_waiting_for_results=false; // waiting for results window to open
+Test.AnotherWay._g_record_events; // recorded events
+Test.AnotherWay._g_record_under_cursor; // track element under cursor
+Test.AnotherWay._g_record_checkpoint_count; // for checkpoint numbering
+Test.AnotherWay._g_record_mouse_over_record_control; // for avoiding record control highlight on mouseover
+Test.AnotherWay._g_record_highlighted_element={ element: null, x: null, y: null };
+
+Test.AnotherWay._record_control_get_element=function( id )
+{
+ if( Test.AnotherWay._g_record_wnd!=null && Test.AnotherWay._g_record_wnd.document!=null ) {
+ return Test.AnotherWay._g_record_wnd.document.getElementById( id+Test.AnotherWay._g_record_random_id );
+ }else {
+ return null;
+ }
+}
+Test.AnotherWay._record_start_onclick=function() // "record" button on the run_tests.html: open a window for a page for which input is recorded
+{
+ if( Test.AnotherWay._g_no_record_msg!=null ) {
+ alert( Test.AnotherWay._g_no_record_msg );
+ return;
+ }
+ if( Test.AnotherWay._g_record_timeout_cnt>0
+ || (Test.AnotherWay._g_record_wnd!=null && (Test.AnotherWay._g_record_wnd.closed!=null && !Test.AnotherWay._g_record_wnd.closed)) ) { // in opera, closed is null.
+ alert( "there is already window opened for recording input for a page "+Test.AnotherWay._g_record_url );
+ return;
+ }
+ var div=document.getElementById( "record_div" );
+ var inputs=div.getElementsByTagName( "input" );
+ var url=null;
+ for( var i=0; i<inputs.length; ++i ) {
+ var input=inputs[i];
+ if( input.type=="radio" ) {
+ if( input.value=="select" && input.checked ) {
+ var index=document.getElementById( "record_select" ).selectedIndex;
+ if( index>0 ) {
+ url=Test.AnotherWay._g_test_page_urls[index-1].url;
+ }
+ }else if( input.value=="input" && input.checked ) {
+ url=document.getElementById( "record_input" ).value;
+ }
+ }
+ }
+ if( url!=null ) {
+ Test.AnotherWay._g_record_url=url;
+ Test.AnotherWay._g_record_wnd=window.open( url, "_blank" );
+ if( Test.AnotherWay._g_record_wnd==null ) {
+ alert( "unable to open new window for a page: "+url );
+ }else {
+ Test.AnotherWay._g_record_timeout_cnt=50;
+ setTimeout( Test.AnotherWay._record_window_timeout, 100 );
+ }
+ }
+}
+Test.AnotherWay._record_window_timeout=function()
+{
+ if( Test.AnotherWay._is_url_loaded( Test.AnotherWay._g_record_url, Test.AnotherWay._g_record_wnd ) ) {
+ Test.AnotherWay._record_window_setup( Test.AnotherWay._g_record_wnd );
+ }else {
+ if( --Test.AnotherWay._g_record_timeout_cnt>0 ) {
+ setTimeout( Test.AnotherWay._record_window_timeout, 100 );
+ }else {
+ alert( "timeout expired while opening new window for a page: "+Test.AnotherWay._g_record_url );
+ Test.AnotherWay._g_record_wnd=null;
+ Test.AnotherWay._g_record_url=null;
+ Test.AnotherWay._g_record_timeout_cnt=0;
+ }
+ }
+}
+Test.AnotherWay._record_control_randomize_id=function( e, r )
+{
+ if( e.id!="" ) {
+ e.id=e.id+r;
+ }
+ for( var c=e.firstChild; c!=null; c=c.nextSibling ) {
+ Test.AnotherWay._record_control_randomize_id( c, r );
+ }
+}
+Test.AnotherWay._record_window_setup=function( wnd ) // insert recording control into the page for which input is recorded
+{
+ Test.AnotherWay._g_record_timeout_cnt=0;
+ var this_div=document.getElementById( "record_control" );
+ var record_control=wnd.document.importNode( this_div, true );
+ Test.AnotherWay._g_record_random_id=(1000*Math.random()).toFixed();
+ Test.AnotherWay._record_control_randomize_id( record_control, Test.AnotherWay._g_record_random_id );
+ Test.AnotherWay._g_record_control_visible=true;
+ Test.AnotherWay._g_record_started=false;
+ Test.AnotherWay._g_record_paused=false;
+ Test.AnotherWay._g_record_checkpoint_count=0;
+ Test.AnotherWay._g_record_mouse_over_record_control=false;
+ var doc=wnd.document;
+ doc.body.appendChild( record_control );
+ // opera sans-serif font is different
+ if( window.opera ) {
+ cursor_over_indicator=Test.AnotherWay._record_control_get_element( "record_cursor_over" );
+ cursor_over_indicator.style.width="18em";
+ cursor_over_indicator.style.height="2em";
+ cursor_over_indicator.style.fontSize="7pt";
+ }
+ doc.addEventListener( "keydown", Test.AnotherWay._record_control_keydown, true );
+ doc.addEventListener( "keyup", Test.AnotherWay._record_control_keyup, true );
+// doc.addEventListener( "keypress", Test.AnotherWay._record_event, true ); // replaying is not supported by any known browser
+
+ doc.body.addEventListener( "mousemove", Test.AnotherWay._record_on_mousemove, true );
+ doc.body.addEventListener( "click", Test.AnotherWay._record_event, true );
+ doc.body.addEventListener( "mouseover", Test.AnotherWay._record_event, true );
+ doc.body.addEventListener( "mouseout", Test.AnotherWay._record_event, true );
+ doc.body.addEventListener( "mousedown", Test.AnotherWay._record_event, true );
+ doc.body.addEventListener( "mouseup", Test.AnotherWay._record_event, true );
+}
+Test.AnotherWay._record_control_key_disabled=function( k )
+{
+ if( k=="c" ) {
+ return !Test.AnotherWay._g_record_started;
+ }else if( k=="p" ) {
+ return !Test.AnotherWay._g_record_started;
+ }else if( k=="s" ) {
+ return Test.AnotherWay._g_record_waiting_for_results;
+ }else {
+ return false;
+ }
+}
+
+Test.AnotherWay._record_control_update_ui=function()
+{
+ var keydown_color="#fff";
+ var disabled_color="#aaa";
+ var button_color="#adf";
+ var active_color="#fdf";
+
+ var display={};
+ display[false]="none";
+ display[true]="inline";
+
+ var s_button=Test.AnotherWay._record_control_get_element( "record_s" );
+ var record_on=Test.AnotherWay._record_control_get_element( "record_on" );
+ var record_off=Test.AnotherWay._record_control_get_element( "record_off" );
+
+ s_button.style.backgroundColor= Test.AnotherWay._record_control_key_disabled( "s" ) ? disabled_color
+ : Test.AnotherWay._g_record_keydown=="s" ? keydown_color : Test.AnotherWay._g_record_started ? active_color : button_color;
+ record_on.style.display=display[!Test.AnotherWay._g_record_started];
+ record_off.style.display=display[Test.AnotherWay._g_record_started];
+
+ var h_button=Test.AnotherWay._record_control_get_element( "record_h" );
+ h_button.style.backgroundColor= Test.AnotherWay._g_record_keydown=="h" ? keydown_color : button_color;
+
+ var p_button=Test.AnotherWay._record_control_get_element( "record_p" );
+ var record_pause_on=Test.AnotherWay._record_control_get_element( "record_pause_on" );
+ var record_pause_off=Test.AnotherWay._record_control_get_element( "record_pause_off" );
+ p_button.style.backgroundColor= Test.AnotherWay._record_control_key_disabled( "p" ) ? disabled_color
+ : Test.AnotherWay._g_record_keydown=="p" ? keydown_color : Test.AnotherWay._g_record_paused ? active_color : button_color;
+ record_pause_on.style.display=display[!Test.AnotherWay._g_record_paused];
+ record_pause_off.style.display=display[Test.AnotherWay._g_record_paused];
+
+ var m_button=Test.AnotherWay._record_control_get_element( "record_m" );
+ var record_include_mousemove=Test.AnotherWay._record_control_get_element( "record_include_mousemove" );
+ var record_omit_mousemove=Test.AnotherWay._record_control_get_element( "record_omit_mousemove" );
+ m_button.style.backgroundColor= Test.AnotherWay._g_record_keydown=="m" ? keydown_color : Test.AnotherWay._g_record_include_mousemove ? active_color : button_color;
+ record_include_mousemove.style.display=display[!Test.AnotherWay._g_record_include_mousemove];
+ record_omit_mousemove.style.display=display[Test.AnotherWay._g_record_include_mousemove];
+
+ var c_button=Test.AnotherWay._record_control_get_element( "record_c" );
+ c_button.style.backgroundColor= Test.AnotherWay._record_control_key_disabled( "c" ) ? disabled_color
+ : Test.AnotherWay._g_record_keydown=="c" ? keydown_color : button_color;
+
+ var record_indicator=Test.AnotherWay._record_control_get_element( "record_indicator" );
+ record_indicator.style.display=display[Test.AnotherWay._g_record_started];
+
+ var pause_indicator=Test.AnotherWay._record_control_get_element( "record_pause_indicator" );
+ pause_indicator.style.display=display[Test.AnotherWay._g_record_paused];
+
+ var record_control=Test.AnotherWay._record_control_get_element( "record_control" );
+ record_control.style.display= Test.AnotherWay._g_record_control_visible ? "block" : "none";
+
+ var shift_button=Test.AnotherWay._record_control_get_element( "record_shift_key" );
+ shift_button.style.backgroundColor= Test.AnotherWay._g_record_shift_keydown ? keydown_color : button_color;
+
+ var ctrl_button=Test.AnotherWay._record_control_get_element( "record_ctrl_key" );
+ ctrl_button.style.backgroundColor= Test.AnotherWay._g_record_ctrl_keydown ? keydown_color : button_color;
+}
+Test.AnotherWay._record_format_time=function( t )
+{
+ t=new Date( t );
+ var m=t.getMinutes();
+ var s=t.getSeconds();
+ var str= m==0 ? "" : m+"m ";
+ str+=s+"s.";
+ return str;
+}
+Test.AnotherWay._record_control_update_time=function()
+{
+ var time_display=Test.AnotherWay._record_control_get_element( "record_time" );
+ if( time_display!=null ) {
+ time_display.innerHTML=Test.AnotherWay._record_format_time( (new Date()).getTime()-Test.AnotherWay._g_record_start_time );
+ }
+}
+Test.AnotherWay._record_control_update_highlight=function( elem, style, event )
+{
+ if( elem==null ) {
+ Test.AnotherWay._record_highlight_border( null );
+ }else {
+ var pos=Test.AnotherWay._get_page_coords( elem );
+ if( style=="ball" || elem!=Test.AnotherWay._g_record_highlighted_element.element || pos.x!=Test.AnotherWay._g_record_highlighted_element.x || pos.y!=Test.AnotherWay._g_record_highlighted_element.y ) {
+ Test.AnotherWay._g_record_highlighted_element={ element: elem, x: pos.x, y: pos.y };
+ Test.AnotherWay._record_highlight_border( elem, style, event );
+ }
+ }
+}
+Test.AnotherWay._record_decode_key=function( event )
+{
+ var k=null;
+ if( event==null ) {
+ k=Test.AnotherWay._g_record_wnd.event.keyCode;
+ }else {
+ k=event.which;
+ }
+ if( k==83 ) {
+ return "s";
+ }else if( k==72 ) {
+ return "h";
+ }else if( k==73 ) {
+ return "i";
+ }else if( k==80 ) {
+ return "p";
+ }else if( k==67 ) {
+ return "c";
+ }else if( k==77 ) {
+ return "m";
+ }else if( k==16 ) {
+ return "shift";
+ }else if( k==17 ) {
+ return "ctrl";
+ }else if( k==18 ) {
+ return "alt";
+ }else if( k==19 ) {
+ return "pause";
+ }else if( k==123 ) {
+ return "f12";
+ }
+ return "";
+}
+Test.AnotherWay._record_control_keydown=function( event )
+{
+ var handled=false;
+ var k=Test.AnotherWay._record_decode_key( event );
+ if( k=="shift" ) {
+ Test.AnotherWay._g_record_shift_keydown=true;
+ }else if( k=="ctrl" ) {
+ Test.AnotherWay._g_record_ctrl_keydown=true;
+ }else if( k!="" && (Test.AnotherWay._g_record_keydown==null || Test.AnotherWay._g_record_keydown==k) ) {
+ if( Test.AnotherWay._g_record_ctrl_keydown && Test.AnotherWay._g_record_shift_keydown && !Test.AnotherWay._record_control_key_disabled( k ) ) {
+ Test.AnotherWay._g_record_keydown=k;
+ handled=true;
+ }
+ }else {
+ Test.AnotherWay._g_record_keydown="";
+ }
+ Test.AnotherWay._record_control_update_ui();
+ if( !handled ) {
+// Test.AnotherWay._record_event( event ); // replaying is not supported in any known browser
+ }
+ return;
+}
+Test.AnotherWay._record_control_keyup=function( event )
+{
+ var handled=false;
+ var k=Test.AnotherWay._record_decode_key( event );
+ if( k=="shift" ) {
+ Test.AnotherWay._g_record_shift_keydown=false;
+ }else if( k=="ctrl" ) {
+ Test.AnotherWay._g_record_ctrl_keydown=false;
+ }else if( k!="" && k==Test.AnotherWay._g_record_keydown && Test.AnotherWay._g_record_ctrl_keydown && Test.AnotherWay._g_record_shift_keydown ) {
+ if( k=="s" ) {
+ Test.AnotherWay._g_record_started=!Test.AnotherWay._g_record_started;
+ if( Test.AnotherWay._g_record_started ) {
+ Test.AnotherWay._g_record_events=[];
+ Test.AnotherWay._g_record_start_time=(new Date()).getTime();
+ Test.AnotherWay._record_control_update_time();
+ Test.AnotherWay._g_record_update_time_interval=window.setInterval( Test.AnotherWay._record_control_update_time, 200 );
+ }else {
+ Test.AnotherWay._record_control_update_highlight( null );
+ if( !Test.AnotherWay._g_record_paused ) {
+ window.clearInterval( Test.AnotherWay._g_record_update_time_interval );
+ }
+ Test.AnotherWay._g_record_waiting_for_results=true;
+ // open a new window for self, pass a parameter to dump recorded events as javascript code there
+ // (the easiest way to obtain a document from the same origin, so it's writable, is to open this same page again)
+ Test.AnotherWay._g_record_paused=false;
+ var loc=window.location;
+ loc=loc.protocol+"//"+loc.host+loc.pathname+"?recording_results="+Test.AnotherWay._g_record_random_id;
+ if( window.open( loc, "_blank" )==null ) {
+ alert( "unable to open new window for results" );
+ }
+ }
+ handled=true;
+ }else if( k=="h" ) {
+ Test.AnotherWay._g_record_control_visible=!Test.AnotherWay._g_record_control_visible;
+ handled=true;
+ }else if( k=="p" ) {
+ Test.AnotherWay._g_record_paused=!Test.AnotherWay._g_record_paused;
+ if( Test.AnotherWay._g_record_paused ) {
+ Test.AnotherWay._g_record_pause_start_time=(new Date()).getTime();
+ if( Test.AnotherWay._g_record_started ) {
+ window.clearInterval( Test.AnotherWay._g_record_update_time_interval );
+ }
+ Test.AnotherWay._record_control_update_highlight( null );
+ }else {
+ var pause_duration=(new Date()).getTime()-Test.AnotherWay._g_record_pause_start_time;
+ Test.AnotherWay._g_record_start_time+=pause_duration;
+ Test.AnotherWay._g_record_update_time_interval=window.setInterval( Test.AnotherWay._record_control_update_time, 200 );
+ }
+ handled=true;
+ }else if( k=="m" ) {
+ Test.AnotherWay._g_record_include_mousemove=!Test.AnotherWay._g_record_include_mousemove;
+ handled=true;
+ }else if( k=="c" ) {
+ var o=Test.AnotherWay._record_checkpoint();
+ Test.AnotherWay._record_display_checkpoint( o );
+ Test.AnotherWay._record_flash_border( "#24d" );
+ handled=true;
+ }
+ }
+ Test.AnotherWay._g_record_keydown=null;
+ Test.AnotherWay._record_control_update_ui();
+ if( !handled ) {
+// Test.AnotherWay._record_event( event ); // replaying is not supported in any known browser
+ }
+ return;
+}
+Test.AnotherWay._record_html_node_path=function( node )
+{
+ if( node==null ) {
+ return null;
+ }
+ var path=[];
+ while( true ) {
+ if( node.id!=null && node.id!="" ) {
+ path.unshift( "#"+node.id+" "+node.nodeName );
+ break;
+ }else {
+ var parent_node=node.parentNode;
+ if( parent_node==null ) {
+ return []; // no BODY up the path - this node is screwed (browsers differ in what's above the body), discard
+ }else {
+ var i=0;
+ var found=false;
+ for( var child=parent_node.firstChild; child!=null; child=child.nextSibling ) {
+ if( child==node ) {
+ found=true;
+ break;
+ }
+ if( child.nodeType==1 ) { // count only HTML element nodes
+ ++i;
+ }
+ }
+ if( !found ) {
+ i=-1;
+ }
+ path.unshift( i+" "+node.nodeName );
+ if( parent_node.nodeName=="BODY" || parent_node.nodeName=="body" ) {
+ break;
+ }
+ node=parent_node;
+ }
+ }
+ }
+ return path;
+}
+Test.AnotherWay._record_node_path_to_string=function( path )
+{
+ var s="";
+ if( path!=null ) {
+ for( var i=0; i<path.length; ++i ) {
+ s+= i==0 ? "" : ", ";
+ var elem=path[i].split( " " );
+ if( elem[0].charAt( 0 )=="#" ) {
+ s+=elem[1]+" "+elem[0];
+ }else {
+ s+=elem[1]+" ["+elem[0]+"]";
+ }
+ }
+ }
+ return s;
+}
+Test.AnotherWay._record_node_path_to_node=function( path_str, doc )
+{
+ if( path_str==null ) {
+ return null;
+ }
+ var path=path_str.split( "," );
+ var node=doc.body;
+ for( var i=0; i<path.length; ++i ) {
+ var node_i=path[i].split( " " )[0];
+ if( node_i.charAt( 0 )=="#" ) {
+ node=doc.getElementById( node_i.substring( 1 ) );
+ }else {
+ if( node_i<0 || node_i>=node.childNodes.length ) {
+ node=null;
+ }else {
+ node=node.firstChild;
+ while( node!=null ) {
+ if( node.nodeType==1 ) { // count only HTML element nodes
+ if( node_i==0 ) {
+ break;
+ }
+ --node_i;
+ }
+ node=node.nextSibling;
+ }
+ }
+ }
+ if( node==null ) {
+ return null;
+ }
+ }
+ return node;
+}
+Test.AnotherWay._record_control_contains_id=function( s )
+{
+ return s.match( /^#record_[\w_]+/ ) && s.match( Test.AnotherWay._g_record_random_id );
+}
+Test.AnotherWay._record_checkpoint=function()
+{
+ var o={ type: "_checkpoint", time: (new Date()).getTime()-Test.AnotherWay._g_record_start_time, which: Test.AnotherWay._g_record_checkpoint_count++,
+ target: Test.AnotherWay._record_html_node_path( Test.AnotherWay._g_record_under_cursor ) };
+ Test.AnotherWay._g_record_events.push( o );
+ return o;
+}
+Test.AnotherWay._record_event=function( event )
+{
+ var unneeded=["rangeOffset","eventPhase","timeStamp","isTrusted","popupWindowFeatures","rangeOffset"];
+ if( Test.AnotherWay._g_record_started && !Test.AnotherWay._g_record_paused ) {
+ var o={};
+ for( var n in event ) {
+ var needed=!n.match( /^[A-Z0-9_]+$/ );
+ if( needed ) {
+ for( var ui=0; ui<unneeded.length; ++ui ) {
+ if( unneeded[ui]==n ) {
+ needed=false;
+ break;
+ }
+ }
+ if( needed ) {
+ var value=event[n];
+ if( typeof( value )!="object" && typeof( value )!="function" ) {
+ o[n]=value;
+ }else if( n=="target" || n=="relatedTarget" ) {
+ o[n]=Test.AnotherWay._record_html_node_path( value );
+ }
+ }
+ }
+ }
+ o["time"]=(new Date()).getTime()-Test.AnotherWay._g_record_start_time;
+ var over_record_control= o["target"]!=null && o["target"][0]!=null && Test.AnotherWay._record_control_contains_id( o["target"][0] );
+ if( !over_record_control ) {
+ Test.AnotherWay._g_record_events.push( o );
+ }
+ }
+ return true;
+}
+Test.AnotherWay._record_on_mousemove=function( event )
+{
+ var path=Test.AnotherWay._record_html_node_path( event.target );
+ var new_mouse_over_record_control= path!=null && path[0]!=null && Test.AnotherWay._record_control_contains_id( path[0] );
+ if( new_mouse_over_record_control!=Test.AnotherWay._g_record_mouse_over_record_control ) {
+ Test.AnotherWay._g_record_mouse_over_record_control=new_mouse_over_record_control;
+ Test.AnotherWay._record_control_update_ui();
+ }
+ if( event.target!=null && event.target!=Test.AnotherWay._g_record_under_cursor ) {
+ Test.AnotherWay._g_record_under_cursor=event.target;
+ var s="";
+ if( path==null || path[0]==null || !Test.AnotherWay._record_control_contains_id( path[0] ) ) {
+ s=Test.AnotherWay._record_node_path_to_string( path );
+ }
+ if( s=="" ) {
+ s="&nbsp;";
+ }
+ var cursor_over_indicator=Test.AnotherWay._record_control_get_element( "record_cursor_over" );
+ cursor_over_indicator.innerHTML=s;
+ }
+
+ var highlight_element=null;
+ if( !Test.AnotherWay._g_record_mouse_over_record_control && Test.AnotherWay._g_record_started && !Test.AnotherWay._g_record_paused ) {
+ highlight_element=event.target;
+ }
+ // highlight border disabled on recording - it causes page to scroll, issuing spurious mouseover/mouseout event
+ //Test.AnotherWay._record_control_update_highlight( highlight_element, "border" );
+
+ if( Test.AnotherWay._g_record_include_mousemove ) {
+ Test.AnotherWay._record_event( event );
+ }
+ return true;
+}
+Test.AnotherWay._record_display_checkpoint=function( o )
+{
+ var checkpoints_div=Test.AnotherWay._record_control_get_element( "record_checkpoints" );
+ var p=checkpoints_div.appendChild( checkpoints_div.ownerDocument.createElement( "div" ) );
+ p.style.marginTop="3px";
+ p.style.font="normal normal 8pt sans-serif";
+ p.style.color="#000";
+ p.style.textAligh="left";
+ p.style.position="relative";
+ p.style.width="100%";
+ var checkpoint_text="";
+ checkpoint_text+="#"+(o.which+1);
+ checkpoint_text+=" "+Test.AnotherWay._record_format_time( o.time );
+ if( o.target!=null ) {
+ checkpoint_text+=Test.AnotherWay._record_node_path_to_string( o.target );
+ }
+ p.appendChild( p.ownerDocument.createTextNode( checkpoint_text ) );
+}
+Test.AnotherWay._record_save_results=function( doc )
+{
+ // strange, but DOM-style append does not work here in opera 8.
+ var append=function( s ) { doc.write( "<div>"+s+"</div>" ); };
+ append( "/* paste this data into your javascript and pass it as an argument to replay_events method */" );
+ append( "{ checkpoints: [" );
+ var first_checkpoint=true;
+ for( var i=0; i<Test.AnotherWay._g_record_events.length; ++i ) {
+ var o=Test.AnotherWay._g_record_events[i];
+ if( o.type=="_checkpoint" ) {
+ var str= first_checkpoint ? "" : "}, ";
+ str+="function( tst, wnd ) { // #"+o.which+" time "+Test.AnotherWay._record_format_time( o.time )+" cursor was over "+Test.AnotherWay._record_node_path_to_string( o.target );
+ append( str );
+ first_checkpoint=false;
+ }
+ }
+ if( !first_checkpoint ) {
+ append( "}" );
+ }
+ append( "], events: [ " );
+ var prev_time=0;
+ for( var i=0; i<Test.AnotherWay._g_record_events.length; ++i ) {
+ var o=Test.AnotherWay._g_record_events[i];
+ var s="";
+ s+= "{";
+ var n_first=true;
+ for( var n in o ) {
+ if( n=="time" ) { // convert to relative time
+ var cur_time=o[n]-0;
+ o[n]=cur_time-prev_time;
+ prev_time=cur_time;
+ }
+ s+=n_first ? n : ", "+n;
+ s+=":";
+ if( o[n]==null ) {
+ s+="null";
+ }else {
+ s+="\""+o[n]+"\"";
+ }
+ n_first=false;
+ }
+ s+= i==Test.AnotherWay._g_record_events.length-1 ? "}" : "},";
+ append( s );
+ }
+ append( "] }" );
+ append( ";" );
+}
+
+Test.AnotherWay._g_record_border; // border highlighting element under cursor
+Test.AnotherWay._g_record_border_flashes=[]; // array of { color: color, timeout: milliseconds }
+Test.AnotherWay._g_record_border_flashing=false;
+Test.AnotherWay._g_record_border_normal_color="#d4b";
+Test.AnotherWay._record_flash_border_timeout=function()
+{
+ var color=Test.AnotherWay._g_record_border_normal_color;
+ var timeout=null;
+ if( Test.AnotherWay._g_record_border_flashes.length!=0 ) {
+ color=Test.AnotherWay._g_record_border_flashes[0].color;
+ timeout=Test.AnotherWay._g_record_border_flashes[0].timeout;
+ Test.AnotherWay._g_record_border_flashes.splice( 0, 1 );
+ }
+ if( Test.AnotherWay._g_record_border!=null ) {
+ for( var i=0; i<Test.AnotherWay._g_record_border.length; ++i ) {
+ Test.AnotherWay._g_record_border[i].style.backgroundColor=color;
+ }
+ }
+ if( timeout!=null ) {
+ setTimeout( Test.AnotherWay._record_flash_border_timeout, timeout );
+ }else {
+ Test.AnotherWay._g_record_border_flashing=false;
+ }
+}
+Test.AnotherWay._get_page_coords=function( elm )
+{
+ var point = { x: 0, y: 0 };
+ while( elm ) {
+ point.x+=elm.offsetLeft;
+ point.y+=elm.offsetTop;
+ elm=elm.offsetParent;
+ }
+ return point;
+}
+Test.AnotherWay._set_page_coords=function( elm, x, y )
+{
+ var parent_coords={ x: 0, y: 0 };
+ if( elm.offsetParent ) {
+ parent_coords=Test.AnotherWay._get_page_coords( elm.offsetParent );
+ }
+ var new_x=x-parent_coords.x;
+ if( new_x<0 ) {
+ new_x=0;
+ }
+ elm.style.left=new_x+'px';
+ var new_y=y-parent_coords.y;
+ if( new_y<0 ) {
+ new_y=0;
+ }
+ elm.style.top=new_y+'px';
+}
+Test.AnotherWay._record_setup_highlight_positions=function( element, style, coords, positions )
+{
+ if( style=="border" ) {
+ var width=element.clientWidth;
+ var height=element.clientHeight;
+ var step=0;
+ var thickness=2;
+ var fudge_expand=4;
+ positions.push( { x: coords.x-step-thickness, y: coords.y-step-thickness, width: width+2*step+2*thickness+fudge_expand, height: thickness } );
+ positions.push( { x: coords.x+width+step+fudge_expand, y: coords.y-step-thickness, width: thickness, height: height+2*step+2*thickness+fudge_expand } );
+ positions.push( { x:positions[0].x, y:positions[0].y, width:positions[0].width, height:positions[0].height } );
+ positions.push( { x:positions[1].x, y:positions[1].y, width:positions[1].width, height:positions[1].height } );
+ positions[2].y+=height+thickness+2*step+fudge_expand;
+ positions[3].x-=width+thickness+2*step+fudge_expand;
+ }else if( style=="ball" ) {
+ positions.push( { x: coords.x+2, y: coords.y, width: 2, height: 6 } );
+ positions.push( { x: coords.x, y: coords.y+2, width: 6, height: 2 } );
+ positions.push( { x: coords.x+1, y: coords.y+1, width: 4, height: 4 } );
+ }
+}
+Test.AnotherWay._record_highlight_border=function( element, style, event ) // null - hide border
+{
+ if( element!=null ) {
+ if( Test.AnotherWay._g_record_border==null || Test.AnotherWay._g_record_border[0].ownerDocument!=element.ownerDocument ) {
+ Test.AnotherWay._g_record_border=[];
+ var n= style=="border" ? 4 : style=="ball" ? 3 : 0;
+ for( var i=0; i<4; ++i ) {
+ var b=element.ownerDocument.createElement( "div" );
+ b.style.position="absolute";
+ b.style.zIndex="1";
+ b.style.backgroundColor=Test.AnotherWay._g_record_border_normal_color;
+ element.ownerDocument.body.appendChild( b );
+ Test.AnotherWay._g_record_border.push( b );
+ }
+ }
+ var coords=null;
+ if( style=="border" ) {
+ coords=Test.AnotherWay._get_page_coords( element );
+ }else if( style=="ball" ) {
+ if( event!=null ) {
+ if( event.pageX!=null && event.pageY!=null ) {
+ coords={ x: event.pageX-0, y: event.pageY-0 };
+ }else if( event.clientX!=null && event.clientY!=null ) {
+ var doc=element.ownerDocument;
+ if( doc!=null ) {
+ coords={ x: (event.clientX-0)+doc.body.scrollLeft, y: (event.clientY-0)+doc.body.scrollTop };
+ }
+ }
+ }
+ }
+ if( coords!=null && element.clientWidth!=null && element.clientHeight!=null ) {
+ var positions=[];
+ Test.AnotherWay._record_setup_highlight_positions( element, style, coords, positions );
+ for( var i=0; i<positions.length; ++i ) {
+ var b=Test.AnotherWay._g_record_border[i];
+ var p=positions[i];
+ Test.AnotherWay._set_page_coords( b, p.x, p.y );
+ b.style.width=p.width+"px";
+ b.style.height=p.height+"px";
+ b.style.display="block";
+ }
+ }
+ }else {
+ if( Test.AnotherWay._g_record_border!=null ) {
+ for( var i=0; i<Test.AnotherWay._g_record_border.length; ++i ) {
+ Test.AnotherWay._g_record_border[i].style.display="none";
+ }
+ }
+ }
+}
+Test.AnotherWay._record_flash_border=function( color )
+{
+ if( Test.AnotherWay._g_record_border_flashing ) { //already
+ Test.AnotherWay._g_record_border_flashes.push( { color: Test.AnotherWay._g_record_border_normal_color, timeout:300 } );
+ Test.AnotherWay._g_record_border_flashes.push( { color: color, timeout:600 } );
+ }else {
+ Test.AnotherWay._g_record_border_flashing=true;
+ Test.AnotherWay._g_record_border_flashes.push( { color: color, timeout:600 } );
+ Test.AnotherWay._record_flash_border_timeout();
+ }
+}
+Test.AnotherWay._record_prepare_doc_for_results=function()
+{
+ document.open();
+ document.write( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" );
+ document.write( "<html><head><title> Input recording results</title>" );
+ document.write( "<style type=\"text/css\">" );
+ document.write( "body { font: normal normal smaller sans-serif; }" );
+ document.write( "div { margin-top: 3px; }" );
+ document.write( "</style></head><body>" );
+ // opera and mozilla disagree over who the opener is.
+ if( typeof( window.opener.Test )!="undefined" && typeof( window.opener.Test.AnotherWay )!="undefined" ) {
+ window.opener.Test.AnotherWay._record_save_results( document );
+ window.opener.Test.AnotherWay._g_record_waiting_for_results=false;
+ window.opener.Test.AnotherWay._record_control_update_ui();
+ }else if( typeof( window.opener.opener.Test )!="undefined" && typeof( window.opener.opener.Test.AnotherWay )!="undefined" ) {
+ window.opener.opener.Test.AnotherWay._record_save_results( document );
+ window.opener.opener.Test.AnotherWay._g_record_waiting_for_results=false;
+ window.opener.opener.Test.AnotherWay._record_control_update_ui();
+ }
+ document.write( "</body>" );
+ document.close();
+}
+
+// global initialization
+onload=function()
+{
+ if( window.opera ) {
+ var good_opera=typeof( window.opera.version )=="function";
+ good_opera=good_opera && window.opera.version().match( /^\s*(\d+)/ );
+ good_opera=good_opera && RegExp.$1>=8;
+ }
+ var span=document.createElement( "SPAN" );
+ span.innerHTML="<!--[if IE]><br /><![endif]-"+"->";
+ var is_ie=span.getElementsByTagName( "BR" ).length>0;
+
+ Test.AnotherWay._g_test_iframe=window.frames.test_iframe;
+
+ var query_str=window.location.search;
+ if( query_str.charAt( 0 )=="?" ) {
+ query_str=query_str.substring( 1 );
+ }
+ var testlist_page="list-tests.html";
+ var auto_run=false;
+ if( query_str!="" ) {
+ var params=[query_str];
+ if( query_str.indexOf( ";" )!=-1 ) {
+ params=query_str.split( ";" );
+ }else if( query_str.indexOf( "&" )!=-1 ) {
+ params=query_str.split( "&" );
+ }
+ for( var param_i=0; param_i<params.length; ++param_i ) {
+ var param=params[param_i].split( "=" );
+ if( param[0]=="recording_results" ) {
+ if( window.opener!=null ) {
+ // we were told to show recording results - replace everything in the document with the results
+ Test.AnotherWay._record_prepare_doc_for_results();
+ return;
+ }
+ }else if( param[0]=="testpage" ) {
+ Test.AnotherWay._add_test_page_url( decodeURIComponent( param[1] ), "anotherway" );
+ }else if( param[0]=="jsantestpage" ) {
+ Test.AnotherWay._add_test_page_url( decodeURIComponent( param[1] ), "jsan" );
+ }else if( param[0]=="testlist" ) {
+ testlist_page=decodeURIComponent( param[1] );
+ }else if( param[0]=="testframe" ) {
+ if( window.opera && !good_opera ) {
+ Test.AnotherWay._show_error( "testframe parameter does not work in versions of Opera prior to 8.0. Sorry (pathches are welcome)." );
+ // Opera 7 barfs on attempt to access frame.frameElement.
+ // if someone knows a way to assign onload handler to that iframe in Opera 7
+ // without disrupting code that works in other browsers, patches are welcome.
+ }else {
+ var frame_path=param[1].split( "." );
+ var frame=top;
+ for( var frame_path_i=0; frame_path_i<frame_path.length; ++frame_path_i ) {
+ frame=frame[frame_path[frame_path_i]];
+ }
+ if( frame==null ) {
+ Test.AnotherWay._show_error( "unable to find frame specified for loading test pages: "+param[1] );
+ }else {
+ if( frame.frameElement!=null ) { // for the following assignement to onload to work, frameElement is required
+ frame=frame.frameElement;
+ }
+ Test.AnotherWay._g_test_iframe=frame;
+ }
+ }
+ }else if( param[0]=="testframe_no_clear" ) {
+ Test.AnotherWay._g_test_frame_no_clear=true;
+ }else if( param[0]=="windows" ) {
+ if (param[1] == "none") {
+ }
+ }else if( param[0]=="run" ) {
+ auto_run=true;
+ if( param[1]=="all" ) {
+ Test.AnotherWay._g_pages_to_run="all";
+ }else {
+ if( Test.AnotherWay._g_pages_to_run==null || Test.AnotherWay._g_pages_to_run=="all" ) {
+ Test.AnotherWay._g_pages_to_run=[];
+ }
+ var pages=param[1].split( "," );
+ for( var i=0; i<pages.length; ++i ) {
+ Test.AnotherWay._g_pages_to_run.push( pages[i] );
+ }
+ }
+ }
+ }
+ }
+ if( Test.AnotherWay._g_test_page_urls.length==0 ) { // if no individual pages were given on the command line, load the list
+ var result=Test.AnotherWay._set_iframe_location( window.frames["list_iframe"], testlist_page );
+ if( result.msg!=null ) {
+ Test.AnotherWay._show_error( result.msg );
+ }
+ Test.AnotherWay._g_run_on_list_load=auto_run;
+ }else {
+ Test.AnotherWay._g_run_on_main_load=auto_run;
+ }
+
+ var f=Test.AnotherWay._g_test_iframe;
+ try {
+ if( f.attachEvent!=null ) {
+ f.attachEvent( "onload", Test.AnotherWay._test_page_onload );
+ }else {
+ f.onload=Test.AnotherWay._test_page_onload;
+ }
+ if( Test.AnotherWay._g_test_iframe.nodeType!=null && Test.AnotherWay._g_test_iframe.contentWindow!=null ) { // it's iframe element, not the iframe. we need iframe.
+ Test.AnotherWay._g_test_iframe=Test.AnotherWay._g_test_iframe.contentWindow;
+ }
+ }catch(e) {
+ // ignore stupid opera error if the frame has onload handler assigned in the inline html
+ }
+ var handlers={
+ "run_all": { "onclick": Test.AnotherWay._run_all_onclick },
+ "run_selected": { "onclick": Test.AnotherWay._run_selected_onclick },
+ "unselect_all": { "onclick": Test.AnotherWay._unselect_all_onclick },
+ "record_select": { "onfocus": Test.AnotherWay._record_check_onfocus },
+ "record_input": { "onfocus": Test.AnotherWay._record_check_onfocus },
+ "record_start": { "onclick": Test.AnotherWay._record_start_onclick },
+ "clear_btn": { "onclick": Test.AnotherWay._results_clear_onclick },
+ "results_tab": { "onclick": Test.AnotherWay._tab_onclick, "onmouseover": Test.AnotherWay._tab_mouseover, "onmouseout": Test.AnotherWay._tab_mouseout },
+ "debug_tab": { "onclick": Test.AnotherWay._tab_onclick, "onmouseover": Test.AnotherWay._tab_mouseover, "onmouseout": Test.AnotherWay._tab_mouseout }
+ };
+ for( var hs in handlers ) {
+ var o=document.getElementById( hs );
+ if( o!=null ) {
+ for( var h in handlers[hs] ) {
+ o[h]=handlers[hs][h];
+ }
+ }else {
+ Test.AnotherWay._show_error( "unable to set "+h+" handler: id "+hs+" not found" );
+ }
+ }
+
+ if( window.opera && !good_opera ) {
+ Test.AnotherWay._g_no_record_msg="Input events recording and replaying is not available in opera versions prior to 8.0.";
+ }
+ if( is_ie ) {
+ Test.AnotherWay._g_no_record_msg="Input events recording and replaying is not available in internet explorer.";
+ }
+ if( Test.AnotherWay._g_no_record_msg!=null ) {
+ var no_record_p=document.getElementById( "record_not_supported" );
+ no_record_p.style.display="block";
+ no_record_p.appendChild( document.createTextNode( Test.AnotherWay._g_no_record_msg ) );
+ }
+
+ Test.AnotherWay._g_main_loaded=true;
+ if( Test.AnotherWay._g_run_on_main_load ) {
+ Test.AnotherWay._g_run_on_main_load=false;
+ Test.AnotherWay._run_pages_to_run();
+ }
+}
+Test.AnotherWay._test_object_t.prototype.open_window=null;
+// -->
+</script>
+<script type="text/javascript" src="xml_eq.js"></script>
+<script type="text/javascript" src="geom_eq.js"></script>
+</head><body>
+
+<div id="col1">
+<div id="col1_header">Test pages:</div>
+<div id="scroller">
+<table id="testtable">
+</table>
+</div>
+<div id="run_buttons">
+<input type="button" value=" clear " id="clear_btn" />
+<input type="button" value=" run all " id="run_all" />
+<input type="button" value=" run selected " id="run_selected" />
+<input type="button" value=" unselect all " id="unselect_all" />
+</div>
+<input type="checkbox" id="dont_close_test_windows" /> do not close windows opened by tests
+<div id="error"></div>
+<div id="record_div">
+<p id="record_not_supported" style="display:none"></p>
+<p>Record mouse input for the page:</p>
+<p><input type="radio" name="record_choose" value="select" checked="checked" /> <select id="record_select"><option selected="selected">-- select a page: --</option></select></p>
+<p><input type="radio" name="record_choose" value="input" /> or enter page url: <input type="text" id="record_input" /></p>
+<p><input type="button" value=" record " id="record_start" /></p>
+</div>
+</div>
+
+<div id="col2">
+<div id="right_header">
+<span id="results_count">Results: <span id="total"></span></span>
+<span id="results_tab" class="active_tab" style="visibility:hidden">Results</span>
+<span id="debug_tab" class="inactive_tab" style="visibility:hidden">Debug</span>
+</div>
+<div id="right_frame">
+<div id="results"></div>
+<div id="debug"></div>
+</div>
+</div>
+
+<span style="display:none">
+<iframe name="list_iframe" onload="Test.AnotherWay._list_iframe_onload();"></iframe>
+<iframe name="test_iframe" onload="Test.AnotherWay._test_page_onload();"></iframe>
+
+<!-- record_control div is to be imported into other documents, so all its styles are inline -->
+-<div id="record_control" style="position:absolute;bottom:0;left:0;margin:0;padding:0.5em;width:22em;height:22em;border:1px solid;background:#ffd;font: normal normal 8pt sans-serif; color:#000; text-align: left">
+
+<p style="margin:0 0 0 0; padding:0">
+&nbsp;
+<span style="display:none;font-weight:bold;color:#408" id="record_indicator">
+recording. <span style="font-weight:normal">time: <span id="record_time"></span></span><span id="record_pause_indicator"> paused</span>
+</span>
+</p>
+
+<div id="record_cursor_over" style="margin:0;padding:2px;width:14em;height:1.1em;overflow:hidden;float:right;border:1px solid #777;background:#fff;font: normal normal 8pt sans-serif;position:relative;top:3px;color:#000;text-align:left;">&nbsp;</div>
+<p style="margin:2px 0 0 0; padding:0">
+cursor is over
+</p>
+
+<p style="margin:8px 0 0 0; padding:0;">
+ keyboard control: press
+ <span id="record_ctrl_key" style="border:1px solid #226;background:#adf;padding:0 0.5em">ctrl</span> -
+ <span id="record_shift_key" style="border:1px solid #226;background:#adf;padding:0 0.5em">shift</span> -
+</p>
+
+<p style="margin:4px 0 0 0; padding:0">
+<span id="record_s" style="border:1px solid #226;background:#adf;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">s</span>
+<span id="record_on">to <b>start</b> recording</span>
+<span id="record_off" style="display:none">to <b>stop</b> recording</span>
+</p>
+
+<p style="margin:4px 0 0 0; padding:0">
+<span id="record_h" style="border:1px solid #226;background:#adf;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">h</span>
+<span>to <b>hide/show</b> this window</span>
+</p>
+
+<p style="margin:4px 0 0 0; padding:0">
+<span id="record_m" style="border:1px solid #226;background:#adf;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">m</span>
+<span id="record_include_mousemove">to <b> record</b> mousemove</span>
+<span id="record_omit_mousemove" style="display:none">to <b>omit</b> mousemove</span>
+</p>
+
+<p style="margin:4px 0 0 0; padding:0">
+<span id="record_p" style="border:1px solid #226;background:#aaa;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">p</span>
+<span id="record_pause_on">to <b> pause</b> recording</span>
+<span id="record_pause_off" style="display:none">to <b>continue</b> recording</span>
+</p>
+
+<p style="margin:4px 0 0 0; padding:0">
+<span id="record_c" style="border:1px solid #226;background:#aaa;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">c</span>
+<span>to add checkpoint</span>
+</p>
+
+<p style="margin:6px 0 0 0; padding:0">
+checkpoints:
+</p>
+<div id="record_checkpoints" style="position:relative;width:100%;height:6em;overflow:auto;font: normal normal 8pt sans-serif; color:#000; text-align: left">
+</div>
+</div>
+
+</span>
+</body></html>
diff --git a/misc/openlayers/tests/data/geos_wkt_intersects.js b/misc/openlayers/tests/data/geos_wkt_intersects.js
new file mode 100644
index 0000000..e0a355b
--- /dev/null
+++ b/misc/openlayers/tests/data/geos_wkt_intersects.js
@@ -0,0 +1,495 @@
+var geos_test_data = [
+{'wkt1':'POLYGON ((100 100,100 200,200 200,200 100,100 100))', 'wkt2':'POLYGON ((100 100,1000000000000000.0 110.0,1000000000000000.0 100.0,100 100))', result:true},
+{'wkt1':'POLYGON ((120 100,120 200,200 200,200 100,120 100))', 'wkt2':'POLYGON ((100 100,1000000000000000.0 110.0,1000000000000000.0 100.0,100 100))', result:true},
+{'wkt1':'POLYGON ((20 20,20 100,120 100,140 20,20 20))', 'wkt2':'POLYGON ((20 20,20 100,120 100,140 20,20 20))', result:true},
+{'wkt1':'POLYGON ((20 20,20 100,120 100,140 20,20 20))', 'wkt2':'POLYGON ((20 20,140 20,120 100,20 100,20 20))', result:true},
+{'wkt1':'POLYGON ((20 20,20 100,120 100,140 20,20 20))', 'wkt2':'POLYGON ((120 100,140 20,20 20,20 100,120 100))', result:true},
+{'wkt1':'POLYGON ((20 20,20 100,120 100,140 20,20 20))', 'wkt2':'POLYGON ((20 100,60 100,120 100,140 20,80 20,20 20,20 100))', result:true},
+{'wkt1':'POLYGON ((0 0,80 0,80 80,0 80,0 0))', 'wkt2':'POLYGON ((100 200,100 140,180 140,180 200,100 200))', result:false},
+{'wkt1':'POLYGON ((140 120,160 20,20 20,20 120,140 120))', 'wkt2':'POLYGON ((140 120,140 200,240 200,240 120,140 120))', result:true},
+{'wkt1':'POLYGON ((140 120,160 20,20 20,20 120,140 120))', 'wkt2':'POLYGON ((80 180,140 260,260 200,200 60,80 180))', result:true},
+{'wkt1':'POLYGON ((140 120,160 20,20 20,20 120,140 120))', 'wkt2':'POLYGON ((240 80,140 120,180 240,280 200,240 80))', result:true},
+{'wkt1':'POLYGON ((140 160,20 20,270 20,150 160,230 40,60 40,140 160))', 'wkt2':'POLYGON ((140 40,180 80,120 100,140 40))', result:true},
+{'wkt1':'POLYGON ((140 160,20 20,270 20,150 160,230 40,60 40,140 160))', 'wkt2':'POLYGON ((120 100,180 80,130 40,120 100))', result:true},
+{'wkt1':'POLYGON ((20 20,180 20,140 140,20 140,20 20))', 'wkt2':'POLYGON ((180 100,80 200,180 280,260 200,180 100))', result:true},
+{'wkt1':'POLYGON ((140 120,160 20,20 20,20 120,140 120))', 'wkt2':'POLYGON ((140 140,20 120,0 220,120 240,140 140))', result:true},
+{'wkt1':'POLYGON ((160 200,210 70,120 70,160 200))', 'wkt2':'POLYGON ((160 200,260 40,70 40,160 200,20 20,310 20,160 200))', result:true},
+{'wkt1':'POLYGON ((110 140,200 70,200 160,110 140))', 'wkt2':'POLYGON ((110 140,110 50,60 50,60 90,160 190,20 110,20 20,200 20,110 140))', result:true},
+{'wkt1':'POLYGON ((20 120,20 20,260 20,260 120,200 40,140 120,80 40,20 120))', 'wkt2':'POLYGON ((20 120,20 240,260 240,260 120,200 200,140 120,80 200,20 120))', result:true},
+{'wkt1':'POLYGON ((20 120,20 20,260 20,260 120,180 40,140 120,100 40,20 120))', 'wkt2':'POLYGON ((20 120,300 120,140 240,20 120))', result:true},
+{'wkt1':'POLYGON ((20 20,20 300,280 300,280 260,220 260,60 100,60 60,280 60,280 20,20 20))', 'wkt2':'POLYGON ((100 140,160 80,280 180,200 240,220 160,160 200,180 120,100 140))', result:true},
+{'wkt1':'POLYGON ((20 20,20 300,280 300,280 260,220 260,60 100,60 60,280 60,280 20,20 20))', 'wkt2':'POLYGON ((260 200,180 80,120 160,200 160,180 220,260 200))', result:true},
+{'wkt1':'POLYGON ((20 20,280 20,280 140,220 60,140 140,80 60,20 140,20 20))', 'wkt2':'POLYGON ((0 140,300 140,140 240,0 140))', result:true},
+{'wkt1':'POLYGON ((20 20,280 20,280 140,220 60,140 140,80 60,20 140,20 20))', 'wkt2':'POLYGON ((20 240,20 140,320 140,180 240,20 240))', result:true},
+{'wkt1':'POLYGON ((20 20,280 20,280 140,220 60,140 140,80 60,20 140,20 20))', 'wkt2':'POLYGON ((20 240,20 140,80 180,140 140,220 180,280 140,280 240,20 240))', result:true},
+{'wkt1':'POLYGON ((120 120,180 60,20 20,20 120,120 120))', 'wkt2':'POLYGON ((120 120,220 20,280 20,240 160,120 120))', result:true},
+{'wkt1':'POLYGON ((140 120,160 20,20 20,20 120,140 120))', 'wkt2':'POLYGON ((140 120,160 20,260 120,220 200,140 120))', result:true},
+{'wkt1':'POLYGON ((20 140,120 40,20 40,20 140))', 'wkt2':'POLYGON ((190 140,190 20,140 20,20 140,190 140))', result:true},
+{'wkt1':'POLYGON ((120 120,180 60,20 20,20 120,120 120))', 'wkt2':'POLYGON ((300 20,220 20,120 120,260 160,300 20))', result:true},
+{'wkt1':'POLYGON ((140 120,160 20,20 20,20 120,140 120))', 'wkt2':'POLYGON ((140 120,240 160,280 60,160 20,140 120))', result:true},
+{'wkt1':'POLYGON ((120 120,180 60,20 20,20 120,120 120))', 'wkt2':'POLYGON ((280 60,180 60,120 120,260 180,280 60))', result:true},
+{'wkt1':'POLYGON ((140 120,160 20,20 20,20 120,140 120))', 'wkt2':'POLYGON ((120 200,120 120,40 120,40 200,120 200))', result:true},
+{'wkt1':'POLYGON ((140 120,160 20,20 20,20 120,140 120))', 'wkt2':'POLYGON ((160 220,140 120,60 120,40 220,160 220))', result:true},
+{'wkt1':'POLYGON ((140 120,160 20,20 20,20 120,140 120))', 'wkt2':'POLYGON ((140 120,20 120,20 220,140 220,140 120))', result:true},
+{'wkt1':'POLYGON ((120 120,180 60,20 20,20 120,120 120))', 'wkt2':'POLYGON ((320 20,220 20,80 160,240 140,320 20))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((60 40,60 140,180 140,180 40,60 40))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((20 20,80 140,160 60,20 20))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((160 60,20 20,100 140,160 60))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((20 100,140 160,160 40,20 100))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((160 40,20 100,160 160,160 40))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((20 180,180 120,80 40,20 180))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((180 120,100 40,20 180,180 120))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((20 20,140 40,140 120,20 160,80 80,20 20))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((20 20,140 40,140 140,20 180,80 100,20 20))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((40 180,60 100,180 100,200 180,120 120,40 180))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((20 180,60 80,180 80,220 180,120 120,20 180))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((40 60,20 180,100 100,140 180,160 120,220 100,140 40,40 60))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((60 100,180 100,220 180,120 140,20 180,60 100))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((20 20,20 140,120 120,120 40,20 20))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((20 20,20 180,140 140,140 60,20 20))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((20 20,120 40,120 120,20 140,20 20))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((120 40,20 20,20 140,120 120,120 40))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((20 20,140 60,140 140,20 180,20 20))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((140 60,20 20,20 180,140 140,140 60))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((20 20,60 120,140 120,180 20,20 20))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((20 40,120 40,120 120,20 140,20 40))', result:true},
+{'wkt1':'POLYGON ((20 20,20 180,220 180,220 20,20 20))', 'wkt2':'POLYGON ((20 20,20 180,60 120,100 180,140 120,220 180,200 120,140 60,20 20))', result:true},
+{'wkt1':'POLYGON ((150 150,330 150,250 70,70 70,150 150))', 'wkt2':'POLYGON ((150 150,270 150,140 20,20 20,150 150))', result:true},
+{'wkt1':'POLYGON ((150 150,270 150,330 150,250 70,190 70,70 70,150 150))', 'wkt2':'POLYGON ((150 150,270 150,190 70,140 20,20 20,70 70,150 150))', result:true},
+{'wkt1':'POLYGON ((20 20,60 50,20 40,60 70,20 60,60 90,20 90,70 110,20 130,80 130,20 150,80 160,20 170,80 180,20 200,80 200,30 240,80 220,50 260,100 220,100 260,120 220,130 260,140 220,150 280,150 190,160 280,170 190,180 280,190 190,200 280,210 190,220 280,230 190,240 260,250 230,260 260,260 220,290 270,290 220,330 260,300 210,340 240,290 180,340 210,290 170,350 170,240 150,350 150,240 140,350 130,240 120,350 120,240 110,350 110,240 100,350 100,240 90,350 90,240 80,350 80,300 70,340 60,290 60,340 40,300 50,340 20,270 60,310 20,250 60,270 20,230 60,240 20,210 60,210 20,190 70,190 20,180 90,170 20,160 90,150 20,140 90,130 20,120 90,110 20,100 90,100 20,90 60,80 20,70 40,20 20))', 'wkt2':'POLYGON ((190 140,140 130,200 160,130 150,210 170,130 170,210 180,120 190,220 200,120 200,250 210,120 210,250 220,120 220,250 230,120 240,230 240,120 250,240 260,120 260,240 270,120 270,270 290,120 290,230 300,150 310,250 310,180 320,250 320,200 360,260 330,240 360,280 320,290 370,290 320,320 360,310 320,360 360,310 310,380 340,310 290,390 330,310 280,410 310,310 270,420 280,310 260,430 250,300 250,440 240,300 240,450 230,280 220,440 220,280 210,440 210,300 200,430 190,300 190,440 180,330 180,430 150,320 180,420 130,300 180,410 120,280 180,400 110,280 170,390 90,280 160,400 70,270 160,450 30,260 160,420 30,250 160,390 30,240 160,370 30,230 160,360 30,230 150,330 50,240 130,330 30,230 130,310 30,220 130,280 30,230 100,270 40,220 110,250 30,210 130,240 30,210 100,220 40,200 90,200 20,190 100,180 30,20 20,180 40,20 30,180 50,20 50,180 60,30 60,180 70,20 70,170 80,80 80,170 90,20 80,180 100,40 100,200 110,60 110,200 120,120 120,190 140))', result:true},
+{'wkt1':'POLYGON ((70 150,20 160,110 160,20 180,100 200,20 200,190 210,20 210,160 220,20 220,150 230,60 240,180 250,20 260,170 260,60 270,160 270,100 310,170 280,200 260,180 230,210 260,130 330,230 250,210 290,240 250,230 210,260 300,250 230,270 300,270 240,300 340,280 250,320 330,290 250,340 350,290 240,350 360,270 190,350 340,290 200,350 330,300 190,360 320,310 190,360 300,320 200,360 280,330 200,360 260,340 200,370 260,340 180,390 290,340 170,400 260,350 170,400 250,350 160,410 240,350 150,400 170,350 140,310 170,340 140,270 180,330 140,260 170,310 140,240 170,290 140,200 190,270 140,180 190,260 140,170 190,260 130,170 180,250 130,170 170,240 120,170 160,210 120,170 150,210 110,340 130,230 110,420 140,220 100,410 130,220 90,400 120,220 80,390 110,220 70,420 110,240 70,420 100,260 70,420 90,280 70,430 80,230 60,430 60,270 50,450 40,210 50,370 40,260 40,460 30,160 40,210 60,200 110,190 60,190 120,170 50,180 130,150 30,170 130,140 20,160 120,130 20,160 150,120 20,160 170,110 20,160 190,100 20,150 190,90 20,140 180,80 20,120 140,70 20,120 150,60 20,110 150,50 20,100 140,50 30,90 130,40 30,80 120,30 30,80 130,30 40,80 140,20 40,70 140,40 90,60 130,20 90,60 140,20 130,70 150))', 'wkt2':'POLYGON ((190 140,140 130,200 160,130 150,210 170,130 170,210 180,120 190,220 200,120 200,250 210,120 210,250 220,120 220,250 230,120 240,230 240,120 250,240 260,120 260,240 270,120 270,270 290,120 290,230 300,150 310,250 310,180 320,250 320,200 360,260 330,240 360,280 320,290 370,290 320,320 360,310 320,360 360,310 310,380 340,310 290,390 330,310 280,410 310,310 270,420 280,310 260,430 250,300 250,440 240,300 240,450 230,280 220,440 220,280 210,440 210,300 200,430 190,300 190,440 180,330 180,430 150,320 180,420 130,300 180,410 120,280 180,400 110,280 170,390 90,280 160,400 70,270 160,450 30,260 160,420 30,250 160,390 30,240 160,370 30,230 160,360 30,230 150,330 50,240 130,330 30,230 130,310 30,220 130,280 30,230 100,270 40,220 110,250 30,210 130,240 30,210 100,220 40,200 90,200 20,190 100,180 30,20 20,180 40,20 30,180 50,20 50,180 60,30 60,180 70,20 70,170 80,80 80,170 90,20 80,180 100,40 100,200 110,60 110,200 120,120 120,190 140))', result:true},
+{'wkt1':'POLYGON ((60 160,220 160,220 20,60 20,60 160))', 'wkt2':'POLYGON ((60 160,20 200,260 200,220 160,140 80,60 160))', result:true},
+{'wkt1':'POLYGON ((60 160,220 160,220 20,60 20,60 160))', 'wkt2':'POLYGON ((60 160,20 200,260 200,140 80,60 160))', result:true},
+{'wkt1':'POLYGON ((60 160,220 160,220 20,60 20,60 160))', 'wkt2':'POLYGON ((20 200,140 80,260 200,20 200))', result:true},
+{'wkt1':'POLYGON ((60 160,220 160,220 20,60 20,60 160))', 'wkt2':'POLYGON ((20 200,60 160,140 80,220 160,260 200,20 200))', result:true},
+{'wkt1':'POLYGON ((60 160,220 160,220 20,60 20,60 160))', 'wkt2':'POLYGON ((20 200,60 160,140 80,260 200,20 200))', result:true},
+{'wkt1':'POLYGON ((0 0,0 200,200 200,200 0,0 0))', 'wkt2':'POLYGON ((100 100,1000000 110,10000000 100,100 100))', result:true},
+{'wkt1':'POLYGON ((100 0,100 200,200 200,200 0,100 0))', 'wkt2':'POLYGON ((100 100,1000000 110,10000000 100,100 100))', result:true},
+{'wkt1':'POLYGON ((120 0,120 200,200 200,200 0,120 0))', 'wkt2':'POLYGON ((100 100,1000000 110,10000000 100,100 100))', result:true},
+{'wkt1':'POLYGON ((0 0,0 200,110 200,110 0,0 0))', 'wkt2':'POLYGON ((100 100,1000000 110,10000000 100,100 100))', result:true},
+{'wkt1':'POLYGON ((100 100,100 200,200 200,200 100,100 100))', 'wkt2':'POLYGON ((100 100,2100 110,2100 100,100 100))', result:true},
+{'wkt1':'POLYGON ((100 100,100 200,200 200,200 100,100 100))', 'wkt2':'POLYGON ((100 100,2101 110,2101 100,100 100))', result:true},
+{'wkt1':'POLYGON ((100 100,200 200,200 100,100 100))', 'wkt2':'POLYGON ((100 100,2101 110,2101 100,100 100))', result:true},
+{'wkt1':'POLYGON ((100 100,100 200,200 200,200 100,100 100))', 'wkt2':'POLYGON ((100 100,1000000 110,1000000 100,100 100))', result:true},
+{'wkt1':'POLYGON ((120 100,120 200,200 200,200 100,120 100))', 'wkt2':'POLYGON ((100 100,500 110,500 100,100 100))', result:true},
+{'wkt1':'POLYGON ((120 100,120 200,200 200,200 100,120 100))', 'wkt2':'POLYGON ((100 100,501 110,501 100,100 100))', result:true},
+{'wkt1':'POLYGON ((120 100,130 200,200 200,200 100,120 100))', 'wkt2':'POLYGON ((100 100,501 110,501 100,100 100))', result:true},
+{'wkt1':'POLYGON ((120 100,17 200,200 200,200 100,120 100))', 'wkt2':'POLYGON ((100 100,501 110,501 100,100 100))', result:true},
+{'wkt1':'POLYGON ((120 100,120 200,200 200,200 100,120 100))', 'wkt2':'POLYGON ((100 100,1000000 110,1000000 100,100 100))', result:true},
+{'wkt1':'POLYGON ((101 99,101 1000000,102 1000000,101 99))', 'wkt2':'POLYGON ((100 100,1000000 110,1000000 100,100 100))', result:true},
+{'wkt1':'POLYGON ((100 100,200 101,200 100,100 100))', 'wkt2':'POLYGON ((100 100,2101 110,2101 100,100 100))', result:true},
+{'wkt1':'POLYGON ((16 319,150 39,25 302,160 20,265 20,127 317,16 319))', 'wkt2':'POLYGON ((10 307,22 307,153 34,22 34,10 307))', result:true},
+{'wkt1':'POLYGON ((160 200,210 70,120 70,160 200))', 'wkt2':'POLYGON ((160 200,310 20,20 20,160 200),(160 200,260 40,70 40,160 200))', result:true},
+{'wkt1':'POLYGON ((170 120,240 100,260 50,190 70,170 120))', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,330 120,260 50,100 50,170 120))', result:true},
+{'wkt1':'POLYGON ((270 90,200 50,150 80,210 120,270 90))', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,330 120,260 50,100 50,170 120))', result:true},
+{'wkt1':'POLYGON ((170 120,260 100,240 60,150 80,170 120))', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,330 120,260 50,100 50,170 120))', result:true},
+{'wkt1':'POLYGON ((220 120,270 80,200 60,160 100,220 120))', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,330 120,260 50,100 50,170 120))', result:true},
+{'wkt1':'POLYGON ((260 50,180 70,180 110,260 90,260 50))', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,330 120,260 50,100 50,170 120))', result:true},
+{'wkt1':'POLYGON ((230 110,290 80,190 60,140 90,230 110))', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,330 120,260 50,100 50,170 120))', result:true},
+{'wkt1':'POLYGON ((170 120,330 120,260 50,100 50,170 120))', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,330 120,260 50,100 50,170 120))', result:true},
+{'wkt1':'POLYGON ((170 120,330 120,280 70,120 70,170 120))', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,330 120,260 50,100 50,170 120))', result:true},
+{'wkt1':'POLYGON ((170 120,300 120,250 70,120 70,170 120))', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,330 120,260 50,100 50,170 120))', result:true},
+{'wkt1':'POLYGON ((190 100,310 100,260 50,140 50,190 100))', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,330 120,260 50,100 50,170 120))', result:true},
+{'wkt1':'POLYGON ((280 130,360 130,270 40,190 40,280 130))', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,250 120,180 50,100 50,170 120))', result:true},
+{'wkt1':'POLYGON ((220 80,180 40,80 40,170 130,270 130,230 90,300 90,250 30,280 30,390 140,150 140,40 30,230 30,280 80,220 80))', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,250 120,180 50,100 50,170 120))', result:true},
+{'wkt1':'POLYGON ((260 130,360 130,280 40,170 40,260 130))', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,250 120,180 50,100 50,170 120))', result:true},
+{'wkt1':'POLYGON ((240 110,340 110,290 60,190 60,240 110))', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,250 120,180 50,100 50,170 120))', result:true},
+{'wkt1':'POLYGON ((250 120,350 120,280 50,180 50,250 120))', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,250 120,180 50,100 50,170 120))', result:true},
+{'wkt1':'POLYGON ((230 210,230 20,20 20,20 210,230 210),(120 180,50 50,200 50,120 180))', 'wkt2':'POLYGON ((230 210,230 20,20 20,20 210,230 210),(120 180,50 50,200 50,120 180))', result:true},
+{'wkt1':'POLYGON ((230 210,230 20,20 20,20 210,230 210),(140 40,40 40,40 170,140 40),(110 190,210 190,210 50,110 190))', 'wkt2':'POLYGON ((230 210,230 20,20 20,20 210,230 210),(140 40,40 40,40 170,140 40),(110 190,210 190,210 50,110 190))', result:true},
+{'wkt1':'POLYGON ((280 190,330 150,200 110,150 150,280 190))', 'wkt2':'MULTIPOLYGON (((140 110,260 110,170 20,50 20,140 110)),((300 270,420 270,340 190,220 190,300 270)))', result:true},
+{'wkt1':'POLYGON ((80 190,220 190,140 110,0 110,80 190))', 'wkt2':'MULTIPOLYGON (((140 110,260 110,170 20,50 20,140 110)),((300 270,420 270,340 190,220 190,300 270)))', result:true},
+{'wkt1':'POLYGON ((330 150,200 110,150 150,280 190,330 150))', 'wkt2':'MULTIPOLYGON (((140 110,260 110,170 20,50 20,140 110)),((300 270,420 270,340 190,220 190,300 270)))', result:true},
+{'wkt1':'POLYGON ((290 190,340 150,220 120,170 170,290 190))', 'wkt2':'MULTIPOLYGON (((140 110,260 110,170 20,50 20,140 110)),((300 270,420 270,340 190,220 190,300 270)))', result:true},
+{'wkt1':'POLYGON ((220 190,340 190,260 110,140 110,220 190))', 'wkt2':'MULTIPOLYGON (((140 110,260 110,170 20,50 20,140 110)),((300 270,420 270,340 190,220 190,300 270)))', result:true},
+{'wkt1':'POLYGON ((140 190,220 190,100 70,20 70,140 190))', 'wkt2':'MULTIPOLYGON (((140 110,260 110,170 20,50 20,140 110)),((300 270,420 270,340 190,220 190,300 270)))', result:true},
+{'wkt1':'POLYGON ((140 220,60 140,140 60,220 140,140 220))', 'wkt2':'MULTIPOLYGON (((100 20,180 20,180 100,100 100,100 20)),((20 100,100 100,100 180,20 180,20 100)),((100 180,180 180,180 260,100 260,100 180)),((180 100,260 100,260 180,180 180,180 100)))', result:true},
+{'wkt1':'MULTIPOLYGON (((110 110,70 200,150 200,110 110)),((110 110,150 20,70 20,110 110)))', 'wkt2':'MULTIPOLYGON (((110 110,160 160,210 110,160 60,110 110)),((110 110,60 60,10 110,60 160,110 110)))', result:true},
+{'wkt1':'MULTIPOLYGON (((110 110,70 200,150 200,110 110),(110 110,100 180,120 180,110 110)),((110 110,150 20,70 20,110 110),(110 110,120 40,100 40,110 110)))', 'wkt2':'MULTIPOLYGON (((110 110,160 160,210 110,160 60,110 110),(110 110,160 130,160 90,110 110)),((110 110,60 60,10 110,60 160,110 110),(110 110,60 90,60 130,110 110)))', result:true},
+{'wkt1':'MULTIPOLYGON (((110 110,70 200,200 200,110 110),(110 110,100 180,120 180,110 110)),((110 110,200 20,70 20,110 110),(110 110,120 40,100 40,110 110)))', 'wkt2':'MULTIPOLYGON (((110 110,160 160,210 110,160 60,110 110),(110 110,160 130,160 90,110 110)),((110 110,60 60,10 110,60 160,110 110),(110 110,60 90,60 130,110 110)))', result:true},
+{'wkt1':'MULTIPOLYGON (((110 110,20 200,200 200,110 110),(110 110,100 180,120 180,110 110)),((110 110,200 20,20 20,110 110),(110 110,120 40,100 40,110 110)))', 'wkt2':'MULTIPOLYGON (((110 110,160 160,210 110,160 60,110 110),(110 110,160 130,160 90,110 110)),((110 110,60 60,10 110,60 160,110 110),(110 110,60 90,60 130,110 110)))', result:true},
+{'wkt1':'MULTIPOLYGON (((110 110,70 200,200 200,110 110),(110 110,100 180,120 180,110 110)),((110 110,200 20,70 20,110 110),(110 110,120 40,100 40,110 110)))', 'wkt2':'MULTIPOLYGON (((110 110,160 160,210 110,160 60,110 110),(110 110,160 130,160 90,110 110)),((110 110,60 60,10 110,60 160,110 110),(110 110,60 90,60 130,110 110)))', result:true},
+{'wkt1':'MULTIPOLYGON (((110 110,70 200,200 200,110 110),(110 110,100 180,120 180,110 110)),((110 110,200 20,70 20,110 110),(110 110,120 40,100 40,110 110)))', 'wkt2':'MULTIPOLYGON (((110 110,70 200,210 110,70 20,110 110),(110 110,110 140,150 110,110 80,110 110)),((110 110,60 60,10 110,60 160,110 110),(110 110,60 90,60 130,110 110)))', result:true},
+{'wkt1':'POLYGON ((100 60,140 100,100 140,60 100,100 60))', 'wkt2':'MULTIPOLYGON (((80 40,120 40,120 80,80 80,80 40)),((120 80,160 80,160 120,120 120,120 80)),((80 120,120 120,120 160,80 160,80 120)),((40 80,80 80,80 120,40 120,40 80)))', result:true},
+{'wkt1':'LINESTRING (150 150,40 230)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (40 40,50 130,130 130)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (40 230,150 150)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (210 150,330 150)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (200 150,310 150,360 220)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (180 150,250 150,230 250,370 250,410 150)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (210 210,220 150,320 150,370 210)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (20 60,150 60)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (60 90,310 180)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (90 210,210 90)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (290 10,130 170)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (30 100,100 100,180 100)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (20 100,100 100,360 100,410 100)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (90 210,150 150,210 90)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (180 90,280 120)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (70 70,80 20)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (130 20,150 60)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (70 70,80 20,140 20,150 60)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (170 50,170 20,240 20,260 60)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150))', result:true},
+{'wkt1':'LINESTRING (50 100,140 190,280 190)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,330 120,260 50,100 50,170 120))', result:false},
+{'wkt1':'LINESTRING (140 60,180 100,290 100)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,330 120,260 50,100 50,170 120))', result:false},
+{'wkt1':'LINESTRING (170 120,210 80,270 80)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,330 120,260 50,100 50,170 120))', result:true},
+{'wkt1':'LINESTRING (170 120,260 50)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,330 120,260 50,100 50,170 120))', result:true},
+{'wkt1':'LINESTRING (190 90,190 270)', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(190 190,280 50,100 50,190 190))', result:true},
+{'wkt1':'LINESTRING (60 160,150 70)', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(110 110,250 100,140 30,110 110))', result:true},
+{'wkt1':'LINESTRING (60 160,150 70)', 'wkt2':'POLYGON ((190 190,20 20,360 20,190 190),(250 100,110 110,140 30,250 100))', result:true},
+{'wkt1':'LINESTRING (60 160,150 70)', 'wkt2':'POLYGON ((190 190,20 20,360 20,190 190),(250 100,110 110,140 30,250 100))', result:true},
+{'wkt1':'LINESTRING (190 90,190 190,190 270)', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(190 190,280 50,100 50,190 190))', result:true},
+{'wkt1':'LINESTRING (60 160,110 110,150 70)', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(110 110,250 100,140 30,110 110))', result:true},
+{'wkt1':'LINESTRING (60 160,110 110,150 70)', 'wkt2':'POLYGON ((190 190,20 20,360 20,190 190),(250 100,110 110,140 30,250 100))', result:true},
+{'wkt1':'LINESTRING (60 160,110 110,150 70)', 'wkt2':'POLYGON ((190 190,110 110,20 20,360 20,190 190),(250 100,110 110,140 30,250 100))', result:true},
+{'wkt1':'LINESTRING (130 110,180 110,190 60)', 'wkt2':'POLYGON ((20 200,240 200,240 20,20 20,20 200),(130 110,60 180,60 40,130 110),(130 110,200 40,200 180,130 110))', result:true},
+{'wkt1':'LINESTRING (80 110,180 110)', 'wkt2':'POLYGON ((20 200,240 200,240 20,20 20,20 200),(130 110,60 180,60 40,130 110),(130 110,200 40,200 180,130 110))', result:true},
+{'wkt1':'LINESTRING (80 110,180 110)', 'wkt2':'POLYGON ((20 200,20 20,240 20,240 200,20 200),(60 180,130 110,60 40,60 180),(130 110,200 40,200 180,130 110))', result:true},
+{'wkt1':'LINESTRING (80 110,170 110)', 'wkt2':'POLYGON ((20 200,20 20,240 20,240 200,20 200),(130 110,60 40,60 180,130 110),(130 180,130 40,200 110,130 180))', result:true},
+{'wkt1':'LINESTRING (80 110,130 110,170 110)', 'wkt2':'POLYGON ((20 200,20 20,240 20,240 200,20 200),(130 110,60 40,60 180,130 110),(130 180,130 40,200 110,130 180))', result:true},
+{'wkt1':'LINESTRING (80 110,130 110,180 110)', 'wkt2':'POLYGON ((20 200,240 200,240 20,20 20,20 200),(130 110,60 180,60 40,130 110),(130 110,200 40,200 180,130 110))', result:true},
+{'wkt1':'LINESTRING (80 110,130 110,180 110)', 'wkt2':'POLYGON ((20 200,20 20,240 20,240 200,20 200),(60 180,130 110,60 40,60 180),(130 110,200 40,200 180,130 110))', result:true},
+{'wkt1':'LINESTRING (80 110,130 110,170 110)', 'wkt2':'POLYGON ((20 200,20 20,240 20,240 200,20 200),(130 110,60 40,60 180,130 110),(130 180,130 40,200 110,130 180))', result:true},
+{'wkt1':'LINESTRING (160 70,320 230)', 'wkt2':'MULTIPOLYGON (((140 110,260 110,170 20,50 20,140 110)),((300 270,420 270,340 190,220 190,300 270)))', result:true},
+{'wkt1':'LINESTRING (160 70,200 110,280 190,320 230)', 'wkt2':'MULTIPOLYGON (((140 110,260 110,170 20,50 20,140 110)),((300 270,420 270,340 190,220 190,300 270)))', result:true},
+{'wkt1':'LINESTRING (70 50,70 150)', 'wkt2':'MULTIPOLYGON (((0 0,0 100,140 100,140 0,0 0)),((20 170,70 100,130 170,20 170)))', result:true},
+{'wkt1':'LINESTRING (110 110,20 200,200 200,110 110)', 'wkt2':'POLYGON ((20 20,200 20,110 110,20 20))', result:true},
+{'wkt1':'LINESTRING (150 70,160 110,200 60,150 70)', 'wkt2':'POLYGON ((20 20,200 20,110 110,20 20))', result:true},
+{'wkt1':'LINESTRING (80 60,120 40,120 70,80 60)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110),(110 90,50 30,170 30,110 90))', result:true},
+{'wkt1':'LINESTRING (20 20,200 20,110 110,20 20)', 'wkt2':'POLYGON ((20 20,200 20,110 110,20 20))', result:true},
+{'wkt1':'LINESTRING (110 90,170 30,50 30,110 90)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110),(110 90,50 30,170 30,110 90))', result:true},
+{'wkt1':'LINESTRING (110 110,170 50,170 110,110 110)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110),(110 90,50 30,170 30,110 90))', result:true},
+{'wkt1':'LINESTRING (110 90,70 50,130 50,110 90)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110),(110 90,50 30,170 30,110 90))', result:true},
+{'wkt1':'LINESTRING (110 60,20 150,200 150,110 60)', 'wkt2':'POLYGON ((20 20,200 20,110 110,20 20))', result:true},
+{'wkt1':'LINESTRING (110 130,110 70,200 100,110 130)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110),(110 90,50 30,170 30,110 90))', result:true},
+{'wkt1':'LINESTRING (110 90,160 40,60 40,110 90)', 'wkt2':'POLYGON ((20 20,200 20,110 110,20 20))', result:true},
+{'wkt1':'LINESTRING (110 100,40 30,180 30,110 100)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110),(110 90,60 40,160 40,110 90))', result:true},
+{'wkt1':'LINESTRING (110 110,180 30,40 30,110 110)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110),(110 90,60 40,160 40,110 90))', result:true},
+{'wkt1':'LINESTRING (110 90,180 30,40 30,110 90)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110),(110 90,60 40,160 40,110 90))', result:true},
+{'wkt1':'LINESTRING (110 90,50 30,180 30,110 90)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110),(110 90,60 40,160 40,110 90))', result:true},
+{'wkt1':'LINESTRING (110 110,200 200,200 110,110 200)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'LINESTRING (110 110,200 200,110 110,20 200,20 110,200 110)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'LINESTRING (110 110,20 110,200 110,50 110,110 170)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'LINESTRING (110 110,20 200,110 200,110 110,200 200)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'LINESTRING (110 110,170 50,20 200,20 110,200 110)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'LINESTRING (110 110,180 40,110 40,110 180)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'LINESTRING (110 60,50 30,170 30,90 70)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'LINESTRING (110 110,180 40,110 40,110 110,70 40)', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'LINESTRING (230 70,170 120,190 60,140 60,170 120,270 90)', 'wkt2':'POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,330 120,260 50,100 50,170 120))', result:true},
+{'wkt1':'MULTILINESTRING ((20 110,200 110),(200 200,110 110,20 210,110 110))', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'MULTILINESTRING ((20 110,200 110),(60 180,60 110,160 110,110 110))', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'MULTILINESTRING ((20 110,200 110),(200 200,110 110,20 200,110 200,110 110))', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'MULTILINESTRING ((20 110,200 110),(110 50,110 170,110 70,110 150,200 150))', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'MULTILINESTRING ((20 110,200 110),(50 110,170 110,110 170,110 50,110 170,110 50))', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'MULTILINESTRING ((20 110,200 110),(110 60,110 160,200 160))', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'MULTILINESTRING ((20 110,200 110),(110 60,110 160,200 160))', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'MULTILINESTRING ((110 100,40 30,180 30),(170 30,110 90,50 30))', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'MULTILINESTRING ((110 110,60 40,70 20,150 20,170 40),(180 30,40 30,110 80))', 'wkt2':'POLYGON ((110 110,200 20,20 20,110 110))', result:true},
+{'wkt1':'MULTILINESTRING ((20 110,200 110,200 160),(110 110,200 110,200 70,20 150))', 'wkt2':'MULTIPOLYGON (((110 110,20 20,200 20,110 110)),((110 110,20 200,200 200,110 110)))', result:true},
+{'wkt1':'MULTILINESTRING ((20 160,70 110,150 110,200 160),(110 110,20 110,50 80,70 110,200 110))', 'wkt2':'MULTIPOLYGON (((110 110,20 20,200 20,110 110)),((110 110,20 200,200 200,110 110)))', result:true},
+{'wkt1':'MULTILINESTRING ((20 110,200 110),(110 110,20 170,20 130,200 90))', 'wkt2':'MULTIPOLYGON (((110 110,20 20,200 20,110 110)),((110 110,20 200,200 200,110 110)))', result:true},
+{'wkt1':'LINESTRING (0 0,0 50,50 50,50 0,0 0)', 'wkt2':'MULTILINESTRING ((0 0,0 50),(0 50,50 50),(50 50,50 0),(50 0,0 0))', result:true},
+{'wkt1':'LINESTRING (40 180,140 180)', 'wkt2':'MULTIPOLYGON (((20 320,180 320,180 180,20 180,20 320)),((20 180,20 80,180 80,180 180,20 180)))', result:true},
+{'wkt1':'LINESTRING (40 180,140 180)', 'wkt2':'MULTIPOLYGON (((20 320,180 320,180 180,20 180,20 320)),((60 180,60 80,180 80,180 180,60 180)))', result:true},
+{'wkt1':'LINESTRING (0 0,60 0,60 60,60 0,120 0)', 'wkt2':'MULTILINESTRING ((0 0,60 0),(60 0,120 0),(60 0,60 60))', result:true},
+{'wkt1':'LINESTRING (40 40,120 120)', 'wkt2':'LINESTRING (40 40,60 120)', result:true},
+{'wkt1':'LINESTRING (40 40,120 120)', 'wkt2':'LINESTRING (60 240,40 40)', result:true},
+{'wkt1':'LINESTRING (40 40,180 180)', 'wkt2':'LINESTRING (120 120,20 200)', result:true},
+{'wkt1':'LINESTRING (40 40,120 120)', 'wkt2':'LINESTRING (60 240,120 120)', result:true},
+{'wkt1':'LINESTRING (40 40,180 180)', 'wkt2':'LINESTRING (20 180,140 140)', result:true},
+{'wkt1':'LINESTRING (40 40,120 120)', 'wkt2':'LINESTRING (40 120,120 40)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100)', 'wkt2':'LINESTRING (40 40,100 100)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100)', 'wkt2':'LINESTRING (100 100,40 40)', result:true},
+{'wkt1':'LINESTRING (40 40,120 120)', 'wkt2':'LINESTRING (40 120,120 160)', result:false},
+{'wkt1':'LINESTRING (20 20,180 180)', 'wkt2':'LINESTRING (20 20,180 180)', result:true},
+{'wkt1':'LINESTRING (20 20,180 180)', 'wkt2':'LINESTRING (20 20,110 110)', result:true},
+{'wkt1':'LINESTRING (20 20,180 180)', 'wkt2':'LINESTRING (50 50,140 140)', result:true},
+{'wkt1':'LINESTRING (180 180,40 40)', 'wkt2':'LINESTRING (120 120,260 260)', result:true},
+{'wkt1':'LINESTRING (40 40,180 180)', 'wkt2':'LINESTRING (260 260,120 120)', result:true},
+{'wkt1':'LINESTRING (40 40,180 180)', 'wkt2':'LINESTRING (120 120,260 260)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (40 40,20 100,40 160,20 200)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (20 200,40 160,20 100,40 40)', result:true},
+{'wkt1':'LINESTRING (80 240,200 120,100 100,40 40)', 'wkt2':'LINESTRING (20 200,40 160,20 100,40 40)', result:true},
+{'wkt1':'LINESTRING (60 60,60 230,140 230,250 160)', 'wkt2':'LINESTRING (20 20,60 60,250 160,310 230)', result:true},
+{'wkt1':'LINESTRING (60 60,60 230,140 230,250 160)', 'wkt2':'LINESTRING (20 20,110 110,200 110,320 230)', result:true},
+{'wkt1':'LINESTRING (60 110,60 250,360 210)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (60 110,60 250,360 210)', 'wkt2':'LINESTRING (360 210,310 160,110 160,60 110)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (160 160,240 240)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (240 240,160 160)', result:true},
+{'wkt1':'LINESTRING (60 60,60 230,140 230,250 160)', 'wkt2':'LINESTRING (60 150,110 100,170 100,110 230)', result:true},
+{'wkt1':'LINESTRING (60 60,60 230,140 230,250 160)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (200 120,200 190,150 240,200 240)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (200 240,150 240,200 200,200 120)', result:true},
+{'wkt1':'LINESTRING (60 60,60 230,140 230,250 160)', 'wkt2':'LINESTRING (60 230,80 140,120 140,140 230)', result:true},
+{'wkt1':'LINESTRING (60 110,200 110,250 160,300 210)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (60 110,200 110,250 160,300 210,360 210)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (60 110,220 110,250 160,280 110)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (60 110,150 110,200 160,250 110,360 110,360 210)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (130 160,160 110,220 110,250 160,250 210)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (130 160,160 110,190 110,230 210)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (130 160,160 110,200 110,230 160,260 210,360 210)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (130 160,160 110,200 110,230 160,260 210,360 210,380 210)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (130 160,160 110,200 110,230 160,260 210,380 210)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (110 160,160 110,200 110,250 160,250 210)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (110 160,180 110,250 160,320 110)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (140 160,180 80,220 160,250 80)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,130 190)', 'wkt2':'LINESTRING (20 130,70 130,160 40)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,130 190)', 'wkt2':'LINESTRING (40 160,40 100,110 40,170 40)', result:true},
+{'wkt1':'LINESTRING (130 110,180 160,230 110,280 160,330 110)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,130 190)', 'wkt2':'LINESTRING (30 140,80 140,100 100,200 30)', result:true},
+{'wkt1':'LINESTRING (110 110,110 160,180 110,250 160,250 110)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (20 20,80 80,160 80,240 80,300 140)', 'wkt2':'LINESTRING (20 60,60 60,60 140,80 80,100 20,140 140,180 20,200 80,220 20,240 80,300 80,270 110,200 110)', result:true},
+{'wkt1':'LINESTRING (20 20,230 20,20 30,170 30,20 40,230 40,20 50,230 60,60 60,230 70,20 70,180 80,60 80,230 90,20 90,230 100,30 100,210 110,20 110,80 120,20 130,170 130,90 120,230 130,170 140,230 140,80 150,160 140,20 140,70 150,20 150,230 160,80 160,230 170,20 160,180 170,20 170,230 180,20 180,40 190,230 190,20 200,230 200)', 'wkt2':'LINESTRING (30 210,30 60,40 210,40 30,50 190,50 20,60 160,60 50,70 220,70 50,80 20,80 210,90 50,90 150,100 30,100 210,110 20,110 190,120 50,120 180,130 210,120 20,140 210,130 50,150 210,130 20,160 210,140 30,170 210,150 20,180 210,160 20,190 210,180 80,170 50,170 20,180 70,180 20,190 190,190 30,200 210,200 30,210 210,210 20,220 150,220 20)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (40 40,100 100,200 120,80 240)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (80 240,200 120,100 100,40 40)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (80 240,120 200,200 120,100 100,80 80,40 40)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (260 210,240 130,280 120,260 40)', result:false},
+{'wkt1':'LINESTRING (100 20,20 20,20 160,210 160,210 20,110 20,50 120,120 150,200 150)', 'wkt2':'LINESTRING (140 130,100 110,120 60,170 60)', result:false},
+{'wkt1':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (60 110,110 160,310 160,360 210)', 'wkt2':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', result:true},
+{'wkt1':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', 'wkt2':'LINESTRING (60 110,110 160,250 160)', result:true},
+{'wkt1':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', 'wkt2':'LINESTRING (110 160,310 160,340 190)', result:true},
+{'wkt1':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', 'wkt2':'LINESTRING (140 160,250 160,310 160,340 190)', result:true},
+{'wkt1':'LINESTRING (60 110,110 160,250 160,310 160,360 210)', 'wkt2':'LINESTRING (110 160,250 160,310 160)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (200 120,100 100,40 40,140 80,200 40)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (280 240,240 140,200 120,100 100,40 40)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (80 190,140 140,40 40)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (240 200,200 260,80 240,140 180)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (140 180,80 240,200 260,240 200)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (280 240,240 140,200 120,80 240)', result:true},
+{'wkt1':'LINESTRING (20 20,80 80,160 80,240 80,300 140)', 'wkt2':'LINESTRING (20 80,120 80,200 80,260 20)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (100 100,200 120,240 140,280 240)', result:true},
+{'wkt1':'LINESTRING (40 40,100 100,200 120,80 240)', 'wkt2':'LINESTRING (280 240,240 140,200 120,100 100)', result:true},
+{'wkt1':'LINESTRING (20 20,80 80,160 80,240 80,300 140)', 'wkt2':'LINESTRING (80 20,80 80,240 80,300 20)', result:true},
+{'wkt1':'LINESTRING (20 20,80 80,160 80,240 80,300 140)', 'wkt2':'LINESTRING (20 80,80 80,120 80,140 140,160 80,200 80,220 20,240 80,270 110,300 80)', result:true},
+{'wkt1':'LINESTRING (100 100,20 180,180 180)', 'wkt2':'LINESTRING (100 100,180 20,20 20,100 100)', result:true},
+{'wkt1':'LINESTRING (20 100,180 100,100 180)', 'wkt2':'LINESTRING (100 100,180 20,20 20,100 100)', result:true},
+{'wkt1':'LINESTRING (100 40,100 160,180 160)', 'wkt2':'LINESTRING (100 100,180 20,20 20,100 100)', result:true},
+{'wkt1':'LINESTRING (20 100,100 100,180 100,100 180)', 'wkt2':'LINESTRING (100 100,180 20,20 20,100 100)', result:true},
+{'wkt1':'LINESTRING (100 100,160 40)', 'wkt2':'LINESTRING (100 100,180 20,20 20,100 100)', result:true},
+{'wkt1':'LINESTRING (100 100,180 20)', 'wkt2':'LINESTRING (100 100,180 20,20 20,100 100)', result:true},
+{'wkt1':'LINESTRING (60 60,100 100,140 60)', 'wkt2':'LINESTRING (100 100,180 20,20 20,100 100)', result:true},
+{'wkt1':'LINESTRING (100 100,190 10,190 100)', 'wkt2':'LINESTRING (100 100,180 20,20 20,100 100)', result:true},
+{'wkt1':'LINESTRING (100 100,160 40,160 100)', 'wkt2':'LINESTRING (100 100,180 20,20 20,100 100)', result:true},
+{'wkt1':'LINESTRING (60 140,160 40,160 140)', 'wkt2':'LINESTRING (100 100,180 20,20 20,100 100)', result:true},
+{'wkt1':'LINESTRING (20 20,140 140)', 'wkt2':'LINESTRING (80 80,20 80,140 80,80 20,80 140)', result:true},
+{'wkt1':'LINESTRING (20 20,140 140)', 'wkt2':'LINESTRING (80 80,20 80,140 80)', result:true},
+{'wkt1':'LINESTRING (20 20,140 140)', 'wkt2':'LINESTRING (80 80,140 80,80 20,80 140)', result:true},
+{'wkt1':'LINESTRING (20 20,140 140)', 'wkt2':'LINESTRING (80 80,20 80,140 80,80 20,80 80)', result:true},
+{'wkt1':'LINESTRING (20 20,140 140)', 'wkt2':'LINESTRING (80 80,20 80,140 80,80 80)', result:true},
+{'wkt1':'LINESTRING (20 20,140 140)', 'wkt2':'LINESTRING (80 80,20 80,20 140,140 20,80 20,80 80)', result:true},
+{'wkt1':'LINESTRING (20 20,140 140)', 'wkt2':'LINESTRING (20 140,140 20,100 20,100 80)', result:true},
+{'wkt1':'LINESTRING (20 20,140 140)', 'wkt2':'LINESTRING (140 80,20 80,120 80,80 20,80 140)', result:true},
+{'wkt1':'LINESTRING (20 20,140 140)', 'wkt2':'LINESTRING (140 80,20 80,140 80)', result:true},
+{'wkt1':'LINESTRING (20 20,140 140)', 'wkt2':'LINESTRING (140 80,20 80,80 140,80 20)', result:true},
+{'wkt1':'LINESTRING (20 20,140 140)', 'wkt2':'LINESTRING (140 80,80 80,20 80,50 140,50 60)', result:true},
+{'wkt1':'LINESTRING (20 20,140 140)', 'wkt2':'LINESTRING (140 80,20 80,120 80,80 20,80 80,80 140)', result:true},
+{'wkt1':'LINESTRING (20 20,140 140)', 'wkt2':'LINESTRING (140 80,20 80,80 80,140 80)', result:true},
+{'wkt1':'LINESTRING (20 20,140 140)', 'wkt2':'LINESTRING (140 80,20 80,80 140,80 80,80 20)', result:true},
+{'wkt1':'LINESTRING (130 150,220 150,220 240)', 'wkt2':'LINESTRING (130 240,130 150,220 20,50 20,130 150)', result:true},
+{'wkt1':'LINESTRING (30 150,130 150,250 150)', 'wkt2':'LINESTRING (130 240,130 150,220 20,50 20,130 150)', result:true},
+{'wkt1':'LINESTRING (30 150,250 150)', 'wkt2':'LINESTRING (130 240,130 150,220 20,50 20,130 150)', result:true},
+{'wkt1':'LINESTRING (30 150,130 150,250 150)', 'wkt2':'LINESTRING (130 240,130 20,30 20,130 150)', result:true},
+{'wkt1':'LINESTRING (30 150,250 150)', 'wkt2':'LINESTRING (120 240,120 20,20 20,120 170)', result:true},
+{'wkt1':'LINESTRING (200 200,20 20,200 20,110 110,20 200,110 200,110 110)', 'wkt2':'LINESTRING (110 110,200 110)', result:true},
+{'wkt1':'LINESTRING (110 110,200 110)', 'wkt2':'LINESTRING (200 200,20 20,200 20,110 110,20 200,110 200,110 110)', result:true},
+{'wkt1':'LINESTRING (20 110,200 110)', 'wkt2':'LINESTRING (200 200,20 20,200 20,110 110,20 200,110 200,110 110)', result:true},
+{'wkt1':'LINESTRING (200 200,20 20,200 20,110 110,20 200,110 200,110 110)', 'wkt2':'LINESTRING (20 110,200 110)', result:true},
+{'wkt1':'LINESTRING (90 200,90 130,110 110,150 200)', 'wkt2':'LINESTRING (200 200,20 20,200 20,20 200,20 130,90 130)', result:true},
+{'wkt1':'LINESTRING (200 110,110 110,90 130,90 200)', 'wkt2':'LINESTRING (200 200,20 20,200 20,20 200,20 130,90 130)', result:true},
+{'wkt1':'LINESTRING (80 80,150 80,210 80)', 'wkt2':'MULTILINESTRING ((20 20,140 140),(20 140,140 20))', result:true},
+{'wkt1':'LINESTRING (40 80,160 200,260 20,40 80)', 'wkt2':'LINESTRING (40 80,160 200,260 20,40 80)', result:true},
+{'wkt1':'LINESTRING (40 80,160 200,260 20,40 80)', 'wkt2':'LINESTRING (40 80,260 20,160 200,40 80)', result:true},
+{'wkt1':'LINESTRING (40 80,160 200,260 20,40 80)', 'wkt2':'LINESTRING (260 20,40 80,160 200,260 20)', result:true},
+{'wkt1':'LINESTRING (40 80,160 200,260 20,40 80)', 'wkt2':'LINESTRING (100 140,160 200,260 20,40 80,100 140)', result:true},
+{'wkt1':'LINESTRING (100 100,180 20,20 20,100 100)', 'wkt2':'LINESTRING (100 100,180 180,20 180,100 100)', result:true},
+{'wkt1':'LINESTRING (40 150,40 40,150 40,150 150,40 150)', 'wkt2':'LINESTRING (40 150,150 40,170 20,170 190,40 150)', result:true},
+{'wkt1':'LINESTRING (100 100,180 20,20 20,100 100)', 'wkt2':'LINESTRING (180 100,20 100,100 180,180 100)', result:true},
+{'wkt1':'LINESTRING (100 100,180 20,20 20,100 100)', 'wkt2':'LINESTRING (180 180,100 100,20 180,180 180)', result:true},
+{'wkt1':'LINESTRING (20 180,100 100,20 20,20 180)', 'wkt2':'LINESTRING (100 20,100 180,180 100,100 20)', result:true},
+{'wkt1':'LINESTRING (40 150,40 40,150 40,150 150,40 150)', 'wkt2':'LINESTRING (170 20,20 170,170 170,170 20)', result:true},
+{'wkt1':'LINESTRING (40 150,40 40,150 40,150 150,40 150)', 'wkt2':'LINESTRING (40 150,150 150,90 210,40 150)', result:true},
+{'wkt1':'LINESTRING (40 150,40 40,150 40,150 150,40 150)', 'wkt2':'LINESTRING (20 150,170 150,90 230,20 150)', result:true},
+{'wkt1':'LINESTRING (40 150,40 40,150 40,150 150,40 150)', 'wkt2':'LINESTRING (40 150,150 150,150 40,20 40,20 150,40 150)', result:true},
+{'wkt1':'LINESTRING (110 110,200 20,20 20,110 110)', 'wkt2':'LINESTRING (110 110,200 200,110 110,20 200,20 110,200 110)', result:true},
+{'wkt1':'LINESTRING (110 110,200 20,20 20,110 110)', 'wkt2':'LINESTRING (110 110,20 110,200 110,50 110,110 170)', result:true},
+{'wkt1':'LINESTRING (110 110,200 20,20 20,110 110)', 'wkt2':'LINESTRING (110 110,20 200,110 200,110 110,200 200)', result:true},
+{'wkt1':'LINESTRING (110 110,200 20,20 20,110 110)', 'wkt2':'LINESTRING (200 20,20 200,200 200,110 110,110 40)', result:true},
+{'wkt1':'LINESTRING (110 110,200 20,20 20,110 110)', 'wkt2':'LINESTRING (200 20,20 200,200 200,20 20)', result:true},
+{'wkt1':'LINESTRING (110 110,20 110,110 20,20 20,110 110)', 'wkt2':'LINESTRING (110 110,200 200,110 200,200 110,110 110)', result:true},
+{'wkt1':'LINESTRING (20 120,120 120,20 20,120 20,20 120)', 'wkt2':'LINESTRING (170 100,70 100,170 170,70 170,170 100)', result:true},
+{'wkt1':'LINESTRING (20 110,110 110,20 20,110 20,20 110)', 'wkt2':'LINESTRING (110 160,70 110,60 160,20 130,110 160)', result:true},
+{'wkt1':'LINESTRING (20 200,200 200,20 20,200 20,20 200)', 'wkt2':'LINESTRING (20 110,200 110,200 160,20 60,20 110)', result:true},
+{'wkt1':'LINESTRING (20 110,110 110,20 20,110 20,20 110)', 'wkt2':'LINESTRING (200 200,110 110,200 110,110 200,200 200)', result:true},
+{'wkt1':'LINESTRING (20 120,120 120,20 20,120 20,20 120)', 'wkt2':'LINESTRING (220 120,120 20,220 20,120 120,220 120)', result:true},
+{'wkt1':'MULTILINESTRING ((70 20,20 90,70 170),(70 170,120 90,70 20))', 'wkt2':'MULTILINESTRING ((70 20,20 90,70 170),(70 170,120 90,70 20))', result:true},
+{'wkt1':'MULTILINESTRING ((20 20,90 20,170 20),(90 20,90 80,90 140))', 'wkt2':'MULTILINESTRING ((20 20,90 20,170 20),(90 20,90 80,90 140))', result:true},
+{'wkt1':'MULTILINESTRING ((20 20,90 20,170 20),(90 20,90 80,90 140))', 'wkt2':'MULTILINESTRING ((90 140,90 60,90 20),(170 20,130 20,20 20))', result:true},
+{'wkt1':'MULTILINESTRING ((20 20,90 20,170 20),(90 20,90 80,90 140))', 'wkt2':'MULTILINESTRING ((90 20,170 100,170 140),(170 60,90 20,20 60),(130 100,130 60,90 20,50 90))', result:true},
+{'wkt1':'MULTILINESTRING ((20 20,90 20,170 20),(90 20,90 80,90 140))', 'wkt2':'MULTILINESTRING ((90 20,170 100,170 140),(130 140,130 60,90 20,20 90,90 20,130 60,170 60))', result:true},
+{'wkt1':'MULTILINESTRING ((20 20,90 20,170 20),(90 20,90 80,90 140))', 'wkt2':'MULTILINESTRING ((90 20,170 100,170 140),(170 60,90 20,20 60))', result:true},
+{'wkt1':'MULTILINESTRING ((20 20,90 20,170 20),(90 20,90 80,90 140))', 'wkt2':'MULTILINESTRING ((90 20,170 100,170 140),(170 60,90 20,20 60),(130 100,90 20))', result:true},
+{'wkt1':'MULTILINESTRING ((20 20,90 20,170 20),(90 20,90 80,90 140))', 'wkt2':'MULTILINESTRING ((90 20,170 100,170 140),(170 60,90 20,20 60),(120 100,170 100,90 20))', result:true},
+{'wkt1':'MULTILINESTRING ((20 20,90 20,170 20),(90 20,90 80,90 140))', 'wkt2':'MULTILINESTRING ((90 20,170 100,170 140),(170 60,90 20,20 60),(120 100,170 100,90 20))', result:true},
+{'wkt1':'MULTILINESTRING ((20 20,90 20,170 20),(90 20,90 80,90 140))', 'wkt2':'MULTILINESTRING ((90 20,170 100,170 140),(130 140,130 60,90 20,20 90,90 20))', result:true},
+{'wkt1':'MULTILINESTRING ((20 20,90 20,170 20),(90 20,90 80,90 140))', 'wkt2':'MULTILINESTRING ((90 20,170 100,170 140),(170 60,90 20,20 60,20 140,90 20))', result:true},
+{'wkt1':'MULTILINESTRING ((20 20,90 90,20 160),(90 160,90 20))', 'wkt2':'MULTILINESTRING ((160 160,90 90,160 20),(160 120,120 120,90 90,160 60))', result:true},
+{'wkt1':'MULTILINESTRING ((20 20,90 90,20 160),(90 160,90 20))', 'wkt2':'MULTILINESTRING ((160 160,90 90,160 20),(160 120,120 120,90 90,120 60,160 60))', result:true},
+{'wkt1':'MULTILINESTRING ((20 20,90 90,20 160),(90 160,90 20))', 'wkt2':'MULTILINESTRING ((160 160,90 90,160 20),(160 120,90 90,160 60))', result:true},
+{'wkt1':'POINT (20 20)', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:false},
+{'wkt1':'POINT (70 170)', 'wkt2':'POLYGON ((110 230,80 160,20 160,20 20,200 20,200 160,140 160,110 230))', result:false},
+{'wkt1':'POINT (110 130)', 'wkt2':'POLYGON ((20 160,80 160,110 100,140 160,200 160,200 20,20 20,20 160))', result:false},
+{'wkt1':'POINT (100 70)', 'wkt2':'POLYGON ((20 150,100 150,40 50,170 50,110 150,190 150,190 20,20 20,20 150))', result:false},
+{'wkt1':'POINT (100 70)', 'wkt2':'POLYGON ((20 150,100 150,40 50,160 50,100 150,180 150,180 20,20 20,20 150))', result:false},
+{'wkt1':'POINT (60 120)', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'POINT (110 120)', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'POINT (160 120)', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'POINT (100 150)', 'wkt2':'POLYGON ((20 150,100 150,40 50,160 50,100 150,180 150,180 20,20 20,20 150))', result:true},
+{'wkt1':'POINT (100 80)', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'POINT (60 160)', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(280 50,100 50,190 140,280 50))', result:false},
+{'wkt1':'POINT (190 90)', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(280 50,100 50,190 140,280 50))', result:false},
+{'wkt1':'POINT (190 190)', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(280 50,100 50,190 140,280 50))', result:true},
+{'wkt1':'POINT (360 20)', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(280 50,100 50,190 140,280 50))', result:true},
+{'wkt1':'POINT (130 130)', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(280 50,100 50,190 140,280 50))', result:true},
+{'wkt1':'POINT (280 50)', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(280 50,100 50,190 140,280 50))', result:true},
+{'wkt1':'POINT (150 100)', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(280 50,100 50,190 140,280 50))', result:true},
+{'wkt1':'POINT (100 50)', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(280 50,100 50,190 140,280 50))', result:true},
+{'wkt1':'POINT (140 120)', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(280 50,100 50,190 140,280 50))', result:true},
+{'wkt1':'POINT (190 50)', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(90 50,150 110,190 50,90 50),(190 50,230 110,290 50,190 50))', result:true},
+{'wkt1':'POINT (180 90)', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(180 140,180 40,80 40,180 140),(180 90,210 140,310 40,230 40,180 90))', result:true},
+{'wkt1':'MULTIPOINT ((20 80),(110 160),(20 160))', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:false},
+{'wkt1':'MULTIPOINT ((20 80),(60 120),(20 160))', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'MULTIPOINT ((10 80),(110 170),(110 120))', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'MULTIPOINT ((10 80),(110 170),(160 120))', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'MULTIPOINT ((20 120),(60 120),(110 120),(160 120),(200 120))', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'MULTIPOINT ((60 120),(110 120),(160 120))', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'MULTIPOINT ((60 120),(160 120),(160 40),(60 40))', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'MULTIPOINT ((20 150),(60 120),(110 80))', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'MULTIPOINT ((110 80),(160 120),(200 160))', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'MULTIPOINT ((110 80),(110 120),(110 160))', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'MULTIPOINT ((110 170),(110 80))', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'MULTIPOINT ((60 120),(160 120),(110 80),(110 170))', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'MULTIPOINT ((90 80),(130 80))', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'MULTIPOINT ((60 120),(160 120),(110 80))', 'wkt2':'POLYGON ((60 120,60 40,160 40,160 120,60 120))', result:true},
+{'wkt1':'MULTIPOINT ((40 170),(40 90),(130 170))', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(280 50,100 50,190 140,280 50))', result:false},
+{'wkt1':'MULTIPOINT ((90 170),(280 170),(190 90))', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(280 50,100 50,190 140,280 50))', result:false},
+{'wkt1':'MULTIPOINT ((190 110),(150 70),(230 70))', 'wkt2':'POLYGON ((190 190,360 20,20 20,190 190),(280 50,100 50,190 140,280 50))', result:false},
+{'wkt1':'POINT (100 100)', 'wkt2':'MULTIPOLYGON (((20 100,20 20,100 20,100 100,20 100)),((100 180,100 100,180 100,180 180,100 180)))', result:true},
+{'wkt1':'POINT (20 100)', 'wkt2':'MULTIPOLYGON (((20 100,20 20,100 20,100 100,20 100)),((100 180,100 100,180 100,180 180,100 180)))', result:true},
+{'wkt1':'POINT (60 100)', 'wkt2':'MULTIPOLYGON (((20 100,20 20,100 20,100 100,20 100)),((100 180,100 100,180 100,180 180,100 180)))', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'MULTIPOLYGON (((110 110,20 200,200 200,110 110),(110 110,80 180,140 180,110 110)),((110 110,20 20,200 20,110 110),(110 110,80 40,140 40,110 110)))', result:true},
+{'wkt1':'POINT (110 200)', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:false},
+{'wkt1':'POINT (90 80)', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'POINT (340 240)', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'POINT (230 150)', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'POINT (160 150)', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'POINT (90 150)', 'wkt2':'LINESTRING (150 150,20 20,280 20,150 150)', result:false},
+{'wkt1':'POINT (150 80)', 'wkt2':'LINESTRING (150 150,20 20,280 20,150 150)', result:false},
+{'wkt1':'POINT (150 150)', 'wkt2':'LINESTRING (150 150,20 20,280 20,150 150)', result:true},
+{'wkt1':'POINT (100 20)', 'wkt2':'LINESTRING (150 150,20 20,280 20,150 150)', result:true},
+{'wkt1':'POINT (20 20)', 'wkt2':'LINESTRING (150 150,20 20,280 20,150 150)', result:true},
+{'wkt1':'POINT (220 220)', 'wkt2':'LINESTRING (110 110,220 20,20 20,110 110,220 220)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,220 20,20 20,110 110,220 220)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,220 20,20 20,220 220)', result:true},
+{'wkt1':'POINT (110 20)', 'wkt2':'LINESTRING (110 110,220 20,20 20,220 220)', result:true},
+{'wkt1':'POINT (220 20)', 'wkt2':'LINESTRING (110 110,220 20,20 20,220 220)', result:true},
+{'wkt1':'POINT (110 20)', 'wkt2':'LINESTRING (220 220,20 20,220 20,110 110)', result:true},
+{'wkt1':'POINT (20 20)', 'wkt2':'LINESTRING (220 220,20 20,220 20,110 110)', result:true},
+{'wkt1':'POINT (20 110)', 'wkt2':'LINESTRING (20 200,20 20,110 20,20 110,110 200)', result:true},
+{'wkt1':'POINT (20 200)', 'wkt2':'LINESTRING (20 200,200 20,20 20,200 200)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (20 200,200 20,140 20,140 80,80 140,20 140)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (20 200,200 20,20 20,200 200)', result:true},
+{'wkt1':'POINT (80 140)', 'wkt2':'LINESTRING (20 200,110 110,200 20,140 20,140 80,110 110,80 140,20 140)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (20 200,110 110,200 20,140 20,140 80,110 110,80 140,20 140)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (20 200,200 20,140 20,140 80,110 110,80 140,20 140)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (20 200,110 110,200 20,20 20,110 110,200 200)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (20 200,200 20,20 20,110 110,200 200)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (20 200,110 110,20 20,200 20,110 110,200 200)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,110 200,20 200,110 110,200 20,140 20,140 80,110 110,80 140,20 140)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,110 200,20 200,200 20,140 20,140 80,110 110,80 140,20 140)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,110 200,20 200,200 20,140 20,140 80,80 140,20 140)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,110 200,20 200,110 110,200 20,20 20,110 110,200 200)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,110 200,20 200,200 20,20 20,110 110,200 200)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,110 200,20 200,200 20,20 20,200 200)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,110 200,20 200,110 110,20 20,200 20,110 110,200 200)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,110 200,20 200,200 20,200 110,110 110,200 200)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (200 200,110 110,20 20,200 20,110 110,20 200,110 200,110 110)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (200 200,20 20,200 20,110 110,20 200,110 200,110 110)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (200 200,20 20,200 20,20 200,110 200,110 110)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (200 200,110 110,200 20,20 20,110 110,20 200,110 200,110 110)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (200 200,20 20,20 110,110 110,20 200,110 200,110 110)', result:true},
+{'wkt1':'POINT (110 160)', 'wkt2':'LINESTRING (110 160,200 250,110 250,110 160,110 110,110 20,20 20,110 110)', result:true},
+{'wkt1':'POINT (110 160)', 'wkt2':'LINESTRING (110 160,200 250,110 250,110 110,110 20,20 20,110 110)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 160,200 250,110 250,110 160,110 110,110 20,20 20,110 110)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 160,200 250,110 250,110 160,110 20,20 20,110 110)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,200 200,110 200,110 110,110 20,20 20,110 110)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,200 200,110 200,110 20,20 20,110 110)', result:true},
+{'wkt1':'POINT (140 200)', 'wkt2':'LINESTRING (110 110,200 200,110 200,110 110,110 20,20 20,110 110)', result:true},
+{'wkt1':'POINT (110 200)', 'wkt2':'LINESTRING (110 110,200 200,110 200,110 110,110 20,20 20,110 110)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,200 200,110 200,110 110,110 20,200 20,110 110)', result:true},
+{'wkt1':'POINT (140 200)', 'wkt2':'LINESTRING (110 110,200 200,110 200,110 110,110 20,200 20,110 110)', result:true},
+{'wkt1':'POINT (110 200)', 'wkt2':'LINESTRING (110 110,200 200,110 200,110 110,110 20,200 20,110 110)', result:true},
+{'wkt1':'POINT (90 130)', 'wkt2':'LINESTRING (90 130,20 130,20 200,90 130,200 20,20 20,200 200)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (90 130,20 130,20 200,90 130,200 20,20 20,200 200)', result:true},
+{'wkt1':'POINT (90 130)', 'wkt2':'LINESTRING (90 130,20 130,20 200,200 20,20 20,200 200)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (90 130,20 130,20 200,200 20,20 20,200 200)', result:true},
+{'wkt1':'POINT (90 130)', 'wkt2':'LINESTRING (200 200,20 20,200 20,90 130,20 200,20 130,90 130)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (200 200,20 20,200 20,90 130,20 200,20 130,90 130)', result:true},
+{'wkt1':'POINT (90 130)', 'wkt2':'LINESTRING (200 200,20 20,200 20,20 200,20 130,90 130)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (200 200,20 20,200 20,20 200,20 130,90 130)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,20 130,20 200,110 110,200 20,20 20,110 110,200 200,200 130,110 110)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,20 130,20 200,200 20,20 20,200 200,200 130,110 110)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,80 200,20 200,110 110,200 20,20 20,110 110,200 200,140 200,110 110)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 110,80 200,20 200,200 20,20 20,200 200,140 200,110 110)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (200 200,20 20,200 20,20 200,200 200)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (200 200,110 110,20 20,200 20,110 110,20 200,200 200)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (200 200,110 110,200 20,20 20,110 110,20 200,200 200)', result:true},
+{'wkt1':'POINT (90 130)', 'wkt2':'LINESTRING (90 130,20 130,20 200,90 130,110 110,200 20,20 20,110 110,200 200,90 130)', result:true},
+{'wkt1':'POINT (90 130)', 'wkt2':'LINESTRING (90 130,20 130,20 200,110 110,200 20,20 20,110 110,200 200,90 130)', result:true},
+{'wkt1':'POINT (90 130)', 'wkt2':'LINESTRING (90 130,90 200,20 200,90 130,110 110,200 20,20 20,110 110,200 200,90 130)', result:true},
+{'wkt1':'POINT (90 130)', 'wkt2':'LINESTRING (90 130,90 200,20 200,200 20,20 20,200 200,90 130)', result:true},
+{'wkt1':'POINT (90 130)', 'wkt2':'LINESTRING (90 130,90 200,20 200,110 110,200 20,20 20,110 110,200 200,90 130)', result:true},
+{'wkt1':'POINT (90 130)', 'wkt2':'LINESTRING (90 130,90 200,20 200,200 20,20 20,200 200,90 130)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (90 130,90 200,20 200,200 20,20 20,200 200,90 130)', result:true},
+{'wkt1':'POINT (110 200)', 'wkt2':'LINESTRING (110 200,110 110,20 20,200 20,110 110,110 200,200 200)', result:true},
+{'wkt1':'POINT (110 150)', 'wkt2':'LINESTRING (110 200,110 110,20 20,200 20,110 110,110 200,200 200)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 200,110 110,20 20,200 20,110 110,110 200,200 200)', result:true},
+{'wkt1':'POINT (110 200)', 'wkt2':'LINESTRING (110 200,110 110,20 20,200 20,110 110,110 200)', result:true},
+{'wkt1':'POINT (110 150)', 'wkt2':'LINESTRING (110 200,110 110,20 20,200 20,110 110,110 200)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (110 200,110 110,20 20,200 20,110 110,110 200)', result:true},
+{'wkt1':'POINT (110 150)', 'wkt2':'LINESTRING (20 200,110 200,110 110,20 20,200 20,110 110,110 200,200 200)', result:true},
+{'wkt1':'POINT (110 110)', 'wkt2':'LINESTRING (20 200,110 200,110 110,20 20,200 20,110 110,110 200,200 200)', result:true},
+{'wkt1':'POINT (110 200)', 'wkt2':'LINESTRING (20 200,110 200,110 110,20 20,200 20,110 110,110 200,200 200)', result:true},
+{'wkt1':'MULTIPOINT ((50 250),(90 220),(130 190))', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:false},
+{'wkt1':'MULTIPOINT ((180 180),(230 130),(280 80))', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:false},
+{'wkt1':'MULTIPOINT ((50 120),(90 80),(130 40))', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'MULTIPOINT ((300 280),(340 240),(380 200))', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'MULTIPOINT ((230 150),(260 120),(290 90))', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'MULTIPOINT ((200 190),(240 150),(270 110))', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'MULTIPOINT ((160 150),(190 120),(220 90))', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'MULTIPOINT ((120 190),(160 150),(200 110))', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'MULTIPOINT ((90 80),(160 150),(340 240))', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'MULTIPOINT ((90 80),(160 150),(300 150))', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'MULTIPOINT ((90 80),(160 150),(240 150))', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'MULTIPOINT ((90 80),(130 120),(210 150))', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'MULTIPOINT ((130 120),(210 150),(340 200))', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'MULTIPOINT ((160 150),(240 150),(340 210))', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'MULTIPOINT ((160 150),(300 150),(340 150))', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'MULTIPOINT ((160 150),(240 150),(340 240))', 'wkt2':'LINESTRING (90 80,160 150,300 150,340 150,340 240)', result:true},
+{'wkt1':'POINT (20 20)', 'wkt2':'POINT (20 20)', result:true},
+{'wkt1':'POINT (20 20)', 'wkt2':'POINT (40 60)', result:false},
+{'wkt1':'POINT (40 40)', 'wkt2':'MULTIPOINT ((20 20),(80 80),(20 120))', result:false},
+{'wkt1':'POINT (20 20)', 'wkt2':'MULTIPOINT ((20 20),(80 80),(20 120))', result:true},
+{'wkt1':'MULTIPOINT ((40 40),(80 60),(120 100))', 'wkt2':'MULTIPOINT ((40 40),(80 60),(120 100))', result:true},
+{'wkt1':'MULTIPOINT ((40 40),(80 60),(120 100))', 'wkt2':'MULTIPOINT ((40 40),(120 100),(80 60))', result:true},
+{'wkt1':'MULTIPOINT ((40 40),(60 100),(100 60),(120 120))', 'wkt2':'MULTIPOINT ((20 120),(60 60),(100 100),(140 40))', result:false},
+{'wkt1':'MULTIPOINT ((20 20),(80 70),(140 120),(200 170))', 'wkt2':'MULTIPOINT ((20 20),80 70),(140 120),(200 170))', result:true},
+{'wkt1':'MULTIPOINT ((20 20),(140 120),(80 70),(200 170))', 'wkt2':'MULTIPOINT ((80 70),20 20),(200 170),(140 120))', result:true},
+{'wkt1':'MULTIPOINT ((20 20),(80 70),(140 120),(200 170))', 'wkt2':'MULTIPOINT ((80 70),(140 120))', result:true},
+{'wkt1':'MULTIPOINT ((80 70),(20 20),(200 170),(140 120))', 'wkt2':'MULTIPOINT ((140 120),(80 70))', result:true},
+{'wkt1':'MULTIPOINT ((80 70),(20 20),(200 170),(140 120))', 'wkt2':'MULTIPOINT ((80 170),(140 120),(200 80))', result:true},
+{'wkt1':'MULTIPOINT ((80 70),(20 20),(200 170),(140 120))', 'wkt2':'MULTIPOINT ((80 170),(140 120),(200 80),(80 70))', result:true},
+{'wkt1':'POLYGON((-8239529.462853361 4980952.065110421,-8224242.057199065 4980952.065110421,-8224242.057199064 4988844.188279452,-8239529.462853361 4988844.188279452,-8239529.462853361 4980952.065110421))', 'wkt2':'POINT(-8225445.94039435 4982695.78481786)', result:true},
+{'wkt1':'POLYGON((-8239529.462853361 4980952.065110421,-8224242.057199065 4980952.065110421,-8224242.057199064 4988844.188279452,-8239529.462853361 4988844.188279452,-8239529.462853361 4980952.065110421))', 'wkt2':'POINT(-8224242.0571985 4982695.78481786)', result:false},
+{'wkt1':'POLYGON((-8239529.462853361 4980952.065110421,-8224242.057199065 4980952.065110421,-8224242.057199064 4988844.188279452,-8239529.462853361 4988844.188279452,-8239529.462853361 4980952.065110421))', 'wkt2':'POINT(-8224242.0571995 4982695.78481786)', result:true}
+];
diff --git a/misc/openlayers/tests/data/osm.js b/misc/openlayers/tests/data/osm.js
new file mode 100644
index 0000000..6c94459
--- /dev/null
+++ b/misc/openlayers/tests/data/osm.js
@@ -0,0 +1,14 @@
+var osm_test_data = {
+ 'node': '<?xml version="1.0" encoding="UTF-8"?><osm version="0.5" generator="OpenStreetMap server"> <node id="200545" lat="52.5503033" lon="-1.8166417" user="blackadder" visible="true" timestamp="2006-03-22T16:33:41+00:00"/></osm>',
+ 'node_with_tags': '<?xml version="1.0" encoding="UTF-8"?><osm version="0.5" generator="OpenStreetMap server"> <node id="200545" lat="52.5503033" lon="-1.8166417" user="blackadder" visible="true" timestamp="2006-03-22T16:33:41+00:00"><tag k="a" v="b" /></node></osm>',
+ 'way': '<?xml version="1.0" encoding="UTF-8"?><osm version="0.5" generator="OpenStreetMap server"> <node id="29783468" lat="52.5506446" lon="-1.8141177" user="blackadder" visible="true" timestamp="2007-05-30T14:22:33+01:00"> <tag k="created_by" v="JOSM"/> </node> <node id="29783470" lat="52.5501275" lon="-1.8151451" user="blackadder" visible="true" timestamp="2007-05-30T14:22:33+01:00"> <tag k="created_by" v="JOSM"/> </node> <node id="29783471" lat="52.5505521" lon="-1.8157703" user="blackadder" visible="true" timestamp="2007-12-18T15:33:59+00:00"> <tag k="created_by" v="JOSM"/> </node> <node id="29783472" lat="52.5501836" lon="-1.8164007" user="blackadder" visible="true" timestamp="2007-12-18T15:33:59+00:00"> <tag k="created_by" v="JOSM"/> </node> <node id="29783473" lat="52.5506035" lon="-1.8170311" user="blackadder" visible="true" timestamp="2007-05-30T14:21:32+01:00"> <tag k="created_by" v="JOSM"/> </node> <node id="29783474" lat="52.5509559" lon="-1.8164092" user="blackadder" visible="true" timestamp="2007-05-30T14:21:33+01:00"> <tag k="created_by" v="JOSM"/> </node> <node id="29783476" lat="52.5513103" lon="-1.8169385" user="blackadder" visible="true" timestamp="2007-05-30T14:21:33+01:00"> <tag k="created_by" v="JOSM"/> </node> <node id="29783477" lat="52.5517893" lon="-1.8159626" user="blackadder" visible="true" timestamp="2007-05-30T14:21:33+01:00"> <tag k="created_by" v="JOSM"/> </node> <node id="29783478" lat="52.5518461" lon="-1.8145067" user="blackadder" visible="true" timestamp="2007-05-30T14:21:33+01:00"> <tag k="created_by" v="JOSM"/> </node> <node id="29783479" lat="52.5511883" lon="-1.8143197" user="blackadder" visible="true" timestamp="2007-05-30T14:21:33+01:00"> <tag k="created_by" v="JOSM"/> </node> <way id="4685537" visible="true" timestamp="2007-05-30T14:21:35+01:00" user="blackadder"> <nd ref="29783472"/> <nd ref="29783473"/> <nd ref="29783474"/> <nd ref="29783476"/> <nd ref="29783477"/> <nd ref="29783478"/> <nd ref="29783479"/> <nd ref="29783468"/> <nd ref="29783470"/> <nd ref="29783471"/> <nd ref="29783472"/> <tag k="name" v="Maney Hill School"/> <tag k="created_by" v="JOSM"/> <tag k="landuse" v="school"/> <tag k="amenity" v="school"/> </way></osm>',
+ 'node_way': '<?xml version="1.0" encoding="UTF-8"?><osm version="0.5" generator="OpenStreetMap server"> <node id="200565" lat="52.5526654" lon="-1.8146664" user="blackadder" visible="true" timestamp="2006-03-22T16:34:29+00:00"/> <node id="200571" lat="52.5535575" lon="-1.8148566" user="blackadder" visible="true" timestamp="2007-11-15T12:54:40+00:00"/> <node id="200572" lat="52.5522848" lon="-1.8145676" user="blackadder" visible="true" timestamp="2008-01-15T17:36:32+00:00"> <tag k="ref" v="0562901"/> <tag k="both_sides" v="true"/> <tag k="route_ref" v="167|757"/> <tag k="highway" v="bus_stop"/> <tag k="location" v="East View Road, Shooters Hill"/> </node> <node id="200573" lat="52.5520736" lon="-1.8145054" user="blackadder" visible="true" timestamp="2006-03-22T16:34:49+00:00"/> <node id="200751" lat="52.5511951" lon="-1.8142246" user="blackadder" visible="true" timestamp="2006-03-22T16:36:18+00:00"/> <node id="200752" lat="52.5505598" lon="-1.8140051" user="blackadder" visible="true" timestamp="2006-03-22T16:36:20+00:00"/> <node id="200753" lat="52.5496876" lon="-1.8136891" user="blackadder" visible="true" timestamp="2006-03-22T21:55:13+00:00"/> <node id="200754" lat="52.549009" lon="-1.8133906" user="blackadder" visible="true" timestamp="2006-03-22T16:36:24+00:00"/> <node id="200755" lat="52.5478879" lon="-1.8128287" user="blackadder" visible="true" timestamp="2006-03-22T16:36:26+00:00"/> <node id="200759" lat="52.5464722" lon="-1.8119684" user="blackadder" visible="true" timestamp="2006-03-22T16:36:34+00:00"/> <node id="200771" lat="52.5466788" lon="-1.8121387" user="blackadder" visible="true" timestamp="2008-01-15T16:56:49+00:00"/> <node id="645730" lat="52.5491787" lon="-1.8134657" user="blackadder" visible="true" timestamp="2008-01-15T17:36:32+00:00"> <tag k="created_by" v="JOSM"/> <tag k="ref" v="0562201"/> <tag k="both_sides" v="true"/> <tag k="route_ref" v="167|757"/> <tag k="highway" v="bus_stop"/> <tag k="location" v="East View Road, Recretaion Ground"/> <tag k="amenity" v="bus_stop"/> </node> <way id="21329267" visible="true" timestamp="2008-01-15T16:24:55+00:00" user="blackadder"> <nd ref="200571"/> <nd ref="200565"/> <nd ref="200572"/> <nd ref="200573"/> <nd ref="200751"/> <nd ref="200752"/> <nd ref="200753"/> <nd ref="645730"/> <nd ref="200754"/> <nd ref="200755"/> <nd ref="200771"/> <nd ref="200759"/> <tag k="name" v="East View Road"/> <tag k="postal_code" v="B72"/> <tag k="place_name" v="Sutton Coldfield"/> <tag k="created_by" v="JOSM"/> <tag k="highway" v="unclassified"/> </way></osm>'
+};
+
+var osm_serialized_data = {
+ 'node':'<osm version="0.5" generator="OpenLayers '+OpenLayers.VERSION_NUMBER+'"><node id="200545" lon="-1.8166417" lat="52.5503033"/></osm>',
+ 'node_with_tags':'<osm version="0.5" generator="OpenLayers '+OpenLayers.VERSION_NUMBER+'"><node id="200545" lon="-1.8166417" lat="52.5503033"><tag k="a" v="b"/></node></osm>',
+ 'way':'<osm version="0.5" generator="OpenLayers '+OpenLayers.VERSION_NUMBER+'"><node id="29783472" lon="-1.8164007" lat="52.5501836"/><node id="29783473" lon="-1.8170311" lat="52.5506035"/><node id="29783474" lon="-1.8164092" lat="52.5509559"/><node id="29783476" lon="-1.8169385" lat="52.5513103"/><node id="29783477" lon="-1.8159626" lat="52.5517893"/><node id="29783478" lon="-1.8145067" lat="52.5518461"/><node id="29783479" lon="-1.8143197" lat="52.5511883"/><node id="29783468" lon="-1.8141177" lat="52.5506446"/><node id="29783470" lon="-1.8151451" lat="52.5501275"/><node id="29783471" lon="-1.8157703" lat="52.5505521"/><way id="4685537"><nd ref="29783472"/><nd ref="29783473"/><nd ref="29783474"/><nd ref="29783476"/><nd ref="29783477"/><nd ref="29783478"/><nd ref="29783479"/><nd ref="29783468"/><nd ref="29783470"/><nd ref="29783471"/><nd ref="29783472"/><tag k="area" v="yes"/><tag k="name" v="Maney Hill School"/><tag k="created_by" v="JOSM"/><tag k="landuse" v="school"/><tag k="amenity" v="school"/></way></osm>',
+ 'node_way':'<osm version="0.5" generator="OpenLayers '+OpenLayers.VERSION_NUMBER+'"><node id="645730" lon="-1.8134657" lat="52.5491787"><tag k="created_by" v="JOSM"/><tag k="ref" v="0562201"/><tag k="both_sides" v="true"/><tag k="route_ref" v="167|757"/><tag k="highway" v="bus_stop"/><tag k="location" v="East View Road, Recretaion Ground"/><tag k="amenity" v="bus_stop"/></node><node id="200572" lon="-1.8145676" lat="52.5522848"><tag k="ref" v="0562901"/><tag k="both_sides" v="true"/><tag k="route_ref" v="167|757"/><tag k="highway" v="bus_stop"/><tag k="location" v="East View Road, Shooters Hill"/></node><node id="200571" lon="-1.8148566" lat="52.5535575"/><node id="200565" lon="-1.8146664" lat="52.5526654"/><node id="200573" lon="-1.8145054" lat="52.5520736"/><node id="200751" lon="-1.8142246" lat="52.5511951"/><node id="200752" lon="-1.8140051" lat="52.5505598"/><node id="200753" lon="-1.8136891" lat="52.5496876"/><node id="200754" lon="-1.8133906" lat="52.549009"/><node id="200755" lon="-1.8128287" lat="52.5478879"/><node id="200771" lon="-1.8121387" lat="52.5466788"/><node id="200759" lon="-1.8119684" lat="52.5464722"/><way id="21329267"><nd ref="200571"/><nd ref="200565"/><nd ref="200572"/><nd ref="200573"/><nd ref="200751"/><nd ref="200752"/><nd ref="200753"/><nd ref="645730"/><nd ref="200754"/><nd ref="200755"/><nd ref="200771"/><nd ref="200759"/><tag k="name" v="East View Road"/><tag k="postal_code" v="B72"/><tag k="place_name" v="Sutton Coldfield"/><tag k="created_by" v="JOSM"/><tag k="highway" v="unclassified"/></way></osm>'
+};
+
diff --git a/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/0/0.json b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/0/0.json
new file mode 100644
index 0000000..e1f305b
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/0/0.json
@@ -0,0 +1 @@
+{"keys": ["", "269", "270", "572", "271", "272", "585", "586", "273", "589", "573", "274", "275", "560", "558", "559", "562", "561", "279", "563", "566", "564", "281", "574", "565", "285", "286", "287", "576", "575", "1", "289", "569", "568", "567", "590", "295", "292", "294", "2", "299", "297", "578", "587", "556", "309", "570", "577", "313", "310", "312", "588", "315", "579", "592", "591", "557", "582", "580", "318", "319", "583", "321", "571", "584", "322", "323", "326", "325", "329", "332", "331", "336", "337", "611", "612", "339", "341", "617", "622", "623", "18", "349", "624", "350", "19", "20", "619", "625", "353", "357", "361", "5", "364", "359", "338", "620", "367", "370", "626", "365", "627", "376", "9", "7", "377", "378", "621", "383", "6", "11", "374", "380", "385", "394", "386", "396", "399", "398", "407", "400", "409", "412", "4", "24", "405", "427", "424", "420", "404", "431", "432", "433", "419", "429", "92", "117", "88", "440", "441", "94", "442", "91", "444", "97", "96", "95", "443", "439", "449", "446", "100", "451", "106", "109", "105", "103", "102", "456", "453", "450", "113", "112", "459", "114", "458", "461", "111", "467", "473", "118", "462", "474", "480", "479"], "data": {"623": {"dom_desc": "", "pro_desc": ""}, "622": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "SMALL-LEAFED AND CONIFEROUS WOODED STEPPES OF CONTINENTAL CLIMATE"}, "621": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC MIXED SCLEROPHYLL FORESTS AND SHRUB"}, "620": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC CONIFEROUS AND MIXED FORESTS"}, "627": {"dom_desc": "DRY DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS"}, "571": {"dom_desc": "", "pro_desc": ""}, "626": {"dom_desc": "DRY DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS"}, "24": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OPEN WOODLAND, SAVANNAS, AND SHRUB OF EASTERN PARTS OF CONTINENTS"}, "1": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "20": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS"}, "624": {"dom_desc": "DRY DOMAIN", "pro_desc": "STEPPES OF MODERATELY CONTINENTAL CLIMATE"}, "289": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL MOSS-AND-LICHEN (TYPICAL) TUNDRA"}, "573": {"dom_desc": "POLAR DOMAIN", "pro_desc": ""}, "405": {"dom_desc": "DRY DOMAIN", "pro_desc": "SHRUB AND SEMI-SHRUB SEMI-DESERTS OF CONTINENTAL CLIMATE"}, "404": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-STEPPE OF CONTINENTAL CLIMATE"}, "4": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-STEPPE OF CONTINENTAL CLIMATE"}, "400": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "SHRUB-FOREST-MEADOW OF MEDITERRANEAN CLIMATE"}, "281": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "5": {"dom_desc": "POLAR DOMAIN", "pro_desc": "OCEANIC FOREST-TUNDRA"}, "285": {"dom_desc": "POLAR DOMAIN", "pro_desc": "ARCTIC TUNDRAS"}, "349": {"dom_desc": "DRY DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS"}, "287": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "286": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "453": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "577": {"dom_desc": "POLAR DOMAIN", "pro_desc": "ARCTIC TUNDRAS"}, "575": {"dom_desc": "POLAR DOMAIN", "pro_desc": ""}, "420": {"dom_desc": "DRY DOMAIN", "pro_desc": "SHRUB AND SEMI-SHRUB SEMI-DESERTS AND DESERTS OF CONTINENTAL CLIMATE"}, "269": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "574": {"dom_desc": "POLAR DOMAIN", "pro_desc": ""}, "378": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "DRY STEPPES AND SHRUB OF MODERATE CONTINENTAL CLIMATE"}, "412": {"dom_desc": "DRY DOMAIN", "pro_desc": "DRY STEPPES AND SHRUB OF MODERATE CONTINENTAL CLIMATE"}, "299": {"dom_desc": "POLAR DOMAIN", "pro_desc": "TUNDRA-POLAR DESERT"}, "370": {"dom_desc": "DRY DOMAIN", "pro_desc": "SEMI-DESERTS AND DESERTS OF CONTINENTAL CLIMATE"}, "294": {"dom_desc": "POLAR DOMAIN", "pro_desc": "ARCTIC TUNDRAS"}, "295": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL MOSS-AND-LICHEN (TYPICAL) TUNDRA"}, "292": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL MOSS-AND-LICHEN (TYPICAL) TUNDRA"}, "374": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC MIXED SCLEROPHYLL FORESTS AND SHRUB"}, "377": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS"}, "376": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "TEMPERATE PRAIRIES (HUMID STEPPES AND WOODED STEPPES) OF EASTERN PARTS OF CONTINENTS"}, "591": {"dom_desc": "POLAR DOMAIN", "pro_desc": "TUNDRA-POLAR DESERT"}, "586": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "319": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC FOREST-TUNDRA"}, "318": {"dom_desc": "POLAR DOMAIN", "pro_desc": "OPEN WOODLAND-TUNDRA"}, "587": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "313": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL DARK EVERGREEN NEEDLELEAF OPEN FOREST"}, "312": {"dom_desc": "", "pro_desc": ""}, "310": {"dom_desc": "POLAR DOMAIN", "pro_desc": "OPEN WOODLAND-TUNDRA"}, "584": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL MOSS-AND-LICHEN (TYPICAL) TUNDRA"}, "315": {"dom_desc": "POLAR DOMAIN", "pro_desc": "OPEN WOODLAND-TUNDRA"}, "270": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "271": {"dom_desc": "POLAR DOMAIN", "pro_desc": ""}, "117": {"dom_desc": "DRY DOMAIN", "pro_desc": "WESTERN OCEANIC SEMI-DESERTS AND DESERTS WITH HIGH RELATIVE HUMIDITY"}, "273": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "111": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID TALL-GRASS SAVANNAS AND SAVANNA FORESTS"}, "275": {"dom_desc": "POLAR DOMAIN", "pro_desc": ""}, "113": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "112": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "EASTERN OCEANIC CONSTANTLY HUMID FORESTS"}, "279": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "399": {"dom_desc": "DRY DOMAIN", "pro_desc": "DRY STEPPES AND SHRUB OF MODERATE CONTINENTAL CLIMATE"}, "398": {"dom_desc": "DRY DOMAIN", "pro_desc": "STEPPES AND SHRUB OF MODERATE CONTINENTAL CLIMATE"}, "118": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID FORESTS WITH SHORT DRY SEASON"}, "429": {"dom_desc": "DRY DOMAIN", "pro_desc": "WESTERN OCEANIC SEMI-DESERTS AND DESERTS WITH HIGH RELATIVE HUMIDITY"}, "7": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "MODERATE CONTINENTAL MIXED FORESTS"}, "367": {"dom_desc": "DRY DOMAIN", "pro_desc": "DRY STEPPES OF CONTINENTAL CLIMATE"}, "364": {"dom_desc": "POLAR DOMAIN", "pro_desc": "EASTERN OCEANIC TAYGA"}, "365": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "MODERATELY HUMID BROADLEAF FOREST IN MODERATELY CONTINENTAL CLIMATE"}, "424": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-STEPPE OF CONTINENTAL CLIMATE"}, "427": {"dom_desc": "DRY DOMAIN", "pro_desc": "WESTERN OCEANIC SEMI-DESERTS AND DESERTS WITH HIGH RELATIVE HUMIDITY"}, "361": {"dom_desc": "", "pro_desc": ""}, "570": {"dom_desc": "POLAR DOMAIN", "pro_desc": "ARCTIC TUNDRAS"}, "309": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL DARK EVERGREEN NEEDLELEAF OPEN FOREST"}, "449": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SAVANNAS, OPEN WOODLAND AND SHRUB WITH SEASONAL MOISTURE SUPPLY"}, "585": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "582": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL MOSS-AND-LICHEN (TYPICAL) TUNDRA"}, "583": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "580": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "443": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERT-LIKE SAVANNAS, OPEN WOODLAND, AND SHRUB"}, "442": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-STEPPE AND FOREST-MEADOW OF SEASONALLY HUMID TYPE"}, "441": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "EASTERN OCEANIC CONSTANTLY HUMID FORESTS"}, "440": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SAVANNAS, OPEN WOODLAND AND SHRUB WITH SEASONAL MOISTURE SUPPLY"}, "446": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-MEADOW OF CONSTANTLY HUMID EASTERN OCEANIC TYPE"}, "588": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "444": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID MIXED (DECIDUOUS AND EVERGREEN) FORESTS"}, "380": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "SHRUB-FOREST-MEADOW OF MEDITERRANEAN CLIMATE"}, "109": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MODERATELY HUMID GRASSY SAVANNAS"}, "385": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "DRY STEPPES AND SHRUB OF MODERATE CONTINENTAL CLIMATE"}, "386": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MIXED CONSTANTLY HUMID FORESTS"}, "297": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL MOSS-AND-LICHEN (TYPICAL) TUNDRA"}, "102": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-MEADOW, SEASONALLY HUMID"}, "103": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MODERATELY HUMID GRASSY SAVANNAS"}, "100": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID MIXED (DECIDUOUS AND EVERGREEN) FORESTS"}, "589": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "106": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "DRY SAVANNAS AND OPEN WOODLAND"}, "105": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "DRY SAVANNAS AND OPEN WOODLAND"}, "419": {"dom_desc": "DRY DOMAIN", "pro_desc": "INNER CONTINENTAL DESERTS OF CONTINENTAL CLIMATE"}, "383": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS OF WESTERN OCEANIC (MEDITERRANEAN) CLIMATE"}, "88": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-STEPPE"}, "439": {"dom_desc": "DRY DOMAIN", "pro_desc": "INNER CONTINENTAL SHRUB SEMI-DESERT"}, "432": {"dom_desc": "DRY DOMAIN", "pro_desc": "DRY STEPPES AND SHRUB OF MODERATE CONTINENTAL CLIMATE"}, "433": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-STEPPE OF CONTINENTAL CLIMATE"}, "431": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERT"}, "458": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID, PREDOMINANTLY DECIDUOUS FORESTS"}, "459": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW"}, "579": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "578": {"dom_desc": "POLAR DOMAIN", "pro_desc": "ARCTIC TUNDRAS"}, "339": {"dom_desc": "POLAR DOMAIN", "pro_desc": "MODERATE CONTINENTAL DARK EVERGREEN NEEDLELEAF TAYGA"}, "338": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "PERMANENTLY HUMID WESTERN OCEANIC BROADLEAF FORESTS"}, "625": {"dom_desc": "DRY DOMAIN", "pro_desc": "DRY STEPPES OF CONTINENTAL CLIMATE"}, "590": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "450": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID TALL-GRASS SAVANNAS AND SAVANNA FORESTS"}, "451": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MODERATELY HUMID GRASSY SAVANNAS"}, "337": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MEADOW"}, "336": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MEADOW"}, "331": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL MOSS-AND-LICHEN (TYPICAL) TUNDRA"}, "576": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "456": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID TALL-GRASS SAVANNAS AND SAVANNA FORESTS"}, "332": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-TUNDRA OF MODERATELY CONTINENTAL AND CONTINENTAL CLIMATE"}, "592": {"dom_desc": "POLAR DOMAIN", "pro_desc": "OCEANIC MOSS-AND-GRASS TUNDRA"}, "407": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-MEADOW OF EASTERN OCEANIC (MONSOON CLIMATE)"}, "2": {"dom_desc": "POLAR DOMAIN", "pro_desc": "ARCTIC TUNDRAS"}, "6": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS"}, "341": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "PERMANENTLY HUMID WESTERN OCEANIC BROADLEAF FORESTS"}, "568": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "569": {"dom_desc": "POLAR DOMAIN", "pro_desc": "ARCTIC TUNDRAS"}, "556": {"dom_desc": "POLAR DOMAIN", "pro_desc": "TUNDRA-POLAR DESERT"}, "560": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "561": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "467": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-MEADOW, SEASONALLY HUMID"}, "563": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "461": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "EASTERN OCEANIC CONSTANTLY HUMID FORESTS"}, "565": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "566": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "462": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "CONSTANTLY HUMID EVERGREEN FORESTS"}, "91": {"dom_desc": "DRY DOMAIN", "pro_desc": "INNER CONTINENTAL SHRUB SEMI-DESERT"}, "92": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "EASTERN OCEANIC CONSTANTLY HUMID FORESTS"}, "95": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID MIXED (DECIDUOUS AND EVERGREEN) FORESTS"}, "94": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SAVANNAS, OPEN WOODLAND AND SHRUB WITH SEASONAL MOISTURE SUPPLY"}, "97": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-STEPPE AND FOREST-MEADOW OF SEASONALLY HUMID TYPE"}, "96": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-STEPPE AND FOREST-MEADOW OF SEASONALLY HUMID TYPE"}, "11": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "PERMANENTLY HUMID EASTERN OCEANIC BROADLEAF FORESTS"}, "114": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MODERATELY HUMID GRASSY SAVANNAS"}, "19": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC FOREST-TUNDRA"}, "18": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "PERMANENTLY HUMID WESTERN OCEANIC BROADLEAF FORESTS"}, "272": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "409": {"dom_desc": "DRY DOMAIN", "pro_desc": "SHRUB AND SEMI-SHRUB SEMI-DESERTS AND DESERTS OF CONTINENTAL CLIMATE"}, "274": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "396": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC MIXED SCLEROPHYLL FORESTS AND SHRUB"}, "559": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "558": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "557": {"dom_desc": "POLAR DOMAIN", "pro_desc": "OCEANIC MOSS-AND-GRASS TUNDRA"}, "394": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "SUBTROPICAL PRAIRIES (HUMID STEPPES AND WOODED STEPPES) OF EASTERN PARTS OF CONTINENTS"}, "322": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL MOSS-AND-LICHEN (TYPICAL) TUNDRA"}, "323": {"dom_desc": "POLAR DOMAIN", "pro_desc": "OPEN WOODLAND-TUNDRA"}, "321": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL DARK EVERGREEN NEEDLELEAF TAYGA"}, "326": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL MIXED CONIFEROUS AND SMALL-LEAFED FOREST"}, "325": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL DARK EVERGREEN NEEDLELEAF TAYGA"}, "9": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "PERMANENTLY HUMID EASTERN OCEANIC BROADLEAF FORESTS"}, "329": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC CONIFEROUS AND MIXED FORESTS"}, "562": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "619": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC FOREST-TUNDRA"}, "612": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC FOREST-TUNDRA"}, "564": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "611": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MEADOW-TUNDRA"}, "617": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC FOREST-TUNDRA"}, "567": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "480": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "357": {"dom_desc": "POLAR DOMAIN", "pro_desc": "OCEANIC FOREST-TUNDRA"}, "473": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MODERATELY HUMID GRASSY SAVANNAS"}, "353": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "MODERATE CONTINENTAL MIXED FORESTS"}, "474": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "CONSTANTLY HUMID EVERGREEN FORESTS"}, "350": {"dom_desc": "POLAR DOMAIN", "pro_desc": "EASTERN OCEANIC TAYGA"}, "479": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "572": {"dom_desc": "POLAR DOMAIN", "pro_desc": ""}, "359": {"dom_desc": "POLAR DOMAIN", "pro_desc": "EASTERN OCEANIC TAYGA"}}, "grid": [" ", " ", " ", " ", " ! ", " ! !!!! ", " !!!!!!!! ## # ", " !!!!!!!!!! ##### ", " !!$!$$!!!!! ####%%# ", " &&!!$$$$$$$!! ' (%%#%%%%# ", " &&!!$$$$$$$!!)'' (%%%%%%%## * ", " &&!!!!!!!!!! )''(((%%%%%%## ** ", " & !!! !!!!! %%%%%(%%%%%%%* * ", " &&& !!++!,,%%%%%%%%%%%%**** ", " --&!!!+++,,,%%%%%%%%%%%%**** ", " &-&!!+++ ,%%%%%%%%%%%%**** ", " .. &-&!++ ,,%%%%%%%%%%%%%%** ", " / ..00&&!!+++,,,,%%%%%%%%%%%%%%%* ", " / .. 0& !!+++%%%%%%%%%%%%%%%%%%%* ", " 1 2 !!!!+ %%%%%%%%%%%%%%%%%%* ", " 333 1 4 !!!!! %%%%%%%%%%%%%%%%%%* ", " 3333 5 666447!!! %%%%%%%%%%%%%%%%%* ", " 5555 66677788 % %%%%%%%%%%%%%%* ", " 5555 66997888 %%%%%%%%%%%%%%* ", " :::5555 ; < => %%%%%%%%%%%%** ", " ?? @::A BB;;;<<<= %%%%%%%%%%%%* ", " @@@AA AACBB;;<<<<<< %%%%%%%%%%%%D ", " E @@FAAAA BBG <<<<<<<< %%%%%%%%%%DD ", "H EEEEEEI J FFAAA GG <<KKK<<< %%%%%%%%%%%% ", "HH IIEEEIIIIJJJJJJJ FFFAAA GG GK KK<< LL%%%%%%%%% ", "MMH IIIIIIIIIIINNJJJJJJJ JAAOOGG G PK<< LL%%%%%%%% ", "MMM MMIIIIIIQQRRNNNNNSNJJJJJJJGGGGG P KK< TT%%%%%% ", "MMMMMUUUUUUUQQQRRRNNNSSNJJJJJJJJGGG KVKK< TT%%%%% WXX ", "YM M UUUUUUUQQRRRRRRRSNNNJJJJJJJJZ KKK[KK TT%%% WXWX ", " UUU]]]^^RQRRRRRRNNNNJJJJJJJJZZZKKK_KK T%%% WWW ", " QU]]^``^RRRRRRRNNaaNNJJJJJ b c K_K %%%% ", " QQdd^````^^RRRRReeafNNNNJJ cccc %%% ", " Qdd```ggg^^^RRheeeeffNNNN cccc i % ", " dj^ ^^^RhheeeefffNNN cccNii k", " lmm ^hhhheeeeefnnnNJ cccNiii kk", " l ^hhhheeeeennnnNNJ cNNNNii ko", " p^^hhhqqqqqernnnnN NNNNNNNi ssso", " ^hhtqquuuqrnnnnNNNNNNvvvvN wsxo", " ythttuzzzuq{{nnnNNvvvvvv|| oo", " yytttzzzzzu{{}}{nnvvv~\u007f \u0080\u0080 \u0081\u0081", " \u0082\u0083\u0083ttzzzzuu}}}}{{{{{\u007f\u007f\u007f \u0080\u0080 \u0081", " \u0082\u0083\u0084tt\u0084\u0085zzzu\u0086{}}}{{{\u007f\u007f\u007f\u007f ", " ^\u0084\u0084\u0084\u0087\u0084\u0085zzuu\u0088\u0088}\u0086}{\u0089\u008a\u008a \u008b\u008b\u008c", " \u008d\u008e\u0084\u0084\u0087\u0087\u0085\u0085zu\u0088\u0088\u0088\u0088\u0086\u0089\u0089\u008f\u0090\u008a \u0091\u0091\u0092", " \u008d\u0093\u0084\u0084\u0084\u0087\u0085\u0085zuu\u0094\u0088\u0088\u0086\u0089\u0095\u0095 \u0091\u0091\u0092", " \u0096\u008e\u0084\u0084\u0084\u0085z\u0097\u0098\u0099\u0099\u0094\u0095\u0095\u0095\u0095\u0095 \u009a ", " \u0096\u0096\u009b\u009c\u009d\u009d\u0085\u0097\u0098\u0094\u0099\u0095\u0095\u0095\u0095\u0095 \u009e\u009a\u009f", " \u0096\u009b\u00a0\u00a1\u00a2\u00a2\u0097\u0098\u0094\u0095\u0095\u0095\u0095\u0095 \u009a\u00a3\u00a3", " \u00a4\u00a0\u00a1\u00a5\u00a6\u00a6\u0097\u0094\u0095\u0095 \u0095\u0095 \u00a3\u00a3\u00a7\u00a7", " \u00a8\u00a4\u00a0\u00a5\u00a2\u00a6\u0097 \u00a9 \u00aa\u00a7\u00a7\u00a7\u00a7", " \u00a8\u00a4\u00a1\u00a5\u00a5\u00a6 \u00aa\u00a7\u00a7\u00a7\u00a7", " \u00ab\u00a1\u00a5\u00a5 \u00ac\u00ad\u00ae\u00ae\u00ae\u00ae \u00aa\u00a7\u00a7\u00a7\u00a7\u00a7", " \u00af\u00af\u00b0\u00b1\u00ad\u00ad \u00b2 \u00b3\u00b3\u00b4 \u00b5\u00b5\u00b5\u00b6\u00b6\u00b6", " \u00af\u00af\u00b7\u00b8\u00b8\u00b9 \u00b5\u00b5\u00b5\u00b5\u00b5\u00b5", " \u00b7\u00af\u00b8 \u00ba\u00ba\u00ba\u00ba\u00ba\u00ba", " \u00af \u00bb\u00bc\u00bd\u00be\u00bf\u00c0 \u00c1\u00c2\u00c2\u00c2\u00ba", " \u00c3\u00c4\u00c4\u00c5\u00c6\u00c6\u00c7\u00c8\u00c0 \u00c1\u00c1\u00c1\u00c1\u00c1", " \u00c5\u00c5\u00c9\u00c7\u00ca\u00ca\u00cb\u00c8\u00c8 \u00cc\u00cc\u00c1\u00c1", " \u00cd\u00ce\u00ce\u00ce\u00ce\u00ce\u00ce\u00cf\u00c8\u00d0\u00c0 "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/0/1.json b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/0/1.json
new file mode 100644
index 0000000..d3a0f0d
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/0/1.json
@@ -0,0 +1 @@
+{"keys": ["", "487", "474", "483", "489", "161", "492", "494", "171", "162", "459", "173", "164", "172", "502", "505", "175", "166", "511", "508", "510", "512", "519", "518", "177", "168", "176", "517", "522", "528", "538", "541", "542", "534", "549", "235", "237", "550", "239", "241", "240", "243", "242", "244", "245", "246", "247", "552", "248", "249", "553", "268", "555", "554"], "data": {"459": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW"}, "489": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "555": {"dom_desc": "POLAR DOMAIN", "pro_desc": ""}, "554": {"dom_desc": "POLAR DOMAIN", "pro_desc": ""}, "510": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID, PREDOMINANTLY DECIDUOUS FORESTS"}, "550": {"dom_desc": "DRY DOMAIN", "pro_desc": "STEPPES AND SHRUB OF MODERATE CONTINENTAL CLIMATE"}, "553": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "552": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MEADOW"}, "487": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "DRY SAVANNAS AND OPEN WOODLAND"}, "239": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MIXED CONSTANTLY HUMID FORESTS"}, "176": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID MIXED (DECIDUOUS AND EVERGREEN) FORESTS"}, "177": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-STEPPE AND FOREST-MEADOW OF SEASONALLY HUMID TYPE"}, "235": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC MIXED SCLEROPHYLL FORESTS AND SHRUB"}, "175": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "EASTERN OCEANIC CONSTANTLY HUMID FORESTS"}, "237": {"dom_desc": "DRY DOMAIN", "pro_desc": "SHRUB AND SEMI-SHRUB SEMI-DESERTS AND DESERTS OF CONTINENTAL CLIMATE"}, "173": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID MIXED (DECIDUOUS AND EVERGREEN) FORESTS"}, "172": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MODERATELY HUMID GRASSY SAVANNAS"}, "171": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERT-LIKE SAVANNAS, OPEN WOODLAND, AND SHRUB"}, "483": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID FORESTS WITH SHORT DRY SEASON"}, "248": {"dom_desc": "DRY DOMAIN", "pro_desc": "STEPPES OF MODERATELY CONTINENTAL CLIMATE"}, "542": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "SUBTROPICAL PRAIRIES (HUMID STEPPES AND WOODED STEPPES) OF EASTERN PARTS OF CONTINENTS"}, "541": {"dom_desc": "DRY DOMAIN", "pro_desc": "DRY STEPPES, OPEN WOODLAND, AND SHRUB OF CONTINENTAL CLIMATE"}, "492": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID, PREDOMINANTLY DECIDUOUS FORESTS"}, "508": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "DRY SAVANNAS AND OPEN WOODLAND"}, "505": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID, PREDOMINANTLY DECIDUOUS FORESTS"}, "502": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID TALL-GRASS SAVANNAS AND SAVANNA FORESTS"}, "522": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MIXED CONSTANTLY HUMID FORESTS"}, "245": {"dom_desc": "DRY DOMAIN", "pro_desc": "DRY STEPPES OF CONTINENTAL CLIMATE"}, "244": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC CONIFEROUS AND MIXED FORESTS"}, "247": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "MODERATE CONTINENTAL MIXED FORESTS"}, "246": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC CONIFEROUS AND MIXED FORESTS"}, "241": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC CONSTANTLY HUMID FOREST-ALPINE MEADOWS"}, "240": {"dom_desc": "DRY DOMAIN", "pro_desc": "SEMI-DESERTS OF CONTINENTAL CLIMATE"}, "243": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS"}, "242": {"dom_desc": "DRY DOMAIN", "pro_desc": "SHRUB AND SEMI-SHRUB SEMI-DESERTS OF CONTINENTAL CLIMATE"}, "164": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-STEPPE, INNER CONTINENTAL AND LEEWARD SLOPES"}, "166": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERT-STEPPE"}, "268": {"dom_desc": "POLAR DOMAIN", "pro_desc": ""}, "249": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MEADOW"}, "161": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERT-LIKE SAVANNAS, OPEN WOODLAND, AND SHRUB"}, "162": {"dom_desc": "DRY DOMAIN", "pro_desc": "WESTERN OCEANIC SEMI-DESERTS AND DESERTS WITH HIGH RELATIVE HUMIDITY"}, "549": {"dom_desc": "DRY DOMAIN", "pro_desc": "FOREST-MEADOW-STEPPE OF CONTINENTAL CLIMATE"}, "494": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "DRY SAVANNAS AND OPEN WOODLAND"}, "528": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-SHRUB-DESERT"}, "519": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERT-LIKE SAVANNAS, OPEN WOODLAND, AND SHRUB"}, "518": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SAVANNAS, OPEN WOODLAND AND SHRUB WITH SEASONAL MOISTURE SUPPLY"}, "534": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OPEN WOODLAND, SAVANNAS, AND SHRUB OF EASTERN PARTS OF CONTINENTS"}, "474": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "CONSTANTLY HUMID EVERGREEN FORESTS"}, "511": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERT"}, "168": {"dom_desc": "DRY DOMAIN", "pro_desc": "WESTERN OCEANIC SEMI-DESERTS AND DESERTS WITH HIGH RELATIVE HUMIDITY"}, "512": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-STEPPE"}, "517": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-MEADOW OF CONSTANTLY HUMID EASTERN OCEANIC TYPE"}, "538": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERT-STEPPE AND DESERT-STEPPE-DESERT OF CONTINENTAL CLIMATE"}}, "grid": [" !######$$$$% ", " &!######$$%%'()) ", " *+#####$%''''()), ", " -+###%%''''.(),, ", " -#//((''(.(((, ", " *-++/0(((((((1 ", " 232445/(((((1 ", " 336778((((91 ", " :36778(;;<< ", " :36788==< ", " 3>7788== ", " ??@AABBB ", " ??CAABB ", " D?EFAAB ", " G?@FFF ", " GHI@FF ", " JJIK ", " LMII ", " NOI ", " NJOI ", " JJOI ", " JM P ", " JQ ", " JRJ ", " ", " ", " ", " ", " ", " S ", " SS ", " SS ", " SSS ", " S ", " TSU ", " TUU ", " TTUUU V ", " TTTUUU VV", " TTTUUU V", " UU ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/0/2.json b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/0/2.json
new file mode 100644
index 0000000..a0e62f4
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/0/2.json
@@ -0,0 +1 @@
+{"keys": [""], "data": {}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/1/0.json b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/1/0.json
new file mode 100644
index 0000000..0c2dede
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/1/0.json
@@ -0,0 +1 @@
+{"keys": ["", "276", "593", "277", "595", "594", "602", "604", "596", "603", "597", "278", "280", "606", "282", "283", "284", "288", "607", "608", "598", "600", "290", "291", "293", "301", "296", "300", "601", "605", "609", "298", "303", "599", "304", "305", "306", "302", "307", "308", "610", "311", "316", "314", "317", "320", "324", "328", "330", "327", "333", "334", "335", "338", "25", "26", "342", "340", "87", "341", "27", "344", "345", "343", "347", "68", "346", "348", "351", "71", "32", "28", "29", "30", "352", "356", "355", "74", "69", "72", "31", "358", "363", "360", "65", "362", "70", "34", "35", "369", "66", "366", "368", "374", "44", "52", "53", "54", "373", "375", "372", "371", "79", "380", "38", "40", "379", "56", "78", "46", "41", "384", "382", "57", "381", "80", "82", "47", "48", "393", "58", "387", "62", "389", "390", "391", "392", "388", "86", "83", "400", "405", "404", "49", "403", "60", "401", "402", "406", "397", "419", "61", "408", "418", "414", "411", "415", "410", "85", "417", "84", "416", "422", "425", "421", "423", "426", "434", "430", "428", "436", "136", "138", "435", "438", "439", "126", "146", "149", "437", "128", "140", "141", "150", "153", "155", "152", "443", "127", "154", "448", "451", "129", "131", "143", "452", "132", "130", "133", "455", "134", "142", "454", "157", "158", "450", "121", "457", "144", "159", "160", "453", "122", "123", "124", "470", "465", "471", "472", "135", "145", "469", "464", "466", "478", "475", "463"], "data": {"133": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-SHRUB-DESERT"}, "132": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-MEADOW, SEASONALLY HUMID"}, "131": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-SHRUB-DESERT"}, "130": {"dom_desc": "DRY DOMAIN", "pro_desc": "INNER CONTINENTAL DESERTS OF CONTINENTAL CLIMATE"}, "136": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MODERATELY HUMID GRASSY SAVANNAS"}, "135": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-STEPPE, INNER CONTINENTAL AND LEEWARD SLOPES"}, "134": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERT-LIKE SAVANNAS, OPEN WOODLAND, AND SHRUB"}, "138": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID, PREDOMINANTLY DECIDUOUS FORESTS"}, "25": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "PERMANENTLY HUMID WESTERN OCEANIC BROADLEAF FORESTS"}, "26": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "PERMANENTLY HUMID WESTERN OCEANIC BROADLEAF FORESTS"}, "27": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "MODERATELY HUMID BROADLEAF FOREST IN MODERATELY CONTINENTAL CLIMATE"}, "28": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS"}, "29": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "MODERATELY HUMID BROADLEAF FOREST IN MODERATELY CONTINENTAL CLIMATE"}, "344": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "SMALL-LEAFED AND CONIFEROUS WOODED STEPPES OF CONTINENTAL CLIMATE"}, "345": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "CONTINENTAL STEPPE-FOREST-TUNDRA AND STEPPE-FOREST-MEADOW"}, "346": {"dom_desc": "DRY DOMAIN", "pro_desc": "STEPPES OF MODERATELY CONTINENTAL CLIMATE"}, "347": {"dom_desc": "POLAR DOMAIN", "pro_desc": "FOREST-TUNDRA OF MODERATELY AND CONTINENTAL CLIMATE"}, "340": {"dom_desc": "POLAR DOMAIN", "pro_desc": "MODERATE CONTINENTAL SMALL-LEAFED FOREST"}, "341": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "PERMANENTLY HUMID WESTERN OCEANIC BROADLEAF FORESTS"}, "342": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "BROADLEAF-WOODED STEPPES AND MEADOW STEPPES OF MODERATELY CONTINENTAL CLIMATE"}, "343": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "SMALL-LEAFED AND CONIFEROUS WOODED STEPPES OF CONTINENTAL CLIMATE"}, "280": {"dom_desc": "POLAR DOMAIN", "pro_desc": ""}, "283": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "282": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "348": {"dom_desc": "DRY DOMAIN", "pro_desc": "DRY STEPPES OF CONTINENTAL CLIMATE"}, "284": {"dom_desc": "POLAR DOMAIN", "pro_desc": "ARCTIC TUNDRAS"}, "408": {"dom_desc": "DRY DOMAIN", "pro_desc": "SEMI-DESERTS AND DESERTS"}, "455": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "DRY SAVANNAS AND OPEN WOODLAND"}, "121": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-STEPPE, INNER CONTINENTAL AND LEEWARD SLOPES"}, "122": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "123": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID FORESTS WITH SHORT DRY SEASON"}, "124": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW"}, "126": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERT"}, "127": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-SHRUB-DESERT"}, "128": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-STEPPE AND FOREST-MEADOW OF SEASONALLY HUMID TYPE"}, "129": {"dom_desc": "DRY DOMAIN", "pro_desc": "INNER CONTINENTAL SHRUB SEMI-DESERT"}, "69": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC FOREST-CREEPING TREES"}, "58": {"dom_desc": "DRY DOMAIN", "pro_desc": "SHRUB AND SEMI-SHRUB SEMI-DESERTS AND DESERTS OF CONTINENTAL CLIMATE"}, "425": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-SHRUB-DESERT"}, "57": {"dom_desc": "DRY DOMAIN", "pro_desc": "DRY STEPPES AND SHRUB OF MODERATE CONTINENTAL CLIMATE"}, "56": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "SHRUB-FOREST-MEADOW OF MEDITERRANEAN CLIMATE"}, "53": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS"}, "52": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS"}, "379": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC CONSTANTLY HUMID FOREST-ALPINE MEADOWS"}, "415": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MIXED CONSTANTLY HUMID FORESTS"}, "416": {"dom_desc": "DRY DOMAIN", "pro_desc": "SHRUB AND SEMI-SHRUB SEMI-DESERTS OF CONTINENTAL CLIMATE"}, "417": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MIXED CONSTANTLY HUMID FORESTS"}, "410": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "SUBTROPICAL PRAIRIES (HUMID STEPPES AND WOODED STEPPES) OF EASTERN PARTS OF CONTINENTS"}, "411": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-MEADOW OF EASTERN OCEANIC (MONSOON CLIMATE)"}, "298": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC TAYGA"}, "54": {"dom_desc": "", "pro_desc": ""}, "296": {"dom_desc": "POLAR DOMAIN", "pro_desc": "TUNDRA-POLAR DESERT"}, "373": {"dom_desc": "DRY DOMAIN", "pro_desc": "EXTREME CONTINENTAL DESERT-STEPPE"}, "372": {"dom_desc": "DRY DOMAIN", "pro_desc": "SEMI-DESERTS OF EXTREME CONTINENTAL CLIMATE"}, "375": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERTS OF EXTREME CONTINENTAL CLIMATE"}, "293": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL LIGHT DECIDUOUS NEEDLELEAF OPEN FOREST"}, "290": {"dom_desc": "POLAR DOMAIN", "pro_desc": "OPEN WOODLAND-CREEPING TREES-TUNDRA"}, "291": {"dom_desc": "POLAR DOMAIN", "pro_desc": "ARCTIC TUNDRAS"}, "593": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "443": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERT-LIKE SAVANNAS, OPEN WOODLAND, AND SHRUB"}, "595": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERT"}, "594": {"dom_desc": "POLAR DOMAIN", "pro_desc": ""}, "597": {"dom_desc": "POLAR DOMAIN", "pro_desc": ""}, "596": {"dom_desc": "POLAR DOMAIN", "pro_desc": ""}, "599": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL MOSS-AND-LICHEN (TYPICAL) TUNDRA"}, "598": {"dom_desc": "POLAR DOMAIN", "pro_desc": "ARCTIC TUNDRAS"}, "311": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL DARK EVERGREEN NEEDLELEAF OPEN FOREST"}, "317": {"dom_desc": "POLAR DOMAIN", "pro_desc": "OPEN WOODLAND-TUNDRA"}, "316": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL DARK EVERGREEN NEEDLELEAF TAYGA"}, "314": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL AND EXTREME CONTINENTAL LIGHT DECIDUOUS TAYGA"}, "393": {"dom_desc": "DRY DOMAIN", "pro_desc": "FOREST-MEADOW-STEPPE OF CONTINENTAL CLIMATE"}, "392": {"dom_desc": "DRY DOMAIN", "pro_desc": "FOREST-MEADOW-STEPPE OF CONTINENTAL CLIMATE"}, "391": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERT-STEPPE AND DESERT-STEPPE-DESERT OF CONTINENTAL CLIMATE"}, "390": {"dom_desc": "DRY DOMAIN", "pro_desc": "EXTREME CONTINENTAL DESERT"}, "397": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "PERMANENTLY HUMID EASTERN OCEANIC BROADLEAF FORESTS"}, "276": {"dom_desc": "POLAR DOMAIN", "pro_desc": ""}, "277": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "278": {"dom_desc": "POLAR DOMAIN", "pro_desc": "ARCTIC TUNDRAS"}, "83": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "PERMANENTLY HUMID EASTERN OCEANIC BROADLEAF FORESTS"}, "80": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS"}, "86": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "PERMANENTLY HUMID EASTERN OCEANIC BROADLEAF FORESTS"}, "87": {"dom_desc": "POLAR DOMAIN", "pro_desc": "EASTERN OCEANIC TAYGA"}, "84": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MIXED CONSTANTLY HUMID FORESTS"}, "85": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MIXED CONSTANTLY HUMID FORESTS"}, "414": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-MEADOW OF CONSTANTLY HUMID EASTERN OCEANIC TYPE"}, "428": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-STEPPE, INNER CONTINENTAL AND LEEWARD SLOPES"}, "368": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "PERMANENTLY HUMID EASTERN OCEANIC BROADLEAF FORESTS"}, "369": {"dom_desc": "", "pro_desc": ""}, "366": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "TEMPERATE PRAIRIES (HUMID STEPPES AND WOODED STEPPES) OF EASTERN PARTS OF CONTINENTS"}, "423": {"dom_desc": "DRY DOMAIN", "pro_desc": "INNER CONTINENTAL SHRUB SEMI-DESERT"}, "422": {"dom_desc": "DRY DOMAIN", "pro_desc": "INNER CONTINENTAL SEMI-DESERTS AND DESERTS OF EXTREME CONTINENTAL CLIMATE"}, "362": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "MODERATELY HUMID BROADLEAF FOREST IN MODERATELY CONTINENTAL CLIMATE"}, "363": {"dom_desc": "DRY DOMAIN", "pro_desc": "CONTINENTAL OPEN WOODLAND-STEPPE"}, "360": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERTS OF EXTREME CONTINENTAL CLIMATE"}, "426": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-STEPPE"}, "308": {"dom_desc": "POLAR DOMAIN", "pro_desc": "OPEN WOODLAND-TUNDRA"}, "448": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "300": {"dom_desc": "POLAR DOMAIN", "pro_desc": "TUNDRA-POLAR DESERT"}, "301": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC FOREST-TUNDRA"}, "302": {"dom_desc": "POLAR DOMAIN", "pro_desc": "MODERATE CONTINENTAL DARK EVERGREEN NEEDLELEAF TAYGA"}, "303": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL DARK EVERGREEN NEEDLELEAF OPEN FOREST"}, "304": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL LIGHT DECIDUOUS NEEDLELEAF OPEN FOREST"}, "305": {"dom_desc": "POLAR DOMAIN", "pro_desc": "OPEN WOODLAND-TUNDRA"}, "306": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL BUSH-AND-SHRUB TUNDRA"}, "307": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL MOSS-AND-LICHEN (TYPICAL) TUNDRA"}, "380": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "SHRUB-FOREST-MEADOW OF MEDITERRANEAN CLIMATE"}, "371": {"dom_desc": "DRY DOMAIN", "pro_desc": "STEPPES OF MODERATELY CONTINENTAL CLIMATE"}, "382": {"dom_desc": "DRY DOMAIN", "pro_desc": "DRY STEPPES, OPEN WOODLAND, AND SHRUB OF CONTINENTAL CLIMATE"}, "384": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS OF WESTERN OCEANIC (MEDITERRANEAN) CLIMATE"}, "406": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS"}, "387": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-STEPPE OF CONTINENTAL CLIMATE"}, "388": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "TEMPERATE PRAIRIES (HUMID STEPPES AND WOODED STEPPES) OF EASTERN PARTS OF CONTINENTS"}, "389": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-STEPPE OF CONTINENTAL CLIMATE"}, "607": {"dom_desc": "POLAR DOMAIN", "pro_desc": "ARCTIC TUNDRAS"}, "38": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "SHRUB-FOREST-MEADOW OF MEDITERRANEAN CLIMATE"}, "381": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS"}, "32": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "PERMANENTLY HUMID WESTERN OCEANIC BROADLEAF FORESTS"}, "31": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "BROADLEAF-WOODED STEPPES AND MEADOW STEPPES OF MODERATELY CONTINENTAL CLIMATE"}, "30": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS"}, "35": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS OF WESTERN OCEANIC (MEDITERRANEAN) CLIMATE"}, "34": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "PERMANENTLY HUMID WESTERN OCEANIC BROADLEAF FORESTS"}, "438": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID, PREDOMINANTLY DECIDUOUS FORESTS"}, "439": {"dom_desc": "DRY DOMAIN", "pro_desc": "INNER CONTINENTAL SHRUB SEMI-DESERT"}, "436": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-STEPPE"}, "437": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW OF CONSTANTLY HUMID OCEANIC (AND WINDWARD-SLOPE) TYPE"}, "434": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "EASTERN OCEANIC CONSTANTLY HUMID FORESTS"}, "435": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "430": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW OF CONSTANTLY HUMID OCEANIC (AND WINDWARD-SLOPE) TYPE"}, "338": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "PERMANENTLY HUMID WESTERN OCEANIC BROADLEAF FORESTS"}, "604": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "335": {"dom_desc": "POLAR DOMAIN", "pro_desc": "FOREST-TUNDRA OF MODERATELY AND CONTINENTAL CLIMATE"}, "334": {"dom_desc": "POLAR DOMAIN", "pro_desc": "FOREST-CREEPING TREES-TUNDRA OF EXTREME CONTINENTAL CLIMATE"}, "452": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "453": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "454": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "330": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "MODERATE CONTINENTAL MIXED FORESTS"}, "333": {"dom_desc": "POLAR DOMAIN", "pro_desc": "FOREST-TUNDRA OF MODERATELY AND CONTINENTAL CLIMATE"}, "457": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID TALL-GRASS SAVANNAS AND SAVANNA FORESTS"}, "60": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC CONSTANTLY HUMID FOREST-ALPINE MEADOWS"}, "61": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS OF WESTERN OCEANIC (MEDITERRANEAN) CLIMATE"}, "62": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERT-STEPPE AND DESERT-STEPPE-DESERT OF CONTINENTAL CLIMATE"}, "606": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "65": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "CONTINENTAL STEPPE-FOREST-TUNDRA AND STEPPE-FOREST-MEADOW"}, "66": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "CONTINENTAL STEPPE-FOREST-TUNDRA AND STEPPE-FOREST-MEADOW"}, "68": {"dom_desc": "POLAR DOMAIN", "pro_desc": "EASTERN OCEANIC TAYGA"}, "601": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL MOSS-AND-LICHEN (TYPICAL) TUNDRA"}, "600": {"dom_desc": "POLAR DOMAIN", "pro_desc": "ARCTIC TUNDRAS"}, "603": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "288": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL BUSH-AND-SHRUB TUNDRA"}, "405": {"dom_desc": "DRY DOMAIN", "pro_desc": "SHRUB AND SEMI-SHRUB SEMI-DESERTS OF CONTINENTAL CLIMATE"}, "404": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-STEPPE OF CONTINENTAL CLIMATE"}, "403": {"dom_desc": "DRY DOMAIN", "pro_desc": "SHRUB AND SEMI-SHRUB SEMI-DESERTS AND DESERTS OF CONTINENTAL CLIMATE"}, "402": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-STEPPE OF CONTINENTAL CLIMATE"}, "469": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "CONSTANTLY HUMID EVERGREEN FORESTS"}, "401": {"dom_desc": "DRY DOMAIN", "pro_desc": "SEMI-DESERTS AND DESERTS"}, "465": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "464": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "CONSTANTLY HUMID EVERGREEN FORESTS"}, "400": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "SHRUB-FOREST-MEADOW OF MEDITERRANEAN CLIMATE"}, "463": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "CONSTANTLY HUMID EVERGREEN FORESTS"}, "160": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW"}, "419": {"dom_desc": "DRY DOMAIN", "pro_desc": "INNER CONTINENTAL DESERTS OF CONTINENTAL CLIMATE"}, "605": {"dom_desc": "POLAR DOMAIN", "pro_desc": "OPEN WOODLAND-TUNDRA"}, "150": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID TALL-GRASS SAVANNAS AND SAVANNA FORESTS"}, "153": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MODERATELY HUMID GRASSY SAVANNAS"}, "152": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW OF CONSTANTLY HUMID OCEANIC (AND WINDWARD-SLOPE) TYPE"}, "155": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID TALL-GRASS SAVANNAS AND SAVANNA FORESTS"}, "154": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "EASTERN OCEANIC CONSTANTLY HUMID FORESTS"}, "157": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "602": {"dom_desc": "POLAR DOMAIN", "pro_desc": ""}, "159": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW OF CONSTANTLY HUMID OCEANIC (AND WINDWARD-SLOPE) TYPE"}, "158": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "609": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL BUSH-AND-SHRUB TUNDRA"}, "608": {"dom_desc": "POLAR DOMAIN", "pro_desc": "POLAR DESERTS"}, "82": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "EASTERN OCEANIC MIXED MONSOON FOREST"}, "466": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW"}, "48": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS OF WESTERN OCEANIC (MEDITERRANEAN) CLIMATE"}, "49": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OPEN WOODLAND, SAVANNAS, AND SHRUB OF EASTERN PARTS OF CONTINENTS"}, "46": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC MIXED SCLEROPHYLL FORESTS AND SHRUB"}, "47": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC MIXED SCLEROPHYLL FORESTS AND SHRUB"}, "44": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC MIXED SCLEROPHYLL FORESTS AND SHRUB"}, "470": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID FORESTS WITH SHORT DRY SEASON"}, "40": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS OF WESTERN OCEANIC (MEDITERRANEAN) CLIMATE"}, "41": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC MIXED SCLEROPHYLL FORESTS AND SHRUB"}, "418": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SAVANNAS, OPEN WOODLAND AND SHRUB WITH SEASONAL MOISTURE SUPPLY"}, "320": {"dom_desc": "POLAR DOMAIN", "pro_desc": "OPEN WOODLAND-CREEPING TREES-TUNDRA"}, "327": {"dom_desc": "POLAR DOMAIN", "pro_desc": "OCEANIC FOREST-TUNDRA"}, "324": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "MODERATE CONTINENTAL MIXED FORESTS"}, "328": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC CONIFEROUS AND MIXED FORESTS"}, "374": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC MIXED SCLEROPHYLL FORESTS AND SHRUB"}, "146": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-STEPPE, INNER CONTINENTAL AND LEEWARD SLOPES"}, "144": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "DRY SAVANNAS AND OPEN WOODLAND"}, "145": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW"}, "142": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "143": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID, PREDOMINANTLY DECIDUOUS FORESTS"}, "140": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID, PREDOMINANTLY DECIDUOUS FORESTS"}, "141": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "DRY SAVANNAS AND OPEN WOODLAND"}, "610": {"dom_desc": "POLAR DOMAIN", "pro_desc": "CONTINENTAL LIGHT DECIDUOUS NEEDLELEAF OPEN FOREST"}, "475": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "CONSTANTLY HUMID EVERGREEN FORESTS"}, "450": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID TALL-GRASS SAVANNAS AND SAVANNA FORESTS"}, "149": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-MEADOW, SEASONALLY HUMID"}, "74": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "MODERATELY HUMID BROADLEAF FOREST IN MODERATELY CONTINENTAL CLIMATE"}, "72": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC FOREST-CREEPING TREES"}, "71": {"dom_desc": "POLAR DOMAIN", "pro_desc": "EASTERN OCEANIC TAYGA"}, "70": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "MODERATE CONTINENTAL MIXED FORESTS"}, "79": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC FOREST-TUNDRA"}, "78": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "EASTERN OCEANIC MIXED MONSOON FOREST"}, "451": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MODERATELY HUMID GRASSY SAVANNAS"}, "472": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID TALL-GRASS SAVANNAS AND SAVANNA FORESTS"}, "356": {"dom_desc": "DRY DOMAIN", "pro_desc": "DRY STEPPES OF CONTINENTAL CLIMATE"}, "355": {"dom_desc": "DRY DOMAIN", "pro_desc": "DRY STEPPES OF EXTREME CONTINENTAL CLIMATE"}, "471": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-STEPPE, INNER CONTINENTAL AND LEEWARD SLOPES"}, "352": {"dom_desc": "DRY DOMAIN", "pro_desc": "SEMI-DESERTS OF CONTINENTAL CLIMATE"}, "351": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-TUNDRA OF MODERATELY CONTINENTAL AND CONTINENTAL CLIMATE"}, "421": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-STEPPE OF CONTINENTAL CLIMATE"}, "478": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "358": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERTS OF CONTINENTAL CLIMATE"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ! ", " #### $!! ", " %%% && $!''( ", " ))%& ****(( ", " )))+ (( ", " )) + ( ", " )) + ,, ", " - ,,, ", " --- ,,,,,, .../// ", " --- ,,,,,00,, ...// ", " 1- ,,,,,00000 . ", " 11 ,,,0000200 333 ", " 11 ,,,,00222222223333 4 ", " 55 66 ,,,,002222222222733 888 ", " 55 66,,,,222222999999229738888888 ", " :;; < 6======22229999>>9999977?????88888 8 ", " @:::AAB < =======22999999>>>99997777?C?DEEE88888 ", " :FFFFABB G GGGG<=======9HHHHH9>>>9999777II77CDEEEEE777 ", " @:FFFFFFBB GGJJJJG<======9HHHHH99999999777II7CDDCCCCCC777 ", " :FFFFFFFAB GGFJJJJ<======KHHHHH9999999LL7III7CCDCCCCCC777 ", " @:FF FFFF FFFFFFFFK=K=9999KHHHHH999999LLL7II7777CCC7777777 ", " :FFFFFFFFFFFFFFFMMKKKKKKKKKLHHHH9999LLLLLL7777777CC777777 ", " @::FF FFFFFFFFFFFFMMKKKKKKKKKLLLLLLLLLLLLLLLLLL77777777777NNNN", " @::FF FOOFFFFFFFFFMMKKKKKKKKKLLLLLLLLLLLLLLLLLL7777777777NNN ", " @:FFP OQQQFFFFFFFFMMKKKKKKKKKLLLLLLLLLLLLLLLLL77777777 RRN ", " @:PPP QQQQQFFFFFFFMMKKKKKKKKKSSLLLLLLLLLLLLLLTTUU 7 R ", " VWP QQQQQQQQFFFFQMKKKKKKKKKSSLLLLLLTTTTTTTTTUU RR ", " VXW QQQQQQQQQQQYQQZZZZZZKKZZKSLLLLLTTTTTTTTTUU [RR ", "] VVV^QQQQQQQYYYYYYY________``ZabLLLTTTTTTTTTUUcc [RR ", "] VVVV^^QQQQQYYddddddeeeeeee_```bbbaLTTTTTfffUUUccg [R ", "]VVhijik^YYYYdddlllllelleeeee`bmmbbbaTTTnnnffoUUfpqq R ", "VVVhirrrrYYdddddlsssslllleeee`ttubbnnvvnnnnwwfffxpqq ", "VVViyzrrrdddddddsssss{ssllllllttun|nnnnnnnn}}ww~pp q ", "V\u007fVy\u007f\u0080\u0080rddd \u0081 \u0082ds\u0083sss{{sssss\u0084\u0084\u0085\u0086\u0086tnnnnnnnnn\u0087}~~~p \u0088\u0088 ", "\u0089\u007f\u007f\u007f\u007f\u007f\u0080\u008a\u008b\u008b \u008c \u008c\u008d\u0083\u0083sssss\u0084s\u0084\u0084\u0084\u0085\u0085\u0085\u0085\u0085\u0085\u0086\u0086\u0086\u0086nnn\u0087\u0087~~~p \u008e\u0088 ", "\u0089 \u008f \u007f\u007f\u0080\u0090\u0090\u0091\u0092\u0092\u0092\u008c\u008d\u0093\u0093\u0093sssss\u0084\u0084\u0084\u0084\u0085\u0085\u0085\u0085\u0085\u0085\u0085\u0085\u0085\u0085\u0086nn\u0094\u0094~~\u0095~ \u0096 ", "\u007f \u008f \u0097\u007f\u0090\u0098\u0091\u0091\u0092\u0092\u0092\u008d\u008d\u0099\u0099\u0083\u009a\u009b\u009bss\u009c\u009d\u009e\u0085\u0085\u0085\u0085\u009f\u009f\u009f\u00a0\u00a0\u0085\u0085\u0086n\u0094\u00a1\u00a1~~~ \u00a2\u00a3 ", "\u00a4\u00a5\u00a5\u00a6\u00a7\u0097 \u0098 \u0091\u0091\u0091\u00a8\u00a8\u00a8\u0099\u0099\u00a9\u00aa\u00aa\u009bss\u009d\u009f\u009f\u009e\u009f\u009f\u009f\u009e\u009f\u00ab\u00ab\u00a0\u00a0\u0086n\u0094\u0094\u00a1\u00ac\u00ad ~~ \u00a2\u00a2\u00a3 ", "\u00a5\u00a6\u00ae\u00ae \u00af\u00a8\u00a8\u00a8\u00a8\u0099\u00aa\u00aa\u00aa\u009b\u00b0\u009d\u009d\u00b1\u00b2\u009f\u009e\u009e\u009e\u009e\u009f\u00ab\u00ab\u00ab\u00a0\u00b3\u00b3\u00b3\u00b3\u00b4\u00b5 \u00b6 \u00b7\u00b8 ", "\u00ae\u00ae\u00ae\u00ae\u00ae\u00b9\u00b9\u00b9\u00b9\u00b9\u00b9\u00b9\u00b9\u00a8\u00ba\u00ba\u00ba\u00ba\u00bb\u00bc\u00bc\u00aa\u00b0\u00b0\u00b0\u00bd\u00b1\u00b2\u009f\u009f\u009e\u009e\u009f\u00ab\u00ab\u00ab\u00b3\u00b4\u00b4\u00b3\u00b4\u00b4\u00b4\u00b4 \u00b7 ", "\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ba\u00ba\u00ba\u00ba\u00ba\u00ba\u00bb\u00bb\u00bb\u00bc\u00b0\u00bb\u00be\u00bd\u00bd\u00bd\u00bf\u00c0\u009f\u009f\u009f\u00c1\u00c0\u00ab\u00b3\u00b4\u00b4\u00b4\u00b4\u00b4\u00b4\u00b4 ", "\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ba\u00ba\u00ba\u00ba\u00ba \u00bb\u00c2\u00bb\u00bb\u00bb\u00bd\u00bd\u00bd\u00b1\u00c3\u00bf\u00bf\u00c0\u00bf\u00bf\u00bf\u00c0\u00b3\u00b4\u00b4\u00b4\u00b4\u00b4\u00b4\u00b4 ", "\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae \u00ba\u00ba\u00ba\u00ba\u00ba\u00ba\u00c2\u00c2 \u00bd\u00bd\u00b1\u00b1\u00c3\u00c4\u00bf\u00bf\u00bf\u00bf\u00c0\u00b3\u00b3\u00b3\u00b4\u00b4\u00b4\u00c5\u00c5\u00c6 ", "\u00ae\u00c7\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00ae\u00c7\u00c8\u00ba\u00ba\u00ba\u00ba\u00ba\u00ba\u00ba\u00ba \u00bd\u00b1\u00c3\u00c3\u00c4\u00c4\u00bf\u00bf\u00bf\u00c0\u00c9\u00c9\u00ca\u00c5\u00c5\u00c5\u00c5 \u00cb ", "\u00c7\u00c7\u00c7\u00c7\u00ae\u00ae\u00ae\u00c7\u00c7\u00ae\u00ae\u00c7\u00c7\u00c8\u00cc\u00ba\u00ba\u00ba\u00ba\u00ba\u00ba \u00cd\u00c3\u00c3\u00ce\u00ce \u00bf\u00cf\u00c9\u00d0\u00d1 \u00d2 ", "\u00d3\u00d3\u00c7\u00d3\u00d3\u00c7\u00c7\u00d3\u00d3\u00c7\u00c7\u00c7\u00d3\u00d3\u00d4\u00cc\u00ba\u00ba\u00ba \u00cd\u00c3\u00ce\u00ce \u00bf\u00d5\u00d0\u00d0\u00c5 \u00d6 ", "\u00d7\u00d7\u00d7\u00d7\u00d3\u00d3\u00d7\u00d7\u00d7\u00d3\u00d3\u00d3\u00d3\u00d3\u00d8\u00d9\u00cc\u00cc \u00da\u00da\u00ce \u00d5\u00d5\u00d0\u00d1\u00c5 \u00db\u00d6 ", "\u00d7\u00d7\u00d7\u00d7\u00d7\u00d7\u00d7\u00d7\u00d7\u00d7\u00d7\u00d3\u00d3\u00dc\u00dc\u00dd\u00de\u00df\u00e0 \u00e1\u00da \u00d5 \u00d5\u00d1 \u00e2\u00e3\u00e4 ", "\u00e5\u00e5\u00e5\u00e5\u00e6\u00e7\u00e7\u00e7\u00e7\u00e7\u00e7\u00d7\u00d7\u00dc\u00dc\u00df\u00df\u00e0\u00e0 \u00e1\u00e8\u00e8 \u00d5 \u00d5 \u00e9\u00ea ", "\u00eb\u00ec\u00ed\u00ee\u00ef\u00ef\u00ef\u00ef\u00ef\u00f0\u00f0\u00f1\u00f2\u00d3\u00f3\u00df\u00df\u00df \u00f4 \u00f5 \u00f6 \u00f7\u00f7 ", " \u00ef\u00f8\u00ef\u00f9\u00f9\u00f9\u00f9\u00f9\u00f1\u00f2\u00df\u00df\u00df\u00df \u00f5\u00f5\u00f6 \u00fa\u00fa\u00f7\u00fa "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/1/1.json b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/1/1.json
new file mode 100644
index 0000000..5457be3
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/1/1.json
@@ -0,0 +1 @@
+{"keys": ["", "486", "478", "475", "471", "493", "491", "484", "455", "468", "469", "463", "466", "481", "490", "201", "207", "208", "209", "488", "495", "179", "497", "496", "185", "202", "498", "499", "500", "234", "181", "186", "203", "501", "215", "214", "213", "504", "188", "218", "217", "216", "183", "195", "189", "219", "220", "221", "506", "507", "196", "194", "509", "228", "513", "199", "514", "520", "515", "516", "182", "521", "198", "523", "525", "524", "530", "527", "537", "539", "531", "545", "544", "543", "536", "548", "546", "547", "255", "250", "254", "262", "258", "257", "259", "263", "261", "265", "264", "266", "267", "554"], "data": {"216": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "EASTERN OCEANIC CONSTANTLY HUMID FORESTS"}, "217": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "214": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID TALL-GRASS SAVANNAS AND SAVANNA FORESTS"}, "215": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-STEPPE, INNER CONTINENTAL AND LEEWARD SLOPES"}, "213": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "218": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}, "219": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID, PREDOMINANTLY DECIDUOUS FORESTS"}, "498": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW OF CONSTANTLY HUMID OCEANIC (AND WINDWARD-SLOPE) TYPE"}, "499": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW OF CONSTANTLY HUMID OCEANIC (AND WINDWARD-SLOPE) TYPE"}, "495": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW"}, "496": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID, PREDOMINANTLY DECIDUOUS FORESTS"}, "497": {"dom_desc": "", "pro_desc": ""}, "490": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID FORESTS WITH SHORT DRY SEASON"}, "491": {"dom_desc": "", "pro_desc": ""}, "493": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID TALL-GRASS SAVANNAS AND SAVANNA FORESTS"}, "543": {"dom_desc": "DRY DOMAIN", "pro_desc": "DRY STEPPES AND SHRUB OF MODERATE CONTINENTAL CLIMATE"}, "546": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "SUBTROPICAL PRAIRIES (HUMID STEPPES AND WOODED STEPPES) OF EASTERN PARTS OF CONTINENTS"}, "547": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "SUBTROPICAL PRAIRIES (HUMID STEPPES AND WOODED STEPPES) OF EASTERN PARTS OF CONTINENTS"}, "544": {"dom_desc": "DRY DOMAIN", "pro_desc": "SHRUB AND SEMI-SHRUB SEMI-DESERTS AND DESERTS OF CONTINENTAL CLIMATE"}, "545": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC MIXED SCLEROPHYLL FORESTS AND SHRUB"}, "548": {"dom_desc": "DRY DOMAIN", "pro_desc": "DRY STEPPES AND SHRUB OF MODERATE CONTINENTAL CLIMATE"}, "263": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "SUBTROPICAL PRAIRIES (HUMID STEPPES AND WOODED STEPPES) OF EASTERN PARTS OF CONTINENTS"}, "262": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MIXED CONSTANTLY HUMID FORESTS"}, "261": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS"}, "267": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC CONIFEROUS AND MIXED FORESTS"}, "266": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "TEMPERATE PRAIRIES (HUMID STEPPES AND WOODED STEPPES) OF EASTERN PARTS OF CONTINENTS"}, "265": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MIXED CONSTANTLY HUMID FORESTS"}, "264": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-ALPINE MEADOWS"}, "537": {"dom_desc": "DRY DOMAIN", "pro_desc": "SHRUB AND SEMI-SHRUB SEMI-DESERTS OF CONTINENTAL CLIMATE"}, "536": {"dom_desc": "DRY DOMAIN", "pro_desc": "SHRUB AND SEMI-SHRUB SEMI-DESERTS OF CONTINENTAL CLIMATE"}, "531": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MIXED CONSTANTLY HUMID FORESTS"}, "530": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OPEN WOODLAND, SAVANNAS, AND SHRUB OF EASTERN PARTS OF CONTINENTS"}, "539": {"dom_desc": "DRY DOMAIN", "pro_desc": "SHRUB AND SEMI-SHRUB SEMI-DESERTS AND DESERTS OF CONTINENTAL CLIMATE"}, "199": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "EASTERN OCEANIC CONSTANTLY HUMID FORESTS"}, "198": {"dom_desc": "DRY DOMAIN", "pro_desc": "INNER CONTINENTAL SHRUB SEMI-DESERT"}, "195": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID TALL-GRASS SAVANNAS AND SAVANNA FORESTS"}, "194": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-STEPPE AND FOREST-MEADOW OF SEASONALLY HUMID TYPE"}, "196": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SAVANNAS, OPEN WOODLAND AND SHRUB WITH SEASONAL MOISTURE SUPPLY"}, "524": {"dom_desc": "DRY DOMAIN", "pro_desc": "OPEN WOODLAND-STEPPE OF CONTINENTAL CLIMATE"}, "525": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MIXED CONSTANTLY HUMID FORESTS"}, "527": {"dom_desc": "DRY DOMAIN", "pro_desc": "SHRUB AND SEMI-SHRUB SEMI-DESERTS OF CONTINENTAL CLIMATE"}, "520": {"dom_desc": "DRY DOMAIN", "pro_desc": "INNER CONTINENTAL SHRUB SEMI-DESERT"}, "521": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-MEADOW OF CONSTANTLY HUMID EASTERN OCEANIC TYPE"}, "523": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OPEN WOODLAND, SAVANNAS, AND SHRUB OF EASTERN PARTS OF CONTINENTS"}, "513": {"dom_desc": "DRY DOMAIN", "pro_desc": "WESTERN OCEANIC SEMI-DESERTS AND DESERTS WITH HIGH RELATIVE HUMIDITY"}, "515": {"dom_desc": "DRY DOMAIN", "pro_desc": "INNER CONTINENTAL DESERTS OF CONTINENTAL CLIMATE"}, "514": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERT-LIKE SAVANNAS, OPEN WOODLAND, AND SHRUB"}, "516": {"dom_desc": "DRY DOMAIN", "pro_desc": "INNER CONTINENTAL SHRUB SEMI-DESERT"}, "455": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "DRY SAVANNAS AND OPEN WOODLAND"}, "258": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "FOREST-MEADOW OF EASTERN OCEANIC (MONSOON CLIMATE)"}, "259": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OPEN WOODLAND, SAVANNAS, AND SHRUB OF EASTERN PARTS OF CONTINENTS"}, "179": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID TALL-GRASS SAVANNAS AND SAVANNA FORESTS"}, "250": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "WESTERN OCEANIC MIXED SCLEROPHYLL FORESTS AND SHRUB"}, "257": {"dom_desc": "HUMID TEMPERATE DOMAIN", "pro_desc": "OCEANIC MIXED CONSTANTLY HUMID FORESTS"}, "254": {"dom_desc": "DRY DOMAIN", "pro_desc": "SHRUB AND SEMI-SHRUB SEMI-DESERTS OF CONTINENTAL CLIMATE"}, "255": {"dom_desc": "DRY DOMAIN", "pro_desc": "DRY STEPPES, OPEN WOODLAND, AND SHRUB OF CONTINENTAL CLIMATE"}, "182": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERT-LIKE SAVANNAS, OPEN WOODLAND, AND SHRUB"}, "183": {"dom_desc": "DRY DOMAIN", "pro_desc": "INNER CONTINENTAL SHRUB SEMI-DESERT"}, "181": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MODERATELY HUMID GRASSY SAVANNAS"}, "186": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MODERATELY HUMID GRASSY SAVANNAS"}, "185": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID TALL-GRASS SAVANNAS AND SAVANNA FORESTS"}, "506": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID MIXED (DECIDUOUS AND EVERGREEN) FORESTS"}, "507": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SAVANNAS, OPEN WOODLAND AND SHRUB WITH SEASONAL MOISTURE SUPPLY"}, "188": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-MEADOW, SEASONALLY HUMID"}, "189": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "EASTERN OCEANIC CONSTANTLY HUMID FORESTS"}, "500": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "EASTERN OCEANIC CONSTANTLY HUMID FORESTS"}, "501": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-STEPPE, INNER CONTINENTAL AND LEEWARD SLOPES"}, "469": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "CONSTANTLY HUMID EVERGREEN FORESTS"}, "468": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW"}, "509": {"dom_desc": "DRY DOMAIN", "pro_desc": "DESERT-LIKE SAVANNAS, OPEN WOODLAND, AND SHRUB"}, "463": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "CONSTANTLY HUMID EVERGREEN FORESTS"}, "228": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "DRY SAVANNAS AND OPEN WOODLAND"}, "504": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MODERATELY HUMID GRASSY SAVANNAS"}, "221": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID TALL-GRASS SAVANNAS AND SAVANNA FORESTS"}, "220": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "HUMID TALL-GRASS SAVANNAS AND SAVANNA FORESTS"}, "554": {"dom_desc": "POLAR DOMAIN", "pro_desc": ""}, "234": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW OF CONSTANTLY HUMID OCEANIC (AND WINDWARD-SLOPE) TYPE"}, "466": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW"}, "201": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "CONSTANTLY HUMID EVERGREEN FORESTS"}, "203": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-STEPPE, INNER CONTINENTAL AND LEEWARD SLOPES"}, "202": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW"}, "207": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW"}, "209": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW"}, "208": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW"}, "488": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW"}, "486": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "SEASONALLY HUMID, PREDOMINANTLY DECIDUOUS FORESTS"}, "484": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MODERATELY HUMID GRASSY SAVANNAS"}, "481": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-PARAMO AND FOREST-MEADOW"}, "471": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "FOREST-STEPPE, INNER CONTINENTAL AND LEEWARD SLOPES"}, "475": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "CONSTANTLY HUMID EVERGREEN FORESTS"}, "478": {"dom_desc": "HUMID TROPICAL DOMAIN", "pro_desc": "MIXED FORESTS WITH SHORT DRY SEASON"}}, "grid": [" !##$$$$%&'()) *++ ,-- .. / ", " !!####%&'(( ++0,,,.. 1234444 5 ", " 6!!!!!78889 +: . 4;;;<=> ", " ?!!!!!788@ AA B CC DDE;; ", " ?!!!!FF%88 G HH IJ ", " K!!!!!!!%88 LM NNNON PQ ", " KRRRRRRR%8@ ST UVVUUUUQ ", " WRRRRRRRR SX YZ[[[]]]UUQ ", " W^^^RRRR SX ZZZ[[]]]]]UUQ ", " WK^^RR_R ` ZZZZ[]]][]UUab ", " WKKccd eeZZ[[]][]]Uab ", " fggcdh ieee[[[jjklmno ", " ggdh iieeeepjjklmbb ", " qh ii rpmmmb s ", " matb ss", " uvt sw", " xx yss", " x z{ ", " || ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " }}} ", " }}}}}}}}} ", " } }}}}}}}}}}}} ", " } }}}}}}}} }} ", " }}}}}}}}}}}} }} ", "}}}}}}}}} }} ", "}}}}}} ", " }}} ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/1/2.json b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/1/2.json
new file mode 100644
index 0000000..a0e62f4
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/1/2.json
@@ -0,0 +1 @@
+{"keys": [""], "data": {}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/2/0.json b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/2/0.json
new file mode 100644
index 0000000..a0e62f4
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/2/0.json
@@ -0,0 +1 @@
+{"keys": [""], "data": {}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/2/1.json b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/2/1.json
new file mode 100644
index 0000000..a0e62f4
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/2/1.json
@@ -0,0 +1 @@
+{"keys": [""], "data": {}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/2/2.json b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/2/2.json
new file mode 100644
index 0000000..a0e62f4
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/bio_utfgrid/1/2/2.json
@@ -0,0 +1 @@
+{"keys": [""], "data": {}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/demo-1.1.json b/misc/openlayers/tests/data/utfgrid/demo-1.1.json
new file mode 100644
index 0000000..0848e26
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/demo-1.1.json
@@ -0,0 +1 @@
+{"grid":[" !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġ","ĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟȠȡ","ȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɂɃɄɅɆɇɈɉɊɋɌɍɎɏɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼʽʾʿˀˁ˂˃˄˅ˆˇˈˉˊˋˌˍˎˏːˑ˒˓˔˕˖˗˘˙˚˛˜˝˞˟ˠˡˢˣˤ˥˦˧˨˩˪˫ˬ˭ˮ˯˰˱˲˳˴˵˶˷˸˹˺˻˼˽˾˿̡̛̖̗̘̙̜̝̞̟̠̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̕̚","̴̵̶̷̸̢̧̨̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̽̾̿̀́͂̓̈́͆͊͋͌ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡ͰͱͲͳʹ͵Ͷͷ͸͹ͺͻͼͽ;Ϳ΀΁΂΃΄΅Ά·ΈΉΊ΋Ό΍ΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύώϏϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵ϶ϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРС","ТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁ҂҃҄҅҆҇҈҉ҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӏӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹӺӻӼӽӾӿԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԐԑԒԓԔԕԖԗԘԙԚԛԜԝԞԟԠԡ","ԢԣԤԥԦԧԨԩԪԫԬԭԮԯ԰ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖ՗՘ՙ՚՛՜՝՞՟ՠաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևֈ։֊֋֌֍֎֏֐ְֱֲֳִֵֶַָֹֺֻּֽ֑֖֛֢֣֤֥֦֧֪֚֭֮֒֓֔֕֗֘֙֜֝֞֟֠֡֨֩֫֬֯־ֿ׀ׁׂ׃ׅׄ׆ׇ׈׉׊׋׌׍׎׏אבגדהוזחטיךכלםמןנסעףפץצקרשת׫׬׭׮ׯװױײ׳״׵׶׷׸׹׺׻׼׽׾׿؀؁؂؃؄؅؆؇؈؉؊؋،؍؎؏ؘؙؚؐؑؒؓؔؕؖؗ؛؜؝؞؟ؠء","آأؤإئابةتثجحخدذرزسشصضطظعغػؼؽؾؿـفقكلمنهوىيًٌٍَُِّْٕٖٜٟٓٔٗ٘ٙٚٛٝٞ٠١٢٣٤٥٦٧٨٩٪٫٬٭ٮٯٰٱٲٳٴٵٶٷٸٹٺٻټٽپٿڀځڂڃڄڅچڇڈډڊڋڌڍڎڏڐڑڒړڔڕږڗژڙښڛڜڝڞڟڠڡڢڣڤڥڦڧڨکڪګڬڭڮگڰڱڲڳڴڵڶڷڸڹںڻڼڽھڿۀہۂۃۄۅۆۇۈۉۊۋیۍێۏېۑےۓ۔ەۖۗۘۙۚۛۜ۝۞ۣ۟۠ۡۢۤۥۦۧۨ۩۪ۭ۫۬ۮۯ۰۱۲۳۴۵۶۷۸۹ۺۻۼ۽۾ۿ܀܁܂܃܄܅܆܇܈܉܊܋܌܍܎܏ܐܑܒܓܔܕܖܗܘܙܚܛܜܝܞܟܠܡ","ܢܣܤܥܦܧܨܩܪܫܬܭܮܯܱܴܷܸܹܻܼܾ݂݄݆݈ܰܲܳܵܶܺܽܿ݀݁݃݅݇݉݊݋݌ݍݎݏݐݑݒݓݔݕݖݗݘݙݚݛݜݝݞݟݠݡݢݣݤݥݦݧݨݩݪݫݬݭݮݯݰݱݲݳݴݵݶݷݸݹݺݻݼݽݾݿހށނރބޅކއވމފދތލގޏސޑޒޓޔޕޖޗޘޙޚޛޜޝޞޟޠޡޢޣޤޥަާިީުޫެޭޮޯްޱ޲޳޴޵޶޷޸޹޺޻޼޽޾޿߀߁߂߃߄߅߆߇߈߉ߊߋߌߍߎߏߐߑߒߓߔߕߖߗߘߙߚߛߜߝߞߟߠߡߢߣߤߥߦߧߨߩߪ߲߫߬߭߮߯߰߱߳ߴߵ߶߷߸߹ߺ߻߼߽߾߿ࠀࠁࠂࠃࠄࠅࠆࠇࠈࠉࠊࠋࠌࠍࠎࠏࠐࠑࠒࠓࠔࠕࠖࠗ࠘࠙ࠚࠛࠜࠝࠞࠟࠠࠡ","ࠢࠣࠤࠥࠦࠧࠨࠩࠪࠫࠬ࠭࠮࠯࠰࠱࠲࠳࠴࠵࠶࠷࠸࠹࠺࠻࠼࠽࠾࠿ࡀࡁࡂࡃࡄࡅࡆࡇࡈࡉࡊࡋࡌࡍࡎࡏࡐࡑࡒࡓࡔࡕࡖࡗࡘ࡙࡚࡛࡜࡝࡞࡟ࡠࡡࡢࡣࡤࡥࡦࡧࡨࡩࡪ࡫࡬࡭࡮࡯ࡰࡱࡲࡳࡴࡵࡶࡷࡸࡹࡺࡻࡼࡽࡾࡿࢀࢁࢂࢃࢄࢅࢆࢇ࢈ࢉࢊࢋࢌࢍࢎ࢏࢐࢑࢒࢓࢔࢕࢖࢙࢚࢛ࢗ࢘࢜࢝࢞࢟ࢠࢡࢢࢣࢤࢥࢦࢧࢨࢩࢪࢫࢬࢭࢮࢯࢰࢱࢲࢳࢴࢵࢶࢷࢸࢹࢺࢻࢼࢽࢾࢿࣀࣁࣂࣃࣄࣅࣆࣇࣈࣉ࣏࣐࣑࣒࣓࣊࣋࣌࣍࣎ࣔࣕࣖࣗࣘࣙࣚࣛࣜࣝࣞࣟ࣠࣡࣢ࣰࣱࣲࣣࣦࣩ࣭࣮࣯ࣶࣹࣺࣤࣥࣧࣨ࣪࣫࣬ࣳࣴࣵࣷࣸࣻࣼࣽࣾࣿऀँंःऄअआइईउऊऋऌऍऎएऐऑऒओऔकखगघङचछजझञटठड","ढणतथदधनऩपफबभमयरऱलळऴवशषसहऺऻ़ऽािीुूृॄॅॆेैॉॊोौ्ॎॏॐ॒॑॓॔ॕॖॗक़ख़ग़ज़ड़ढ़फ़य़ॠॡॢॣ।॥०१२३४५६७८९॰ॱॲॳॴॵॶॷॸॹॺॻॼॽॾॿঀঁংঃ঄অআইঈউঊঋঌ঍঎এঐ঑঒ওঔকখগঘঙচছজঝঞটঠডঢণতথদধন঩পফবভমযর঱ল঳঴঵শষসহ঺঻়ঽািীুূৃৄ৅৆েৈ৉৊োৌ্ৎ৏৐৑৒৓৔৕৖ৗ৘৙৚৛ড়ঢ়৞য়ৠৡৢৣ৤৥০১২৩৪৫৬৭৮৯ৰৱ৲৳৴৵৶৷৸৹৺৻ৼ৽৾৿਀ਁਂਃ਄ਅਆਇਈਉਊ਋਌਍਎ਏਐ਑਒ਓਔਕਖਗਘਙਚਛਜਝਞਟਠਡ","ਢਣਤਥਦਧਨ਩ਪਫਬਭਮਯਰ਱ਲਲ਼਴ਵਸ਼਷ਸਹ਺਻਼਽ਾਿੀੁੂ੃੄੅੆ੇੈ੉੊ੋੌ੍੎੏੐ੑ੒੓੔੕੖੗੘ਖ਼ਗ਼ਜ਼ੜ੝ਫ਼੟੠੡੢੣੤੥੦੧੨੩੪੫੬੭੮੯ੰੱੲੳੴੵ੶੷੸੹੺੻੼੽੾੿઀ઁંઃ઄અઆઇઈઉઊઋઌઍ઎એઐઑ઒ઓઔકખગઘઙચછજઝઞટઠડઢણતથદધન઩પફબભમયર઱લળ઴વશષસહ઺઻઼ઽાિીુૂૃૄૅ૆ેૈૉ૊ોૌ્૎૏ૐ૑૒૓૔૕૖૗૘૙૚૛૜૝૞૟ૠૡૢૣ૤૥૦૧૨૩૪૫૬૭૮૯૰૱૲૳૴૵૶૷૸ૹૺૻૼ૽૾૿଀ଁଂଃ଄ଅଆଇଈଉଊଋଌ଍଎ଏଐ଑଒ଓଔକଖଗଘଙଚଛଜଝଞଟଠଡ","ଢଣତଥଦଧନ଩ପଫବଭମଯର଱ଲଳ଴ଵଶଷସହ଺଻଼ଽାିୀୁୂୃୄ୅୆େୈ୉୊ୋୌ୍୎୏୐୑୒୓୔୕ୖୗ୘୙୚୛ଡ଼ଢ଼୞ୟୠୡୢୣ୤୥୦୧୨୩୪୫୬୭୮୯୰ୱ୲୳୴୵୶୷୸୹୺୻୼୽୾୿஀஁ஂஃ஄அஆஇஈஉஊ஋஌஍எஏஐ஑ஒஓஔக஖஗஘ஙச஛ஜ஝ஞட஠஡஢ணத஥஦஧நனப஫஬஭மயரறலளழவஶஷஸஹ஺஻஼஽ாிீுூ௃௄௅ெேை௉ொோௌ்௎௏ௐ௑௒௓௔௕௖ௗ௘௙௚௛௜௝௞௟௠௡௢௣௤௥௦௧௨௩௪௫௬௭௮௯௰௱௲௳௴௵௶௷௸௹௺௻௼௽௾௿ఀఁంఃఄఅఆఇఈఉఊఋఌ఍ఎఏఐ఑ఒఓఔకఖగఘఙచఛజఝఞటఠడ","ఢణతథదధన఩పఫబభమయరఱలళఴవశషసహ఺఻఼ఽాిీుూృౄ౅ెేై౉ొోౌ్౎౏౐౑౒౓౔ౕౖ౗ౘౙౚ౛౜ౝ౞౟ౠౡౢౣ౤౥౦౧౨౩౪౫౬౭౮౯౰౱౲౳౴౵౶౷౸౹౺౻౼౽౾౿ಀಁಂಃ಄ಅಆಇಈಉಊಋಌ಍ಎಏಐ಑ಒಓಔಕಖಗಘಙಚಛಜಝಞಟಠಡಢಣತಥದಧನ಩ಪಫಬಭಮಯರಱಲಳ಴ವಶಷಸಹ಺಻಼ಽಾಿೀುೂೃೄ೅ೆೇೈ೉ೊೋೌ್೎೏೐೑೒೓೔ೕೖ೗೘೙೚೛೜ೝೞ೟ೠೡೢೣ೤೥೦೧೨೩೪೫೬೭೮೯೰ೱೲೳ೴೵೶೷೸೹೺೻೼೽೾೿ഀഁംഃഄഅആഇഈഉഊഋഌ഍എഏഐ഑ഒഓഔകഖഗഘങചഛജഝഞടഠഡ","ഢണതഥദധനഩപഫബഭമയരറലളഴവശഷസഹഺ഻഼ഽാിീുൂൃൄ൅െേൈ൉ൊോൌ്ൎ൏൐൑൒൓ൔൕൖൗ൘൙൚൛൜൝൞ൟൠൡൢൣ൤൥൦൧൨൩൪൫൬൭൮൯൰൱൲൳൴൵൶൷൸൹ൺൻർൽൾൿ඀ඁංඃ඄අආඇඈඉඊඋඌඍඎඏඐඑඒඓඔඕඖ඗඘඙කඛගඝඞඟචඡජඣඤඥඦටඨඩඪණඬතථදධන඲ඳපඵබභමඹයර඼ල඾඿වශෂසහළෆ෇෈෉්෋෌෍෎ාැෑිීු෕ූ෗ෘෙේෛොෝෞෟ෠෡෢෣෤෥෦෧෨෩෪෫෬෭෮෯෰෱ෲෳ෴෵෶෷෸෹෺෻෼෽෾෿฀กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภม","ยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฻฼฽฾฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛๜๝๞๟๠๡๢๣๤๥๦๧๨๩๪๫๬๭๮๯๰๱๲๳๴๵๶๷๸๹๺๻๼๽๾๿຀ກຂ຃ຄ຅ຆງຈຉຊ຋ຌຍຎຏຐຑຒຓດຕຖທຘນບປຜຝພຟຠມຢຣ຤ລ຦ວຨຩສຫຬອຮຯະັາຳິີຶື຺ຸູົຼຽ຾຿ເແໂໃໄ໅ໆ໇່້໊໋໌ໍ໎໏໐໑໒໓໔໕໖໗໘໙໚໛ໜໝໞໟ໠໡໢໣໤໥໦໧໨໩໪໫໬໭໮໯໰໱໲໳໴໵໶໷໸໹໺໻໼໽໾໿ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗༘༙༚༛༜༝༞༟༠༡","༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿ཀཁགགྷངཅཆཇ཈ཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬ཭཮཯཰ཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚࿛࿜࿝࿞࿟࿠࿡࿢࿣࿤࿥࿦࿧࿨࿩࿪࿫࿬࿭࿮࿯࿰࿱࿲࿳࿴࿵࿶࿷࿸࿹࿺࿻࿼࿽࿾࿿ကခဂဃငစဆဇဈဉညဋဌဍဎဏတထဒဓနပဖဗဘမယရလဝသဟဠအ","ဢဣဤဥဦဧဨဩဪါာိီုူေဲဳဴဵံ့း္်ျြွှဿ၀၁၂၃၄၅၆၇၈၉၊။၌၍၎၏ၐၑၒၓၔၕၖၗၘၙၚၛၜၝၞၟၠၡၢၣၤၥၦၧၨၩၪၫၬၭၮၯၰၱၲၳၴၵၶၷၸၹၺၻၼၽၾၿႀႁႂႃႄႅႆႇႈႉႊႋႌႍႎႏ႐႑႒႓႔႕႖႗႘႙ႚႛႜႝ႞႟ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ჆Ⴧ჈჉჊჋჌Ⴭ჎჏აბგდევზთიკლმნოპჟრსტუფქღყშჩცძწჭხჯჰჱჲჳჴჵჶჷჸჹჺ჻ჼჽჾჿᄀᄁᄂᄃᄄᄅᄆᄇᄈᄉᄊᄋᄌᄍᄎᄏᄐᄑᄒᄓᄔᄕᄖᄗᄘᄙᄚᄛᄜᄝᄞᄟᄠᄡ","ᄢᄣᄤᄥᄦᄧᄨᄩᄪᄫᄬᄭᄮᄯᄰᄱᄲᄳᄴᄵᄶᄷᄸᄹᄺᄻᄼᄽᄾᄿᅀᅁᅂᅃᅄᅅᅆᅇᅈᅉᅊᅋᅌᅍᅎᅏᅐᅑᅒᅓᅔᅕᅖᅗᅘᅙᅚᅛᅜᅝᅞᅟᅠᅡᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵᅶᅷᅸᅹᅺᅻᅼᅽᅾᅿᆀᆁᆂᆃᆄᆅᆆᆇᆈᆉᆊᆋᆌᆍᆎᆏᆐᆑᆒᆓᆔᆕᆖᆗᆘᆙᆚᆛᆜᆝᆞᆟᆠᆡᆢᆣᆤᆥᆦᆧᆨᆩᆪᆫᆬᆭᆮᆯᆰᆱᆲᆳᆴᆵᆶᆷᆸᆹᆺᆻᆼᆽᆾᆿᇀᇁᇂᇃᇄᇅᇆᇇᇈᇉᇊᇋᇌᇍᇎᇏᇐᇑᇒᇓᇔᇕᇖᇗᇘᇙᇚᇛᇜᇝᇞᇟᇠᇡᇢᇣᇤᇥᇦᇧᇨᇩᇪᇫᇬᇭᇮᇯᇰᇱᇲᇳᇴᇵᇶᇷᇸᇹᇺᇻᇼᇽᇾᇿሀሁሂሃሄህሆሇለሉሊላሌልሎሏሐሑሒሓሔሕሖሗመሙሚማሜምሞሟሠሡ","ሢሣሤሥሦሧረሩሪራሬርሮሯሰሱሲሳሴስሶሷሸሹሺሻሼሽሾሿቀቁቂቃቄቅቆቇቈ቉ቊቋቌቍ቎቏ቐቑቒቓቔቕቖ቗ቘ቙ቚቛቜቝ቞቟በቡቢባቤብቦቧቨቩቪቫቬቭቮቯተቱቲታቴትቶቷቸቹቺቻቼችቾቿኀኁኂኃኄኅኆኇኈ኉ኊኋኌኍ኎኏ነኑኒናኔንኖኗኘኙኚኛኜኝኞኟአኡኢኣኤእኦኧከኩኪካኬክኮኯኰ኱ኲኳኴኵ኶኷ኸኹኺኻኼኽኾ኿ዀ዁ዂዃዄዅ዆዇ወዉዊዋዌውዎዏዐዑዒዓዔዕዖ዗ዘዙዚዛዜዝዞዟዠዡዢዣዤዥዦዧየዩዪያዬይዮዯደዱዲዳዴድዶዷዸዹዺዻዼዽዾዿጀጁጂጃጄጅጆጇገጉጊጋጌግጎጏጐ጑ጒጓጔጕ጖጗ጘጙጚጛጜጝጞጟጠጡ","ጢጣጤጥጦጧጨጩጪጫጬጭጮጯጰጱጲጳጴጵጶጷጸጹጺጻጼጽጾጿፀፁፂፃፄፅፆፇፈፉፊፋፌፍፎፏፐፑፒፓፔፕፖፗፘፙፚ፛፜፝፞፟፠፡።፣፤፥፦፧፨፩፪፫፬፭፮፯፰፱፲፳፴፵፶፷፸፹፺፻፼፽፾፿ᎀᎁᎂᎃᎄᎅᎆᎇᎈᎉᎊᎋᎌᎍᎎᎏ᎐᎑᎒᎓᎔᎕᎖᎗᎘᎙᎚᎛᎜᎝᎞᎟ᎠᎡᎢᎣᎤᎥᎦᎧᎨᎩᎪᎫᎬᎭᎮᎯᎰᎱᎲᎳᎴᎵᎶᎷᎸᎹᎺᎻᎼᎽᎾᎿᏀᏁᏂᏃᏄᏅᏆᏇᏈᏉᏊᏋᏌᏍᏎᏏᏐᏑᏒᏓᏔᏕᏖᏗᏘᏙᏚᏛᏜᏝᏞᏟᏠᏡᏢᏣᏤᏥᏦᏧᏨᏩᏪᏫᏬᏭᏮᏯᏰᏱᏲᏳᏴᏵ᏶᏷ᏸᏹᏺᏻᏼᏽ᏾᏿᐀ᐁᐂᐃᐄᐅᐆᐇᐈᐉᐊᐋᐌᐍᐎᐏᐐᐑᐒᐓᐔᐕᐖᐗᐘᐙᐚᐛᐜᐝᐞᐟᐠᐡ","ᐢᐣᐤᐥᐦᐧᐨᐩᐪᐫᐬᐭᐮᐯᐰᐱᐲᐳᐴᐵᐶᐷᐸᐹᐺᐻᐼᐽᐾᐿᑀᑁᑂᑃᑄᑅᑆᑇᑈᑉᑊᑋᑌᑍᑎᑏᑐᑑᑒᑓᑔᑕᑖᑗᑘᑙᑚᑛᑜᑝᑞᑟᑠᑡᑢᑣᑤᑥᑦᑧᑨᑩᑪᑫᑬᑭᑮᑯᑰᑱᑲᑳᑴᑵᑶᑷᑸᑹᑺᑻᑼᑽᑾᑿᒀᒁᒂᒃᒄᒅᒆᒇᒈᒉᒊᒋᒌᒍᒎᒏᒐᒑᒒᒓᒔᒕᒖᒗᒘᒙᒚᒛᒜᒝᒞᒟᒠᒡᒢᒣᒤᒥᒦᒧᒨᒩᒪᒫᒬᒭᒮᒯᒰᒱᒲᒳᒴᒵᒶᒷᒸᒹᒺᒻᒼᒽᒾᒿᓀᓁᓂᓃᓄᓅᓆᓇᓈᓉᓊᓋᓌᓍᓎᓏᓐᓑᓒᓓᓔᓕᓖᓗᓘᓙᓚᓛᓜᓝᓞᓟᓠᓡᓢᓣᓤᓥᓦᓧᓨᓩᓪᓫᓬᓭᓮᓯᓰᓱᓲᓳᓴᓵᓶᓷᓸᓹᓺᓻᓼᓽᓾᓿᔀᔁᔂᔃᔄᔅᔆᔇᔈᔉᔊᔋᔌᔍᔎᔏᔐᔑᔒᔓᔔᔕᔖᔗᔘᔙᔚᔛᔜᔝᔞᔟᔠᔡ","ᔢᔣᔤᔥᔦᔧᔨᔩᔪᔫᔬᔭᔮᔯᔰᔱᔲᔳᔴᔵᔶᔷᔸᔹᔺᔻᔼᔽᔾᔿᕀᕁᕂᕃᕄᕅᕆᕇᕈᕉᕊᕋᕌᕍᕎᕏᕐᕑᕒᕓᕔᕕᕖᕗᕘᕙᕚᕛᕜᕝᕞᕟᕠᕡᕢᕣᕤᕥᕦᕧᕨᕩᕪᕫᕬᕭᕮᕯᕰᕱᕲᕳᕴᕵᕶᕷᕸᕹᕺᕻᕼᕽᕾᕿᖀᖁᖂᖃᖄᖅᖆᖇᖈᖉᖊᖋᖌᖍᖎᖏᖐᖑᖒᖓᖔᖕᖖᖗᖘᖙᖚᖛᖜᖝᖞᖟᖠᖡᖢᖣᖤᖥᖦᖧᖨᖩᖪᖫᖬᖭᖮᖯᖰᖱᖲᖳᖴᖵᖶᖷᖸᖹᖺᖻᖼᖽᖾᖿᗀᗁᗂᗃᗄᗅᗆᗇᗈᗉᗊᗋᗌᗍᗎᗏᗐᗑᗒᗓᗔᗕᗖᗗᗘᗙᗚᗛᗜᗝᗞᗟᗠᗡᗢᗣᗤᗥᗦᗧᗨᗩᗪᗫᗬᗭᗮᗯᗰᗱᗲᗳᗴᗵᗶᗷᗸᗹᗺᗻᗼᗽᗾᗿᘀᘁᘂᘃᘄᘅᘆᘇᘈᘉᘊᘋᘌᘍᘎᘏᘐᘑᘒᘓᘔᘕᘖᘗᘘᘙᘚᘛᘜᘝᘞᘟᘠᘡ","ᘢᘣᘤᘥᘦᘧᘨᘩᘪᘫᘬᘭᘮᘯᘰᘱᘲᘳᘴᘵᘶᘷᘸᘹᘺᘻᘼᘽᘾᘿᙀᙁᙂᙃᙄᙅᙆᙇᙈᙉᙊᙋᙌᙍᙎᙏᙐᙑᙒᙓᙔᙕᙖᙗᙘᙙᙚᙛᙜᙝᙞᙟᙠᙡᙢᙣᙤᙥᙦᙧᙨᙩᙪᙫᙬ᙭᙮ᙯᙰᙱᙲᙳᙴᙵᙶᙷᙸᙹᙺᙻᙼᙽᙾᙿ ᚁᚂᚃᚄᚅᚆᚇᚈᚉᚊᚋᚌᚍᚎᚏᚐᚑᚒᚓᚔᚕᚖᚗᚘᚙᚚ᚛᚜᚝᚞᚟ᚠᚡᚢᚣᚤᚥᚦᚧᚨᚩᚪᚫᚬᚭᚮᚯᚰᚱᚲᚳᚴᚵᚶᚷᚸᚹᚺᚻᚼᚽᚾᚿᛀᛁᛂᛃᛄᛅᛆᛇᛈᛉᛊᛋᛌᛍᛎᛏᛐᛑᛒᛓᛔᛕᛖᛗᛘᛙᛚᛛᛜᛝᛞᛟᛠᛡᛢᛣᛤᛥᛦᛧᛨᛩᛪ᛫᛬᛭ᛮᛯᛰᛱᛲᛳᛴᛵᛶᛷᛸ᛹᛺᛻᛼᛽᛾᛿ᜀᜁᜂᜃᜄᜅᜆᜇᜈᜉᜊᜋᜌᜍᜎᜏᜐᜑᜒᜓ᜔᜕᜖᜗᜘᜙᜚᜛᜜᜝᜞ᜟᜠᜡ","ᜢᜣᜤᜥᜦᜧᜨᜩᜪᜫᜬᜭᜮᜯᜰᜱᜲᜳ᜴᜵᜶᜷᜸᜹᜺᜻᜼᜽᜾᜿ᝀᝁᝂᝃᝄᝅᝆᝇᝈᝉᝊᝋᝌᝍᝎᝏᝐᝑᝒᝓ᝔᝕᝖᝗᝘᝙᝚᝛᝜᝝᝞᝟ᝠᝡᝢᝣᝤᝥᝦᝧᝨᝩᝪᝫᝬ᝭ᝮᝯᝰ᝱ᝲᝳ᝴᝵᝶᝷᝸᝹᝺᝻᝼᝽᝾᝿កខគឃងចឆជឈញដឋឌឍណតថទធនបផពភមយរលវឝឞសហឡអឣឤឥឦឧឨឩឪឫឬឭឮឯឰឱឲឳ឴឵ាិីឹឺុូួើឿៀេែៃោៅំះៈ៉៊់៌៍៎៏័៑្៓។៕៖ៗ៘៙៚៛ៜ៝៞៟០១២៣៤៥៦៧៨៩៪៫៬៭៮៯៰៱៲៳៴៵៶៷៸៹៺៻៼៽៾៿᠀᠁᠂᠃᠄᠅᠆᠇᠈᠉᠊᠋᠌᠍᠎᠏᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙᠚᠛᠜᠝᠞᠟ᠠᠡ","ᠢᠣᠤᠥᠦᠧᠨᠩᠪᠫᠬᠭᠮᠯᠰᠱᠲᠳᠴᠵᠶᠷᠸᠹᠺᠻᠼᠽᠾᠿᡀᡁᡂᡃᡄᡅᡆᡇᡈᡉᡊᡋᡌᡍᡎᡏᡐᡑᡒᡓᡔᡕᡖᡗᡘᡙᡚᡛᡜᡝᡞᡟᡠᡡᡢᡣᡤᡥᡦᡧᡨᡩᡪᡫᡬᡭᡮᡯᡰᡱᡲᡳᡴᡵᡶᡷᡸ᡹᡺᡻᡼᡽᡾᡿ᢀᢁᢂᢃᢄᢅᢆᢇᢈᢉᢊᢋᢌᢍᢎᢏᢐᢑᢒᢓᢔᢕᢖᢗᢘᢙᢚᢛᢜᢝᢞᢟᢠᢡᢢᢣᢤᢥᢦᢧᢨᢩᢪ᢫᢬᢭᢮᢯ᢰᢱᢲᢳᢴᢵᢶᢷᢸᢹᢺᢻᢼᢽᢾᢿᣀᣁᣂᣃᣄᣅᣆᣇᣈᣉᣊᣋᣌᣍᣎᣏᣐᣑᣒᣓᣔᣕᣖᣗᣘᣙᣚᣛᣜᣝᣞᣟᣠᣡᣢᣣᣤᣥᣦᣧᣨᣩᣪᣫᣬᣭᣮᣯᣰᣱᣲᣳᣴᣵ᣶᣷᣸᣹᣺᣻᣼᣽᣾᣿ᤀᤁᤂᤃᤄᤅᤆᤇᤈᤉᤊᤋᤌᤍᤎᤏᤐᤑᤒᤓᤔᤕᤖᤗᤘᤙᤚᤛᤜᤝᤞ᤟ᤠᤡ","ᤢᤣᤤᤥᤦᤧᤨᤩᤪᤫ᤬᤭᤮᤯ᤰᤱᤲᤳᤴᤵᤶᤷᤸ᤻᤹᤺᤼᤽᤾᤿᥀᥁᥂᥃᥄᥅᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏ᥐᥑᥒᥓᥔᥕᥖᥗᥘᥙᥚᥛᥜᥝᥞᥟᥠᥡᥢᥣᥤᥥᥦᥧᥨᥩᥪᥫᥬᥭ᥮᥯ᥰᥱᥲᥳᥴ᥵᥶᥷᥸᥹᥺᥻᥼᥽᥾᥿ᦀᦁᦂᦃᦄᦅᦆᦇᦈᦉᦊᦋᦌᦍᦎᦏᦐᦑᦒᦓᦔᦕᦖᦗᦘᦙᦚᦛᦜᦝᦞᦟᦠᦡᦢᦣᦤᦥᦦᦧᦨᦩᦪᦫ᦬᦭᦮᦯ᦰᦱᦲᦳᦴᦵᦶᦷᦸᦹᦺᦻᦼᦽᦾᦿᧀᧁᧂᧃᧄᧅᧆᧇᧈᧉ᧊᧋᧌᧍᧎᧏᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙᧚᧛᧜᧝᧞᧟᧠᧡᧢᧣᧤᧥᧦᧧᧨᧩᧪᧫᧬᧭᧮᧯᧰᧱᧲᧳᧴᧵᧶᧷᧸᧹᧺᧻᧼᧽᧾᧿ᨀᨁᨂᨃᨄᨅᨆᨇᨈᨉᨊᨋᨌᨍᨎᨏᨐᨑᨒᨓᨔᨕᨖᨘᨗᨙᨚᨛ᨜᨝᨞᨟ᨠᨡ","ᨢᨣᨤᨥᨦᨧᨨᨩᨪᨫᨬᨭᨮᨯᨰᨱᨲᨳᨴᨵᨶᨷᨸᨹᨺᨻᨼᨽᨾᨿᩀᩁᩂᩃᩄᩅᩆᩇᩈᩉᩊᩋᩌᩍᩎᩏᩐᩑᩒᩓᩔᩕᩖᩗᩘᩙᩚᩛᩜᩝᩞ᩟᩠ᩡᩢᩣᩤᩥᩦᩧᩨᩩᩪᩫᩬᩭᩮᩯᩰᩱᩲᩳᩴ᩵᩶᩷᩸᩹᩺᩻᩼᩽᩾᩿᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉᪊᪋᪌᪍᪎᪏᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙᪚᪛᪜᪝᪞᪟᪠᪡᪢᪣᪤᪥᪦ᪧ᪨᪩᪪᪫᪬᪭᪮᪯᪵᪶᪷᪸᪹᪺᪽᪰᪱᪲᪳᪴᪻᪼᪾ᪿᫀ᫃᫄᫊᫁᫂᫅᫆᫇᫈᫉᫋ᫌᫍᫎ᫏᫐᫑᫒᫓᫔᫕᫖᫗᫘᫙᫚᫛᫜᫝᫞᫟᫠᫡᫢᫣᫤᫥᫦᫧᫨᫩᫪᫫᫬᫭᫮᫯᫰᫱᫲᫳᫴᫵᫶᫷᫸᫹᫺᫻᫼᫽᫾᫿ᬀᬁᬂᬃᬄᬅᬆᬇᬈᬉᬊᬋᬌᬍᬎᬏᬐᬑᬒᬓᬔᬕᬖᬗᬘᬙᬚᬛᬜᬝᬞᬟᬠᬡ","ᬢᬣᬤᬥᬦᬧᬨᬩᬪᬫᬬᬭᬮᬯᬰᬱᬲᬳ᬴ᬵᬶᬷᬸᬹᬺᬻᬼᬽᬾᬿᭀᭁᭂᭃ᭄ᭅᭆᭇᭈᭉᭊᭋᭌ᭍᭎᭏᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙᭚᭛᭜᭝᭞᭟᭠᭡᭢᭣᭤᭥᭦᭧᭨᭩᭪᭬᭫᭭᭮᭯᭰᭱᭲᭳᭴᭵᭶᭷᭸᭹᭺᭻᭼᭽᭾᭿ᮀᮁᮂᮃᮄᮅᮆᮇᮈᮉᮊᮋᮌᮍᮎᮏᮐᮑᮒᮓᮔᮕᮖᮗᮘᮙᮚᮛᮜᮝᮞᮟᮠᮡᮢᮣᮤᮥᮦᮧᮨᮩ᮪᮫ᮬᮭᮮᮯ᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹ᮺᮻᮼᮽᮾᮿᯀᯁᯂᯃᯄᯅᯆᯇᯈᯉᯊᯋᯌᯍᯎᯏᯐᯑᯒᯓᯔᯕᯖᯗᯘᯙᯚᯛᯜᯝᯞᯟᯠᯡᯢᯣᯤᯥ᯦ᯧᯨᯩᯪᯫᯬᯭᯮᯯᯰᯱ᯲᯳᯴᯵᯶᯷᯸᯹᯺᯻᯼᯽᯾᯿ᰀᰁᰂᰃᰄᰅᰆᰇᰈᰉᰊᰋᰌᰍᰎᰏᰐᰑᰒᰓᰔᰕᰖᰗᰘᰙᰚᰛᰜᰝᰞᰟᰠᰡ","ᰢᰣᰤᰥᰦᰧᰨᰩᰪᰫᰬᰭᰮᰯᰰᰱᰲᰳᰴᰵᰶ᰷᰸᰹᰺᰻᰼᰽᰾᰿᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉᱊᱋᱌ᱍᱎᱏ᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙ᱚᱛᱜᱝᱞᱟᱠᱡᱢᱣᱤᱥᱦᱧᱨᱩᱪᱫᱬᱭᱮᱯᱰᱱᱲᱳᱴᱵᱶᱷᱸᱹᱺᱻᱼᱽ᱾᱿ᲀᲁᲂᲃᲄᲅᲆᲇᲈᲉᲊ᲋᲌᲍᲎᲏ᲐᲑᲒᲓᲔᲕᲖᲗᲘᲙᲚᲛᲜᲝᲞᲟᲠᲡᲢᲣᲤᲥᲦᲧᲨᲩᲪᲫᲬᲭᲮᲯᲰᲱᲲᲳᲴᲵᲶᲷᲸᲹᲺ᲻᲼ᲽᲾᲿ᳀᳁᳂᳃᳄᳅᳆᳇᳈᳉᳊᳋᳌᳍᳎᳏᳐᳑᳒᳓᳔᳕᳖᳗᳘᳙᳜᳝᳞᳟᳚᳛᳠᳡᳢᳣᳤᳥᳦᳧᳨ᳩᳪᳫᳬ᳭ᳮᳯᳰᳱᳲᳳ᳴ᳵᳶ᳷᳸᳹ᳺ᳻᳼᳽᳾᳿ᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡ","ᴢᴣᴤᴥᴦᴧᴨᴩᴪᴫᴬᴭᴮᴯᴰᴱᴲᴳᴴᴵᴶᴷᴸᴹᴺᴻᴼᴽᴾᴿᵀᵁᵂᵃᵄᵅᵆᵇᵈᵉᵊᵋᵌᵍᵎᵏᵐᵑᵒᵓᵔᵕᵖᵗᵘᵙᵚᵛᵜᵝᵞᵟᵠᵡᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵸᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚᶛᶜᶝᶞᶟᶠᶡᶢᶣᶤᶥᶦᶧᶨᶩᶪᶫᶬᶭᶮᶯᶰᶱᶲᶳᶴᶵᶶᶷᶸᶹᶺᶻᶼᶽᶾᶿ᷐᷎᷺᷂᷊᷏᷹᷽᷿᷷᷸᷀᷁᷃᷄᷅᷆᷇᷈᷉᷋᷌᷑᷒ᷓᷔᷕᷖᷗᷘᷙᷚᷛᷜᷝᷞᷟᷠᷡᷢᷣᷤᷥᷦᷧᷨᷩᷪᷫᷬᷭᷮᷯᷰᷱᷲᷳᷴ᷵᷻᷾᷶᷼᷍ḀḁḂḃḄḅḆḇḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡ","ḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẜẝẞẟẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐốỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹỺỻỼỽỾỿἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕ἖἗ἘἙἚἛἜἝ἞἟ἠἡ","ἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅ὆὇ὈὉὊὋὌὍ὎὏ὐὑὒὓὔὕὖὗ὘Ὑ὚Ὓ὜Ὕ὞ὟὠὡὢὣὤὥὦὧὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώ὾὿ᾀᾁᾂᾃᾄᾅᾆᾇᾈᾉᾊᾋᾌᾍᾎᾏᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾞᾟᾠᾡᾢᾣᾤᾥᾦᾧᾨᾩᾪᾫᾬᾭᾮᾯᾰᾱᾲᾳᾴ᾵ᾶᾷᾸᾹᾺΆᾼ᾽ι᾿῀῁ῂῃῄ῅ῆῇῈΈῊΉῌ῍῎῏ῐῑῒΐ῔῕ῖῗῘῙῚΊ῜῝῞῟ῠῡῢΰῤῥῦῧῨῩῪΎῬ῭΅`῰῱ῲῳῴ῵ῶῷῸΌῺΏῼ´῾῿           ​‌‍‎‏‐‑‒–—―‖‗‘’‚‛“”„‟†‡","•‣․‥…‧

‪‫‬‭‮ ‰‱′″‴‵‶‷‸‹›※‼‽‾‿⁀⁁⁂⁃⁄⁅⁆⁇⁈⁉⁊⁋⁌⁍⁎⁏⁐⁑⁒⁓⁔⁕⁖⁗⁘⁙⁚⁛⁜⁝⁞ ⁠⁡⁢⁣⁤⁥⁦⁧⁨⁩⁰ⁱ⁲⁳⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎₏ₐₑₒₓₔₕₖₗₘₙₚₛₜ₝₞₟₠₡₢₣₤₥₦₧₨₩₪₫€₭₮₯₰₱₲₳₴₵₶₷₸₹₺₻₼₽₾₿⃀⃁⃂⃃⃄⃅⃆⃇⃈⃉⃊⃋⃌⃍⃎⃏⃒⃓⃘⃙⃚⃐⃑⃔⃕⃖⃗⃛⃜⃝⃞⃟⃠⃡⃢⃣⃤⃥⃦⃪⃫⃨⃬⃭⃮⃯⃧⃩⃰⃱⃲⃳⃴⃵⃶⃷⃸⃹⃺⃻⃼⃽⃾⃿℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠℡","™℣ℤ℥Ω℧ℨ℩KÅℬℭ℮ℯℰℱℲℳℴℵℶℷℸℹ℺℻ℼℽℾℿ⅀⅁⅂⅃⅄ⅅⅆⅇⅈⅉ⅊⅋⅌⅍ⅎ⅏⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↀↁↂↃↄↅↆↇↈ↉↊↋↌↍↎↏←↑→↓↔↕↖↗↘↙↚↛↜↝↞↟↠↡↢↣↤↥↦↧↨↩↪↫↬↭↮↯↰↱↲↳↴↵↶↷↸↹↺↻↼↽↾↿⇀⇁⇂⇃⇄⇅⇆⇇⇈⇉⇊⇋⇌⇍⇎⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇚⇛⇜⇝⇞⇟⇠⇡⇢⇣⇤⇥⇦⇧⇨⇩⇪⇫⇬⇭⇮⇯⇰⇱⇲⇳⇴⇵⇶⇷⇸⇹⇺⇻⇼⇽⇾⇿∀∁∂∃∄∅∆∇∈∉∊∋∌∍∎∏∐∑−∓∔∕∖∗∘∙√∛∜∝∞∟∠∡","∢∣∤∥∦∧∨∩∪∫∬∭∮∯∰∱∲∳∴∵∶∷∸∹∺∻∼∽∾∿≀≁≂≃≄≅≆≇≈≉≊≋≌≍≎≏≐≑≒≓≔≕≖≗≘≙≚≛≜≝≞≟≠≡≢≣≤≥≦≧≨≩≪≫≬≭≮≯≰≱≲≳≴≵≶≷≸≹≺≻≼≽≾≿⊀⊁⊂⊃⊄⊅⊆⊇⊈⊉⊊⊋⊌⊍⊎⊏⊐⊑⊒⊓⊔⊕⊖⊗⊘⊙⊚⊛⊜⊝⊞⊟⊠⊡⊢⊣⊤⊥⊦⊧⊨⊩⊪⊫⊬⊭⊮⊯⊰⊱⊲⊳⊴⊵⊶⊷⊸⊹⊺⊻⊼⊽⊾⊿⋀⋁⋂⋃⋄⋅⋆⋇⋈⋉⋊⋋⋌⋍⋎⋏⋐⋑⋒⋓⋔⋕⋖⋗⋘⋙⋚⋛⋜⋝⋞⋟⋠⋡⋢⋣⋤⋥⋦⋧⋨⋩⋪⋫⋬⋭⋮⋯⋰⋱⋲⋳⋴⋵⋶⋷⋸⋹⋺⋻⋼⋽⋾⋿⌀⌁⌂⌃⌄⌅⌆⌇⌈⌉⌊⌋⌌⌍⌎⌏⌐⌑⌒⌓⌔⌕⌖⌗⌘⌙⌚⌛⌜⌝⌞⌟⌠⌡","⌢⌣⌤⌥⌦⌧⌨〈〉⌫⌬⌭⌮⌯⌰⌱⌲⌳⌴⌵⌶⌷⌸⌹⌺⌻⌼⌽⌾⌿⍀⍁⍂⍃⍄⍅⍆⍇⍈⍉⍊⍋⍌⍍⍎⍏⍐⍑⍒⍓⍔⍕⍖⍗⍘⍙⍚⍛⍜⍝⍞⍟⍠⍡⍢⍣⍤⍥⍦⍧⍨⍩⍪⍫⍬⍭⍮⍯⍰⍱⍲⍳⍴⍵⍶⍷⍸⍹⍺⍻⍼⍽⍾⍿⎀⎁⎂⎃⎄⎅⎆⎇⎈⎉⎊⎋⎌⎍⎎⎏⎐⎑⎒⎓⎔⎕⎖⎗⎘⎙⎚⎛⎜⎝⎞⎟⎠⎡⎢⎣⎤⎥⎦⎧⎨⎩⎪⎫⎬⎭⎮⎯⎰⎱⎲⎳⎴⎵⎶⎷⎸⎹⎺⎻⎼⎽⎾⎿⏀⏁⏂⏃⏄⏅⏆⏇⏈⏉⏊⏋⏌⏍⏎⏏⏐⏑⏒⏓⏔⏕⏖⏗⏘⏙⏚⏛⏜⏝⏞⏟⏠⏡⏢⏣⏤⏥⏦⏧⏨⏩⏪⏫⏬⏭⏮⏯⏰⏱⏲⏳⏴⏵⏶⏷⏸⏹⏺⏻⏼⏽⏾⏿␀␁␂␃␄␅␆␇␈␉␊␋␌␍␎␏␐␑␒␓␔␕␖␗␘␙␚␛␜␝␞␟␠␡","␢␣␤␥␦␧␨␩␪␫␬␭␮␯␰␱␲␳␴␵␶␷␸␹␺␻␼␽␾␿⑀⑁⑂⑃⑄⑅⑆⑇⑈⑉⑊⑋⑌⑍⑎⑏⑐⑑⑒⑓⑔⑕⑖⑗⑘⑙⑚⑛⑜⑝⑞⑟①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇⒈⒉⒊⒋⒌⒍⒎⒏⒐⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ⓪⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴⓵⓶⓷⓸⓹⓺⓻⓼⓽⓾⓿─━│┃┄┅┆┇┈┉┊┋┌┍┎┏┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟┠┡","┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿╀╁╂╃╄╅╆╇╈╉╊╋╌╍╎╏═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡╢╣╤╥╦╧╨╩╪╫╬╭╮╯╰╱╲╳╴╵╶╷╸╹╺╻╼╽╾╿▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▐░▒▓▔▕▖▗▘▙▚▛▜▝▞▟■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿◀◁◂◃◄◅◆◇◈◉◊○◌◍◎●◐◑◒◓◔◕◖◗◘◙◚◛◜◝◞◟◠◡◢◣◤◥◦◧◨◩◪◫◬◭◮◯◰◱◲◳◴◵◶◷◸◹◺◻◼◽◾◿☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓☔☕☖☗☘☙☚☛☜☝☞☟☠☡","☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿♀♁♂♃♄♅♆♇♈♉♊♋♌♍♎♏♐♑♒♓♔♕♖♗♘♙♚♛♜♝♞♟♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚇⚈⚉⚊⚋⚌⚍⚎⚏⚐⚑⚒⚓⚔⚕⚖⚗⚘⚙⚚⚛⚜⚝⚞⚟⚠⚡⚢⚣⚤⚥⚦⚧⚨⚩⚪⚫⚬⚭⚮⚯⚰⚱⚲⚳⚴⚵⚶⚷⚸⚹⚺⚻⚼⚽⚾⚿⛀⛁⛂⛃⛄⛅⛆⛇⛈⛉⛊⛋⛌⛍⛎⛏⛐⛑⛒⛓⛔⛕⛖⛗⛘⛙⛚⛛⛜⛝⛞⛟⛠⛡⛢⛣⛤⛥⛦⛧⛨⛩⛪⛫⛬⛭⛮⛯⛰⛱⛲⛳⛴⛵⛶⛷⛸⛹⛺⛻⛼⛽⛾⛿✀✁✂✃✄✅✆✇✈✉✊✋✌✍✎✏✐✑✒✓✔✕✖✗✘✙✚✛✜✝✞✟✠✡","✢✣✤✥✦✧✨✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞❟❠❡❢❣❤❥❦❧❨❩❪❫❬❭❮❯❰❱❲❳❴❵❶❷❸❹❺❻❼❽❾❿➀➁➂➃➄➅➆➇➈➉➊➋➌➍➎➏➐➑➒➓➔➕➖➗➘➙➚➛➜➝➞➟➠➡➢➣➤➥➦➧➨➩➪➫➬➭➮➯➰➱➲➳➴➵➶➷➸➹➺➻➼➽➾➿⟀⟁⟂⟃⟄⟅⟆⟇⟈⟉⟊⟋⟌⟍⟎⟏⟐⟑⟒⟓⟔⟕⟖⟗⟘⟙⟚⟛⟜⟝⟞⟟⟠⟡⟢⟣⟤⟥⟦⟧⟨⟩⟪⟫⟬⟭⟮⟯⟰⟱⟲⟳⟴⟵⟶⟷⟸⟹⟺⟻⟼⟽⟾⟿⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡","⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿⤀⤁⤂⤃⤄⤅⤆⤇⤈⤉⤊⤋⤌⤍⤎⤏⤐⤑⤒⤓⤔⤕⤖⤗⤘⤙⤚⤛⤜⤝⤞⤟⤠⤡","⤢⤣⤤⤥⤦⤧⤨⤩⤪⤫⤬⤭⤮⤯⤰⤱⤲⤳⤴⤵⤶⤷⤸⤹⤺⤻⤼⤽⤾⤿⥀⥁⥂⥃⥄⥅⥆⥇⥈⥉⥊⥋⥌⥍⥎⥏⥐⥑⥒⥓⥔⥕⥖⥗⥘⥙⥚⥛⥜⥝⥞⥟⥠⥡⥢⥣⥤⥥⥦⥧⥨⥩⥪⥫⥬⥭⥮⥯⥰⥱⥲⥳⥴⥵⥶⥷⥸⥹⥺⥻⥼⥽⥾⥿⦀⦁⦂⦃⦄⦅⦆⦇⦈⦉⦊⦋⦌⦍⦎⦏⦐⦑⦒⦓⦔⦕⦖⦗⦘⦙⦚⦛⦜⦝⦞⦟⦠⦡⦢⦣⦤⦥⦦⦧⦨⦩⦪⦫⦬⦭⦮⦯⦰⦱⦲⦳⦴⦵⦶⦷⦸⦹⦺⦻⦼⦽⦾⦿⧀⧁⧂⧃⧄⧅⧆⧇⧈⧉⧊⧋⧌⧍⧎⧏⧐⧑⧒⧓⧔⧕⧖⧗⧘⧙⧚⧛⧜⧝⧞⧟⧠⧡⧢⧣⧤⧥⧦⧧⧨⧩⧪⧫⧬⧭⧮⧯⧰⧱⧲⧳⧴⧵⧶⧷⧸⧹⧺⧻⧼⧽⧾⧿⨀⨁⨂⨃⨄⨅⨆⨇⨈⨉⨊⨋⨌⨍⨎⨏⨐⨑⨒⨓⨔⨕⨖⨗⨘⨙⨚⨛⨜⨝⨞⨟⨠⨡","⨢⨣⨤⨥⨦⨧⨨⨩⨪⨫⨬⨭⨮⨯⨰⨱⨲⨳⨴⨵⨶⨷⨸⨹⨺⨻⨼⨽⨾⨿⩀⩁⩂⩃⩄⩅⩆⩇⩈⩉⩊⩋⩌⩍⩎⩏⩐⩑⩒⩓⩔⩕⩖⩗⩘⩙⩚⩛⩜⩝⩞⩟⩠⩡⩢⩣⩤⩥⩦⩧⩨⩩⩪⩫⩬⩭⩮⩯⩰⩱⩲⩳⩴⩵⩶⩷⩸⩹⩺⩻⩼⩽⩾⩿⪀⪁⪂⪃⪄⪅⪆⪇⪈⪉⪊⪋⪌⪍⪎⪏⪐⪑⪒⪓⪔⪕⪖⪗⪘⪙⪚⪛⪜⪝⪞⪟⪠⪡⪢⪣⪤⪥⪦⪧⪨⪩⪪⪫⪬⪭⪮⪯⪰⪱⪲⪳⪴⪵⪶⪷⪸⪹⪺⪻⪼⪽⪾⪿⫀⫁⫂⫃⫄⫅⫆⫇⫈⫉⫊⫋⫌⫍⫎⫏⫐⫑⫒⫓⫔⫕⫖⫗⫘⫙⫚⫛⫝̸⫝⫞⫟⫠⫡⫢⫣⫤⫥⫦⫧⫨⫩⫪⫫⫬⫭⫮⫯⫰⫱⫲⫳⫴⫵⫶⫷⫸⫹⫺⫻⫼⫽⫾⫿⬀⬁⬂⬃⬄⬅⬆⬇⬈⬉⬊⬋⬌⬍⬎⬏⬐⬑⬒⬓⬔⬕⬖⬗⬘⬙⬚⬛⬜⬝⬞⬟⬠⬡","⬢⬣⬤⬥⬦⬧⬨⬩⬪⬫⬬⬭⬮⬯⬰⬱⬲⬳⬴⬵⬶⬷⬸⬹⬺⬻⬼⬽⬾⬿⭀⭁⭂⭃⭄⭅⭆⭇⭈⭉⭊⭋⭌⭍⭎⭏⭐⭑⭒⭓⭔⭕⭖⭗⭘⭙⭚⭛⭜⭝⭞⭟⭠⭡⭢⭣⭤⭥⭦⭧⭨⭩⭪⭫⭬⭭⭮⭯⭰⭱⭲⭳⭴⭵⭶⭷⭸⭹⭺⭻⭼⭽⭾⭿⮀⮁⮂⮃⮄⮅⮆⮇⮈⮉⮊⮋⮌⮍⮎⮏⮐⮑⮒⮓⮔⮕⮖⮗⮘⮙⮚⮛⮜⮝⮞⮟⮠⮡⮢⮣⮤⮥⮦⮧⮨⮩⮪⮫⮬⮭⮮⮯⮰⮱⮲⮳⮴⮵⮶⮷⮸⮹⮺⮻⮼⮽⮾⮿⯀⯁⯂⯃⯄⯅⯆⯇⯈⯉⯊⯋⯌⯍⯎⯏⯐⯑⯒⯓⯔⯕⯖⯗⯘⯙⯚⯛⯜⯝⯞⯟⯠⯡⯢⯣⯤⯥⯦⯧⯨⯩⯪⯫⯬⯭⯮⯯⯰⯱⯲⯳⯴⯵⯶⯷⯸⯹⯺⯻⯼⯽⯾⯿ⰀⰁⰂⰃⰄⰅⰆⰇⰈⰉⰊⰋⰌⰍⰎⰏⰐⰑⰒⰓⰔⰕⰖⰗⰘⰙⰚⰛⰜⰝⰞⰟⰠⰡ","ⰢⰣⰤⰥⰦⰧⰨⰩⰪⰫⰬⰭⰮⰯⰰⰱⰲⰳⰴⰵⰶⰷⰸⰹⰺⰻⰼⰽⰾⰿⱀⱁⱂⱃⱄⱅⱆⱇⱈⱉⱊⱋⱌⱍⱎⱏⱐⱑⱒⱓⱔⱕⱖⱗⱘⱙⱚⱛⱜⱝⱞⱟⱠⱡⱢⱣⱤⱥⱦⱧⱨⱩⱪⱫⱬⱭⱮⱯⱰⱱⱲⱳⱴⱵⱶⱷⱸⱹⱺⱻⱼⱽⱾⱿⲀⲁⲂⲃⲄⲅⲆⲇⲈⲉⲊⲋⲌⲍⲎⲏⲐⲑⲒⲓⲔⲕⲖⲗⲘⲙⲚⲛⲜⲝⲞⲟⲠⲡⲢⲣⲤⲥⲦⲧⲨⲩⲪⲫⲬⲭⲮⲯⲰⲱⲲⲳⲴⲵⲶⲷⲸⲹⲺⲻⲼⲽⲾⲿⳀⳁⳂⳃⳄⳅⳆⳇⳈⳉⳊⳋⳌⳍⳎⳏⳐⳑⳒⳓⳔⳕⳖⳗⳘⳙⳚⳛⳜⳝⳞⳟⳠⳡⳢⳣⳤ⳥⳦⳧⳨⳩⳪ⳫⳬⳭⳮ⳯⳰⳱Ⳳⳳ⳴⳵⳶⳷⳸⳹⳺⳻⳼⳽⳾⳿ⴀⴁⴂⴃⴄⴅⴆⴇⴈⴉⴊⴋⴌⴍⴎⴏⴐⴑⴒⴓⴔⴕⴖⴗⴘⴙⴚⴛⴜⴝⴞⴟⴠⴡ","ⴢⴣⴤⴥ⴦ⴧ⴨⴩⴪⴫⴬ⴭ⴮⴯ⴰⴱⴲⴳⴴⴵⴶⴷⴸⴹⴺⴻⴼⴽⴾⴿⵀⵁⵂⵃⵄⵅⵆⵇⵈⵉⵊⵋⵌⵍⵎⵏⵐⵑⵒⵓⵔⵕⵖⵗⵘⵙⵚⵛⵜⵝⵞⵟⵠⵡⵢⵣⵤⵥⵦⵧ⵨⵩⵪⵫⵬⵭⵮ⵯ⵰⵱⵲⵳⵴⵵⵶⵷⵸⵹⵺⵻⵼⵽⵾⵿ⶀⶁⶂⶃⶄⶅⶆⶇⶈⶉⶊⶋⶌⶍⶎⶏⶐⶑⶒⶓⶔⶕⶖ⶗⶘⶙⶚⶛⶜⶝⶞⶟ⶠⶡⶢⶣⶤⶥⶦ⶧ⶨⶩⶪⶫⶬⶭⶮ⶯ⶰⶱⶲⶳⶴⶵⶶ⶷ⶸⶹⶺⶻⶼⶽⶾ⶿ⷀⷁⷂⷃⷄⷅⷆ⷇ⷈⷉⷊⷋⷌⷍⷎ⷏ⷐⷑⷒⷓⷔⷕⷖ⷗ⷘⷙⷚⷛⷜⷝⷞ⷟ⷠⷡⷢⷣⷤⷥⷦⷧⷨⷩⷪⷫⷬⷭⷮⷯⷰⷱⷲⷳⷴⷵⷶⷷⷸⷹⷺⷻⷼⷽⷾⷿ⸀⸁⸂⸃⸄⸅⸆⸇⸈⸉⸊⸋⸌⸍⸎⸏⸐⸑⸒⸓⸔⸕⸖⸗⸘⸙⸚⸛⸜⸝⸞⸟⸠⸡","⸢⸣⸤⸥⸦⸧⸨⸩⸪⸫⸬⸭⸮ⸯ⸰⸱⸲⸳⸴⸵⸶⸷⸸⸹⸺⸻⸼⸽⸾⸿⹀⹁⹂⹃⹄⹅⹆⹇⹈⹉⹊⹋⹌⹍⹎⹏⹐⹑⹒⹓⹔⹕⹖⹗⹘⹙⹚⹛⹜⹝⹞⹟⹠⹡⹢⹣⹤⹥⹦⹧⹨⹩⹪⹫⹬⹭⹮⹯⹰⹱⹲⹳⹴⹵⹶⹷⹸⹹⹺⹻⹼⹽⹾⹿⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺚⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠⻡⻢⻣⻤⻥⻦⻧⻨⻩⻪⻫⻬⻭⻮⻯⻰⻱⻲⻳⻴⻵⻶⻷⻸⻹⻺⻻⻼⻽⻾⻿⼀⼁⼂⼃⼄⼅⼆⼇⼈⼉⼊⼋⼌⼍⼎⼏⼐⼑⼒⼓⼔⼕⼖⼗⼘⼙⼚⼛⼜⼝⼞⼟⼠⼡","⼢⼣⼤⼥⼦⼧⼨⼩⼪⼫⼬⼭⼮⼯⼰⼱⼲⼳⼴⼵⼶⼷⼸⼹⼺⼻⼼⼽⼾⼿⽀⽁⽂⽃⽄⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿⾀⾁⾂⾃⾄⾅⾆⾇⾈⾉⾊⾋⾌⾍⾎⾏⾐⾑⾒⾓⾔⾕⾖⾗⾘⾙⾚⾛⾜⾝⾞⾟⾠⾡⾢⾣⾤⾥⾦⾧⾨⾩⾪⾫⾬⾭⾮⾯⾰⾱⾲⾳⾴⾵⾶⾷⾸⾹⾺⾻⾼⾽⾾⾿⿀⿁⿂⿃⿄⿅⿆⿇⿈⿉⿊⿋⿌⿍⿎⿏⿐⿑⿒⿓⿔⿕⿖⿗⿘⿙⿚⿛⿜⿝⿞⿟⿠⿡⿢⿣⿤⿥⿦⿧⿨⿩⿪⿫⿬⿭⿮⿯⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻⿼⿽⿾⿿ 、。〃〄々〆〇〈〉《》「」『』【】〒〓〔〕〖〗〘〙〚〛〜〝〞〟〠〡","〢〣〤〥〦〧〨〩〪〭〮〯〫〬〰〱〲〳〴〵〶〷〸〹〺〻〼〽〾〿぀ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんゔゕゖ゗゘゙゚゛゜ゝゞゟ゠ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶヷヸヹヺ・ーヽヾヿ㄀㄁㄂㄃㄄ㄅㄆㄇㄈㄉㄊㄋㄌㄍㄎㄏㄐㄑㄒㄓㄔㄕㄖㄗㄘㄙㄚㄛㄜㄝㄞㄟㄠㄡ","ㄢㄣㄤㄥㄦㄧㄨㄩㄪㄫㄬㄭㄮㄯ㄰ㄱㄲㄳㄴㄵㄶㄷㄸㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅃㅄㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎㅏㅐㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅣㅤㅥㅦㅧㅨㅩㅪㅫㅬㅭㅮㅯㅰㅱㅲㅳㅴㅵㅶㅷㅸㅹㅺㅻㅼㅽㅾㅿㆀㆁㆂㆃㆄㆅㆆㆇㆈㆉㆊㆋㆌㆍㆎ㆏㆐㆑㆒㆓㆔㆕㆖㆗㆘㆙㆚㆛㆜㆝㆞㆟ㆠㆡㆢㆣㆤㆥㆦㆧㆨㆩㆪㆫㆬㆭㆮㆯㆰㆱㆲㆳㆴㆵㆶㆷㆸㆹㆺㆻㆼㆽㆾㆿ㇀㇁㇂㇃㇄㇅㇆㇇㇈㇉㇊㇋㇌㇍㇎㇏㇐㇑㇒㇓㇔㇕㇖㇗㇘㇙㇚㇛㇜㇝㇞㇟㇠㇡㇢㇣㇤㇥㇦㇧㇨㇩㇪㇫㇬㇭㇮㇯ㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ㈀㈁㈂㈃㈄㈅㈆㈇㈈㈉㈊㈋㈌㈍㈎㈏㈐㈑㈒㈓㈔㈕㈖㈗㈘㈙㈚㈛㈜㈝㈞㈟㈠㈡","㈢㈣㈤㈥㈦㈧㈨㈩㈪㈫㈬㈭㈮㈯㈰㈱㈲㈳㈴㈵㈶㈷㈸㈹㈺㈻㈼㈽㈾㈿㉀㉁㉂㉃㉄㉅㉆㉇㉈㉉㉊㉋㉌㉍㉎㉏㉐㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟㉠㉡㉢㉣㉤㉥㉦㉧㉨㉩㉪㉫㉬㉭㉮㉯㉰㉱㉲㉳㉴㉵㉶㉷㉸㉹㉺㉻㉼㉽㉾㉿㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉㊊㊋㊌㊍㊎㊏㊐㊑㊒㊓㊔㊕㊖㊗㊘㊙㊚㊛㊜㊝㊞㊟㊠㊡㊢㊣㊤㊥㊦㊧㊨㊩㊪㊫㊬㊭㊮㊯㊰㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿㋀㋁㋂㋃㋄㋅㋆㋇㋈㋉㋊㋋㋌㋍㋎㋏㋐㋑㋒㋓㋔㋕㋖㋗㋘㋙㋚㋛㋜㋝㋞㋟㋠㋡㋢㋣㋤㋥㋦㋧㋨㋩㋪㋫㋬㋭㋮㋯㋰㋱㋲㋳㋴㋵㋶㋷㋸㋹㋺㋻㋼㋽㋾㋿㌀㌁㌂㌃㌄㌅㌆㌇㌈㌉㌊㌋㌌㌍㌎㌏㌐㌑㌒㌓㌔㌕㌖㌗㌘㌙㌚㌛㌜㌝㌞㌟㌠㌡","㌢㌣㌤㌥㌦㌧㌨㌩㌪㌫㌬㌭㌮㌯㌰㌱㌲㌳㌴㌵㌶㌷㌸㌹㌺㌻㌼㌽㌾㌿㍀㍁㍂㍃㍄㍅㍆㍇㍈㍉㍊㍋㍌㍍㍎㍏㍐㍑㍒㍓㍔㍕㍖㍗㍘㍙㍚㍛㍜㍝㍞㍟㍠㍡㍢㍣㍤㍥㍦㍧㍨㍩㍪㍫㍬㍭㍮㍯㍰㍱㍲㍳㍴㍵㍶㍷㍸㍹㍺㍻㍼㍽㍾㍿㎀㎁㎂㎃㎄㎅㎆㎇㎈㎉㎊㎋㎌㎍㎎㎏㎐㎑㎒㎓㎔㎕㎖㎗㎘㎙㎚㎛㎜㎝㎞㎟㎠㎡㎢㎣㎤㎥㎦㎧㎨㎩㎪㎫㎬㎭㎮㎯㎰㎱㎲㎳㎴㎵㎶㎷㎸㎹㎺㎻㎼㎽㎾㎿㏀㏁㏂㏃㏄㏅㏆㏇㏈㏉㏊㏋㏌㏍㏎㏏㏐㏑㏒㏓㏔㏕㏖㏗㏘㏙㏚㏛㏜㏝㏞㏟㏠㏡㏢㏣㏤㏥㏦㏧㏨㏩㏪㏫㏬㏭㏮㏯㏰㏱㏲㏳㏴㏵㏶㏷㏸㏹㏺㏻㏼㏽㏾㏿㐀㐁㐂㐃㐄㐅㐆㐇㐈㐉㐊㐋㐌㐍㐎㐏㐐㐑㐒㐓㐔㐕㐖㐗㐘㐙㐚㐛㐜㐝㐞㐟㐠㐡","㐢㐣㐤㐥㐦㐧㐨㐩㐪㐫㐬㐭㐮㐯㐰㐱㐲㐳㐴㐵㐶㐷㐸㐹㐺㐻㐼㐽㐾㐿㑀㑁㑂㑃㑄㑅㑆㑇㑈㑉㑊㑋㑌㑍㑎㑏㑐㑑㑒㑓㑔㑕㑖㑗㑘㑙㑚㑛㑜㑝㑞㑟㑠㑡㑢㑣㑤㑥㑦㑧㑨㑩㑪㑫㑬㑭㑮㑯㑰㑱㑲㑳㑴㑵㑶㑷㑸㑹㑺㑻㑼㑽㑾㑿㒀㒁㒂㒃㒄㒅㒆㒇㒈㒉㒊㒋㒌㒍㒎㒏㒐㒑㒒㒓㒔㒕㒖㒗㒘㒙㒚㒛㒜㒝㒞㒟㒠㒡㒢㒣㒤㒥㒦㒧㒨㒩㒪㒫㒬㒭㒮㒯㒰㒱㒲㒳㒴㒵㒶㒷㒸㒹㒺㒻㒼㒽㒾㒿㓀㓁㓂㓃㓄㓅㓆㓇㓈㓉㓊㓋㓌㓍㓎㓏㓐㓑㓒㓓㓔㓕㓖㓗㓘㓙㓚㓛㓜㓝㓞㓟㓠㓡㓢㓣㓤㓥㓦㓧㓨㓩㓪㓫㓬㓭㓮㓯㓰㓱㓲㓳㓴㓵㓶㓷㓸㓹㓺㓻㓼㓽㓾㓿㔀㔁㔂㔃㔄㔅㔆㔇㔈㔉㔊㔋㔌㔍㔎㔏㔐㔑㔒㔓㔔㔕㔖㔗㔘㔙㔚㔛㔜㔝㔞㔟㔠㔡","㔢㔣㔤㔥㔦㔧㔨㔩㔪㔫㔬㔭㔮㔯㔰㔱㔲㔳㔴㔵㔶㔷㔸㔹㔺㔻㔼㔽㔾㔿㕀㕁㕂㕃㕄㕅㕆㕇㕈㕉㕊㕋㕌㕍㕎㕏㕐㕑㕒㕓㕔㕕㕖㕗㕘㕙㕚㕛㕜㕝㕞㕟㕠㕡㕢㕣㕤㕥㕦㕧㕨㕩㕪㕫㕬㕭㕮㕯㕰㕱㕲㕳㕴㕵㕶㕷㕸㕹㕺㕻㕼㕽㕾㕿㖀㖁㖂㖃㖄㖅㖆㖇㖈㖉㖊㖋㖌㖍㖎㖏㖐㖑㖒㖓㖔㖕㖖㖗㖘㖙㖚㖛㖜㖝㖞㖟㖠㖡㖢㖣㖤㖥㖦㖧㖨㖩㖪㖫㖬㖭㖮㖯㖰㖱㖲㖳㖴㖵㖶㖷㖸㖹㖺㖻㖼㖽㖾㖿㗀㗁㗂㗃㗄㗅㗆㗇㗈㗉㗊㗋㗌㗍㗎㗏㗐㗑㗒㗓㗔㗕㗖㗗㗘㗙㗚㗛㗜㗝㗞㗟㗠㗡㗢㗣㗤㗥㗦㗧㗨㗩㗪㗫㗬㗭㗮㗯㗰㗱㗲㗳㗴㗵㗶㗷㗸㗹㗺㗻㗼㗽㗾㗿㘀㘁㘂㘃㘄㘅㘆㘇㘈㘉㘊㘋㘌㘍㘎㘏㘐㘑㘒㘓㘔㘕㘖㘗㘘㘙㘚㘛㘜㘝㘞㘟㘠㘡","㘢㘣㘤㘥㘦㘧㘨㘩㘪㘫㘬㘭㘮㘯㘰㘱㘲㘳㘴㘵㘶㘷㘸㘹㘺㘻㘼㘽㘾㘿㙀㙁㙂㙃㙄㙅㙆㙇㙈㙉㙊㙋㙌㙍㙎㙏㙐㙑㙒㙓㙔㙕㙖㙗㙘㙙㙚㙛㙜㙝㙞㙟㙠㙡㙢㙣㙤㙥㙦㙧㙨㙩㙪㙫㙬㙭㙮㙯㙰㙱㙲㙳㙴㙵㙶㙷㙸㙹㙺㙻㙼㙽㙾㙿㚀㚁㚂㚃㚄㚅㚆㚇㚈㚉㚊㚋㚌㚍㚎㚏㚐㚑㚒㚓㚔㚕㚖㚗㚘㚙㚚㚛㚜㚝㚞㚟㚠㚡㚢㚣㚤㚥㚦㚧㚨㚩㚪㚫㚬㚭㚮㚯㚰㚱㚲㚳㚴㚵㚶㚷㚸㚹㚺㚻㚼㚽㚾㚿㛀㛁㛂㛃㛄㛅㛆㛇㛈㛉㛊㛋㛌㛍㛎㛏㛐㛑㛒㛓㛔㛕㛖㛗㛘㛙㛚㛛㛜㛝㛞㛟㛠㛡㛢㛣㛤㛥㛦㛧㛨㛩㛪㛫㛬㛭㛮㛯㛰㛱㛲㛳㛴㛵㛶㛷㛸㛹㛺㛻㛼㛽㛾㛿㜀㜁㜂㜃㜄㜅㜆㜇㜈㜉㜊㜋㜌㜍㜎㜏㜐㜑㜒㜓㜔㜕㜖㜗㜘㜙㜚㜛㜜㜝㜞㜟㜠㜡","㜢㜣㜤㜥㜦㜧㜨㜩㜪㜫㜬㜭㜮㜯㜰㜱㜲㜳㜴㜵㜶㜷㜸㜹㜺㜻㜼㜽㜾㜿㝀㝁㝂㝃㝄㝅㝆㝇㝈㝉㝊㝋㝌㝍㝎㝏㝐㝑㝒㝓㝔㝕㝖㝗㝘㝙㝚㝛㝜㝝㝞㝟㝠㝡㝢㝣㝤㝥㝦㝧㝨㝩㝪㝫㝬㝭㝮㝯㝰㝱㝲㝳㝴㝵㝶㝷㝸㝹㝺㝻㝼㝽㝾㝿㞀㞁㞂㞃㞄㞅㞆㞇㞈㞉㞊㞋㞌㞍㞎㞏㞐㞑㞒㞓㞔㞕㞖㞗㞘㞙㞚㞛㞜㞝㞞㞟㞠㞡㞢㞣㞤㞥㞦㞧㞨㞩㞪㞫㞬㞭㞮㞯㞰㞱㞲㞳㞴㞵㞶㞷㞸㞹㞺㞻㞼㞽㞾㞿㟀㟁㟂㟃㟄㟅㟆㟇㟈㟉㟊㟋㟌㟍㟎㟏㟐㟑㟒㟓㟔㟕㟖㟗㟘㟙㟚㟛㟜㟝㟞㟟㟠㟡㟢㟣㟤㟥㟦㟧㟨㟩㟪㟫㟬㟭㟮㟯㟰㟱㟲㟳㟴㟵㟶㟷㟸㟹㟺㟻㟼㟽㟾㟿㠀㠁㠂㠃㠄㠅㠆㠇㠈㠉㠊㠋㠌㠍㠎㠏㠐㠑㠒㠓㠔㠕㠖㠗㠘㠙㠚㠛㠜㠝㠞㠟㠠㠡","㠢㠣㠤㠥㠦㠧㠨㠩㠪㠫㠬㠭㠮㠯㠰㠱㠲㠳㠴㠵㠶㠷㠸㠹㠺㠻㠼㠽㠾㠿㡀㡁㡂㡃㡄㡅㡆㡇㡈㡉㡊㡋㡌㡍㡎㡏㡐㡑㡒㡓㡔㡕㡖㡗㡘㡙㡚㡛㡜㡝㡞㡟㡠㡡㡢㡣㡤㡥㡦㡧㡨㡩㡪㡫㡬㡭㡮㡯㡰㡱㡲㡳㡴㡵㡶㡷㡸㡹㡺㡻㡼㡽㡾㡿㢀㢁㢂㢃㢄㢅㢆㢇㢈㢉㢊㢋㢌㢍㢎㢏㢐㢑㢒㢓㢔㢕㢖㢗㢘㢙㢚㢛㢜㢝㢞㢟㢠㢡㢢㢣㢤㢥㢦㢧㢨㢩㢪㢫㢬㢭㢮㢯㢰㢱㢲㢳㢴㢵㢶㢷㢸㢹㢺㢻㢼㢽㢾㢿㣀㣁㣂㣃㣄㣅㣆㣇㣈㣉㣊㣋㣌㣍㣎㣏㣐㣑㣒㣓㣔㣕㣖㣗㣘㣙㣚㣛㣜㣝㣞㣟㣠㣡㣢㣣㣤㣥㣦㣧㣨㣩㣪㣫㣬㣭㣮㣯㣰㣱㣲㣳㣴㣵㣶㣷㣸㣹㣺㣻㣼㣽㣾㣿㤀㤁㤂㤃㤄㤅㤆㤇㤈㤉㤊㤋㤌㤍㤎㤏㤐㤑㤒㤓㤔㤕㤖㤗㤘㤙㤚㤛㤜㤝㤞㤟㤠㤡","㤢㤣㤤㤥㤦㤧㤨㤩㤪㤫㤬㤭㤮㤯㤰㤱㤲㤳㤴㤵㤶㤷㤸㤹㤺㤻㤼㤽㤾㤿㥀㥁㥂㥃㥄㥅㥆㥇㥈㥉㥊㥋㥌㥍㥎㥏㥐㥑㥒㥓㥔㥕㥖㥗㥘㥙㥚㥛㥜㥝㥞㥟㥠㥡㥢㥣㥤㥥㥦㥧㥨㥩㥪㥫㥬㥭㥮㥯㥰㥱㥲㥳㥴㥵㥶㥷㥸㥹㥺㥻㥼㥽㥾㥿㦀㦁㦂㦃㦄㦅㦆㦇㦈㦉㦊㦋㦌㦍㦎㦏㦐㦑㦒㦓㦔㦕㦖㦗㦘㦙㦚㦛㦜㦝㦞㦟㦠㦡㦢㦣㦤㦥㦦㦧㦨㦩㦪㦫㦬㦭㦮㦯㦰㦱㦲㦳㦴㦵㦶㦷㦸㦹㦺㦻㦼㦽㦾㦿㧀㧁㧂㧃㧄㧅㧆㧇㧈㧉㧊㧋㧌㧍㧎㧏㧐㧑㧒㧓㧔㧕㧖㧗㧘㧙㧚㧛㧜㧝㧞㧟㧠㧡㧢㧣㧤㧥㧦㧧㧨㧩㧪㧫㧬㧭㧮㧯㧰㧱㧲㧳㧴㧵㧶㧷㧸㧹㧺㧻㧼㧽㧾㧿㨀㨁㨂㨃㨄㨅㨆㨇㨈㨉㨊㨋㨌㨍㨎㨏㨐㨑㨒㨓㨔㨕㨖㨗㨘㨙㨚㨛㨜㨝㨞㨟㨠㨡","㨢㨣㨤㨥㨦㨧㨨㨩㨪㨫㨬㨭㨮㨯㨰㨱㨲㨳㨴㨵㨶㨷㨸㨹㨺㨻㨼㨽㨾㨿㩀㩁㩂㩃㩄㩅㩆㩇㩈㩉㩊㩋㩌㩍㩎㩏㩐㩑㩒㩓㩔㩕㩖㩗㩘㩙㩚㩛㩜㩝㩞㩟㩠㩡㩢㩣㩤㩥㩦㩧㩨㩩㩪㩫㩬㩭㩮㩯㩰㩱㩲㩳㩴㩵㩶㩷㩸㩹㩺㩻㩼㩽㩾㩿㪀㪁㪂㪃㪄㪅㪆㪇㪈㪉㪊㪋㪌㪍㪎㪏㪐㪑㪒㪓㪔㪕㪖㪗㪘㪙㪚㪛㪜㪝㪞㪟㪠㪡㪢㪣㪤㪥㪦㪧㪨㪩㪪㪫㪬㪭㪮㪯㪰㪱㪲㪳㪴㪵㪶㪷㪸㪹㪺㪻㪼㪽㪾㪿㫀㫁㫂㫃㫄㫅㫆㫇㫈㫉㫊㫋㫌㫍㫎㫏㫐㫑㫒㫓㫔㫕㫖㫗㫘㫙㫚㫛㫜㫝㫞㫟㫠㫡㫢㫣㫤㫥㫦㫧㫨㫩㫪㫫㫬㫭㫮㫯㫰㫱㫲㫳㫴㫵㫶㫷㫸㫹㫺㫻㫼㫽㫾㫿㬀㬁㬂㬃㬄㬅㬆㬇㬈㬉㬊㬋㬌㬍㬎㬏㬐㬑㬒㬓㬔㬕㬖㬗㬘㬙㬚㬛㬜㬝㬞㬟㬠㬡","㬢㬣㬤㬥㬦㬧㬨㬩㬪㬫㬬㬭㬮㬯㬰㬱㬲㬳㬴㬵㬶㬷㬸㬹㬺㬻㬼㬽㬾㬿㭀㭁㭂㭃㭄㭅㭆㭇㭈㭉㭊㭋㭌㭍㭎㭏㭐㭑㭒㭓㭔㭕㭖㭗㭘㭙㭚㭛㭜㭝㭞㭟㭠㭡㭢㭣㭤㭥㭦㭧㭨㭩㭪㭫㭬㭭㭮㭯㭰㭱㭲㭳㭴㭵㭶㭷㭸㭹㭺㭻㭼㭽㭾㭿㮀㮁㮂㮃㮄㮅㮆㮇㮈㮉㮊㮋㮌㮍㮎㮏㮐㮑㮒㮓㮔㮕㮖㮗㮘㮙㮚㮛㮜㮝㮞㮟㮠㮡㮢㮣㮤㮥㮦㮧㮨㮩㮪㮫㮬㮭㮮㮯㮰㮱㮲㮳㮴㮵㮶㮷㮸㮹㮺㮻㮼㮽㮾㮿㯀㯁㯂㯃㯄㯅㯆㯇㯈㯉㯊㯋㯌㯍㯎㯏㯐㯑㯒㯓㯔㯕㯖㯗㯘㯙㯚㯛㯜㯝㯞㯟㯠㯡㯢㯣㯤㯥㯦㯧㯨㯩㯪㯫㯬㯭㯮㯯㯰㯱㯲㯳㯴㯵㯶㯷㯸㯹㯺㯻㯼㯽㯾㯿㰀㰁㰂㰃㰄㰅㰆㰇㰈㰉㰊㰋㰌㰍㰎㰏㰐㰑㰒㰓㰔㰕㰖㰗㰘㰙㰚㰛㰜㰝㰞㰟㰠㰡","㰢㰣㰤㰥㰦㰧㰨㰩㰪㰫㰬㰭㰮㰯㰰㰱㰲㰳㰴㰵㰶㰷㰸㰹㰺㰻㰼㰽㰾㰿㱀㱁㱂㱃㱄㱅㱆㱇㱈㱉㱊㱋㱌㱍㱎㱏㱐㱑㱒㱓㱔㱕㱖㱗㱘㱙㱚㱛㱜㱝㱞㱟㱠㱡㱢㱣㱤㱥㱦㱧㱨㱩㱪㱫㱬㱭㱮㱯㱰㱱㱲㱳㱴㱵㱶㱷㱸㱹㱺㱻㱼㱽㱾㱿㲀㲁㲂㲃㲄㲅㲆㲇㲈㲉㲊㲋㲌㲍㲎㲏㲐㲑㲒㲓㲔㲕㲖㲗㲘㲙㲚㲛㲜㲝㲞㲟㲠㲡㲢㲣㲤㲥㲦㲧㲨㲩㲪㲫㲬㲭㲮㲯㲰㲱㲲㲳㲴㲵㲶㲷㲸㲹㲺㲻㲼㲽㲾㲿㳀㳁㳂㳃㳄㳅㳆㳇㳈㳉㳊㳋㳌㳍㳎㳏㳐㳑㳒㳓㳔㳕㳖㳗㳘㳙㳚㳛㳜㳝㳞㳟㳠㳡㳢㳣㳤㳥㳦㳧㳨㳩㳪㳫㳬㳭㳮㳯㳰㳱㳲㳳㳴㳵㳶㳷㳸㳹㳺㳻㳼㳽㳾㳿㴀㴁㴂㴃㴄㴅㴆㴇㴈㴉㴊㴋㴌㴍㴎㴏㴐㴑㴒㴓㴔㴕㴖㴗㴘㴙㴚㴛㴜㴝㴞㴟㴠㴡","㴢㴣㴤㴥㴦㴧㴨㴩㴪㴫㴬㴭㴮㴯㴰㴱㴲㴳㴴㴵㴶㴷㴸㴹㴺㴻㴼㴽㴾㴿㵀㵁㵂㵃㵄㵅㵆㵇㵈㵉㵊㵋㵌㵍㵎㵏㵐㵑㵒㵓㵔㵕㵖㵗㵘㵙㵚㵛㵜㵝㵞㵟㵠㵡㵢㵣㵤㵥㵦㵧㵨㵩㵪㵫㵬㵭㵮㵯㵰㵱㵲㵳㵴㵵㵶㵷㵸㵹㵺㵻㵼㵽㵾㵿㶀㶁㶂㶃㶄㶅㶆㶇㶈㶉㶊㶋㶌㶍㶎㶏㶐㶑㶒㶓㶔㶕㶖㶗㶘㶙㶚㶛㶜㶝㶞㶟㶠㶡㶢㶣㶤㶥㶦㶧㶨㶩㶪㶫㶬㶭㶮㶯㶰㶱㶲㶳㶴㶵㶶㶷㶸㶹㶺㶻㶼㶽㶾㶿㷀㷁㷂㷃㷄㷅㷆㷇㷈㷉㷊㷋㷌㷍㷎㷏㷐㷑㷒㷓㷔㷕㷖㷗㷘㷙㷚㷛㷜㷝㷞㷟㷠㷡㷢㷣㷤㷥㷦㷧㷨㷩㷪㷫㷬㷭㷮㷯㷰㷱㷲㷳㷴㷵㷶㷷㷸㷹㷺㷻㷼㷽㷾㷿㸀㸁㸂㸃㸄㸅㸆㸇㸈㸉㸊㸋㸌㸍㸎㸏㸐㸑㸒㸓㸔㸕㸖㸗㸘㸙㸚㸛㸜㸝㸞㸟㸠㸡","㸢㸣㸤㸥㸦㸧㸨㸩㸪㸫㸬㸭㸮㸯㸰㸱㸲㸳㸴㸵㸶㸷㸸㸹㸺㸻㸼㸽㸾㸿㹀㹁㹂㹃㹄㹅㹆㹇㹈㹉㹊㹋㹌㹍㹎㹏㹐㹑㹒㹓㹔㹕㹖㹗㹘㹙㹚㹛㹜㹝㹞㹟㹠㹡㹢㹣㹤㹥㹦㹧㹨㹩㹪㹫㹬㹭㹮㹯㹰㹱㹲㹳㹴㹵㹶㹷㹸㹹㹺㹻㹼㹽㹾㹿㺀㺁㺂㺃㺄㺅㺆㺇㺈㺉㺊㺋㺌㺍㺎㺏㺐㺑㺒㺓㺔㺕㺖㺗㺘㺙㺚㺛㺜㺝㺞㺟㺠㺡㺢㺣㺤㺥㺦㺧㺨㺩㺪㺫㺬㺭㺮㺯㺰㺱㺲㺳㺴㺵㺶㺷㺸㺹㺺㺻㺼㺽㺾㺿㻀㻁㻂㻃㻄㻅㻆㻇㻈㻉㻊㻋㻌㻍㻎㻏㻐㻑㻒㻓㻔㻕㻖㻗㻘㻙㻚㻛㻜㻝㻞㻟㻠㻡㻢㻣㻤㻥㻦㻧㻨㻩㻪㻫㻬㻭㻮㻯㻰㻱㻲㻳㻴㻵㻶㻷㻸㻹㻺㻻㻼㻽㻾㻿㼀㼁㼂㼃㼄㼅㼆㼇㼈㼉㼊㼋㼌㼍㼎㼏㼐㼑㼒㼓㼔㼕㼖㼗㼘㼙㼚㼛㼜㼝㼞㼟㼠㼡","㼢㼣㼤㼥㼦㼧㼨㼩㼪㼫㼬㼭㼮㼯㼰㼱㼲㼳㼴㼵㼶㼷㼸㼹㼺㼻㼼㼽㼾㼿㽀㽁㽂㽃㽄㽅㽆㽇㽈㽉㽊㽋㽌㽍㽎㽏㽐㽑㽒㽓㽔㽕㽖㽗㽘㽙㽚㽛㽜㽝㽞㽟㽠㽡㽢㽣㽤㽥㽦㽧㽨㽩㽪㽫㽬㽭㽮㽯㽰㽱㽲㽳㽴㽵㽶㽷㽸㽹㽺㽻㽼㽽㽾㽿㾀㾁㾂㾃㾄㾅㾆㾇㾈㾉㾊㾋㾌㾍㾎㾏㾐㾑㾒㾓㾔㾕㾖㾗㾘㾙㾚㾛㾜㾝㾞㾟㾠㾡㾢㾣㾤㾥㾦㾧㾨㾩㾪㾫㾬㾭㾮㾯㾰㾱㾲㾳㾴㾵㾶㾷㾸㾹㾺㾻㾼㾽㾾㾿㿀㿁㿂㿃㿄㿅㿆㿇㿈㿉㿊㿋㿌㿍㿎㿏㿐㿑㿒㿓㿔㿕㿖㿗㿘㿙㿚㿛㿜㿝㿞㿟㿠㿡㿢㿣㿤㿥㿦㿧㿨㿩㿪㿫㿬㿭㿮㿯㿰㿱㿲㿳㿴㿵㿶㿷㿸㿹㿺㿻㿼㿽㿾㿿䀀䀁䀂䀃䀄䀅䀆䀇䀈䀉䀊䀋䀌䀍䀎䀏䀐䀑䀒䀓䀔䀕䀖䀗䀘䀙䀚䀛䀜䀝䀞䀟䀠䀡","䀢䀣䀤䀥䀦䀧䀨䀩䀪䀫䀬䀭䀮䀯䀰䀱䀲䀳䀴䀵䀶䀷䀸䀹䀺䀻䀼䀽䀾䀿䁀䁁䁂䁃䁄䁅䁆䁇䁈䁉䁊䁋䁌䁍䁎䁏䁐䁑䁒䁓䁔䁕䁖䁗䁘䁙䁚䁛䁜䁝䁞䁟䁠䁡䁢䁣䁤䁥䁦䁧䁨䁩䁪䁫䁬䁭䁮䁯䁰䁱䁲䁳䁴䁵䁶䁷䁸䁹䁺䁻䁼䁽䁾䁿䂀䂁䂂䂃䂄䂅䂆䂇䂈䂉䂊䂋䂌䂍䂎䂏䂐䂑䂒䂓䂔䂕䂖䂗䂘䂙䂚䂛䂜䂝䂞䂟䂠䂡䂢䂣䂤䂥䂦䂧䂨䂩䂪䂫䂬䂭䂮䂯䂰䂱䂲䂳䂴䂵䂶䂷䂸䂹䂺䂻䂼䂽䂾䂿䃀䃁䃂䃃䃄䃅䃆䃇䃈䃉䃊䃋䃌䃍䃎䃏䃐䃑䃒䃓䃔䃕䃖䃗䃘䃙䃚䃛䃜䃝䃞䃟䃠䃡䃢䃣䃤䃥䃦䃧䃨䃩䃪䃫䃬䃭䃮䃯䃰䃱䃲䃳䃴䃵䃶䃷䃸䃹䃺䃻䃼䃽䃾䃿䄀䄁䄂䄃䄄䄅䄆䄇䄈䄉䄊䄋䄌䄍䄎䄏䄐䄑䄒䄓䄔䄕䄖䄗䄘䄙䄚䄛䄜䄝䄞䄟䄠䄡","䄢䄣䄤䄥䄦䄧䄨䄩䄪䄫䄬䄭䄮䄯䄰䄱䄲䄳䄴䄵䄶䄷䄸䄹䄺䄻䄼䄽䄾䄿䅀䅁䅂䅃䅄䅅䅆䅇䅈䅉䅊䅋䅌䅍䅎䅏䅐䅑䅒䅓䅔䅕䅖䅗䅘䅙䅚䅛䅜䅝䅞䅟䅠䅡䅢䅣䅤䅥䅦䅧䅨䅩䅪䅫䅬䅭䅮䅯䅰䅱䅲䅳䅴䅵䅶䅷䅸䅹䅺䅻䅼䅽䅾䅿䆀䆁䆂䆃䆄䆅䆆䆇䆈䆉䆊䆋䆌䆍䆎䆏䆐䆑䆒䆓䆔䆕䆖䆗䆘䆙䆚䆛䆜䆝䆞䆟䆠䆡䆢䆣䆤䆥䆦䆧䆨䆩䆪䆫䆬䆭䆮䆯䆰䆱䆲䆳䆴䆵䆶䆷䆸䆹䆺䆻䆼䆽䆾䆿䇀䇁䇂䇃䇄䇅䇆䇇䇈䇉䇊䇋䇌䇍䇎䇏䇐䇑䇒䇓䇔䇕䇖䇗䇘䇙䇚䇛䇜䇝䇞䇟䇠䇡䇢䇣䇤䇥䇦䇧䇨䇩䇪䇫䇬䇭䇮䇯䇰䇱䇲䇳䇴䇵䇶䇷䇸䇹䇺䇻䇼䇽䇾䇿䈀䈁䈂䈃䈄䈅䈆䈇䈈䈉䈊䈋䈌䈍䈎䈏䈐䈑䈒䈓䈔䈕䈖䈗䈘䈙䈚䈛䈜䈝䈞䈟䈠䈡","䈢䈣䈤䈥䈦䈧䈨䈩䈪䈫䈬䈭䈮䈯䈰䈱䈲䈳䈴䈵䈶䈷䈸䈹䈺䈻䈼䈽䈾䈿䉀䉁䉂䉃䉄䉅䉆䉇䉈䉉䉊䉋䉌䉍䉎䉏䉐䉑䉒䉓䉔䉕䉖䉗䉘䉙䉚䉛䉜䉝䉞䉟䉠䉡䉢䉣䉤䉥䉦䉧䉨䉩䉪䉫䉬䉭䉮䉯䉰䉱䉲䉳䉴䉵䉶䉷䉸䉹䉺䉻䉼䉽䉾䉿䊀䊁䊂䊃䊄䊅䊆䊇䊈䊉䊊䊋䊌䊍䊎䊏䊐䊑䊒䊓䊔䊕䊖䊗䊘䊙䊚䊛䊜䊝䊞䊟䊠䊡䊢䊣䊤䊥䊦䊧䊨䊩䊪䊫䊬䊭䊮䊯䊰䊱䊲䊳䊴䊵䊶䊷䊸䊹䊺䊻䊼䊽䊾䊿䋀䋁䋂䋃䋄䋅䋆䋇䋈䋉䋊䋋䋌䋍䋎䋏䋐䋑䋒䋓䋔䋕䋖䋗䋘䋙䋚䋛䋜䋝䋞䋟䋠䋡䋢䋣䋤䋥䋦䋧䋨䋩䋪䋫䋬䋭䋮䋯䋰䋱䋲䋳䋴䋵䋶䋷䋸䋹䋺䋻䋼䋽䋾䋿䌀䌁䌂䌃䌄䌅䌆䌇䌈䌉䌊䌋䌌䌍䌎䌏䌐䌑䌒䌓䌔䌕䌖䌗䌘䌙䌚䌛䌜䌝䌞䌟䌠䌡","䌢䌣䌤䌥䌦䌧䌨䌩䌪䌫䌬䌭䌮䌯䌰䌱䌲䌳䌴䌵䌶䌷䌸䌹䌺䌻䌼䌽䌾䌿䍀䍁䍂䍃䍄䍅䍆䍇䍈䍉䍊䍋䍌䍍䍎䍏䍐䍑䍒䍓䍔䍕䍖䍗䍘䍙䍚䍛䍜䍝䍞䍟䍠䍡䍢䍣䍤䍥䍦䍧䍨䍩䍪䍫䍬䍭䍮䍯䍰䍱䍲䍳䍴䍵䍶䍷䍸䍹䍺䍻䍼䍽䍾䍿䎀䎁䎂䎃䎄䎅䎆䎇䎈䎉䎊䎋䎌䎍䎎䎏䎐䎑䎒䎓䎔䎕䎖䎗䎘䎙䎚䎛䎜䎝䎞䎟䎠䎡䎢䎣䎤䎥䎦䎧䎨䎩䎪䎫䎬䎭䎮䎯䎰䎱䎲䎳䎴䎵䎶䎷䎸䎹䎺䎻䎼䎽䎾䎿䏀䏁䏂䏃䏄䏅䏆䏇䏈䏉䏊䏋䏌䏍䏎䏏䏐䏑䏒䏓䏔䏕䏖䏗䏘䏙䏚䏛䏜䏝䏞䏟䏠䏡䏢䏣䏤䏥䏦䏧䏨䏩䏪䏫䏬䏭䏮䏯䏰䏱䏲䏳䏴䏵䏶䏷䏸䏹䏺䏻䏼䏽䏾䏿䐀䐁䐂䐃䐄䐅䐆䐇䐈䐉䐊䐋䐌䐍䐎䐏䐐䐑䐒䐓䐔䐕䐖䐗䐘䐙䐚䐛䐜䐝䐞䐟䐠䐡","䐢䐣䐤䐥䐦䐧䐨䐩䐪䐫䐬䐭䐮䐯䐰䐱䐲䐳䐴䐵䐶䐷䐸䐹䐺䐻䐼䐽䐾䐿䑀䑁䑂䑃䑄䑅䑆䑇䑈䑉䑊䑋䑌䑍䑎䑏䑐䑑䑒䑓䑔䑕䑖䑗䑘䑙䑚䑛䑜䑝䑞䑟䑠䑡䑢䑣䑤䑥䑦䑧䑨䑩䑪䑫䑬䑭䑮䑯䑰䑱䑲䑳䑴䑵䑶䑷䑸䑹䑺䑻䑼䑽䑾䑿䒀䒁䒂䒃䒄䒅䒆䒇䒈䒉䒊䒋䒌䒍䒎䒏䒐䒑䒒䒓䒔䒕䒖䒗䒘䒙䒚䒛䒜䒝䒞䒟䒠䒡䒢䒣䒤䒥䒦䒧䒨䒩䒪䒫䒬䒭䒮䒯䒰䒱䒲䒳䒴䒵䒶䒷䒸䒹䒺䒻䒼䒽䒾䒿䓀䓁䓂䓃䓄䓅䓆䓇䓈䓉䓊䓋䓌䓍䓎䓏䓐䓑䓒䓓䓔䓕䓖䓗䓘䓙䓚䓛䓜䓝䓞䓟䓠䓡䓢䓣䓤䓥䓦䓧䓨䓩䓪䓫䓬䓭䓮䓯䓰䓱䓲䓳䓴䓵䓶䓷䓸䓹䓺䓻䓼䓽䓾䓿䔀䔁䔂䔃䔄䔅䔆䔇䔈䔉䔊䔋䔌䔍䔎䔏䔐䔑䔒䔓䔔䔕䔖䔗䔘䔙䔚䔛䔜䔝䔞䔟䔠䔡","䔢䔣䔤䔥䔦䔧䔨䔩䔪䔫䔬䔭䔮䔯䔰䔱䔲䔳䔴䔵䔶䔷䔸䔹䔺䔻䔼䔽䔾䔿䕀䕁䕂䕃䕄䕅䕆䕇䕈䕉䕊䕋䕌䕍䕎䕏䕐䕑䕒䕓䕔䕕䕖䕗䕘䕙䕚䕛䕜䕝䕞䕟䕠䕡䕢䕣䕤䕥䕦䕧䕨䕩䕪䕫䕬䕭䕮䕯䕰䕱䕲䕳䕴䕵䕶䕷䕸䕹䕺䕻䕼䕽䕾䕿䖀䖁䖂䖃䖄䖅䖆䖇䖈䖉䖊䖋䖌䖍䖎䖏䖐䖑䖒䖓䖔䖕䖖䖗䖘䖙䖚䖛䖜䖝䖞䖟䖠䖡䖢䖣䖤䖥䖦䖧䖨䖩䖪䖫䖬䖭䖮䖯䖰䖱䖲䖳䖴䖵䖶䖷䖸䖹䖺䖻䖼䖽䖾䖿䗀䗁䗂䗃䗄䗅䗆䗇䗈䗉䗊䗋䗌䗍䗎䗏䗐䗑䗒䗓䗔䗕䗖䗗䗘䗙䗚䗛䗜䗝䗞䗟䗠䗡䗢䗣䗤䗥䗦䗧䗨䗩䗪䗫䗬䗭䗮䗯䗰䗱䗲䗳䗴䗵䗶䗷䗸䗹䗺䗻䗼䗽䗾䗿䘀䘁䘂䘃䘄䘅䘆䘇䘈䘉䘊䘋䘌䘍䘎䘏䘐䘑䘒䘓䘔䘕䘖䘗䘘䘙䘚䘛䘜䘝䘞䘟䘠䘡","䘢䘣䘤䘥䘦䘧䘨䘩䘪䘫䘬䘭䘮䘯䘰䘱䘲䘳䘴䘵䘶䘷䘸䘹䘺䘻䘼䘽䘾䘿䙀䙁䙂䙃䙄䙅䙆䙇䙈䙉䙊䙋䙌䙍䙎䙏䙐䙑䙒䙓䙔䙕䙖䙗䙘䙙䙚䙛䙜䙝䙞䙟䙠䙡䙢䙣䙤䙥䙦䙧䙨䙩䙪䙫䙬䙭䙮䙯䙰䙱䙲䙳䙴䙵䙶䙷䙸䙹䙺䙻䙼䙽䙾䙿䚀䚁䚂䚃䚄䚅䚆䚇䚈䚉䚊䚋䚌䚍䚎䚏䚐䚑䚒䚓䚔䚕䚖䚗䚘䚙䚚䚛䚜䚝䚞䚟䚠䚡䚢䚣䚤䚥䚦䚧䚨䚩䚪䚫䚬䚭䚮䚯䚰䚱䚲䚳䚴䚵䚶䚷䚸䚹䚺䚻䚼䚽䚾䚿䛀䛁䛂䛃䛄䛅䛆䛇䛈䛉䛊䛋䛌䛍䛎䛏䛐䛑䛒䛓䛔䛕䛖䛗䛘䛙䛚䛛䛜䛝䛞䛟䛠䛡䛢䛣䛤䛥䛦䛧䛨䛩䛪䛫䛬䛭䛮䛯䛰䛱䛲䛳䛴䛵䛶䛷䛸䛹䛺䛻䛼䛽䛾䛿䜀䜁䜂䜃䜄䜅䜆䜇䜈䜉䜊䜋䜌䜍䜎䜏䜐䜑䜒䜓䜔䜕䜖䜗䜘䜙䜚䜛䜜䜝䜞䜟䜠䜡","䜢䜣䜤䜥䜦䜧䜨䜩䜪䜫䜬䜭䜮䜯䜰䜱䜲䜳䜴䜵䜶䜷䜸䜹䜺䜻䜼䜽䜾䜿䝀䝁䝂䝃䝄䝅䝆䝇䝈䝉䝊䝋䝌䝍䝎䝏䝐䝑䝒䝓䝔䝕䝖䝗䝘䝙䝚䝛䝜䝝䝞䝟䝠䝡䝢䝣䝤䝥䝦䝧䝨䝩䝪䝫䝬䝭䝮䝯䝰䝱䝲䝳䝴䝵䝶䝷䝸䝹䝺䝻䝼䝽䝾䝿䞀䞁䞂䞃䞄䞅䞆䞇䞈䞉䞊䞋䞌䞍䞎䞏䞐䞑䞒䞓䞔䞕䞖䞗䞘䞙䞚䞛䞜䞝䞞䞟䞠䞡䞢䞣䞤䞥䞦䞧䞨䞩䞪䞫䞬䞭䞮䞯䞰䞱䞲䞳䞴䞵䞶䞷䞸䞹䞺䞻䞼䞽䞾䞿䟀䟁䟂䟃䟄䟅䟆䟇䟈䟉䟊䟋䟌䟍䟎䟏䟐䟑䟒䟓䟔䟕䟖䟗䟘䟙䟚䟛䟜䟝䟞䟟䟠䟡䟢䟣䟤䟥䟦䟧䟨䟩䟪䟫䟬䟭䟮䟯䟰䟱䟲䟳䟴䟵䟶䟷䟸䟹䟺䟻䟼䟽䟾䟿䠀䠁䠂䠃䠄䠅䠆䠇䠈䠉䠊䠋䠌䠍䠎䠏䠐䠑䠒䠓䠔䠕䠖䠗䠘䠙䠚䠛䠜䠝䠞䠟䠠䠡","䠢䠣䠤䠥䠦䠧䠨䠩䠪䠫䠬䠭䠮䠯䠰䠱䠲䠳䠴䠵䠶䠷䠸䠹䠺䠻䠼䠽䠾䠿䡀䡁䡂䡃䡄䡅䡆䡇䡈䡉䡊䡋䡌䡍䡎䡏䡐䡑䡒䡓䡔䡕䡖䡗䡘䡙䡚䡛䡜䡝䡞䡟䡠䡡䡢䡣䡤䡥䡦䡧䡨䡩䡪䡫䡬䡭䡮䡯䡰䡱䡲䡳䡴䡵䡶䡷䡸䡹䡺䡻䡼䡽䡾䡿䢀䢁䢂䢃䢄䢅䢆䢇䢈䢉䢊䢋䢌䢍䢎䢏䢐䢑䢒䢓䢔䢕䢖䢗䢘䢙䢚䢛䢜䢝䢞䢟䢠䢡䢢䢣䢤䢥䢦䢧䢨䢩䢪䢫䢬䢭䢮䢯䢰䢱䢲䢳䢴䢵䢶䢷䢸䢹䢺䢻䢼䢽䢾䢿䣀䣁䣂䣃䣄䣅䣆䣇䣈䣉䣊䣋䣌䣍䣎䣏䣐䣑䣒䣓䣔䣕䣖䣗䣘䣙䣚䣛䣜䣝䣞䣟䣠䣡䣢䣣䣤䣥䣦䣧䣨䣩䣪䣫䣬䣭䣮䣯䣰䣱䣲䣳䣴䣵䣶䣷䣸䣹䣺䣻䣼䣽䣾䣿䤀䤁䤂䤃䤄䤅䤆䤇䤈䤉䤊䤋䤌䤍䤎䤏䤐䤑䤒䤓䤔䤕䤖䤗䤘䤙䤚䤛䤜䤝䤞䤟䤠䤡","䤢䤣䤤䤥䤦䤧䤨䤩䤪䤫䤬䤭䤮䤯䤰䤱䤲䤳䤴䤵䤶䤷䤸䤹䤺䤻䤼䤽䤾䤿䥀䥁䥂䥃䥄䥅䥆䥇䥈䥉䥊䥋䥌䥍䥎䥏䥐䥑䥒䥓䥔䥕䥖䥗䥘䥙䥚䥛䥜䥝䥞䥟䥠䥡䥢䥣䥤䥥䥦䥧䥨䥩䥪䥫䥬䥭䥮䥯䥰䥱䥲䥳䥴䥵䥶䥷䥸䥹䥺䥻䥼䥽䥾䥿䦀䦁䦂䦃䦄䦅䦆䦇䦈䦉䦊䦋䦌䦍䦎䦏䦐䦑䦒䦓䦔䦕䦖䦗䦘䦙䦚䦛䦜䦝䦞䦟䦠䦡䦢䦣䦤䦥䦦䦧䦨䦩䦪䦫䦬䦭䦮䦯䦰䦱䦲䦳䦴䦵䦶䦷䦸䦹䦺䦻䦼䦽䦾䦿䧀䧁䧂䧃䧄䧅䧆䧇䧈䧉䧊䧋䧌䧍䧎䧏䧐䧑䧒䧓䧔䧕䧖䧗䧘䧙䧚䧛䧜䧝䧞䧟䧠䧡䧢䧣䧤䧥䧦䧧䧨䧩䧪䧫䧬䧭䧮䧯䧰䧱䧲䧳䧴䧵䧶䧷䧸䧹䧺䧻䧼䧽䧾䧿䨀䨁䨂䨃䨄䨅䨆䨇䨈䨉䨊䨋䨌䨍䨎䨏䨐䨑䨒䨓䨔䨕䨖䨗䨘䨙䨚䨛䨜䨝䨞䨟䨠䨡","䨢䨣䨤䨥䨦䨧䨨䨩䨪䨫䨬䨭䨮䨯䨰䨱䨲䨳䨴䨵䨶䨷䨸䨹䨺䨻䨼䨽䨾䨿䩀䩁䩂䩃䩄䩅䩆䩇䩈䩉䩊䩋䩌䩍䩎䩏䩐䩑䩒䩓䩔䩕䩖䩗䩘䩙䩚䩛䩜䩝䩞䩟䩠䩡䩢䩣䩤䩥䩦䩧䩨䩩䩪䩫䩬䩭䩮䩯䩰䩱䩲䩳䩴䩵䩶䩷䩸䩹䩺䩻䩼䩽䩾䩿䪀䪁䪂䪃䪄䪅䪆䪇䪈䪉䪊䪋䪌䪍䪎䪏䪐䪑䪒䪓䪔䪕䪖䪗䪘䪙䪚䪛䪜䪝䪞䪟䪠䪡䪢䪣䪤䪥䪦䪧䪨䪩䪪䪫䪬䪭䪮䪯䪰䪱䪲䪳䪴䪵䪶䪷䪸䪹䪺䪻䪼䪽䪾䪿䫀䫁䫂䫃䫄䫅䫆䫇䫈䫉䫊䫋䫌䫍䫎䫏䫐䫑䫒䫓䫔䫕䫖䫗䫘䫙䫚䫛䫜䫝䫞䫟䫠䫡䫢䫣䫤䫥䫦䫧䫨䫩䫪䫫䫬䫭䫮䫯䫰䫱䫲䫳䫴䫵䫶䫷䫸䫹䫺䫻䫼䫽䫾䫿䬀䬁䬂䬃䬄䬅䬆䬇䬈䬉䬊䬋䬌䬍䬎䬏䬐䬑䬒䬓䬔䬕䬖䬗䬘䬙䬚䬛䬜䬝䬞䬟䬠䬡","䬢䬣䬤䬥䬦䬧䬨䬩䬪䬫䬬䬭䬮䬯䬰䬱䬲䬳䬴䬵䬶䬷䬸䬹䬺䬻䬼䬽䬾䬿䭀䭁䭂䭃䭄䭅䭆䭇䭈䭉䭊䭋䭌䭍䭎䭏䭐䭑䭒䭓䭔䭕䭖䭗䭘䭙䭚䭛䭜䭝䭞䭟䭠䭡䭢䭣䭤䭥䭦䭧䭨䭩䭪䭫䭬䭭䭮䭯䭰䭱䭲䭳䭴䭵䭶䭷䭸䭹䭺䭻䭼䭽䭾䭿䮀䮁䮂䮃䮄䮅䮆䮇䮈䮉䮊䮋䮌䮍䮎䮏䮐䮑䮒䮓䮔䮕䮖䮗䮘䮙䮚䮛䮜䮝䮞䮟䮠䮡䮢䮣䮤䮥䮦䮧䮨䮩䮪䮫䮬䮭䮮䮯䮰䮱䮲䮳䮴䮵䮶䮷䮸䮹䮺䮻䮼䮽䮾䮿䯀䯁䯂䯃䯄䯅䯆䯇䯈䯉䯊䯋䯌䯍䯎䯏䯐䯑䯒䯓䯔䯕䯖䯗䯘䯙䯚䯛䯜䯝䯞䯟䯠䯡䯢䯣䯤䯥䯦䯧䯨䯩䯪䯫䯬䯭䯮䯯䯰䯱䯲䯳䯴䯵䯶䯷䯸䯹䯺䯻䯼䯽䯾䯿䰀䰁䰂䰃䰄䰅䰆䰇䰈䰉䰊䰋䰌䰍䰎䰏䰐䰑䰒䰓䰔䰕䰖䰗䰘䰙䰚䰛䰜䰝䰞䰟䰠䰡","䰢䰣䰤䰥䰦䰧䰨䰩䰪䰫䰬䰭䰮䰯䰰䰱䰲䰳䰴䰵䰶䰷䰸䰹䰺䰻䰼䰽䰾䰿䱀䱁䱂䱃䱄䱅䱆䱇䱈䱉䱊䱋䱌䱍䱎䱏䱐䱑䱒䱓䱔䱕䱖䱗䱘䱙䱚䱛䱜䱝䱞䱟䱠䱡䱢䱣䱤䱥䱦䱧䱨䱩䱪䱫䱬䱭䱮䱯䱰䱱䱲䱳䱴䱵䱶䱷䱸䱹䱺䱻䱼䱽䱾䱿䲀䲁䲂䲃䲄䲅䲆䲇䲈䲉䲊䲋䲌䲍䲎䲏䲐䲑䲒䲓䲔䲕䲖䲗䲘䲙䲚䲛䲜䲝䲞䲟䲠䲡䲢䲣䲤䲥䲦䲧䲨䲩䲪䲫䲬䲭䲮䲯䲰䲱䲲䲳䲴䲵䲶䲷䲸䲹䲺䲻䲼䲽䲾䲿䳀䳁䳂䳃䳄䳅䳆䳇䳈䳉䳊䳋䳌䳍䳎䳏䳐䳑䳒䳓䳔䳕䳖䳗䳘䳙䳚䳛䳜䳝䳞䳟䳠䳡䳢䳣䳤䳥䳦䳧䳨䳩䳪䳫䳬䳭䳮䳯䳰䳱䳲䳳䳴䳵䳶䳷䳸䳹䳺䳻䳼䳽䳾䳿䴀䴁䴂䴃䴄䴅䴆䴇䴈䴉䴊䴋䴌䴍䴎䴏䴐䴑䴒䴓䴔䴕䴖䴗䴘䴙䴚䴛䴜䴝䴞䴟䴠䴡","䴢䴣䴤䴥䴦䴧䴨䴩䴪䴫䴬䴭䴮䴯䴰䴱䴲䴳䴴䴵䴶䴷䴸䴹䴺䴻䴼䴽䴾䴿䵀䵁䵂䵃䵄䵅䵆䵇䵈䵉䵊䵋䵌䵍䵎䵏䵐䵑䵒䵓䵔䵕䵖䵗䵘䵙䵚䵛䵜䵝䵞䵟䵠䵡䵢䵣䵤䵥䵦䵧䵨䵩䵪䵫䵬䵭䵮䵯䵰䵱䵲䵳䵴䵵䵶䵷䵸䵹䵺䵻䵼䵽䵾䵿䶀䶁䶂䶃䶄䶅䶆䶇䶈䶉䶊䶋䶌䶍䶎䶏䶐䶑䶒䶓䶔䶕䶖䶗䶘䶙䶚䶛䶜䶝䶞䶟䶠䶡䶢䶣䶤䶥䶦䶧䶨䶩䶪䶫䶬䶭䶮䶯䶰䶱䶲䶳䶴䶵䶶䶷䶸䶹䶺䶻䶼䶽䶾䶿䷀䷁䷂䷃䷄䷅䷆䷇䷈䷉䷊䷋䷌䷍䷎䷏䷐䷑䷒䷓䷔䷕䷖䷗䷘䷙䷚䷛䷜䷝䷞䷟䷠䷡䷢䷣䷤䷥䷦䷧䷨䷩䷪䷫䷬䷭䷮䷯䷰䷱䷲䷳䷴䷵䷶䷷䷸䷹䷺䷻䷼䷽䷾䷿一丁丂七丄丅丆万丈三上下丌不与丏丐丑丒专且丕世丗丘丙业丛东丝丞丟丠両","丢丣两严並丧丨丩个丫丬中丮丯丰丱串丳临丵丶丷丸丹为主丼丽举丿乀乁乂乃乄久乆乇么义乊之乌乍乎乏乐乑乒乓乔乕乖乗乘乙乚乛乜九乞也习乡乢乣乤乥书乧乨乩乪乫乬乭乮乯买乱乲乳乴乵乶乷乸乹乺乻乼乽乾乿亀亁亂亃亄亅了亇予争亊事二亍于亏亐云互亓五井亖亗亘亙亚些亜亝亞亟亠亡亢亣交亥亦产亨亩亪享京亭亮亯亰亱亲亳亴亵亶亷亸亹人亻亼亽亾亿什仁仂仃仄仅仆仇仈仉今介仌仍从仏仐仑仒仓仔仕他仗付仙仚仛仜仝仞仟仠仡仢代令以仦仧仨仩仪仫们仭仮仯仰仱仲仳仴仵件价仸仹仺任仼份仾仿伀企伂伃伄伅伆伇伈伉伊伋伌伍伎伏伐休伒伓伔伕伖众优伙会伛伜伝伞伟传伡","伢伣伤伥伦伧伨伩伪伫伬伭伮伯估伱伲伳伴伵伶伷伸伹伺伻似伽伾伿佀佁佂佃佄佅但佇佈佉佊佋佌位低住佐佑佒体佔何佖佗佘余佚佛作佝佞佟你佡佢佣佤佥佦佧佨佩佪佫佬佭佮佯佰佱佲佳佴併佶佷佸佹佺佻佼佽佾使侀侁侂侃侄侅來侇侈侉侊例侌侍侎侏侐侑侒侓侔侕侖侗侘侙侚供侜依侞侟侠価侢侣侤侥侦侧侨侩侪侫侬侭侮侯侰侱侲侳侴侵侶侷侸侹侺侻侼侽侾便俀俁係促俄俅俆俇俈俉俊俋俌俍俎俏俐俑俒俓俔俕俖俗俘俙俚俛俜保俞俟俠信俢俣俤俥俦俧俨俩俪俫俬俭修俯俰俱俲俳俴俵俶俷俸俹俺俻俼俽俾俿倀倁倂倃倄倅倆倇倈倉倊個倌倍倎倏倐們倒倓倔倕倖倗倘候倚倛倜倝倞借倠倡","倢倣値倥倦倧倨倩倪倫倬倭倮倯倰倱倲倳倴倵倶倷倸倹债倻值倽倾倿偀偁偂偃偄偅偆假偈偉偊偋偌偍偎偏偐偑偒偓偔偕偖偗偘偙做偛停偝偞偟偠偡偢偣偤健偦偧偨偩偪偫偬偭偮偯偰偱偲偳側偵偶偷偸偹偺偻偼偽偾偿傀傁傂傃傄傅傆傇傈傉傊傋傌傍傎傏傐傑傒傓傔傕傖傗傘備傚傛傜傝傞傟傠傡傢傣傤傥傦傧储傩傪傫催傭傮傯傰傱傲傳傴債傶傷傸傹傺傻傼傽傾傿僀僁僂僃僄僅僆僇僈僉僊僋僌働僎像僐僑僒僓僔僕僖僗僘僙僚僛僜僝僞僟僠僡僢僣僤僥僦僧僨僩僪僫僬僭僮僯僰僱僲僳僴僵僶僷僸價僺僻僼僽僾僿儀儁儂儃億儅儆儇儈儉儊儋儌儍儎儏儐儑儒儓儔儕儖儗儘儙儚儛儜儝儞償儠儡","儢儣儤儥儦儧儨儩優儫儬儭儮儯儰儱儲儳儴儵儶儷儸儹儺儻儼儽儾儿兀允兂元兄充兆兇先光兊克兌免兎兏児兑兒兓兔兕兖兗兘兙党兛兜兝兞兟兠兡兢兣兤入兦內全兩兪八公六兮兯兰共兲关兴兵其具典兹兺养兼兽兾兿冀冁冂冃冄内円冇冈冉冊冋册再冎冏冐冑冒冓冔冕冖冗冘写冚军农冝冞冟冠冡冢冣冤冥冦冧冨冩冪冫冬冭冮冯冰冱冲决冴况冶冷冸冹冺冻冼冽冾冿净凁凂凃凄凅准凇凈凉凊凋凌凍凎减凐凑凒凓凔凕凖凗凘凙凚凛凜凝凞凟几凡凢凣凤凥処凧凨凩凪凫凬凭凮凯凰凱凲凳凴凵凶凷凸凹出击凼函凾凿刀刁刂刃刄刅分切刈刉刊刋刌刍刎刏刐刑划刓刔刕刖列刘则刚创刜初刞刟删刡","刢刣判別刦刧刨利刪别刬刭刮刯到刱刲刳刴刵制刷券刹刺刻刼刽刾刿剀剁剂剃剄剅剆則剈剉削剋剌前剎剏剐剑剒剓剔剕剖剗剘剙剚剛剜剝剞剟剠剡剢剣剤剥剦剧剨剩剪剫剬剭剮副剰剱割剳剴創剶剷剸剹剺剻剼剽剾剿劀劁劂劃劄劅劆劇劈劉劊劋劌劍劎劏劐劑劒劓劔劕劖劗劘劙劚力劜劝办功加务劢劣劤劥劦劧动助努劫劬劭劮劯劰励劲劳労劵劶劷劸効劺劻劼劽劾势勀勁勂勃勄勅勆勇勈勉勊勋勌勍勎勏勐勑勒勓勔動勖勗勘務勚勛勜勝勞募勠勡勢勣勤勥勦勧勨勩勪勫勬勭勮勯勰勱勲勳勴勵勶勷勸勹勺勻勼勽勾勿匀匁匂匃匄包匆匇匈匉匊匋匌匍匎匏匐匑匒匓匔匕化北匘匙匚匛匜匝匞匟匠匡","匢匣匤匥匦匧匨匩匪匫匬匭匮匯匰匱匲匳匴匵匶匷匸匹区医匼匽匾匿區十卂千卄卅卆升午卉半卋卌卍华协卐卑卒卓協单卖南単卙博卛卜卝卞卟占卡卢卣卤卥卦卧卨卩卪卫卬卭卮卯印危卲即却卵卶卷卸卹卺卻卼卽卾卿厀厁厂厃厄厅历厇厈厉厊压厌厍厎厏厐厑厒厓厔厕厖厗厘厙厚厛厜厝厞原厠厡厢厣厤厥厦厧厨厩厪厫厬厭厮厯厰厱厲厳厴厵厶厷厸厹厺去厼厽厾县叀叁参參叄叅叆叇又叉及友双反収叏叐发叒叓叔叕取受变叙叚叛叜叝叞叟叠叡叢口古句另叧叨叩只叫召叭叮可台叱史右叴叵叶号司叹叺叻叼叽叾叿吀吁吂吃各吅吆吇合吉吊吋同名后吏吐向吒吓吔吕吖吗吘吙吚君吜吝吞吟吠吡","吢吣吤吥否吧吨吩吪含听吭吮启吰吱吲吳吴吵吶吷吸吹吺吻吼吽吾吿呀呁呂呃呄呅呆呇呈呉告呋呌呍呎呏呐呑呒呓呔呕呖呗员呙呚呛呜呝呞呟呠呡呢呣呤呥呦呧周呩呪呫呬呭呮呯呰呱呲味呴呵呶呷呸呹呺呻呼命呾呿咀咁咂咃咄咅咆咇咈咉咊咋和咍咎咏咐咑咒咓咔咕咖咗咘咙咚咛咜咝咞咟咠咡咢咣咤咥咦咧咨咩咪咫咬咭咮咯咰咱咲咳咴咵咶咷咸咹咺咻咼咽咾咿哀品哂哃哄哅哆哇哈哉哊哋哌响哎哏哐哑哒哓哔哕哖哗哘哙哚哛哜哝哞哟哠員哢哣哤哥哦哧哨哩哪哫哬哭哮哯哰哱哲哳哴哵哶哷哸哹哺哻哼哽哾哿唀唁唂唃唄唅唆唇唈唉唊唋唌唍唎唏唐唑唒唓唔唕唖唗唘唙唚唛唜唝唞唟唠唡","唢唣唤唥唦唧唨唩唪唫唬唭售唯唰唱唲唳唴唵唶唷唸唹唺唻唼唽唾唿啀啁啂啃啄啅商啇啈啉啊啋啌啍啎問啐啑啒啓啔啕啖啗啘啙啚啛啜啝啞啟啠啡啢啣啤啥啦啧啨啩啪啫啬啭啮啯啰啱啲啳啴啵啶啷啸啹啺啻啼啽啾啿喀喁喂喃善喅喆喇喈喉喊喋喌喍喎喏喐喑喒喓喔喕喖喗喘喙喚喛喜喝喞喟喠喡喢喣喤喥喦喧喨喩喪喫喬喭單喯喰喱喲喳喴喵営喷喸喹喺喻喼喽喾喿嗀嗁嗂嗃嗄嗅嗆嗇嗈嗉嗊嗋嗌嗍嗎嗏嗐嗑嗒嗓嗔嗕嗖嗗嗘嗙嗚嗛嗜嗝嗞嗟嗠嗡嗢嗣嗤嗥嗦嗧嗨嗩嗪嗫嗬嗭嗮嗯嗰嗱嗲嗳嗴嗵嗶嗷嗸嗹嗺嗻嗼嗽嗾嗿嘀嘁嘂嘃嘄嘅嘆嘇嘈嘉嘊嘋嘌嘍嘎嘏嘐嘑嘒嘓嘔嘕嘖嘗嘘嘙嘚嘛嘜嘝嘞嘟嘠嘡","嘢嘣嘤嘥嘦嘧嘨嘩嘪嘫嘬嘭嘮嘯嘰嘱嘲嘳嘴嘵嘶嘷嘸嘹嘺嘻嘼嘽嘾嘿噀噁噂噃噄噅噆噇噈噉噊噋噌噍噎噏噐噑噒噓噔噕噖噗噘噙噚噛噜噝噞噟噠噡噢噣噤噥噦噧器噩噪噫噬噭噮噯噰噱噲噳噴噵噶噷噸噹噺噻噼噽噾噿嚀嚁嚂嚃嚄嚅嚆嚇嚈嚉嚊嚋嚌嚍嚎嚏嚐嚑嚒嚓嚔嚕嚖嚗嚘嚙嚚嚛嚜嚝嚞嚟嚠嚡嚢嚣嚤嚥嚦嚧嚨嚩嚪嚫嚬嚭嚮嚯嚰嚱嚲嚳嚴嚵嚶嚷嚸嚹嚺嚻嚼嚽嚾嚿囀囁囂囃囄囅囆囇囈囉囊囋囌囍囎囏囐囑囒囓囔囕囖囗囘囙囚四囜囝回囟因囡团団囤囥囦囧囨囩囪囫囬园囮囯困囱囲図围囵囶囷囸囹固囻囼国图囿圀圁圂圃圄圅圆圇圈圉圊國圌圍圎圏圐圑園圓圔圕圖圗團圙圚圛圜圝圞土圠圡","圢圣圤圥圦圧在圩圪圫圬圭圮圯地圱圲圳圴圵圶圷圸圹场圻圼圽圾圿址坁坂坃坄坅坆均坈坉坊坋坌坍坎坏坐坑坒坓坔坕坖块坘坙坚坛坜坝坞坟坠坡坢坣坤坥坦坧坨坩坪坫坬坭坮坯坰坱坲坳坴坵坶坷坸坹坺坻坼坽坾坿垀垁垂垃垄垅垆垇垈垉垊型垌垍垎垏垐垑垒垓垔垕垖垗垘垙垚垛垜垝垞垟垠垡垢垣垤垥垦垧垨垩垪垫垬垭垮垯垰垱垲垳垴垵垶垷垸垹垺垻垼垽垾垿埀埁埂埃埄埅埆埇埈埉埊埋埌埍城埏埐埑埒埓埔埕埖埗埘埙埚埛埜埝埞域埠埡埢埣埤埥埦埧埨埩埪埫埬埭埮埯埰埱埲埳埴埵埶執埸培基埻埼埽埾埿堀堁堂堃堄堅堆堇堈堉堊堋堌堍堎堏堐堑堒堓堔堕堖堗堘堙堚堛堜堝堞堟堠堡","堢堣堤堥堦堧堨堩堪堫堬堭堮堯堰報堲堳場堵堶堷堸堹堺堻堼堽堾堿塀塁塂塃塄塅塆塇塈塉塊塋塌塍塎塏塐塑塒塓塔塕塖塗塘塙塚塛塜塝塞塟塠塡塢塣塤塥塦塧塨塩塪填塬塭塮塯塰塱塲塳塴塵塶塷塸塹塺塻塼塽塾塿墀墁墂境墄墅墆墇墈墉墊墋墌墍墎墏墐墑墒墓墔墕墖増墘墙墚墛墜墝增墟墠墡墢墣墤墥墦墧墨墩墪墫墬墭墮墯墰墱墲墳墴墵墶墷墸墹墺墻墼墽墾墿壀壁壂壃壄壅壆壇壈壉壊壋壌壍壎壏壐壑壒壓壔壕壖壗壘壙壚壛壜壝壞壟壠壡壢壣壤壥壦壧壨壩壪士壬壭壮壯声壱売壳壴壵壶壷壸壹壺壻壼壽壾壿夀夁夂夃处夅夆备夈変夊夋夌复夎夏夐夑夒夓夔夕外夗夘夙多夛夜夝夞够夠夡","夢夣夤夥夦大夨天太夫夬夭央夯夰失夲夳头夵夶夷夸夹夺夻夼夽夾夿奀奁奂奃奄奅奆奇奈奉奊奋奌奍奎奏奐契奒奓奔奕奖套奘奙奚奛奜奝奞奟奠奡奢奣奤奥奦奧奨奩奪奫奬奭奮奯奰奱奲女奴奵奶奷奸她奺奻奼好奾奿妀妁如妃妄妅妆妇妈妉妊妋妌妍妎妏妐妑妒妓妔妕妖妗妘妙妚妛妜妝妞妟妠妡妢妣妤妥妦妧妨妩妪妫妬妭妮妯妰妱妲妳妴妵妶妷妸妹妺妻妼妽妾妿姀姁姂姃姄姅姆姇姈姉姊始姌姍姎姏姐姑姒姓委姕姖姗姘姙姚姛姜姝姞姟姠姡姢姣姤姥姦姧姨姩姪姫姬姭姮姯姰姱姲姳姴姵姶姷姸姹姺姻姼姽姾姿娀威娂娃娄娅娆娇娈娉娊娋娌娍娎娏娐娑娒娓娔娕娖娗娘娙娚娛娜娝娞娟娠娡","娢娣娤娥娦娧娨娩娪娫娬娭娮娯娰娱娲娳娴娵娶娷娸娹娺娻娼娽娾娿婀婁婂婃婄婅婆婇婈婉婊婋婌婍婎婏婐婑婒婓婔婕婖婗婘婙婚婛婜婝婞婟婠婡婢婣婤婥婦婧婨婩婪婫婬婭婮婯婰婱婲婳婴婵婶婷婸婹婺婻婼婽婾婿媀媁媂媃媄媅媆媇媈媉媊媋媌媍媎媏媐媑媒媓媔媕媖媗媘媙媚媛媜媝媞媟媠媡媢媣媤媥媦媧媨媩媪媫媬媭媮媯媰媱媲媳媴媵媶媷媸媹媺媻媼媽媾媿嫀嫁嫂嫃嫄嫅嫆嫇嫈嫉嫊嫋嫌嫍嫎嫏嫐嫑嫒嫓嫔嫕嫖嫗嫘嫙嫚嫛嫜嫝嫞嫟嫠嫡嫢嫣嫤嫥嫦嫧嫨嫩嫪嫫嫬嫭嫮嫯嫰嫱嫲嫳嫴嫵嫶嫷嫸嫹嫺嫻嫼嫽嫾嫿嬀嬁嬂嬃嬄嬅嬆嬇嬈嬉嬊嬋嬌嬍嬎嬏嬐嬑嬒嬓嬔嬕嬖嬗嬘嬙嬚嬛嬜嬝嬞嬟嬠嬡","嬢嬣嬤嬥嬦嬧嬨嬩嬪嬫嬬嬭嬮嬯嬰嬱嬲嬳嬴嬵嬶嬷嬸嬹嬺嬻嬼嬽嬾嬿孀孁孂孃孄孅孆孇孈孉孊孋孌孍孎孏子孑孒孓孔孕孖字存孙孚孛孜孝孞孟孠孡孢季孤孥学孧孨孩孪孫孬孭孮孯孰孱孲孳孴孵孶孷學孹孺孻孼孽孾孿宀宁宂它宄宅宆宇守安宊宋完宍宎宏宐宑宒宓宔宕宖宗官宙定宛宜宝实実宠审客宣室宥宦宧宨宩宪宫宬宭宮宯宰宱宲害宴宵家宷宸容宺宻宼宽宾宿寀寁寂寃寄寅密寇寈寉寊寋富寍寎寏寐寑寒寓寔寕寖寗寘寙寚寛寜寝寞察寠寡寢寣寤寥實寧寨審寪寫寬寭寮寯寰寱寲寳寴寵寶寷寸对寺寻导寽対寿尀封専尃射尅将將專尉尊尋尌對導小尐少尒尓尔尕尖尗尘尙尚尛尜尝尞尟尠尡","尢尣尤尥尦尧尨尩尪尫尬尭尮尯尰就尲尳尴尵尶尷尸尹尺尻尼尽尾尿局屁层屃屄居屆屇屈屉届屋屌屍屎屏屐屑屒屓屔展屖屗屘屙屚屛屜屝属屟屠屡屢屣層履屦屧屨屩屪屫屬屭屮屯屰山屲屳屴屵屶屷屸屹屺屻屼屽屾屿岀岁岂岃岄岅岆岇岈岉岊岋岌岍岎岏岐岑岒岓岔岕岖岗岘岙岚岛岜岝岞岟岠岡岢岣岤岥岦岧岨岩岪岫岬岭岮岯岰岱岲岳岴岵岶岷岸岹岺岻岼岽岾岿峀峁峂峃峄峅峆峇峈峉峊峋峌峍峎峏峐峑峒峓峔峕峖峗峘峙峚峛峜峝峞峟峠峡峢峣峤峥峦峧峨峩峪峫峬峭峮峯峰峱峲峳峴峵島峷峸峹峺峻峼峽峾峿崀崁崂崃崄崅崆崇崈崉崊崋崌崍崎崏崐崑崒崓崔崕崖崗崘崙崚崛崜崝崞崟崠崡","崢崣崤崥崦崧崨崩崪崫崬崭崮崯崰崱崲崳崴崵崶崷崸崹崺崻崼崽崾崿嵀嵁嵂嵃嵄嵅嵆嵇嵈嵉嵊嵋嵌嵍嵎嵏嵐嵑嵒嵓嵔嵕嵖嵗嵘嵙嵚嵛嵜嵝嵞嵟嵠嵡嵢嵣嵤嵥嵦嵧嵨嵩嵪嵫嵬嵭嵮嵯嵰嵱嵲嵳嵴嵵嵶嵷嵸嵹嵺嵻嵼嵽嵾嵿嶀嶁嶂嶃嶄嶅嶆嶇嶈嶉嶊嶋嶌嶍嶎嶏嶐嶑嶒嶓嶔嶕嶖嶗嶘嶙嶚嶛嶜嶝嶞嶟嶠嶡嶢嶣嶤嶥嶦嶧嶨嶩嶪嶫嶬嶭嶮嶯嶰嶱嶲嶳嶴嶵嶶嶷嶸嶹嶺嶻嶼嶽嶾嶿巀巁巂巃巄巅巆巇巈巉巊巋巌巍巎巏巐巑巒巓巔巕巖巗巘巙巚巛巜川州巟巠巡巢巣巤工左巧巨巩巪巫巬巭差巯巰己已巳巴巵巶巷巸巹巺巻巼巽巾巿帀币市布帄帅帆帇师帉帊帋希帍帎帏帐帑帒帓帔帕帖帗帘帙帚帛帜帝帞帟帠帡","帢帣帤帥带帧帨帩帪師帬席帮帯帰帱帲帳帴帵帶帷常帹帺帻帼帽帾帿幀幁幂幃幄幅幆幇幈幉幊幋幌幍幎幏幐幑幒幓幔幕幖幗幘幙幚幛幜幝幞幟幠幡幢幣幤幥幦幧幨幩幪幫幬幭幮幯幰幱干平年幵并幷幸幹幺幻幼幽幾广庀庁庂広庄庅庆庇庈庉床庋庌庍庎序庐庑庒库应底庖店庘庙庚庛府庝庞废庠庡庢庣庤庥度座庨庩庪庫庬庭庮庯庰庱庲庳庴庵庶康庸庹庺庻庼庽庾庿廀廁廂廃廄廅廆廇廈廉廊廋廌廍廎廏廐廑廒廓廔廕廖廗廘廙廚廛廜廝廞廟廠廡廢廣廤廥廦廧廨廩廪廫廬廭廮廯廰廱廲廳廴廵延廷廸廹建廻廼廽廾廿开弁异弃弄弅弆弇弈弉弊弋弌弍弎式弐弑弒弓弔引弖弗弘弙弚弛弜弝弞弟张弡","弢弣弤弥弦弧弨弩弪弫弬弭弮弯弰弱弲弳弴張弶強弸弹强弻弼弽弾弿彀彁彂彃彄彅彆彇彈彉彊彋彌彍彎彏彐彑归当彔录彖彗彘彙彚彛彜彝彞彟彠彡形彣彤彥彦彧彨彩彪彫彬彭彮彯彰影彲彳彴彵彶彷彸役彺彻彼彽彾彿往征徂徃径待徆徇很徉徊律後徍徎徏徐徑徒従徔徕徖得徘徙徚徛徜徝從徟徠御徢徣徤徥徦徧徨復循徫徬徭微徯徰徱徲徳徴徵徶德徸徹徺徻徼徽徾徿忀忁忂心忄必忆忇忈忉忊忋忌忍忎忏忐忑忒忓忔忕忖志忘忙忚忛応忝忞忟忠忡忢忣忤忥忦忧忨忩忪快忬忭忮忯忰忱忲忳忴念忶忷忸忹忺忻忼忽忾忿怀态怂怃怄怅怆怇怈怉怊怋怌怍怎怏怐怑怒怓怔怕怖怗怘怙怚怛怜思怞怟怠怡","怢怣怤急怦性怨怩怪怫怬怭怮怯怰怱怲怳怴怵怶怷怸怹怺总怼怽怾怿恀恁恂恃恄恅恆恇恈恉恊恋恌恍恎恏恐恑恒恓恔恕恖恗恘恙恚恛恜恝恞恟恠恡恢恣恤恥恦恧恨恩恪恫恬恭恮息恰恱恲恳恴恵恶恷恸恹恺恻恼恽恾恿悀悁悂悃悄悅悆悇悈悉悊悋悌悍悎悏悐悑悒悓悔悕悖悗悘悙悚悛悜悝悞悟悠悡悢患悤悥悦悧您悩悪悫悬悭悮悯悰悱悲悳悴悵悶悷悸悹悺悻悼悽悾悿惀惁惂惃惄情惆惇惈惉惊惋惌惍惎惏惐惑惒惓惔惕惖惗惘惙惚惛惜惝惞惟惠惡惢惣惤惥惦惧惨惩惪惫惬惭惮惯惰惱惲想惴惵惶惷惸惹惺惻惼惽惾惿愀愁愂愃愄愅愆愇愈愉愊愋愌愍愎意愐愑愒愓愔愕愖愗愘愙愚愛愜愝愞感愠愡","愢愣愤愥愦愧愨愩愪愫愬愭愮愯愰愱愲愳愴愵愶愷愸愹愺愻愼愽愾愿慀慁慂慃慄慅慆慇慈慉慊態慌慍慎慏慐慑慒慓慔慕慖慗慘慙慚慛慜慝慞慟慠慡慢慣慤慥慦慧慨慩慪慫慬慭慮慯慰慱慲慳慴慵慶慷慸慹慺慻慼慽慾慿憀憁憂憃憄憅憆憇憈憉憊憋憌憍憎憏憐憑憒憓憔憕憖憗憘憙憚憛憜憝憞憟憠憡憢憣憤憥憦憧憨憩憪憫憬憭憮憯憰憱憲憳憴憵憶憷憸憹憺憻憼憽憾憿懀懁懂懃懄懅懆懇懈應懊懋懌懍懎懏懐懑懒懓懔懕懖懗懘懙懚懛懜懝懞懟懠懡懢懣懤懥懦懧懨懩懪懫懬懭懮懯懰懱懲懳懴懵懶懷懸懹懺懻懼懽懾懿戀戁戂戃戄戅戆戇戈戉戊戋戌戍戎戏成我戒戓戔戕或戗战戙戚戛戜戝戞戟戠戡","戢戣戤戥戦戧戨戩截戫戬戭戮戯戰戱戲戳戴戵戶户戸戹戺戻戼戽戾房所扁扂扃扄扅扆扇扈扉扊手扌才扎扏扐扑扒打扔払扖扗托扙扚扛扜扝扞扟扠扡扢扣扤扥扦执扨扩扪扫扬扭扮扯扰扱扲扳扴扵扶扷扸批扺扻扼扽找承技抁抂抃抄抅抆抇抈抉把抋抌抍抎抏抐抑抒抓抔投抖抗折抙抚抛抜抝択抟抠抡抢抣护报抦抧抨抩抪披抬抭抮抯抰抱抲抳抴抵抶抷抸抹抺抻押抽抾抿拀拁拂拃拄担拆拇拈拉拊拋拌拍拎拏拐拑拒拓拔拕拖拗拘拙拚招拜拝拞拟拠拡拢拣拤拥拦拧拨择拪拫括拭拮拯拰拱拲拳拴拵拶拷拸拹拺拻拼拽拾拿挀持挂挃挄挅挆指挈按挊挋挌挍挎挏挐挑挒挓挔挕挖挗挘挙挚挛挜挝挞挟挠挡","挢挣挤挥挦挧挨挩挪挫挬挭挮振挰挱挲挳挴挵挶挷挸挹挺挻挼挽挾挿捀捁捂捃捄捅捆捇捈捉捊捋捌捍捎捏捐捑捒捓捔捕捖捗捘捙捚捛捜捝捞损捠捡换捣捤捥捦捧捨捩捪捫捬捭据捯捰捱捲捳捴捵捶捷捸捹捺捻捼捽捾捿掀掁掂掃掄掅掆掇授掉掊掋掌掍掎掏掐掑排掓掔掕掖掗掘掙掚掛掜掝掞掟掠採探掣掤接掦控推掩措掫掬掭掮掯掰掱掲掳掴掵掶掷掸掹掺掻掼掽掾掿揀揁揂揃揄揅揆揇揈揉揊揋揌揍揎描提揑插揓揔揕揖揗揘揙揚換揜揝揞揟揠握揢揣揤揥揦揧揨揩揪揫揬揭揮揯揰揱揲揳援揵揶揷揸揹揺揻揼揽揾揿搀搁搂搃搄搅搆搇搈搉搊搋搌損搎搏搐搑搒搓搔搕搖搗搘搙搚搛搜搝搞搟搠搡","搢搣搤搥搦搧搨搩搪搫搬搭搮搯搰搱搲搳搴搵搶搷搸搹携搻搼搽搾搿摀摁摂摃摄摅摆摇摈摉摊摋摌摍摎摏摐摑摒摓摔摕摖摗摘摙摚摛摜摝摞摟摠摡摢摣摤摥摦摧摨摩摪摫摬摭摮摯摰摱摲摳摴摵摶摷摸摹摺摻摼摽摾摿撀撁撂撃撄撅撆撇撈撉撊撋撌撍撎撏撐撑撒撓撔撕撖撗撘撙撚撛撜撝撞撟撠撡撢撣撤撥撦撧撨撩撪撫撬播撮撯撰撱撲撳撴撵撶撷撸撹撺撻撼撽撾撿擀擁擂擃擄擅擆擇擈擉擊擋擌操擎擏擐擑擒擓擔擕擖擗擘擙據擛擜擝擞擟擠擡擢擣擤擥擦擧擨擩擪擫擬擭擮擯擰擱擲擳擴擵擶擷擸擹擺擻擼擽擾擿攀攁攂攃攄攅攆攇攈攉攊攋攌攍攎攏攐攑攒攓攔攕攖攗攘攙攚攛攜攝攞攟攠攡","攢攣攤攥攦攧攨攩攪攫攬攭攮支攰攱攲攳攴攵收攷攸改攺攻攼攽放政敀敁敂敃敄故敆敇效敉敊敋敌敍敎敏敐救敒敓敔敕敖敗敘教敚敛敜敝敞敟敠敡敢散敤敥敦敧敨敩敪敫敬敭敮敯数敱敲敳整敵敶敷數敹敺敻敼敽敾敿斀斁斂斃斄斅斆文斈斉斊斋斌斍斎斏斐斑斒斓斔斕斖斗斘料斚斛斜斝斞斟斠斡斢斣斤斥斦斧斨斩斪斫斬断斮斯新斱斲斳斴斵斶斷斸方斺斻於施斾斿旀旁旂旃旄旅旆旇旈旉旊旋旌旍旎族旐旑旒旓旔旕旖旗旘旙旚旛旜旝旞旟无旡既旣旤日旦旧旨早旪旫旬旭旮旯旰旱旲旳旴旵时旷旸旹旺旻旼旽旾旿昀昁昂昃昄昅昆昇昈昉昊昋昌昍明昏昐昑昒易昔昕昖昗昘昙昚昛昜昝昞星映昡","昢昣昤春昦昧昨昩昪昫昬昭昮是昰昱昲昳昴昵昶昷昸昹昺昻昼昽显昿晀晁時晃晄晅晆晇晈晉晊晋晌晍晎晏晐晑晒晓晔晕晖晗晘晙晚晛晜晝晞晟晠晡晢晣晤晥晦晧晨晩晪晫晬晭普景晰晱晲晳晴晵晶晷晸晹智晻晼晽晾晿暀暁暂暃暄暅暆暇暈暉暊暋暌暍暎暏暐暑暒暓暔暕暖暗暘暙暚暛暜暝暞暟暠暡暢暣暤暥暦暧暨暩暪暫暬暭暮暯暰暱暲暳暴暵暶暷暸暹暺暻暼暽暾暿曀曁曂曃曄曅曆曇曈曉曊曋曌曍曎曏曐曑曒曓曔曕曖曗曘曙曚曛曜曝曞曟曠曡曢曣曤曥曦曧曨曩曪曫曬曭曮曯曰曱曲曳更曵曶曷書曹曺曻曼曽曾替最朁朂會朄朅朆朇月有朊朋朌服朎朏朐朑朒朓朔朕朖朗朘朙朚望朜朝朞期朠朡","朢朣朤朥朦朧木朩未末本札朮术朰朱朲朳朴朵朶朷朸朹机朻朼朽朾朿杀杁杂权杄杅杆杇杈杉杊杋杌杍李杏材村杒杓杔杕杖杗杘杙杚杛杜杝杞束杠条杢杣杤来杦杧杨杩杪杫杬杭杮杯杰東杲杳杴杵杶杷杸杹杺杻杼杽松板枀极枂枃构枅枆枇枈枉枊枋枌枍枎枏析枑枒枓枔枕枖林枘枙枚枛果枝枞枟枠枡枢枣枤枥枦枧枨枩枪枫枬枭枮枯枰枱枲枳枴枵架枷枸枹枺枻枼枽枾枿柀柁柂柃柄柅柆柇柈柉柊柋柌柍柎柏某柑柒染柔柕柖柗柘柙柚柛柜柝柞柟柠柡柢柣柤查柦柧柨柩柪柫柬柭柮柯柰柱柲柳柴柵柶柷柸柹柺査柼柽柾柿栀栁栂栃栄栅栆标栈栉栊栋栌栍栎栏栐树栒栓栔栕栖栗栘栙栚栛栜栝栞栟栠校","栢栣栤栥栦栧栨栩株栫栬栭栮栯栰栱栲栳栴栵栶样核根栺栻格栽栾栿桀桁桂桃桄桅框桇案桉桊桋桌桍桎桏桐桑桒桓桔桕桖桗桘桙桚桛桜桝桞桟桠桡桢档桤桥桦桧桨桩桪桫桬桭桮桯桰桱桲桳桴桵桶桷桸桹桺桻桼桽桾桿梀梁梂梃梄梅梆梇梈梉梊梋梌梍梎梏梐梑梒梓梔梕梖梗梘梙梚梛梜條梞梟梠梡梢梣梤梥梦梧梨梩梪梫梬梭梮梯械梱梲梳梴梵梶梷梸梹梺梻梼梽梾梿检棁棂棃棄棅棆棇棈棉棊棋棌棍棎棏棐棑棒棓棔棕棖棗棘棙棚棛棜棝棞棟棠棡棢棣棤棥棦棧棨棩棪棫棬棭森棯棰棱棲棳棴棵棶棷棸棹棺棻棼棽棾棿椀椁椂椃椄椅椆椇椈椉椊椋椌植椎椏椐椑椒椓椔椕椖椗椘椙椚椛検椝椞椟椠椡","椢椣椤椥椦椧椨椩椪椫椬椭椮椯椰椱椲椳椴椵椶椷椸椹椺椻椼椽椾椿楀楁楂楃楄楅楆楇楈楉楊楋楌楍楎楏楐楑楒楓楔楕楖楗楘楙楚楛楜楝楞楟楠楡楢楣楤楥楦楧楨楩楪楫楬業楮楯楰楱楲楳楴極楶楷楸楹楺楻楼楽楾楿榀榁概榃榄榅榆榇榈榉榊榋榌榍榎榏榐榑榒榓榔榕榖榗榘榙榚榛榜榝榞榟榠榡榢榣榤榥榦榧榨榩榪榫榬榭榮榯榰榱榲榳榴榵榶榷榸榹榺榻榼榽榾榿槀槁槂槃槄槅槆槇槈槉槊構槌槍槎槏槐槑槒槓槔槕槖槗様槙槚槛槜槝槞槟槠槡槢槣槤槥槦槧槨槩槪槫槬槭槮槯槰槱槲槳槴槵槶槷槸槹槺槻槼槽槾槿樀樁樂樃樄樅樆樇樈樉樊樋樌樍樎樏樐樑樒樓樔樕樖樗樘標樚樛樜樝樞樟樠模","樢樣樤樥樦樧樨権横樫樬樭樮樯樰樱樲樳樴樵樶樷樸樹樺樻樼樽樾樿橀橁橂橃橄橅橆橇橈橉橊橋橌橍橎橏橐橑橒橓橔橕橖橗橘橙橚橛橜橝橞機橠橡橢橣橤橥橦橧橨橩橪橫橬橭橮橯橰橱橲橳橴橵橶橷橸橹橺橻橼橽橾橿檀檁檂檃檄檅檆檇檈檉檊檋檌檍檎檏檐檑檒檓檔檕檖檗檘檙檚檛檜檝檞檟檠檡檢檣檤檥檦檧檨檩檪檫檬檭檮檯檰檱檲檳檴檵檶檷檸檹檺檻檼檽檾檿櫀櫁櫂櫃櫄櫅櫆櫇櫈櫉櫊櫋櫌櫍櫎櫏櫐櫑櫒櫓櫔櫕櫖櫗櫘櫙櫚櫛櫜櫝櫞櫟櫠櫡櫢櫣櫤櫥櫦櫧櫨櫩櫪櫫櫬櫭櫮櫯櫰櫱櫲櫳櫴櫵櫶櫷櫸櫹櫺櫻櫼櫽櫾櫿欀欁欂欃欄欅欆欇欈欉權欋欌欍欎欏欐欑欒欓欔欕欖欗欘欙欚欛欜欝欞欟欠次","欢欣欤欥欦欧欨欩欪欫欬欭欮欯欰欱欲欳欴欵欶欷欸欹欺欻欼欽款欿歀歁歂歃歄歅歆歇歈歉歊歋歌歍歎歏歐歑歒歓歔歕歖歗歘歙歚歛歜歝歞歟歠歡止正此步武歧歨歩歪歫歬歭歮歯歰歱歲歳歴歵歶歷歸歹歺死歼歽歾歿殀殁殂殃殄殅殆殇殈殉殊残殌殍殎殏殐殑殒殓殔殕殖殗殘殙殚殛殜殝殞殟殠殡殢殣殤殥殦殧殨殩殪殫殬殭殮殯殰殱殲殳殴段殶殷殸殹殺殻殼殽殾殿毀毁毂毃毄毅毆毇毈毉毊毋毌母毎每毐毑毒毓比毕毖毗毘毙毚毛毜毝毞毟毠毡毢毣毤毥毦毧毨毩毪毫毬毭毮毯毰毱毲毳毴毵毶毷毸毹毺毻毼毽毾毿氀氁氂氃氄氅氆氇氈氉氊氋氌氍氎氏氐民氒氓气氕氖気氘氙氚氛氜氝氞氟氠氡","氢氣氤氥氦氧氨氩氪氫氬氭氮氯氰氱氲氳水氵氶氷永氹氺氻氼氽氾氿汀汁求汃汄汅汆汇汈汉汊汋汌汍汎汏汐汑汒汓汔汕汖汗汘汙汚汛汜汝汞江池污汢汣汤汥汦汧汨汩汪汫汬汭汮汯汰汱汲汳汴汵汶汷汸汹決汻汼汽汾汿沀沁沂沃沄沅沆沇沈沉沊沋沌沍沎沏沐沑沒沓沔沕沖沗沘沙沚沛沜沝沞沟沠没沢沣沤沥沦沧沨沩沪沫沬沭沮沯沰沱沲河沴沵沶沷沸油沺治沼沽沾沿泀況泂泃泄泅泆泇泈泉泊泋泌泍泎泏泐泑泒泓泔法泖泗泘泙泚泛泜泝泞泟泠泡波泣泤泥泦泧注泩泪泫泬泭泮泯泰泱泲泳泴泵泶泷泸泹泺泻泼泽泾泿洀洁洂洃洄洅洆洇洈洉洊洋洌洍洎洏洐洑洒洓洔洕洖洗洘洙洚洛洜洝洞洟洠洡","洢洣洤津洦洧洨洩洪洫洬洭洮洯洰洱洲洳洴洵洶洷洸洹洺活洼洽派洿浀流浂浃浄浅浆浇浈浉浊测浌浍济浏浐浑浒浓浔浕浖浗浘浙浚浛浜浝浞浟浠浡浢浣浤浥浦浧浨浩浪浫浬浭浮浯浰浱浲浳浴浵浶海浸浹浺浻浼浽浾浿涀涁涂涃涄涅涆涇消涉涊涋涌涍涎涏涐涑涒涓涔涕涖涗涘涙涚涛涜涝涞涟涠涡涢涣涤涥润涧涨涩涪涫涬涭涮涯涰涱液涳涴涵涶涷涸涹涺涻涼涽涾涿淀淁淂淃淄淅淆淇淈淉淊淋淌淍淎淏淐淑淒淓淔淕淖淗淘淙淚淛淜淝淞淟淠淡淢淣淤淥淦淧淨淩淪淫淬淭淮淯淰深淲淳淴淵淶混淸淹淺添淼淽淾淿渀渁渂渃渄清渆渇済渉渊渋渌渍渎渏渐渑渒渓渔渕渖渗渘渙渚減渜渝渞渟渠渡","渢渣渤渥渦渧渨温渪渫測渭渮港渰渱渲渳渴渵渶渷游渹渺渻渼渽渾渿湀湁湂湃湄湅湆湇湈湉湊湋湌湍湎湏湐湑湒湓湔湕湖湗湘湙湚湛湜湝湞湟湠湡湢湣湤湥湦湧湨湩湪湫湬湭湮湯湰湱湲湳湴湵湶湷湸湹湺湻湼湽湾湿満溁溂溃溄溅溆溇溈溉溊溋溌溍溎溏源溑溒溓溔溕準溗溘溙溚溛溜溝溞溟溠溡溢溣溤溥溦溧溨溩溪溫溬溭溮溯溰溱溲溳溴溵溶溷溸溹溺溻溼溽溾溿滀滁滂滃滄滅滆滇滈滉滊滋滌滍滎滏滐滑滒滓滔滕滖滗滘滙滚滛滜滝滞滟滠满滢滣滤滥滦滧滨滩滪滫滬滭滮滯滰滱滲滳滴滵滶滷滸滹滺滻滼滽滾滿漀漁漂漃漄漅漆漇漈漉漊漋漌漍漎漏漐漑漒漓演漕漖漗漘漙漚漛漜漝漞漟漠漡","漢漣漤漥漦漧漨漩漪漫漬漭漮漯漰漱漲漳漴漵漶漷漸漹漺漻漼漽漾漿潀潁潂潃潄潅潆潇潈潉潊潋潌潍潎潏潐潑潒潓潔潕潖潗潘潙潚潛潜潝潞潟潠潡潢潣潤潥潦潧潨潩潪潫潬潭潮潯潰潱潲潳潴潵潶潷潸潹潺潻潼潽潾潿澀澁澂澃澄澅澆澇澈澉澊澋澌澍澎澏澐澑澒澓澔澕澖澗澘澙澚澛澜澝澞澟澠澡澢澣澤澥澦澧澨澩澪澫澬澭澮澯澰澱澲澳澴澵澶澷澸澹澺澻澼澽澾澿激濁濂濃濄濅濆濇濈濉濊濋濌濍濎濏濐濑濒濓濔濕濖濗濘濙濚濛濜濝濞濟濠濡濢濣濤濥濦濧濨濩濪濫濬濭濮濯濰濱濲濳濴濵濶濷濸濹濺濻濼濽濾濿瀀瀁瀂瀃瀄瀅瀆瀇瀈瀉瀊瀋瀌瀍瀎瀏瀐瀑瀒瀓瀔瀕瀖瀗瀘瀙瀚瀛瀜瀝瀞瀟瀠瀡","瀢瀣瀤瀥瀦瀧瀨瀩瀪瀫瀬瀭瀮瀯瀰瀱瀲瀳瀴瀵瀶瀷瀸瀹瀺瀻瀼瀽瀾瀿灀灁灂灃灄灅灆灇灈灉灊灋灌灍灎灏灐灑灒灓灔灕灖灗灘灙灚灛灜灝灞灟灠灡灢灣灤灥灦灧灨灩灪火灬灭灮灯灰灱灲灳灴灵灶灷灸灹灺灻灼災灾灿炀炁炂炃炄炅炆炇炈炉炊炋炌炍炎炏炐炑炒炓炔炕炖炗炘炙炚炛炜炝炞炟炠炡炢炣炤炥炦炧炨炩炪炫炬炭炮炯炰炱炲炳炴炵炶炷炸点為炻炼炽炾炿烀烁烂烃烄烅烆烇烈烉烊烋烌烍烎烏烐烑烒烓烔烕烖烗烘烙烚烛烜烝烞烟烠烡烢烣烤烥烦烧烨烩烪烫烬热烮烯烰烱烲烳烴烵烶烷烸烹烺烻烼烽烾烿焀焁焂焃焄焅焆焇焈焉焊焋焌焍焎焏焐焑焒焓焔焕焖焗焘焙焚焛焜焝焞焟焠無","焢焣焤焥焦焧焨焩焪焫焬焭焮焯焰焱焲焳焴焵然焷焸焹焺焻焼焽焾焿煀煁煂煃煄煅煆煇煈煉煊煋煌煍煎煏煐煑煒煓煔煕煖煗煘煙煚煛煜煝煞煟煠煡煢煣煤煥煦照煨煩煪煫煬煭煮煯煰煱煲煳煴煵煶煷煸煹煺煻煼煽煾煿熀熁熂熃熄熅熆熇熈熉熊熋熌熍熎熏熐熑熒熓熔熕熖熗熘熙熚熛熜熝熞熟熠熡熢熣熤熥熦熧熨熩熪熫熬熭熮熯熰熱熲熳熴熵熶熷熸熹熺熻熼熽熾熿燀燁燂燃燄燅燆燇燈燉燊燋燌燍燎燏燐燑燒燓燔燕燖燗燘燙燚燛燜燝燞營燠燡燢燣燤燥燦燧燨燩燪燫燬燭燮燯燰燱燲燳燴燵燶燷燸燹燺燻燼燽燾燿爀爁爂爃爄爅爆爇爈爉爊爋爌爍爎爏爐爑爒爓爔爕爖爗爘爙爚爛爜爝爞爟爠爡","爢爣爤爥爦爧爨爩爪爫爬爭爮爯爰爱爲爳爴爵父爷爸爹爺爻爼爽爾爿牀牁牂牃牄牅牆片版牉牊牋牌牍牎牏牐牑牒牓牔牕牖牗牘牙牚牛牜牝牞牟牠牡牢牣牤牥牦牧牨物牪牫牬牭牮牯牰牱牲牳牴牵牶牷牸特牺牻牼牽牾牿犀犁犂犃犄犅犆犇犈犉犊犋犌犍犎犏犐犑犒犓犔犕犖犗犘犙犚犛犜犝犞犟犠犡犢犣犤犥犦犧犨犩犪犫犬犭犮犯犰犱犲犳犴犵状犷犸犹犺犻犼犽犾犿狀狁狂狃狄狅狆狇狈狉狊狋狌狍狎狏狐狑狒狓狔狕狖狗狘狙狚狛狜狝狞狟狠狡狢狣狤狥狦狧狨狩狪狫独狭狮狯狰狱狲狳狴狵狶狷狸狹狺狻狼狽狾狿猀猁猂猃猄猅猆猇猈猉猊猋猌猍猎猏猐猑猒猓猔猕猖猗猘猙猚猛猜猝猞猟猠猡","猢猣猤猥猦猧猨猩猪猫猬猭献猯猰猱猲猳猴猵猶猷猸猹猺猻猼猽猾猿獀獁獂獃獄獅獆獇獈獉獊獋獌獍獎獏獐獑獒獓獔獕獖獗獘獙獚獛獜獝獞獟獠獡獢獣獤獥獦獧獨獩獪獫獬獭獮獯獰獱獲獳獴獵獶獷獸獹獺獻獼獽獾獿玀玁玂玃玄玅玆率玈玉玊王玌玍玎玏玐玑玒玓玔玕玖玗玘玙玚玛玜玝玞玟玠玡玢玣玤玥玦玧玨玩玪玫玬玭玮环现玱玲玳玴玵玶玷玸玹玺玻玼玽玾玿珀珁珂珃珄珅珆珇珈珉珊珋珌珍珎珏珐珑珒珓珔珕珖珗珘珙珚珛珜珝珞珟珠珡珢珣珤珥珦珧珨珩珪珫珬班珮珯珰珱珲珳珴珵珶珷珸珹珺珻珼珽現珿琀琁琂球琄琅理琇琈琉琊琋琌琍琎琏琐琑琒琓琔琕琖琗琘琙琚琛琜琝琞琟琠琡","琢琣琤琥琦琧琨琩琪琫琬琭琮琯琰琱琲琳琴琵琶琷琸琹琺琻琼琽琾琿瑀瑁瑂瑃瑄瑅瑆瑇瑈瑉瑊瑋瑌瑍瑎瑏瑐瑑瑒瑓瑔瑕瑖瑗瑘瑙瑚瑛瑜瑝瑞瑟瑠瑡瑢瑣瑤瑥瑦瑧瑨瑩瑪瑫瑬瑭瑮瑯瑰瑱瑲瑳瑴瑵瑶瑷瑸瑹瑺瑻瑼瑽瑾瑿璀璁璂璃璄璅璆璇璈璉璊璋璌璍璎璏璐璑璒璓璔璕璖璗璘璙璚璛璜璝璞璟璠璡璢璣璤璥璦璧璨璩璪璫璬璭璮璯環璱璲璳璴璵璶璷璸璹璺璻璼璽璾璿瓀瓁瓂瓃瓄瓅瓆瓇瓈瓉瓊瓋瓌瓍瓎瓏瓐瓑瓒瓓瓔瓕瓖瓗瓘瓙瓚瓛瓜瓝瓞瓟瓠瓡瓢瓣瓤瓥瓦瓧瓨瓩瓪瓫瓬瓭瓮瓯瓰瓱瓲瓳瓴瓵瓶瓷瓸瓹瓺瓻瓼瓽瓾瓿甀甁甂甃甄甅甆甇甈甉甊甋甌甍甎甏甐甑甒甓甔甕甖甗甘甙甚甛甜甝甞生甠甡","產産甤甥甦甧用甩甪甫甬甭甮甯田由甲申甴电甶男甸甹町画甼甽甾甿畀畁畂畃畄畅畆畇畈畉畊畋界畍畎畏畐畑畒畓畔畕畖畗畘留畚畛畜畝畞畟畠畡畢畣畤略畦畧畨畩番畫畬畭畮畯異畱畲畳畴畵當畷畸畹畺畻畼畽畾畿疀疁疂疃疄疅疆疇疈疉疊疋疌疍疎疏疐疑疒疓疔疕疖疗疘疙疚疛疜疝疞疟疠疡疢疣疤疥疦疧疨疩疪疫疬疭疮疯疰疱疲疳疴疵疶疷疸疹疺疻疼疽疾疿痀痁痂痃痄病痆症痈痉痊痋痌痍痎痏痐痑痒痓痔痕痖痗痘痙痚痛痜痝痞痟痠痡痢痣痤痥痦痧痨痩痪痫痬痭痮痯痰痱痲痳痴痵痶痷痸痹痺痻痼痽痾痿瘀瘁瘂瘃瘄瘅瘆瘇瘈瘉瘊瘋瘌瘍瘎瘏瘐瘑瘒瘓瘔瘕瘖瘗瘘瘙瘚瘛瘜瘝瘞瘟瘠瘡","瘢瘣瘤瘥瘦瘧瘨瘩瘪瘫瘬瘭瘮瘯瘰瘱瘲瘳瘴瘵瘶瘷瘸瘹瘺瘻瘼瘽瘾瘿癀癁療癃癄癅癆癇癈癉癊癋癌癍癎癏癐癑癒癓癔癕癖癗癘癙癚癛癜癝癞癟癠癡癢癣癤癥癦癧癨癩癪癫癬癭癮癯癰癱癲癳癴癵癶癷癸癹発登發白百癿皀皁皂皃的皅皆皇皈皉皊皋皌皍皎皏皐皑皒皓皔皕皖皗皘皙皚皛皜皝皞皟皠皡皢皣皤皥皦皧皨皩皪皫皬皭皮皯皰皱皲皳皴皵皶皷皸皹皺皻皼皽皾皿盀盁盂盃盄盅盆盇盈盉益盋盌盍盎盏盐监盒盓盔盕盖盗盘盙盚盛盜盝盞盟盠盡盢監盤盥盦盧盨盩盪盫盬盭目盯盰盱盲盳直盵盶盷相盹盺盻盼盽盾盿眀省眂眃眄眅眆眇眈眉眊看県眍眎眏眐眑眒眓眔眕眖眗眘眙眚眛眜眝眞真眠眡","眢眣眤眥眦眧眨眩眪眫眬眭眮眯眰眱眲眳眴眵眶眷眸眹眺眻眼眽眾眿着睁睂睃睄睅睆睇睈睉睊睋睌睍睎睏睐睑睒睓睔睕睖睗睘睙睚睛睜睝睞睟睠睡睢督睤睥睦睧睨睩睪睫睬睭睮睯睰睱睲睳睴睵睶睷睸睹睺睻睼睽睾睿瞀瞁瞂瞃瞄瞅瞆瞇瞈瞉瞊瞋瞌瞍瞎瞏瞐瞑瞒瞓瞔瞕瞖瞗瞘瞙瞚瞛瞜瞝瞞瞟瞠瞡瞢瞣瞤瞥瞦瞧瞨瞩瞪瞫瞬瞭瞮瞯瞰瞱瞲瞳瞴瞵瞶瞷瞸瞹瞺瞻瞼瞽瞾瞿矀矁矂矃矄矅矆矇矈矉矊矋矌矍矎矏矐矑矒矓矔矕矖矗矘矙矚矛矜矝矞矟矠矡矢矣矤知矦矧矨矩矪矫矬短矮矯矰矱矲石矴矵矶矷矸矹矺矻矼矽矾矿砀码砂砃砄砅砆砇砈砉砊砋砌砍砎砏砐砑砒砓研砕砖砗砘砙砚砛砜砝砞砟砠砡","砢砣砤砥砦砧砨砩砪砫砬砭砮砯砰砱砲砳破砵砶砷砸砹砺砻砼砽砾砿础硁硂硃硄硅硆硇硈硉硊硋硌硍硎硏硐硑硒硓硔硕硖硗硘硙硚硛硜硝硞硟硠硡硢硣硤硥硦硧硨硩硪硫硬硭确硯硰硱硲硳硴硵硶硷硸硹硺硻硼硽硾硿碀碁碂碃碄碅碆碇碈碉碊碋碌碍碎碏碐碑碒碓碔碕碖碗碘碙碚碛碜碝碞碟碠碡碢碣碤碥碦碧碨碩碪碫碬碭碮碯碰碱碲碳碴碵碶碷碸碹確碻碼碽碾碿磀磁磂磃磄磅磆磇磈磉磊磋磌磍磎磏磐磑磒磓磔磕磖磗磘磙磚磛磜磝磞磟磠磡磢磣磤磥磦磧磨磩磪磫磬磭磮磯磰磱磲磳磴磵磶磷磸磹磺磻磼磽磾磿礀礁礂礃礄礅礆礇礈礉礊礋礌礍礎礏礐礑礒礓礔礕礖礗礘礙礚礛礜礝礞礟礠礡","礢礣礤礥礦礧礨礩礪礫礬礭礮礯礰礱礲礳礴礵礶礷礸礹示礻礼礽社礿祀祁祂祃祄祅祆祇祈祉祊祋祌祍祎祏祐祑祒祓祔祕祖祗祘祙祚祛祜祝神祟祠祡祢祣祤祥祦祧票祩祪祫祬祭祮祯祰祱祲祳祴祵祶祷祸祹祺祻祼祽祾祿禀禁禂禃禄禅禆禇禈禉禊禋禌禍禎福禐禑禒禓禔禕禖禗禘禙禚禛禜禝禞禟禠禡禢禣禤禥禦禧禨禩禪禫禬禭禮禯禰禱禲禳禴禵禶禷禸禹禺离禼禽禾禿秀私秂秃秄秅秆秇秈秉秊秋秌种秎秏秐科秒秓秔秕秖秗秘秙秚秛秜秝秞租秠秡秢秣秤秥秦秧秨秩秪秫秬秭秮积称秱秲秳秴秵秶秷秸秹秺移秼秽秾秿稀稁稂稃稄稅稆稇稈稉稊程稌稍税稏稐稑稒稓稔稕稖稗稘稙稚稛稜稝稞稟稠稡","稢稣稤稥稦稧稨稩稪稫稬稭種稯稰稱稲稳稴稵稶稷稸稹稺稻稼稽稾稿穀穁穂穃穄穅穆穇穈穉穊穋穌積穎穏穐穑穒穓穔穕穖穗穘穙穚穛穜穝穞穟穠穡穢穣穤穥穦穧穨穩穪穫穬穭穮穯穰穱穲穳穴穵究穷穸穹空穻穼穽穾穿窀突窂窃窄窅窆窇窈窉窊窋窌窍窎窏窐窑窒窓窔窕窖窗窘窙窚窛窜窝窞窟窠窡窢窣窤窥窦窧窨窩窪窫窬窭窮窯窰窱窲窳窴窵窶窷窸窹窺窻窼窽窾窿竀竁竂竃竄竅竆竇竈竉竊立竌竍竎竏竐竑竒竓竔竕竖竗竘站竚竛竜竝竞竟章竡竢竣竤童竦竧竨竩竪竫竬竭竮端竰竱竲竳竴竵競竷竸竹竺竻竼竽竾竿笀笁笂笃笄笅笆笇笈笉笊笋笌笍笎笏笐笑笒笓笔笕笖笗笘笙笚笛笜笝笞笟笠笡","笢笣笤笥符笧笨笩笪笫第笭笮笯笰笱笲笳笴笵笶笷笸笹笺笻笼笽笾笿筀筁筂筃筄筅筆筇筈等筊筋筌筍筎筏筐筑筒筓答筕策筗筘筙筚筛筜筝筞筟筠筡筢筣筤筥筦筧筨筩筪筫筬筭筮筯筰筱筲筳筴筵筶筷筸筹筺筻筼筽签筿简箁箂箃箄箅箆箇箈箉箊箋箌箍箎箏箐箑箒箓箔箕箖算箘箙箚箛箜箝箞箟箠管箢箣箤箥箦箧箨箩箪箫箬箭箮箯箰箱箲箳箴箵箶箷箸箹箺箻箼箽箾箿節篁篂篃範篅篆篇篈築篊篋篌篍篎篏篐篑篒篓篔篕篖篗篘篙篚篛篜篝篞篟篠篡篢篣篤篥篦篧篨篩篪篫篬篭篮篯篰篱篲篳篴篵篶篷篸篹篺篻篼篽篾篿簀簁簂簃簄簅簆簇簈簉簊簋簌簍簎簏簐簑簒簓簔簕簖簗簘簙簚簛簜簝簞簟簠簡","簢簣簤簥簦簧簨簩簪簫簬簭簮簯簰簱簲簳簴簵簶簷簸簹簺簻簼簽簾簿籀籁籂籃籄籅籆籇籈籉籊籋籌籍籎籏籐籑籒籓籔籕籖籗籘籙籚籛籜籝籞籟籠籡籢籣籤籥籦籧籨籩籪籫籬籭籮籯籰籱籲米籴籵籶籷籸籹籺类籼籽籾籿粀粁粂粃粄粅粆粇粈粉粊粋粌粍粎粏粐粑粒粓粔粕粖粗粘粙粚粛粜粝粞粟粠粡粢粣粤粥粦粧粨粩粪粫粬粭粮粯粰粱粲粳粴粵粶粷粸粹粺粻粼粽精粿糀糁糂糃糄糅糆糇糈糉糊糋糌糍糎糏糐糑糒糓糔糕糖糗糘糙糚糛糜糝糞糟糠糡糢糣糤糥糦糧糨糩糪糫糬糭糮糯糰糱糲糳糴糵糶糷糸糹糺系糼糽糾糿紀紁紂紃約紅紆紇紈紉紊紋紌納紎紏紐紑紒紓純紕紖紗紘紙級紛紜紝紞紟素紡","索紣紤紥紦紧紨紩紪紫紬紭紮累細紱紲紳紴紵紶紷紸紹紺紻紼紽紾紿絀絁終絃組絅絆絇絈絉絊絋経絍絎絏結絑絒絓絔絕絖絗絘絙絚絛絜絝絞絟絠絡絢絣絤絥給絧絨絩絪絫絬絭絮絯絰統絲絳絴絵絶絷絸絹絺絻絼絽絾絿綀綁綂綃綄綅綆綇綈綉綊綋綌綍綎綏綐綑綒經綔綕綖綗綘継続綛綜綝綞綟綠綡綢綣綤綥綦綧綨綩綪綫綬維綮綯綰綱網綳綴綵綶綷綸綹綺綻綼綽綾綿緀緁緂緃緄緅緆緇緈緉緊緋緌緍緎総緐緑緒緓緔緕緖緗緘緙線緛緜緝緞緟締緡緢緣緤緥緦緧編緩緪緫緬緭緮緯緰緱緲緳練緵緶緷緸緹緺緻緼緽緾緿縀縁縂縃縄縅縆縇縈縉縊縋縌縍縎縏縐縑縒縓縔縕縖縗縘縙縚縛縜縝縞縟縠縡","縢縣縤縥縦縧縨縩縪縫縬縭縮縯縰縱縲縳縴縵縶縷縸縹縺縻縼總績縿繀繁繂繃繄繅繆繇繈繉繊繋繌繍繎繏繐繑繒繓織繕繖繗繘繙繚繛繜繝繞繟繠繡繢繣繤繥繦繧繨繩繪繫繬繭繮繯繰繱繲繳繴繵繶繷繸繹繺繻繼繽繾繿纀纁纂纃纄纅纆纇纈纉纊纋續纍纎纏纐纑纒纓纔纕纖纗纘纙纚纛纜纝纞纟纠纡红纣纤纥约级纨纩纪纫纬纭纮纯纰纱纲纳纴纵纶纷纸纹纺纻纼纽纾线绀绁绂练组绅细织终绉绊绋绌绍绎经绐绑绒结绔绕绖绗绘给绚绛络绝绞统绠绡绢绣绤绥绦继绨绩绪绫绬续绮绯绰绱绲绳维绵绶绷绸绹绺绻综绽绾绿缀缁缂缃缄缅缆缇缈缉缊缋缌缍缎缏缐缑缒缓缔缕编缗缘缙缚缛缜缝缞缟缠缡","缢缣缤缥缦缧缨缩缪缫缬缭缮缯缰缱缲缳缴缵缶缷缸缹缺缻缼缽缾缿罀罁罂罃罄罅罆罇罈罉罊罋罌罍罎罏罐网罒罓罔罕罖罗罘罙罚罛罜罝罞罟罠罡罢罣罤罥罦罧罨罩罪罫罬罭置罯罰罱署罳罴罵罶罷罸罹罺罻罼罽罾罿羀羁羂羃羄羅羆羇羈羉羊羋羌羍美羏羐羑羒羓羔羕羖羗羘羙羚羛羜羝羞羟羠羡羢羣群羥羦羧羨義羪羫羬羭羮羯羰羱羲羳羴羵羶羷羸羹羺羻羼羽羾羿翀翁翂翃翄翅翆翇翈翉翊翋翌翍翎翏翐翑習翓翔翕翖翗翘翙翚翛翜翝翞翟翠翡翢翣翤翥翦翧翨翩翪翫翬翭翮翯翰翱翲翳翴翵翶翷翸翹翺翻翼翽翾翿耀老耂考耄者耆耇耈耉耊耋而耍耎耏耐耑耒耓耔耕耖耗耘耙耚耛耜耝耞耟耠耡","耢耣耤耥耦耧耨耩耪耫耬耭耮耯耰耱耲耳耴耵耶耷耸耹耺耻耼耽耾耿聀聁聂聃聄聅聆聇聈聉聊聋职聍聎聏聐聑聒聓联聕聖聗聘聙聚聛聜聝聞聟聠聡聢聣聤聥聦聧聨聩聪聫聬聭聮聯聰聱聲聳聴聵聶職聸聹聺聻聼聽聾聿肀肁肂肃肄肅肆肇肈肉肊肋肌肍肎肏肐肑肒肓肔肕肖肗肘肙肚肛肜肝肞肟肠股肢肣肤肥肦肧肨肩肪肫肬肭肮肯肰肱育肳肴肵肶肷肸肹肺肻肼肽肾肿胀胁胂胃胄胅胆胇胈胉胊胋背胍胎胏胐胑胒胓胔胕胖胗胘胙胚胛胜胝胞胟胠胡胢胣胤胥胦胧胨胩胪胫胬胭胮胯胰胱胲胳胴胵胶胷胸胹胺胻胼能胾胿脀脁脂脃脄脅脆脇脈脉脊脋脌脍脎脏脐脑脒脓脔脕脖脗脘脙脚脛脜脝脞脟脠脡","脢脣脤脥脦脧脨脩脪脫脬脭脮脯脰脱脲脳脴脵脶脷脸脹脺脻脼脽脾脿腀腁腂腃腄腅腆腇腈腉腊腋腌腍腎腏腐腑腒腓腔腕腖腗腘腙腚腛腜腝腞腟腠腡腢腣腤腥腦腧腨腩腪腫腬腭腮腯腰腱腲腳腴腵腶腷腸腹腺腻腼腽腾腿膀膁膂膃膄膅膆膇膈膉膊膋膌膍膎膏膐膑膒膓膔膕膖膗膘膙膚膛膜膝膞膟膠膡膢膣膤膥膦膧膨膩膪膫膬膭膮膯膰膱膲膳膴膵膶膷膸膹膺膻膼膽膾膿臀臁臂臃臄臅臆臇臈臉臊臋臌臍臎臏臐臑臒臓臔臕臖臗臘臙臚臛臜臝臞臟臠臡臢臣臤臥臦臧臨臩自臫臬臭臮臯臰臱臲至致臵臶臷臸臹臺臻臼臽臾臿舀舁舂舃舄舅舆與興舉舊舋舌舍舎舏舐舑舒舓舔舕舖舗舘舙舚舛舜舝舞舟舠舡","舢舣舤舥舦舧舨舩航舫般舭舮舯舰舱舲舳舴舵舶舷舸船舺舻舼舽舾舿艀艁艂艃艄艅艆艇艈艉艊艋艌艍艎艏艐艑艒艓艔艕艖艗艘艙艚艛艜艝艞艟艠艡艢艣艤艥艦艧艨艩艪艫艬艭艮良艰艱色艳艴艵艶艷艸艹艺艻艼艽艾艿芀芁节芃芄芅芆芇芈芉芊芋芌芍芎芏芐芑芒芓芔芕芖芗芘芙芚芛芜芝芞芟芠芡芢芣芤芥芦芧芨芩芪芫芬芭芮芯芰花芲芳芴芵芶芷芸芹芺芻芼芽芾芿苀苁苂苃苄苅苆苇苈苉苊苋苌苍苎苏苐苑苒苓苔苕苖苗苘苙苚苛苜苝苞苟苠苡苢苣苤若苦苧苨苩苪苫苬苭苮苯苰英苲苳苴苵苶苷苸苹苺苻苼苽苾苿茀茁茂范茄茅茆茇茈茉茊茋茌茍茎茏茐茑茒茓茔茕茖茗茘茙茚茛茜茝茞茟茠茡","茢茣茤茥茦茧茨茩茪茫茬茭茮茯茰茱茲茳茴茵茶茷茸茹茺茻茼茽茾茿荀荁荂荃荄荅荆荇荈草荊荋荌荍荎荏荐荑荒荓荔荕荖荗荘荙荚荛荜荝荞荟荠荡荢荣荤荥荦荧荨荩荪荫荬荭荮药荰荱荲荳荴荵荶荷荸荹荺荻荼荽荾荿莀莁莂莃莄莅莆莇莈莉莊莋莌莍莎莏莐莑莒莓莔莕莖莗莘莙莚莛莜莝莞莟莠莡莢莣莤莥莦莧莨莩莪莫莬莭莮莯莰莱莲莳莴莵莶获莸莹莺莻莼莽莾莿菀菁菂菃菄菅菆菇菈菉菊菋菌菍菎菏菐菑菒菓菔菕菖菗菘菙菚菛菜菝菞菟菠菡菢菣菤菥菦菧菨菩菪菫菬菭菮華菰菱菲菳菴菵菶菷菸菹菺菻菼菽菾菿萀萁萂萃萄萅萆萇萈萉萊萋萌萍萎萏萐萑萒萓萔萕萖萗萘萙萚萛萜萝萞萟萠萡","萢萣萤营萦萧萨萩萪萫萬萭萮萯萰萱萲萳萴萵萶萷萸萹萺萻萼落萾萿葀葁葂葃葄葅葆葇葈葉葊葋葌葍葎葏葐葑葒葓葔葕葖著葘葙葚葛葜葝葞葟葠葡葢董葤葥葦葧葨葩葪葫葬葭葮葯葰葱葲葳葴葵葶葷葸葹葺葻葼葽葾葿蒀蒁蒂蒃蒄蒅蒆蒇蒈蒉蒊蒋蒌蒍蒎蒏蒐蒑蒒蒓蒔蒕蒖蒗蒘蒙蒚蒛蒜蒝蒞蒟蒠蒡蒢蒣蒤蒥蒦蒧蒨蒩蒪蒫蒬蒭蒮蒯蒰蒱蒲蒳蒴蒵蒶蒷蒸蒹蒺蒻蒼蒽蒾蒿蓀蓁蓂蓃蓄蓅蓆蓇蓈蓉蓊蓋蓌蓍蓎蓏蓐蓑蓒蓓蓔蓕蓖蓗蓘蓙蓚蓛蓜蓝蓞蓟蓠蓡蓢蓣蓤蓥蓦蓧蓨蓩蓪蓫蓬蓭蓮蓯蓰蓱蓲蓳蓴蓵蓶蓷蓸蓹蓺蓻蓼蓽蓾蓿蔀蔁蔂蔃蔄蔅蔆蔇蔈蔉蔊蔋蔌蔍蔎蔏蔐蔑蔒蔓蔔蔕蔖蔗蔘蔙蔚蔛蔜蔝蔞蔟蔠蔡","蔢蔣蔤蔥蔦蔧蔨蔩蔪蔫蔬蔭蔮蔯蔰蔱蔲蔳蔴蔵蔶蔷蔸蔹蔺蔻蔼蔽蔾蔿蕀蕁蕂蕃蕄蕅蕆蕇蕈蕉蕊蕋蕌蕍蕎蕏蕐蕑蕒蕓蕔蕕蕖蕗蕘蕙蕚蕛蕜蕝蕞蕟蕠蕡蕢蕣蕤蕥蕦蕧蕨蕩蕪蕫蕬蕭蕮蕯蕰蕱蕲蕳蕴蕵蕶蕷蕸蕹蕺蕻蕼蕽蕾蕿薀薁薂薃薄薅薆薇薈薉薊薋薌薍薎薏薐薑薒薓薔薕薖薗薘薙薚薛薜薝薞薟薠薡薢薣薤薥薦薧薨薩薪薫薬薭薮薯薰薱薲薳薴薵薶薷薸薹薺薻薼薽薾薿藀藁藂藃藄藅藆藇藈藉藊藋藌藍藎藏藐藑藒藓藔藕藖藗藘藙藚藛藜藝藞藟藠藡藢藣藤藥藦藧藨藩藪藫藬藭藮藯藰藱藲藳藴藵藶藷藸藹藺藻藼藽藾藿蘀蘁蘂蘃蘄蘅蘆蘇蘈蘉蘊蘋蘌蘍蘎蘏蘐蘑蘒蘓蘔蘕蘖蘗蘘蘙蘚蘛蘜蘝蘞蘟蘠蘡","蘢蘣蘤蘥蘦蘧蘨蘩蘪蘫蘬蘭蘮蘯蘰蘱蘲蘳蘴蘵蘶蘷蘸蘹蘺蘻蘼蘽蘾蘿虀虁虂虃虄虅虆虇虈虉虊虋虌虍虎虏虐虑虒虓虔處虖虗虘虙虚虛虜虝虞號虠虡虢虣虤虥虦虧虨虩虪虫虬虭虮虯虰虱虲虳虴虵虶虷虸虹虺虻虼虽虾虿蚀蚁蚂蚃蚄蚅蚆蚇蚈蚉蚊蚋蚌蚍蚎蚏蚐蚑蚒蚓蚔蚕蚖蚗蚘蚙蚚蚛蚜蚝蚞蚟蚠蚡蚢蚣蚤蚥蚦蚧蚨蚩蚪蚫蚬蚭蚮蚯蚰蚱蚲蚳蚴蚵蚶蚷蚸蚹蚺蚻蚼蚽蚾蚿蛀蛁蛂蛃蛄蛅蛆蛇蛈蛉蛊蛋蛌蛍蛎蛏蛐蛑蛒蛓蛔蛕蛖蛗蛘蛙蛚蛛蛜蛝蛞蛟蛠蛡蛢蛣蛤蛥蛦蛧蛨蛩蛪蛫蛬蛭蛮蛯蛰蛱蛲蛳蛴蛵蛶蛷蛸蛹蛺蛻蛼蛽蛾蛿蜀蜁蜂蜃蜄蜅蜆蜇蜈蜉蜊蜋蜌蜍蜎蜏蜐蜑蜒蜓蜔蜕蜖蜗蜘蜙蜚蜛蜜蜝蜞蜟蜠蜡","蜢蜣蜤蜥蜦蜧蜨蜩蜪蜫蜬蜭蜮蜯蜰蜱蜲蜳蜴蜵蜶蜷蜸蜹蜺蜻蜼蜽蜾蜿蝀蝁蝂蝃蝄蝅蝆蝇蝈蝉蝊蝋蝌蝍蝎蝏蝐蝑蝒蝓蝔蝕蝖蝗蝘蝙蝚蝛蝜蝝蝞蝟蝠蝡蝢蝣蝤蝥蝦蝧蝨蝩蝪蝫蝬蝭蝮蝯蝰蝱蝲蝳蝴蝵蝶蝷蝸蝹蝺蝻蝼蝽蝾蝿螀螁螂螃螄螅螆螇螈螉螊螋螌融螎螏螐螑螒螓螔螕螖螗螘螙螚螛螜螝螞螟螠螡螢螣螤螥螦螧螨螩螪螫螬螭螮螯螰螱螲螳螴螵螶螷螸螹螺螻螼螽螾螿蟀蟁蟂蟃蟄蟅蟆蟇蟈蟉蟊蟋蟌蟍蟎蟏蟐蟑蟒蟓蟔蟕蟖蟗蟘蟙蟚蟛蟜蟝蟞蟟蟠蟡蟢蟣蟤蟥蟦蟧蟨蟩蟪蟫蟬蟭蟮蟯蟰蟱蟲蟳蟴蟵蟶蟷蟸蟹蟺蟻蟼蟽蟾蟿蠀蠁蠂蠃蠄蠅蠆蠇蠈蠉蠊蠋蠌蠍蠎蠏蠐蠑蠒蠓蠔蠕蠖蠗蠘蠙蠚蠛蠜蠝蠞蠟蠠蠡","蠢蠣蠤蠥蠦蠧蠨蠩蠪蠫蠬蠭蠮蠯蠰蠱蠲蠳蠴蠵蠶蠷蠸蠹蠺蠻蠼蠽蠾蠿血衁衂衃衄衅衆衇衈衉衊衋行衍衎衏衐衑衒術衔衕衖街衘衙衚衛衜衝衞衟衠衡衢衣衤补衦衧表衩衪衫衬衭衮衯衰衱衲衳衴衵衶衷衸衹衺衻衼衽衾衿袀袁袂袃袄袅袆袇袈袉袊袋袌袍袎袏袐袑袒袓袔袕袖袗袘袙袚袛袜袝袞袟袠袡袢袣袤袥袦袧袨袩袪被袬袭袮袯袰袱袲袳袴袵袶袷袸袹袺袻袼袽袾袿裀裁裂裃裄装裆裇裈裉裊裋裌裍裎裏裐裑裒裓裔裕裖裗裘裙裚裛補裝裞裟裠裡裢裣裤裥裦裧裨裩裪裫裬裭裮裯裰裱裲裳裴裵裶裷裸裹裺裻裼製裾裿褀褁褂褃褄褅褆複褈褉褊褋褌褍褎褏褐褑褒褓褔褕褖褗褘褙褚褛褜褝褞褟褠褡","褢褣褤褥褦褧褨褩褪褫褬褭褮褯褰褱褲褳褴褵褶褷褸褹褺褻褼褽褾褿襀襁襂襃襄襅襆襇襈襉襊襋襌襍襎襏襐襑襒襓襔襕襖襗襘襙襚襛襜襝襞襟襠襡襢襣襤襥襦襧襨襩襪襫襬襭襮襯襰襱襲襳襴襵襶襷襸襹襺襻襼襽襾西覀要覂覃覄覅覆覇覈覉覊見覌覍覎規覐覑覒覓覔覕視覗覘覙覚覛覜覝覞覟覠覡覢覣覤覥覦覧覨覩親覫覬覭覮覯覰覱覲観覴覵覶覷覸覹覺覻覼覽覾覿觀见观觃规觅视觇览觉觊觋觌觍觎觏觐觑角觓觔觕觖觗觘觙觚觛觜觝觞觟觠觡觢解觤觥触觧觨觩觪觫觬觭觮觯觰觱觲觳觴觵觶觷觸觹觺觻觼觽觾觿言訁訂訃訄訅訆訇計訉訊訋訌訍討訏訐訑訒訓訔訕訖託記訙訚訛訜訝訞訟訠訡","訢訣訤訥訦訧訨訩訪訫訬設訮訯訰許訲訳訴訵訶訷訸訹診註証訽訾訿詀詁詂詃詄詅詆詇詈詉詊詋詌詍詎詏詐詑詒詓詔評詖詗詘詙詚詛詜詝詞詟詠詡詢詣詤詥試詧詨詩詪詫詬詭詮詯詰話該詳詴詵詶詷詸詹詺詻詼詽詾詿誀誁誂誃誄誅誆誇誈誉誊誋誌認誎誏誐誑誒誓誔誕誖誗誘誙誚誛誜誝語誟誠誡誢誣誤誥誦誧誨誩說誫説読誮誯誰誱課誳誴誵誶誷誸誹誺誻誼誽誾調諀諁諂諃諄諅諆談諈諉諊請諌諍諎諏諐諑諒諓諔諕論諗諘諙諚諛諜諝諞諟諠諡諢諣諤諥諦諧諨諩諪諫諬諭諮諯諰諱諲諳諴諵諶諷諸諹諺諻諼諽諾諿謀謁謂謃謄謅謆謇謈謉謊謋謌謍謎謏謐謑謒謓謔謕謖謗謘謙謚講謜謝謞謟謠謡","謢謣謤謥謦謧謨謩謪謫謬謭謮謯謰謱謲謳謴謵謶謷謸謹謺謻謼謽謾謿譀譁譂譃譄譅譆譇譈證譊譋譌譍譎譏譐譑譒譓譔譕譖譗識譙譚譛譜譝譞譟譠譡譢譣譤譥警譧譨譩譪譫譬譭譮譯議譱譲譳譴譵譶護譸譹譺譻譼譽譾譿讀讁讂讃讄讅讆讇讈讉變讋讌讍讎讏讐讑讒讓讔讕讖讗讘讙讚讛讜讝讞讟讠计订讣认讥讦讧讨让讪讫讬训议讯记讱讲讳讴讵讶讷许讹论讻讼讽设访诀证诂诃评诅识诇诈诉诊诋诌词诎诏诐译诒诓诔试诖诗诘诙诚诛诜话诞诟诠诡询诣诤该详诧诨诩诪诫诬语诮误诰诱诲诳说诵诶请诸诹诺读诼诽课诿谀谁谂调谄谅谆谇谈谉谊谋谌谍谎谏谐谑谒谓谔谕谖谗谘谙谚谛谜谝谞谟谠谡","谢谣谤谥谦谧谨谩谪谫谬谭谮谯谰谱谲谳谴谵谶谷谸谹谺谻谼谽谾谿豀豁豂豃豄豅豆豇豈豉豊豋豌豍豎豏豐豑豒豓豔豕豖豗豘豙豚豛豜豝豞豟豠象豢豣豤豥豦豧豨豩豪豫豬豭豮豯豰豱豲豳豴豵豶豷豸豹豺豻豼豽豾豿貀貁貂貃貄貅貆貇貈貉貊貋貌貍貎貏貐貑貒貓貔貕貖貗貘貙貚貛貜貝貞貟負財貢貣貤貥貦貧貨販貪貫責貭貮貯貰貱貲貳貴貵貶買貸貹貺費貼貽貾貿賀賁賂賃賄賅賆資賈賉賊賋賌賍賎賏賐賑賒賓賔賕賖賗賘賙賚賛賜賝賞賟賠賡賢賣賤賥賦賧賨賩質賫賬賭賮賯賰賱賲賳賴賵賶賷賸賹賺賻購賽賾賿贀贁贂贃贄贅贆贇贈贉贊贋贌贍贎贏贐贑贒贓贔贕贖贗贘贙贚贛贜贝贞负贠贡","财责贤败账货质贩贪贫贬购贮贯贰贱贲贳贴贵贶贷贸费贺贻贼贽贾贿赀赁赂赃资赅赆赇赈赉赊赋赌赍赎赏赐赑赒赓赔赕赖赗赘赙赚赛赜赝赞赟赠赡赢赣赤赥赦赧赨赩赪赫赬赭赮赯走赱赲赳赴赵赶起赸赹赺赻赼赽赾赿趀趁趂趃趄超趆趇趈趉越趋趌趍趎趏趐趑趒趓趔趕趖趗趘趙趚趛趜趝趞趟趠趡趢趣趤趥趦趧趨趩趪趫趬趭趮趯趰趱趲足趴趵趶趷趸趹趺趻趼趽趾趿跀跁跂跃跄跅跆跇跈跉跊跋跌跍跎跏跐跑跒跓跔跕跖跗跘跙跚跛跜距跞跟跠跡跢跣跤跥跦跧跨跩跪跫跬跭跮路跰跱跲跳跴践跶跷跸跹跺跻跼跽跾跿踀踁踂踃踄踅踆踇踈踉踊踋踌踍踎踏踐踑踒踓踔踕踖踗踘踙踚踛踜踝踞踟踠踡","踢踣踤踥踦踧踨踩踪踫踬踭踮踯踰踱踲踳踴踵踶踷踸踹踺踻踼踽踾踿蹀蹁蹂蹃蹄蹅蹆蹇蹈蹉蹊蹋蹌蹍蹎蹏蹐蹑蹒蹓蹔蹕蹖蹗蹘蹙蹚蹛蹜蹝蹞蹟蹠蹡蹢蹣蹤蹥蹦蹧蹨蹩蹪蹫蹬蹭蹮蹯蹰蹱蹲蹳蹴蹵蹶蹷蹸蹹蹺蹻蹼蹽蹾蹿躀躁躂躃躄躅躆躇躈躉躊躋躌躍躎躏躐躑躒躓躔躕躖躗躘躙躚躛躜躝躞躟躠躡躢躣躤躥躦躧躨躩躪身躬躭躮躯躰躱躲躳躴躵躶躷躸躹躺躻躼躽躾躿軀軁軂軃軄軅軆軇軈軉車軋軌軍軎軏軐軑軒軓軔軕軖軗軘軙軚軛軜軝軞軟軠軡転軣軤軥軦軧軨軩軪軫軬軭軮軯軰軱軲軳軴軵軶軷軸軹軺軻軼軽軾軿輀輁輂較輄輅輆輇輈載輊輋輌輍輎輏輐輑輒輓輔輕輖輗輘輙輚輛輜輝輞輟輠輡","輢輣輤輥輦輧輨輩輪輫輬輭輮輯輰輱輲輳輴輵輶輷輸輹輺輻輼輽輾輿轀轁轂轃轄轅轆轇轈轉轊轋轌轍轎轏轐轑轒轓轔轕轖轗轘轙轚轛轜轝轞轟轠轡轢轣轤轥车轧轨轩轪轫转轭轮软轰轱轲轳轴轵轶轷轸轹轺轻轼载轾轿辀辁辂较辄辅辆辇辈辉辊辋辌辍辎辏辐辑辒输辔辕辖辗辘辙辚辛辜辝辞辟辠辡辢辣辤辥辦辧辨辩辪辫辬辭辮辯辰辱農辳辴辵辶辷辸边辺辻込辽达辿迀迁迂迃迄迅迆过迈迉迊迋迌迍迎迏运近迒迓返迕迖迗还这迚进远违连迟迠迡迢迣迤迥迦迧迨迩迪迫迬迭迮迯述迱迲迳迴迵迶迷迸迹迺迻迼追迾迿退送适逃逄逅逆逇逈选逊逋逌逍逎透逐逑递逓途逕逖逗逘這通逛逜逝逞速造逡","逢連逤逥逦逧逨逩逪逫逬逭逮逯逰週進逳逴逵逶逷逸逹逺逻逼逽逾逿遀遁遂遃遄遅遆遇遈遉遊運遌遍過遏遐遑遒道達違遖遗遘遙遚遛遜遝遞遟遠遡遢遣遤遥遦遧遨適遪遫遬遭遮遯遰遱遲遳遴遵遶遷選遹遺遻遼遽遾避邀邁邂邃還邅邆邇邈邉邊邋邌邍邎邏邐邑邒邓邔邕邖邗邘邙邚邛邜邝邞邟邠邡邢那邤邥邦邧邨邩邪邫邬邭邮邯邰邱邲邳邴邵邶邷邸邹邺邻邼邽邾邿郀郁郂郃郄郅郆郇郈郉郊郋郌郍郎郏郐郑郒郓郔郕郖郗郘郙郚郛郜郝郞郟郠郡郢郣郤郥郦郧部郩郪郫郬郭郮郯郰郱郲郳郴郵郶郷郸郹郺郻郼都郾郿鄀鄁鄂鄃鄄鄅鄆鄇鄈鄉鄊鄋鄌鄍鄎鄏鄐鄑鄒鄓鄔鄕鄖鄗鄘鄙鄚鄛鄜鄝鄞鄟鄠鄡","鄢鄣鄤鄥鄦鄧鄨鄩鄪鄫鄬鄭鄮鄯鄰鄱鄲鄳鄴鄵鄶鄷鄸鄹鄺鄻鄼鄽鄾鄿酀酁酂酃酄酅酆酇酈酉酊酋酌配酎酏酐酑酒酓酔酕酖酗酘酙酚酛酜酝酞酟酠酡酢酣酤酥酦酧酨酩酪酫酬酭酮酯酰酱酲酳酴酵酶酷酸酹酺酻酼酽酾酿醀醁醂醃醄醅醆醇醈醉醊醋醌醍醎醏醐醑醒醓醔醕醖醗醘醙醚醛醜醝醞醟醠醡醢醣醤醥醦醧醨醩醪醫醬醭醮醯醰醱醲醳醴醵醶醷醸醹醺醻醼醽醾醿釀釁釂釃釄釅釆采釈釉释釋里重野量釐金釒釓釔釕釖釗釘釙釚釛釜針釞釟釠釡釢釣釤釥釦釧釨釩釪釫釬釭釮釯釰釱釲釳釴釵釶釷釸釹釺釻釼釽釾釿鈀鈁鈂鈃鈄鈅鈆鈇鈈鈉鈊鈋鈌鈍鈎鈏鈐鈑鈒鈓鈔鈕鈖鈗鈘鈙鈚鈛鈜鈝鈞鈟鈠鈡","鈢鈣鈤鈥鈦鈧鈨鈩鈪鈫鈬鈭鈮鈯鈰鈱鈲鈳鈴鈵鈶鈷鈸鈹鈺鈻鈼鈽鈾鈿鉀鉁鉂鉃鉄鉅鉆鉇鉈鉉鉊鉋鉌鉍鉎鉏鉐鉑鉒鉓鉔鉕鉖鉗鉘鉙鉚鉛鉜鉝鉞鉟鉠鉡鉢鉣鉤鉥鉦鉧鉨鉩鉪鉫鉬鉭鉮鉯鉰鉱鉲鉳鉴鉵鉶鉷鉸鉹鉺鉻鉼鉽鉾鉿銀銁銂銃銄銅銆銇銈銉銊銋銌銍銎銏銐銑銒銓銔銕銖銗銘銙銚銛銜銝銞銟銠銡銢銣銤銥銦銧銨銩銪銫銬銭銮銯銰銱銲銳銴銵銶銷銸銹銺銻銼銽銾銿鋀鋁鋂鋃鋄鋅鋆鋇鋈鋉鋊鋋鋌鋍鋎鋏鋐鋑鋒鋓鋔鋕鋖鋗鋘鋙鋚鋛鋜鋝鋞鋟鋠鋡鋢鋣鋤鋥鋦鋧鋨鋩鋪鋫鋬鋭鋮鋯鋰鋱鋲鋳鋴鋵鋶鋷鋸鋹鋺鋻鋼鋽鋾鋿錀錁錂錃錄錅錆錇錈錉錊錋錌錍錎錏錐錑錒錓錔錕錖錗錘錙錚錛錜錝錞錟錠錡","錢錣錤錥錦錧錨錩錪錫錬錭錮錯錰錱録錳錴錵錶錷錸錹錺錻錼錽錾錿鍀鍁鍂鍃鍄鍅鍆鍇鍈鍉鍊鍋鍌鍍鍎鍏鍐鍑鍒鍓鍔鍕鍖鍗鍘鍙鍚鍛鍜鍝鍞鍟鍠鍡鍢鍣鍤鍥鍦鍧鍨鍩鍪鍫鍬鍭鍮鍯鍰鍱鍲鍳鍴鍵鍶鍷鍸鍹鍺鍻鍼鍽鍾鍿鎀鎁鎂鎃鎄鎅鎆鎇鎈鎉鎊鎋鎌鎍鎎鎏鎐鎑鎒鎓鎔鎕鎖鎗鎘鎙鎚鎛鎜鎝鎞鎟鎠鎡鎢鎣鎤鎥鎦鎧鎨鎩鎪鎫鎬鎭鎮鎯鎰鎱鎲鎳鎴鎵鎶鎷鎸鎹鎺鎻鎼鎽鎾鎿鏀鏁鏂鏃鏄鏅鏆鏇鏈鏉鏊鏋鏌鏍鏎鏏鏐鏑鏒鏓鏔鏕鏖鏗鏘鏙鏚鏛鏜鏝鏞鏟鏠鏡鏢鏣鏤鏥鏦鏧鏨鏩鏪鏫鏬鏭鏮鏯鏰鏱鏲鏳鏴鏵鏶鏷鏸鏹鏺鏻鏼鏽鏾鏿鐀鐁鐂鐃鐄鐅鐆鐇鐈鐉鐊鐋鐌鐍鐎鐏鐐鐑鐒鐓鐔鐕鐖鐗鐘鐙鐚鐛鐜鐝鐞鐟鐠鐡","鐢鐣鐤鐥鐦鐧鐨鐩鐪鐫鐬鐭鐮鐯鐰鐱鐲鐳鐴鐵鐶鐷鐸鐹鐺鐻鐼鐽鐾鐿鑀鑁鑂鑃鑄鑅鑆鑇鑈鑉鑊鑋鑌鑍鑎鑏鑐鑑鑒鑓鑔鑕鑖鑗鑘鑙鑚鑛鑜鑝鑞鑟鑠鑡鑢鑣鑤鑥鑦鑧鑨鑩鑪鑫鑬鑭鑮鑯鑰鑱鑲鑳鑴鑵鑶鑷鑸鑹鑺鑻鑼鑽鑾鑿钀钁钂钃钄钅钆钇针钉钊钋钌钍钎钏钐钑钒钓钔钕钖钗钘钙钚钛钜钝钞钟钠钡钢钣钤钥钦钧钨钩钪钫钬钭钮钯钰钱钲钳钴钵钶钷钸钹钺钻钼钽钾钿铀铁铂铃铄铅铆铇铈铉铊铋铌铍铎铏铐铑铒铓铔铕铖铗铘铙铚铛铜铝铞铟铠铡铢铣铤铥铦铧铨铩铪铫铬铭铮铯铰铱铲铳铴铵银铷铸铹铺铻铼铽链铿销锁锂锃锄锅锆锇锈锉锊锋锌锍锎锏锐锑锒锓锔锕锖锗锘错锚锛锜锝锞锟锠锡","锢锣锤锥锦锧锨锩锪锫锬锭键锯锰锱锲锳锴锵锶锷锸锹锺锻锼锽锾锿镀镁镂镃镄镅镆镇镈镉镊镋镌镍镎镏镐镑镒镓镔镕镖镗镘镙镚镛镜镝镞镟镠镡镢镣镤镥镦镧镨镩镪镫镬镭镮镯镰镱镲镳镴镵镶長镸镹镺镻镼镽镾长門閁閂閃閄閅閆閇閈閉閊開閌閍閎閏閐閑閒間閔閕閖閗閘閙閚閛閜閝閞閟閠閡関閣閤閥閦閧閨閩閪閫閬閭閮閯閰閱閲閳閴閵閶閷閸閹閺閻閼閽閾閿闀闁闂闃闄闅闆闇闈闉闊闋闌闍闎闏闐闑闒闓闔闕闖闗闘闙闚闛關闝闞闟闠闡闢闣闤闥闦闧门闩闪闫闬闭问闯闰闱闲闳间闵闶闷闸闹闺闻闼闽闾闿阀阁阂阃阄阅阆阇阈阉阊阋阌阍阎阏阐阑阒阓阔阕阖阗阘阙阚阛阜阝阞队阠阡","阢阣阤阥阦阧阨阩阪阫阬阭阮阯阰阱防阳阴阵阶阷阸阹阺阻阼阽阾阿陀陁陂陃附际陆陇陈陉陊陋陌降陎陏限陑陒陓陔陕陖陗陘陙陚陛陜陝陞陟陠陡院陣除陥陦陧陨险陪陫陬陭陮陯陰陱陲陳陴陵陶陷陸陹険陻陼陽陾陿隀隁隂隃隄隅隆隇隈隉隊隋隌隍階随隐隑隒隓隔隕隖隗隘隙隚際障隝隞隟隠隡隢隣隤隥隦隧隨隩險隫隬隭隮隯隰隱隲隳隴隵隶隷隸隹隺隻隼隽难隿雀雁雂雃雄雅集雇雈雉雊雋雌雍雎雏雐雑雒雓雔雕雖雗雘雙雚雛雜雝雞雟雠雡離難雤雥雦雧雨雩雪雫雬雭雮雯雰雱雲雳雴雵零雷雸雹雺電雼雽雾雿需霁霂霃霄霅霆震霈霉霊霋霌霍霎霏霐霑霒霓霔霕霖霗霘霙霚霛霜霝霞霟霠霡","霢霣霤霥霦霧霨霩霪霫霬霭霮霯霰霱露霳霴霵霶霷霸霹霺霻霼霽霾霿靀靁靂靃靄靅靆靇靈靉靊靋靌靍靎靏靐靑青靓靔靕靖靗靘静靚靛靜靝非靟靠靡面靣靤靥靦靧靨革靪靫靬靭靮靯靰靱靲靳靴靵靶靷靸靹靺靻靼靽靾靿鞀鞁鞂鞃鞄鞅鞆鞇鞈鞉鞊鞋鞌鞍鞎鞏鞐鞑鞒鞓鞔鞕鞖鞗鞘鞙鞚鞛鞜鞝鞞鞟鞠鞡鞢鞣鞤鞥鞦鞧鞨鞩鞪鞫鞬鞭鞮鞯鞰鞱鞲鞳鞴鞵鞶鞷鞸鞹鞺鞻鞼鞽鞾鞿韀韁韂韃韄韅韆韇韈韉韊韋韌韍韎韏韐韑韒韓韔韕韖韗韘韙韚韛韜韝韞韟韠韡韢韣韤韥韦韧韨韩韪韫韬韭韮韯韰韱韲音韴韵韶韷韸韹韺韻韼韽韾響頀頁頂頃頄項順頇須頉頊頋頌頍頎頏預頑頒頓頔頕頖頗領頙頚頛頜頝頞頟頠頡","頢頣頤頥頦頧頨頩頪頫頬頭頮頯頰頱頲頳頴頵頶頷頸頹頺頻頼頽頾頿顀顁顂顃顄顅顆顇顈顉顊顋題額顎顏顐顑顒顓顔顕顖顗願顙顚顛顜顝類顟顠顡顢顣顤顥顦顧顨顩顪顫顬顭顮顯顰顱顲顳顴页顶顷顸项顺须顼顽顾顿颀颁颂颃预颅领颇颈颉颊颋颌颍颎颏颐频颒颓颔颕颖颗题颙颚颛颜额颞颟颠颡颢颣颤颥颦颧風颩颪颫颬颭颮颯颰颱颲颳颴颵颶颷颸颹颺颻颼颽颾颿飀飁飂飃飄飅飆飇飈飉飊飋飌飍风飏飐飑飒飓飔飕飖飗飘飙飚飛飜飝飞食飠飡飢飣飤飥飦飧飨飩飪飫飬飭飮飯飰飱飲飳飴飵飶飷飸飹飺飻飼飽飾飿餀餁餂餃餄餅餆餇餈餉養餋餌餍餎餏餐餑餒餓餔餕餖餗餘餙餚餛餜餝餞餟餠餡","餢餣餤餥餦餧館餩餪餫餬餭餮餯餰餱餲餳餴餵餶餷餸餹餺餻餼餽餾餿饀饁饂饃饄饅饆饇饈饉饊饋饌饍饎饏饐饑饒饓饔饕饖饗饘饙饚饛饜饝饞饟饠饡饢饣饤饥饦饧饨饩饪饫饬饭饮饯饰饱饲饳饴饵饶饷饸饹饺饻饼饽饾饿馀馁馂馃馄馅馆馇馈馉馊馋馌馍馎馏馐馑馒馓馔馕首馗馘香馚馛馜馝馞馟馠馡馢馣馤馥馦馧馨馩馪馫馬馭馮馯馰馱馲馳馴馵馶馷馸馹馺馻馼馽馾馿駀駁駂駃駄駅駆駇駈駉駊駋駌駍駎駏駐駑駒駓駔駕駖駗駘駙駚駛駜駝駞駟駠駡駢駣駤駥駦駧駨駩駪駫駬駭駮駯駰駱駲駳駴駵駶駷駸駹駺駻駼駽駾駿騀騁騂騃騄騅騆騇騈騉騊騋騌騍騎騏騐騑騒験騔騕騖騗騘騙騚騛騜騝騞騟騠騡","騢騣騤騥騦騧騨騩騪騫騬騭騮騯騰騱騲騳騴騵騶騷騸騹騺騻騼騽騾騿驀驁驂驃驄驅驆驇驈驉驊驋驌驍驎驏驐驑驒驓驔驕驖驗驘驙驚驛驜驝驞驟驠驡驢驣驤驥驦驧驨驩驪驫马驭驮驯驰驱驲驳驴驵驶驷驸驹驺驻驼驽驾驿骀骁骂骃骄骅骆骇骈骉骊骋验骍骎骏骐骑骒骓骔骕骖骗骘骙骚骛骜骝骞骟骠骡骢骣骤骥骦骧骨骩骪骫骬骭骮骯骰骱骲骳骴骵骶骷骸骹骺骻骼骽骾骿髀髁髂髃髄髅髆髇髈髉髊髋髌髍髎髏髐髑髒髓體髕髖髗高髙髚髛髜髝髞髟髠髡髢髣髤髥髦髧髨髩髪髫髬髭髮髯髰髱髲髳髴髵髶髷髸髹髺髻髼髽髾髿鬀鬁鬂鬃鬄鬅鬆鬇鬈鬉鬊鬋鬌鬍鬎鬏鬐鬑鬒鬓鬔鬕鬖鬗鬘鬙鬚鬛鬜鬝鬞鬟鬠鬡","鬢鬣鬤鬥鬦鬧鬨鬩鬪鬫鬬鬭鬮鬯鬰鬱鬲鬳鬴鬵鬶鬷鬸鬹鬺鬻鬼鬽鬾鬿魀魁魂魃魄魅魆魇魈魉魊魋魌魍魎魏魐魑魒魓魔魕魖魗魘魙魚魛魜魝魞魟魠魡魢魣魤魥魦魧魨魩魪魫魬魭魮魯魰魱魲魳魴魵魶魷魸魹魺魻魼魽魾魿鮀鮁鮂鮃鮄鮅鮆鮇鮈鮉鮊鮋鮌鮍鮎鮏鮐鮑鮒鮓鮔鮕鮖鮗鮘鮙鮚鮛鮜鮝鮞鮟鮠鮡鮢鮣鮤鮥鮦鮧鮨鮩鮪鮫鮬鮭鮮鮯鮰鮱鮲鮳鮴鮵鮶鮷鮸鮹鮺鮻鮼鮽鮾鮿鯀鯁鯂鯃鯄鯅鯆鯇鯈鯉鯊鯋鯌鯍鯎鯏鯐鯑鯒鯓鯔鯕鯖鯗鯘鯙鯚鯛鯜鯝鯞鯟鯠鯡鯢鯣鯤鯥鯦鯧鯨鯩鯪鯫鯬鯭鯮鯯鯰鯱鯲鯳鯴鯵鯶鯷鯸鯹鯺鯻鯼鯽鯾鯿鰀鰁鰂鰃鰄鰅鰆鰇鰈鰉鰊鰋鰌鰍鰎鰏鰐鰑鰒鰓鰔鰕鰖鰗鰘鰙鰚鰛鰜鰝鰞鰟鰠鰡","鰢鰣鰤鰥鰦鰧鰨鰩鰪鰫鰬鰭鰮鰯鰰鰱鰲鰳鰴鰵鰶鰷鰸鰹鰺鰻鰼鰽鰾鰿鱀鱁鱂鱃鱄鱅鱆鱇鱈鱉鱊鱋鱌鱍鱎鱏鱐鱑鱒鱓鱔鱕鱖鱗鱘鱙鱚鱛鱜鱝鱞鱟鱠鱡鱢鱣鱤鱥鱦鱧鱨鱩鱪鱫鱬鱭鱮鱯鱰鱱鱲鱳鱴鱵鱶鱷鱸鱹鱺鱻鱼鱽鱾鱿鲀鲁鲂鲃鲄鲅鲆鲇鲈鲉鲊鲋鲌鲍鲎鲏鲐鲑鲒鲓鲔鲕鲖鲗鲘鲙鲚鲛鲜鲝鲞鲟鲠鲡鲢鲣鲤鲥鲦鲧鲨鲩鲪鲫鲬鲭鲮鲯鲰鲱鲲鲳鲴鲵鲶鲷鲸鲹鲺鲻鲼鲽鲾鲿鳀鳁鳂鳃鳄鳅鳆鳇鳈鳉鳊鳋鳌鳍鳎鳏鳐鳑鳒鳓鳔鳕鳖鳗鳘鳙鳚鳛鳜鳝鳞鳟鳠鳡鳢鳣鳤鳥鳦鳧鳨鳩鳪鳫鳬鳭鳮鳯鳰鳱鳲鳳鳴鳵鳶鳷鳸鳹鳺鳻鳼鳽鳾鳿鴀鴁鴂鴃鴄鴅鴆鴇鴈鴉鴊鴋鴌鴍鴎鴏鴐鴑鴒鴓鴔鴕鴖鴗鴘鴙鴚鴛鴜鴝鴞鴟鴠鴡","鴢鴣鴤鴥鴦鴧鴨鴩鴪鴫鴬鴭鴮鴯鴰鴱鴲鴳鴴鴵鴶鴷鴸鴹鴺鴻鴼鴽鴾鴿鵀鵁鵂鵃鵄鵅鵆鵇鵈鵉鵊鵋鵌鵍鵎鵏鵐鵑鵒鵓鵔鵕鵖鵗鵘鵙鵚鵛鵜鵝鵞鵟鵠鵡鵢鵣鵤鵥鵦鵧鵨鵩鵪鵫鵬鵭鵮鵯鵰鵱鵲鵳鵴鵵鵶鵷鵸鵹鵺鵻鵼鵽鵾鵿鶀鶁鶂鶃鶄鶅鶆鶇鶈鶉鶊鶋鶌鶍鶎鶏鶐鶑鶒鶓鶔鶕鶖鶗鶘鶙鶚鶛鶜鶝鶞鶟鶠鶡鶢鶣鶤鶥鶦鶧鶨鶩鶪鶫鶬鶭鶮鶯鶰鶱鶲鶳鶴鶵鶶鶷鶸鶹鶺鶻鶼鶽鶾鶿鷀鷁鷂鷃鷄鷅鷆鷇鷈鷉鷊鷋鷌鷍鷎鷏鷐鷑鷒鷓鷔鷕鷖鷗鷘鷙鷚鷛鷜鷝鷞鷟鷠鷡鷢鷣鷤鷥鷦鷧鷨鷩鷪鷫鷬鷭鷮鷯鷰鷱鷲鷳鷴鷵鷶鷷鷸鷹鷺鷻鷼鷽鷾鷿鸀鸁鸂鸃鸄鸅鸆鸇鸈鸉鸊鸋鸌鸍鸎鸏鸐鸑鸒鸓鸔鸕鸖鸗鸘鸙鸚鸛鸜鸝鸞鸟鸠鸡","鸢鸣鸤鸥鸦鸧鸨鸩鸪鸫鸬鸭鸮鸯鸰鸱鸲鸳鸴鸵鸶鸷鸸鸹鸺鸻鸼鸽鸾鸿鹀鹁鹂鹃鹄鹅鹆鹇鹈鹉鹊鹋鹌鹍鹎鹏鹐鹑鹒鹓鹔鹕鹖鹗鹘鹙鹚鹛鹜鹝鹞鹟鹠鹡鹢鹣鹤鹥鹦鹧鹨鹩鹪鹫鹬鹭鹮鹯鹰鹱鹲鹳鹴鹵鹶鹷鹸鹹鹺鹻鹼鹽鹾鹿麀麁麂麃麄麅麆麇麈麉麊麋麌麍麎麏麐麑麒麓麔麕麖麗麘麙麚麛麜麝麞麟麠麡麢麣麤麥麦麧麨麩麪麫麬麭麮麯麰麱麲麳麴麵麶麷麸麹麺麻麼麽麾麿黀黁黂黃黄黅黆黇黈黉黊黋黌黍黎黏黐黑黒黓黔黕黖黗默黙黚黛黜黝點黟黠黡黢黣黤黥黦黧黨黩黪黫黬黭黮黯黰黱黲黳黴黵黶黷黸黹黺黻黼黽黾黿鼀鼁鼂鼃鼄鼅鼆鼇鼈鼉鼊鼋鼌鼍鼎鼏鼐鼑鼒鼓鼔鼕鼖鼗鼘鼙鼚鼛鼜鼝鼞鼟鼠鼡","鼢鼣鼤鼥鼦鼧鼨鼩鼪鼫鼬鼭鼮鼯鼰鼱鼲鼳鼴鼵鼶鼷鼸鼹鼺鼻鼼鼽鼾鼿齀齁齂齃齄齅齆齇齈齉齊齋齌齍齎齏齐齑齒齓齔齕齖齗齘齙齚齛齜齝齞齟齠齡齢齣齤齥齦齧齨齩齪齫齬齭齮齯齰齱齲齳齴齵齶齷齸齹齺齻齼齽齾齿龀龁龂龃龄龅龆龇龈龉龊龋龌龍龎龏龐龑龒龓龔龕龖龗龘龙龚龛龜龝龞龟龠龡龢龣龤龥龦龧龨龩龪龫龬龭龮龯龰龱龲龳龴龵龶龷龸龹龺龻龼龽龾龿鿀鿁鿂鿃鿄鿅鿆鿇鿈鿉鿊鿋鿌鿍鿎鿏鿐鿑鿒鿓鿔鿕鿖鿗鿘鿙鿚鿛鿜鿝鿞鿟鿠鿡鿢鿣鿤鿥鿦鿧鿨鿩鿪鿫鿬鿭鿮鿯鿰鿱鿲鿳鿴鿵鿶鿷鿸鿹鿺鿻鿼鿽鿾鿿ꀀꀁꀂꀃꀄꀅꀆꀇꀈꀉꀊꀋꀌꀍꀎꀏꀐꀑꀒꀓꀔꀕꀖꀗꀘꀙꀚꀛꀜꀝꀞꀟꀠꀡ","ꀢꀣꀤꀥꀦꀧꀨꀩꀪꀫꀬꀭꀮꀯꀰꀱꀲꀳꀴꀵꀶꀷꀸꀹꀺꀻꀼꀽꀾꀿꁀꁁꁂꁃꁄꁅꁆꁇꁈꁉꁊꁋꁌꁍꁎꁏꁐꁑꁒꁓꁔꁕꁖꁗꁘꁙꁚꁛꁜꁝꁞꁟꁠꁡꁢꁣꁤꁥꁦꁧꁨꁩꁪꁫꁬꁭꁮꁯꁰꁱꁲꁳꁴꁵꁶꁷꁸꁹꁺꁻꁼꁽꁾꁿꂀꂁꂂꂃꂄꂅꂆꂇꂈꂉꂊꂋꂌꂍꂎꂏꂐꂑꂒꂓꂔꂕꂖꂗꂘꂙꂚꂛꂜꂝꂞꂟꂠꂡꂢꂣꂤꂥꂦꂧꂨꂩꂪꂫꂬꂭꂮꂯꂰꂱꂲꂳꂴꂵꂶꂷꂸꂹꂺꂻꂼꂽꂾꂿꃀꃁꃂꃃꃄꃅꃆꃇꃈꃉꃊꃋꃌꃍꃎꃏꃐꃑꃒꃓꃔꃕꃖꃗꃘꃙꃚꃛꃜꃝꃞꃟꃠꃡꃢꃣꃤꃥꃦꃧꃨꃩꃪꃫꃬꃭꃮꃯꃰꃱꃲꃳꃴꃵꃶꃷꃸꃹꃺꃻꃼꃽꃾꃿꄀꄁꄂꄃꄄꄅꄆꄇꄈꄉꄊꄋꄌꄍꄎꄏꄐꄑꄒꄓꄔꄕꄖꄗꄘꄙꄚꄛꄜꄝꄞꄟꄠꄡ","ꄢꄣꄤꄥꄦꄧꄨꄩꄪꄫꄬꄭꄮꄯꄰꄱꄲꄳꄴꄵꄶꄷꄸꄹꄺꄻꄼꄽꄾꄿꅀꅁꅂꅃꅄꅅꅆꅇꅈꅉꅊꅋꅌꅍꅎꅏꅐꅑꅒꅓꅔꅕꅖꅗꅘꅙꅚꅛꅜꅝꅞꅟꅠꅡꅢꅣꅤꅥꅦꅧꅨꅩꅪꅫꅬꅭꅮꅯꅰꅱꅲꅳꅴꅵꅶꅷꅸꅹꅺꅻꅼꅽꅾꅿꆀꆁꆂꆃꆄꆅꆆꆇꆈꆉꆊꆋꆌꆍꆎꆏꆐꆑꆒꆓꆔꆕꆖꆗꆘꆙꆚꆛꆜꆝꆞꆟꆠꆡꆢꆣꆤꆥꆦꆧꆨꆩꆪꆫꆬꆭꆮꆯꆰꆱꆲꆳꆴꆵꆶꆷꆸꆹꆺꆻꆼꆽꆾꆿꇀꇁꇂꇃꇄꇅꇆꇇꇈꇉꇊꇋꇌꇍꇎꇏꇐꇑꇒꇓꇔꇕꇖꇗꇘꇙꇚꇛꇜꇝꇞꇟꇠꇡꇢꇣꇤꇥꇦꇧꇨꇩꇪꇫꇬꇭꇮꇯꇰꇱꇲꇳꇴꇵꇶꇷꇸꇹꇺꇻꇼꇽꇾꇿꈀꈁꈂꈃꈄꈅꈆꈇꈈꈉꈊꈋꈌꈍꈎꈏꈐꈑꈒꈓꈔꈕꈖꈗꈘꈙꈚꈛꈜꈝꈞꈟꈠꈡ","ꈢꈣꈤꈥꈦꈧꈨꈩꈪꈫꈬꈭꈮꈯꈰꈱꈲꈳꈴꈵꈶꈷꈸꈹꈺꈻꈼꈽꈾꈿꉀꉁꉂꉃꉄꉅꉆꉇꉈꉉꉊꉋꉌꉍꉎꉏꉐꉑꉒꉓꉔꉕꉖꉗꉘꉙꉚꉛꉜꉝꉞꉟꉠꉡꉢꉣꉤꉥꉦꉧꉨꉩꉪꉫꉬꉭꉮꉯꉰꉱꉲꉳꉴꉵꉶꉷꉸꉹꉺꉻꉼꉽꉾꉿꊀꊁꊂꊃꊄꊅꊆꊇꊈꊉꊊꊋꊌꊍꊎꊏꊐꊑꊒꊓꊔꊕꊖꊗꊘꊙꊚꊛꊜꊝꊞꊟꊠꊡꊢꊣꊤꊥꊦꊧꊨꊩꊪꊫꊬꊭꊮꊯꊰꊱꊲꊳꊴꊵꊶꊷꊸꊹꊺꊻꊼꊽꊾꊿꋀꋁꋂꋃꋄꋅꋆꋇꋈꋉꋊꋋꋌꋍꋎꋏꋐꋑꋒꋓꋔꋕꋖꋗꋘꋙꋚꋛꋜꋝꋞꋟꋠꋡꋢꋣꋤꋥꋦꋧꋨꋩꋪꋫꋬꋭꋮꋯꋰꋱꋲꋳꋴꋵꋶꋷꋸꋹꋺꋻꋼꋽꋾꋿꌀꌁꌂꌃꌄꌅꌆꌇꌈꌉꌊꌋꌌꌍꌎꌏꌐꌑꌒꌓꌔꌕꌖꌗꌘꌙꌚꌛꌜꌝꌞꌟꌠꌡ","ꌢꌣꌤꌥꌦꌧꌨꌩꌪꌫꌬꌭꌮꌯꌰꌱꌲꌳꌴꌵꌶꌷꌸꌹꌺꌻꌼꌽꌾꌿꍀꍁꍂꍃꍄꍅꍆꍇꍈꍉꍊꍋꍌꍍꍎꍏꍐꍑꍒꍓꍔꍕꍖꍗꍘꍙꍚꍛꍜꍝꍞꍟꍠꍡꍢꍣꍤꍥꍦꍧꍨꍩꍪꍫꍬꍭꍮꍯꍰꍱꍲꍳꍴꍵꍶꍷꍸꍹꍺꍻꍼꍽꍾꍿꎀꎁꎂꎃꎄꎅꎆꎇꎈꎉꎊꎋꎌꎍꎎꎏꎐꎑꎒꎓꎔꎕꎖꎗꎘꎙꎚꎛꎜꎝꎞꎟꎠꎡꎢꎣꎤꎥꎦꎧꎨꎩꎪꎫꎬꎭꎮꎯꎰꎱꎲꎳꎴꎵꎶꎷꎸꎹꎺꎻꎼꎽꎾꎿꏀꏁꏂꏃꏄꏅꏆꏇꏈꏉꏊꏋꏌꏍꏎꏏꏐꏑꏒꏓꏔꏕꏖꏗꏘꏙꏚꏛꏜꏝꏞꏟꏠꏡꏢꏣꏤꏥꏦꏧꏨꏩꏪꏫꏬꏭꏮꏯꏰꏱꏲꏳꏴꏵꏶꏷꏸꏹꏺꏻꏼꏽꏾꏿꐀꐁꐂꐃꐄꐅꐆꐇꐈꐉꐊꐋꐌꐍꐎꐏꐐꐑꐒꐓꐔꐕꐖꐗꐘꐙꐚꐛꐜꐝꐞꐟꐠꐡ","ꐢꐣꐤꐥꐦꐧꐨꐩꐪꐫꐬꐭꐮꐯꐰꐱꐲꐳꐴꐵꐶꐷꐸꐹꐺꐻꐼꐽꐾꐿꑀꑁꑂꑃꑄꑅꑆꑇꑈꑉꑊꑋꑌꑍꑎꑏꑐꑑꑒꑓꑔꑕꑖꑗꑘꑙꑚꑛꑜꑝꑞꑟꑠꑡꑢꑣꑤꑥꑦꑧꑨꑩꑪꑫꑬꑭꑮꑯꑰꑱꑲꑳꑴꑵꑶꑷꑸꑹꑺꑻꑼꑽꑾꑿꒀꒁꒂꒃꒄꒅꒆꒇꒈꒉꒊꒋꒌ꒍꒎꒏꒐꒑꒒꒓꒔꒕꒖꒗꒘꒙꒚꒛꒜꒝꒞꒟꒠꒡꒢꒣꒤꒥꒦꒧꒨꒩꒪꒫꒬꒭꒮꒯꒰꒱꒲꒳꒴꒵꒶꒷꒸꒹꒺꒻꒼꒽꒾꒿꓀꓁꓂꓃꓄꓅꓆꓇꓈꓉꓊꓋꓌꓍꓎꓏ꓐꓑꓒꓓꓔꓕꓖꓗꓘꓙꓚꓛꓜꓝꓞꓟꓠꓡꓢꓣꓤꓥꓦꓧꓨꓩꓪꓫꓬꓭꓮꓯꓰꓱꓲꓳꓴꓵꓶꓷꓸꓹꓺꓻꓼꓽ꓾꓿ꔀꔁꔂꔃꔄꔅꔆꔇꔈꔉꔊꔋꔌꔍꔎꔏꔐꔑꔒꔓꔔꔕꔖꔗꔘꔙꔚꔛꔜꔝꔞꔟꔠꔡ","ꔢꔣꔤꔥꔦꔧꔨꔩꔪꔫꔬꔭꔮꔯꔰꔱꔲꔳꔴꔵꔶꔷꔸꔹꔺꔻꔼꔽꔾꔿꕀꕁꕂꕃꕄꕅꕆꕇꕈꕉꕊꕋꕌꕍꕎꕏꕐꕑꕒꕓꕔꕕꕖꕗꕘꕙꕚꕛꕜꕝꕞꕟꕠꕡꕢꕣꕤꕥꕦꕧꕨꕩꕪꕫꕬꕭꕮꕯꕰꕱꕲꕳꕴꕵꕶꕷꕸꕹꕺꕻꕼꕽꕾꕿꖀꖁꖂꖃꖄꖅꖆꖇꖈꖉꖊꖋꖌꖍꖎꖏꖐꖑꖒꖓꖔꖕꖖꖗꖘꖙꖚꖛꖜꖝꖞꖟꖠꖡꖢꖣꖤꖥꖦꖧꖨꖩꖪꖫꖬꖭꖮꖯꖰꖱꖲꖳꖴꖵꖶꖷꖸꖹꖺꖻꖼꖽꖾꖿꗀꗁꗂꗃꗄꗅꗆꗇꗈꗉꗊꗋꗌꗍꗎꗏꗐꗑꗒꗓꗔꗕꗖꗗꗘꗙꗚꗛꗜꗝꗞꗟꗠꗡꗢꗣꗤꗥꗦꗧꗨꗩꗪꗫꗬꗭꗮꗯꗰꗱꗲꗳꗴꗵꗶꗷꗸꗹꗺꗻꗼꗽꗾꗿꘀꘁꘂꘃꘄꘅꘆꘇꘈꘉꘊꘋꘌ꘍꘎꘏ꘐꘑꘒꘓꘔꘕꘖꘗꘘꘙꘚꘛꘜꘝꘞꘟ꘠꘡","꘢꘣꘤꘥꘦꘧꘨꘩ꘪꘫ꘬꘭꘮꘯꘰꘱꘲꘳꘴꘵꘶꘷꘸꘹꘺꘻꘼꘽꘾꘿ꙀꙁꙂꙃꙄꙅꙆꙇꙈꙉꙊꙋꙌꙍꙎꙏꙐꙑꙒꙓꙔꙕꙖꙗꙘꙙꙚꙛꙜꙝꙞꙟꙠꙡꙢꙣꙤꙥꙦꙧꙨꙩꙪꙫꙬꙭꙮ꙯꙰꙱꙲꙳ꙴꙵꙶꙷꙸꙹꙺꙻ꙼꙽꙾ꙿꚀꚁꚂꚃꚄꚅꚆꚇꚈꚉꚊꚋꚌꚍꚎꚏꚐꚑꚒꚓꚔꚕꚖꚗꚘꚙꚚꚛꚜꚝꚞꚟꚠꚡꚢꚣꚤꚥꚦꚧꚨꚩꚪꚫꚬꚭꚮꚯꚰꚱꚲꚳꚴꚵꚶꚷꚸꚹꚺꚻꚼꚽꚾꚿꛀꛁꛂꛃꛄꛅꛆꛇꛈꛉꛊꛋꛌꛍꛎꛏꛐꛑꛒꛓꛔꛕꛖꛗꛘꛙꛚꛛꛜꛝꛞꛟꛠꛡꛢꛣꛤꛥꛦꛧꛨꛩꛪꛫꛬꛭꛮꛯ꛰꛱꛲꛳꛴꛵꛶꛷꛸꛹꛺꛻꛼꛽꛾꛿꜀꜁꜂꜃꜄꜅꜆꜇꜈꜉꜊꜋꜌꜍꜎꜏꜐꜑꜒꜓꜔꜕꜖ꜗꜘꜙꜚꜛꜜꜝꜞꜟ꜠꜡","ꜢꜣꜤꜥꜦꜧꜨꜩꜪꜫꜬꜭꜮꜯꜰꜱꜲꜳꜴꜵꜶꜷꜸꜹꜺꜻꜼꜽꜾꜿꝀꝁꝂꝃꝄꝅꝆꝇꝈꝉꝊꝋꝌꝍꝎꝏꝐꝑꝒꝓꝔꝕꝖꝗꝘꝙꝚꝛꝜꝝꝞꝟꝠꝡꝢꝣꝤꝥꝦꝧꝨꝩꝪꝫꝬꝭꝮꝯꝰꝱꝲꝳꝴꝵꝶꝷꝸꝹꝺꝻꝼꝽꝾꝿꞀꞁꞂꞃꞄꞅꞆꞇꞈ꞉꞊ꞋꞌꞍꞎꞏꞐꞑꞒꞓꞔꞕꞖꞗꞘꞙꞚꞛꞜꞝꞞꞟꞠꞡꞢꞣꞤꞥꞦꞧꞨꞩꞪꞫꞬꞭꞮꞯꞰꞱꞲꞳꞴꞵꞶꞷꞸꞹꞺꞻꞼꞽꞾꞿꟀꟁꟂꟃꟄꟅꟆꟇꟈꟉꟊꟋꟌꟍ꟎꟏Ꟑꟑ꟒ꟓ꟔ꟕꟖꟗꟘꟙꟚꟛꟜ꟝꟞꟟꟠꟡꟢꟣꟤꟥꟦꟧꟨꟩꟪꟫꟬꟭꟮꟯꟰꟱ꟲꟳꟴꟵꟶꟷꟸꟹꟺꟻꟼꟽꟾꟿꠀꠁꠂꠃꠄꠅ꠆ꠇꠈꠉꠊꠋꠌꠍꠎꠏꠐꠑꠒꠓꠔꠕꠖꠗꠘꠙꠚꠛꠜꠝꠞꠟꠠꠡ","ꠢꠣꠤꠥꠦꠧ꠨꠩꠪꠫꠬꠭꠮꠯꠰꠱꠲꠳꠴꠵꠶꠷꠸꠹꠺꠻꠼꠽꠾꠿ꡀꡁꡂꡃꡄꡅꡆꡇꡈꡉꡊꡋꡌꡍꡎꡏꡐꡑꡒꡓꡔꡕꡖꡗꡘꡙꡚꡛꡜꡝꡞꡟꡠꡡꡢꡣꡤꡥꡦꡧꡨꡩꡪꡫꡬꡭꡮꡯꡰꡱꡲꡳ꡴꡵꡶꡷꡸꡹꡺꡻꡼꡽꡾꡿ꢀꢁꢂꢃꢄꢅꢆꢇꢈꢉꢊꢋꢌꢍꢎꢏꢐꢑꢒꢓꢔꢕꢖꢗꢘꢙꢚꢛꢜꢝꢞꢟꢠꢡꢢꢣꢤꢥꢦꢧꢨꢩꢪꢫꢬꢭꢮꢯꢰꢱꢲꢳꢴꢵꢶꢷꢸꢹꢺꢻꢼꢽꢾꢿꣀꣁꣂꣃ꣄ꣅ꣆꣇꣈꣉꣊꣋꣌꣍꣎꣏꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙꣚꣛꣜꣝꣞꣟꣠꣡꣢꣣꣤꣥꣦꣧꣨꣩꣪꣫꣬꣭꣮꣯꣰꣱ꣲꣳꣴꣵꣶꣷ꣸꣹꣺ꣻ꣼ꣽꣾꣿ꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉ꤊꤋꤌꤍꤎꤏꤐꤑꤒꤓꤔꤕꤖꤗꤘꤙꤚꤛꤜꤝꤞꤟꤠꤡ","ꤢꤣꤤꤥꤦꤧꤨꤩꤪ꤫꤬꤭꤮꤯ꤰꤱꤲꤳꤴꤵꤶꤷꤸꤹꤺꤻꤼꤽꤾꤿꥀꥁꥂꥃꥄꥅꥆꥇꥈꥉꥊꥋꥌꥍꥎꥏꥐꥑꥒ꥓꥔꥕꥖꥗꥘꥙꥚꥛꥜꥝꥞꥟ꥠꥡꥢꥣꥤꥥꥦꥧꥨꥩꥪꥫꥬꥭꥮꥯꥰꥱꥲꥳꥴꥵꥶꥷꥸꥹꥺꥻꥼ꥽꥾꥿ꦀꦁꦂꦃꦄꦅꦆꦇꦈꦉꦊꦋꦌꦍꦎꦏꦐꦑꦒꦓꦔꦕꦖꦗꦘꦙꦚꦛꦜꦝꦞꦟꦠꦡꦢꦣꦤꦥꦦꦧꦨꦩꦪꦫꦬꦭꦮꦯꦰꦱꦲ꦳ꦴꦵꦶꦷꦸꦹꦺꦻꦼꦽꦾꦿ꧀꧁꧂꧃꧄꧅꧆꧇꧈꧉꧊꧋꧌꧍꧎ꧏ꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙꧚꧛꧜꧝꧞꧟ꧠꧡꧢꧣꧤꧥꧦꧧꧨꧩꧪꧫꧬꧭꧮꧯ꧰꧱꧲꧳꧴꧵꧶꧷꧸꧹ꧺꧻꧼꧽꧾ꧿ꨀꨁꨂꨃꨄꨅꨆꨇꨈꨉꨊꨋꨌꨍꨎꨏꨐꨑꨒꨓꨔꨕꨖꨗꨘꨙꨚꨛꨜꨝꨞꨟꨠꨡ","ꨢꨣꨤꨥꨦꨧꨨꨩꨪꨫꨬꨭꨮꨯꨰꨱꨲꨳꨴꨵꨶ꨷꨸꨹꨺꨻꨼꨽꨾꨿ꩀꩁꩂꩃꩄꩅꩆꩇꩈꩉꩊꩋꩌꩍ꩎꩏꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙꩚꩛꩜꩝꩞꩟ꩠꩡꩢꩣꩤꩥꩦꩧꩨꩩꩪꩫꩬꩭꩮꩯꩰꩱꩲꩳꩴꩵꩶ꩷꩸꩹ꩺꩻꩼꩽꩾꩿꪀꪁꪂꪃꪄꪅꪆꪇꪈꪉꪊꪋꪌꪍꪎꪏꪐꪑꪒꪓꪔꪕꪖꪗꪘꪙꪚꪛꪜꪝꪞꪟꪠꪡꪢꪣꪤꪥꪦꪧꪨꪩꪪꪫꪬꪭꪮꪯꪰꪱꪴꪲꪳꪵꪶꪷꪸꪹꪺꪻꪼꪽꪾ꪿ꫀ꫁ꫂ꫃꫄꫅꫆꫇꫈꫉꫊꫋꫌꫍꫎꫏꫐꫑꫒꫓꫔꫕꫖꫗꫘꫙꫚ꫛꫜꫝ꫞꫟ꫠꫡꫢꫣꫤꫥꫦꫧꫨꫩꫪꫫꫬꫭꫮꫯ꫰꫱ꫲꫳꫴꫵ꫶꫷꫸꫹꫺꫻꫼꫽꫾꫿꬀ꬁꬂꬃꬄꬅꬆ꬇꬈ꬉꬊꬋꬌꬍꬎ꬏꬐ꬑꬒꬓꬔꬕꬖ꬗꬘꬙꬚꬛꬜꬝꬞꬟ꬠꬡ","ꬢꬣꬤꬥꬦ꬧ꬨꬩꬪꬫꬬꬭꬮ꬯ꬰꬱꬲꬳꬴꬵꬶꬷꬸꬹꬺꬻꬼꬽꬾꬿꭀꭁꭂꭃꭄꭅꭆꭇꭈꭉꭊꭋꭌꭍꭎꭏꭐꭑꭒꭓꭔꭕꭖꭗꭘꭙꭚ꭛ꭜꭝꭞꭟꭠꭡꭢꭣꭤꭥꭦꭧꭨꭩ꭪꭫꭬꭭꭮꭯ꭰꭱꭲꭳꭴꭵꭶꭷꭸꭹꭺꭻꭼꭽꭾꭿꮀꮁꮂꮃꮄꮅꮆꮇꮈꮉꮊꮋꮌꮍꮎꮏꮐꮑꮒꮓꮔꮕꮖꮗꮘꮙꮚꮛꮜꮝꮞꮟꮠꮡꮢꮣꮤꮥꮦꮧꮨꮩꮪꮫꮬꮭꮮꮯꮰꮱꮲꮳꮴꮵꮶꮷꮸꮹꮺꮻꮼꮽꮾꮿꯀꯁꯂꯃꯄꯅꯆꯇꯈꯉꯊꯋꯌꯍꯎꯏꯐꯑꯒꯓꯔꯕꯖꯗꯘꯙꯚꯛꯜꯝꯞꯟꯠꯡꯢꯣꯤꯥꯦꯧꯨꯩꯪ꯫꯬꯭꯮꯯꯰꯱꯲꯳꯴꯵꯶꯷꯸꯹꯺꯻꯼꯽꯾꯿가각갂갃간갅갆갇갈갉갊갋갌갍갎갏감갑값갓갔강갖갗갘같갚갛개객갞갟갠갡","갢갣갤갥갦갧갨갩갪갫갬갭갮갯갰갱갲갳갴갵갶갷갸갹갺갻갼갽갾갿걀걁걂걃걄걅걆걇걈걉걊걋걌걍걎걏걐걑걒걓걔걕걖걗걘걙걚걛걜걝걞걟걠걡걢걣걤걥걦걧걨걩걪걫걬걭걮걯거걱걲걳건걵걶걷걸걹걺걻걼걽걾걿검겁겂것겄겅겆겇겈겉겊겋게겍겎겏겐겑겒겓겔겕겖겗겘겙겚겛겜겝겞겟겠겡겢겣겤겥겦겧겨격겪겫견겭겮겯결겱겲겳겴겵겶겷겸겹겺겻겼경겾겿곀곁곂곃계곅곆곇곈곉곊곋곌곍곎곏곐곑곒곓곔곕곖곗곘곙곚곛곜곝곞곟고곡곢곣곤곥곦곧골곩곪곫곬곭곮곯곰곱곲곳곴공곶곷곸곹곺곻과곽곾곿관괁괂괃괄괅괆괇괈괉괊괋괌괍괎괏괐광괒괓괔괕괖괗괘괙괚괛괜괝괞괟괠괡","괢괣괤괥괦괧괨괩괪괫괬괭괮괯괰괱괲괳괴괵괶괷괸괹괺괻괼괽괾괿굀굁굂굃굄굅굆굇굈굉굊굋굌굍굎굏교굑굒굓굔굕굖굗굘굙굚굛굜굝굞굟굠굡굢굣굤굥굦굧굨굩굪굫구국굮굯군굱굲굳굴굵굶굷굸굹굺굻굼굽굾굿궀궁궂궃궄궅궆궇궈궉궊궋권궍궎궏궐궑궒궓궔궕궖궗궘궙궚궛궜궝궞궟궠궡궢궣궤궥궦궧궨궩궪궫궬궭궮궯궰궱궲궳궴궵궶궷궸궹궺궻궼궽궾궿귀귁귂귃귄귅귆귇귈귉귊귋귌귍귎귏귐귑귒귓귔귕귖귗귘귙귚귛규귝귞귟균귡귢귣귤귥귦귧귨귩귪귫귬귭귮귯귰귱귲귳귴귵귶귷그극귺귻근귽귾귿글긁긂긃긄긅긆긇금급긊긋긌긍긎긏긐긑긒긓긔긕긖긗긘긙긚긛긜긝긞긟긠긡","긢긣긤긥긦긧긨긩긪긫긬긭긮긯기긱긲긳긴긵긶긷길긹긺긻긼긽긾긿김깁깂깃깄깅깆깇깈깉깊깋까깍깎깏깐깑깒깓깔깕깖깗깘깙깚깛깜깝깞깟깠깡깢깣깤깥깦깧깨깩깪깫깬깭깮깯깰깱깲깳깴깵깶깷깸깹깺깻깼깽깾깿꺀꺁꺂꺃꺄꺅꺆꺇꺈꺉꺊꺋꺌꺍꺎꺏꺐꺑꺒꺓꺔꺕꺖꺗꺘꺙꺚꺛꺜꺝꺞꺟꺠꺡꺢꺣꺤꺥꺦꺧꺨꺩꺪꺫꺬꺭꺮꺯꺰꺱꺲꺳꺴꺵꺶꺷꺸꺹꺺꺻꺼꺽꺾꺿껀껁껂껃껄껅껆껇껈껉껊껋껌껍껎껏껐껑껒껓껔껕껖껗께껙껚껛껜껝껞껟껠껡껢껣껤껥껦껧껨껩껪껫껬껭껮껯껰껱껲껳껴껵껶껷껸껹껺껻껼껽껾껿꼀꼁꼂꼃꼄꼅꼆꼇꼈꼉꼊꼋꼌꼍꼎꼏꼐꼑꼒꼓꼔꼕꼖꼗꼘꼙꼚꼛꼜꼝꼞꼟꼠꼡","꼢꼣꼤꼥꼦꼧꼨꼩꼪꼫꼬꼭꼮꼯꼰꼱꼲꼳꼴꼵꼶꼷꼸꼹꼺꼻꼼꼽꼾꼿꽀꽁꽂꽃꽄꽅꽆꽇꽈꽉꽊꽋꽌꽍꽎꽏꽐꽑꽒꽓꽔꽕꽖꽗꽘꽙꽚꽛꽜꽝꽞꽟꽠꽡꽢꽣꽤꽥꽦꽧꽨꽩꽪꽫꽬꽭꽮꽯꽰꽱꽲꽳꽴꽵꽶꽷꽸꽹꽺꽻꽼꽽꽾꽿꾀꾁꾂꾃꾄꾅꾆꾇꾈꾉꾊꾋꾌꾍꾎꾏꾐꾑꾒꾓꾔꾕꾖꾗꾘꾙꾚꾛꾜꾝꾞꾟꾠꾡꾢꾣꾤꾥꾦꾧꾨꾩꾪꾫꾬꾭꾮꾯꾰꾱꾲꾳꾴꾵꾶꾷꾸꾹꾺꾻꾼꾽꾾꾿꿀꿁꿂꿃꿄꿅꿆꿇꿈꿉꿊꿋꿌꿍꿎꿏꿐꿑꿒꿓꿔꿕꿖꿗꿘꿙꿚꿛꿜꿝꿞꿟꿠꿡꿢꿣꿤꿥꿦꿧꿨꿩꿪꿫꿬꿭꿮꿯꿰꿱꿲꿳꿴꿵꿶꿷꿸꿹꿺꿻꿼꿽꿾꿿뀀뀁뀂뀃뀄뀅뀆뀇뀈뀉뀊뀋뀌뀍뀎뀏뀐뀑뀒뀓뀔뀕뀖뀗뀘뀙뀚뀛뀜뀝뀞뀟뀠뀡","뀢뀣뀤뀥뀦뀧뀨뀩뀪뀫뀬뀭뀮뀯뀰뀱뀲뀳뀴뀵뀶뀷뀸뀹뀺뀻뀼뀽뀾뀿끀끁끂끃끄끅끆끇끈끉끊끋끌끍끎끏끐끑끒끓끔끕끖끗끘끙끚끛끜끝끞끟끠끡끢끣끤끥끦끧끨끩끪끫끬끭끮끯끰끱끲끳끴끵끶끷끸끹끺끻끼끽끾끿낀낁낂낃낄낅낆낇낈낉낊낋낌낍낎낏낐낑낒낓낔낕낖낗나낙낚낛난낝낞낟날낡낢낣낤낥낦낧남납낪낫났낭낮낯낰낱낲낳내낵낶낷낸낹낺낻낼낽낾낿냀냁냂냃냄냅냆냇냈냉냊냋냌냍냎냏냐냑냒냓냔냕냖냗냘냙냚냛냜냝냞냟냠냡냢냣냤냥냦냧냨냩냪냫냬냭냮냯냰냱냲냳냴냵냶냷냸냹냺냻냼냽냾냿넀넁넂넃넄넅넆넇너넉넊넋넌넍넎넏널넑넒넓넔넕넖넗넘넙넚넛넜넝넞넟넠넡","넢넣네넥넦넧넨넩넪넫넬넭넮넯넰넱넲넳넴넵넶넷넸넹넺넻넼넽넾넿녀녁녂녃년녅녆녇녈녉녊녋녌녍녎녏념녑녒녓녔녕녖녗녘녙녚녛녜녝녞녟녠녡녢녣녤녥녦녧녨녩녪녫녬녭녮녯녰녱녲녳녴녵녶녷노녹녺녻논녽녾녿놀놁놂놃놄놅놆놇놈놉놊놋놌농놎놏놐놑높놓놔놕놖놗놘놙놚놛놜놝놞놟놠놡놢놣놤놥놦놧놨놩놪놫놬놭놮놯놰놱놲놳놴놵놶놷놸놹놺놻놼놽놾놿뇀뇁뇂뇃뇄뇅뇆뇇뇈뇉뇊뇋뇌뇍뇎뇏뇐뇑뇒뇓뇔뇕뇖뇗뇘뇙뇚뇛뇜뇝뇞뇟뇠뇡뇢뇣뇤뇥뇦뇧뇨뇩뇪뇫뇬뇭뇮뇯뇰뇱뇲뇳뇴뇵뇶뇷뇸뇹뇺뇻뇼뇽뇾뇿눀눁눂눃누눅눆눇눈눉눊눋눌눍눎눏눐눑눒눓눔눕눖눗눘눙눚눛눜눝눞눟눠눡","눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄늅늆늇늈늉늊늋늌늍늎늏느늑늒늓는늕늖늗늘늙늚늛늜늝늞늟늠늡늢늣늤능늦늧늨늩늪늫늬늭늮늯늰늱늲늳늴늵늶늷늸늹늺늻늼늽늾늿닀닁닂닃닄닅닆닇니닉닊닋닌닍닎닏닐닑닒닓닔닕닖닗님닙닚닛닜닝닞닟닠닡닢닣다닥닦닧단닩닪닫달닭닮닯닰닱닲닳담답닶닷닸당닺닻닼닽닾닿대댁댂댃댄댅댆댇댈댉댊댋댌댍댎댏댐댑댒댓댔댕댖댗댘댙댚댛댜댝댞댟댠댡","댢댣댤댥댦댧댨댩댪댫댬댭댮댯댰댱댲댳댴댵댶댷댸댹댺댻댼댽댾댿덀덁덂덃덄덅덆덇덈덉덊덋덌덍덎덏덐덑덒덓더덕덖덗던덙덚덛덜덝덞덟덠덡덢덣덤덥덦덧덨덩덪덫덬덭덮덯데덱덲덳덴덵덶덷델덹덺덻덼덽덾덿뎀뎁뎂뎃뎄뎅뎆뎇뎈뎉뎊뎋뎌뎍뎎뎏뎐뎑뎒뎓뎔뎕뎖뎗뎘뎙뎚뎛뎜뎝뎞뎟뎠뎡뎢뎣뎤뎥뎦뎧뎨뎩뎪뎫뎬뎭뎮뎯뎰뎱뎲뎳뎴뎵뎶뎷뎸뎹뎺뎻뎼뎽뎾뎿돀돁돂돃도독돆돇돈돉돊돋돌돍돎돏돐돑돒돓돔돕돖돗돘동돚돛돜돝돞돟돠돡돢돣돤돥돦돧돨돩돪돫돬돭돮돯돰돱돲돳돴돵돶돷돸돹돺돻돼돽돾돿됀됁됂됃됄됅됆됇됈됉됊됋됌됍됎됏됐됑됒됓됔됕됖됗되됙됚됛된됝됞됟될됡","됢됣됤됥됦됧됨됩됪됫됬됭됮됯됰됱됲됳됴됵됶됷됸됹됺됻됼됽됾됿둀둁둂둃둄둅둆둇둈둉둊둋둌둍둎둏두둑둒둓둔둕둖둗둘둙둚둛둜둝둞둟둠둡둢둣둤둥둦둧둨둩둪둫둬둭둮둯둰둱둲둳둴둵둶둷둸둹둺둻둼둽둾둿뒀뒁뒂뒃뒄뒅뒆뒇뒈뒉뒊뒋뒌뒍뒎뒏뒐뒑뒒뒓뒔뒕뒖뒗뒘뒙뒚뒛뒜뒝뒞뒟뒠뒡뒢뒣뒤뒥뒦뒧뒨뒩뒪뒫뒬뒭뒮뒯뒰뒱뒲뒳뒴뒵뒶뒷뒸뒹뒺뒻뒼뒽뒾뒿듀듁듂듃듄듅듆듇듈듉듊듋듌듍듎듏듐듑듒듓듔듕듖듗듘듙듚듛드득듞듟든듡듢듣들듥듦듧듨듩듪듫듬듭듮듯듰등듲듳듴듵듶듷듸듹듺듻듼듽듾듿딀딁딂딃딄딅딆딇딈딉딊딋딌딍딎딏딐딑딒딓디딕딖딗딘딙딚딛딜딝딞딟딠딡","딢딣딤딥딦딧딨딩딪딫딬딭딮딯따딱딲딳딴딵딶딷딸딹딺딻딼딽딾딿땀땁땂땃땄땅땆땇땈땉땊땋때땍땎땏땐땑땒땓땔땕땖땗땘땙땚땛땜땝땞땟땠땡땢땣땤땥땦땧땨땩땪땫땬땭땮땯땰땱땲땳땴땵땶땷땸땹땺땻땼땽땾땿떀떁떂떃떄떅떆떇떈떉떊떋떌떍떎떏떐떑떒떓떔떕떖떗떘떙떚떛떜떝떞떟떠떡떢떣떤떥떦떧떨떩떪떫떬떭떮떯떰떱떲떳떴떵떶떷떸떹떺떻떼떽떾떿뗀뗁뗂뗃뗄뗅뗆뗇뗈뗉뗊뗋뗌뗍뗎뗏뗐뗑뗒뗓뗔뗕뗖뗗뗘뗙뗚뗛뗜뗝뗞뗟뗠뗡뗢뗣뗤뗥뗦뗧뗨뗩뗪뗫뗬뗭뗮뗯뗰뗱뗲뗳뗴뗵뗶뗷뗸뗹뗺뗻뗼뗽뗾뗿똀똁똂똃똄똅똆똇똈똉똊똋똌똍똎똏또똑똒똓똔똕똖똗똘똙똚똛똜똝똞똟똠똡","똢똣똤똥똦똧똨똩똪똫똬똭똮똯똰똱똲똳똴똵똶똷똸똹똺똻똼똽똾똿뙀뙁뙂뙃뙄뙅뙆뙇뙈뙉뙊뙋뙌뙍뙎뙏뙐뙑뙒뙓뙔뙕뙖뙗뙘뙙뙚뙛뙜뙝뙞뙟뙠뙡뙢뙣뙤뙥뙦뙧뙨뙩뙪뙫뙬뙭뙮뙯뙰뙱뙲뙳뙴뙵뙶뙷뙸뙹뙺뙻뙼뙽뙾뙿뚀뚁뚂뚃뚄뚅뚆뚇뚈뚉뚊뚋뚌뚍뚎뚏뚐뚑뚒뚓뚔뚕뚖뚗뚘뚙뚚뚛뚜뚝뚞뚟뚠뚡뚢뚣뚤뚥뚦뚧뚨뚩뚪뚫뚬뚭뚮뚯뚰뚱뚲뚳뚴뚵뚶뚷뚸뚹뚺뚻뚼뚽뚾뚿뛀뛁뛂뛃뛄뛅뛆뛇뛈뛉뛊뛋뛌뛍뛎뛏뛐뛑뛒뛓뛔뛕뛖뛗뛘뛙뛚뛛뛜뛝뛞뛟뛠뛡뛢뛣뛤뛥뛦뛧뛨뛩뛪뛫뛬뛭뛮뛯뛰뛱뛲뛳뛴뛵뛶뛷뛸뛹뛺뛻뛼뛽뛾뛿뜀뜁뜂뜃뜄뜅뜆뜇뜈뜉뜊뜋뜌뜍뜎뜏뜐뜑뜒뜓뜔뜕뜖뜗뜘뜙뜚뜛뜜뜝뜞뜟뜠뜡","뜢뜣뜤뜥뜦뜧뜨뜩뜪뜫뜬뜭뜮뜯뜰뜱뜲뜳뜴뜵뜶뜷뜸뜹뜺뜻뜼뜽뜾뜿띀띁띂띃띄띅띆띇띈띉띊띋띌띍띎띏띐띑띒띓띔띕띖띗띘띙띚띛띜띝띞띟띠띡띢띣띤띥띦띧띨띩띪띫띬띭띮띯띰띱띲띳띴띵띶띷띸띹띺띻라락띾띿란랁랂랃랄랅랆랇랈랉랊랋람랍랎랏랐랑랒랓랔랕랖랗래랙랚랛랜랝랞랟랠랡랢랣랤랥랦랧램랩랪랫랬랭랮랯랰랱랲랳랴략랶랷랸랹랺랻랼랽랾랿럀럁럂럃럄럅럆럇럈량럊럋럌럍럎럏럐럑럒럓럔럕럖럗럘럙럚럛럜럝럞럟럠럡럢럣럤럥럦럧럨럩럪럫러럭럮럯런럱럲럳럴럵럶럷럸럹럺럻럼럽럾럿렀렁렂렃렄렅렆렇레렉렊렋렌렍렎렏렐렑렒렓렔렕렖렗렘렙렚렛렜렝렞렟렠렡","렢렣려력렦렧련렩렪렫렬렭렮렯렰렱렲렳렴렵렶렷렸령렺렻렼렽렾렿례롁롂롃롄롅롆롇롈롉롊롋롌롍롎롏롐롑롒롓롔롕롖롗롘롙롚롛로록롞롟론롡롢롣롤롥롦롧롨롩롪롫롬롭롮롯롰롱롲롳롴롵롶롷롸롹롺롻롼롽롾롿뢀뢁뢂뢃뢄뢅뢆뢇뢈뢉뢊뢋뢌뢍뢎뢏뢐뢑뢒뢓뢔뢕뢖뢗뢘뢙뢚뢛뢜뢝뢞뢟뢠뢡뢢뢣뢤뢥뢦뢧뢨뢩뢪뢫뢬뢭뢮뢯뢰뢱뢲뢳뢴뢵뢶뢷뢸뢹뢺뢻뢼뢽뢾뢿룀룁룂룃룄룅룆룇룈룉룊룋료룍룎룏룐룑룒룓룔룕룖룗룘룙룚룛룜룝룞룟룠룡룢룣룤룥룦룧루룩룪룫룬룭룮룯룰룱룲룳룴룵룶룷룸룹룺룻룼룽룾룿뤀뤁뤂뤃뤄뤅뤆뤇뤈뤉뤊뤋뤌뤍뤎뤏뤐뤑뤒뤓뤔뤕뤖뤗뤘뤙뤚뤛뤜뤝뤞뤟뤠뤡","뤢뤣뤤뤥뤦뤧뤨뤩뤪뤫뤬뤭뤮뤯뤰뤱뤲뤳뤴뤵뤶뤷뤸뤹뤺뤻뤼뤽뤾뤿륀륁륂륃륄륅륆륇륈륉륊륋륌륍륎륏륐륑륒륓륔륕륖륗류륙륚륛륜륝륞륟률륡륢륣륤륥륦륧륨륩륪륫륬륭륮륯륰륱륲륳르륵륶륷른륹륺륻를륽륾륿릀릁릂릃름릅릆릇릈릉릊릋릌릍릎릏릐릑릒릓릔릕릖릗릘릙릚릛릜릝릞릟릠릡릢릣릤릥릦릧릨릩릪릫리릭릮릯린릱릲릳릴릵릶릷릸릹릺릻림립릾릿맀링맂맃맄맅맆맇마막맊맋만맍많맏말맑맒맓맔맕맖맗맘맙맚맛맜망맞맟맠맡맢맣매맥맦맧맨맩맪맫맬맭맮맯맰맱맲맳맴맵맶맷맸맹맺맻맼맽맾맿먀먁먂먃먄먅먆먇먈먉먊먋먌먍먎먏먐먑먒먓먔먕먖먗먘먙먚먛먜먝먞먟먠먡","먢먣먤먥먦먧먨먩먪먫먬먭먮먯먰먱먲먳먴먵먶먷머먹먺먻먼먽먾먿멀멁멂멃멄멅멆멇멈멉멊멋멌멍멎멏멐멑멒멓메멕멖멗멘멙멚멛멜멝멞멟멠멡멢멣멤멥멦멧멨멩멪멫멬멭멮멯며멱멲멳면멵멶멷멸멹멺멻멼멽멾멿몀몁몂몃몄명몆몇몈몉몊몋몌몍몎몏몐몑몒몓몔몕몖몗몘몙몚몛몜몝몞몟몠몡몢몣몤몥몦몧모목몪몫몬몭몮몯몰몱몲몳몴몵몶몷몸몹몺못몼몽몾몿뫀뫁뫂뫃뫄뫅뫆뫇뫈뫉뫊뫋뫌뫍뫎뫏뫐뫑뫒뫓뫔뫕뫖뫗뫘뫙뫚뫛뫜뫝뫞뫟뫠뫡뫢뫣뫤뫥뫦뫧뫨뫩뫪뫫뫬뫭뫮뫯뫰뫱뫲뫳뫴뫵뫶뫷뫸뫹뫺뫻뫼뫽뫾뫿묀묁묂묃묄묅묆묇묈묉묊묋묌묍묎묏묐묑묒묓묔묕묖묗묘묙묚묛묜묝묞묟묠묡","묢묣묤묥묦묧묨묩묪묫묬묭묮묯묰묱묲묳무묵묶묷문묹묺묻물묽묾묿뭀뭁뭂뭃뭄뭅뭆뭇뭈뭉뭊뭋뭌뭍뭎뭏뭐뭑뭒뭓뭔뭕뭖뭗뭘뭙뭚뭛뭜뭝뭞뭟뭠뭡뭢뭣뭤뭥뭦뭧뭨뭩뭪뭫뭬뭭뭮뭯뭰뭱뭲뭳뭴뭵뭶뭷뭸뭹뭺뭻뭼뭽뭾뭿뮀뮁뮂뮃뮄뮅뮆뮇뮈뮉뮊뮋뮌뮍뮎뮏뮐뮑뮒뮓뮔뮕뮖뮗뮘뮙뮚뮛뮜뮝뮞뮟뮠뮡뮢뮣뮤뮥뮦뮧뮨뮩뮪뮫뮬뮭뮮뮯뮰뮱뮲뮳뮴뮵뮶뮷뮸뮹뮺뮻뮼뮽뮾뮿므믁믂믃믄믅믆믇믈믉믊믋믌믍믎믏믐믑믒믓믔믕믖믗믘믙믚믛믜믝믞믟믠믡믢믣믤믥믦믧믨믩믪믫믬믭믮믯믰믱믲믳믴믵믶믷미믹믺믻민믽믾믿밀밁밂밃밄밅밆밇밈밉밊밋밌밍밎및밐밑밒밓바박밖밗반밙밚받발밝밞밟밠밡","밢밣밤밥밦밧밨방밪밫밬밭밮밯배백밲밳밴밵밶밷밸밹밺밻밼밽밾밿뱀뱁뱂뱃뱄뱅뱆뱇뱈뱉뱊뱋뱌뱍뱎뱏뱐뱑뱒뱓뱔뱕뱖뱗뱘뱙뱚뱛뱜뱝뱞뱟뱠뱡뱢뱣뱤뱥뱦뱧뱨뱩뱪뱫뱬뱭뱮뱯뱰뱱뱲뱳뱴뱵뱶뱷뱸뱹뱺뱻뱼뱽뱾뱿벀벁벂벃버벅벆벇번벉벊벋벌벍벎벏벐벑벒벓범법벖벗벘벙벚벛벜벝벞벟베벡벢벣벤벥벦벧벨벩벪벫벬벭벮벯벰벱벲벳벴벵벶벷벸벹벺벻벼벽벾벿변볁볂볃별볅볆볇볈볉볊볋볌볍볎볏볐병볒볓볔볕볖볗볘볙볚볛볜볝볞볟볠볡볢볣볤볥볦볧볨볩볪볫볬볭볮볯볰볱볲볳보복볶볷본볹볺볻볼볽볾볿봀봁봂봃봄봅봆봇봈봉봊봋봌봍봎봏봐봑봒봓봔봕봖봗봘봙봚봛봜봝봞봟봠봡","봢봣봤봥봦봧봨봩봪봫봬봭봮봯봰봱봲봳봴봵봶봷봸봹봺봻봼봽봾봿뵀뵁뵂뵃뵄뵅뵆뵇뵈뵉뵊뵋뵌뵍뵎뵏뵐뵑뵒뵓뵔뵕뵖뵗뵘뵙뵚뵛뵜뵝뵞뵟뵠뵡뵢뵣뵤뵥뵦뵧뵨뵩뵪뵫뵬뵭뵮뵯뵰뵱뵲뵳뵴뵵뵶뵷뵸뵹뵺뵻뵼뵽뵾뵿부북붂붃분붅붆붇불붉붊붋붌붍붎붏붐붑붒붓붔붕붖붗붘붙붚붛붜붝붞붟붠붡붢붣붤붥붦붧붨붩붪붫붬붭붮붯붰붱붲붳붴붵붶붷붸붹붺붻붼붽붾붿뷀뷁뷂뷃뷄뷅뷆뷇뷈뷉뷊뷋뷌뷍뷎뷏뷐뷑뷒뷓뷔뷕뷖뷗뷘뷙뷚뷛뷜뷝뷞뷟뷠뷡뷢뷣뷤뷥뷦뷧뷨뷩뷪뷫뷬뷭뷮뷯뷰뷱뷲뷳뷴뷵뷶뷷뷸뷹뷺뷻뷼뷽뷾뷿븀븁븂븃븄븅븆븇븈븉븊븋브븍븎븏븐븑븒븓블븕븖븗븘븙븚븛븜븝븞븟븠븡","븢븣븤븥븦븧븨븩븪븫븬븭븮븯븰븱븲븳븴븵븶븷븸븹븺븻븼븽븾븿빀빁빂빃비빅빆빇빈빉빊빋빌빍빎빏빐빑빒빓빔빕빖빗빘빙빚빛빜빝빞빟빠빡빢빣빤빥빦빧빨빩빪빫빬빭빮빯빰빱빲빳빴빵빶빷빸빹빺빻빼빽빾빿뺀뺁뺂뺃뺄뺅뺆뺇뺈뺉뺊뺋뺌뺍뺎뺏뺐뺑뺒뺓뺔뺕뺖뺗뺘뺙뺚뺛뺜뺝뺞뺟뺠뺡뺢뺣뺤뺥뺦뺧뺨뺩뺪뺫뺬뺭뺮뺯뺰뺱뺲뺳뺴뺵뺶뺷뺸뺹뺺뺻뺼뺽뺾뺿뻀뻁뻂뻃뻄뻅뻆뻇뻈뻉뻊뻋뻌뻍뻎뻏뻐뻑뻒뻓뻔뻕뻖뻗뻘뻙뻚뻛뻜뻝뻞뻟뻠뻡뻢뻣뻤뻥뻦뻧뻨뻩뻪뻫뻬뻭뻮뻯뻰뻱뻲뻳뻴뻵뻶뻷뻸뻹뻺뻻뻼뻽뻾뻿뼀뼁뼂뼃뼄뼅뼆뼇뼈뼉뼊뼋뼌뼍뼎뼏뼐뼑뼒뼓뼔뼕뼖뼗뼘뼙뼚뼛뼜뼝뼞뼟뼠뼡","뼢뼣뼤뼥뼦뼧뼨뼩뼪뼫뼬뼭뼮뼯뼰뼱뼲뼳뼴뼵뼶뼷뼸뼹뼺뼻뼼뼽뼾뼿뽀뽁뽂뽃뽄뽅뽆뽇뽈뽉뽊뽋뽌뽍뽎뽏뽐뽑뽒뽓뽔뽕뽖뽗뽘뽙뽚뽛뽜뽝뽞뽟뽠뽡뽢뽣뽤뽥뽦뽧뽨뽩뽪뽫뽬뽭뽮뽯뽰뽱뽲뽳뽴뽵뽶뽷뽸뽹뽺뽻뽼뽽뽾뽿뾀뾁뾂뾃뾄뾅뾆뾇뾈뾉뾊뾋뾌뾍뾎뾏뾐뾑뾒뾓뾔뾕뾖뾗뾘뾙뾚뾛뾜뾝뾞뾟뾠뾡뾢뾣뾤뾥뾦뾧뾨뾩뾪뾫뾬뾭뾮뾯뾰뾱뾲뾳뾴뾵뾶뾷뾸뾹뾺뾻뾼뾽뾾뾿뿀뿁뿂뿃뿄뿅뿆뿇뿈뿉뿊뿋뿌뿍뿎뿏뿐뿑뿒뿓뿔뿕뿖뿗뿘뿙뿚뿛뿜뿝뿞뿟뿠뿡뿢뿣뿤뿥뿦뿧뿨뿩뿪뿫뿬뿭뿮뿯뿰뿱뿲뿳뿴뿵뿶뿷뿸뿹뿺뿻뿼뿽뿾뿿쀀쀁쀂쀃쀄쀅쀆쀇쀈쀉쀊쀋쀌쀍쀎쀏쀐쀑쀒쀓쀔쀕쀖쀗쀘쀙쀚쀛쀜쀝쀞쀟쀠쀡","쀢쀣쀤쀥쀦쀧쀨쀩쀪쀫쀬쀭쀮쀯쀰쀱쀲쀳쀴쀵쀶쀷쀸쀹쀺쀻쀼쀽쀾쀿쁀쁁쁂쁃쁄쁅쁆쁇쁈쁉쁊쁋쁌쁍쁎쁏쁐쁑쁒쁓쁔쁕쁖쁗쁘쁙쁚쁛쁜쁝쁞쁟쁠쁡쁢쁣쁤쁥쁦쁧쁨쁩쁪쁫쁬쁭쁮쁯쁰쁱쁲쁳쁴쁵쁶쁷쁸쁹쁺쁻쁼쁽쁾쁿삀삁삂삃삄삅삆삇삈삉삊삋삌삍삎삏삐삑삒삓삔삕삖삗삘삙삚삛삜삝삞삟삠삡삢삣삤삥삦삧삨삩삪삫사삭삮삯산삱삲삳살삵삶삷삸삹삺삻삼삽삾삿샀상샂샃샄샅샆샇새색샊샋샌샍샎샏샐샑샒샓샔샕샖샗샘샙샚샛샜생샞샟샠샡샢샣샤샥샦샧샨샩샪샫샬샭샮샯샰샱샲샳샴샵샶샷샸샹샺샻샼샽샾샿섀섁섂섃섄섅섆섇섈섉섊섋섌섍섎섏섐섑섒섓섔섕섖섗섘섙섚섛서석섞섟선섡","섢섣설섥섦섧섨섩섪섫섬섭섮섯섰성섲섳섴섵섶섷세섹섺섻센섽섾섿셀셁셂셃셄셅셆셇셈셉셊셋셌셍셎셏셐셑셒셓셔셕셖셗션셙셚셛셜셝셞셟셠셡셢셣셤셥셦셧셨셩셪셫셬셭셮셯셰셱셲셳셴셵셶셷셸셹셺셻셼셽셾셿솀솁솂솃솄솅솆솇솈솉솊솋소속솎솏손솑솒솓솔솕솖솗솘솙솚솛솜솝솞솟솠송솢솣솤솥솦솧솨솩솪솫솬솭솮솯솰솱솲솳솴솵솶솷솸솹솺솻솼솽솾솿쇀쇁쇂쇃쇄쇅쇆쇇쇈쇉쇊쇋쇌쇍쇎쇏쇐쇑쇒쇓쇔쇕쇖쇗쇘쇙쇚쇛쇜쇝쇞쇟쇠쇡쇢쇣쇤쇥쇦쇧쇨쇩쇪쇫쇬쇭쇮쇯쇰쇱쇲쇳쇴쇵쇶쇷쇸쇹쇺쇻쇼쇽쇾쇿숀숁숂숃숄숅숆숇숈숉숊숋숌숍숎숏숐숑숒숓숔숕숖숗수숙숚숛순숝숞숟술숡","숢숣숤숥숦숧숨숩숪숫숬숭숮숯숰숱숲숳숴숵숶숷숸숹숺숻숼숽숾숿쉀쉁쉂쉃쉄쉅쉆쉇쉈쉉쉊쉋쉌쉍쉎쉏쉐쉑쉒쉓쉔쉕쉖쉗쉘쉙쉚쉛쉜쉝쉞쉟쉠쉡쉢쉣쉤쉥쉦쉧쉨쉩쉪쉫쉬쉭쉮쉯쉰쉱쉲쉳쉴쉵쉶쉷쉸쉹쉺쉻쉼쉽쉾쉿슀슁슂슃슄슅슆슇슈슉슊슋슌슍슎슏슐슑슒슓슔슕슖슗슘슙슚슛슜슝슞슟슠슡슢슣스슥슦슧슨슩슪슫슬슭슮슯슰슱슲슳슴습슶슷슸승슺슻슼슽슾슿싀싁싂싃싄싅싆싇싈싉싊싋싌싍싎싏싐싑싒싓싔싕싖싗싘싙싚싛시식싞싟신싡싢싣실싥싦싧싨싩싪싫심십싮싯싰싱싲싳싴싵싶싷싸싹싺싻싼싽싾싿쌀쌁쌂쌃쌄쌅쌆쌇쌈쌉쌊쌋쌌쌍쌎쌏쌐쌑쌒쌓쌔쌕쌖쌗쌘쌙쌚쌛쌜쌝쌞쌟쌠쌡","쌢쌣쌤쌥쌦쌧쌨쌩쌪쌫쌬쌭쌮쌯쌰쌱쌲쌳쌴쌵쌶쌷쌸쌹쌺쌻쌼쌽쌾쌿썀썁썂썃썄썅썆썇썈썉썊썋썌썍썎썏썐썑썒썓썔썕썖썗썘썙썚썛썜썝썞썟썠썡썢썣썤썥썦썧써썩썪썫썬썭썮썯썰썱썲썳썴썵썶썷썸썹썺썻썼썽썾썿쎀쎁쎂쎃쎄쎅쎆쎇쎈쎉쎊쎋쎌쎍쎎쎏쎐쎑쎒쎓쎔쎕쎖쎗쎘쎙쎚쎛쎜쎝쎞쎟쎠쎡쎢쎣쎤쎥쎦쎧쎨쎩쎪쎫쎬쎭쎮쎯쎰쎱쎲쎳쎴쎵쎶쎷쎸쎹쎺쎻쎼쎽쎾쎿쏀쏁쏂쏃쏄쏅쏆쏇쏈쏉쏊쏋쏌쏍쏎쏏쏐쏑쏒쏓쏔쏕쏖쏗쏘쏙쏚쏛쏜쏝쏞쏟쏠쏡쏢쏣쏤쏥쏦쏧쏨쏩쏪쏫쏬쏭쏮쏯쏰쏱쏲쏳쏴쏵쏶쏷쏸쏹쏺쏻쏼쏽쏾쏿쐀쐁쐂쐃쐄쐅쐆쐇쐈쐉쐊쐋쐌쐍쐎쐏쐐쐑쐒쐓쐔쐕쐖쐗쐘쐙쐚쐛쐜쐝쐞쐟쐠쐡","쐢쐣쐤쐥쐦쐧쐨쐩쐪쐫쐬쐭쐮쐯쐰쐱쐲쐳쐴쐵쐶쐷쐸쐹쐺쐻쐼쐽쐾쐿쑀쑁쑂쑃쑄쑅쑆쑇쑈쑉쑊쑋쑌쑍쑎쑏쑐쑑쑒쑓쑔쑕쑖쑗쑘쑙쑚쑛쑜쑝쑞쑟쑠쑡쑢쑣쑤쑥쑦쑧쑨쑩쑪쑫쑬쑭쑮쑯쑰쑱쑲쑳쑴쑵쑶쑷쑸쑹쑺쑻쑼쑽쑾쑿쒀쒁쒂쒃쒄쒅쒆쒇쒈쒉쒊쒋쒌쒍쒎쒏쒐쒑쒒쒓쒔쒕쒖쒗쒘쒙쒚쒛쒜쒝쒞쒟쒠쒡쒢쒣쒤쒥쒦쒧쒨쒩쒪쒫쒬쒭쒮쒯쒰쒱쒲쒳쒴쒵쒶쒷쒸쒹쒺쒻쒼쒽쒾쒿쓀쓁쓂쓃쓄쓅쓆쓇쓈쓉쓊쓋쓌쓍쓎쓏쓐쓑쓒쓓쓔쓕쓖쓗쓘쓙쓚쓛쓜쓝쓞쓟쓠쓡쓢쓣쓤쓥쓦쓧쓨쓩쓪쓫쓬쓭쓮쓯쓰쓱쓲쓳쓴쓵쓶쓷쓸쓹쓺쓻쓼쓽쓾쓿씀씁씂씃씄씅씆씇씈씉씊씋씌씍씎씏씐씑씒씓씔씕씖씗씘씙씚씛씜씝씞씟씠씡","씢씣씤씥씦씧씨씩씪씫씬씭씮씯씰씱씲씳씴씵씶씷씸씹씺씻씼씽씾씿앀앁앂앃아악앆앇안앉않앋알앍앎앏앐앑앒앓암압앖앗았앙앚앛앜앝앞앟애액앢앣앤앥앦앧앨앩앪앫앬앭앮앯앰앱앲앳앴앵앶앷앸앹앺앻야약앾앿얀얁얂얃얄얅얆얇얈얉얊얋얌얍얎얏얐양얒얓얔얕얖얗얘얙얚얛얜얝얞얟얠얡얢얣얤얥얦얧얨얩얪얫얬얭얮얯얰얱얲얳어억얶얷언얹얺얻얼얽얾얿엀엁엂엃엄업없엇었엉엊엋엌엍엎엏에엑엒엓엔엕엖엗엘엙엚엛엜엝엞엟엠엡엢엣엤엥엦엧엨엩엪엫여역엮엯연엱엲엳열엵엶엷엸엹엺엻염엽엾엿였영옂옃옄옅옆옇예옉옊옋옌옍옎옏옐옑옒옓옔옕옖옗옘옙옚옛옜옝옞옟옠옡","옢옣오옥옦옧온옩옪옫올옭옮옯옰옱옲옳옴옵옶옷옸옹옺옻옼옽옾옿와왁왂왃완왅왆왇왈왉왊왋왌왍왎왏왐왑왒왓왔왕왖왗왘왙왚왛왜왝왞왟왠왡왢왣왤왥왦왧왨왩왪왫왬왭왮왯왰왱왲왳왴왵왶왷외왹왺왻왼왽왾왿욀욁욂욃욄욅욆욇욈욉욊욋욌욍욎욏욐욑욒욓요욕욖욗욘욙욚욛욜욝욞욟욠욡욢욣욤욥욦욧욨용욪욫욬욭욮욯우욱욲욳운욵욶욷울욹욺욻욼욽욾욿움웁웂웃웄웅웆웇웈웉웊웋워웍웎웏원웑웒웓월웕웖웗웘웙웚웛웜웝웞웟웠웡웢웣웤웥웦웧웨웩웪웫웬웭웮웯웰웱웲웳웴웵웶웷웸웹웺웻웼웽웾웿윀윁윂윃위윅윆윇윈윉윊윋윌윍윎윏윐윑윒윓윔윕윖윗윘윙윚윛윜윝윞윟유육","윢윣윤윥윦윧율윩윪윫윬윭윮윯윰윱윲윳윴융윶윷윸윹윺윻으윽윾윿은읁읂읃을읅읆읇읈읉읊읋음읍읎읏읐응읒읓읔읕읖읗의읙읚읛읜읝읞읟읠읡읢읣읤읥읦읧읨읩읪읫읬읭읮읯읰읱읲읳이익읶읷인읹읺읻일읽읾읿잀잁잂잃임입잆잇있잉잊잋잌잍잎잏자작잒잓잔잕잖잗잘잙잚잛잜잝잞잟잠잡잢잣잤장잦잧잨잩잪잫재잭잮잯잰잱잲잳잴잵잶잷잸잹잺잻잼잽잾잿쟀쟁쟂쟃쟄쟅쟆쟇쟈쟉쟊쟋쟌쟍쟎쟏쟐쟑쟒쟓쟔쟕쟖쟗쟘쟙쟚쟛쟜쟝쟞쟟쟠쟡쟢쟣쟤쟥쟦쟧쟨쟩쟪쟫쟬쟭쟮쟯쟰쟱쟲쟳쟴쟵쟶쟷쟸쟹쟺쟻쟼쟽쟾쟿저적젂젃전젅젆젇절젉젊젋젌젍젎젏점접젒젓젔정젖젗젘젙젚젛제젝젞젟젠젡","젢젣젤젥젦젧젨젩젪젫젬젭젮젯젰젱젲젳젴젵젶젷져젹젺젻젼젽젾젿졀졁졂졃졄졅졆졇졈졉졊졋졌졍졎졏졐졑졒졓졔졕졖졗졘졙졚졛졜졝졞졟졠졡졢졣졤졥졦졧졨졩졪졫졬졭졮졯조족졲졳존졵졶졷졸졹졺졻졼졽졾졿좀좁좂좃좄종좆좇좈좉좊좋좌좍좎좏좐좑좒좓좔좕좖좗좘좙좚좛좜좝좞좟좠좡좢좣좤좥좦좧좨좩좪좫좬좭좮좯좰좱좲좳좴좵좶좷좸좹좺좻좼좽좾좿죀죁죂죃죄죅죆죇죈죉죊죋죌죍죎죏죐죑죒죓죔죕죖죗죘죙죚죛죜죝죞죟죠죡죢죣죤죥죦죧죨죩죪죫죬죭죮죯죰죱죲죳죴죵죶죷죸죹죺죻주죽죾죿준줁줂줃줄줅줆줇줈줉줊줋줌줍줎줏줐중줒줓줔줕줖줗줘줙줚줛줜줝줞줟줠줡","줢줣줤줥줦줧줨줩줪줫줬줭줮줯줰줱줲줳줴줵줶줷줸줹줺줻줼줽줾줿쥀쥁쥂쥃쥄쥅쥆쥇쥈쥉쥊쥋쥌쥍쥎쥏쥐쥑쥒쥓쥔쥕쥖쥗쥘쥙쥚쥛쥜쥝쥞쥟쥠쥡쥢쥣쥤쥥쥦쥧쥨쥩쥪쥫쥬쥭쥮쥯쥰쥱쥲쥳쥴쥵쥶쥷쥸쥹쥺쥻쥼쥽쥾쥿즀즁즂즃즄즅즆즇즈즉즊즋즌즍즎즏즐즑즒즓즔즕즖즗즘즙즚즛즜증즞즟즠즡즢즣즤즥즦즧즨즩즪즫즬즭즮즯즰즱즲즳즴즵즶즷즸즹즺즻즼즽즾즿지직짂짃진짅짆짇질짉짊짋짌짍짎짏짐집짒짓짔징짖짗짘짙짚짛짜짝짞짟짠짡짢짣짤짥짦짧짨짩짪짫짬짭짮짯짰짱짲짳짴짵짶짷째짹짺짻짼짽짾짿쨀쨁쨂쨃쨄쨅쨆쨇쨈쨉쨊쨋쨌쨍쨎쨏쨐쨑쨒쨓쨔쨕쨖쨗쨘쨙쨚쨛쨜쨝쨞쨟쨠쨡","쨢쨣쨤쨥쨦쨧쨨쨩쨪쨫쨬쨭쨮쨯쨰쨱쨲쨳쨴쨵쨶쨷쨸쨹쨺쨻쨼쨽쨾쨿쩀쩁쩂쩃쩄쩅쩆쩇쩈쩉쩊쩋쩌쩍쩎쩏쩐쩑쩒쩓쩔쩕쩖쩗쩘쩙쩚쩛쩜쩝쩞쩟쩠쩡쩢쩣쩤쩥쩦쩧쩨쩩쩪쩫쩬쩭쩮쩯쩰쩱쩲쩳쩴쩵쩶쩷쩸쩹쩺쩻쩼쩽쩾쩿쪀쪁쪂쪃쪄쪅쪆쪇쪈쪉쪊쪋쪌쪍쪎쪏쪐쪑쪒쪓쪔쪕쪖쪗쪘쪙쪚쪛쪜쪝쪞쪟쪠쪡쪢쪣쪤쪥쪦쪧쪨쪩쪪쪫쪬쪭쪮쪯쪰쪱쪲쪳쪴쪵쪶쪷쪸쪹쪺쪻쪼쪽쪾쪿쫀쫁쫂쫃쫄쫅쫆쫇쫈쫉쫊쫋쫌쫍쫎쫏쫐쫑쫒쫓쫔쫕쫖쫗쫘쫙쫚쫛쫜쫝쫞쫟쫠쫡쫢쫣쫤쫥쫦쫧쫨쫩쫪쫫쫬쫭쫮쫯쫰쫱쫲쫳쫴쫵쫶쫷쫸쫹쫺쫻쫼쫽쫾쫿쬀쬁쬂쬃쬄쬅쬆쬇쬈쬉쬊쬋쬌쬍쬎쬏쬐쬑쬒쬓쬔쬕쬖쬗쬘쬙쬚쬛쬜쬝쬞쬟쬠쬡","쬢쬣쬤쬥쬦쬧쬨쬩쬪쬫쬬쬭쬮쬯쬰쬱쬲쬳쬴쬵쬶쬷쬸쬹쬺쬻쬼쬽쬾쬿쭀쭁쭂쭃쭄쭅쭆쭇쭈쭉쭊쭋쭌쭍쭎쭏쭐쭑쭒쭓쭔쭕쭖쭗쭘쭙쭚쭛쭜쭝쭞쭟쭠쭡쭢쭣쭤쭥쭦쭧쭨쭩쭪쭫쭬쭭쭮쭯쭰쭱쭲쭳쭴쭵쭶쭷쭸쭹쭺쭻쭼쭽쭾쭿쮀쮁쮂쮃쮄쮅쮆쮇쮈쮉쮊쮋쮌쮍쮎쮏쮐쮑쮒쮓쮔쮕쮖쮗쮘쮙쮚쮛쮜쮝쮞쮟쮠쮡쮢쮣쮤쮥쮦쮧쮨쮩쮪쮫쮬쮭쮮쮯쮰쮱쮲쮳쮴쮵쮶쮷쮸쮹쮺쮻쮼쮽쮾쮿쯀쯁쯂쯃쯄쯅쯆쯇쯈쯉쯊쯋쯌쯍쯎쯏쯐쯑쯒쯓쯔쯕쯖쯗쯘쯙쯚쯛쯜쯝쯞쯟쯠쯡쯢쯣쯤쯥쯦쯧쯨쯩쯪쯫쯬쯭쯮쯯쯰쯱쯲쯳쯴쯵쯶쯷쯸쯹쯺쯻쯼쯽쯾쯿찀찁찂찃찄찅찆찇찈찉찊찋찌찍찎찏찐찑찒찓찔찕찖찗찘찙찚찛찜찝찞찟찠찡","찢찣찤찥찦찧차착찪찫찬찭찮찯찰찱찲찳찴찵찶찷참찹찺찻찼창찾찿챀챁챂챃채책챆챇챈챉챊챋챌챍챎챏챐챑챒챓챔챕챖챗챘챙챚챛챜챝챞챟챠챡챢챣챤챥챦챧챨챩챪챫챬챭챮챯챰챱챲챳챴챵챶챷챸챹챺챻챼챽챾챿첀첁첂첃첄첅첆첇첈첉첊첋첌첍첎첏첐첑첒첓첔첕첖첗처척첚첛천첝첞첟철첡첢첣첤첥첦첧첨첩첪첫첬청첮첯첰첱첲첳체첵첶첷첸첹첺첻첼첽첾첿쳀쳁쳂쳃쳄쳅쳆쳇쳈쳉쳊쳋쳌쳍쳎쳏쳐쳑쳒쳓쳔쳕쳖쳗쳘쳙쳚쳛쳜쳝쳞쳟쳠쳡쳢쳣쳤쳥쳦쳧쳨쳩쳪쳫쳬쳭쳮쳯쳰쳱쳲쳳쳴쳵쳶쳷쳸쳹쳺쳻쳼쳽쳾쳿촀촁촂촃촄촅촆촇초촉촊촋촌촍촎촏촐촑촒촓촔촕촖촗촘촙촚촛촜총촞촟촠촡","촢촣촤촥촦촧촨촩촪촫촬촭촮촯촰촱촲촳촴촵촶촷촸촹촺촻촼촽촾촿쵀쵁쵂쵃쵄쵅쵆쵇쵈쵉쵊쵋쵌쵍쵎쵏쵐쵑쵒쵓쵔쵕쵖쵗쵘쵙쵚쵛최쵝쵞쵟쵠쵡쵢쵣쵤쵥쵦쵧쵨쵩쵪쵫쵬쵭쵮쵯쵰쵱쵲쵳쵴쵵쵶쵷쵸쵹쵺쵻쵼쵽쵾쵿춀춁춂춃춄춅춆춇춈춉춊춋춌춍춎춏춐춑춒춓추축춖춗춘춙춚춛출춝춞춟춠춡춢춣춤춥춦춧춨충춪춫춬춭춮춯춰춱춲춳춴춵춶춷춸춹춺춻춼춽춾춿췀췁췂췃췄췅췆췇췈췉췊췋췌췍췎췏췐췑췒췓췔췕췖췗췘췙췚췛췜췝췞췟췠췡췢췣췤췥췦췧취췩췪췫췬췭췮췯췰췱췲췳췴췵췶췷췸췹췺췻췼췽췾췿츀츁츂츃츄츅츆츇츈츉츊츋츌츍츎츏츐츑츒츓츔츕츖츗츘츙츚츛츜츝츞츟츠측","츢츣츤츥츦츧츨츩츪츫츬츭츮츯츰츱츲츳츴층츶츷츸츹츺츻츼츽츾츿칀칁칂칃칄칅칆칇칈칉칊칋칌칍칎칏칐칑칒칓칔칕칖칗치칙칚칛친칝칞칟칠칡칢칣칤칥칦칧침칩칪칫칬칭칮칯칰칱칲칳카칵칶칷칸칹칺칻칼칽칾칿캀캁캂캃캄캅캆캇캈캉캊캋캌캍캎캏캐캑캒캓캔캕캖캗캘캙캚캛캜캝캞캟캠캡캢캣캤캥캦캧캨캩캪캫캬캭캮캯캰캱캲캳캴캵캶캷캸캹캺캻캼캽캾캿컀컁컂컃컄컅컆컇컈컉컊컋컌컍컎컏컐컑컒컓컔컕컖컗컘컙컚컛컜컝컞컟컠컡컢컣커컥컦컧컨컩컪컫컬컭컮컯컰컱컲컳컴컵컶컷컸컹컺컻컼컽컾컿케켁켂켃켄켅켆켇켈켉켊켋켌켍켎켏켐켑켒켓켔켕켖켗켘켙켚켛켜켝켞켟켠켡","켢켣켤켥켦켧켨켩켪켫켬켭켮켯켰켱켲켳켴켵켶켷켸켹켺켻켼켽켾켿콀콁콂콃콄콅콆콇콈콉콊콋콌콍콎콏콐콑콒콓코콕콖콗콘콙콚콛콜콝콞콟콠콡콢콣콤콥콦콧콨콩콪콫콬콭콮콯콰콱콲콳콴콵콶콷콸콹콺콻콼콽콾콿쾀쾁쾂쾃쾄쾅쾆쾇쾈쾉쾊쾋쾌쾍쾎쾏쾐쾑쾒쾓쾔쾕쾖쾗쾘쾙쾚쾛쾜쾝쾞쾟쾠쾡쾢쾣쾤쾥쾦쾧쾨쾩쾪쾫쾬쾭쾮쾯쾰쾱쾲쾳쾴쾵쾶쾷쾸쾹쾺쾻쾼쾽쾾쾿쿀쿁쿂쿃쿄쿅쿆쿇쿈쿉쿊쿋쿌쿍쿎쿏쿐쿑쿒쿓쿔쿕쿖쿗쿘쿙쿚쿛쿜쿝쿞쿟쿠쿡쿢쿣쿤쿥쿦쿧쿨쿩쿪쿫쿬쿭쿮쿯쿰쿱쿲쿳쿴쿵쿶쿷쿸쿹쿺쿻쿼쿽쿾쿿퀀퀁퀂퀃퀄퀅퀆퀇퀈퀉퀊퀋퀌퀍퀎퀏퀐퀑퀒퀓퀔퀕퀖퀗퀘퀙퀚퀛퀜퀝퀞퀟퀠퀡","퀢퀣퀤퀥퀦퀧퀨퀩퀪퀫퀬퀭퀮퀯퀰퀱퀲퀳퀴퀵퀶퀷퀸퀹퀺퀻퀼퀽퀾퀿큀큁큂큃큄큅큆큇큈큉큊큋큌큍큎큏큐큑큒큓큔큕큖큗큘큙큚큛큜큝큞큟큠큡큢큣큤큥큦큧큨큩큪큫크큭큮큯큰큱큲큳클큵큶큷큸큹큺큻큼큽큾큿킀킁킂킃킄킅킆킇킈킉킊킋킌킍킎킏킐킑킒킓킔킕킖킗킘킙킚킛킜킝킞킟킠킡킢킣키킥킦킧킨킩킪킫킬킭킮킯킰킱킲킳킴킵킶킷킸킹킺킻킼킽킾킿타탁탂탃탄탅탆탇탈탉탊탋탌탍탎탏탐탑탒탓탔탕탖탗탘탙탚탛태택탞탟탠탡탢탣탤탥탦탧탨탩탪탫탬탭탮탯탰탱탲탳탴탵탶탷탸탹탺탻탼탽탾탿턀턁턂턃턄턅턆턇턈턉턊턋턌턍턎턏턐턑턒턓턔턕턖턗턘턙턚턛턜턝턞턟턠턡","턢턣턤턥턦턧턨턩턪턫턬턭턮턯터턱턲턳턴턵턶턷털턹턺턻턼턽턾턿텀텁텂텃텄텅텆텇텈텉텊텋테텍텎텏텐텑텒텓텔텕텖텗텘텙텚텛템텝텞텟텠텡텢텣텤텥텦텧텨텩텪텫텬텭텮텯텰텱텲텳텴텵텶텷텸텹텺텻텼텽텾텿톀톁톂톃톄톅톆톇톈톉톊톋톌톍톎톏톐톑톒톓톔톕톖톗톘톙톚톛톜톝톞톟토톡톢톣톤톥톦톧톨톩톪톫톬톭톮톯톰톱톲톳톴통톶톷톸톹톺톻톼톽톾톿퇀퇁퇂퇃퇄퇅퇆퇇퇈퇉퇊퇋퇌퇍퇎퇏퇐퇑퇒퇓퇔퇕퇖퇗퇘퇙퇚퇛퇜퇝퇞퇟퇠퇡퇢퇣퇤퇥퇦퇧퇨퇩퇪퇫퇬퇭퇮퇯퇰퇱퇲퇳퇴퇵퇶퇷퇸퇹퇺퇻퇼퇽퇾퇿툀툁툂툃툄툅툆툇툈툉툊툋툌툍툎툏툐툑툒툓툔툕툖툗툘툙툚툛툜툝툞툟툠툡","툢툣툤툥툦툧툨툩툪툫투툭툮툯툰툱툲툳툴툵툶툷툸툹툺툻툼툽툾툿퉀퉁퉂퉃퉄퉅퉆퉇퉈퉉퉊퉋퉌퉍퉎퉏퉐퉑퉒퉓퉔퉕퉖퉗퉘퉙퉚퉛퉜퉝퉞퉟퉠퉡퉢퉣퉤퉥퉦퉧퉨퉩퉪퉫퉬퉭퉮퉯퉰퉱퉲퉳퉴퉵퉶퉷퉸퉹퉺퉻퉼퉽퉾퉿튀튁튂튃튄튅튆튇튈튉튊튋튌튍튎튏튐튑튒튓튔튕튖튗튘튙튚튛튜튝튞튟튠튡튢튣튤튥튦튧튨튩튪튫튬튭튮튯튰튱튲튳튴튵튶튷트특튺튻튼튽튾튿틀틁틂틃틄틅틆틇틈틉틊틋틌틍틎틏틐틑틒틓틔틕틖틗틘틙틚틛틜틝틞틟틠틡틢틣틤틥틦틧틨틩틪틫틬틭틮틯티틱틲틳틴틵틶틷틸틹틺틻틼틽틾틿팀팁팂팃팄팅팆팇팈팉팊팋파팍팎팏판팑팒팓팔팕팖팗팘팙팚팛팜팝팞팟팠팡","팢팣팤팥팦팧패팩팪팫팬팭팮팯팰팱팲팳팴팵팶팷팸팹팺팻팼팽팾팿퍀퍁퍂퍃퍄퍅퍆퍇퍈퍉퍊퍋퍌퍍퍎퍏퍐퍑퍒퍓퍔퍕퍖퍗퍘퍙퍚퍛퍜퍝퍞퍟퍠퍡퍢퍣퍤퍥퍦퍧퍨퍩퍪퍫퍬퍭퍮퍯퍰퍱퍲퍳퍴퍵퍶퍷퍸퍹퍺퍻퍼퍽퍾퍿펀펁펂펃펄펅펆펇펈펉펊펋펌펍펎펏펐펑펒펓펔펕펖펗페펙펚펛펜펝펞펟펠펡펢펣펤펥펦펧펨펩펪펫펬펭펮펯펰펱펲펳펴펵펶펷편펹펺펻펼펽펾펿폀폁폂폃폄폅폆폇폈평폊폋폌폍폎폏폐폑폒폓폔폕폖폗폘폙폚폛폜폝폞폟폠폡폢폣폤폥폦폧폨폩폪폫포폭폮폯폰폱폲폳폴폵폶폷폸폹폺폻폼폽폾폿퐀퐁퐂퐃퐄퐅퐆퐇퐈퐉퐊퐋퐌퐍퐎퐏퐐퐑퐒퐓퐔퐕퐖퐗퐘퐙퐚퐛퐜퐝퐞퐟퐠퐡","퐢퐣퐤퐥퐦퐧퐨퐩퐪퐫퐬퐭퐮퐯퐰퐱퐲퐳퐴퐵퐶퐷퐸퐹퐺퐻퐼퐽퐾퐿푀푁푂푃푄푅푆푇푈푉푊푋푌푍푎푏푐푑푒푓푔푕푖푗푘푙푚푛표푝푞푟푠푡푢푣푤푥푦푧푨푩푪푫푬푭푮푯푰푱푲푳푴푵푶푷푸푹푺푻푼푽푾푿풀풁풂풃풄풅풆풇품풉풊풋풌풍풎풏풐풑풒풓풔풕풖풗풘풙풚풛풜풝풞풟풠풡풢풣풤풥풦풧풨풩풪풫풬풭풮풯풰풱풲풳풴풵풶풷풸풹풺풻풼풽풾풿퓀퓁퓂퓃퓄퓅퓆퓇퓈퓉퓊퓋퓌퓍퓎퓏퓐퓑퓒퓓퓔퓕퓖퓗퓘퓙퓚퓛퓜퓝퓞퓟퓠퓡퓢퓣퓤퓥퓦퓧퓨퓩퓪퓫퓬퓭퓮퓯퓰퓱퓲퓳퓴퓵퓶퓷퓸퓹퓺퓻퓼퓽퓾퓿픀픁픂픃프픅픆픇픈픉픊픋플픍픎픏픐픑픒픓픔픕픖픗픘픙픚픛픜픝픞픟픠픡","픢픣픤픥픦픧픨픩픪픫픬픭픮픯픰픱픲픳픴픵픶픷픸픹픺픻피픽픾픿핀핁핂핃필핅핆핇핈핉핊핋핌핍핎핏핐핑핒핓핔핕핖핗하학핚핛한핝핞핟할핡핢핣핤핥핦핧함합핪핫핬항핮핯핰핱핲핳해핵핶핷핸핹핺핻핼핽핾핿햀햁햂햃햄햅햆햇했행햊햋햌햍햎햏햐햑햒햓햔햕햖햗햘햙햚햛햜햝햞햟햠햡햢햣햤향햦햧햨햩햪햫햬햭햮햯햰햱햲햳햴햵햶햷햸햹햺햻햼햽햾햿헀헁헂헃헄헅헆헇허헉헊헋헌헍헎헏헐헑헒헓헔헕헖헗험헙헚헛헜헝헞헟헠헡헢헣헤헥헦헧헨헩헪헫헬헭헮헯헰헱헲헳헴헵헶헷헸헹헺헻헼헽헾헿혀혁혂혃현혅혆혇혈혉혊혋혌혍혎혏혐협혒혓혔형혖혗혘혙혚혛혜혝혞혟혠혡","혢혣혤혥혦혧혨혩혪혫혬혭혮혯혰혱혲혳혴혵혶혷호혹혺혻혼혽혾혿홀홁홂홃홄홅홆홇홈홉홊홋홌홍홎홏홐홑홒홓화확홖홗환홙홚홛활홝홞홟홠홡홢홣홤홥홦홧홨황홪홫홬홭홮홯홰홱홲홳홴홵홶홷홸홹홺홻홼홽홾홿횀횁횂횃횄횅횆횇횈횉횊횋회획횎횏횐횑횒횓횔횕횖횗횘횙횚횛횜횝횞횟횠횡횢횣횤횥횦횧효횩횪횫횬횭횮횯횰횱횲횳횴횵횶횷횸횹횺횻횼횽횾횿훀훁훂훃후훅훆훇훈훉훊훋훌훍훎훏훐훑훒훓훔훕훖훗훘훙훚훛훜훝훞훟훠훡훢훣훤훥훦훧훨훩훪훫훬훭훮훯훰훱훲훳훴훵훶훷훸훹훺훻훼훽훾훿휀휁휂휃휄휅휆휇휈휉휊휋휌휍휎휏휐휑휒휓휔휕휖휗휘휙휚휛휜휝휞휟휠휡","휢휣휤휥휦휧휨휩휪휫휬휭휮휯휰휱휲휳휴휵휶휷휸휹휺휻휼휽휾휿흀흁흂흃흄흅흆흇흈흉흊흋흌흍흎흏흐흑흒흓흔흕흖흗흘흙흚흛흜흝흞흟흠흡흢흣흤흥흦흧흨흩흪흫희흭흮흯흰흱흲흳흴흵흶흷흸흹흺흻흼흽흾흿힀힁힂힃힄힅힆힇히힉힊힋힌힍힎힏힐힑힒힓힔힕힖힗힘힙힚힛힜힝힞힟힠힡힢힣힤힥힦힧힨힩힪힫힬힭힮힯ힰힱힲힳힴힵힶힷힸힹힺힻힼힽힾힿퟀퟁퟂퟃퟄퟅퟆ퟇퟈퟉퟊ퟋퟌퟍퟎퟏퟐퟑퟒퟓퟔퟕퟖퟗퟘퟙퟚퟛퟜퟝퟞퟟퟠퟡퟢퟣퟤퟥퟦퟧퟨퟩퟪퟫퟬퟭퟮퟯퟰퟱퟲퟳퟴퟵퟶퟷퟸퟹퟺퟻ퟼퟽퟾퟿������������������������������������������������������������������������������������������������������","������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������","������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������","������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������","������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������","������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������","������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������","������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������","������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������","","","","","","","","","","","","","","","","","","","","","","","","","豈更車賈滑串句龜龜契金喇奈懶癩羅蘿螺裸邏樂洛烙珞落酪駱亂卵欄爛蘭鸞嵐","濫藍襤拉臘蠟廊朗浪狼郎來冷勞擄櫓爐盧老蘆虜路露魯鷺碌祿綠菉錄鹿論壟弄籠聾牢磊賂雷壘屢樓淚漏累縷陋勒肋凜凌稜綾菱陵讀拏樂諾丹寧怒率異北磻便復不泌數索參塞省葉說殺辰沈拾若掠略亮兩凉梁糧良諒量勵呂女廬旅濾礪閭驪麗黎力曆歷轢年憐戀撚漣煉璉秊練聯輦蓮連鍊列劣咽烈裂說廉念捻殮簾獵令囹寧嶺怜玲瑩羚聆鈴零靈領例禮醴隸惡了僚寮尿料樂燎療蓼遼龍暈阮劉杻柳流溜琉留硫紐類六戮陸倫崙淪輪律慄栗率隆利吏履易李梨泥理痢罹裏裡里離匿溺吝燐璘藺隣鱗麟林淋臨立笠粒狀炙識什茶刺切度拓糖宅洞暴輻行降見廓兀嗀﨎﨏塚﨑晴﨓﨔凞猪益礼神祥福靖精羽﨟蘒﨡","諸﨣﨤逸都﨧﨨﨩飯飼館鶴郞隷侮僧免勉勤卑喝嘆器塀墨層屮悔慨憎懲敏既暑梅海渚漢煮爫琢碑社祉祈祐祖祝禍禎穀突節練縉繁署者臭艹艹著褐視謁謹賓贈辶逸難響頻恵𤋮舘﩮﩯並况全侀充冀勇勺喝啕喙嗢塚墳奄奔婢嬨廒廙彩徭惘慎愈憎慠懲戴揄搜摒敖晴朗望杖歹殺流滛滋漢瀞煮瞧爵犯猪瑱甆画瘝瘟益盛直睊着磌窱節类絛練缾者荒華蝹襁覆視調諸請謁諾諭謹變贈輸遲醙鉶陼難靖韛響頋頻鬒龜𢡊𢡄𣏕㮝䀘䀹𥉉𥳐𧻓齃龎﫚﫛﫜﫝﫞﫟﫠﫡﫢﫣﫤﫥﫦﫧﫨﫩﫪﫫﫬﫭﫮﫯﫰﫱﫲﫳﫴﫵﫶﫷﫸﫹﫺﫻﫼﫽﫾﫿fffiflffifflſtst﬇﬈﬉﬊﬋﬌﬍﬎﬏﬐﬑﬒ﬓﬔﬕﬖﬗ﬘﬙﬚﬛﬜יִﬞײַﬠﬡ","ﬢﬣﬤﬥﬦﬧﬨ﬩שׁשׂשּׁשּׂאַאָאּבּגּדּהּוּזּ﬷טּיּךּכּלּ﬽מּ﬿נּסּ﭂ףּפּ﭅צּקּרּשּתּוֹבֿכֿפֿﭏﭐﭑﭒﭓﭔﭕﭖﭗﭘﭙﭚﭛﭜﭝﭞﭟﭠﭡﭢﭣﭤﭥﭦﭧﭨﭩﭪﭫﭬﭭﭮﭯﭰﭱﭲﭳﭴﭵﭶﭷﭸﭹﭺﭻﭼﭽﭾﭿﮀﮁﮂﮃﮄﮅﮆﮇﮈﮉﮊﮋﮌﮍﮎﮏﮐﮑﮒﮓﮔﮕﮖﮗﮘﮙﮚﮛﮜﮝﮞﮟﮠﮡﮢﮣﮤﮥﮦﮧﮨﮩﮪﮫﮬﮭﮮﮯﮰﮱ﮲﮳﮴﮵﮶﮷﮸﮹﮺﮻﮼﮽﮾﮿﯀﯁﯂﯃﯄﯅﯆﯇﯈﯉﯊﯋﯌﯍﯎﯏﯐﯑﯒ﯓﯔﯕﯖﯗﯘﯙﯚﯛﯜﯝﯞﯟﯠﯡﯢﯣﯤﯥﯦﯧﯨﯩﯪﯫﯬﯭﯮﯯﯰﯱﯲﯳﯴﯵﯶﯷﯸﯹﯺﯻﯼﯽﯾﯿﰀﰁﰂﰃﰄﰅﰆﰇﰈﰉﰊﰋﰌﰍﰎﰏﰐﰑﰒﰓﰔﰕﰖﰗﰘﰙﰚﰛﰜﰝﰞﰟﰠﰡ","ﰢﰣﰤﰥﰦﰧﰨﰩﰪﰫﰬﰭﰮﰯﰰﰱﰲﰳﰴﰵﰶﰷﰸﰹﰺﰻﰼﰽﰾﰿﱀﱁﱂﱃﱄﱅﱆﱇﱈﱉﱊﱋﱌﱍﱎﱏﱐﱑﱒﱓﱔﱕﱖﱗﱘﱙﱚﱛﱜﱝﱞﱟﱠﱡﱢﱣﱤﱥﱦﱧﱨﱩﱪﱫﱬﱭﱮﱯﱰﱱﱲﱳﱴﱵﱶﱷﱸﱹﱺﱻﱼﱽﱾﱿﲀﲁﲂﲃﲄﲅﲆﲇﲈﲉﲊﲋﲌﲍﲎﲏﲐﲑﲒﲓﲔﲕﲖﲗﲘﲙﲚﲛﲜﲝﲞﲟﲠﲡﲢﲣﲤﲥﲦﲧﲨﲩﲪﲫﲬﲭﲮﲯﲰﲱﲲﲳﲴﲵﲶﲷﲸﲹﲺﲻﲼﲽﲾﲿﳀﳁﳂﳃﳄﳅﳆﳇﳈﳉﳊﳋﳌﳍﳎﳏﳐﳑﳒﳓﳔﳕﳖﳗﳘﳙﳚﳛﳜﳝﳞﳟﳠﳡﳢﳣﳤﳥﳦﳧﳨﳩﳪﳫﳬﳭﳮﳯﳰﳱﳲﳳﳴﳵﳶﳷﳸﳹﳺﳻﳼﳽﳾﳿﴀﴁﴂﴃﴄﴅﴆﴇﴈﴉﴊﴋﴌﴍﴎﴏﴐﴑﴒﴓﴔﴕﴖﴗﴘﴙﴚﴛﴜﴝﴞﴟﴠﴡ","ﴢﴣﴤﴥﴦﴧﴨﴩﴪﴫﴬﴭﴮﴯﴰﴱﴲﴳﴴﴵﴶﴷﴸﴹﴺﴻﴼﴽ﴾﴿﵀﵁﵂﵃﵄﵅﵆﵇﵈﵉﵊﵋﵌﵍﵎﵏ﵐﵑﵒﵓﵔﵕﵖﵗﵘﵙﵚﵛﵜﵝﵞﵟﵠﵡﵢﵣﵤﵥﵦﵧﵨﵩﵪﵫﵬﵭﵮﵯﵰﵱﵲﵳﵴﵵﵶﵷﵸﵹﵺﵻﵼﵽﵾﵿﶀﶁﶂﶃﶄﶅﶆﶇﶈﶉﶊﶋﶌﶍﶎﶏ﶐﶑ﶒﶓﶔﶕﶖﶗﶘﶙﶚﶛﶜﶝﶞﶟﶠﶡﶢﶣﶤﶥﶦﶧﶨﶩﶪﶫﶬﶭﶮﶯﶰﶱﶲﶳﶴﶵﶶﶷﶸﶹﶺﶻﶼﶽﶾﶿﷀﷁﷂﷃﷄﷅﷆﷇ﷈﷉﷊﷋﷌﷍﷎﷏﷐﷑﷒﷓﷔﷕﷖﷗﷘﷙﷚﷛﷜﷝﷞﷟﷠﷡﷢﷣﷤﷥﷦﷧﷨﷩﷪﷫﷬﷭﷮﷯ﷰﷱﷲﷳﷴﷵﷶﷷﷸﷹﷺﷻ﷼﷽﷾﷿︀︁︂︃︄︅︆︇︈︉︊︋︌︍︎️︐︑︒︓︔︕︖︗︘︙︚︛︜︝︞︟︠︡","︧︨︩︪︫︬︭︢︣︤︥︦︮︯︰︱︲︳︴︵︶︷︸︹︺︻︼︽︾︿﹀﹁﹂﹃﹄﹅﹆﹇﹈﹉﹊﹋﹌﹍﹎﹏﹐﹑﹒﹓﹔﹕﹖﹗﹘﹙﹚﹛﹜﹝﹞﹟﹠﹡﹢﹣﹤﹥﹦﹧﹨﹩﹪﹫﹬﹭﹮﹯ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ﻽﻾＀!"#$%&'()*+,-./0123456789:;<=>?@A","BCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~⦅⦆。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚ᅠᄀᄁᆪᄂᆬᆭᄃᄄᄅᆰᆱᆲᆳᆴᆵᄚᄆᄇᄈᄡᄉᄊᄋᄌᄍᄎᄏᄐᄑᄒ﾿￀￁ᅡᅢᅣᅤᅥᅦ￈￉ᅧᅨᅩᅪᅫᅬ￐￑ᅭᅮᅯᅰᅱᅲ￘￙ᅳᅴᅵ￝￞￟¢£¬ ̄¦¥₩￧│←↑→↓■○￯￰￱￲￳￴￵￶￷￸�￾￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿"],"keys":["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49","50","51","52","53","54","55","56","57","58","59","60","61","62","63","64","65","66","67","68","69","70","71","72","73","74","75","76","77","78","79","80","81","82","83","84","85","86","87","88","89","90","91","92","93","94","95","96","97","98","99","100","101","102","103","104","105","106","107","108","109","110","111","112","113","114","115","116","117","118","119","120","121","122","123","124","125","126","127","128","129","130","131","132","133","134","135","136","137","138","139","140","141","142","143","144","145","146","147","148","149","150","151","152","153","154","155","156","157","158","159","160","161","162","163","164","165","166","167","168","169","170","171","172","173","174","175","176","177","178","179","180","181","182","183","184","185","186","187","188","189","190","191","192","193","194","195","196","197","198","199","200","201","202","203","204","205","206","207","208","209","210","211","212","213","214","215","216","217","218","219","220","221","222","223","224","225","226","227","228","229","230","231","232","233","234","235","236","237","238","239","240","241","242","243","244","245","246","247","248","249","250","251","252","253","254","255","256","257","258","259","260","261","262","263","264","265","266","267","268","269","270","271","272","273","274","275","276","277","278","279","280","281","282","283","284","285","286","287","288","289","290","291","292","293","294","295","296","297","298","299","300","301","302","303","304","305","306","307","308","309","310","311","312","313","314","315","316","317","318","319","320","321","322","323","324","325","326","327","328","329","330","331","332","333","334","335","336","337","338","339","340","341","342","343","344","345","346","347","348","349","350","351","352","353","354","355","356","357","358","359","360","361","362","363","364","365","366","367","368","369","370","371","372","373","374","375","376","377","378","379","380","381","382","383","384","385","386","387","388","389","390","391","392","393","394","395","396","397","398","399","400","401","402","403","404","405","406","407","408","409","410","411","412","413","414","415","416","417","418","419","420","421","422","423","424","425","426","427","428","429","430","431","432","433","434","435","436","437","438","439","440","441","442","443","444","445","446","447","448","449","450","451","452","453","454","455","456","457","458","459","460","461","462","463","464","465","466","467","468","469","470","471","472","473","474","475","476","477","478","479","480","481","482","483","484","485","486","487","488","489","490","491","492","493","494","495","496","497","498","499","500","501","502","503","504","505","506","507","508","509","510","511","512","513","514","515","516","517","518","519","520","521","522","523","524","525","526","527","528","529","530","531","532","533","534","535","536","537","538","539","540","541","542","543","544","545","546","547","548","549","550","551","552","553","554","555","556","557","558","559","560","561","562","563","564","565","566","567","568","569","570","571","572","573","574","575","576","577","578","579","580","581","582","583","584","585","586","587","588","589","590","591","592","593","594","595","596","597","598","599","600","601","602","603","604","605","606","607","608","609","610","611","612","613","614","615","616","617","618","619","620","621","622","623","624","625","626","627","628","629","630","631","632","633","634","635","636","637","638","639","640","641","642","643","644","645","646","647","648","649","650","651","652","653","654","655","656","657","658","659","660","661","662","663","664","665","666","667","668","669","670","671","672","673","674","675","676","677","678","679","680","681","682","683","684","685","686","687","688","689","690","691","692","693","694","695","696","697","698","699","700","701","702","703","704","705","706","707","708","709","710","711","712","713","714","715","716","717","718","719","720","721","722","723","724","725","726","727","728","729","730","731","732","733","734","735","736","737","738","739","740","741","742","743","744","745","746","747","748","749","750","751","752","753","754","755","756","757","758","759","760","761","762","763","764","765","766","767","768","769","770","771","772","773","774","775","776","777","778","779","780","781","782","783","784","785","786","787","788","789","790","791","792","793","794","795","796","797","798","799","800","801","802","803","804","805","806","807","808","809","810","811","812","813","814","815","816","817","818","819","820","821","822","823","824","825","826","827","828","829","830","831","832","833","834","835","836","837","838","839","840","841","842","843","844","845","846","847","848","849","850","851","852","853","854","855","856","857","858","859","860","861","862","863","864","865","866","867","868","869","870","871","872","873","874","875","876","877","878","879","880","881","882","883","884","885","886","887","888","889","890","891","892","893","894","895","896","897","898","899","900","901","902","903","904","905","906","907","908","909","910","911","912","913","914","915","916","917","918","919","920","921","922","923","924","925","926","927","928","929","930","931","932","933","934","935","936","937","938","939","940","941","942","943","944","945","946","947","948","949","950","951","952","953","954","955","956","957","958","959","960","961","962","963","964","965","966","967","968","969","970","971","972","973","974","975","976","977","978","979","980","981","982","983","984","985","986","987","988","989","990","991","992","993","994","995","996","997","998","999","1000","1001","1002","1003","1004","1005","1006","1007","1008","1009","1010","1011","1012","1013","1014","1015","1016","1017","1018","1019","1020","1021","1022","1023","1024","1025","1026","1027","1028","1029","1030","1031","1032","1033","1034","1035","1036","1037","1038","1039","1040","1041","1042","1043","1044","1045","1046","1047","1048","1049","1050","1051","1052","1053","1054","1055","1056","1057","1058","1059","1060","1061","1062","1063","1064","1065","1066","1067","1068","1069","1070","1071","1072","1073","1074","1075","1076","1077","1078","1079","1080","1081","1082","1083","1084","1085","1086","1087","1088","1089","1090","1091","1092","1093","1094","1095","1096","1097","1098","1099","1100","1101","1102","1103","1104","1105","1106","1107","1108","1109","1110","1111","1112","1113","1114","1115","1116","1117","1118","1119","1120","1121","1122","1123","1124","1125","1126","1127","1128","1129","1130","1131","1132","1133","1134","1135","1136","1137","1138","1139","1140","1141","1142","1143","1144","1145","1146","1147","1148","1149","1150","1151","1152","1153","1154","1155","1156","1157","1158","1159","1160","1161","1162","1163","1164","1165","1166","1167","1168","1169","1170","1171","1172","1173","1174","1175","1176","1177","1178","1179","1180","1181","1182","1183","1184","1185","1186","1187","1188","1189","1190","1191","1192","1193","1194","1195","1196","1197","1198","1199","1200","1201","1202","1203","1204","1205","1206","1207","1208","1209","1210","1211","1212","1213","1214","1215","1216","1217","1218","1219","1220","1221","1222","1223","1224","1225","1226","1227","1228","1229","1230","1231","1232","1233","1234","1235","1236","1237","1238","1239","1240","1241","1242","1243","1244","1245","1246","1247","1248","1249","1250","1251","1252","1253","1254","1255","1256","1257","1258","1259","1260","1261","1262","1263","1264","1265","1266","1267","1268","1269","1270","1271","1272","1273","1274","1275","1276","1277","1278","1279","1280","1281","1282","1283","1284","1285","1286","1287","1288","1289","1290","1291","1292","1293","1294","1295","1296","1297","1298","1299","1300","1301","1302","1303","1304","1305","1306","1307","1308","1309","1310","1311","1312","1313","1314","1315","1316","1317","1318","1319","1320","1321","1322","1323","1324","1325","1326","1327","1328","1329","1330","1331","1332","1333","1334","1335","1336","1337","1338","1339","1340","1341","1342","1343","1344","1345","1346","1347","1348","1349","1350","1351","1352","1353","1354","1355","1356","1357","1358","1359","1360","1361","1362","1363","1364","1365","1366","1367","1368","1369","1370","1371","1372","1373","1374","1375","1376","1377","1378","1379","1380","1381","1382","1383","1384","1385","1386","1387","1388","1389","1390","1391","1392","1393","1394","1395","1396","1397","1398","1399","1400","1401","1402","1403","1404","1405","1406","1407","1408","1409","1410","1411","1412","1413","1414","1415","1416","1417","1418","1419","1420","1421","1422","1423","1424","1425","1426","1427","1428","1429","1430","1431","1432","1433","1434","1435","1436","1437","1438","1439","1440","1441","1442","1443","1444","1445","1446","1447","1448","1449","1450","1451","1452","1453","1454","1455","1456","1457","1458","1459","1460","1461","1462","1463","1464","1465","1466","1467","1468","1469","1470","1471","1472","1473","1474","1475","1476","1477","1478","1479","1480","1481","1482","1483","1484","1485","1486","1487","1488","1489","1490","1491","1492","1493","1494","1495","1496","1497","1498","1499","1500","1501","1502","1503","1504","1505","1506","1507","1508","1509","1510","1511","1512","1513","1514","1515","1516","1517","1518","1519","1520","1521","1522","1523","1524","1525","1526","1527","1528","1529","1530","1531","1532","1533","1534","1535","1536","1537","1538","1539","1540","1541","1542","1543","1544","1545","1546","1547","1548","1549","1550","1551","1552","1553","1554","1555","1556","1557","1558","1559","1560","1561","1562","1563","1564","1565","1566","1567","1568","1569","1570","1571","1572","1573","1574","1575","1576","1577","1578","1579","1580","1581","1582","1583","1584","1585","1586","1587","1588","1589","1590","1591","1592","1593","1594","1595","1596","1597","1598","1599","1600","1601","1602","1603","1604","1605","1606","1607","1608","1609","1610","1611","1612","1613","1614","1615","1616","1617","1618","1619","1620","1621","1622","1623","1624","1625","1626","1627","1628","1629","1630","1631","1632","1633","1634","1635","1636","1637","1638","1639","1640","1641","1642","1643","1644","1645","1646","1647","1648","1649","1650","1651","1652","1653","1654","1655","1656","1657","1658","1659","1660","1661","1662","1663","1664","1665","1666","1667","1668","1669","1670","1671","1672","1673","1674","1675","1676","1677","1678","1679","1680","1681","1682","1683","1684","1685","1686","1687","1688","1689","1690","1691","1692","1693","1694","1695","1696","1697","1698","1699","1700","1701","1702","1703","1704","1705","1706","1707","1708","1709","1710","1711","1712","1713","1714","1715","1716","1717","1718","1719","1720","1721","1722","1723","1724","1725","1726","1727","1728","1729","1730","1731","1732","1733","1734","1735","1736","1737","1738","1739","1740","1741","1742","1743","1744","1745","1746","1747","1748","1749","1750","1751","1752","1753","1754","1755","1756","1757","1758","1759","1760","1761","1762","1763","1764","1765","1766","1767","1768","1769","1770","1771","1772","1773","1774","1775","1776","1777","1778","1779","1780","1781","1782","1783","1784","1785","1786","1787","1788","1789","1790","1791","1792","1793","1794","1795","1796","1797","1798","1799","1800","1801","1802","1803","1804","1805","1806","1807","1808","1809","1810","1811","1812","1813","1814","1815","1816","1817","1818","1819","1820","1821","1822","1823","1824","1825","1826","1827","1828","1829","1830","1831","1832","1833","1834","1835","1836","1837","1838","1839","1840","1841","1842","1843","1844","1845","1846","1847","1848","1849","1850","1851","1852","1853","1854","1855","1856","1857","1858","1859","1860","1861","1862","1863","1864","1865","1866","1867","1868","1869","1870","1871","1872","1873","1874","1875","1876","1877","1878","1879","1880","1881","1882","1883","1884","1885","1886","1887","1888","1889","1890","1891","1892","1893","1894","1895","1896","1897","1898","1899","1900","1901","1902","1903","1904","1905","1906","1907","1908","1909","1910","1911","1912","1913","1914","1915","1916","1917","1918","1919","1920","1921","1922","1923","1924","1925","1926","1927","1928","1929","1930","1931","1932","1933","1934","1935","1936","1937","1938","1939","1940","1941","1942","1943","1944","1945","1946","1947","1948","1949","1950","1951","1952","1953","1954","1955","1956","1957","1958","1959","1960","1961","1962","1963","1964","1965","1966","1967","1968","1969","1970","1971","1972","1973","1974","1975","1976","1977","1978","1979","1980","1981","1982","1983","1984","1985","1986","1987","1988","1989","1990","1991","1992","1993","1994","1995","1996","1997","1998","1999","2000","2001","2002","2003","2004","2005","2006","2007","2008","2009","2010","2011","2012","2013","2014","2015","2016","2017","2018","2019","2020","2021","2022","2023","2024","2025","2026","2027","2028","2029","2030","2031","2032","2033","2034","2035","2036","2037","2038","2039","2040","2041","2042","2043","2044","2045","2046","2047","2048","2049","2050","2051","2052","2053","2054","2055","2056","2057","2058","2059","2060","2061","2062","2063","2064","2065","2066","2067","2068","2069","2070","2071","2072","2073","2074","2075","2076","2077","2078","2079","2080","2081","2082","2083","2084","2085","2086","2087","2088","2089","2090","2091","2092","2093","2094","2095","2096","2097","2098","2099","2100","2101","2102","2103","2104","2105","2106","2107","2108","2109","2110","2111","2112","2113","2114","2115","2116","2117","2118","2119","2120","2121","2122","2123","2124","2125","2126","2127","2128","2129","2130","2131","2132","2133","2134","2135","2136","2137","2138","2139","2140","2141","2142","2143","2144","2145","2146","2147","2148","2149","2150","2151","2152","2153","2154","2155","2156","2157","2158","2159","2160","2161","2162","2163","2164","2165","2166","2167","2168","2169","2170","2171","2172","2173","2174","2175","2176","2177","2178","2179","2180","2181","2182","2183","2184","2185","2186","2187","2188","2189","2190","2191","2192","2193","2194","2195","2196","2197","2198","2199","2200","2201","2202","2203","2204","2205","2206","2207","2208","2209","2210","2211","2212","2213","2214","2215","2216","2217","2218","2219","2220","2221","2222","2223","2224","2225","2226","2227","2228","2229","2230","2231","2232","2233","2234","2235","2236","2237","2238","2239","2240","2241","2242","2243","2244","2245","2246","2247","2248","2249","2250","2251","2252","2253","2254","2255","2256","2257","2258","2259","2260","2261","2262","2263","2264","2265","2266","2267","2268","2269","2270","2271","2272","2273","2274","2275","2276","2277","2278","2279","2280","2281","2282","2283","2284","2285","2286","2287","2288","2289","2290","2291","2292","2293","2294","2295","2296","2297","2298","2299","2300","2301","2302","2303","2304","2305","2306","2307","2308","2309","2310","2311","2312","2313","2314","2315","2316","2317","2318","2319","2320","2321","2322","2323","2324","2325","2326","2327","2328","2329","2330","2331","2332","2333","2334","2335","2336","2337","2338","2339","2340","2341","2342","2343","2344","2345","2346","2347","2348","2349","2350","2351","2352","2353","2354","2355","2356","2357","2358","2359","2360","2361","2362","2363","2364","2365","2366","2367","2368","2369","2370","2371","2372","2373","2374","2375","2376","2377","2378","2379","2380","2381","2382","2383","2384","2385","2386","2387","2388","2389","2390","2391","2392","2393","2394","2395","2396","2397","2398","2399","2400","2401","2402","2403","2404","2405","2406","2407","2408","2409","2410","2411","2412","2413","2414","2415","2416","2417","2418","2419","2420","2421","2422","2423","2424","2425","2426","2427","2428","2429","2430","2431","2432","2433","2434","2435","2436","2437","2438","2439","2440","2441","2442","2443","2444","2445","2446","2447","2448","2449","2450","2451","2452","2453","2454","2455","2456","2457","2458","2459","2460","2461","2462","2463","2464","2465","2466","2467","2468","2469","2470","2471","2472","2473","2474","2475","2476","2477","2478","2479","2480","2481","2482","2483","2484","2485","2486","2487","2488","2489","2490","2491","2492","2493","2494","2495","2496","2497","2498","2499","2500","2501","2502","2503","2504","2505","2506","2507","2508","2509","2510","2511","2512","2513","2514","2515","2516","2517","2518","2519","2520","2521","2522","2523","2524","2525","2526","2527","2528","2529","2530","2531","2532","2533","2534","2535","2536","2537","2538","2539","2540","2541","2542","2543","2544","2545","2546","2547","2548","2549","2550","2551","2552","2553","2554","2555","2556","2557","2558","2559","2560","2561","2562","2563","2564","2565","2566","2567","2568","2569","2570","2571","2572","2573","2574","2575","2576","2577","2578","2579","2580","2581","2582","2583","2584","2585","2586","2587","2588","2589","2590","2591","2592","2593","2594","2595","2596","2597","2598","2599","2600","2601","2602","2603","2604","2605","2606","2607","2608","2609","2610","2611","2612","2613","2614","2615","2616","2617","2618","2619","2620","2621","2622","2623","2624","2625","2626","2627","2628","2629","2630","2631","2632","2633","2634","2635","2636","2637","2638","2639","2640","2641","2642","2643","2644","2645","2646","2647","2648","2649","2650","2651","2652","2653","2654","2655","2656","2657","2658","2659","2660","2661","2662","2663","2664","2665","2666","2667","2668","2669","2670","2671","2672","2673","2674","2675","2676","2677","2678","2679","2680","2681","2682","2683","2684","2685","2686","2687","2688","2689","2690","2691","2692","2693","2694","2695","2696","2697","2698","2699","2700","2701","2702","2703","2704","2705","2706","2707","2708","2709","2710","2711","2712","2713","2714","2715","2716","2717","2718","2719","2720","2721","2722","2723","2724","2725","2726","2727","2728","2729","2730","2731","2732","2733","2734","2735","2736","2737","2738","2739","2740","2741","2742","2743","2744","2745","2746","2747","2748","2749","2750","2751","2752","2753","2754","2755","2756","2757","2758","2759","2760","2761","2762","2763","2764","2765","2766","2767","2768","2769","2770","2771","2772","2773","2774","2775","2776","2777","2778","2779","2780","2781","2782","2783","2784","2785","2786","2787","2788","2789","2790","2791","2792","2793","2794","2795","2796","2797","2798","2799","2800","2801","2802","2803","2804","2805","2806","2807","2808","2809","2810","2811","2812","2813","2814","2815","2816","2817","2818","2819","2820","2821","2822","2823","2824","2825","2826","2827","2828","2829","2830","2831","2832","2833","2834","2835","2836","2837","2838","2839","2840","2841","2842","2843","2844","2845","2846","2847","2848","2849","2850","2851","2852","2853","2854","2855","2856","2857","2858","2859","2860","2861","2862","2863","2864","2865","2866","2867","2868","2869","2870","2871","2872","2873","2874","2875","2876","2877","2878","2879","2880","2881","2882","2883","2884","2885","2886","2887","2888","2889","2890","2891","2892","2893","2894","2895","2896","2897","2898","2899","2900","2901","2902","2903","2904","2905","2906","2907","2908","2909","2910","2911","2912","2913","2914","2915","2916","2917","2918","2919","2920","2921","2922","2923","2924","2925","2926","2927","2928","2929","2930","2931","2932","2933","2934","2935","2936","2937","2938","2939","2940","2941","2942","2943","2944","2945","2946","2947","2948","2949","2950","2951","2952","2953","2954","2955","2956","2957","2958","2959","2960","2961","2962","2963","2964","2965","2966","2967","2968","2969","2970","2971","2972","2973","2974","2975","2976","2977","2978","2979","2980","2981","2982","2983","2984","2985","2986","2987","2988","2989","2990","2991","2992","2993","2994","2995","2996","2997","2998","2999","3000","3001","3002","3003","3004","3005","3006","3007","3008","3009","3010","3011","3012","3013","3014","3015","3016","3017","3018","3019","3020","3021","3022","3023","3024","3025","3026","3027","3028","3029","3030","3031","3032","3033","3034","3035","3036","3037","3038","3039","3040","3041","3042","3043","3044","3045","3046","3047","3048","3049","3050","3051","3052","3053","3054","3055","3056","3057","3058","3059","3060","3061","3062","3063","3064","3065","3066","3067","3068","3069","3070","3071","3072","3073","3074","3075","3076","3077","3078","3079","3080","3081","3082","3083","3084","3085","3086","3087","3088","3089","3090","3091","3092","3093","3094","3095","3096","3097","3098","3099","3100","3101","3102","3103","3104","3105","3106","3107","3108","3109","3110","3111","3112","3113","3114","3115","3116","3117","3118","3119","3120","3121","3122","3123","3124","3125","3126","3127","3128","3129","3130","3131","3132","3133","3134","3135","3136","3137","3138","3139","3140","3141","3142","3143","3144","3145","3146","3147","3148","3149","3150","3151","3152","3153","3154","3155","3156","3157","3158","3159","3160","3161","3162","3163","3164","3165","3166","3167","3168","3169","3170","3171","3172","3173","3174","3175","3176","3177","3178","3179","3180","3181","3182","3183","3184","3185","3186","3187","3188","3189","3190","3191","3192","3193","3194","3195","3196","3197","3198","3199","3200","3201","3202","3203","3204","3205","3206","3207","3208","3209","3210","3211","3212","3213","3214","3215","3216","3217","3218","3219","3220","3221","3222","3223","3224","3225","3226","3227","3228","3229","3230","3231","3232","3233","3234","3235","3236","3237","3238","3239","3240","3241","3242","3243","3244","3245","3246","3247","3248","3249","3250","3251","3252","3253","3254","3255","3256","3257","3258","3259","3260","3261","3262","3263","3264","3265","3266","3267","3268","3269","3270","3271","3272","3273","3274","3275","3276","3277","3278","3279","3280","3281","3282","3283","3284","3285","3286","3287","3288","3289","3290","3291","3292","3293","3294","3295","3296","3297","3298","3299","3300","3301","3302","3303","3304","3305","3306","3307","3308","3309","3310","3311","3312","3313","3314","3315","3316","3317","3318","3319","3320","3321","3322","3323","3324","3325","3326","3327","3328","3329","3330","3331","3332","3333","3334","3335","3336","3337","3338","3339","3340","3341","3342","3343","3344","3345","3346","3347","3348","3349","3350","3351","3352","3353","3354","3355","3356","3357","3358","3359","3360","3361","3362","3363","3364","3365","3366","3367","3368","3369","3370","3371","3372","3373","3374","3375","3376","3377","3378","3379","3380","3381","3382","3383","3384","3385","3386","3387","3388","3389","3390","3391","3392","3393","3394","3395","3396","3397","3398","3399","3400","3401","3402","3403","3404","3405","3406","3407","3408","3409","3410","3411","3412","3413","3414","3415","3416","3417","3418","3419","3420","3421","3422","3423","3424","3425","3426","3427","3428","3429","3430","3431","3432","3433","3434","3435","3436","3437","3438","3439","3440","3441","3442","3443","3444","3445","3446","3447","3448","3449","3450","3451","3452","3453","3454","3455","3456","3457","3458","3459","3460","3461","3462","3463","3464","3465","3466","3467","3468","3469","3470","3471","3472","3473","3474","3475","3476","3477","3478","3479","3480","3481","3482","3483","3484","3485","3486","3487","3488","3489","3490","3491","3492","3493","3494","3495","3496","3497","3498","3499","3500","3501","3502","3503","3504","3505","3506","3507","3508","3509","3510","3511","3512","3513","3514","3515","3516","3517","3518","3519","3520","3521","3522","3523","3524","3525","3526","3527","3528","3529","3530","3531","3532","3533","3534","3535","3536","3537","3538","3539","3540","3541","3542","3543","3544","3545","3546","3547","3548","3549","3550","3551","3552","3553","3554","3555","3556","3557","3558","3559","3560","3561","3562","3563","3564","3565","3566","3567","3568","3569","3570","3571","3572","3573","3574","3575","3576","3577","3578","3579","3580","3581","3582","3583","3584","3585","3586","3587","3588","3589","3590","3591","3592","3593","3594","3595","3596","3597","3598","3599","3600","3601","3602","3603","3604","3605","3606","3607","3608","3609","3610","3611","3612","3613","3614","3615","3616","3617","3618","3619","3620","3621","3622","3623","3624","3625","3626","3627","3628","3629","3630","3631","3632","3633","3634","3635","3636","3637","3638","3639","3640","3641","3642","3643","3644","3645","3646","3647","3648","3649","3650","3651","3652","3653","3654","3655","3656","3657","3658","3659","3660","3661","3662","3663","3664","3665","3666","3667","3668","3669","3670","3671","3672","3673","3674","3675","3676","3677","3678","3679","3680","3681","3682","3683","3684","3685","3686","3687","3688","3689","3690","3691","3692","3693","3694","3695","3696","3697","3698","3699","3700","3701","3702","3703","3704","3705","3706","3707","3708","3709","3710","3711","3712","3713","3714","3715","3716","3717","3718","3719","3720","3721","3722","3723","3724","3725","3726","3727","3728","3729","3730","3731","3732","3733","3734","3735","3736","3737","3738","3739","3740","3741","3742","3743","3744","3745","3746","3747","3748","3749","3750","3751","3752","3753","3754","3755","3756","3757","3758","3759","3760","3761","3762","3763","3764","3765","3766","3767","3768","3769","3770","3771","3772","3773","3774","3775","3776","3777","3778","3779","3780","3781","3782","3783","3784","3785","3786","3787","3788","3789","3790","3791","3792","3793","3794","3795","3796","3797","3798","3799","3800","3801","3802","3803","3804","3805","3806","3807","3808","3809","3810","3811","3812","3813","3814","3815","3816","3817","3818","3819","3820","3821","3822","3823","3824","3825","3826","3827","3828","3829","3830","3831","3832","3833","3834","3835","3836","3837","3838","3839","3840","3841","3842","3843","3844","3845","3846","3847","3848","3849","3850","3851","3852","3853","3854","3855","3856","3857","3858","3859","3860","3861","3862","3863","3864","3865","3866","3867","3868","3869","3870","3871","3872","3873","3874","3875","3876","3877","3878","3879","3880","3881","3882","3883","3884","3885","3886","3887","3888","3889","3890","3891","3892","3893","3894","3895","3896","3897","3898","3899","3900","3901","3902","3903","3904","3905","3906","3907","3908","3909","3910","3911","3912","3913","3914","3915","3916","3917","3918","3919","3920","3921","3922","3923","3924","3925","3926","3927","3928","3929","3930","3931","3932","3933","3934","3935","3936","3937","3938","3939","3940","3941","3942","3943","3944","3945","3946","3947","3948","3949","3950","3951","3952","3953","3954","3955","3956","3957","3958","3959","3960","3961","3962","3963","3964","3965","3966","3967","3968","3969","3970","3971","3972","3973","3974","3975","3976","3977","3978","3979","3980","3981","3982","3983","3984","3985","3986","3987","3988","3989","3990","3991","3992","3993","3994","3995","3996","3997","3998","3999","4000","4001","4002","4003","4004","4005","4006","4007","4008","4009","4010","4011","4012","4013","4014","4015","4016","4017","4018","4019","4020","4021","4022","4023","4024","4025","4026","4027","4028","4029","4030","4031","4032","4033","4034","4035","4036","4037","4038","4039","4040","4041","4042","4043","4044","4045","4046","4047","4048","4049","4050","4051","4052","4053","4054","4055","4056","4057","4058","4059","4060","4061","4062","4063","4064","4065","4066","4067","4068","4069","4070","4071","4072","4073","4074","4075","4076","4077","4078","4079","4080","4081","4082","4083","4084","4085","4086","4087","4088","4089","4090","4091","4092","4093","4094","4095","4096","4097","4098","4099","4100","4101","4102","4103","4104","4105","4106","4107","4108","4109","4110","4111","4112","4113","4114","4115","4116","4117","4118","4119","4120","4121","4122","4123","4124","4125","4126","4127","4128","4129","4130","4131","4132","4133","4134","4135","4136","4137","4138","4139","4140","4141","4142","4143","4144","4145","4146","4147","4148","4149","4150","4151","4152","4153","4154","4155","4156","4157","4158","4159","4160","4161","4162","4163","4164","4165","4166","4167","4168","4169","4170","4171","4172","4173","4174","4175","4176","4177","4178","4179","4180","4181","4182","4183","4184","4185","4186","4187","4188","4189","4190","4191","4192","4193","4194","4195","4196","4197","4198","4199","4200","4201","4202","4203","4204","4205","4206","4207","4208","4209","4210","4211","4212","4213","4214","4215","4216","4217","4218","4219","4220","4221","4222","4223","4224","4225","4226","4227","4228","4229","4230","4231","4232","4233","4234","4235","4236","4237","4238","4239","4240","4241","4242","4243","4244","4245","4246","4247","4248","4249","4250","4251","4252","4253","4254","4255","4256","4257","4258","4259","4260","4261","4262","4263","4264","4265","4266","4267","4268","4269","4270","4271","4272","4273","4274","4275","4276","4277","4278","4279","4280","4281","4282","4283","4284","4285","4286","4287","4288","4289","4290","4291","4292","4293","4294","4295","4296","4297","4298","4299","4300","4301","4302","4303","4304","4305","4306","4307","4308","4309","4310","4311","4312","4313","4314","4315","4316","4317","4318","4319","4320","4321","4322","4323","4324","4325","4326","4327","4328","4329","4330","4331","4332","4333","4334","4335","4336","4337","4338","4339","4340","4341","4342","4343","4344","4345","4346","4347","4348","4349","4350","4351","4352","4353","4354","4355","4356","4357","4358","4359","4360","4361","4362","4363","4364","4365","4366","4367","4368","4369","4370","4371","4372","4373","4374","4375","4376","4377","4378","4379","4380","4381","4382","4383","4384","4385","4386","4387","4388","4389","4390","4391","4392","4393","4394","4395","4396","4397","4398","4399","4400","4401","4402","4403","4404","4405","4406","4407","4408","4409","4410","4411","4412","4413","4414","4415","4416","4417","4418","4419","4420","4421","4422","4423","4424","4425","4426","4427","4428","4429","4430","4431","4432","4433","4434","4435","4436","4437","4438","4439","4440","4441","4442","4443","4444","4445","4446","4447","4448","4449","4450","4451","4452","4453","4454","4455","4456","4457","4458","4459","4460","4461","4462","4463","4464","4465","4466","4467","4468","4469","4470","4471","4472","4473","4474","4475","4476","4477","4478","4479","4480","4481","4482","4483","4484","4485","4486","4487","4488","4489","4490","4491","4492","4493","4494","4495","4496","4497","4498","4499","4500","4501","4502","4503","4504","4505","4506","4507","4508","4509","4510","4511","4512","4513","4514","4515","4516","4517","4518","4519","4520","4521","4522","4523","4524","4525","4526","4527","4528","4529","4530","4531","4532","4533","4534","4535","4536","4537","4538","4539","4540","4541","4542","4543","4544","4545","4546","4547","4548","4549","4550","4551","4552","4553","4554","4555","4556","4557","4558","4559","4560","4561","4562","4563","4564","4565","4566","4567","4568","4569","4570","4571","4572","4573","4574","4575","4576","4577","4578","4579","4580","4581","4582","4583","4584","4585","4586","4587","4588","4589","4590","4591","4592","4593","4594","4595","4596","4597","4598","4599","4600","4601","4602","4603","4604","4605","4606","4607","4608","4609","4610","4611","4612","4613","4614","4615","4616","4617","4618","4619","4620","4621","4622","4623","4624","4625","4626","4627","4628","4629","4630","4631","4632","4633","4634","4635","4636","4637","4638","4639","4640","4641","4642","4643","4644","4645","4646","4647","4648","4649","4650","4651","4652","4653","4654","4655","4656","4657","4658","4659","4660","4661","4662","4663","4664","4665","4666","4667","4668","4669","4670","4671","4672","4673","4674","4675","4676","4677","4678","4679","4680","4681","4682","4683","4684","4685","4686","4687","4688","4689","4690","4691","4692","4693","4694","4695","4696","4697","4698","4699","4700","4701","4702","4703","4704","4705","4706","4707","4708","4709","4710","4711","4712","4713","4714","4715","4716","4717","4718","4719","4720","4721","4722","4723","4724","4725","4726","4727","4728","4729","4730","4731","4732","4733","4734","4735","4736","4737","4738","4739","4740","4741","4742","4743","4744","4745","4746","4747","4748","4749","4750","4751","4752","4753","4754","4755","4756","4757","4758","4759","4760","4761","4762","4763","4764","4765","4766","4767","4768","4769","4770","4771","4772","4773","4774","4775","4776","4777","4778","4779","4780","4781","4782","4783","4784","4785","4786","4787","4788","4789","4790","4791","4792","4793","4794","4795","4796","4797","4798","4799","4800","4801","4802","4803","4804","4805","4806","4807","4808","4809","4810","4811","4812","4813","4814","4815","4816","4817","4818","4819","4820","4821","4822","4823","4824","4825","4826","4827","4828","4829","4830","4831","4832","4833","4834","4835","4836","4837","4838","4839","4840","4841","4842","4843","4844","4845","4846","4847","4848","4849","4850","4851","4852","4853","4854","4855","4856","4857","4858","4859","4860","4861","4862","4863","4864","4865","4866","4867","4868","4869","4870","4871","4872","4873","4874","4875","4876","4877","4878","4879","4880","4881","4882","4883","4884","4885","4886","4887","4888","4889","4890","4891","4892","4893","4894","4895","4896","4897","4898","4899","4900","4901","4902","4903","4904","4905","4906","4907","4908","4909","4910","4911","4912","4913","4914","4915","4916","4917","4918","4919","4920","4921","4922","4923","4924","4925","4926","4927","4928","4929","4930","4931","4932","4933","4934","4935","4936","4937","4938","4939","4940","4941","4942","4943","4944","4945","4946","4947","4948","4949","4950","4951","4952","4953","4954","4955","4956","4957","4958","4959","4960","4961","4962","4963","4964","4965","4966","4967","4968","4969","4970","4971","4972","4973","4974","4975","4976","4977","4978","4979","4980","4981","4982","4983","4984","4985","4986","4987","4988","4989","4990","4991","4992","4993","4994","4995","4996","4997","4998","4999","5000","5001","5002","5003","5004","5005","5006","5007","5008","5009","5010","5011","5012","5013","5014","5015","5016","5017","5018","5019","5020","5021","5022","5023","5024","5025","5026","5027","5028","5029","5030","5031","5032","5033","5034","5035","5036","5037","5038","5039","5040","5041","5042","5043","5044","5045","5046","5047","5048","5049","5050","5051","5052","5053","5054","5055","5056","5057","5058","5059","5060","5061","5062","5063","5064","5065","5066","5067","5068","5069","5070","5071","5072","5073","5074","5075","5076","5077","5078","5079","5080","5081","5082","5083","5084","5085","5086","5087","5088","5089","5090","5091","5092","5093","5094","5095","5096","5097","5098","5099","5100","5101","5102","5103","5104","5105","5106","5107","5108","5109","5110","5111","5112","5113","5114","5115","5116","5117","5118","5119","5120","5121","5122","5123","5124","5125","5126","5127","5128","5129","5130","5131","5132","5133","5134","5135","5136","5137","5138","5139","5140","5141","5142","5143","5144","5145","5146","5147","5148","5149","5150","5151","5152","5153","5154","5155","5156","5157","5158","5159","5160","5161","5162","5163","5164","5165","5166","5167","5168","5169","5170","5171","5172","5173","5174","5175","5176","5177","5178","5179","5180","5181","5182","5183","5184","5185","5186","5187","5188","5189","5190","5191","5192","5193","5194","5195","5196","5197","5198","5199","5200","5201","5202","5203","5204","5205","5206","5207","5208","5209","5210","5211","5212","5213","5214","5215","5216","5217","5218","5219","5220","5221","5222","5223","5224","5225","5226","5227","5228","5229","5230","5231","5232","5233","5234","5235","5236","5237","5238","5239","5240","5241","5242","5243","5244","5245","5246","5247","5248","5249","5250","5251","5252","5253","5254","5255","5256","5257","5258","5259","5260","5261","5262","5263","5264","5265","5266","5267","5268","5269","5270","5271","5272","5273","5274","5275","5276","5277","5278","5279","5280","5281","5282","5283","5284","5285","5286","5287","5288","5289","5290","5291","5292","5293","5294","5295","5296","5297","5298","5299","5300","5301","5302","5303","5304","5305","5306","5307","5308","5309","5310","5311","5312","5313","5314","5315","5316","5317","5318","5319","5320","5321","5322","5323","5324","5325","5326","5327","5328","5329","5330","5331","5332","5333","5334","5335","5336","5337","5338","5339","5340","5341","5342","5343","5344","5345","5346","5347","5348","5349","5350","5351","5352","5353","5354","5355","5356","5357","5358","5359","5360","5361","5362","5363","5364","5365","5366","5367","5368","5369","5370","5371","5372","5373","5374","5375","5376","5377","5378","5379","5380","5381","5382","5383","5384","5385","5386","5387","5388","5389","5390","5391","5392","5393","5394","5395","5396","5397","5398","5399","5400","5401","5402","5403","5404","5405","5406","5407","5408","5409","5410","5411","5412","5413","5414","5415","5416","5417","5418","5419","5420","5421","5422","5423","5424","5425","5426","5427","5428","5429","5430","5431","5432","5433","5434","5435","5436","5437","5438","5439","5440","5441","5442","5443","5444","5445","5446","5447","5448","5449","5450","5451","5452","5453","5454","5455","5456","5457","5458","5459","5460","5461","5462","5463","5464","5465","5466","5467","5468","5469","5470","5471","5472","5473","5474","5475","5476","5477","5478","5479","5480","5481","5482","5483","5484","5485","5486","5487","5488","5489","5490","5491","5492","5493","5494","5495","5496","5497","5498","5499","5500","5501","5502","5503","5504","5505","5506","5507","5508","5509","5510","5511","5512","5513","5514","5515","5516","5517","5518","5519","5520","5521","5522","5523","5524","5525","5526","5527","5528","5529","5530","5531","5532","5533","5534","5535","5536","5537","5538","5539","5540","5541","5542","5543","5544","5545","5546","5547","5548","5549","5550","5551","5552","5553","5554","5555","5556","5557","5558","5559","5560","5561","5562","5563","5564","5565","5566","5567","5568","5569","5570","5571","5572","5573","5574","5575","5576","5577","5578","5579","5580","5581","5582","5583","5584","5585","5586","5587","5588","5589","5590","5591","5592","5593","5594","5595","5596","5597","5598","5599","5600","5601","5602","5603","5604","5605","5606","5607","5608","5609","5610","5611","5612","5613","5614","5615","5616","5617","5618","5619","5620","5621","5622","5623","5624","5625","5626","5627","5628","5629","5630","5631","5632","5633","5634","5635","5636","5637","5638","5639","5640","5641","5642","5643","5644","5645","5646","5647","5648","5649","5650","5651","5652","5653","5654","5655","5656","5657","5658","5659","5660","5661","5662","5663","5664","5665","5666","5667","5668","5669","5670","5671","5672","5673","5674","5675","5676","5677","5678","5679","5680","5681","5682","5683","5684","5685","5686","5687","5688","5689","5690","5691","5692","5693","5694","5695","5696","5697","5698","5699","5700","5701","5702","5703","5704","5705","5706","5707","5708","5709","5710","5711","5712","5713","5714","5715","5716","5717","5718","5719","5720","5721","5722","5723","5724","5725","5726","5727","5728","5729","5730","5731","5732","5733","5734","5735","5736","5737","5738","5739","5740","5741","5742","5743","5744","5745","5746","5747","5748","5749","5750","5751","5752","5753","5754","5755","5756","5757","5758","5759","5760","5761","5762","5763","5764","5765","5766","5767","5768","5769","5770","5771","5772","5773","5774","5775","5776","5777","5778","5779","5780","5781","5782","5783","5784","5785","5786","5787","5788","5789","5790","5791","5792","5793","5794","5795","5796","5797","5798","5799","5800","5801","5802","5803","5804","5805","5806","5807","5808","5809","5810","5811","5812","5813","5814","5815","5816","5817","5818","5819","5820","5821","5822","5823","5824","5825","5826","5827","5828","5829","5830","5831","5832","5833","5834","5835","5836","5837","5838","5839","5840","5841","5842","5843","5844","5845","5846","5847","5848","5849","5850","5851","5852","5853","5854","5855","5856","5857","5858","5859","5860","5861","5862","5863","5864","5865","5866","5867","5868","5869","5870","5871","5872","5873","5874","5875","5876","5877","5878","5879","5880","5881","5882","5883","5884","5885","5886","5887","5888","5889","5890","5891","5892","5893","5894","5895","5896","5897","5898","5899","5900","5901","5902","5903","5904","5905","5906","5907","5908","5909","5910","5911","5912","5913","5914","5915","5916","5917","5918","5919","5920","5921","5922","5923","5924","5925","5926","5927","5928","5929","5930","5931","5932","5933","5934","5935","5936","5937","5938","5939","5940","5941","5942","5943","5944","5945","5946","5947","5948","5949","5950","5951","5952","5953","5954","5955","5956","5957","5958","5959","5960","5961","5962","5963","5964","5965","5966","5967","5968","5969","5970","5971","5972","5973","5974","5975","5976","5977","5978","5979","5980","5981","5982","5983","5984","5985","5986","5987","5988","5989","5990","5991","5992","5993","5994","5995","5996","5997","5998","5999","6000","6001","6002","6003","6004","6005","6006","6007","6008","6009","6010","6011","6012","6013","6014","6015","6016","6017","6018","6019","6020","6021","6022","6023","6024","6025","6026","6027","6028","6029","6030","6031","6032","6033","6034","6035","6036","6037","6038","6039","6040","6041","6042","6043","6044","6045","6046","6047","6048","6049","6050","6051","6052","6053","6054","6055","6056","6057","6058","6059","6060","6061","6062","6063","6064","6065","6066","6067","6068","6069","6070","6071","6072","6073","6074","6075","6076","6077","6078","6079","6080","6081","6082","6083","6084","6085","6086","6087","6088","6089","6090","6091","6092","6093","6094","6095","6096","6097","6098","6099","6100","6101","6102","6103","6104","6105","6106","6107","6108","6109","6110","6111","6112","6113","6114","6115","6116","6117","6118","6119","6120","6121","6122","6123","6124","6125","6126","6127","6128","6129","6130","6131","6132","6133","6134","6135","6136","6137","6138","6139","6140","6141","6142","6143","6144","6145","6146","6147","6148","6149","6150","6151","6152","6153","6154","6155","6156","6157","6158","6159","6160","6161","6162","6163","6164","6165","6166","6167","6168","6169","6170","6171","6172","6173","6174","6175","6176","6177","6178","6179","6180","6181","6182","6183","6184","6185","6186","6187","6188","6189","6190","6191","6192","6193","6194","6195","6196","6197","6198","6199","6200","6201","6202","6203","6204","6205","6206","6207","6208","6209","6210","6211","6212","6213","6214","6215","6216","6217","6218","6219","6220","6221","6222","6223","6224","6225","6226","6227","6228","6229","6230","6231","6232","6233","6234","6235","6236","6237","6238","6239","6240","6241","6242","6243","6244","6245","6246","6247","6248","6249","6250","6251","6252","6253","6254","6255","6256","6257","6258","6259","6260","6261","6262","6263","6264","6265","6266","6267","6268","6269","6270","6271","6272","6273","6274","6275","6276","6277","6278","6279","6280","6281","6282","6283","6284","6285","6286","6287","6288","6289","6290","6291","6292","6293","6294","6295","6296","6297","6298","6299","6300","6301","6302","6303","6304","6305","6306","6307","6308","6309","6310","6311","6312","6313","6314","6315","6316","6317","6318","6319","6320","6321","6322","6323","6324","6325","6326","6327","6328","6329","6330","6331","6332","6333","6334","6335","6336","6337","6338","6339","6340","6341","6342","6343","6344","6345","6346","6347","6348","6349","6350","6351","6352","6353","6354","6355","6356","6357","6358","6359","6360","6361","6362","6363","6364","6365","6366","6367","6368","6369","6370","6371","6372","6373","6374","6375","6376","6377","6378","6379","6380","6381","6382","6383","6384","6385","6386","6387","6388","6389","6390","6391","6392","6393","6394","6395","6396","6397","6398","6399","6400","6401","6402","6403","6404","6405","6406","6407","6408","6409","6410","6411","6412","6413","6414","6415","6416","6417","6418","6419","6420","6421","6422","6423","6424","6425","6426","6427","6428","6429","6430","6431","6432","6433","6434","6435","6436","6437","6438","6439","6440","6441","6442","6443","6444","6445","6446","6447","6448","6449","6450","6451","6452","6453","6454","6455","6456","6457","6458","6459","6460","6461","6462","6463","6464","6465","6466","6467","6468","6469","6470","6471","6472","6473","6474","6475","6476","6477","6478","6479","6480","6481","6482","6483","6484","6485","6486","6487","6488","6489","6490","6491","6492","6493","6494","6495","6496","6497","6498","6499","6500","6501","6502","6503","6504","6505","6506","6507","6508","6509","6510","6511","6512","6513","6514","6515","6516","6517","6518","6519","6520","6521","6522","6523","6524","6525","6526","6527","6528","6529","6530","6531","6532","6533","6534","6535","6536","6537","6538","6539","6540","6541","6542","6543","6544","6545","6546","6547","6548","6549","6550","6551","6552","6553","6554","6555","6556","6557","6558","6559","6560","6561","6562","6563","6564","6565","6566","6567","6568","6569","6570","6571","6572","6573","6574","6575","6576","6577","6578","6579","6580","6581","6582","6583","6584","6585","6586","6587","6588","6589","6590","6591","6592","6593","6594","6595","6596","6597","6598","6599","6600","6601","6602","6603","6604","6605","6606","6607","6608","6609","6610","6611","6612","6613","6614","6615","6616","6617","6618","6619","6620","6621","6622","6623","6624","6625","6626","6627","6628","6629","6630","6631","6632","6633","6634","6635","6636","6637","6638","6639","6640","6641","6642","6643","6644","6645","6646","6647","6648","6649","6650","6651","6652","6653","6654","6655","6656","6657","6658","6659","6660","6661","6662","6663","6664","6665","6666","6667","6668","6669","6670","6671","6672","6673","6674","6675","6676","6677","6678","6679","6680","6681","6682","6683","6684","6685","6686","6687","6688","6689","6690","6691","6692","6693","6694","6695","6696","6697","6698","6699","6700","6701","6702","6703","6704","6705","6706","6707","6708","6709","6710","6711","6712","6713","6714","6715","6716","6717","6718","6719","6720","6721","6722","6723","6724","6725","6726","6727","6728","6729","6730","6731","6732","6733","6734","6735","6736","6737","6738","6739","6740","6741","6742","6743","6744","6745","6746","6747","6748","6749","6750","6751","6752","6753","6754","6755","6756","6757","6758","6759","6760","6761","6762","6763","6764","6765","6766","6767","6768","6769","6770","6771","6772","6773","6774","6775","6776","6777","6778","6779","6780","6781","6782","6783","6784","6785","6786","6787","6788","6789","6790","6791","6792","6793","6794","6795","6796","6797","6798","6799","6800","6801","6802","6803","6804","6805","6806","6807","6808","6809","6810","6811","6812","6813","6814","6815","6816","6817","6818","6819","6820","6821","6822","6823","6824","6825","6826","6827","6828","6829","6830","6831","6832","6833","6834","6835","6836","6837","6838","6839","6840","6841","6842","6843","6844","6845","6846","6847","6848","6849","6850","6851","6852","6853","6854","6855","6856","6857","6858","6859","6860","6861","6862","6863","6864","6865","6866","6867","6868","6869","6870","6871","6872","6873","6874","6875","6876","6877","6878","6879","6880","6881","6882","6883","6884","6885","6886","6887","6888","6889","6890","6891","6892","6893","6894","6895","6896","6897","6898","6899","6900","6901","6902","6903","6904","6905","6906","6907","6908","6909","6910","6911","6912","6913","6914","6915","6916","6917","6918","6919","6920","6921","6922","6923","6924","6925","6926","6927","6928","6929","6930","6931","6932","6933","6934","6935","6936","6937","6938","6939","6940","6941","6942","6943","6944","6945","6946","6947","6948","6949","6950","6951","6952","6953","6954","6955","6956","6957","6958","6959","6960","6961","6962","6963","6964","6965","6966","6967","6968","6969","6970","6971","6972","6973","6974","6975","6976","6977","6978","6979","6980","6981","6982","6983","6984","6985","6986","6987","6988","6989","6990","6991","6992","6993","6994","6995","6996","6997","6998","6999","7000","7001","7002","7003","7004","7005","7006","7007","7008","7009","7010","7011","7012","7013","7014","7015","7016","7017","7018","7019","7020","7021","7022","7023","7024","7025","7026","7027","7028","7029","7030","7031","7032","7033","7034","7035","7036","7037","7038","7039","7040","7041","7042","7043","7044","7045","7046","7047","7048","7049","7050","7051","7052","7053","7054","7055","7056","7057","7058","7059","7060","7061","7062","7063","7064","7065","7066","7067","7068","7069","7070","7071","7072","7073","7074","7075","7076","7077","7078","7079","7080","7081","7082","7083","7084","7085","7086","7087","7088","7089","7090","7091","7092","7093","7094","7095","7096","7097","7098","7099","7100","7101","7102","7103","7104","7105","7106","7107","7108","7109","7110","7111","7112","7113","7114","7115","7116","7117","7118","7119","7120","7121","7122","7123","7124","7125","7126","7127","7128","7129","7130","7131","7132","7133","7134","7135","7136","7137","7138","7139","7140","7141","7142","7143","7144","7145","7146","7147","7148","7149","7150","7151","7152","7153","7154","7155","7156","7157","7158","7159","7160","7161","7162","7163","7164","7165","7166","7167","7168","7169","7170","7171","7172","7173","7174","7175","7176","7177","7178","7179","7180","7181","7182","7183","7184","7185","7186","7187","7188","7189","7190","7191","7192","7193","7194","7195","7196","7197","7198","7199","7200","7201","7202","7203","7204","7205","7206","7207","7208","7209","7210","7211","7212","7213","7214","7215","7216","7217","7218","7219","7220","7221","7222","7223","7224","7225","7226","7227","7228","7229","7230","7231","7232","7233","7234","7235","7236","7237","7238","7239","7240","7241","7242","7243","7244","7245","7246","7247","7248","7249","7250","7251","7252","7253","7254","7255","7256","7257","7258","7259","7260","7261","7262","7263","7264","7265","7266","7267","7268","7269","7270","7271","7272","7273","7274","7275","7276","7277","7278","7279","7280","7281","7282","7283","7284","7285","7286","7287","7288","7289","7290","7291","7292","7293","7294","7295","7296","7297","7298","7299","7300","7301","7302","7303","7304","7305","7306","7307","7308","7309","7310","7311","7312","7313","7314","7315","7316","7317","7318","7319","7320","7321","7322","7323","7324","7325","7326","7327","7328","7329","7330","7331","7332","7333","7334","7335","7336","7337","7338","7339","7340","7341","7342","7343","7344","7345","7346","7347","7348","7349","7350","7351","7352","7353","7354","7355","7356","7357","7358","7359","7360","7361","7362","7363","7364","7365","7366","7367","7368","7369","7370","7371","7372","7373","7374","7375","7376","7377","7378","7379","7380","7381","7382","7383","7384","7385","7386","7387","7388","7389","7390","7391","7392","7393","7394","7395","7396","7397","7398","7399","7400","7401","7402","7403","7404","7405","7406","7407","7408","7409","7410","7411","7412","7413","7414","7415","7416","7417","7418","7419","7420","7421","7422","7423","7424","7425","7426","7427","7428","7429","7430","7431","7432","7433","7434","7435","7436","7437","7438","7439","7440","7441","7442","7443","7444","7445","7446","7447","7448","7449","7450","7451","7452","7453","7454","7455","7456","7457","7458","7459","7460","7461","7462","7463","7464","7465","7466","7467","7468","7469","7470","7471","7472","7473","7474","7475","7476","7477","7478","7479","7480","7481","7482","7483","7484","7485","7486","7487","7488","7489","7490","7491","7492","7493","7494","7495","7496","7497","7498","7499","7500","7501","7502","7503","7504","7505","7506","7507","7508","7509","7510","7511","7512","7513","7514","7515","7516","7517","7518","7519","7520","7521","7522","7523","7524","7525","7526","7527","7528","7529","7530","7531","7532","7533","7534","7535","7536","7537","7538","7539","7540","7541","7542","7543","7544","7545","7546","7547","7548","7549","7550","7551","7552","7553","7554","7555","7556","7557","7558","7559","7560","7561","7562","7563","7564","7565","7566","7567","7568","7569","7570","7571","7572","7573","7574","7575","7576","7577","7578","7579","7580","7581","7582","7583","7584","7585","7586","7587","7588","7589","7590","7591","7592","7593","7594","7595","7596","7597","7598","7599","7600","7601","7602","7603","7604","7605","7606","7607","7608","7609","7610","7611","7612","7613","7614","7615","7616","7617","7618","7619","7620","7621","7622","7623","7624","7625","7626","7627","7628","7629","7630","7631","7632","7633","7634","7635","7636","7637","7638","7639","7640","7641","7642","7643","7644","7645","7646","7647","7648","7649","7650","7651","7652","7653","7654","7655","7656","7657","7658","7659","7660","7661","7662","7663","7664","7665","7666","7667","7668","7669","7670","7671","7672","7673","7674","7675","7676","7677","7678","7679","7680","7681","7682","7683","7684","7685","7686","7687","7688","7689","7690","7691","7692","7693","7694","7695","7696","7697","7698","7699","7700","7701","7702","7703","7704","7705","7706","7707","7708","7709","7710","7711","7712","7713","7714","7715","7716","7717","7718","7719","7720","7721","7722","7723","7724","7725","7726","7727","7728","7729","7730","7731","7732","7733","7734","7735","7736","7737","7738","7739","7740","7741","7742","7743","7744","7745","7746","7747","7748","7749","7750","7751","7752","7753","7754","7755","7756","7757","7758","7759","7760","7761","7762","7763","7764","7765","7766","7767","7768","7769","7770","7771","7772","7773","7774","7775","7776","7777","7778","7779","7780","7781","7782","7783","7784","7785","7786","7787","7788","7789","7790","7791","7792","7793","7794","7795","7796","7797","7798","7799","7800","7801","7802","7803","7804","7805","7806","7807","7808","7809","7810","7811","7812","7813","7814","7815","7816","7817","7818","7819","7820","7821","7822","7823","7824","7825","7826","7827","7828","7829","7830","7831","7832","7833","7834","7835","7836","7837","7838","7839","7840","7841","7842","7843","7844","7845","7846","7847","7848","7849","7850","7851","7852","7853","7854","7855","7856","7857","7858","7859","7860","7861","7862","7863","7864","7865","7866","7867","7868","7869","7870","7871","7872","7873","7874","7875","7876","7877","7878","7879","7880","7881","7882","7883","7884","7885","7886","7887","7888","7889","7890","7891","7892","7893","7894","7895","7896","7897","7898","7899","7900","7901","7902","7903","7904","7905","7906","7907","7908","7909","7910","7911","7912","7913","7914","7915","7916","7917","7918","7919","7920","7921","7922","7923","7924","7925","7926","7927","7928","7929","7930","7931","7932","7933","7934","7935","7936","7937","7938","7939","7940","7941","7942","7943","7944","7945","7946","7947","7948","7949","7950","7951","7952","7953","7954","7955","7956","7957","7958","7959","7960","7961","7962","7963","7964","7965","7966","7967","7968","7969","7970","7971","7972","7973","7974","7975","7976","7977","7978","7979","7980","7981","7982","7983","7984","7985","7986","7987","7988","7989","7990","7991","7992","7993","7994","7995","7996","7997","7998","7999","8000","8001","8002","8003","8004","8005","8006","8007","8008","8009","8010","8011","8012","8013","8014","8015","8016","8017","8018","8019","8020","8021","8022","8023","8024","8025","8026","8027","8028","8029","8030","8031","8032","8033","8034","8035","8036","8037","8038","8039","8040","8041","8042","8043","8044","8045","8046","8047","8048","8049","8050","8051","8052","8053","8054","8055","8056","8057","8058","8059","8060","8061","8062","8063","8064","8065","8066","8067","8068","8069","8070","8071","8072","8073","8074","8075","8076","8077","8078","8079","8080","8081","8082","8083","8084","8085","8086","8087","8088","8089","8090","8091","8092","8093","8094","8095","8096","8097","8098","8099","8100","8101","8102","8103","8104","8105","8106","8107","8108","8109","8110","8111","8112","8113","8114","8115","8116","8117","8118","8119","8120","8121","8122","8123","8124","8125","8126","8127","8128","8129","8130","8131","8132","8133","8134","8135","8136","8137","8138","8139","8140","8141","8142","8143","8144","8145","8146","8147","8148","8149","8150","8151","8152","8153","8154","8155","8156","8157","8158","8159","8160","8161","8162","8163","8164","8165","8166","8167","8168","8169","8170","8171","8172","8173","8174","8175","8176","8177","8178","8179","8180","8181","8182","8183","8184","8185","8186","8187","8188","8189","8190","8191","8192","8193","8194","8195","8196","8197","8198","8199","8200","8201","8202","8203","8204","8205","8206","8207","8208","8209","8210","8211","8212","8213","8214","8215","8216","8217","8218","8219","8220","8221","8222","8223","8224","8225","8226","8227","8228","8229","8230","8231","8232","8233","8234","8235","8236","8237","8238","8239","8240","8241","8242","8243","8244","8245","8246","8247","8248","8249","8250","8251","8252","8253","8254","8255","8256","8257","8258","8259","8260","8261","8262","8263","8264","8265","8266","8267","8268","8269","8270","8271","8272","8273","8274","8275","8276","8277","8278","8279","8280","8281","8282","8283","8284","8285","8286","8287","8288","8289","8290","8291","8292","8293","8294","8295","8296","8297","8298","8299","8300","8301","8302","8303","8304","8305","8306","8307","8308","8309","8310","8311","8312","8313","8314","8315","8316","8317","8318","8319","8320","8321","8322","8323","8324","8325","8326","8327","8328","8329","8330","8331","8332","8333","8334","8335","8336","8337","8338","8339","8340","8341","8342","8343","8344","8345","8346","8347","8348","8349","8350","8351","8352","8353","8354","8355","8356","8357","8358","8359","8360","8361","8362","8363","8364","8365","8366","8367","8368","8369","8370","8371","8372","8373","8374","8375","8376","8377","8378","8379","8380","8381","8382","8383","8384","8385","8386","8387","8388","8389","8390","8391","8392","8393","8394","8395","8396","8397","8398","8399","8400","8401","8402","8403","8404","8405","8406","8407","8408","8409","8410","8411","8412","8413","8414","8415","8416","8417","8418","8419","8420","8421","8422","8423","8424","8425","8426","8427","8428","8429","8430","8431","8432","8433","8434","8435","8436","8437","8438","8439","8440","8441","8442","8443","8444","8445","8446","8447","8448","8449","8450","8451","8452","8453","8454","8455","8456","8457","8458","8459","8460","8461","8462","8463","8464","8465","8466","8467","8468","8469","8470","8471","8472","8473","8474","8475","8476","8477","8478","8479","8480","8481","8482","8483","8484","8485","8486","8487","8488","8489","8490","8491","8492","8493","8494","8495","8496","8497","8498","8499","8500","8501","8502","8503","8504","8505","8506","8507","8508","8509","8510","8511","8512","8513","8514","8515","8516","8517","8518","8519","8520","8521","8522","8523","8524","8525","8526","8527","8528","8529","8530","8531","8532","8533","8534","8535","8536","8537","8538","8539","8540","8541","8542","8543","8544","8545","8546","8547","8548","8549","8550","8551","8552","8553","8554","8555","8556","8557","8558","8559","8560","8561","8562","8563","8564","8565","8566","8567","8568","8569","8570","8571","8572","8573","8574","8575","8576","8577","8578","8579","8580","8581","8582","8583","8584","8585","8586","8587","8588","8589","8590","8591","8592","8593","8594","8595","8596","8597","8598","8599","8600","8601","8602","8603","8604","8605","8606","8607","8608","8609","8610","8611","8612","8613","8614","8615","8616","8617","8618","8619","8620","8621","8622","8623","8624","8625","8626","8627","8628","8629","8630","8631","8632","8633","8634","8635","8636","8637","8638","8639","8640","8641","8642","8643","8644","8645","8646","8647","8648","8649","8650","8651","8652","8653","8654","8655","8656","8657","8658","8659","8660","8661","8662","8663","8664","8665","8666","8667","8668","8669","8670","8671","8672","8673","8674","8675","8676","8677","8678","8679","8680","8681","8682","8683","8684","8685","8686","8687","8688","8689","8690","8691","8692","8693","8694","8695","8696","8697","8698","8699","8700","8701","8702","8703","8704","8705","8706","8707","8708","8709","8710","8711","8712","8713","8714","8715","8716","8717","8718","8719","8720","8721","8722","8723","8724","8725","8726","8727","8728","8729","8730","8731","8732","8733","8734","8735","8736","8737","8738","8739","8740","8741","8742","8743","8744","8745","8746","8747","8748","8749","8750","8751","8752","8753","8754","8755","8756","8757","8758","8759","8760","8761","8762","8763","8764","8765","8766","8767","8768","8769","8770","8771","8772","8773","8774","8775","8776","8777","8778","8779","8780","8781","8782","8783","8784","8785","8786","8787","8788","8789","8790","8791","8792","8793","8794","8795","8796","8797","8798","8799","8800","8801","8802","8803","8804","8805","8806","8807","8808","8809","8810","8811","8812","8813","8814","8815","8816","8817","8818","8819","8820","8821","8822","8823","8824","8825","8826","8827","8828","8829","8830","8831","8832","8833","8834","8835","8836","8837","8838","8839","8840","8841","8842","8843","8844","8845","8846","8847","8848","8849","8850","8851","8852","8853","8854","8855","8856","8857","8858","8859","8860","8861","8862","8863","8864","8865","8866","8867","8868","8869","8870","8871","8872","8873","8874","8875","8876","8877","8878","8879","8880","8881","8882","8883","8884","8885","8886","8887","8888","8889","8890","8891","8892","8893","8894","8895","8896","8897","8898","8899","8900","8901","8902","8903","8904","8905","8906","8907","8908","8909","8910","8911","8912","8913","8914","8915","8916","8917","8918","8919","8920","8921","8922","8923","8924","8925","8926","8927","8928","8929","8930","8931","8932","8933","8934","8935","8936","8937","8938","8939","8940","8941","8942","8943","8944","8945","8946","8947","8948","8949","8950","8951","8952","8953","8954","8955","8956","8957","8958","8959","8960","8961","8962","8963","8964","8965","8966","8967","8968","8969","8970","8971","8972","8973","8974","8975","8976","8977","8978","8979","8980","8981","8982","8983","8984","8985","8986","8987","8988","8989","8990","8991","8992","8993","8994","8995","8996","8997","8998","8999","9000","9001","9002","9003","9004","9005","9006","9007","9008","9009","9010","9011","9012","9013","9014","9015","9016","9017","9018","9019","9020","9021","9022","9023","9024","9025","9026","9027","9028","9029","9030","9031","9032","9033","9034","9035","9036","9037","9038","9039","9040","9041","9042","9043","9044","9045","9046","9047","9048","9049","9050","9051","9052","9053","9054","9055","9056","9057","9058","9059","9060","9061","9062","9063","9064","9065","9066","9067","9068","9069","9070","9071","9072","9073","9074","9075","9076","9077","9078","9079","9080","9081","9082","9083","9084","9085","9086","9087","9088","9089","9090","9091","9092","9093","9094","9095","9096","9097","9098","9099","9100","9101","9102","9103","9104","9105","9106","9107","9108","9109","9110","9111","9112","9113","9114","9115","9116","9117","9118","9119","9120","9121","9122","9123","9124","9125","9126","9127","9128","9129","9130","9131","9132","9133","9134","9135","9136","9137","9138","9139","9140","9141","9142","9143","9144","9145","9146","9147","9148","9149","9150","9151","9152","9153","9154","9155","9156","9157","9158","9159","9160","9161","9162","9163","9164","9165","9166","9167","9168","9169","9170","9171","9172","9173","9174","9175","9176","9177","9178","9179","9180","9181","9182","9183","9184","9185","9186","9187","9188","9189","9190","9191","9192","9193","9194","9195","9196","9197","9198","9199","9200","9201","9202","9203","9204","9205","9206","9207","9208","9209","9210","9211","9212","9213","9214","9215","9216","9217","9218","9219","9220","9221","9222","9223","9224","9225","9226","9227","9228","9229","9230","9231","9232","9233","9234","9235","9236","9237","9238","9239","9240","9241","9242","9243","9244","9245","9246","9247","9248","9249","9250","9251","9252","9253","9254","9255","9256","9257","9258","9259","9260","9261","9262","9263","9264","9265","9266","9267","9268","9269","9270","9271","9272","9273","9274","9275","9276","9277","9278","9279","9280","9281","9282","9283","9284","9285","9286","9287","9288","9289","9290","9291","9292","9293","9294","9295","9296","9297","9298","9299","9300","9301","9302","9303","9304","9305","9306","9307","9308","9309","9310","9311","9312","9313","9314","9315","9316","9317","9318","9319","9320","9321","9322","9323","9324","9325","9326","9327","9328","9329","9330","9331","9332","9333","9334","9335","9336","9337","9338","9339","9340","9341","9342","9343","9344","9345","9346","9347","9348","9349","9350","9351","9352","9353","9354","9355","9356","9357","9358","9359","9360","9361","9362","9363","9364","9365","9366","9367","9368","9369","9370","9371","9372","9373","9374","9375","9376","9377","9378","9379","9380","9381","9382","9383","9384","9385","9386","9387","9388","9389","9390","9391","9392","9393","9394","9395","9396","9397","9398","9399","9400","9401","9402","9403","9404","9405","9406","9407","9408","9409","9410","9411","9412","9413","9414","9415","9416","9417","9418","9419","9420","9421","9422","9423","9424","9425","9426","9427","9428","9429","9430","9431","9432","9433","9434","9435","9436","9437","9438","9439","9440","9441","9442","9443","9444","9445","9446","9447","9448","9449","9450","9451","9452","9453","9454","9455","9456","9457","9458","9459","9460","9461","9462","9463","9464","9465","9466","9467","9468","9469","9470","9471","9472","9473","9474","9475","9476","9477","9478","9479","9480","9481","9482","9483","9484","9485","9486","9487","9488","9489","9490","9491","9492","9493","9494","9495","9496","9497","9498","9499","9500","9501","9502","9503","9504","9505","9506","9507","9508","9509","9510","9511","9512","9513","9514","9515","9516","9517","9518","9519","9520","9521","9522","9523","9524","9525","9526","9527","9528","9529","9530","9531","9532","9533","9534","9535","9536","9537","9538","9539","9540","9541","9542","9543","9544","9545","9546","9547","9548","9549","9550","9551","9552","9553","9554","9555","9556","9557","9558","9559","9560","9561","9562","9563","9564","9565","9566","9567","9568","9569","9570","9571","9572","9573","9574","9575","9576","9577","9578","9579","9580","9581","9582","9583","9584","9585","9586","9587","9588","9589","9590","9591","9592","9593","9594","9595","9596","9597","9598","9599","9600","9601","9602","9603","9604","9605","9606","9607","9608","9609","9610","9611","9612","9613","9614","9615","9616","9617","9618","9619","9620","9621","9622","9623","9624","9625","9626","9627","9628","9629","9630","9631","9632","9633","9634","9635","9636","9637","9638","9639","9640","9641","9642","9643","9644","9645","9646","9647","9648","9649","9650","9651","9652","9653","9654","9655","9656","9657","9658","9659","9660","9661","9662","9663","9664","9665","9666","9667","9668","9669","9670","9671","9672","9673","9674","9675","9676","9677","9678","9679","9680","9681","9682","9683","9684","9685","9686","9687","9688","9689","9690","9691","9692","9693","9694","9695","9696","9697","9698","9699","9700","9701","9702","9703","9704","9705","9706","9707","9708","9709","9710","9711","9712","9713","9714","9715","9716","9717","9718","9719","9720","9721","9722","9723","9724","9725","9726","9727","9728","9729","9730","9731","9732","9733","9734","9735","9736","9737","9738","9739","9740","9741","9742","9743","9744","9745","9746","9747","9748","9749","9750","9751","9752","9753","9754","9755","9756","9757","9758","9759","9760","9761","9762","9763","9764","9765","9766","9767","9768","9769","9770","9771","9772","9773","9774","9775","9776","9777","9778","9779","9780","9781","9782","9783","9784","9785","9786","9787","9788","9789","9790","9791","9792","9793","9794","9795","9796","9797","9798","9799","9800","9801","9802","9803","9804","9805","9806","9807","9808","9809","9810","9811","9812","9813","9814","9815","9816","9817","9818","9819","9820","9821","9822","9823","9824","9825","9826","9827","9828","9829","9830","9831","9832","9833","9834","9835","9836","9837","9838","9839","9840","9841","9842","9843","9844","9845","9846","9847","9848","9849","9850","9851","9852","9853","9854","9855","9856","9857","9858","9859","9860","9861","9862","9863","9864","9865","9866","9867","9868","9869","9870","9871","9872","9873","9874","9875","9876","9877","9878","9879","9880","9881","9882","9883","9884","9885","9886","9887","9888","9889","9890","9891","9892","9893","9894","9895","9896","9897","9898","9899","9900","9901","9902","9903","9904","9905","9906","9907","9908","9909","9910","9911","9912","9913","9914","9915","9916","9917","9918","9919","9920","9921","9922","9923","9924","9925","9926","9927","9928","9929","9930","9931","9932","9933","9934","9935","9936","9937","9938","9939","9940","9941","9942","9943","9944","9945","9946","9947","9948","9949","9950","9951","9952","9953","9954","9955","9956","9957","9958","9959","9960","9961","9962","9963","9964","9965","9966","9967","9968","9969","9970","9971","9972","9973","9974","9975","9976","9977","9978","9979","9980","9981","9982","9983","9984","9985","9986","9987","9988","9989","9990","9991","9992","9993","9994","9995","9996","9997","9998","9999","10000","10001","10002","10003","10004","10005","10006","10007","10008","10009","10010","10011","10012","10013","10014","10015","10016","10017","10018","10019","10020","10021","10022","10023","10024","10025","10026","10027","10028","10029","10030","10031","10032","10033","10034","10035","10036","10037","10038","10039","10040","10041","10042","10043","10044","10045","10046","10047","10048","10049","10050","10051","10052","10053","10054","10055","10056","10057","10058","10059","10060","10061","10062","10063","10064","10065","10066","10067","10068","10069","10070","10071","10072","10073","10074","10075","10076","10077","10078","10079","10080","10081","10082","10083","10084","10085","10086","10087","10088","10089","10090","10091","10092","10093","10094","10095","10096","10097","10098","10099","10100","10101","10102","10103","10104","10105","10106","10107","10108","10109","10110","10111","10112","10113","10114","10115","10116","10117","10118","10119","10120","10121","10122","10123","10124","10125","10126","10127","10128","10129","10130","10131","10132","10133","10134","10135","10136","10137","10138","10139","10140","10141","10142","10143","10144","10145","10146","10147","10148","10149","10150","10151","10152","10153","10154","10155","10156","10157","10158","10159","10160","10161","10162","10163","10164","10165","10166","10167","10168","10169","10170","10171","10172","10173","10174","10175","10176","10177","10178","10179","10180","10181","10182","10183","10184","10185","10186","10187","10188","10189","10190","10191","10192","10193","10194","10195","10196","10197","10198","10199","10200","10201","10202","10203","10204","10205","10206","10207","10208","10209","10210","10211","10212","10213","10214","10215","10216","10217","10218","10219","10220","10221","10222","10223","10224","10225","10226","10227","10228","10229","10230","10231","10232","10233","10234","10235","10236","10237","10238","10239","10240","10241","10242","10243","10244","10245","10246","10247","10248","10249","10250","10251","10252","10253","10254","10255","10256","10257","10258","10259","10260","10261","10262","10263","10264","10265","10266","10267","10268","10269","10270","10271","10272","10273","10274","10275","10276","10277","10278","10279","10280","10281","10282","10283","10284","10285","10286","10287","10288","10289","10290","10291","10292","10293","10294","10295","10296","10297","10298","10299","10300","10301","10302","10303","10304","10305","10306","10307","10308","10309","10310","10311","10312","10313","10314","10315","10316","10317","10318","10319","10320","10321","10322","10323","10324","10325","10326","10327","10328","10329","10330","10331","10332","10333","10334","10335","10336","10337","10338","10339","10340","10341","10342","10343","10344","10345","10346","10347","10348","10349","10350","10351","10352","10353","10354","10355","10356","10357","10358","10359","10360","10361","10362","10363","10364","10365","10366","10367","10368","10369","10370","10371","10372","10373","10374","10375","10376","10377","10378","10379","10380","10381","10382","10383","10384","10385","10386","10387","10388","10389","10390","10391","10392","10393","10394","10395","10396","10397","10398","10399","10400","10401","10402","10403","10404","10405","10406","10407","10408","10409","10410","10411","10412","10413","10414","10415","10416","10417","10418","10419","10420","10421","10422","10423","10424","10425","10426","10427","10428","10429","10430","10431","10432","10433","10434","10435","10436","10437","10438","10439","10440","10441","10442","10443","10444","10445","10446","10447","10448","10449","10450","10451","10452","10453","10454","10455","10456","10457","10458","10459","10460","10461","10462","10463","10464","10465","10466","10467","10468","10469","10470","10471","10472","10473","10474","10475","10476","10477","10478","10479","10480","10481","10482","10483","10484","10485","10486","10487","10488","10489","10490","10491","10492","10493","10494","10495","10496","10497","10498","10499","10500","10501","10502","10503","10504","10505","10506","10507","10508","10509","10510","10511","10512","10513","10514","10515","10516","10517","10518","10519","10520","10521","10522","10523","10524","10525","10526","10527","10528","10529","10530","10531","10532","10533","10534","10535","10536","10537","10538","10539","10540","10541","10542","10543","10544","10545","10546","10547","10548","10549","10550","10551","10552","10553","10554","10555","10556","10557","10558","10559","10560","10561","10562","10563","10564","10565","10566","10567","10568","10569","10570","10571","10572","10573","10574","10575","10576","10577","10578","10579","10580","10581","10582","10583","10584","10585","10586","10587","10588","10589","10590","10591","10592","10593","10594","10595","10596","10597","10598","10599","10600","10601","10602","10603","10604","10605","10606","10607","10608","10609","10610","10611","10612","10613","10614","10615","10616","10617","10618","10619","10620","10621","10622","10623","10624","10625","10626","10627","10628","10629","10630","10631","10632","10633","10634","10635","10636","10637","10638","10639","10640","10641","10642","10643","10644","10645","10646","10647","10648","10649","10650","10651","10652","10653","10654","10655","10656","10657","10658","10659","10660","10661","10662","10663","10664","10665","10666","10667","10668","10669","10670","10671","10672","10673","10674","10675","10676","10677","10678","10679","10680","10681","10682","10683","10684","10685","10686","10687","10688","10689","10690","10691","10692","10693","10694","10695","10696","10697","10698","10699","10700","10701","10702","10703","10704","10705","10706","10707","10708","10709","10710","10711","10712","10713","10714","10715","10716","10717","10718","10719","10720","10721","10722","10723","10724","10725","10726","10727","10728","10729","10730","10731","10732","10733","10734","10735","10736","10737","10738","10739","10740","10741","10742","10743","10744","10745","10746","10747","10748","10749","10750","10751","10752","10753","10754","10755","10756","10757","10758","10759","10760","10761","10762","10763","10764","10765","10766","10767","10768","10769","10770","10771","10772","10773","10774","10775","10776","10777","10778","10779","10780","10781","10782","10783","10784","10785","10786","10787","10788","10789","10790","10791","10792","10793","10794","10795","10796","10797","10798","10799","10800","10801","10802","10803","10804","10805","10806","10807","10808","10809","10810","10811","10812","10813","10814","10815","10816","10817","10818","10819","10820","10821","10822","10823","10824","10825","10826","10827","10828","10829","10830","10831","10832","10833","10834","10835","10836","10837","10838","10839","10840","10841","10842","10843","10844","10845","10846","10847","10848","10849","10850","10851","10852","10853","10854","10855","10856","10857","10858","10859","10860","10861","10862","10863","10864","10865","10866","10867","10868","10869","10870","10871","10872","10873","10874","10875","10876","10877","10878","10879","10880","10881","10882","10883","10884","10885","10886","10887","10888","10889","10890","10891","10892","10893","10894","10895","10896","10897","10898","10899","10900","10901","10902","10903","10904","10905","10906","10907","10908","10909","10910","10911","10912","10913","10914","10915","10916","10917","10918","10919","10920","10921","10922","10923","10924","10925","10926","10927","10928","10929","10930","10931","10932","10933","10934","10935","10936","10937","10938","10939","10940","10941","10942","10943","10944","10945","10946","10947","10948","10949","10950","10951","10952","10953","10954","10955","10956","10957","10958","10959","10960","10961","10962","10963","10964","10965","10966","10967","10968","10969","10970","10971","10972","10973","10974","10975","10976","10977","10978","10979","10980","10981","10982","10983","10984","10985","10986","10987","10988","10989","10990","10991","10992","10993","10994","10995","10996","10997","10998","10999","11000","11001","11002","11003","11004","11005","11006","11007","11008","11009","11010","11011","11012","11013","11014","11015","11016","11017","11018","11019","11020","11021","11022","11023","11024","11025","11026","11027","11028","11029","11030","11031","11032","11033","11034","11035","11036","11037","11038","11039","11040","11041","11042","11043","11044","11045","11046","11047","11048","11049","11050","11051","11052","11053","11054","11055","11056","11057","11058","11059","11060","11061","11062","11063","11064","11065","11066","11067","11068","11069","11070","11071","11072","11073","11074","11075","11076","11077","11078","11079","11080","11081","11082","11083","11084","11085","11086","11087","11088","11089","11090","11091","11092","11093","11094","11095","11096","11097","11098","11099","11100","11101","11102","11103","11104","11105","11106","11107","11108","11109","11110","11111","11112","11113","11114","11115","11116","11117","11118","11119","11120","11121","11122","11123","11124","11125","11126","11127","11128","11129","11130","11131","11132","11133","11134","11135","11136","11137","11138","11139","11140","11141","11142","11143","11144","11145","11146","11147","11148","11149","11150","11151","11152","11153","11154","11155","11156","11157","11158","11159","11160","11161","11162","11163","11164","11165","11166","11167","11168","11169","11170","11171","11172","11173","11174","11175","11176","11177","11178","11179","11180","11181","11182","11183","11184","11185","11186","11187","11188","11189","11190","11191","11192","11193","11194","11195","11196","11197","11198","11199","11200","11201","11202","11203","11204","11205","11206","11207","11208","11209","11210","11211","11212","11213","11214","11215","11216","11217","11218","11219","11220","11221","11222","11223","11224","11225","11226","11227","11228","11229","11230","11231","11232","11233","11234","11235","11236","11237","11238","11239","11240","11241","11242","11243","11244","11245","11246","11247","11248","11249","11250","11251","11252","11253","11254","11255","11256","11257","11258","11259","11260","11261","11262","11263","11264","11265","11266","11267","11268","11269","11270","11271","11272","11273","11274","11275","11276","11277","11278","11279","11280","11281","11282","11283","11284","11285","11286","11287","11288","11289","11290","11291","11292","11293","11294","11295","11296","11297","11298","11299","11300","11301","11302","11303","11304","11305","11306","11307","11308","11309","11310","11311","11312","11313","11314","11315","11316","11317","11318","11319","11320","11321","11322","11323","11324","11325","11326","11327","11328","11329","11330","11331","11332","11333","11334","11335","11336","11337","11338","11339","11340","11341","11342","11343","11344","11345","11346","11347","11348","11349","11350","11351","11352","11353","11354","11355","11356","11357","11358","11359","11360","11361","11362","11363","11364","11365","11366","11367","11368","11369","11370","11371","11372","11373","11374","11375","11376","11377","11378","11379","11380","11381","11382","11383","11384","11385","11386","11387","11388","11389","11390","11391","11392","11393","11394","11395","11396","11397","11398","11399","11400","11401","11402","11403","11404","11405","11406","11407","11408","11409","11410","11411","11412","11413","11414","11415","11416","11417","11418","11419","11420","11421","11422","11423","11424","11425","11426","11427","11428","11429","11430","11431","11432","11433","11434","11435","11436","11437","11438","11439","11440","11441","11442","11443","11444","11445","11446","11447","11448","11449","11450","11451","11452","11453","11454","11455","11456","11457","11458","11459","11460","11461","11462","11463","11464","11465","11466","11467","11468","11469","11470","11471","11472","11473","11474","11475","11476","11477","11478","11479","11480","11481","11482","11483","11484","11485","11486","11487","11488","11489","11490","11491","11492","11493","11494","11495","11496","11497","11498","11499","11500","11501","11502","11503","11504","11505","11506","11507","11508","11509","11510","11511","11512","11513","11514","11515","11516","11517","11518","11519","11520","11521","11522","11523","11524","11525","11526","11527","11528","11529","11530","11531","11532","11533","11534","11535","11536","11537","11538","11539","11540","11541","11542","11543","11544","11545","11546","11547","11548","11549","11550","11551","11552","11553","11554","11555","11556","11557","11558","11559","11560","11561","11562","11563","11564","11565","11566","11567","11568","11569","11570","11571","11572","11573","11574","11575","11576","11577","11578","11579","11580","11581","11582","11583","11584","11585","11586","11587","11588","11589","11590","11591","11592","11593","11594","11595","11596","11597","11598","11599","11600","11601","11602","11603","11604","11605","11606","11607","11608","11609","11610","11611","11612","11613","11614","11615","11616","11617","11618","11619","11620","11621","11622","11623","11624","11625","11626","11627","11628","11629","11630","11631","11632","11633","11634","11635","11636","11637","11638","11639","11640","11641","11642","11643","11644","11645","11646","11647","11648","11649","11650","11651","11652","11653","11654","11655","11656","11657","11658","11659","11660","11661","11662","11663","11664","11665","11666","11667","11668","11669","11670","11671","11672","11673","11674","11675","11676","11677","11678","11679","11680","11681","11682","11683","11684","11685","11686","11687","11688","11689","11690","11691","11692","11693","11694","11695","11696","11697","11698","11699","11700","11701","11702","11703","11704","11705","11706","11707","11708","11709","11710","11711","11712","11713","11714","11715","11716","11717","11718","11719","11720","11721","11722","11723","11724","11725","11726","11727","11728","11729","11730","11731","11732","11733","11734","11735","11736","11737","11738","11739","11740","11741","11742","11743","11744","11745","11746","11747","11748","11749","11750","11751","11752","11753","11754","11755","11756","11757","11758","11759","11760","11761","11762","11763","11764","11765","11766","11767","11768","11769","11770","11771","11772","11773","11774","11775","11776","11777","11778","11779","11780","11781","11782","11783","11784","11785","11786","11787","11788","11789","11790","11791","11792","11793","11794","11795","11796","11797","11798","11799","11800","11801","11802","11803","11804","11805","11806","11807","11808","11809","11810","11811","11812","11813","11814","11815","11816","11817","11818","11819","11820","11821","11822","11823","11824","11825","11826","11827","11828","11829","11830","11831","11832","11833","11834","11835","11836","11837","11838","11839","11840","11841","11842","11843","11844","11845","11846","11847","11848","11849","11850","11851","11852","11853","11854","11855","11856","11857","11858","11859","11860","11861","11862","11863","11864","11865","11866","11867","11868","11869","11870","11871","11872","11873","11874","11875","11876","11877","11878","11879","11880","11881","11882","11883","11884","11885","11886","11887","11888","11889","11890","11891","11892","11893","11894","11895","11896","11897","11898","11899","11900","11901","11902","11903","11904","11905","11906","11907","11908","11909","11910","11911","11912","11913","11914","11915","11916","11917","11918","11919","11920","11921","11922","11923","11924","11925","11926","11927","11928","11929","11930","11931","11932","11933","11934","11935","11936","11937","11938","11939","11940","11941","11942","11943","11944","11945","11946","11947","11948","11949","11950","11951","11952","11953","11954","11955","11956","11957","11958","11959","11960","11961","11962","11963","11964","11965","11966","11967","11968","11969","11970","11971","11972","11973","11974","11975","11976","11977","11978","11979","11980","11981","11982","11983","11984","11985","11986","11987","11988","11989","11990","11991","11992","11993","11994","11995","11996","11997","11998","11999","12000","12001","12002","12003","12004","12005","12006","12007","12008","12009","12010","12011","12012","12013","12014","12015","12016","12017","12018","12019","12020","12021","12022","12023","12024","12025","12026","12027","12028","12029","12030","12031","12032","12033","12034","12035","12036","12037","12038","12039","12040","12041","12042","12043","12044","12045","12046","12047","12048","12049","12050","12051","12052","12053","12054","12055","12056","12057","12058","12059","12060","12061","12062","12063","12064","12065","12066","12067","12068","12069","12070","12071","12072","12073","12074","12075","12076","12077","12078","12079","12080","12081","12082","12083","12084","12085","12086","12087","12088","12089","12090","12091","12092","12093","12094","12095","12096","12097","12098","12099","12100","12101","12102","12103","12104","12105","12106","12107","12108","12109","12110","12111","12112","12113","12114","12115","12116","12117","12118","12119","12120","12121","12122","12123","12124","12125","12126","12127","12128","12129","12130","12131","12132","12133","12134","12135","12136","12137","12138","12139","12140","12141","12142","12143","12144","12145","12146","12147","12148","12149","12150","12151","12152","12153","12154","12155","12156","12157","12158","12159","12160","12161","12162","12163","12164","12165","12166","12167","12168","12169","12170","12171","12172","12173","12174","12175","12176","12177","12178","12179","12180","12181","12182","12183","12184","12185","12186","12187","12188","12189","12190","12191","12192","12193","12194","12195","12196","12197","12198","12199","12200","12201","12202","12203","12204","12205","12206","12207","12208","12209","12210","12211","12212","12213","12214","12215","12216","12217","12218","12219","12220","12221","12222","12223","12224","12225","12226","12227","12228","12229","12230","12231","12232","12233","12234","12235","12236","12237","12238","12239","12240","12241","12242","12243","12244","12245","12246","12247","12248","12249","12250","12251","12252","12253","12254","12255","12256","12257","12258","12259","12260","12261","12262","12263","12264","12265","12266","12267","12268","12269","12270","12271","12272","12273","12274","12275","12276","12277","12278","12279","12280","12281","12282","12283","12284","12285","12286","12287","12288","12289","12290","12291","12292","12293","12294","12295","12296","12297","12298","12299","12300","12301","12302","12303","12304","12305","12306","12307","12308","12309","12310","12311","12312","12313","12314","12315","12316","12317","12318","12319","12320","12321","12322","12323","12324","12325","12326","12327","12328","12329","12330","12331","12332","12333","12334","12335","12336","12337","12338","12339","12340","12341","12342","12343","12344","12345","12346","12347","12348","12349","12350","12351","12352","12353","12354","12355","12356","12357","12358","12359","12360","12361","12362","12363","12364","12365","12366","12367","12368","12369","12370","12371","12372","12373","12374","12375","12376","12377","12378","12379","12380","12381","12382","12383","12384","12385","12386","12387","12388","12389","12390","12391","12392","12393","12394","12395","12396","12397","12398","12399","12400","12401","12402","12403","12404","12405","12406","12407","12408","12409","12410","12411","12412","12413","12414","12415","12416","12417","12418","12419","12420","12421","12422","12423","12424","12425","12426","12427","12428","12429","12430","12431","12432","12433","12434","12435","12436","12437","12438","12439","12440","12441","12442","12443","12444","12445","12446","12447","12448","12449","12450","12451","12452","12453","12454","12455","12456","12457","12458","12459","12460","12461","12462","12463","12464","12465","12466","12467","12468","12469","12470","12471","12472","12473","12474","12475","12476","12477","12478","12479","12480","12481","12482","12483","12484","12485","12486","12487","12488","12489","12490","12491","12492","12493","12494","12495","12496","12497","12498","12499","12500","12501","12502","12503","12504","12505","12506","12507","12508","12509","12510","12511","12512","12513","12514","12515","12516","12517","12518","12519","12520","12521","12522","12523","12524","12525","12526","12527","12528","12529","12530","12531","12532","12533","12534","12535","12536","12537","12538","12539","12540","12541","12542","12543","12544","12545","12546","12547","12548","12549","12550","12551","12552","12553","12554","12555","12556","12557","12558","12559","12560","12561","12562","12563","12564","12565","12566","12567","12568","12569","12570","12571","12572","12573","12574","12575","12576","12577","12578","12579","12580","12581","12582","12583","12584","12585","12586","12587","12588","12589","12590","12591","12592","12593","12594","12595","12596","12597","12598","12599","12600","12601","12602","12603","12604","12605","12606","12607","12608","12609","12610","12611","12612","12613","12614","12615","12616","12617","12618","12619","12620","12621","12622","12623","12624","12625","12626","12627","12628","12629","12630","12631","12632","12633","12634","12635","12636","12637","12638","12639","12640","12641","12642","12643","12644","12645","12646","12647","12648","12649","12650","12651","12652","12653","12654","12655","12656","12657","12658","12659","12660","12661","12662","12663","12664","12665","12666","12667","12668","12669","12670","12671","12672","12673","12674","12675","12676","12677","12678","12679","12680","12681","12682","12683","12684","12685","12686","12687","12688","12689","12690","12691","12692","12693","12694","12695","12696","12697","12698","12699","12700","12701","12702","12703","12704","12705","12706","12707","12708","12709","12710","12711","12712","12713","12714","12715","12716","12717","12718","12719","12720","12721","12722","12723","12724","12725","12726","12727","12728","12729","12730","12731","12732","12733","12734","12735","12736","12737","12738","12739","12740","12741","12742","12743","12744","12745","12746","12747","12748","12749","12750","12751","12752","12753","12754","12755","12756","12757","12758","12759","12760","12761","12762","12763","12764","12765","12766","12767","12768","12769","12770","12771","12772","12773","12774","12775","12776","12777","12778","12779","12780","12781","12782","12783","12784","12785","12786","12787","12788","12789","12790","12791","12792","12793","12794","12795","12796","12797","12798","12799","12800","12801","12802","12803","12804","12805","12806","12807","12808","12809","12810","12811","12812","12813","12814","12815","12816","12817","12818","12819","12820","12821","12822","12823","12824","12825","12826","12827","12828","12829","12830","12831","12832","12833","12834","12835","12836","12837","12838","12839","12840","12841","12842","12843","12844","12845","12846","12847","12848","12849","12850","12851","12852","12853","12854","12855","12856","12857","12858","12859","12860","12861","12862","12863","12864","12865","12866","12867","12868","12869","12870","12871","12872","12873","12874","12875","12876","12877","12878","12879","12880","12881","12882","12883","12884","12885","12886","12887","12888","12889","12890","12891","12892","12893","12894","12895","12896","12897","12898","12899","12900","12901","12902","12903","12904","12905","12906","12907","12908","12909","12910","12911","12912","12913","12914","12915","12916","12917","12918","12919","12920","12921","12922","12923","12924","12925","12926","12927","12928","12929","12930","12931","12932","12933","12934","12935","12936","12937","12938","12939","12940","12941","12942","12943","12944","12945","12946","12947","12948","12949","12950","12951","12952","12953","12954","12955","12956","12957","12958","12959","12960","12961","12962","12963","12964","12965","12966","12967","12968","12969","12970","12971","12972","12973","12974","12975","12976","12977","12978","12979","12980","12981","12982","12983","12984","12985","12986","12987","12988","12989","12990","12991","12992","12993","12994","12995","12996","12997","12998","12999","13000","13001","13002","13003","13004","13005","13006","13007","13008","13009","13010","13011","13012","13013","13014","13015","13016","13017","13018","13019","13020","13021","13022","13023","13024","13025","13026","13027","13028","13029","13030","13031","13032","13033","13034","13035","13036","13037","13038","13039","13040","13041","13042","13043","13044","13045","13046","13047","13048","13049","13050","13051","13052","13053","13054","13055","13056","13057","13058","13059","13060","13061","13062","13063","13064","13065","13066","13067","13068","13069","13070","13071","13072","13073","13074","13075","13076","13077","13078","13079","13080","13081","13082","13083","13084","13085","13086","13087","13088","13089","13090","13091","13092","13093","13094","13095","13096","13097","13098","13099","13100","13101","13102","13103","13104","13105","13106","13107","13108","13109","13110","13111","13112","13113","13114","13115","13116","13117","13118","13119","13120","13121","13122","13123","13124","13125","13126","13127","13128","13129","13130","13131","13132","13133","13134","13135","13136","13137","13138","13139","13140","13141","13142","13143","13144","13145","13146","13147","13148","13149","13150","13151","13152","13153","13154","13155","13156","13157","13158","13159","13160","13161","13162","13163","13164","13165","13166","13167","13168","13169","13170","13171","13172","13173","13174","13175","13176","13177","13178","13179","13180","13181","13182","13183","13184","13185","13186","13187","13188","13189","13190","13191","13192","13193","13194","13195","13196","13197","13198","13199","13200","13201","13202","13203","13204","13205","13206","13207","13208","13209","13210","13211","13212","13213","13214","13215","13216","13217","13218","13219","13220","13221","13222","13223","13224","13225","13226","13227","13228","13229","13230","13231","13232","13233","13234","13235","13236","13237","13238","13239","13240","13241","13242","13243","13244","13245","13246","13247","13248","13249","13250","13251","13252","13253","13254","13255","13256","13257","13258","13259","13260","13261","13262","13263","13264","13265","13266","13267","13268","13269","13270","13271","13272","13273","13274","13275","13276","13277","13278","13279","13280","13281","13282","13283","13284","13285","13286","13287","13288","13289","13290","13291","13292","13293","13294","13295","13296","13297","13298","13299","13300","13301","13302","13303","13304","13305","13306","13307","13308","13309","13310","13311","13312","13313","13314","13315","13316","13317","13318","13319","13320","13321","13322","13323","13324","13325","13326","13327","13328","13329","13330","13331","13332","13333","13334","13335","13336","13337","13338","13339","13340","13341","13342","13343","13344","13345","13346","13347","13348","13349","13350","13351","13352","13353","13354","13355","13356","13357","13358","13359","13360","13361","13362","13363","13364","13365","13366","13367","13368","13369","13370","13371","13372","13373","13374","13375","13376","13377","13378","13379","13380","13381","13382","13383","13384","13385","13386","13387","13388","13389","13390","13391","13392","13393","13394","13395","13396","13397","13398","13399","13400","13401","13402","13403","13404","13405","13406","13407","13408","13409","13410","13411","13412","13413","13414","13415","13416","13417","13418","13419","13420","13421","13422","13423","13424","13425","13426","13427","13428","13429","13430","13431","13432","13433","13434","13435","13436","13437","13438","13439","13440","13441","13442","13443","13444","13445","13446","13447","13448","13449","13450","13451","13452","13453","13454","13455","13456","13457","13458","13459","13460","13461","13462","13463","13464","13465","13466","13467","13468","13469","13470","13471","13472","13473","13474","13475","13476","13477","13478","13479","13480","13481","13482","13483","13484","13485","13486","13487","13488","13489","13490","13491","13492","13493","13494","13495","13496","13497","13498","13499","13500","13501","13502","13503","13504","13505","13506","13507","13508","13509","13510","13511","13512","13513","13514","13515","13516","13517","13518","13519","13520","13521","13522","13523","13524","13525","13526","13527","13528","13529","13530","13531","13532","13533","13534","13535","13536","13537","13538","13539","13540","13541","13542","13543","13544","13545","13546","13547","13548","13549","13550","13551","13552","13553","13554","13555","13556","13557","13558","13559","13560","13561","13562","13563","13564","13565","13566","13567","13568","13569","13570","13571","13572","13573","13574","13575","13576","13577","13578","13579","13580","13581","13582","13583","13584","13585","13586","13587","13588","13589","13590","13591","13592","13593","13594","13595","13596","13597","13598","13599","13600","13601","13602","13603","13604","13605","13606","13607","13608","13609","13610","13611","13612","13613","13614","13615","13616","13617","13618","13619","13620","13621","13622","13623","13624","13625","13626","13627","13628","13629","13630","13631","13632","13633","13634","13635","13636","13637","13638","13639","13640","13641","13642","13643","13644","13645","13646","13647","13648","13649","13650","13651","13652","13653","13654","13655","13656","13657","13658","13659","13660","13661","13662","13663","13664","13665","13666","13667","13668","13669","13670","13671","13672","13673","13674","13675","13676","13677","13678","13679","13680","13681","13682","13683","13684","13685","13686","13687","13688","13689","13690","13691","13692","13693","13694","13695","13696","13697","13698","13699","13700","13701","13702","13703","13704","13705","13706","13707","13708","13709","13710","13711","13712","13713","13714","13715","13716","13717","13718","13719","13720","13721","13722","13723","13724","13725","13726","13727","13728","13729","13730","13731","13732","13733","13734","13735","13736","13737","13738","13739","13740","13741","13742","13743","13744","13745","13746","13747","13748","13749","13750","13751","13752","13753","13754","13755","13756","13757","13758","13759","13760","13761","13762","13763","13764","13765","13766","13767","13768","13769","13770","13771","13772","13773","13774","13775","13776","13777","13778","13779","13780","13781","13782","13783","13784","13785","13786","13787","13788","13789","13790","13791","13792","13793","13794","13795","13796","13797","13798","13799","13800","13801","13802","13803","13804","13805","13806","13807","13808","13809","13810","13811","13812","13813","13814","13815","13816","13817","13818","13819","13820","13821","13822","13823","13824","13825","13826","13827","13828","13829","13830","13831","13832","13833","13834","13835","13836","13837","13838","13839","13840","13841","13842","13843","13844","13845","13846","13847","13848","13849","13850","13851","13852","13853","13854","13855","13856","13857","13858","13859","13860","13861","13862","13863","13864","13865","13866","13867","13868","13869","13870","13871","13872","13873","13874","13875","13876","13877","13878","13879","13880","13881","13882","13883","13884","13885","13886","13887","13888","13889","13890","13891","13892","13893","13894","13895","13896","13897","13898","13899","13900","13901","13902","13903","13904","13905","13906","13907","13908","13909","13910","13911","13912","13913","13914","13915","13916","13917","13918","13919","13920","13921","13922","13923","13924","13925","13926","13927","13928","13929","13930","13931","13932","13933","13934","13935","13936","13937","13938","13939","13940","13941","13942","13943","13944","13945","13946","13947","13948","13949","13950","13951","13952","13953","13954","13955","13956","13957","13958","13959","13960","13961","13962","13963","13964","13965","13966","13967","13968","13969","13970","13971","13972","13973","13974","13975","13976","13977","13978","13979","13980","13981","13982","13983","13984","13985","13986","13987","13988","13989","13990","13991","13992","13993","13994","13995","13996","13997","13998","13999","14000","14001","14002","14003","14004","14005","14006","14007","14008","14009","14010","14011","14012","14013","14014","14015","14016","14017","14018","14019","14020","14021","14022","14023","14024","14025","14026","14027","14028","14029","14030","14031","14032","14033","14034","14035","14036","14037","14038","14039","14040","14041","14042","14043","14044","14045","14046","14047","14048","14049","14050","14051","14052","14053","14054","14055","14056","14057","14058","14059","14060","14061","14062","14063","14064","14065","14066","14067","14068","14069","14070","14071","14072","14073","14074","14075","14076","14077","14078","14079","14080","14081","14082","14083","14084","14085","14086","14087","14088","14089","14090","14091","14092","14093","14094","14095","14096","14097","14098","14099","14100","14101","14102","14103","14104","14105","14106","14107","14108","14109","14110","14111","14112","14113","14114","14115","14116","14117","14118","14119","14120","14121","14122","14123","14124","14125","14126","14127","14128","14129","14130","14131","14132","14133","14134","14135","14136","14137","14138","14139","14140","14141","14142","14143","14144","14145","14146","14147","14148","14149","14150","14151","14152","14153","14154","14155","14156","14157","14158","14159","14160","14161","14162","14163","14164","14165","14166","14167","14168","14169","14170","14171","14172","14173","14174","14175","14176","14177","14178","14179","14180","14181","14182","14183","14184","14185","14186","14187","14188","14189","14190","14191","14192","14193","14194","14195","14196","14197","14198","14199","14200","14201","14202","14203","14204","14205","14206","14207","14208","14209","14210","14211","14212","14213","14214","14215","14216","14217","14218","14219","14220","14221","14222","14223","14224","14225","14226","14227","14228","14229","14230","14231","14232","14233","14234","14235","14236","14237","14238","14239","14240","14241","14242","14243","14244","14245","14246","14247","14248","14249","14250","14251","14252","14253","14254","14255","14256","14257","14258","14259","14260","14261","14262","14263","14264","14265","14266","14267","14268","14269","14270","14271","14272","14273","14274","14275","14276","14277","14278","14279","14280","14281","14282","14283","14284","14285","14286","14287","14288","14289","14290","14291","14292","14293","14294","14295","14296","14297","14298","14299","14300","14301","14302","14303","14304","14305","14306","14307","14308","14309","14310","14311","14312","14313","14314","14315","14316","14317","14318","14319","14320","14321","14322","14323","14324","14325","14326","14327","14328","14329","14330","14331","14332","14333","14334","14335","14336","14337","14338","14339","14340","14341","14342","14343","14344","14345","14346","14347","14348","14349","14350","14351","14352","14353","14354","14355","14356","14357","14358","14359","14360","14361","14362","14363","14364","14365","14366","14367","14368","14369","14370","14371","14372","14373","14374","14375","14376","14377","14378","14379","14380","14381","14382","14383","14384","14385","14386","14387","14388","14389","14390","14391","14392","14393","14394","14395","14396","14397","14398","14399","14400","14401","14402","14403","14404","14405","14406","14407","14408","14409","14410","14411","14412","14413","14414","14415","14416","14417","14418","14419","14420","14421","14422","14423","14424","14425","14426","14427","14428","14429","14430","14431","14432","14433","14434","14435","14436","14437","14438","14439","14440","14441","14442","14443","14444","14445","14446","14447","14448","14449","14450","14451","14452","14453","14454","14455","14456","14457","14458","14459","14460","14461","14462","14463","14464","14465","14466","14467","14468","14469","14470","14471","14472","14473","14474","14475","14476","14477","14478","14479","14480","14481","14482","14483","14484","14485","14486","14487","14488","14489","14490","14491","14492","14493","14494","14495","14496","14497","14498","14499","14500","14501","14502","14503","14504","14505","14506","14507","14508","14509","14510","14511","14512","14513","14514","14515","14516","14517","14518","14519","14520","14521","14522","14523","14524","14525","14526","14527","14528","14529","14530","14531","14532","14533","14534","14535","14536","14537","14538","14539","14540","14541","14542","14543","14544","14545","14546","14547","14548","14549","14550","14551","14552","14553","14554","14555","14556","14557","14558","14559","14560","14561","14562","14563","14564","14565","14566","14567","14568","14569","14570","14571","14572","14573","14574","14575","14576","14577","14578","14579","14580","14581","14582","14583","14584","14585","14586","14587","14588","14589","14590","14591","14592","14593","14594","14595","14596","14597","14598","14599","14600","14601","14602","14603","14604","14605","14606","14607","14608","14609","14610","14611","14612","14613","14614","14615","14616","14617","14618","14619","14620","14621","14622","14623","14624","14625","14626","14627","14628","14629","14630","14631","14632","14633","14634","14635","14636","14637","14638","14639","14640","14641","14642","14643","14644","14645","14646","14647","14648","14649","14650","14651","14652","14653","14654","14655","14656","14657","14658","14659","14660","14661","14662","14663","14664","14665","14666","14667","14668","14669","14670","14671","14672","14673","14674","14675","14676","14677","14678","14679","14680","14681","14682","14683","14684","14685","14686","14687","14688","14689","14690","14691","14692","14693","14694","14695","14696","14697","14698","14699","14700","14701","14702","14703","14704","14705","14706","14707","14708","14709","14710","14711","14712","14713","14714","14715","14716","14717","14718","14719","14720","14721","14722","14723","14724","14725","14726","14727","14728","14729","14730","14731","14732","14733","14734","14735","14736","14737","14738","14739","14740","14741","14742","14743","14744","14745","14746","14747","14748","14749","14750","14751","14752","14753","14754","14755","14756","14757","14758","14759","14760","14761","14762","14763","14764","14765","14766","14767","14768","14769","14770","14771","14772","14773","14774","14775","14776","14777","14778","14779","14780","14781","14782","14783","14784","14785","14786","14787","14788","14789","14790","14791","14792","14793","14794","14795","14796","14797","14798","14799","14800","14801","14802","14803","14804","14805","14806","14807","14808","14809","14810","14811","14812","14813","14814","14815","14816","14817","14818","14819","14820","14821","14822","14823","14824","14825","14826","14827","14828","14829","14830","14831","14832","14833","14834","14835","14836","14837","14838","14839","14840","14841","14842","14843","14844","14845","14846","14847","14848","14849","14850","14851","14852","14853","14854","14855","14856","14857","14858","14859","14860","14861","14862","14863","14864","14865","14866","14867","14868","14869","14870","14871","14872","14873","14874","14875","14876","14877","14878","14879","14880","14881","14882","14883","14884","14885","14886","14887","14888","14889","14890","14891","14892","14893","14894","14895","14896","14897","14898","14899","14900","14901","14902","14903","14904","14905","14906","14907","14908","14909","14910","14911","14912","14913","14914","14915","14916","14917","14918","14919","14920","14921","14922","14923","14924","14925","14926","14927","14928","14929","14930","14931","14932","14933","14934","14935","14936","14937","14938","14939","14940","14941","14942","14943","14944","14945","14946","14947","14948","14949","14950","14951","14952","14953","14954","14955","14956","14957","14958","14959","14960","14961","14962","14963","14964","14965","14966","14967","14968","14969","14970","14971","14972","14973","14974","14975","14976","14977","14978","14979","14980","14981","14982","14983","14984","14985","14986","14987","14988","14989","14990","14991","14992","14993","14994","14995","14996","14997","14998","14999","15000","15001","15002","15003","15004","15005","15006","15007","15008","15009","15010","15011","15012","15013","15014","15015","15016","15017","15018","15019","15020","15021","15022","15023","15024","15025","15026","15027","15028","15029","15030","15031","15032","15033","15034","15035","15036","15037","15038","15039","15040","15041","15042","15043","15044","15045","15046","15047","15048","15049","15050","15051","15052","15053","15054","15055","15056","15057","15058","15059","15060","15061","15062","15063","15064","15065","15066","15067","15068","15069","15070","15071","15072","15073","15074","15075","15076","15077","15078","15079","15080","15081","15082","15083","15084","15085","15086","15087","15088","15089","15090","15091","15092","15093","15094","15095","15096","15097","15098","15099","15100","15101","15102","15103","15104","15105","15106","15107","15108","15109","15110","15111","15112","15113","15114","15115","15116","15117","15118","15119","15120","15121","15122","15123","15124","15125","15126","15127","15128","15129","15130","15131","15132","15133","15134","15135","15136","15137","15138","15139","15140","15141","15142","15143","15144","15145","15146","15147","15148","15149","15150","15151","15152","15153","15154","15155","15156","15157","15158","15159","15160","15161","15162","15163","15164","15165","15166","15167","15168","15169","15170","15171","15172","15173","15174","15175","15176","15177","15178","15179","15180","15181","15182","15183","15184","15185","15186","15187","15188","15189","15190","15191","15192","15193","15194","15195","15196","15197","15198","15199","15200","15201","15202","15203","15204","15205","15206","15207","15208","15209","15210","15211","15212","15213","15214","15215","15216","15217","15218","15219","15220","15221","15222","15223","15224","15225","15226","15227","15228","15229","15230","15231","15232","15233","15234","15235","15236","15237","15238","15239","15240","15241","15242","15243","15244","15245","15246","15247","15248","15249","15250","15251","15252","15253","15254","15255","15256","15257","15258","15259","15260","15261","15262","15263","15264","15265","15266","15267","15268","15269","15270","15271","15272","15273","15274","15275","15276","15277","15278","15279","15280","15281","15282","15283","15284","15285","15286","15287","15288","15289","15290","15291","15292","15293","15294","15295","15296","15297","15298","15299","15300","15301","15302","15303","15304","15305","15306","15307","15308","15309","15310","15311","15312","15313","15314","15315","15316","15317","15318","15319","15320","15321","15322","15323","15324","15325","15326","15327","15328","15329","15330","15331","15332","15333","15334","15335","15336","15337","15338","15339","15340","15341","15342","15343","15344","15345","15346","15347","15348","15349","15350","15351","15352","15353","15354","15355","15356","15357","15358","15359","15360","15361","15362","15363","15364","15365","15366","15367","15368","15369","15370","15371","15372","15373","15374","15375","15376","15377","15378","15379","15380","15381","15382","15383","15384","15385","15386","15387","15388","15389","15390","15391","15392","15393","15394","15395","15396","15397","15398","15399","15400","15401","15402","15403","15404","15405","15406","15407","15408","15409","15410","15411","15412","15413","15414","15415","15416","15417","15418","15419","15420","15421","15422","15423","15424","15425","15426","15427","15428","15429","15430","15431","15432","15433","15434","15435","15436","15437","15438","15439","15440","15441","15442","15443","15444","15445","15446","15447","15448","15449","15450","15451","15452","15453","15454","15455","15456","15457","15458","15459","15460","15461","15462","15463","15464","15465","15466","15467","15468","15469","15470","15471","15472","15473","15474","15475","15476","15477","15478","15479","15480","15481","15482","15483","15484","15485","15486","15487","15488","15489","15490","15491","15492","15493","15494","15495","15496","15497","15498","15499","15500","15501","15502","15503","15504","15505","15506","15507","15508","15509","15510","15511","15512","15513","15514","15515","15516","15517","15518","15519","15520","15521","15522","15523","15524","15525","15526","15527","15528","15529","15530","15531","15532","15533","15534","15535","15536","15537","15538","15539","15540","15541","15542","15543","15544","15545","15546","15547","15548","15549","15550","15551","15552","15553","15554","15555","15556","15557","15558","15559","15560","15561","15562","15563","15564","15565","15566","15567","15568","15569","15570","15571","15572","15573","15574","15575","15576","15577","15578","15579","15580","15581","15582","15583","15584","15585","15586","15587","15588","15589","15590","15591","15592","15593","15594","15595","15596","15597","15598","15599","15600","15601","15602","15603","15604","15605","15606","15607","15608","15609","15610","15611","15612","15613","15614","15615","15616","15617","15618","15619","15620","15621","15622","15623","15624","15625","15626","15627","15628","15629","15630","15631","15632","15633","15634","15635","15636","15637","15638","15639","15640","15641","15642","15643","15644","15645","15646","15647","15648","15649","15650","15651","15652","15653","15654","15655","15656","15657","15658","15659","15660","15661","15662","15663","15664","15665","15666","15667","15668","15669","15670","15671","15672","15673","15674","15675","15676","15677","15678","15679","15680","15681","15682","15683","15684","15685","15686","15687","15688","15689","15690","15691","15692","15693","15694","15695","15696","15697","15698","15699","15700","15701","15702","15703","15704","15705","15706","15707","15708","15709","15710","15711","15712","15713","15714","15715","15716","15717","15718","15719","15720","15721","15722","15723","15724","15725","15726","15727","15728","15729","15730","15731","15732","15733","15734","15735","15736","15737","15738","15739","15740","15741","15742","15743","15744","15745","15746","15747","15748","15749","15750","15751","15752","15753","15754","15755","15756","15757","15758","15759","15760","15761","15762","15763","15764","15765","15766","15767","15768","15769","15770","15771","15772","15773","15774","15775","15776","15777","15778","15779","15780","15781","15782","15783","15784","15785","15786","15787","15788","15789","15790","15791","15792","15793","15794","15795","15796","15797","15798","15799","15800","15801","15802","15803","15804","15805","15806","15807","15808","15809","15810","15811","15812","15813","15814","15815","15816","15817","15818","15819","15820","15821","15822","15823","15824","15825","15826","15827","15828","15829","15830","15831","15832","15833","15834","15835","15836","15837","15838","15839","15840","15841","15842","15843","15844","15845","15846","15847","15848","15849","15850","15851","15852","15853","15854","15855","15856","15857","15858","15859","15860","15861","15862","15863","15864","15865","15866","15867","15868","15869","15870","15871","15872","15873","15874","15875","15876","15877","15878","15879","15880","15881","15882","15883","15884","15885","15886","15887","15888","15889","15890","15891","15892","15893","15894","15895","15896","15897","15898","15899","15900","15901","15902","15903","15904","15905","15906","15907","15908","15909","15910","15911","15912","15913","15914","15915","15916","15917","15918","15919","15920","15921","15922","15923","15924","15925","15926","15927","15928","15929","15930","15931","15932","15933","15934","15935","15936","15937","15938","15939","15940","15941","15942","15943","15944","15945","15946","15947","15948","15949","15950","15951","15952","15953","15954","15955","15956","15957","15958","15959","15960","15961","15962","15963","15964","15965","15966","15967","15968","15969","15970","15971","15972","15973","15974","15975","15976","15977","15978","15979","15980","15981","15982","15983","15984","15985","15986","15987","15988","15989","15990","15991","15992","15993","15994","15995","15996","15997","15998","15999","16000","16001","16002","16003","16004","16005","16006","16007","16008","16009","16010","16011","16012","16013","16014","16015","16016","16017","16018","16019","16020","16021","16022","16023","16024","16025","16026","16027","16028","16029","16030","16031","16032","16033","16034","16035","16036","16037","16038","16039","16040","16041","16042","16043","16044","16045","16046","16047","16048","16049","16050","16051","16052","16053","16054","16055","16056","16057","16058","16059","16060","16061","16062","16063","16064","16065","16066","16067","16068","16069","16070","16071","16072","16073","16074","16075","16076","16077","16078","16079","16080","16081","16082","16083","16084","16085","16086","16087","16088","16089","16090","16091","16092","16093","16094","16095","16096","16097","16098","16099","16100","16101","16102","16103","16104","16105","16106","16107","16108","16109","16110","16111","16112","16113","16114","16115","16116","16117","16118","16119","16120","16121","16122","16123","16124","16125","16126","16127","16128","16129","16130","16131","16132","16133","16134","16135","16136","16137","16138","16139","16140","16141","16142","16143","16144","16145","16146","16147","16148","16149","16150","16151","16152","16153","16154","16155","16156","16157","16158","16159","16160","16161","16162","16163","16164","16165","16166","16167","16168","16169","16170","16171","16172","16173","16174","16175","16176","16177","16178","16179","16180","16181","16182","16183","16184","16185","16186","16187","16188","16189","16190","16191","16192","16193","16194","16195","16196","16197","16198","16199","16200","16201","16202","16203","16204","16205","16206","16207","16208","16209","16210","16211","16212","16213","16214","16215","16216","16217","16218","16219","16220","16221","16222","16223","16224","16225","16226","16227","16228","16229","16230","16231","16232","16233","16234","16235","16236","16237","16238","16239","16240","16241","16242","16243","16244","16245","16246","16247","16248","16249","16250","16251","16252","16253","16254","16255","16256","16257","16258","16259","16260","16261","16262","16263","16264","16265","16266","16267","16268","16269","16270","16271","16272","16273","16274","16275","16276","16277","16278","16279","16280","16281","16282","16283","16284","16285","16286","16287","16288","16289","16290","16291","16292","16293","16294","16295","16296","16297","16298","16299","16300","16301","16302","16303","16304","16305","16306","16307","16308","16309","16310","16311","16312","16313","16314","16315","16316","16317","16318","16319","16320","16321","16322","16323","16324","16325","16326","16327","16328","16329","16330","16331","16332","16333","16334","16335","16336","16337","16338","16339","16340","16341","16342","16343","16344","16345","16346","16347","16348","16349","16350","16351","16352","16353","16354","16355","16356","16357","16358","16359","16360","16361","16362","16363","16364","16365","16366","16367","16368","16369","16370","16371","16372","16373","16374","16375","16376","16377","16378","16379","16380","16381","16382","16383","16384","16385","16386","16387","16388","16389","16390","16391","16392","16393","16394","16395","16396","16397","16398","16399","16400","16401","16402","16403","16404","16405","16406","16407","16408","16409","16410","16411","16412","16413","16414","16415","16416","16417","16418","16419","16420","16421","16422","16423","16424","16425","16426","16427","16428","16429","16430","16431","16432","16433","16434","16435","16436","16437","16438","16439","16440","16441","16442","16443","16444","16445","16446","16447","16448","16449","16450","16451","16452","16453","16454","16455","16456","16457","16458","16459","16460","16461","16462","16463","16464","16465","16466","16467","16468","16469","16470","16471","16472","16473","16474","16475","16476","16477","16478","16479","16480","16481","16482","16483","16484","16485","16486","16487","16488","16489","16490","16491","16492","16493","16494","16495","16496","16497","16498","16499","16500","16501","16502","16503","16504","16505","16506","16507","16508","16509","16510","16511","16512","16513","16514","16515","16516","16517","16518","16519","16520","16521","16522","16523","16524","16525","16526","16527","16528","16529","16530","16531","16532","16533","16534","16535","16536","16537","16538","16539","16540","16541","16542","16543","16544","16545","16546","16547","16548","16549","16550","16551","16552","16553","16554","16555","16556","16557","16558","16559","16560","16561","16562","16563","16564","16565","16566","16567","16568","16569","16570","16571","16572","16573","16574","16575","16576","16577","16578","16579","16580","16581","16582","16583","16584","16585","16586","16587","16588","16589","16590","16591","16592","16593","16594","16595","16596","16597","16598","16599","16600","16601","16602","16603","16604","16605","16606","16607","16608","16609","16610","16611","16612","16613","16614","16615","16616","16617","16618","16619","16620","16621","16622","16623","16624","16625","16626","16627","16628","16629","16630","16631","16632","16633","16634","16635","16636","16637","16638","16639","16640","16641","16642","16643","16644","16645","16646","16647","16648","16649","16650","16651","16652","16653","16654","16655","16656","16657","16658","16659","16660","16661","16662","16663","16664","16665","16666","16667","16668","16669","16670","16671","16672","16673","16674","16675","16676","16677","16678","16679","16680","16681","16682","16683","16684","16685","16686","16687","16688","16689","16690","16691","16692","16693","16694","16695","16696","16697","16698","16699","16700","16701","16702","16703","16704","16705","16706","16707","16708","16709","16710","16711","16712","16713","16714","16715","16716","16717","16718","16719","16720","16721","16722","16723","16724","16725","16726","16727","16728","16729","16730","16731","16732","16733","16734","16735","16736","16737","16738","16739","16740","16741","16742","16743","16744","16745","16746","16747","16748","16749","16750","16751","16752","16753","16754","16755","16756","16757","16758","16759","16760","16761","16762","16763","16764","16765","16766","16767","16768","16769","16770","16771","16772","16773","16774","16775","16776","16777","16778","16779","16780","16781","16782","16783","16784","16785","16786","16787","16788","16789","16790","16791","16792","16793","16794","16795","16796","16797","16798","16799","16800","16801","16802","16803","16804","16805","16806","16807","16808","16809","16810","16811","16812","16813","16814","16815","16816","16817","16818","16819","16820","16821","16822","16823","16824","16825","16826","16827","16828","16829","16830","16831","16832","16833","16834","16835","16836","16837","16838","16839","16840","16841","16842","16843","16844","16845","16846","16847","16848","16849","16850","16851","16852","16853","16854","16855","16856","16857","16858","16859","16860","16861","16862","16863","16864","16865","16866","16867","16868","16869","16870","16871","16872","16873","16874","16875","16876","16877","16878","16879","16880","16881","16882","16883","16884","16885","16886","16887","16888","16889","16890","16891","16892","16893","16894","16895","16896","16897","16898","16899","16900","16901","16902","16903","16904","16905","16906","16907","16908","16909","16910","16911","16912","16913","16914","16915","16916","16917","16918","16919","16920","16921","16922","16923","16924","16925","16926","16927","16928","16929","16930","16931","16932","16933","16934","16935","16936","16937","16938","16939","16940","16941","16942","16943","16944","16945","16946","16947","16948","16949","16950","16951","16952","16953","16954","16955","16956","16957","16958","16959","16960","16961","16962","16963","16964","16965","16966","16967","16968","16969","16970","16971","16972","16973","16974","16975","16976","16977","16978","16979","16980","16981","16982","16983","16984","16985","16986","16987","16988","16989","16990","16991","16992","16993","16994","16995","16996","16997","16998","16999","17000","17001","17002","17003","17004","17005","17006","17007","17008","17009","17010","17011","17012","17013","17014","17015","17016","17017","17018","17019","17020","17021","17022","17023","17024","17025","17026","17027","17028","17029","17030","17031","17032","17033","17034","17035","17036","17037","17038","17039","17040","17041","17042","17043","17044","17045","17046","17047","17048","17049","17050","17051","17052","17053","17054","17055","17056","17057","17058","17059","17060","17061","17062","17063","17064","17065","17066","17067","17068","17069","17070","17071","17072","17073","17074","17075","17076","17077","17078","17079","17080","17081","17082","17083","17084","17085","17086","17087","17088","17089","17090","17091","17092","17093","17094","17095","17096","17097","17098","17099","17100","17101","17102","17103","17104","17105","17106","17107","17108","17109","17110","17111","17112","17113","17114","17115","17116","17117","17118","17119","17120","17121","17122","17123","17124","17125","17126","17127","17128","17129","17130","17131","17132","17133","17134","17135","17136","17137","17138","17139","17140","17141","17142","17143","17144","17145","17146","17147","17148","17149","17150","17151","17152","17153","17154","17155","17156","17157","17158","17159","17160","17161","17162","17163","17164","17165","17166","17167","17168","17169","17170","17171","17172","17173","17174","17175","17176","17177","17178","17179","17180","17181","17182","17183","17184","17185","17186","17187","17188","17189","17190","17191","17192","17193","17194","17195","17196","17197","17198","17199","17200","17201","17202","17203","17204","17205","17206","17207","17208","17209","17210","17211","17212","17213","17214","17215","17216","17217","17218","17219","17220","17221","17222","17223","17224","17225","17226","17227","17228","17229","17230","17231","17232","17233","17234","17235","17236","17237","17238","17239","17240","17241","17242","17243","17244","17245","17246","17247","17248","17249","17250","17251","17252","17253","17254","17255","17256","17257","17258","17259","17260","17261","17262","17263","17264","17265","17266","17267","17268","17269","17270","17271","17272","17273","17274","17275","17276","17277","17278","17279","17280","17281","17282","17283","17284","17285","17286","17287","17288","17289","17290","17291","17292","17293","17294","17295","17296","17297","17298","17299","17300","17301","17302","17303","17304","17305","17306","17307","17308","17309","17310","17311","17312","17313","17314","17315","17316","17317","17318","17319","17320","17321","17322","17323","17324","17325","17326","17327","17328","17329","17330","17331","17332","17333","17334","17335","17336","17337","17338","17339","17340","17341","17342","17343","17344","17345","17346","17347","17348","17349","17350","17351","17352","17353","17354","17355","17356","17357","17358","17359","17360","17361","17362","17363","17364","17365","17366","17367","17368","17369","17370","17371","17372","17373","17374","17375","17376","17377","17378","17379","17380","17381","17382","17383","17384","17385","17386","17387","17388","17389","17390","17391","17392","17393","17394","17395","17396","17397","17398","17399","17400","17401","17402","17403","17404","17405","17406","17407","17408","17409","17410","17411","17412","17413","17414","17415","17416","17417","17418","17419","17420","17421","17422","17423","17424","17425","17426","17427","17428","17429","17430","17431","17432","17433","17434","17435","17436","17437","17438","17439","17440","17441","17442","17443","17444","17445","17446","17447","17448","17449","17450","17451","17452","17453","17454","17455","17456","17457","17458","17459","17460","17461","17462","17463","17464","17465","17466","17467","17468","17469","17470","17471","17472","17473","17474","17475","17476","17477","17478","17479","17480","17481","17482","17483","17484","17485","17486","17487","17488","17489","17490","17491","17492","17493","17494","17495","17496","17497","17498","17499","17500","17501","17502","17503","17504","17505","17506","17507","17508","17509","17510","17511","17512","17513","17514","17515","17516","17517","17518","17519","17520","17521","17522","17523","17524","17525","17526","17527","17528","17529","17530","17531","17532","17533","17534","17535","17536","17537","17538","17539","17540","17541","17542","17543","17544","17545","17546","17547","17548","17549","17550","17551","17552","17553","17554","17555","17556","17557","17558","17559","17560","17561","17562","17563","17564","17565","17566","17567","17568","17569","17570","17571","17572","17573","17574","17575","17576","17577","17578","17579","17580","17581","17582","17583","17584","17585","17586","17587","17588","17589","17590","17591","17592","17593","17594","17595","17596","17597","17598","17599","17600","17601","17602","17603","17604","17605","17606","17607","17608","17609","17610","17611","17612","17613","17614","17615","17616","17617","17618","17619","17620","17621","17622","17623","17624","17625","17626","17627","17628","17629","17630","17631","17632","17633","17634","17635","17636","17637","17638","17639","17640","17641","17642","17643","17644","17645","17646","17647","17648","17649","17650","17651","17652","17653","17654","17655","17656","17657","17658","17659","17660","17661","17662","17663","17664","17665","17666","17667","17668","17669","17670","17671","17672","17673","17674","17675","17676","17677","17678","17679","17680","17681","17682","17683","17684","17685","17686","17687","17688","17689","17690","17691","17692","17693","17694","17695","17696","17697","17698","17699","17700","17701","17702","17703","17704","17705","17706","17707","17708","17709","17710","17711","17712","17713","17714","17715","17716","17717","17718","17719","17720","17721","17722","17723","17724","17725","17726","17727","17728","17729","17730","17731","17732","17733","17734","17735","17736","17737","17738","17739","17740","17741","17742","17743","17744","17745","17746","17747","17748","17749","17750","17751","17752","17753","17754","17755","17756","17757","17758","17759","17760","17761","17762","17763","17764","17765","17766","17767","17768","17769","17770","17771","17772","17773","17774","17775","17776","17777","17778","17779","17780","17781","17782","17783","17784","17785","17786","17787","17788","17789","17790","17791","17792","17793","17794","17795","17796","17797","17798","17799","17800","17801","17802","17803","17804","17805","17806","17807","17808","17809","17810","17811","17812","17813","17814","17815","17816","17817","17818","17819","17820","17821","17822","17823","17824","17825","17826","17827","17828","17829","17830","17831","17832","17833","17834","17835","17836","17837","17838","17839","17840","17841","17842","17843","17844","17845","17846","17847","17848","17849","17850","17851","17852","17853","17854","17855","17856","17857","17858","17859","17860","17861","17862","17863","17864","17865","17866","17867","17868","17869","17870","17871","17872","17873","17874","17875","17876","17877","17878","17879","17880","17881","17882","17883","17884","17885","17886","17887","17888","17889","17890","17891","17892","17893","17894","17895","17896","17897","17898","17899","17900","17901","17902","17903","17904","17905","17906","17907","17908","17909","17910","17911","17912","17913","17914","17915","17916","17917","17918","17919","17920","17921","17922","17923","17924","17925","17926","17927","17928","17929","17930","17931","17932","17933","17934","17935","17936","17937","17938","17939","17940","17941","17942","17943","17944","17945","17946","17947","17948","17949","17950","17951","17952","17953","17954","17955","17956","17957","17958","17959","17960","17961","17962","17963","17964","17965","17966","17967","17968","17969","17970","17971","17972","17973","17974","17975","17976","17977","17978","17979","17980","17981","17982","17983","17984","17985","17986","17987","17988","17989","17990","17991","17992","17993","17994","17995","17996","17997","17998","17999","18000","18001","18002","18003","18004","18005","18006","18007","18008","18009","18010","18011","18012","18013","18014","18015","18016","18017","18018","18019","18020","18021","18022","18023","18024","18025","18026","18027","18028","18029","18030","18031","18032","18033","18034","18035","18036","18037","18038","18039","18040","18041","18042","18043","18044","18045","18046","18047","18048","18049","18050","18051","18052","18053","18054","18055","18056","18057","18058","18059","18060","18061","18062","18063","18064","18065","18066","18067","18068","18069","18070","18071","18072","18073","18074","18075","18076","18077","18078","18079","18080","18081","18082","18083","18084","18085","18086","18087","18088","18089","18090","18091","18092","18093","18094","18095","18096","18097","18098","18099","18100","18101","18102","18103","18104","18105","18106","18107","18108","18109","18110","18111","18112","18113","18114","18115","18116","18117","18118","18119","18120","18121","18122","18123","18124","18125","18126","18127","18128","18129","18130","18131","18132","18133","18134","18135","18136","18137","18138","18139","18140","18141","18142","18143","18144","18145","18146","18147","18148","18149","18150","18151","18152","18153","18154","18155","18156","18157","18158","18159","18160","18161","18162","18163","18164","18165","18166","18167","18168","18169","18170","18171","18172","18173","18174","18175","18176","18177","18178","18179","18180","18181","18182","18183","18184","18185","18186","18187","18188","18189","18190","18191","18192","18193","18194","18195","18196","18197","18198","18199","18200","18201","18202","18203","18204","18205","18206","18207","18208","18209","18210","18211","18212","18213","18214","18215","18216","18217","18218","18219","18220","18221","18222","18223","18224","18225","18226","18227","18228","18229","18230","18231","18232","18233","18234","18235","18236","18237","18238","18239","18240","18241","18242","18243","18244","18245","18246","18247","18248","18249","18250","18251","18252","18253","18254","18255","18256","18257","18258","18259","18260","18261","18262","18263","18264","18265","18266","18267","18268","18269","18270","18271","18272","18273","18274","18275","18276","18277","18278","18279","18280","18281","18282","18283","18284","18285","18286","18287","18288","18289","18290","18291","18292","18293","18294","18295","18296","18297","18298","18299","18300","18301","18302","18303","18304","18305","18306","18307","18308","18309","18310","18311","18312","18313","18314","18315","18316","18317","18318","18319","18320","18321","18322","18323","18324","18325","18326","18327","18328","18329","18330","18331","18332","18333","18334","18335","18336","18337","18338","18339","18340","18341","18342","18343","18344","18345","18346","18347","18348","18349","18350","18351","18352","18353","18354","18355","18356","18357","18358","18359","18360","18361","18362","18363","18364","18365","18366","18367","18368","18369","18370","18371","18372","18373","18374","18375","18376","18377","18378","18379","18380","18381","18382","18383","18384","18385","18386","18387","18388","18389","18390","18391","18392","18393","18394","18395","18396","18397","18398","18399","18400","18401","18402","18403","18404","18405","18406","18407","18408","18409","18410","18411","18412","18413","18414","18415","18416","18417","18418","18419","18420","18421","18422","18423","18424","18425","18426","18427","18428","18429","18430","18431","18432","18433","18434","18435","18436","18437","18438","18439","18440","18441","18442","18443","18444","18445","18446","18447","18448","18449","18450","18451","18452","18453","18454","18455","18456","18457","18458","18459","18460","18461","18462","18463","18464","18465","18466","18467","18468","18469","18470","18471","18472","18473","18474","18475","18476","18477","18478","18479","18480","18481","18482","18483","18484","18485","18486","18487","18488","18489","18490","18491","18492","18493","18494","18495","18496","18497","18498","18499","18500","18501","18502","18503","18504","18505","18506","18507","18508","18509","18510","18511","18512","18513","18514","18515","18516","18517","18518","18519","18520","18521","18522","18523","18524","18525","18526","18527","18528","18529","18530","18531","18532","18533","18534","18535","18536","18537","18538","18539","18540","18541","18542","18543","18544","18545","18546","18547","18548","18549","18550","18551","18552","18553","18554","18555","18556","18557","18558","18559","18560","18561","18562","18563","18564","18565","18566","18567","18568","18569","18570","18571","18572","18573","18574","18575","18576","18577","18578","18579","18580","18581","18582","18583","18584","18585","18586","18587","18588","18589","18590","18591","18592","18593","18594","18595","18596","18597","18598","18599","18600","18601","18602","18603","18604","18605","18606","18607","18608","18609","18610","18611","18612","18613","18614","18615","18616","18617","18618","18619","18620","18621","18622","18623","18624","18625","18626","18627","18628","18629","18630","18631","18632","18633","18634","18635","18636","18637","18638","18639","18640","18641","18642","18643","18644","18645","18646","18647","18648","18649","18650","18651","18652","18653","18654","18655","18656","18657","18658","18659","18660","18661","18662","18663","18664","18665","18666","18667","18668","18669","18670","18671","18672","18673","18674","18675","18676","18677","18678","18679","18680","18681","18682","18683","18684","18685","18686","18687","18688","18689","18690","18691","18692","18693","18694","18695","18696","18697","18698","18699","18700","18701","18702","18703","18704","18705","18706","18707","18708","18709","18710","18711","18712","18713","18714","18715","18716","18717","18718","18719","18720","18721","18722","18723","18724","18725","18726","18727","18728","18729","18730","18731","18732","18733","18734","18735","18736","18737","18738","18739","18740","18741","18742","18743","18744","18745","18746","18747","18748","18749","18750","18751","18752","18753","18754","18755","18756","18757","18758","18759","18760","18761","18762","18763","18764","18765","18766","18767","18768","18769","18770","18771","18772","18773","18774","18775","18776","18777","18778","18779","18780","18781","18782","18783","18784","18785","18786","18787","18788","18789","18790","18791","18792","18793","18794","18795","18796","18797","18798","18799","18800","18801","18802","18803","18804","18805","18806","18807","18808","18809","18810","18811","18812","18813","18814","18815","18816","18817","18818","18819","18820","18821","18822","18823","18824","18825","18826","18827","18828","18829","18830","18831","18832","18833","18834","18835","18836","18837","18838","18839","18840","18841","18842","18843","18844","18845","18846","18847","18848","18849","18850","18851","18852","18853","18854","18855","18856","18857","18858","18859","18860","18861","18862","18863","18864","18865","18866","18867","18868","18869","18870","18871","18872","18873","18874","18875","18876","18877","18878","18879","18880","18881","18882","18883","18884","18885","18886","18887","18888","18889","18890","18891","18892","18893","18894","18895","18896","18897","18898","18899","18900","18901","18902","18903","18904","18905","18906","18907","18908","18909","18910","18911","18912","18913","18914","18915","18916","18917","18918","18919","18920","18921","18922","18923","18924","18925","18926","18927","18928","18929","18930","18931","18932","18933","18934","18935","18936","18937","18938","18939","18940","18941","18942","18943","18944","18945","18946","18947","18948","18949","18950","18951","18952","18953","18954","18955","18956","18957","18958","18959","18960","18961","18962","18963","18964","18965","18966","18967","18968","18969","18970","18971","18972","18973","18974","18975","18976","18977","18978","18979","18980","18981","18982","18983","18984","18985","18986","18987","18988","18989","18990","18991","18992","18993","18994","18995","18996","18997","18998","18999","19000","19001","19002","19003","19004","19005","19006","19007","19008","19009","19010","19011","19012","19013","19014","19015","19016","19017","19018","19019","19020","19021","19022","19023","19024","19025","19026","19027","19028","19029","19030","19031","19032","19033","19034","19035","19036","19037","19038","19039","19040","19041","19042","19043","19044","19045","19046","19047","19048","19049","19050","19051","19052","19053","19054","19055","19056","19057","19058","19059","19060","19061","19062","19063","19064","19065","19066","19067","19068","19069","19070","19071","19072","19073","19074","19075","19076","19077","19078","19079","19080","19081","19082","19083","19084","19085","19086","19087","19088","19089","19090","19091","19092","19093","19094","19095","19096","19097","19098","19099","19100","19101","19102","19103","19104","19105","19106","19107","19108","19109","19110","19111","19112","19113","19114","19115","19116","19117","19118","19119","19120","19121","19122","19123","19124","19125","19126","19127","19128","19129","19130","19131","19132","19133","19134","19135","19136","19137","19138","19139","19140","19141","19142","19143","19144","19145","19146","19147","19148","19149","19150","19151","19152","19153","19154","19155","19156","19157","19158","19159","19160","19161","19162","19163","19164","19165","19166","19167","19168","19169","19170","19171","19172","19173","19174","19175","19176","19177","19178","19179","19180","19181","19182","19183","19184","19185","19186","19187","19188","19189","19190","19191","19192","19193","19194","19195","19196","19197","19198","19199","19200","19201","19202","19203","19204","19205","19206","19207","19208","19209","19210","19211","19212","19213","19214","19215","19216","19217","19218","19219","19220","19221","19222","19223","19224","19225","19226","19227","19228","19229","19230","19231","19232","19233","19234","19235","19236","19237","19238","19239","19240","19241","19242","19243","19244","19245","19246","19247","19248","19249","19250","19251","19252","19253","19254","19255","19256","19257","19258","19259","19260","19261","19262","19263","19264","19265","19266","19267","19268","19269","19270","19271","19272","19273","19274","19275","19276","19277","19278","19279","19280","19281","19282","19283","19284","19285","19286","19287","19288","19289","19290","19291","19292","19293","19294","19295","19296","19297","19298","19299","19300","19301","19302","19303","19304","19305","19306","19307","19308","19309","19310","19311","19312","19313","19314","19315","19316","19317","19318","19319","19320","19321","19322","19323","19324","19325","19326","19327","19328","19329","19330","19331","19332","19333","19334","19335","19336","19337","19338","19339","19340","19341","19342","19343","19344","19345","19346","19347","19348","19349","19350","19351","19352","19353","19354","19355","19356","19357","19358","19359","19360","19361","19362","19363","19364","19365","19366","19367","19368","19369","19370","19371","19372","19373","19374","19375","19376","19377","19378","19379","19380","19381","19382","19383","19384","19385","19386","19387","19388","19389","19390","19391","19392","19393","19394","19395","19396","19397","19398","19399","19400","19401","19402","19403","19404","19405","19406","19407","19408","19409","19410","19411","19412","19413","19414","19415","19416","19417","19418","19419","19420","19421","19422","19423","19424","19425","19426","19427","19428","19429","19430","19431","19432","19433","19434","19435","19436","19437","19438","19439","19440","19441","19442","19443","19444","19445","19446","19447","19448","19449","19450","19451","19452","19453","19454","19455","19456","19457","19458","19459","19460","19461","19462","19463","19464","19465","19466","19467","19468","19469","19470","19471","19472","19473","19474","19475","19476","19477","19478","19479","19480","19481","19482","19483","19484","19485","19486","19487","19488","19489","19490","19491","19492","19493","19494","19495","19496","19497","19498","19499","19500","19501","19502","19503","19504","19505","19506","19507","19508","19509","19510","19511","19512","19513","19514","19515","19516","19517","19518","19519","19520","19521","19522","19523","19524","19525","19526","19527","19528","19529","19530","19531","19532","19533","19534","19535","19536","19537","19538","19539","19540","19541","19542","19543","19544","19545","19546","19547","19548","19549","19550","19551","19552","19553","19554","19555","19556","19557","19558","19559","19560","19561","19562","19563","19564","19565","19566","19567","19568","19569","19570","19571","19572","19573","19574","19575","19576","19577","19578","19579","19580","19581","19582","19583","19584","19585","19586","19587","19588","19589","19590","19591","19592","19593","19594","19595","19596","19597","19598","19599","19600","19601","19602","19603","19604","19605","19606","19607","19608","19609","19610","19611","19612","19613","19614","19615","19616","19617","19618","19619","19620","19621","19622","19623","19624","19625","19626","19627","19628","19629","19630","19631","19632","19633","19634","19635","19636","19637","19638","19639","19640","19641","19642","19643","19644","19645","19646","19647","19648","19649","19650","19651","19652","19653","19654","19655","19656","19657","19658","19659","19660","19661","19662","19663","19664","19665","19666","19667","19668","19669","19670","19671","19672","19673","19674","19675","19676","19677","19678","19679","19680","19681","19682","19683","19684","19685","19686","19687","19688","19689","19690","19691","19692","19693","19694","19695","19696","19697","19698","19699","19700","19701","19702","19703","19704","19705","19706","19707","19708","19709","19710","19711","19712","19713","19714","19715","19716","19717","19718","19719","19720","19721","19722","19723","19724","19725","19726","19727","19728","19729","19730","19731","19732","19733","19734","19735","19736","19737","19738","19739","19740","19741","19742","19743","19744","19745","19746","19747","19748","19749","19750","19751","19752","19753","19754","19755","19756","19757","19758","19759","19760","19761","19762","19763","19764","19765","19766","19767","19768","19769","19770","19771","19772","19773","19774","19775","19776","19777","19778","19779","19780","19781","19782","19783","19784","19785","19786","19787","19788","19789","19790","19791","19792","19793","19794","19795","19796","19797","19798","19799","19800","19801","19802","19803","19804","19805","19806","19807","19808","19809","19810","19811","19812","19813","19814","19815","19816","19817","19818","19819","19820","19821","19822","19823","19824","19825","19826","19827","19828","19829","19830","19831","19832","19833","19834","19835","19836","19837","19838","19839","19840","19841","19842","19843","19844","19845","19846","19847","19848","19849","19850","19851","19852","19853","19854","19855","19856","19857","19858","19859","19860","19861","19862","19863","19864","19865","19866","19867","19868","19869","19870","19871","19872","19873","19874","19875","19876","19877","19878","19879","19880","19881","19882","19883","19884","19885","19886","19887","19888","19889","19890","19891","19892","19893","19894","19895","19896","19897","19898","19899","19900","19901","19902","19903","19904","19905","19906","19907","19908","19909","19910","19911","19912","19913","19914","19915","19916","19917","19918","19919","19920","19921","19922","19923","19924","19925","19926","19927","19928","19929","19930","19931","19932","19933","19934","19935","19936","19937","19938","19939","19940","19941","19942","19943","19944","19945","19946","19947","19948","19949","19950","19951","19952","19953","19954","19955","19956","19957","19958","19959","19960","19961","19962","19963","19964","19965","19966","19967","19968","19969","19970","19971","19972","19973","19974","19975","19976","19977","19978","19979","19980","19981","19982","19983","19984","19985","19986","19987","19988","19989","19990","19991","19992","19993","19994","19995","19996","19997","19998","19999","20000","20001","20002","20003","20004","20005","20006","20007","20008","20009","20010","20011","20012","20013","20014","20015","20016","20017","20018","20019","20020","20021","20022","20023","20024","20025","20026","20027","20028","20029","20030","20031","20032","20033","20034","20035","20036","20037","20038","20039","20040","20041","20042","20043","20044","20045","20046","20047","20048","20049","20050","20051","20052","20053","20054","20055","20056","20057","20058","20059","20060","20061","20062","20063","20064","20065","20066","20067","20068","20069","20070","20071","20072","20073","20074","20075","20076","20077","20078","20079","20080","20081","20082","20083","20084","20085","20086","20087","20088","20089","20090","20091","20092","20093","20094","20095","20096","20097","20098","20099","20100","20101","20102","20103","20104","20105","20106","20107","20108","20109","20110","20111","20112","20113","20114","20115","20116","20117","20118","20119","20120","20121","20122","20123","20124","20125","20126","20127","20128","20129","20130","20131","20132","20133","20134","20135","20136","20137","20138","20139","20140","20141","20142","20143","20144","20145","20146","20147","20148","20149","20150","20151","20152","20153","20154","20155","20156","20157","20158","20159","20160","20161","20162","20163","20164","20165","20166","20167","20168","20169","20170","20171","20172","20173","20174","20175","20176","20177","20178","20179","20180","20181","20182","20183","20184","20185","20186","20187","20188","20189","20190","20191","20192","20193","20194","20195","20196","20197","20198","20199","20200","20201","20202","20203","20204","20205","20206","20207","20208","20209","20210","20211","20212","20213","20214","20215","20216","20217","20218","20219","20220","20221","20222","20223","20224","20225","20226","20227","20228","20229","20230","20231","20232","20233","20234","20235","20236","20237","20238","20239","20240","20241","20242","20243","20244","20245","20246","20247","20248","20249","20250","20251","20252","20253","20254","20255","20256","20257","20258","20259","20260","20261","20262","20263","20264","20265","20266","20267","20268","20269","20270","20271","20272","20273","20274","20275","20276","20277","20278","20279","20280","20281","20282","20283","20284","20285","20286","20287","20288","20289","20290","20291","20292","20293","20294","20295","20296","20297","20298","20299","20300","20301","20302","20303","20304","20305","20306","20307","20308","20309","20310","20311","20312","20313","20314","20315","20316","20317","20318","20319","20320","20321","20322","20323","20324","20325","20326","20327","20328","20329","20330","20331","20332","20333","20334","20335","20336","20337","20338","20339","20340","20341","20342","20343","20344","20345","20346","20347","20348","20349","20350","20351","20352","20353","20354","20355","20356","20357","20358","20359","20360","20361","20362","20363","20364","20365","20366","20367","20368","20369","20370","20371","20372","20373","20374","20375","20376","20377","20378","20379","20380","20381","20382","20383","20384","20385","20386","20387","20388","20389","20390","20391","20392","20393","20394","20395","20396","20397","20398","20399","20400","20401","20402","20403","20404","20405","20406","20407","20408","20409","20410","20411","20412","20413","20414","20415","20416","20417","20418","20419","20420","20421","20422","20423","20424","20425","20426","20427","20428","20429","20430","20431","20432","20433","20434","20435","20436","20437","20438","20439","20440","20441","20442","20443","20444","20445","20446","20447","20448","20449","20450","20451","20452","20453","20454","20455","20456","20457","20458","20459","20460","20461","20462","20463","20464","20465","20466","20467","20468","20469","20470","20471","20472","20473","20474","20475","20476","20477","20478","20479","20480","20481","20482","20483","20484","20485","20486","20487","20488","20489","20490","20491","20492","20493","20494","20495","20496","20497","20498","20499","20500","20501","20502","20503","20504","20505","20506","20507","20508","20509","20510","20511","20512","20513","20514","20515","20516","20517","20518","20519","20520","20521","20522","20523","20524","20525","20526","20527","20528","20529","20530","20531","20532","20533","20534","20535","20536","20537","20538","20539","20540","20541","20542","20543","20544","20545","20546","20547","20548","20549","20550","20551","20552","20553","20554","20555","20556","20557","20558","20559","20560","20561","20562","20563","20564","20565","20566","20567","20568","20569","20570","20571","20572","20573","20574","20575","20576","20577","20578","20579","20580","20581","20582","20583","20584","20585","20586","20587","20588","20589","20590","20591","20592","20593","20594","20595","20596","20597","20598","20599","20600","20601","20602","20603","20604","20605","20606","20607","20608","20609","20610","20611","20612","20613","20614","20615","20616","20617","20618","20619","20620","20621","20622","20623","20624","20625","20626","20627","20628","20629","20630","20631","20632","20633","20634","20635","20636","20637","20638","20639","20640","20641","20642","20643","20644","20645","20646","20647","20648","20649","20650","20651","20652","20653","20654","20655","20656","20657","20658","20659","20660","20661","20662","20663","20664","20665","20666","20667","20668","20669","20670","20671","20672","20673","20674","20675","20676","20677","20678","20679","20680","20681","20682","20683","20684","20685","20686","20687","20688","20689","20690","20691","20692","20693","20694","20695","20696","20697","20698","20699","20700","20701","20702","20703","20704","20705","20706","20707","20708","20709","20710","20711","20712","20713","20714","20715","20716","20717","20718","20719","20720","20721","20722","20723","20724","20725","20726","20727","20728","20729","20730","20731","20732","20733","20734","20735","20736","20737","20738","20739","20740","20741","20742","20743","20744","20745","20746","20747","20748","20749","20750","20751","20752","20753","20754","20755","20756","20757","20758","20759","20760","20761","20762","20763","20764","20765","20766","20767","20768","20769","20770","20771","20772","20773","20774","20775","20776","20777","20778","20779","20780","20781","20782","20783","20784","20785","20786","20787","20788","20789","20790","20791","20792","20793","20794","20795","20796","20797","20798","20799","20800","20801","20802","20803","20804","20805","20806","20807","20808","20809","20810","20811","20812","20813","20814","20815","20816","20817","20818","20819","20820","20821","20822","20823","20824","20825","20826","20827","20828","20829","20830","20831","20832","20833","20834","20835","20836","20837","20838","20839","20840","20841","20842","20843","20844","20845","20846","20847","20848","20849","20850","20851","20852","20853","20854","20855","20856","20857","20858","20859","20860","20861","20862","20863","20864","20865","20866","20867","20868","20869","20870","20871","20872","20873","20874","20875","20876","20877","20878","20879","20880","20881","20882","20883","20884","20885","20886","20887","20888","20889","20890","20891","20892","20893","20894","20895","20896","20897","20898","20899","20900","20901","20902","20903","20904","20905","20906","20907","20908","20909","20910","20911","20912","20913","20914","20915","20916","20917","20918","20919","20920","20921","20922","20923","20924","20925","20926","20927","20928","20929","20930","20931","20932","20933","20934","20935","20936","20937","20938","20939","20940","20941","20942","20943","20944","20945","20946","20947","20948","20949","20950","20951","20952","20953","20954","20955","20956","20957","20958","20959","20960","20961","20962","20963","20964","20965","20966","20967","20968","20969","20970","20971","20972","20973","20974","20975","20976","20977","20978","20979","20980","20981","20982","20983","20984","20985","20986","20987","20988","20989","20990","20991","20992","20993","20994","20995","20996","20997","20998","20999","21000","21001","21002","21003","21004","21005","21006","21007","21008","21009","21010","21011","21012","21013","21014","21015","21016","21017","21018","21019","21020","21021","21022","21023","21024","21025","21026","21027","21028","21029","21030","21031","21032","21033","21034","21035","21036","21037","21038","21039","21040","21041","21042","21043","21044","21045","21046","21047","21048","21049","21050","21051","21052","21053","21054","21055","21056","21057","21058","21059","21060","21061","21062","21063","21064","21065","21066","21067","21068","21069","21070","21071","21072","21073","21074","21075","21076","21077","21078","21079","21080","21081","21082","21083","21084","21085","21086","21087","21088","21089","21090","21091","21092","21093","21094","21095","21096","21097","21098","21099","21100","21101","21102","21103","21104","21105","21106","21107","21108","21109","21110","21111","21112","21113","21114","21115","21116","21117","21118","21119","21120","21121","21122","21123","21124","21125","21126","21127","21128","21129","21130","21131","21132","21133","21134","21135","21136","21137","21138","21139","21140","21141","21142","21143","21144","21145","21146","21147","21148","21149","21150","21151","21152","21153","21154","21155","21156","21157","21158","21159","21160","21161","21162","21163","21164","21165","21166","21167","21168","21169","21170","21171","21172","21173","21174","21175","21176","21177","21178","21179","21180","21181","21182","21183","21184","21185","21186","21187","21188","21189","21190","21191","21192","21193","21194","21195","21196","21197","21198","21199","21200","21201","21202","21203","21204","21205","21206","21207","21208","21209","21210","21211","21212","21213","21214","21215","21216","21217","21218","21219","21220","21221","21222","21223","21224","21225","21226","21227","21228","21229","21230","21231","21232","21233","21234","21235","21236","21237","21238","21239","21240","21241","21242","21243","21244","21245","21246","21247","21248","21249","21250","21251","21252","21253","21254","21255","21256","21257","21258","21259","21260","21261","21262","21263","21264","21265","21266","21267","21268","21269","21270","21271","21272","21273","21274","21275","21276","21277","21278","21279","21280","21281","21282","21283","21284","21285","21286","21287","21288","21289","21290","21291","21292","21293","21294","21295","21296","21297","21298","21299","21300","21301","21302","21303","21304","21305","21306","21307","21308","21309","21310","21311","21312","21313","21314","21315","21316","21317","21318","21319","21320","21321","21322","21323","21324","21325","21326","21327","21328","21329","21330","21331","21332","21333","21334","21335","21336","21337","21338","21339","21340","21341","21342","21343","21344","21345","21346","21347","21348","21349","21350","21351","21352","21353","21354","21355","21356","21357","21358","21359","21360","21361","21362","21363","21364","21365","21366","21367","21368","21369","21370","21371","21372","21373","21374","21375","21376","21377","21378","21379","21380","21381","21382","21383","21384","21385","21386","21387","21388","21389","21390","21391","21392","21393","21394","21395","21396","21397","21398","21399","21400","21401","21402","21403","21404","21405","21406","21407","21408","21409","21410","21411","21412","21413","21414","21415","21416","21417","21418","21419","21420","21421","21422","21423","21424","21425","21426","21427","21428","21429","21430","21431","21432","21433","21434","21435","21436","21437","21438","21439","21440","21441","21442","21443","21444","21445","21446","21447","21448","21449","21450","21451","21452","21453","21454","21455","21456","21457","21458","21459","21460","21461","21462","21463","21464","21465","21466","21467","21468","21469","21470","21471","21472","21473","21474","21475","21476","21477","21478","21479","21480","21481","21482","21483","21484","21485","21486","21487","21488","21489","21490","21491","21492","21493","21494","21495","21496","21497","21498","21499","21500","21501","21502","21503","21504","21505","21506","21507","21508","21509","21510","21511","21512","21513","21514","21515","21516","21517","21518","21519","21520","21521","21522","21523","21524","21525","21526","21527","21528","21529","21530","21531","21532","21533","21534","21535","21536","21537","21538","21539","21540","21541","21542","21543","21544","21545","21546","21547","21548","21549","21550","21551","21552","21553","21554","21555","21556","21557","21558","21559","21560","21561","21562","21563","21564","21565","21566","21567","21568","21569","21570","21571","21572","21573","21574","21575","21576","21577","21578","21579","21580","21581","21582","21583","21584","21585","21586","21587","21588","21589","21590","21591","21592","21593","21594","21595","21596","21597","21598","21599","21600","21601","21602","21603","21604","21605","21606","21607","21608","21609","21610","21611","21612","21613","21614","21615","21616","21617","21618","21619","21620","21621","21622","21623","21624","21625","21626","21627","21628","21629","21630","21631","21632","21633","21634","21635","21636","21637","21638","21639","21640","21641","21642","21643","21644","21645","21646","21647","21648","21649","21650","21651","21652","21653","21654","21655","21656","21657","21658","21659","21660","21661","21662","21663","21664","21665","21666","21667","21668","21669","21670","21671","21672","21673","21674","21675","21676","21677","21678","21679","21680","21681","21682","21683","21684","21685","21686","21687","21688","21689","21690","21691","21692","21693","21694","21695","21696","21697","21698","21699","21700","21701","21702","21703","21704","21705","21706","21707","21708","21709","21710","21711","21712","21713","21714","21715","21716","21717","21718","21719","21720","21721","21722","21723","21724","21725","21726","21727","21728","21729","21730","21731","21732","21733","21734","21735","21736","21737","21738","21739","21740","21741","21742","21743","21744","21745","21746","21747","21748","21749","21750","21751","21752","21753","21754","21755","21756","21757","21758","21759","21760","21761","21762","21763","21764","21765","21766","21767","21768","21769","21770","21771","21772","21773","21774","21775","21776","21777","21778","21779","21780","21781","21782","21783","21784","21785","21786","21787","21788","21789","21790","21791","21792","21793","21794","21795","21796","21797","21798","21799","21800","21801","21802","21803","21804","21805","21806","21807","21808","21809","21810","21811","21812","21813","21814","21815","21816","21817","21818","21819","21820","21821","21822","21823","21824","21825","21826","21827","21828","21829","21830","21831","21832","21833","21834","21835","21836","21837","21838","21839","21840","21841","21842","21843","21844","21845","21846","21847","21848","21849","21850","21851","21852","21853","21854","21855","21856","21857","21858","21859","21860","21861","21862","21863","21864","21865","21866","21867","21868","21869","21870","21871","21872","21873","21874","21875","21876","21877","21878","21879","21880","21881","21882","21883","21884","21885","21886","21887","21888","21889","21890","21891","21892","21893","21894","21895","21896","21897","21898","21899","21900","21901","21902","21903","21904","21905","21906","21907","21908","21909","21910","21911","21912","21913","21914","21915","21916","21917","21918","21919","21920","21921","21922","21923","21924","21925","21926","21927","21928","21929","21930","21931","21932","21933","21934","21935","21936","21937","21938","21939","21940","21941","21942","21943","21944","21945","21946","21947","21948","21949","21950","21951","21952","21953","21954","21955","21956","21957","21958","21959","21960","21961","21962","21963","21964","21965","21966","21967","21968","21969","21970","21971","21972","21973","21974","21975","21976","21977","21978","21979","21980","21981","21982","21983","21984","21985","21986","21987","21988","21989","21990","21991","21992","21993","21994","21995","21996","21997","21998","21999","22000","22001","22002","22003","22004","22005","22006","22007","22008","22009","22010","22011","22012","22013","22014","22015","22016","22017","22018","22019","22020","22021","22022","22023","22024","22025","22026","22027","22028","22029","22030","22031","22032","22033","22034","22035","22036","22037","22038","22039","22040","22041","22042","22043","22044","22045","22046","22047","22048","22049","22050","22051","22052","22053","22054","22055","22056","22057","22058","22059","22060","22061","22062","22063","22064","22065","22066","22067","22068","22069","22070","22071","22072","22073","22074","22075","22076","22077","22078","22079","22080","22081","22082","22083","22084","22085","22086","22087","22088","22089","22090","22091","22092","22093","22094","22095","22096","22097","22098","22099","22100","22101","22102","22103","22104","22105","22106","22107","22108","22109","22110","22111","22112","22113","22114","22115","22116","22117","22118","22119","22120","22121","22122","22123","22124","22125","22126","22127","22128","22129","22130","22131","22132","22133","22134","22135","22136","22137","22138","22139","22140","22141","22142","22143","22144","22145","22146","22147","22148","22149","22150","22151","22152","22153","22154","22155","22156","22157","22158","22159","22160","22161","22162","22163","22164","22165","22166","22167","22168","22169","22170","22171","22172","22173","22174","22175","22176","22177","22178","22179","22180","22181","22182","22183","22184","22185","22186","22187","22188","22189","22190","22191","22192","22193","22194","22195","22196","22197","22198","22199","22200","22201","22202","22203","22204","22205","22206","22207","22208","22209","22210","22211","22212","22213","22214","22215","22216","22217","22218","22219","22220","22221","22222","22223","22224","22225","22226","22227","22228","22229","22230","22231","22232","22233","22234","22235","22236","22237","22238","22239","22240","22241","22242","22243","22244","22245","22246","22247","22248","22249","22250","22251","22252","22253","22254","22255","22256","22257","22258","22259","22260","22261","22262","22263","22264","22265","22266","22267","22268","22269","22270","22271","22272","22273","22274","22275","22276","22277","22278","22279","22280","22281","22282","22283","22284","22285","22286","22287","22288","22289","22290","22291","22292","22293","22294","22295","22296","22297","22298","22299","22300","22301","22302","22303","22304","22305","22306","22307","22308","22309","22310","22311","22312","22313","22314","22315","22316","22317","22318","22319","22320","22321","22322","22323","22324","22325","22326","22327","22328","22329","22330","22331","22332","22333","22334","22335","22336","22337","22338","22339","22340","22341","22342","22343","22344","22345","22346","22347","22348","22349","22350","22351","22352","22353","22354","22355","22356","22357","22358","22359","22360","22361","22362","22363","22364","22365","22366","22367","22368","22369","22370","22371","22372","22373","22374","22375","22376","22377","22378","22379","22380","22381","22382","22383","22384","22385","22386","22387","22388","22389","22390","22391","22392","22393","22394","22395","22396","22397","22398","22399","22400","22401","22402","22403","22404","22405","22406","22407","22408","22409","22410","22411","22412","22413","22414","22415","22416","22417","22418","22419","22420","22421","22422","22423","22424","22425","22426","22427","22428","22429","22430","22431","22432","22433","22434","22435","22436","22437","22438","22439","22440","22441","22442","22443","22444","22445","22446","22447","22448","22449","22450","22451","22452","22453","22454","22455","22456","22457","22458","22459","22460","22461","22462","22463","22464","22465","22466","22467","22468","22469","22470","22471","22472","22473","22474","22475","22476","22477","22478","22479","22480","22481","22482","22483","22484","22485","22486","22487","22488","22489","22490","22491","22492","22493","22494","22495","22496","22497","22498","22499","22500","22501","22502","22503","22504","22505","22506","22507","22508","22509","22510","22511","22512","22513","22514","22515","22516","22517","22518","22519","22520","22521","22522","22523","22524","22525","22526","22527","22528","22529","22530","22531","22532","22533","22534","22535","22536","22537","22538","22539","22540","22541","22542","22543","22544","22545","22546","22547","22548","22549","22550","22551","22552","22553","22554","22555","22556","22557","22558","22559","22560","22561","22562","22563","22564","22565","22566","22567","22568","22569","22570","22571","22572","22573","22574","22575","22576","22577","22578","22579","22580","22581","22582","22583","22584","22585","22586","22587","22588","22589","22590","22591","22592","22593","22594","22595","22596","22597","22598","22599","22600","22601","22602","22603","22604","22605","22606","22607","22608","22609","22610","22611","22612","22613","22614","22615","22616","22617","22618","22619","22620","22621","22622","22623","22624","22625","22626","22627","22628","22629","22630","22631","22632","22633","22634","22635","22636","22637","22638","22639","22640","22641","22642","22643","22644","22645","22646","22647","22648","22649","22650","22651","22652","22653","22654","22655","22656","22657","22658","22659","22660","22661","22662","22663","22664","22665","22666","22667","22668","22669","22670","22671","22672","22673","22674","22675","22676","22677","22678","22679","22680","22681","22682","22683","22684","22685","22686","22687","22688","22689","22690","22691","22692","22693","22694","22695","22696","22697","22698","22699","22700","22701","22702","22703","22704","22705","22706","22707","22708","22709","22710","22711","22712","22713","22714","22715","22716","22717","22718","22719","22720","22721","22722","22723","22724","22725","22726","22727","22728","22729","22730","22731","22732","22733","22734","22735","22736","22737","22738","22739","22740","22741","22742","22743","22744","22745","22746","22747","22748","22749","22750","22751","22752","22753","22754","22755","22756","22757","22758","22759","22760","22761","22762","22763","22764","22765","22766","22767","22768","22769","22770","22771","22772","22773","22774","22775","22776","22777","22778","22779","22780","22781","22782","22783","22784","22785","22786","22787","22788","22789","22790","22791","22792","22793","22794","22795","22796","22797","22798","22799","22800","22801","22802","22803","22804","22805","22806","22807","22808","22809","22810","22811","22812","22813","22814","22815","22816","22817","22818","22819","22820","22821","22822","22823","22824","22825","22826","22827","22828","22829","22830","22831","22832","22833","22834","22835","22836","22837","22838","22839","22840","22841","22842","22843","22844","22845","22846","22847","22848","22849","22850","22851","22852","22853","22854","22855","22856","22857","22858","22859","22860","22861","22862","22863","22864","22865","22866","22867","22868","22869","22870","22871","22872","22873","22874","22875","22876","22877","22878","22879","22880","22881","22882","22883","22884","22885","22886","22887","22888","22889","22890","22891","22892","22893","22894","22895","22896","22897","22898","22899","22900","22901","22902","22903","22904","22905","22906","22907","22908","22909","22910","22911","22912","22913","22914","22915","22916","22917","22918","22919","22920","22921","22922","22923","22924","22925","22926","22927","22928","22929","22930","22931","22932","22933","22934","22935","22936","22937","22938","22939","22940","22941","22942","22943","22944","22945","22946","22947","22948","22949","22950","22951","22952","22953","22954","22955","22956","22957","22958","22959","22960","22961","22962","22963","22964","22965","22966","22967","22968","22969","22970","22971","22972","22973","22974","22975","22976","22977","22978","22979","22980","22981","22982","22983","22984","22985","22986","22987","22988","22989","22990","22991","22992","22993","22994","22995","22996","22997","22998","22999","23000","23001","23002","23003","23004","23005","23006","23007","23008","23009","23010","23011","23012","23013","23014","23015","23016","23017","23018","23019","23020","23021","23022","23023","23024","23025","23026","23027","23028","23029","23030","23031","23032","23033","23034","23035","23036","23037","23038","23039","23040","23041","23042","23043","23044","23045","23046","23047","23048","23049","23050","23051","23052","23053","23054","23055","23056","23057","23058","23059","23060","23061","23062","23063","23064","23065","23066","23067","23068","23069","23070","23071","23072","23073","23074","23075","23076","23077","23078","23079","23080","23081","23082","23083","23084","23085","23086","23087","23088","23089","23090","23091","23092","23093","23094","23095","23096","23097","23098","23099","23100","23101","23102","23103","23104","23105","23106","23107","23108","23109","23110","23111","23112","23113","23114","23115","23116","23117","23118","23119","23120","23121","23122","23123","23124","23125","23126","23127","23128","23129","23130","23131","23132","23133","23134","23135","23136","23137","23138","23139","23140","23141","23142","23143","23144","23145","23146","23147","23148","23149","23150","23151","23152","23153","23154","23155","23156","23157","23158","23159","23160","23161","23162","23163","23164","23165","23166","23167","23168","23169","23170","23171","23172","23173","23174","23175","23176","23177","23178","23179","23180","23181","23182","23183","23184","23185","23186","23187","23188","23189","23190","23191","23192","23193","23194","23195","23196","23197","23198","23199","23200","23201","23202","23203","23204","23205","23206","23207","23208","23209","23210","23211","23212","23213","23214","23215","23216","23217","23218","23219","23220","23221","23222","23223","23224","23225","23226","23227","23228","23229","23230","23231","23232","23233","23234","23235","23236","23237","23238","23239","23240","23241","23242","23243","23244","23245","23246","23247","23248","23249","23250","23251","23252","23253","23254","23255","23256","23257","23258","23259","23260","23261","23262","23263","23264","23265","23266","23267","23268","23269","23270","23271","23272","23273","23274","23275","23276","23277","23278","23279","23280","23281","23282","23283","23284","23285","23286","23287","23288","23289","23290","23291","23292","23293","23294","23295","23296","23297","23298","23299","23300","23301","23302","23303","23304","23305","23306","23307","23308","23309","23310","23311","23312","23313","23314","23315","23316","23317","23318","23319","23320","23321","23322","23323","23324","23325","23326","23327","23328","23329","23330","23331","23332","23333","23334","23335","23336","23337","23338","23339","23340","23341","23342","23343","23344","23345","23346","23347","23348","23349","23350","23351","23352","23353","23354","23355","23356","23357","23358","23359","23360","23361","23362","23363","23364","23365","23366","23367","23368","23369","23370","23371","23372","23373","23374","23375","23376","23377","23378","23379","23380","23381","23382","23383","23384","23385","23386","23387","23388","23389","23390","23391","23392","23393","23394","23395","23396","23397","23398","23399","23400","23401","23402","23403","23404","23405","23406","23407","23408","23409","23410","23411","23412","23413","23414","23415","23416","23417","23418","23419","23420","23421","23422","23423","23424","23425","23426","23427","23428","23429","23430","23431","23432","23433","23434","23435","23436","23437","23438","23439","23440","23441","23442","23443","23444","23445","23446","23447","23448","23449","23450","23451","23452","23453","23454","23455","23456","23457","23458","23459","23460","23461","23462","23463","23464","23465","23466","23467","23468","23469","23470","23471","23472","23473","23474","23475","23476","23477","23478","23479","23480","23481","23482","23483","23484","23485","23486","23487","23488","23489","23490","23491","23492","23493","23494","23495","23496","23497","23498","23499","23500","23501","23502","23503","23504","23505","23506","23507","23508","23509","23510","23511","23512","23513","23514","23515","23516","23517","23518","23519","23520","23521","23522","23523","23524","23525","23526","23527","23528","23529","23530","23531","23532","23533","23534","23535","23536","23537","23538","23539","23540","23541","23542","23543","23544","23545","23546","23547","23548","23549","23550","23551","23552","23553","23554","23555","23556","23557","23558","23559","23560","23561","23562","23563","23564","23565","23566","23567","23568","23569","23570","23571","23572","23573","23574","23575","23576","23577","23578","23579","23580","23581","23582","23583","23584","23585","23586","23587","23588","23589","23590","23591","23592","23593","23594","23595","23596","23597","23598","23599","23600","23601","23602","23603","23604","23605","23606","23607","23608","23609","23610","23611","23612","23613","23614","23615","23616","23617","23618","23619","23620","23621","23622","23623","23624","23625","23626","23627","23628","23629","23630","23631","23632","23633","23634","23635","23636","23637","23638","23639","23640","23641","23642","23643","23644","23645","23646","23647","23648","23649","23650","23651","23652","23653","23654","23655","23656","23657","23658","23659","23660","23661","23662","23663","23664","23665","23666","23667","23668","23669","23670","23671","23672","23673","23674","23675","23676","23677","23678","23679","23680","23681","23682","23683","23684","23685","23686","23687","23688","23689","23690","23691","23692","23693","23694","23695","23696","23697","23698","23699","23700","23701","23702","23703","23704","23705","23706","23707","23708","23709","23710","23711","23712","23713","23714","23715","23716","23717","23718","23719","23720","23721","23722","23723","23724","23725","23726","23727","23728","23729","23730","23731","23732","23733","23734","23735","23736","23737","23738","23739","23740","23741","23742","23743","23744","23745","23746","23747","23748","23749","23750","23751","23752","23753","23754","23755","23756","23757","23758","23759","23760","23761","23762","23763","23764","23765","23766","23767","23768","23769","23770","23771","23772","23773","23774","23775","23776","23777","23778","23779","23780","23781","23782","23783","23784","23785","23786","23787","23788","23789","23790","23791","23792","23793","23794","23795","23796","23797","23798","23799","23800","23801","23802","23803","23804","23805","23806","23807","23808","23809","23810","23811","23812","23813","23814","23815","23816","23817","23818","23819","23820","23821","23822","23823","23824","23825","23826","23827","23828","23829","23830","23831","23832","23833","23834","23835","23836","23837","23838","23839","23840","23841","23842","23843","23844","23845","23846","23847","23848","23849","23850","23851","23852","23853","23854","23855","23856","23857","23858","23859","23860","23861","23862","23863","23864","23865","23866","23867","23868","23869","23870","23871","23872","23873","23874","23875","23876","23877","23878","23879","23880","23881","23882","23883","23884","23885","23886","23887","23888","23889","23890","23891","23892","23893","23894","23895","23896","23897","23898","23899","23900","23901","23902","23903","23904","23905","23906","23907","23908","23909","23910","23911","23912","23913","23914","23915","23916","23917","23918","23919","23920","23921","23922","23923","23924","23925","23926","23927","23928","23929","23930","23931","23932","23933","23934","23935","23936","23937","23938","23939","23940","23941","23942","23943","23944","23945","23946","23947","23948","23949","23950","23951","23952","23953","23954","23955","23956","23957","23958","23959","23960","23961","23962","23963","23964","23965","23966","23967","23968","23969","23970","23971","23972","23973","23974","23975","23976","23977","23978","23979","23980","23981","23982","23983","23984","23985","23986","23987","23988","23989","23990","23991","23992","23993","23994","23995","23996","23997","23998","23999","24000","24001","24002","24003","24004","24005","24006","24007","24008","24009","24010","24011","24012","24013","24014","24015","24016","24017","24018","24019","24020","24021","24022","24023","24024","24025","24026","24027","24028","24029","24030","24031","24032","24033","24034","24035","24036","24037","24038","24039","24040","24041","24042","24043","24044","24045","24046","24047","24048","24049","24050","24051","24052","24053","24054","24055","24056","24057","24058","24059","24060","24061","24062","24063","24064","24065","24066","24067","24068","24069","24070","24071","24072","24073","24074","24075","24076","24077","24078","24079","24080","24081","24082","24083","24084","24085","24086","24087","24088","24089","24090","24091","24092","24093","24094","24095","24096","24097","24098","24099","24100","24101","24102","24103","24104","24105","24106","24107","24108","24109","24110","24111","24112","24113","24114","24115","24116","24117","24118","24119","24120","24121","24122","24123","24124","24125","24126","24127","24128","24129","24130","24131","24132","24133","24134","24135","24136","24137","24138","24139","24140","24141","24142","24143","24144","24145","24146","24147","24148","24149","24150","24151","24152","24153","24154","24155","24156","24157","24158","24159","24160","24161","24162","24163","24164","24165","24166","24167","24168","24169","24170","24171","24172","24173","24174","24175","24176","24177","24178","24179","24180","24181","24182","24183","24184","24185","24186","24187","24188","24189","24190","24191","24192","24193","24194","24195","24196","24197","24198","24199","24200","24201","24202","24203","24204","24205","24206","24207","24208","24209","24210","24211","24212","24213","24214","24215","24216","24217","24218","24219","24220","24221","24222","24223","24224","24225","24226","24227","24228","24229","24230","24231","24232","24233","24234","24235","24236","24237","24238","24239","24240","24241","24242","24243","24244","24245","24246","24247","24248","24249","24250","24251","24252","24253","24254","24255","24256","24257","24258","24259","24260","24261","24262","24263","24264","24265","24266","24267","24268","24269","24270","24271","24272","24273","24274","24275","24276","24277","24278","24279","24280","24281","24282","24283","24284","24285","24286","24287","24288","24289","24290","24291","24292","24293","24294","24295","24296","24297","24298","24299","24300","24301","24302","24303","24304","24305","24306","24307","24308","24309","24310","24311","24312","24313","24314","24315","24316","24317","24318","24319","24320","24321","24322","24323","24324","24325","24326","24327","24328","24329","24330","24331","24332","24333","24334","24335","24336","24337","24338","24339","24340","24341","24342","24343","24344","24345","24346","24347","24348","24349","24350","24351","24352","24353","24354","24355","24356","24357","24358","24359","24360","24361","24362","24363","24364","24365","24366","24367","24368","24369","24370","24371","24372","24373","24374","24375","24376","24377","24378","24379","24380","24381","24382","24383","24384","24385","24386","24387","24388","24389","24390","24391","24392","24393","24394","24395","24396","24397","24398","24399","24400","24401","24402","24403","24404","24405","24406","24407","24408","24409","24410","24411","24412","24413","24414","24415","24416","24417","24418","24419","24420","24421","24422","24423","24424","24425","24426","24427","24428","24429","24430","24431","24432","24433","24434","24435","24436","24437","24438","24439","24440","24441","24442","24443","24444","24445","24446","24447","24448","24449","24450","24451","24452","24453","24454","24455","24456","24457","24458","24459","24460","24461","24462","24463","24464","24465","24466","24467","24468","24469","24470","24471","24472","24473","24474","24475","24476","24477","24478","24479","24480","24481","24482","24483","24484","24485","24486","24487","24488","24489","24490","24491","24492","24493","24494","24495","24496","24497","24498","24499","24500","24501","24502","24503","24504","24505","24506","24507","24508","24509","24510","24511","24512","24513","24514","24515","24516","24517","24518","24519","24520","24521","24522","24523","24524","24525","24526","24527","24528","24529","24530","24531","24532","24533","24534","24535","24536","24537","24538","24539","24540","24541","24542","24543","24544","24545","24546","24547","24548","24549","24550","24551","24552","24553","24554","24555","24556","24557","24558","24559","24560","24561","24562","24563","24564","24565","24566","24567","24568","24569","24570","24571","24572","24573","24574","24575","24576","24577","24578","24579","24580","24581","24582","24583","24584","24585","24586","24587","24588","24589","24590","24591","24592","24593","24594","24595","24596","24597","24598","24599","24600","24601","24602","24603","24604","24605","24606","24607","24608","24609","24610","24611","24612","24613","24614","24615","24616","24617","24618","24619","24620","24621","24622","24623","24624","24625","24626","24627","24628","24629","24630","24631","24632","24633","24634","24635","24636","24637","24638","24639","24640","24641","24642","24643","24644","24645","24646","24647","24648","24649","24650","24651","24652","24653","24654","24655","24656","24657","24658","24659","24660","24661","24662","24663","24664","24665","24666","24667","24668","24669","24670","24671","24672","24673","24674","24675","24676","24677","24678","24679","24680","24681","24682","24683","24684","24685","24686","24687","24688","24689","24690","24691","24692","24693","24694","24695","24696","24697","24698","24699","24700","24701","24702","24703","24704","24705","24706","24707","24708","24709","24710","24711","24712","24713","24714","24715","24716","24717","24718","24719","24720","24721","24722","24723","24724","24725","24726","24727","24728","24729","24730","24731","24732","24733","24734","24735","24736","24737","24738","24739","24740","24741","24742","24743","24744","24745","24746","24747","24748","24749","24750","24751","24752","24753","24754","24755","24756","24757","24758","24759","24760","24761","24762","24763","24764","24765","24766","24767","24768","24769","24770","24771","24772","24773","24774","24775","24776","24777","24778","24779","24780","24781","24782","24783","24784","24785","24786","24787","24788","24789","24790","24791","24792","24793","24794","24795","24796","24797","24798","24799","24800","24801","24802","24803","24804","24805","24806","24807","24808","24809","24810","24811","24812","24813","24814","24815","24816","24817","24818","24819","24820","24821","24822","24823","24824","24825","24826","24827","24828","24829","24830","24831","24832","24833","24834","24835","24836","24837","24838","24839","24840","24841","24842","24843","24844","24845","24846","24847","24848","24849","24850","24851","24852","24853","24854","24855","24856","24857","24858","24859","24860","24861","24862","24863","24864","24865","24866","24867","24868","24869","24870","24871","24872","24873","24874","24875","24876","24877","24878","24879","24880","24881","24882","24883","24884","24885","24886","24887","24888","24889","24890","24891","24892","24893","24894","24895","24896","24897","24898","24899","24900","24901","24902","24903","24904","24905","24906","24907","24908","24909","24910","24911","24912","24913","24914","24915","24916","24917","24918","24919","24920","24921","24922","24923","24924","24925","24926","24927","24928","24929","24930","24931","24932","24933","24934","24935","24936","24937","24938","24939","24940","24941","24942","24943","24944","24945","24946","24947","24948","24949","24950","24951","24952","24953","24954","24955","24956","24957","24958","24959","24960","24961","24962","24963","24964","24965","24966","24967","24968","24969","24970","24971","24972","24973","24974","24975","24976","24977","24978","24979","24980","24981","24982","24983","24984","24985","24986","24987","24988","24989","24990","24991","24992","24993","24994","24995","24996","24997","24998","24999","25000","25001","25002","25003","25004","25005","25006","25007","25008","25009","25010","25011","25012","25013","25014","25015","25016","25017","25018","25019","25020","25021","25022","25023","25024","25025","25026","25027","25028","25029","25030","25031","25032","25033","25034","25035","25036","25037","25038","25039","25040","25041","25042","25043","25044","25045","25046","25047","25048","25049","25050","25051","25052","25053","25054","25055","25056","25057","25058","25059","25060","25061","25062","25063","25064","25065","25066","25067","25068","25069","25070","25071","25072","25073","25074","25075","25076","25077","25078","25079","25080","25081","25082","25083","25084","25085","25086","25087","25088","25089","25090","25091","25092","25093","25094","25095","25096","25097","25098","25099","25100","25101","25102","25103","25104","25105","25106","25107","25108","25109","25110","25111","25112","25113","25114","25115","25116","25117","25118","25119","25120","25121","25122","25123","25124","25125","25126","25127","25128","25129","25130","25131","25132","25133","25134","25135","25136","25137","25138","25139","25140","25141","25142","25143","25144","25145","25146","25147","25148","25149","25150","25151","25152","25153","25154","25155","25156","25157","25158","25159","25160","25161","25162","25163","25164","25165","25166","25167","25168","25169","25170","25171","25172","25173","25174","25175","25176","25177","25178","25179","25180","25181","25182","25183","25184","25185","25186","25187","25188","25189","25190","25191","25192","25193","25194","25195","25196","25197","25198","25199","25200","25201","25202","25203","25204","25205","25206","25207","25208","25209","25210","25211","25212","25213","25214","25215","25216","25217","25218","25219","25220","25221","25222","25223","25224","25225","25226","25227","25228","25229","25230","25231","25232","25233","25234","25235","25236","25237","25238","25239","25240","25241","25242","25243","25244","25245","25246","25247","25248","25249","25250","25251","25252","25253","25254","25255","25256","25257","25258","25259","25260","25261","25262","25263","25264","25265","25266","25267","25268","25269","25270","25271","25272","25273","25274","25275","25276","25277","25278","25279","25280","25281","25282","25283","25284","25285","25286","25287","25288","25289","25290","25291","25292","25293","25294","25295","25296","25297","25298","25299","25300","25301","25302","25303","25304","25305","25306","25307","25308","25309","25310","25311","25312","25313","25314","25315","25316","25317","25318","25319","25320","25321","25322","25323","25324","25325","25326","25327","25328","25329","25330","25331","25332","25333","25334","25335","25336","25337","25338","25339","25340","25341","25342","25343","25344","25345","25346","25347","25348","25349","25350","25351","25352","25353","25354","25355","25356","25357","25358","25359","25360","25361","25362","25363","25364","25365","25366","25367","25368","25369","25370","25371","25372","25373","25374","25375","25376","25377","25378","25379","25380","25381","25382","25383","25384","25385","25386","25387","25388","25389","25390","25391","25392","25393","25394","25395","25396","25397","25398","25399","25400","25401","25402","25403","25404","25405","25406","25407","25408","25409","25410","25411","25412","25413","25414","25415","25416","25417","25418","25419","25420","25421","25422","25423","25424","25425","25426","25427","25428","25429","25430","25431","25432","25433","25434","25435","25436","25437","25438","25439","25440","25441","25442","25443","25444","25445","25446","25447","25448","25449","25450","25451","25452","25453","25454","25455","25456","25457","25458","25459","25460","25461","25462","25463","25464","25465","25466","25467","25468","25469","25470","25471","25472","25473","25474","25475","25476","25477","25478","25479","25480","25481","25482","25483","25484","25485","25486","25487","25488","25489","25490","25491","25492","25493","25494","25495","25496","25497","25498","25499","25500","25501","25502","25503","25504","25505","25506","25507","25508","25509","25510","25511","25512","25513","25514","25515","25516","25517","25518","25519","25520","25521","25522","25523","25524","25525","25526","25527","25528","25529","25530","25531","25532","25533","25534","25535","25536","25537","25538","25539","25540","25541","25542","25543","25544","25545","25546","25547","25548","25549","25550","25551","25552","25553","25554","25555","25556","25557","25558","25559","25560","25561","25562","25563","25564","25565","25566","25567","25568","25569","25570","25571","25572","25573","25574","25575","25576","25577","25578","25579","25580","25581","25582","25583","25584","25585","25586","25587","25588","25589","25590","25591","25592","25593","25594","25595","25596","25597","25598","25599","25600","25601","25602","25603","25604","25605","25606","25607","25608","25609","25610","25611","25612","25613","25614","25615","25616","25617","25618","25619","25620","25621","25622","25623","25624","25625","25626","25627","25628","25629","25630","25631","25632","25633","25634","25635","25636","25637","25638","25639","25640","25641","25642","25643","25644","25645","25646","25647","25648","25649","25650","25651","25652","25653","25654","25655","25656","25657","25658","25659","25660","25661","25662","25663","25664","25665","25666","25667","25668","25669","25670","25671","25672","25673","25674","25675","25676","25677","25678","25679","25680","25681","25682","25683","25684","25685","25686","25687","25688","25689","25690","25691","25692","25693","25694","25695","25696","25697","25698","25699","25700","25701","25702","25703","25704","25705","25706","25707","25708","25709","25710","25711","25712","25713","25714","25715","25716","25717","25718","25719","25720","25721","25722","25723","25724","25725","25726","25727","25728","25729","25730","25731","25732","25733","25734","25735","25736","25737","25738","25739","25740","25741","25742","25743","25744","25745","25746","25747","25748","25749","25750","25751","25752","25753","25754","25755","25756","25757","25758","25759","25760","25761","25762","25763","25764","25765","25766","25767","25768","25769","25770","25771","25772","25773","25774","25775","25776","25777","25778","25779","25780","25781","25782","25783","25784","25785","25786","25787","25788","25789","25790","25791","25792","25793","25794","25795","25796","25797","25798","25799","25800","25801","25802","25803","25804","25805","25806","25807","25808","25809","25810","25811","25812","25813","25814","25815","25816","25817","25818","25819","25820","25821","25822","25823","25824","25825","25826","25827","25828","25829","25830","25831","25832","25833","25834","25835","25836","25837","25838","25839","25840","25841","25842","25843","25844","25845","25846","25847","25848","25849","25850","25851","25852","25853","25854","25855","25856","25857","25858","25859","25860","25861","25862","25863","25864","25865","25866","25867","25868","25869","25870","25871","25872","25873","25874","25875","25876","25877","25878","25879","25880","25881","25882","25883","25884","25885","25886","25887","25888","25889","25890","25891","25892","25893","25894","25895","25896","25897","25898","25899","25900","25901","25902","25903","25904","25905","25906","25907","25908","25909","25910","25911","25912","25913","25914","25915","25916","25917","25918","25919","25920","25921","25922","25923","25924","25925","25926","25927","25928","25929","25930","25931","25932","25933","25934","25935","25936","25937","25938","25939","25940","25941","25942","25943","25944","25945","25946","25947","25948","25949","25950","25951","25952","25953","25954","25955","25956","25957","25958","25959","25960","25961","25962","25963","25964","25965","25966","25967","25968","25969","25970","25971","25972","25973","25974","25975","25976","25977","25978","25979","25980","25981","25982","25983","25984","25985","25986","25987","25988","25989","25990","25991","25992","25993","25994","25995","25996","25997","25998","25999","26000","26001","26002","26003","26004","26005","26006","26007","26008","26009","26010","26011","26012","26013","26014","26015","26016","26017","26018","26019","26020","26021","26022","26023","26024","26025","26026","26027","26028","26029","26030","26031","26032","26033","26034","26035","26036","26037","26038","26039","26040","26041","26042","26043","26044","26045","26046","26047","26048","26049","26050","26051","26052","26053","26054","26055","26056","26057","26058","26059","26060","26061","26062","26063","26064","26065","26066","26067","26068","26069","26070","26071","26072","26073","26074","26075","26076","26077","26078","26079","26080","26081","26082","26083","26084","26085","26086","26087","26088","26089","26090","26091","26092","26093","26094","26095","26096","26097","26098","26099","26100","26101","26102","26103","26104","26105","26106","26107","26108","26109","26110","26111","26112","26113","26114","26115","26116","26117","26118","26119","26120","26121","26122","26123","26124","26125","26126","26127","26128","26129","26130","26131","26132","26133","26134","26135","26136","26137","26138","26139","26140","26141","26142","26143","26144","26145","26146","26147","26148","26149","26150","26151","26152","26153","26154","26155","26156","26157","26158","26159","26160","26161","26162","26163","26164","26165","26166","26167","26168","26169","26170","26171","26172","26173","26174","26175","26176","26177","26178","26179","26180","26181","26182","26183","26184","26185","26186","26187","26188","26189","26190","26191","26192","26193","26194","26195","26196","26197","26198","26199","26200","26201","26202","26203","26204","26205","26206","26207","26208","26209","26210","26211","26212","26213","26214","26215","26216","26217","26218","26219","26220","26221","26222","26223","26224","26225","26226","26227","26228","26229","26230","26231","26232","26233","26234","26235","26236","26237","26238","26239","26240","26241","26242","26243","26244","26245","26246","26247","26248","26249","26250","26251","26252","26253","26254","26255","26256","26257","26258","26259","26260","26261","26262","26263","26264","26265","26266","26267","26268","26269","26270","26271","26272","26273","26274","26275","26276","26277","26278","26279","26280","26281","26282","26283","26284","26285","26286","26287","26288","26289","26290","26291","26292","26293","26294","26295","26296","26297","26298","26299","26300","26301","26302","26303","26304","26305","26306","26307","26308","26309","26310","26311","26312","26313","26314","26315","26316","26317","26318","26319","26320","26321","26322","26323","26324","26325","26326","26327","26328","26329","26330","26331","26332","26333","26334","26335","26336","26337","26338","26339","26340","26341","26342","26343","26344","26345","26346","26347","26348","26349","26350","26351","26352","26353","26354","26355","26356","26357","26358","26359","26360","26361","26362","26363","26364","26365","26366","26367","26368","26369","26370","26371","26372","26373","26374","26375","26376","26377","26378","26379","26380","26381","26382","26383","26384","26385","26386","26387","26388","26389","26390","26391","26392","26393","26394","26395","26396","26397","26398","26399","26400","26401","26402","26403","26404","26405","26406","26407","26408","26409","26410","26411","26412","26413","26414","26415","26416","26417","26418","26419","26420","26421","26422","26423","26424","26425","26426","26427","26428","26429","26430","26431","26432","26433","26434","26435","26436","26437","26438","26439","26440","26441","26442","26443","26444","26445","26446","26447","26448","26449","26450","26451","26452","26453","26454","26455","26456","26457","26458","26459","26460","26461","26462","26463","26464","26465","26466","26467","26468","26469","26470","26471","26472","26473","26474","26475","26476","26477","26478","26479","26480","26481","26482","26483","26484","26485","26486","26487","26488","26489","26490","26491","26492","26493","26494","26495","26496","26497","26498","26499","26500","26501","26502","26503","26504","26505","26506","26507","26508","26509","26510","26511","26512","26513","26514","26515","26516","26517","26518","26519","26520","26521","26522","26523","26524","26525","26526","26527","26528","26529","26530","26531","26532","26533","26534","26535","26536","26537","26538","26539","26540","26541","26542","26543","26544","26545","26546","26547","26548","26549","26550","26551","26552","26553","26554","26555","26556","26557","26558","26559","26560","26561","26562","26563","26564","26565","26566","26567","26568","26569","26570","26571","26572","26573","26574","26575","26576","26577","26578","26579","26580","26581","26582","26583","26584","26585","26586","26587","26588","26589","26590","26591","26592","26593","26594","26595","26596","26597","26598","26599","26600","26601","26602","26603","26604","26605","26606","26607","26608","26609","26610","26611","26612","26613","26614","26615","26616","26617","26618","26619","26620","26621","26622","26623","26624","26625","26626","26627","26628","26629","26630","26631","26632","26633","26634","26635","26636","26637","26638","26639","26640","26641","26642","26643","26644","26645","26646","26647","26648","26649","26650","26651","26652","26653","26654","26655","26656","26657","26658","26659","26660","26661","26662","26663","26664","26665","26666","26667","26668","26669","26670","26671","26672","26673","26674","26675","26676","26677","26678","26679","26680","26681","26682","26683","26684","26685","26686","26687","26688","26689","26690","26691","26692","26693","26694","26695","26696","26697","26698","26699","26700","26701","26702","26703","26704","26705","26706","26707","26708","26709","26710","26711","26712","26713","26714","26715","26716","26717","26718","26719","26720","26721","26722","26723","26724","26725","26726","26727","26728","26729","26730","26731","26732","26733","26734","26735","26736","26737","26738","26739","26740","26741","26742","26743","26744","26745","26746","26747","26748","26749","26750","26751","26752","26753","26754","26755","26756","26757","26758","26759","26760","26761","26762","26763","26764","26765","26766","26767","26768","26769","26770","26771","26772","26773","26774","26775","26776","26777","26778","26779","26780","26781","26782","26783","26784","26785","26786","26787","26788","26789","26790","26791","26792","26793","26794","26795","26796","26797","26798","26799","26800","26801","26802","26803","26804","26805","26806","26807","26808","26809","26810","26811","26812","26813","26814","26815","26816","26817","26818","26819","26820","26821","26822","26823","26824","26825","26826","26827","26828","26829","26830","26831","26832","26833","26834","26835","26836","26837","26838","26839","26840","26841","26842","26843","26844","26845","26846","26847","26848","26849","26850","26851","26852","26853","26854","26855","26856","26857","26858","26859","26860","26861","26862","26863","26864","26865","26866","26867","26868","26869","26870","26871","26872","26873","26874","26875","26876","26877","26878","26879","26880","26881","26882","26883","26884","26885","26886","26887","26888","26889","26890","26891","26892","26893","26894","26895","26896","26897","26898","26899","26900","26901","26902","26903","26904","26905","26906","26907","26908","26909","26910","26911","26912","26913","26914","26915","26916","26917","26918","26919","26920","26921","26922","26923","26924","26925","26926","26927","26928","26929","26930","26931","26932","26933","26934","26935","26936","26937","26938","26939","26940","26941","26942","26943","26944","26945","26946","26947","26948","26949","26950","26951","26952","26953","26954","26955","26956","26957","26958","26959","26960","26961","26962","26963","26964","26965","26966","26967","26968","26969","26970","26971","26972","26973","26974","26975","26976","26977","26978","26979","26980","26981","26982","26983","26984","26985","26986","26987","26988","26989","26990","26991","26992","26993","26994","26995","26996","26997","26998","26999","27000","27001","27002","27003","27004","27005","27006","27007","27008","27009","27010","27011","27012","27013","27014","27015","27016","27017","27018","27019","27020","27021","27022","27023","27024","27025","27026","27027","27028","27029","27030","27031","27032","27033","27034","27035","27036","27037","27038","27039","27040","27041","27042","27043","27044","27045","27046","27047","27048","27049","27050","27051","27052","27053","27054","27055","27056","27057","27058","27059","27060","27061","27062","27063","27064","27065","27066","27067","27068","27069","27070","27071","27072","27073","27074","27075","27076","27077","27078","27079","27080","27081","27082","27083","27084","27085","27086","27087","27088","27089","27090","27091","27092","27093","27094","27095","27096","27097","27098","27099","27100","27101","27102","27103","27104","27105","27106","27107","27108","27109","27110","27111","27112","27113","27114","27115","27116","27117","27118","27119","27120","27121","27122","27123","27124","27125","27126","27127","27128","27129","27130","27131","27132","27133","27134","27135","27136","27137","27138","27139","27140","27141","27142","27143","27144","27145","27146","27147","27148","27149","27150","27151","27152","27153","27154","27155","27156","27157","27158","27159","27160","27161","27162","27163","27164","27165","27166","27167","27168","27169","27170","27171","27172","27173","27174","27175","27176","27177","27178","27179","27180","27181","27182","27183","27184","27185","27186","27187","27188","27189","27190","27191","27192","27193","27194","27195","27196","27197","27198","27199","27200","27201","27202","27203","27204","27205","27206","27207","27208","27209","27210","27211","27212","27213","27214","27215","27216","27217","27218","27219","27220","27221","27222","27223","27224","27225","27226","27227","27228","27229","27230","27231","27232","27233","27234","27235","27236","27237","27238","27239","27240","27241","27242","27243","27244","27245","27246","27247","27248","27249","27250","27251","27252","27253","27254","27255","27256","27257","27258","27259","27260","27261","27262","27263","27264","27265","27266","27267","27268","27269","27270","27271","27272","27273","27274","27275","27276","27277","27278","27279","27280","27281","27282","27283","27284","27285","27286","27287","27288","27289","27290","27291","27292","27293","27294","27295","27296","27297","27298","27299","27300","27301","27302","27303","27304","27305","27306","27307","27308","27309","27310","27311","27312","27313","27314","27315","27316","27317","27318","27319","27320","27321","27322","27323","27324","27325","27326","27327","27328","27329","27330","27331","27332","27333","27334","27335","27336","27337","27338","27339","27340","27341","27342","27343","27344","27345","27346","27347","27348","27349","27350","27351","27352","27353","27354","27355","27356","27357","27358","27359","27360","27361","27362","27363","27364","27365","27366","27367","27368","27369","27370","27371","27372","27373","27374","27375","27376","27377","27378","27379","27380","27381","27382","27383","27384","27385","27386","27387","27388","27389","27390","27391","27392","27393","27394","27395","27396","27397","27398","27399","27400","27401","27402","27403","27404","27405","27406","27407","27408","27409","27410","27411","27412","27413","27414","27415","27416","27417","27418","27419","27420","27421","27422","27423","27424","27425","27426","27427","27428","27429","27430","27431","27432","27433","27434","27435","27436","27437","27438","27439","27440","27441","27442","27443","27444","27445","27446","27447","27448","27449","27450","27451","27452","27453","27454","27455","27456","27457","27458","27459","27460","27461","27462","27463","27464","27465","27466","27467","27468","27469","27470","27471","27472","27473","27474","27475","27476","27477","27478","27479","27480","27481","27482","27483","27484","27485","27486","27487","27488","27489","27490","27491","27492","27493","27494","27495","27496","27497","27498","27499","27500","27501","27502","27503","27504","27505","27506","27507","27508","27509","27510","27511","27512","27513","27514","27515","27516","27517","27518","27519","27520","27521","27522","27523","27524","27525","27526","27527","27528","27529","27530","27531","27532","27533","27534","27535","27536","27537","27538","27539","27540","27541","27542","27543","27544","27545","27546","27547","27548","27549","27550","27551","27552","27553","27554","27555","27556","27557","27558","27559","27560","27561","27562","27563","27564","27565","27566","27567","27568","27569","27570","27571","27572","27573","27574","27575","27576","27577","27578","27579","27580","27581","27582","27583","27584","27585","27586","27587","27588","27589","27590","27591","27592","27593","27594","27595","27596","27597","27598","27599","27600","27601","27602","27603","27604","27605","27606","27607","27608","27609","27610","27611","27612","27613","27614","27615","27616","27617","27618","27619","27620","27621","27622","27623","27624","27625","27626","27627","27628","27629","27630","27631","27632","27633","27634","27635","27636","27637","27638","27639","27640","27641","27642","27643","27644","27645","27646","27647","27648","27649","27650","27651","27652","27653","27654","27655","27656","27657","27658","27659","27660","27661","27662","27663","27664","27665","27666","27667","27668","27669","27670","27671","27672","27673","27674","27675","27676","27677","27678","27679","27680","27681","27682","27683","27684","27685","27686","27687","27688","27689","27690","27691","27692","27693","27694","27695","27696","27697","27698","27699","27700","27701","27702","27703","27704","27705","27706","27707","27708","27709","27710","27711","27712","27713","27714","27715","27716","27717","27718","27719","27720","27721","27722","27723","27724","27725","27726","27727","27728","27729","27730","27731","27732","27733","27734","27735","27736","27737","27738","27739","27740","27741","27742","27743","27744","27745","27746","27747","27748","27749","27750","27751","27752","27753","27754","27755","27756","27757","27758","27759","27760","27761","27762","27763","27764","27765","27766","27767","27768","27769","27770","27771","27772","27773","27774","27775","27776","27777","27778","27779","27780","27781","27782","27783","27784","27785","27786","27787","27788","27789","27790","27791","27792","27793","27794","27795","27796","27797","27798","27799","27800","27801","27802","27803","27804","27805","27806","27807","27808","27809","27810","27811","27812","27813","27814","27815","27816","27817","27818","27819","27820","27821","27822","27823","27824","27825","27826","27827","27828","27829","27830","27831","27832","27833","27834","27835","27836","27837","27838","27839","27840","27841","27842","27843","27844","27845","27846","27847","27848","27849","27850","27851","27852","27853","27854","27855","27856","27857","27858","27859","27860","27861","27862","27863","27864","27865","27866","27867","27868","27869","27870","27871","27872","27873","27874","27875","27876","27877","27878","27879","27880","27881","27882","27883","27884","27885","27886","27887","27888","27889","27890","27891","27892","27893","27894","27895","27896","27897","27898","27899","27900","27901","27902","27903","27904","27905","27906","27907","27908","27909","27910","27911","27912","27913","27914","27915","27916","27917","27918","27919","27920","27921","27922","27923","27924","27925","27926","27927","27928","27929","27930","27931","27932","27933","27934","27935","27936","27937","27938","27939","27940","27941","27942","27943","27944","27945","27946","27947","27948","27949","27950","27951","27952","27953","27954","27955","27956","27957","27958","27959","27960","27961","27962","27963","27964","27965","27966","27967","27968","27969","27970","27971","27972","27973","27974","27975","27976","27977","27978","27979","27980","27981","27982","27983","27984","27985","27986","27987","27988","27989","27990","27991","27992","27993","27994","27995","27996","27997","27998","27999","28000","28001","28002","28003","28004","28005","28006","28007","28008","28009","28010","28011","28012","28013","28014","28015","28016","28017","28018","28019","28020","28021","28022","28023","28024","28025","28026","28027","28028","28029","28030","28031","28032","28033","28034","28035","28036","28037","28038","28039","28040","28041","28042","28043","28044","28045","28046","28047","28048","28049","28050","28051","28052","28053","28054","28055","28056","28057","28058","28059","28060","28061","28062","28063","28064","28065","28066","28067","28068","28069","28070","28071","28072","28073","28074","28075","28076","28077","28078","28079","28080","28081","28082","28083","28084","28085","28086","28087","28088","28089","28090","28091","28092","28093","28094","28095","28096","28097","28098","28099","28100","28101","28102","28103","28104","28105","28106","28107","28108","28109","28110","28111","28112","28113","28114","28115","28116","28117","28118","28119","28120","28121","28122","28123","28124","28125","28126","28127","28128","28129","28130","28131","28132","28133","28134","28135","28136","28137","28138","28139","28140","28141","28142","28143","28144","28145","28146","28147","28148","28149","28150","28151","28152","28153","28154","28155","28156","28157","28158","28159","28160","28161","28162","28163","28164","28165","28166","28167","28168","28169","28170","28171","28172","28173","28174","28175","28176","28177","28178","28179","28180","28181","28182","28183","28184","28185","28186","28187","28188","28189","28190","28191","28192","28193","28194","28195","28196","28197","28198","28199","28200","28201","28202","28203","28204","28205","28206","28207","28208","28209","28210","28211","28212","28213","28214","28215","28216","28217","28218","28219","28220","28221","28222","28223","28224","28225","28226","28227","28228","28229","28230","28231","28232","28233","28234","28235","28236","28237","28238","28239","28240","28241","28242","28243","28244","28245","28246","28247","28248","28249","28250","28251","28252","28253","28254","28255","28256","28257","28258","28259","28260","28261","28262","28263","28264","28265","28266","28267","28268","28269","28270","28271","28272","28273","28274","28275","28276","28277","28278","28279","28280","28281","28282","28283","28284","28285","28286","28287","28288","28289","28290","28291","28292","28293","28294","28295","28296","28297","28298","28299","28300","28301","28302","28303","28304","28305","28306","28307","28308","28309","28310","28311","28312","28313","28314","28315","28316","28317","28318","28319","28320","28321","28322","28323","28324","28325","28326","28327","28328","28329","28330","28331","28332","28333","28334","28335","28336","28337","28338","28339","28340","28341","28342","28343","28344","28345","28346","28347","28348","28349","28350","28351","28352","28353","28354","28355","28356","28357","28358","28359","28360","28361","28362","28363","28364","28365","28366","28367","28368","28369","28370","28371","28372","28373","28374","28375","28376","28377","28378","28379","28380","28381","28382","28383","28384","28385","28386","28387","28388","28389","28390","28391","28392","28393","28394","28395","28396","28397","28398","28399","28400","28401","28402","28403","28404","28405","28406","28407","28408","28409","28410","28411","28412","28413","28414","28415","28416","28417","28418","28419","28420","28421","28422","28423","28424","28425","28426","28427","28428","28429","28430","28431","28432","28433","28434","28435","28436","28437","28438","28439","28440","28441","28442","28443","28444","28445","28446","28447","28448","28449","28450","28451","28452","28453","28454","28455","28456","28457","28458","28459","28460","28461","28462","28463","28464","28465","28466","28467","28468","28469","28470","28471","28472","28473","28474","28475","28476","28477","28478","28479","28480","28481","28482","28483","28484","28485","28486","28487","28488","28489","28490","28491","28492","28493","28494","28495","28496","28497","28498","28499","28500","28501","28502","28503","28504","28505","28506","28507","28508","28509","28510","28511","28512","28513","28514","28515","28516","28517","28518","28519","28520","28521","28522","28523","28524","28525","28526","28527","28528","28529","28530","28531","28532","28533","28534","28535","28536","28537","28538","28539","28540","28541","28542","28543","28544","28545","28546","28547","28548","28549","28550","28551","28552","28553","28554","28555","28556","28557","28558","28559","28560","28561","28562","28563","28564","28565","28566","28567","28568","28569","28570","28571","28572","28573","28574","28575","28576","28577","28578","28579","28580","28581","28582","28583","28584","28585","28586","28587","28588","28589","28590","28591","28592","28593","28594","28595","28596","28597","28598","28599","28600","28601","28602","28603","28604","28605","28606","28607","28608","28609","28610","28611","28612","28613","28614","28615","28616","28617","28618","28619","28620","28621","28622","28623","28624","28625","28626","28627","28628","28629","28630","28631","28632","28633","28634","28635","28636","28637","28638","28639","28640","28641","28642","28643","28644","28645","28646","28647","28648","28649","28650","28651","28652","28653","28654","28655","28656","28657","28658","28659","28660","28661","28662","28663","28664","28665","28666","28667","28668","28669","28670","28671","28672","28673","28674","28675","28676","28677","28678","28679","28680","28681","28682","28683","28684","28685","28686","28687","28688","28689","28690","28691","28692","28693","28694","28695","28696","28697","28698","28699","28700","28701","28702","28703","28704","28705","28706","28707","28708","28709","28710","28711","28712","28713","28714","28715","28716","28717","28718","28719","28720","28721","28722","28723","28724","28725","28726","28727","28728","28729","28730","28731","28732","28733","28734","28735","28736","28737","28738","28739","28740","28741","28742","28743","28744","28745","28746","28747","28748","28749","28750","28751","28752","28753","28754","28755","28756","28757","28758","28759","28760","28761","28762","28763","28764","28765","28766","28767","28768","28769","28770","28771","28772","28773","28774","28775","28776","28777","28778","28779","28780","28781","28782","28783","28784","28785","28786","28787","28788","28789","28790","28791","28792","28793","28794","28795","28796","28797","28798","28799","28800","28801","28802","28803","28804","28805","28806","28807","28808","28809","28810","28811","28812","28813","28814","28815","28816","28817","28818","28819","28820","28821","28822","28823","28824","28825","28826","28827","28828","28829","28830","28831","28832","28833","28834","28835","28836","28837","28838","28839","28840","28841","28842","28843","28844","28845","28846","28847","28848","28849","28850","28851","28852","28853","28854","28855","28856","28857","28858","28859","28860","28861","28862","28863","28864","28865","28866","28867","28868","28869","28870","28871","28872","28873","28874","28875","28876","28877","28878","28879","28880","28881","28882","28883","28884","28885","28886","28887","28888","28889","28890","28891","28892","28893","28894","28895","28896","28897","28898","28899","28900","28901","28902","28903","28904","28905","28906","28907","28908","28909","28910","28911","28912","28913","28914","28915","28916","28917","28918","28919","28920","28921","28922","28923","28924","28925","28926","28927","28928","28929","28930","28931","28932","28933","28934","28935","28936","28937","28938","28939","28940","28941","28942","28943","28944","28945","28946","28947","28948","28949","28950","28951","28952","28953","28954","28955","28956","28957","28958","28959","28960","28961","28962","28963","28964","28965","28966","28967","28968","28969","28970","28971","28972","28973","28974","28975","28976","28977","28978","28979","28980","28981","28982","28983","28984","28985","28986","28987","28988","28989","28990","28991","28992","28993","28994","28995","28996","28997","28998","28999","29000","29001","29002","29003","29004","29005","29006","29007","29008","29009","29010","29011","29012","29013","29014","29015","29016","29017","29018","29019","29020","29021","29022","29023","29024","29025","29026","29027","29028","29029","29030","29031","29032","29033","29034","29035","29036","29037","29038","29039","29040","29041","29042","29043","29044","29045","29046","29047","29048","29049","29050","29051","29052","29053","29054","29055","29056","29057","29058","29059","29060","29061","29062","29063","29064","29065","29066","29067","29068","29069","29070","29071","29072","29073","29074","29075","29076","29077","29078","29079","29080","29081","29082","29083","29084","29085","29086","29087","29088","29089","29090","29091","29092","29093","29094","29095","29096","29097","29098","29099","29100","29101","29102","29103","29104","29105","29106","29107","29108","29109","29110","29111","29112","29113","29114","29115","29116","29117","29118","29119","29120","29121","29122","29123","29124","29125","29126","29127","29128","29129","29130","29131","29132","29133","29134","29135","29136","29137","29138","29139","29140","29141","29142","29143","29144","29145","29146","29147","29148","29149","29150","29151","29152","29153","29154","29155","29156","29157","29158","29159","29160","29161","29162","29163","29164","29165","29166","29167","29168","29169","29170","29171","29172","29173","29174","29175","29176","29177","29178","29179","29180","29181","29182","29183","29184","29185","29186","29187","29188","29189","29190","29191","29192","29193","29194","29195","29196","29197","29198","29199","29200","29201","29202","29203","29204","29205","29206","29207","29208","29209","29210","29211","29212","29213","29214","29215","29216","29217","29218","29219","29220","29221","29222","29223","29224","29225","29226","29227","29228","29229","29230","29231","29232","29233","29234","29235","29236","29237","29238","29239","29240","29241","29242","29243","29244","29245","29246","29247","29248","29249","29250","29251","29252","29253","29254","29255","29256","29257","29258","29259","29260","29261","29262","29263","29264","29265","29266","29267","29268","29269","29270","29271","29272","29273","29274","29275","29276","29277","29278","29279","29280","29281","29282","29283","29284","29285","29286","29287","29288","29289","29290","29291","29292","29293","29294","29295","29296","29297","29298","29299","29300","29301","29302","29303","29304","29305","29306","29307","29308","29309","29310","29311","29312","29313","29314","29315","29316","29317","29318","29319","29320","29321","29322","29323","29324","29325","29326","29327","29328","29329","29330","29331","29332","29333","29334","29335","29336","29337","29338","29339","29340","29341","29342","29343","29344","29345","29346","29347","29348","29349","29350","29351","29352","29353","29354","29355","29356","29357","29358","29359","29360","29361","29362","29363","29364","29365","29366","29367","29368","29369","29370","29371","29372","29373","29374","29375","29376","29377","29378","29379","29380","29381","29382","29383","29384","29385","29386","29387","29388","29389","29390","29391","29392","29393","29394","29395","29396","29397","29398","29399","29400","29401","29402","29403","29404","29405","29406","29407","29408","29409","29410","29411","29412","29413","29414","29415","29416","29417","29418","29419","29420","29421","29422","29423","29424","29425","29426","29427","29428","29429","29430","29431","29432","29433","29434","29435","29436","29437","29438","29439","29440","29441","29442","29443","29444","29445","29446","29447","29448","29449","29450","29451","29452","29453","29454","29455","29456","29457","29458","29459","29460","29461","29462","29463","29464","29465","29466","29467","29468","29469","29470","29471","29472","29473","29474","29475","29476","29477","29478","29479","29480","29481","29482","29483","29484","29485","29486","29487","29488","29489","29490","29491","29492","29493","29494","29495","29496","29497","29498","29499","29500","29501","29502","29503","29504","29505","29506","29507","29508","29509","29510","29511","29512","29513","29514","29515","29516","29517","29518","29519","29520","29521","29522","29523","29524","29525","29526","29527","29528","29529","29530","29531","29532","29533","29534","29535","29536","29537","29538","29539","29540","29541","29542","29543","29544","29545","29546","29547","29548","29549","29550","29551","29552","29553","29554","29555","29556","29557","29558","29559","29560","29561","29562","29563","29564","29565","29566","29567","29568","29569","29570","29571","29572","29573","29574","29575","29576","29577","29578","29579","29580","29581","29582","29583","29584","29585","29586","29587","29588","29589","29590","29591","29592","29593","29594","29595","29596","29597","29598","29599","29600","29601","29602","29603","29604","29605","29606","29607","29608","29609","29610","29611","29612","29613","29614","29615","29616","29617","29618","29619","29620","29621","29622","29623","29624","29625","29626","29627","29628","29629","29630","29631","29632","29633","29634","29635","29636","29637","29638","29639","29640","29641","29642","29643","29644","29645","29646","29647","29648","29649","29650","29651","29652","29653","29654","29655","29656","29657","29658","29659","29660","29661","29662","29663","29664","29665","29666","29667","29668","29669","29670","29671","29672","29673","29674","29675","29676","29677","29678","29679","29680","29681","29682","29683","29684","29685","29686","29687","29688","29689","29690","29691","29692","29693","29694","29695","29696","29697","29698","29699","29700","29701","29702","29703","29704","29705","29706","29707","29708","29709","29710","29711","29712","29713","29714","29715","29716","29717","29718","29719","29720","29721","29722","29723","29724","29725","29726","29727","29728","29729","29730","29731","29732","29733","29734","29735","29736","29737","29738","29739","29740","29741","29742","29743","29744","29745","29746","29747","29748","29749","29750","29751","29752","29753","29754","29755","29756","29757","29758","29759","29760","29761","29762","29763","29764","29765","29766","29767","29768","29769","29770","29771","29772","29773","29774","29775","29776","29777","29778","29779","29780","29781","29782","29783","29784","29785","29786","29787","29788","29789","29790","29791","29792","29793","29794","29795","29796","29797","29798","29799","29800","29801","29802","29803","29804","29805","29806","29807","29808","29809","29810","29811","29812","29813","29814","29815","29816","29817","29818","29819","29820","29821","29822","29823","29824","29825","29826","29827","29828","29829","29830","29831","29832","29833","29834","29835","29836","29837","29838","29839","29840","29841","29842","29843","29844","29845","29846","29847","29848","29849","29850","29851","29852","29853","29854","29855","29856","29857","29858","29859","29860","29861","29862","29863","29864","29865","29866","29867","29868","29869","29870","29871","29872","29873","29874","29875","29876","29877","29878","29879","29880","29881","29882","29883","29884","29885","29886","29887","29888","29889","29890","29891","29892","29893","29894","29895","29896","29897","29898","29899","29900","29901","29902","29903","29904","29905","29906","29907","29908","29909","29910","29911","29912","29913","29914","29915","29916","29917","29918","29919","29920","29921","29922","29923","29924","29925","29926","29927","29928","29929","29930","29931","29932","29933","29934","29935","29936","29937","29938","29939","29940","29941","29942","29943","29944","29945","29946","29947","29948","29949","29950","29951","29952","29953","29954","29955","29956","29957","29958","29959","29960","29961","29962","29963","29964","29965","29966","29967","29968","29969","29970","29971","29972","29973","29974","29975","29976","29977","29978","29979","29980","29981","29982","29983","29984","29985","29986","29987","29988","29989","29990","29991","29992","29993","29994","29995","29996","29997","29998","29999","30000","30001","30002","30003","30004","30005","30006","30007","30008","30009","30010","30011","30012","30013","30014","30015","30016","30017","30018","30019","30020","30021","30022","30023","30024","30025","30026","30027","30028","30029","30030","30031","30032","30033","30034","30035","30036","30037","30038","30039","30040","30041","30042","30043","30044","30045","30046","30047","30048","30049","30050","30051","30052","30053","30054","30055","30056","30057","30058","30059","30060","30061","30062","30063","30064","30065","30066","30067","30068","30069","30070","30071","30072","30073","30074","30075","30076","30077","30078","30079","30080","30081","30082","30083","30084","30085","30086","30087","30088","30089","30090","30091","30092","30093","30094","30095","30096","30097","30098","30099","30100","30101","30102","30103","30104","30105","30106","30107","30108","30109","30110","30111","30112","30113","30114","30115","30116","30117","30118","30119","30120","30121","30122","30123","30124","30125","30126","30127","30128","30129","30130","30131","30132","30133","30134","30135","30136","30137","30138","30139","30140","30141","30142","30143","30144","30145","30146","30147","30148","30149","30150","30151","30152","30153","30154","30155","30156","30157","30158","30159","30160","30161","30162","30163","30164","30165","30166","30167","30168","30169","30170","30171","30172","30173","30174","30175","30176","30177","30178","30179","30180","30181","30182","30183","30184","30185","30186","30187","30188","30189","30190","30191","30192","30193","30194","30195","30196","30197","30198","30199","30200","30201","30202","30203","30204","30205","30206","30207","30208","30209","30210","30211","30212","30213","30214","30215","30216","30217","30218","30219","30220","30221","30222","30223","30224","30225","30226","30227","30228","30229","30230","30231","30232","30233","30234","30235","30236","30237","30238","30239","30240","30241","30242","30243","30244","30245","30246","30247","30248","30249","30250","30251","30252","30253","30254","30255","30256","30257","30258","30259","30260","30261","30262","30263","30264","30265","30266","30267","30268","30269","30270","30271","30272","30273","30274","30275","30276","30277","30278","30279","30280","30281","30282","30283","30284","30285","30286","30287","30288","30289","30290","30291","30292","30293","30294","30295","30296","30297","30298","30299","30300","30301","30302","30303","30304","30305","30306","30307","30308","30309","30310","30311","30312","30313","30314","30315","30316","30317","30318","30319","30320","30321","30322","30323","30324","30325","30326","30327","30328","30329","30330","30331","30332","30333","30334","30335","30336","30337","30338","30339","30340","30341","30342","30343","30344","30345","30346","30347","30348","30349","30350","30351","30352","30353","30354","30355","30356","30357","30358","30359","30360","30361","30362","30363","30364","30365","30366","30367","30368","30369","30370","30371","30372","30373","30374","30375","30376","30377","30378","30379","30380","30381","30382","30383","30384","30385","30386","30387","30388","30389","30390","30391","30392","30393","30394","30395","30396","30397","30398","30399","30400","30401","30402","30403","30404","30405","30406","30407","30408","30409","30410","30411","30412","30413","30414","30415","30416","30417","30418","30419","30420","30421","30422","30423","30424","30425","30426","30427","30428","30429","30430","30431","30432","30433","30434","30435","30436","30437","30438","30439","30440","30441","30442","30443","30444","30445","30446","30447","30448","30449","30450","30451","30452","30453","30454","30455","30456","30457","30458","30459","30460","30461","30462","30463","30464","30465","30466","30467","30468","30469","30470","30471","30472","30473","30474","30475","30476","30477","30478","30479","30480","30481","30482","30483","30484","30485","30486","30487","30488","30489","30490","30491","30492","30493","30494","30495","30496","30497","30498","30499","30500","30501","30502","30503","30504","30505","30506","30507","30508","30509","30510","30511","30512","30513","30514","30515","30516","30517","30518","30519","30520","30521","30522","30523","30524","30525","30526","30527","30528","30529","30530","30531","30532","30533","30534","30535","30536","30537","30538","30539","30540","30541","30542","30543","30544","30545","30546","30547","30548","30549","30550","30551","30552","30553","30554","30555","30556","30557","30558","30559","30560","30561","30562","30563","30564","30565","30566","30567","30568","30569","30570","30571","30572","30573","30574","30575","30576","30577","30578","30579","30580","30581","30582","30583","30584","30585","30586","30587","30588","30589","30590","30591","30592","30593","30594","30595","30596","30597","30598","30599","30600","30601","30602","30603","30604","30605","30606","30607","30608","30609","30610","30611","30612","30613","30614","30615","30616","30617","30618","30619","30620","30621","30622","30623","30624","30625","30626","30627","30628","30629","30630","30631","30632","30633","30634","30635","30636","30637","30638","30639","30640","30641","30642","30643","30644","30645","30646","30647","30648","30649","30650","30651","30652","30653","30654","30655","30656","30657","30658","30659","30660","30661","30662","30663","30664","30665","30666","30667","30668","30669","30670","30671","30672","30673","30674","30675","30676","30677","30678","30679","30680","30681","30682","30683","30684","30685","30686","30687","30688","30689","30690","30691","30692","30693","30694","30695","30696","30697","30698","30699","30700","30701","30702","30703","30704","30705","30706","30707","30708","30709","30710","30711","30712","30713","30714","30715","30716","30717","30718","30719","30720","30721","30722","30723","30724","30725","30726","30727","30728","30729","30730","30731","30732","30733","30734","30735","30736","30737","30738","30739","30740","30741","30742","30743","30744","30745","30746","30747","30748","30749","30750","30751","30752","30753","30754","30755","30756","30757","30758","30759","30760","30761","30762","30763","30764","30765","30766","30767","30768","30769","30770","30771","30772","30773","30774","30775","30776","30777","30778","30779","30780","30781","30782","30783","30784","30785","30786","30787","30788","30789","30790","30791","30792","30793","30794","30795","30796","30797","30798","30799","30800","30801","30802","30803","30804","30805","30806","30807","30808","30809","30810","30811","30812","30813","30814","30815","30816","30817","30818","30819","30820","30821","30822","30823","30824","30825","30826","30827","30828","30829","30830","30831","30832","30833","30834","30835","30836","30837","30838","30839","30840","30841","30842","30843","30844","30845","30846","30847","30848","30849","30850","30851","30852","30853","30854","30855","30856","30857","30858","30859","30860","30861","30862","30863","30864","30865","30866","30867","30868","30869","30870","30871","30872","30873","30874","30875","30876","30877","30878","30879","30880","30881","30882","30883","30884","30885","30886","30887","30888","30889","30890","30891","30892","30893","30894","30895","30896","30897","30898","30899","30900","30901","30902","30903","30904","30905","30906","30907","30908","30909","30910","30911","30912","30913","30914","30915","30916","30917","30918","30919","30920","30921","30922","30923","30924","30925","30926","30927","30928","30929","30930","30931","30932","30933","30934","30935","30936","30937","30938","30939","30940","30941","30942","30943","30944","30945","30946","30947","30948","30949","30950","30951","30952","30953","30954","30955","30956","30957","30958","30959","30960","30961","30962","30963","30964","30965","30966","30967","30968","30969","30970","30971","30972","30973","30974","30975","30976","30977","30978","30979","30980","30981","30982","30983","30984","30985","30986","30987","30988","30989","30990","30991","30992","30993","30994","30995","30996","30997","30998","30999","31000","31001","31002","31003","31004","31005","31006","31007","31008","31009","31010","31011","31012","31013","31014","31015","31016","31017","31018","31019","31020","31021","31022","31023","31024","31025","31026","31027","31028","31029","31030","31031","31032","31033","31034","31035","31036","31037","31038","31039","31040","31041","31042","31043","31044","31045","31046","31047","31048","31049","31050","31051","31052","31053","31054","31055","31056","31057","31058","31059","31060","31061","31062","31063","31064","31065","31066","31067","31068","31069","31070","31071","31072","31073","31074","31075","31076","31077","31078","31079","31080","31081","31082","31083","31084","31085","31086","31087","31088","31089","31090","31091","31092","31093","31094","31095","31096","31097","31098","31099","31100","31101","31102","31103","31104","31105","31106","31107","31108","31109","31110","31111","31112","31113","31114","31115","31116","31117","31118","31119","31120","31121","31122","31123","31124","31125","31126","31127","31128","31129","31130","31131","31132","31133","31134","31135","31136","31137","31138","31139","31140","31141","31142","31143","31144","31145","31146","31147","31148","31149","31150","31151","31152","31153","31154","31155","31156","31157","31158","31159","31160","31161","31162","31163","31164","31165","31166","31167","31168","31169","31170","31171","31172","31173","31174","31175","31176","31177","31178","31179","31180","31181","31182","31183","31184","31185","31186","31187","31188","31189","31190","31191","31192","31193","31194","31195","31196","31197","31198","31199","31200","31201","31202","31203","31204","31205","31206","31207","31208","31209","31210","31211","31212","31213","31214","31215","31216","31217","31218","31219","31220","31221","31222","31223","31224","31225","31226","31227","31228","31229","31230","31231","31232","31233","31234","31235","31236","31237","31238","31239","31240","31241","31242","31243","31244","31245","31246","31247","31248","31249","31250","31251","31252","31253","31254","31255","31256","31257","31258","31259","31260","31261","31262","31263","31264","31265","31266","31267","31268","31269","31270","31271","31272","31273","31274","31275","31276","31277","31278","31279","31280","31281","31282","31283","31284","31285","31286","31287","31288","31289","31290","31291","31292","31293","31294","31295","31296","31297","31298","31299","31300","31301","31302","31303","31304","31305","31306","31307","31308","31309","31310","31311","31312","31313","31314","31315","31316","31317","31318","31319","31320","31321","31322","31323","31324","31325","31326","31327","31328","31329","31330","31331","31332","31333","31334","31335","31336","31337","31338","31339","31340","31341","31342","31343","31344","31345","31346","31347","31348","31349","31350","31351","31352","31353","31354","31355","31356","31357","31358","31359","31360","31361","31362","31363","31364","31365","31366","31367","31368","31369","31370","31371","31372","31373","31374","31375","31376","31377","31378","31379","31380","31381","31382","31383","31384","31385","31386","31387","31388","31389","31390","31391","31392","31393","31394","31395","31396","31397","31398","31399","31400","31401","31402","31403","31404","31405","31406","31407","31408","31409","31410","31411","31412","31413","31414","31415","31416","31417","31418","31419","31420","31421","31422","31423","31424","31425","31426","31427","31428","31429","31430","31431","31432","31433","31434","31435","31436","31437","31438","31439","31440","31441","31442","31443","31444","31445","31446","31447","31448","31449","31450","31451","31452","31453","31454","31455","31456","31457","31458","31459","31460","31461","31462","31463","31464","31465","31466","31467","31468","31469","31470","31471","31472","31473","31474","31475","31476","31477","31478","31479","31480","31481","31482","31483","31484","31485","31486","31487","31488","31489","31490","31491","31492","31493","31494","31495","31496","31497","31498","31499","31500","31501","31502","31503","31504","31505","31506","31507","31508","31509","31510","31511","31512","31513","31514","31515","31516","31517","31518","31519","31520","31521","31522","31523","31524","31525","31526","31527","31528","31529","31530","31531","31532","31533","31534","31535","31536","31537","31538","31539","31540","31541","31542","31543","31544","31545","31546","31547","31548","31549","31550","31551","31552","31553","31554","31555","31556","31557","31558","31559","31560","31561","31562","31563","31564","31565","31566","31567","31568","31569","31570","31571","31572","31573","31574","31575","31576","31577","31578","31579","31580","31581","31582","31583","31584","31585","31586","31587","31588","31589","31590","31591","31592","31593","31594","31595","31596","31597","31598","31599","31600","31601","31602","31603","31604","31605","31606","31607","31608","31609","31610","31611","31612","31613","31614","31615","31616","31617","31618","31619","31620","31621","31622","31623","31624","31625","31626","31627","31628","31629","31630","31631","31632","31633","31634","31635","31636","31637","31638","31639","31640","31641","31642","31643","31644","31645","31646","31647","31648","31649","31650","31651","31652","31653","31654","31655","31656","31657","31658","31659","31660","31661","31662","31663","31664","31665","31666","31667","31668","31669","31670","31671","31672","31673","31674","31675","31676","31677","31678","31679","31680","31681","31682","31683","31684","31685","31686","31687","31688","31689","31690","31691","31692","31693","31694","31695","31696","31697","31698","31699","31700","31701","31702","31703","31704","31705","31706","31707","31708","31709","31710","31711","31712","31713","31714","31715","31716","31717","31718","31719","31720","31721","31722","31723","31724","31725","31726","31727","31728","31729","31730","31731","31732","31733","31734","31735","31736","31737","31738","31739","31740","31741","31742","31743","31744","31745","31746","31747","31748","31749","31750","31751","31752","31753","31754","31755","31756","31757","31758","31759","31760","31761","31762","31763","31764","31765","31766","31767","31768","31769","31770","31771","31772","31773","31774","31775","31776","31777","31778","31779","31780","31781","31782","31783","31784","31785","31786","31787","31788","31789","31790","31791","31792","31793","31794","31795","31796","31797","31798","31799","31800","31801","31802","31803","31804","31805","31806","31807","31808","31809","31810","31811","31812","31813","31814","31815","31816","31817","31818","31819","31820","31821","31822","31823","31824","31825","31826","31827","31828","31829","31830","31831","31832","31833","31834","31835","31836","31837","31838","31839","31840","31841","31842","31843","31844","31845","31846","31847","31848","31849","31850","31851","31852","31853","31854","31855","31856","31857","31858","31859","31860","31861","31862","31863","31864","31865","31866","31867","31868","31869","31870","31871","31872","31873","31874","31875","31876","31877","31878","31879","31880","31881","31882","31883","31884","31885","31886","31887","31888","31889","31890","31891","31892","31893","31894","31895","31896","31897","31898","31899","31900","31901","31902","31903","31904","31905","31906","31907","31908","31909","31910","31911","31912","31913","31914","31915","31916","31917","31918","31919","31920","31921","31922","31923","31924","31925","31926","31927","31928","31929","31930","31931","31932","31933","31934","31935","31936","31937","31938","31939","31940","31941","31942","31943","31944","31945","31946","31947","31948","31949","31950","31951","31952","31953","31954","31955","31956","31957","31958","31959","31960","31961","31962","31963","31964","31965","31966","31967","31968","31969","31970","31971","31972","31973","31974","31975","31976","31977","31978","31979","31980","31981","31982","31983","31984","31985","31986","31987","31988","31989","31990","31991","31992","31993","31994","31995","31996","31997","31998","31999","32000","32001","32002","32003","32004","32005","32006","32007","32008","32009","32010","32011","32012","32013","32014","32015","32016","32017","32018","32019","32020","32021","32022","32023","32024","32025","32026","32027","32028","32029","32030","32031","32032","32033","32034","32035","32036","32037","32038","32039","32040","32041","32042","32043","32044","32045","32046","32047","32048","32049","32050","32051","32052","32053","32054","32055","32056","32057","32058","32059","32060","32061","32062","32063","32064","32065","32066","32067","32068","32069","32070","32071","32072","32073","32074","32075","32076","32077","32078","32079","32080","32081","32082","32083","32084","32085","32086","32087","32088","32089","32090","32091","32092","32093","32094","32095","32096","32097","32098","32099","32100","32101","32102","32103","32104","32105","32106","32107","32108","32109","32110","32111","32112","32113","32114","32115","32116","32117","32118","32119","32120","32121","32122","32123","32124","32125","32126","32127","32128","32129","32130","32131","32132","32133","32134","32135","32136","32137","32138","32139","32140","32141","32142","32143","32144","32145","32146","32147","32148","32149","32150","32151","32152","32153","32154","32155","32156","32157","32158","32159","32160","32161","32162","32163","32164","32165","32166","32167","32168","32169","32170","32171","32172","32173","32174","32175","32176","32177","32178","32179","32180","32181","32182","32183","32184","32185","32186","32187","32188","32189","32190","32191","32192","32193","32194","32195","32196","32197","32198","32199","32200","32201","32202","32203","32204","32205","32206","32207","32208","32209","32210","32211","32212","32213","32214","32215","32216","32217","32218","32219","32220","32221","32222","32223","32224","32225","32226","32227","32228","32229","32230","32231","32232","32233","32234","32235","32236","32237","32238","32239","32240","32241","32242","32243","32244","32245","32246","32247","32248","32249","32250","32251","32252","32253","32254","32255","32256","32257","32258","32259","32260","32261","32262","32263","32264","32265","32266","32267","32268","32269","32270","32271","32272","32273","32274","32275","32276","32277","32278","32279","32280","32281","32282","32283","32284","32285","32286","32287","32288","32289","32290","32291","32292","32293","32294","32295","32296","32297","32298","32299","32300","32301","32302","32303","32304","32305","32306","32307","32308","32309","32310","32311","32312","32313","32314","32315","32316","32317","32318","32319","32320","32321","32322","32323","32324","32325","32326","32327","32328","32329","32330","32331","32332","32333","32334","32335","32336","32337","32338","32339","32340","32341","32342","32343","32344","32345","32346","32347","32348","32349","32350","32351","32352","32353","32354","32355","32356","32357","32358","32359","32360","32361","32362","32363","32364","32365","32366","32367","32368","32369","32370","32371","32372","32373","32374","32375","32376","32377","32378","32379","32380","32381","32382","32383","32384","32385","32386","32387","32388","32389","32390","32391","32392","32393","32394","32395","32396","32397","32398","32399","32400","32401","32402","32403","32404","32405","32406","32407","32408","32409","32410","32411","32412","32413","32414","32415","32416","32417","32418","32419","32420","32421","32422","32423","32424","32425","32426","32427","32428","32429","32430","32431","32432","32433","32434","32435","32436","32437","32438","32439","32440","32441","32442","32443","32444","32445","32446","32447","32448","32449","32450","32451","32452","32453","32454","32455","32456","32457","32458","32459","32460","32461","32462","32463","32464","32465","32466","32467","32468","32469","32470","32471","32472","32473","32474","32475","32476","32477","32478","32479","32480","32481","32482","32483","32484","32485","32486","32487","32488","32489","32490","32491","32492","32493","32494","32495","32496","32497","32498","32499","32500","32501","32502","32503","32504","32505","32506","32507","32508","32509","32510","32511","32512","32513","32514","32515","32516","32517","32518","32519","32520","32521","32522","32523","32524","32525","32526","32527","32528","32529","32530","32531","32532","32533","32534","32535","32536","32537","32538","32539","32540","32541","32542","32543","32544","32545","32546","32547","32548","32549","32550","32551","32552","32553","32554","32555","32556","32557","32558","32559","32560","32561","32562","32563","32564","32565","32566","32567","32568","32569","32570","32571","32572","32573","32574","32575","32576","32577","32578","32579","32580","32581","32582","32583","32584","32585","32586","32587","32588","32589","32590","32591","32592","32593","32594","32595","32596","32597","32598","32599","32600","32601","32602","32603","32604","32605","32606","32607","32608","32609","32610","32611","32612","32613","32614","32615","32616","32617","32618","32619","32620","32621","32622","32623","32624","32625","32626","32627","32628","32629","32630","32631","32632","32633","32634","32635","32636","32637","32638","32639","32640","32641","32642","32643","32644","32645","32646","32647","32648","32649","32650","32651","32652","32653","32654","32655","32656","32657","32658","32659","32660","32661","32662","32663","32664","32665","32666","32667","32668","32669","32670","32671","32672","32673","32674","32675","32676","32677","32678","32679","32680","32681","32682","32683","32684","32685","32686","32687","32688","32689","32690","32691","32692","32693","32694","32695","32696","32697","32698","32699","32700","32701","32702","32703","32704","32705","32706","32707","32708","32709","32710","32711","32712","32713","32714","32715","32716","32717","32718","32719","32720","32721","32722","32723","32724","32725","32726","32727","32728","32729","32730","32731","32732","32733","32734","32735","32736","32737","32738","32739","32740","32741","32742","32743","32744","32745","32746","32747","32748","32749","32750","32751","32752","32753","32754","32755","32756","32757","32758","32759","32760","32761","32762","32763","32764","32765","32766","32767","32768","32769","32770","32771","32772","32773","32774","32775","32776","32777","32778","32779","32780","32781","32782","32783","32784","32785","32786","32787","32788","32789","32790","32791","32792","32793","32794","32795","32796","32797","32798","32799","32800","32801","32802","32803","32804","32805","32806","32807","32808","32809","32810","32811","32812","32813","32814","32815","32816","32817","32818","32819","32820","32821","32822","32823","32824","32825","32826","32827","32828","32829","32830","32831","32832","32833","32834","32835","32836","32837","32838","32839","32840","32841","32842","32843","32844","32845","32846","32847","32848","32849","32850","32851","32852","32853","32854","32855","32856","32857","32858","32859","32860","32861","32862","32863","32864","32865","32866","32867","32868","32869","32870","32871","32872","32873","32874","32875","32876","32877","32878","32879","32880","32881","32882","32883","32884","32885","32886","32887","32888","32889","32890","32891","32892","32893","32894","32895","32896","32897","32898","32899","32900","32901","32902","32903","32904","32905","32906","32907","32908","32909","32910","32911","32912","32913","32914","32915","32916","32917","32918","32919","32920","32921","32922","32923","32924","32925","32926","32927","32928","32929","32930","32931","32932","32933","32934","32935","32936","32937","32938","32939","32940","32941","32942","32943","32944","32945","32946","32947","32948","32949","32950","32951","32952","32953","32954","32955","32956","32957","32958","32959","32960","32961","32962","32963","32964","32965","32966","32967","32968","32969","32970","32971","32972","32973","32974","32975","32976","32977","32978","32979","32980","32981","32982","32983","32984","32985","32986","32987","32988","32989","32990","32991","32992","32993","32994","32995","32996","32997","32998","32999","33000","33001","33002","33003","33004","33005","33006","33007","33008","33009","33010","33011","33012","33013","33014","33015","33016","33017","33018","33019","33020","33021","33022","33023","33024","33025","33026","33027","33028","33029","33030","33031","33032","33033","33034","33035","33036","33037","33038","33039","33040","33041","33042","33043","33044","33045","33046","33047","33048","33049","33050","33051","33052","33053","33054","33055","33056","33057","33058","33059","33060","33061","33062","33063","33064","33065","33066","33067","33068","33069","33070","33071","33072","33073","33074","33075","33076","33077","33078","33079","33080","33081","33082","33083","33084","33085","33086","33087","33088","33089","33090","33091","33092","33093","33094","33095","33096","33097","33098","33099","33100","33101","33102","33103","33104","33105","33106","33107","33108","33109","33110","33111","33112","33113","33114","33115","33116","33117","33118","33119","33120","33121","33122","33123","33124","33125","33126","33127","33128","33129","33130","33131","33132","33133","33134","33135","33136","33137","33138","33139","33140","33141","33142","33143","33144","33145","33146","33147","33148","33149","33150","33151","33152","33153","33154","33155","33156","33157","33158","33159","33160","33161","33162","33163","33164","33165","33166","33167","33168","33169","33170","33171","33172","33173","33174","33175","33176","33177","33178","33179","33180","33181","33182","33183","33184","33185","33186","33187","33188","33189","33190","33191","33192","33193","33194","33195","33196","33197","33198","33199","33200","33201","33202","33203","33204","33205","33206","33207","33208","33209","33210","33211","33212","33213","33214","33215","33216","33217","33218","33219","33220","33221","33222","33223","33224","33225","33226","33227","33228","33229","33230","33231","33232","33233","33234","33235","33236","33237","33238","33239","33240","33241","33242","33243","33244","33245","33246","33247","33248","33249","33250","33251","33252","33253","33254","33255","33256","33257","33258","33259","33260","33261","33262","33263","33264","33265","33266","33267","33268","33269","33270","33271","33272","33273","33274","33275","33276","33277","33278","33279","33280","33281","33282","33283","33284","33285","33286","33287","33288","33289","33290","33291","33292","33293","33294","33295","33296","33297","33298","33299","33300","33301","33302","33303","33304","33305","33306","33307","33308","33309","33310","33311","33312","33313","33314","33315","33316","33317","33318","33319","33320","33321","33322","33323","33324","33325","33326","33327","33328","33329","33330","33331","33332","33333","33334","33335","33336","33337","33338","33339","33340","33341","33342","33343","33344","33345","33346","33347","33348","33349","33350","33351","33352","33353","33354","33355","33356","33357","33358","33359","33360","33361","33362","33363","33364","33365","33366","33367","33368","33369","33370","33371","33372","33373","33374","33375","33376","33377","33378","33379","33380","33381","33382","33383","33384","33385","33386","33387","33388","33389","33390","33391","33392","33393","33394","33395","33396","33397","33398","33399","33400","33401","33402","33403","33404","33405","33406","33407","33408","33409","33410","33411","33412","33413","33414","33415","33416","33417","33418","33419","33420","33421","33422","33423","33424","33425","33426","33427","33428","33429","33430","33431","33432","33433","33434","33435","33436","33437","33438","33439","33440","33441","33442","33443","33444","33445","33446","33447","33448","33449","33450","33451","33452","33453","33454","33455","33456","33457","33458","33459","33460","33461","33462","33463","33464","33465","33466","33467","33468","33469","33470","33471","33472","33473","33474","33475","33476","33477","33478","33479","33480","33481","33482","33483","33484","33485","33486","33487","33488","33489","33490","33491","33492","33493","33494","33495","33496","33497","33498","33499","33500","33501","33502","33503","33504","33505","33506","33507","33508","33509","33510","33511","33512","33513","33514","33515","33516","33517","33518","33519","33520","33521","33522","33523","33524","33525","33526","33527","33528","33529","33530","33531","33532","33533","33534","33535","33536","33537","33538","33539","33540","33541","33542","33543","33544","33545","33546","33547","33548","33549","33550","33551","33552","33553","33554","33555","33556","33557","33558","33559","33560","33561","33562","33563","33564","33565","33566","33567","33568","33569","33570","33571","33572","33573","33574","33575","33576","33577","33578","33579","33580","33581","33582","33583","33584","33585","33586","33587","33588","33589","33590","33591","33592","33593","33594","33595","33596","33597","33598","33599","33600","33601","33602","33603","33604","33605","33606","33607","33608","33609","33610","33611","33612","33613","33614","33615","33616","33617","33618","33619","33620","33621","33622","33623","33624","33625","33626","33627","33628","33629","33630","33631","33632","33633","33634","33635","33636","33637","33638","33639","33640","33641","33642","33643","33644","33645","33646","33647","33648","33649","33650","33651","33652","33653","33654","33655","33656","33657","33658","33659","33660","33661","33662","33663","33664","33665","33666","33667","33668","33669","33670","33671","33672","33673","33674","33675","33676","33677","33678","33679","33680","33681","33682","33683","33684","33685","33686","33687","33688","33689","33690","33691","33692","33693","33694","33695","33696","33697","33698","33699","33700","33701","33702","33703","33704","33705","33706","33707","33708","33709","33710","33711","33712","33713","33714","33715","33716","33717","33718","33719","33720","33721","33722","33723","33724","33725","33726","33727","33728","33729","33730","33731","33732","33733","33734","33735","33736","33737","33738","33739","33740","33741","33742","33743","33744","33745","33746","33747","33748","33749","33750","33751","33752","33753","33754","33755","33756","33757","33758","33759","33760","33761","33762","33763","33764","33765","33766","33767","33768","33769","33770","33771","33772","33773","33774","33775","33776","33777","33778","33779","33780","33781","33782","33783","33784","33785","33786","33787","33788","33789","33790","33791","33792","33793","33794","33795","33796","33797","33798","33799","33800","33801","33802","33803","33804","33805","33806","33807","33808","33809","33810","33811","33812","33813","33814","33815","33816","33817","33818","33819","33820","33821","33822","33823","33824","33825","33826","33827","33828","33829","33830","33831","33832","33833","33834","33835","33836","33837","33838","33839","33840","33841","33842","33843","33844","33845","33846","33847","33848","33849","33850","33851","33852","33853","33854","33855","33856","33857","33858","33859","33860","33861","33862","33863","33864","33865","33866","33867","33868","33869","33870","33871","33872","33873","33874","33875","33876","33877","33878","33879","33880","33881","33882","33883","33884","33885","33886","33887","33888","33889","33890","33891","33892","33893","33894","33895","33896","33897","33898","33899","33900","33901","33902","33903","33904","33905","33906","33907","33908","33909","33910","33911","33912","33913","33914","33915","33916","33917","33918","33919","33920","33921","33922","33923","33924","33925","33926","33927","33928","33929","33930","33931","33932","33933","33934","33935","33936","33937","33938","33939","33940","33941","33942","33943","33944","33945","33946","33947","33948","33949","33950","33951","33952","33953","33954","33955","33956","33957","33958","33959","33960","33961","33962","33963","33964","33965","33966","33967","33968","33969","33970","33971","33972","33973","33974","33975","33976","33977","33978","33979","33980","33981","33982","33983","33984","33985","33986","33987","33988","33989","33990","33991","33992","33993","33994","33995","33996","33997","33998","33999","34000","34001","34002","34003","34004","34005","34006","34007","34008","34009","34010","34011","34012","34013","34014","34015","34016","34017","34018","34019","34020","34021","34022","34023","34024","34025","34026","34027","34028","34029","34030","34031","34032","34033","34034","34035","34036","34037","34038","34039","34040","34041","34042","34043","34044","34045","34046","34047","34048","34049","34050","34051","34052","34053","34054","34055","34056","34057","34058","34059","34060","34061","34062","34063","34064","34065","34066","34067","34068","34069","34070","34071","34072","34073","34074","34075","34076","34077","34078","34079","34080","34081","34082","34083","34084","34085","34086","34087","34088","34089","34090","34091","34092","34093","34094","34095","34096","34097","34098","34099","34100","34101","34102","34103","34104","34105","34106","34107","34108","34109","34110","34111","34112","34113","34114","34115","34116","34117","34118","34119","34120","34121","34122","34123","34124","34125","34126","34127","34128","34129","34130","34131","34132","34133","34134","34135","34136","34137","34138","34139","34140","34141","34142","34143","34144","34145","34146","34147","34148","34149","34150","34151","34152","34153","34154","34155","34156","34157","34158","34159","34160","34161","34162","34163","34164","34165","34166","34167","34168","34169","34170","34171","34172","34173","34174","34175","34176","34177","34178","34179","34180","34181","34182","34183","34184","34185","34186","34187","34188","34189","34190","34191","34192","34193","34194","34195","34196","34197","34198","34199","34200","34201","34202","34203","34204","34205","34206","34207","34208","34209","34210","34211","34212","34213","34214","34215","34216","34217","34218","34219","34220","34221","34222","34223","34224","34225","34226","34227","34228","34229","34230","34231","34232","34233","34234","34235","34236","34237","34238","34239","34240","34241","34242","34243","34244","34245","34246","34247","34248","34249","34250","34251","34252","34253","34254","34255","34256","34257","34258","34259","34260","34261","34262","34263","34264","34265","34266","34267","34268","34269","34270","34271","34272","34273","34274","34275","34276","34277","34278","34279","34280","34281","34282","34283","34284","34285","34286","34287","34288","34289","34290","34291","34292","34293","34294","34295","34296","34297","34298","34299","34300","34301","34302","34303","34304","34305","34306","34307","34308","34309","34310","34311","34312","34313","34314","34315","34316","34317","34318","34319","34320","34321","34322","34323","34324","34325","34326","34327","34328","34329","34330","34331","34332","34333","34334","34335","34336","34337","34338","34339","34340","34341","34342","34343","34344","34345","34346","34347","34348","34349","34350","34351","34352","34353","34354","34355","34356","34357","34358","34359","34360","34361","34362","34363","34364","34365","34366","34367","34368","34369","34370","34371","34372","34373","34374","34375","34376","34377","34378","34379","34380","34381","34382","34383","34384","34385","34386","34387","34388","34389","34390","34391","34392","34393","34394","34395","34396","34397","34398","34399","34400","34401","34402","34403","34404","34405","34406","34407","34408","34409","34410","34411","34412","34413","34414","34415","34416","34417","34418","34419","34420","34421","34422","34423","34424","34425","34426","34427","34428","34429","34430","34431","34432","34433","34434","34435","34436","34437","34438","34439","34440","34441","34442","34443","34444","34445","34446","34447","34448","34449","34450","34451","34452","34453","34454","34455","34456","34457","34458","34459","34460","34461","34462","34463","34464","34465","34466","34467","34468","34469","34470","34471","34472","34473","34474","34475","34476","34477","34478","34479","34480","34481","34482","34483","34484","34485","34486","34487","34488","34489","34490","34491","34492","34493","34494","34495","34496","34497","34498","34499","34500","34501","34502","34503","34504","34505","34506","34507","34508","34509","34510","34511","34512","34513","34514","34515","34516","34517","34518","34519","34520","34521","34522","34523","34524","34525","34526","34527","34528","34529","34530","34531","34532","34533","34534","34535","34536","34537","34538","34539","34540","34541","34542","34543","34544","34545","34546","34547","34548","34549","34550","34551","34552","34553","34554","34555","34556","34557","34558","34559","34560","34561","34562","34563","34564","34565","34566","34567","34568","34569","34570","34571","34572","34573","34574","34575","34576","34577","34578","34579","34580","34581","34582","34583","34584","34585","34586","34587","34588","34589","34590","34591","34592","34593","34594","34595","34596","34597","34598","34599","34600","34601","34602","34603","34604","34605","34606","34607","34608","34609","34610","34611","34612","34613","34614","34615","34616","34617","34618","34619","34620","34621","34622","34623","34624","34625","34626","34627","34628","34629","34630","34631","34632","34633","34634","34635","34636","34637","34638","34639","34640","34641","34642","34643","34644","34645","34646","34647","34648","34649","34650","34651","34652","34653","34654","34655","34656","34657","34658","34659","34660","34661","34662","34663","34664","34665","34666","34667","34668","34669","34670","34671","34672","34673","34674","34675","34676","34677","34678","34679","34680","34681","34682","34683","34684","34685","34686","34687","34688","34689","34690","34691","34692","34693","34694","34695","34696","34697","34698","34699","34700","34701","34702","34703","34704","34705","34706","34707","34708","34709","34710","34711","34712","34713","34714","34715","34716","34717","34718","34719","34720","34721","34722","34723","34724","34725","34726","34727","34728","34729","34730","34731","34732","34733","34734","34735","34736","34737","34738","34739","34740","34741","34742","34743","34744","34745","34746","34747","34748","34749","34750","34751","34752","34753","34754","34755","34756","34757","34758","34759","34760","34761","34762","34763","34764","34765","34766","34767","34768","34769","34770","34771","34772","34773","34774","34775","34776","34777","34778","34779","34780","34781","34782","34783","34784","34785","34786","34787","34788","34789","34790","34791","34792","34793","34794","34795","34796","34797","34798","34799","34800","34801","34802","34803","34804","34805","34806","34807","34808","34809","34810","34811","34812","34813","34814","34815","34816","34817","34818","34819","34820","34821","34822","34823","34824","34825","34826","34827","34828","34829","34830","34831","34832","34833","34834","34835","34836","34837","34838","34839","34840","34841","34842","34843","34844","34845","34846","34847","34848","34849","34850","34851","34852","34853","34854","34855","34856","34857","34858","34859","34860","34861","34862","34863","34864","34865","34866","34867","34868","34869","34870","34871","34872","34873","34874","34875","34876","34877","34878","34879","34880","34881","34882","34883","34884","34885","34886","34887","34888","34889","34890","34891","34892","34893","34894","34895","34896","34897","34898","34899","34900","34901","34902","34903","34904","34905","34906","34907","34908","34909","34910","34911","34912","34913","34914","34915","34916","34917","34918","34919","34920","34921","34922","34923","34924","34925","34926","34927","34928","34929","34930","34931","34932","34933","34934","34935","34936","34937","34938","34939","34940","34941","34942","34943","34944","34945","34946","34947","34948","34949","34950","34951","34952","34953","34954","34955","34956","34957","34958","34959","34960","34961","34962","34963","34964","34965","34966","34967","34968","34969","34970","34971","34972","34973","34974","34975","34976","34977","34978","34979","34980","34981","34982","34983","34984","34985","34986","34987","34988","34989","34990","34991","34992","34993","34994","34995","34996","34997","34998","34999","35000","35001","35002","35003","35004","35005","35006","35007","35008","35009","35010","35011","35012","35013","35014","35015","35016","35017","35018","35019","35020","35021","35022","35023","35024","35025","35026","35027","35028","35029","35030","35031","35032","35033","35034","35035","35036","35037","35038","35039","35040","35041","35042","35043","35044","35045","35046","35047","35048","35049","35050","35051","35052","35053","35054","35055","35056","35057","35058","35059","35060","35061","35062","35063","35064","35065","35066","35067","35068","35069","35070","35071","35072","35073","35074","35075","35076","35077","35078","35079","35080","35081","35082","35083","35084","35085","35086","35087","35088","35089","35090","35091","35092","35093","35094","35095","35096","35097","35098","35099","35100","35101","35102","35103","35104","35105","35106","35107","35108","35109","35110","35111","35112","35113","35114","35115","35116","35117","35118","35119","35120","35121","35122","35123","35124","35125","35126","35127","35128","35129","35130","35131","35132","35133","35134","35135","35136","35137","35138","35139","35140","35141","35142","35143","35144","35145","35146","35147","35148","35149","35150","35151","35152","35153","35154","35155","35156","35157","35158","35159","35160","35161","35162","35163","35164","35165","35166","35167","35168","35169","35170","35171","35172","35173","35174","35175","35176","35177","35178","35179","35180","35181","35182","35183","35184","35185","35186","35187","35188","35189","35190","35191","35192","35193","35194","35195","35196","35197","35198","35199","35200","35201","35202","35203","35204","35205","35206","35207","35208","35209","35210","35211","35212","35213","35214","35215","35216","35217","35218","35219","35220","35221","35222","35223","35224","35225","35226","35227","35228","35229","35230","35231","35232","35233","35234","35235","35236","35237","35238","35239","35240","35241","35242","35243","35244","35245","35246","35247","35248","35249","35250","35251","35252","35253","35254","35255","35256","35257","35258","35259","35260","35261","35262","35263","35264","35265","35266","35267","35268","35269","35270","35271","35272","35273","35274","35275","35276","35277","35278","35279","35280","35281","35282","35283","35284","35285","35286","35287","35288","35289","35290","35291","35292","35293","35294","35295","35296","35297","35298","35299","35300","35301","35302","35303","35304","35305","35306","35307","35308","35309","35310","35311","35312","35313","35314","35315","35316","35317","35318","35319","35320","35321","35322","35323","35324","35325","35326","35327","35328","35329","35330","35331","35332","35333","35334","35335","35336","35337","35338","35339","35340","35341","35342","35343","35344","35345","35346","35347","35348","35349","35350","35351","35352","35353","35354","35355","35356","35357","35358","35359","35360","35361","35362","35363","35364","35365","35366","35367","35368","35369","35370","35371","35372","35373","35374","35375","35376","35377","35378","35379","35380","35381","35382","35383","35384","35385","35386","35387","35388","35389","35390","35391","35392","35393","35394","35395","35396","35397","35398","35399","35400","35401","35402","35403","35404","35405","35406","35407","35408","35409","35410","35411","35412","35413","35414","35415","35416","35417","35418","35419","35420","35421","35422","35423","35424","35425","35426","35427","35428","35429","35430","35431","35432","35433","35434","35435","35436","35437","35438","35439","35440","35441","35442","35443","35444","35445","35446","35447","35448","35449","35450","35451","35452","35453","35454","35455","35456","35457","35458","35459","35460","35461","35462","35463","35464","35465","35466","35467","35468","35469","35470","35471","35472","35473","35474","35475","35476","35477","35478","35479","35480","35481","35482","35483","35484","35485","35486","35487","35488","35489","35490","35491","35492","35493","35494","35495","35496","35497","35498","35499","35500","35501","35502","35503","35504","35505","35506","35507","35508","35509","35510","35511","35512","35513","35514","35515","35516","35517","35518","35519","35520","35521","35522","35523","35524","35525","35526","35527","35528","35529","35530","35531","35532","35533","35534","35535","35536","35537","35538","35539","35540","35541","35542","35543","35544","35545","35546","35547","35548","35549","35550","35551","35552","35553","35554","35555","35556","35557","35558","35559","35560","35561","35562","35563","35564","35565","35566","35567","35568","35569","35570","35571","35572","35573","35574","35575","35576","35577","35578","35579","35580","35581","35582","35583","35584","35585","35586","35587","35588","35589","35590","35591","35592","35593","35594","35595","35596","35597","35598","35599","35600","35601","35602","35603","35604","35605","35606","35607","35608","35609","35610","35611","35612","35613","35614","35615","35616","35617","35618","35619","35620","35621","35622","35623","35624","35625","35626","35627","35628","35629","35630","35631","35632","35633","35634","35635","35636","35637","35638","35639","35640","35641","35642","35643","35644","35645","35646","35647","35648","35649","35650","35651","35652","35653","35654","35655","35656","35657","35658","35659","35660","35661","35662","35663","35664","35665","35666","35667","35668","35669","35670","35671","35672","35673","35674","35675","35676","35677","35678","35679","35680","35681","35682","35683","35684","35685","35686","35687","35688","35689","35690","35691","35692","35693","35694","35695","35696","35697","35698","35699","35700","35701","35702","35703","35704","35705","35706","35707","35708","35709","35710","35711","35712","35713","35714","35715","35716","35717","35718","35719","35720","35721","35722","35723","35724","35725","35726","35727","35728","35729","35730","35731","35732","35733","35734","35735","35736","35737","35738","35739","35740","35741","35742","35743","35744","35745","35746","35747","35748","35749","35750","35751","35752","35753","35754","35755","35756","35757","35758","35759","35760","35761","35762","35763","35764","35765","35766","35767","35768","35769","35770","35771","35772","35773","35774","35775","35776","35777","35778","35779","35780","35781","35782","35783","35784","35785","35786","35787","35788","35789","35790","35791","35792","35793","35794","35795","35796","35797","35798","35799","35800","35801","35802","35803","35804","35805","35806","35807","35808","35809","35810","35811","35812","35813","35814","35815","35816","35817","35818","35819","35820","35821","35822","35823","35824","35825","35826","35827","35828","35829","35830","35831","35832","35833","35834","35835","35836","35837","35838","35839","35840","35841","35842","35843","35844","35845","35846","35847","35848","35849","35850","35851","35852","35853","35854","35855","35856","35857","35858","35859","35860","35861","35862","35863","35864","35865","35866","35867","35868","35869","35870","35871","35872","35873","35874","35875","35876","35877","35878","35879","35880","35881","35882","35883","35884","35885","35886","35887","35888","35889","35890","35891","35892","35893","35894","35895","35896","35897","35898","35899","35900","35901","35902","35903","35904","35905","35906","35907","35908","35909","35910","35911","35912","35913","35914","35915","35916","35917","35918","35919","35920","35921","35922","35923","35924","35925","35926","35927","35928","35929","35930","35931","35932","35933","35934","35935","35936","35937","35938","35939","35940","35941","35942","35943","35944","35945","35946","35947","35948","35949","35950","35951","35952","35953","35954","35955","35956","35957","35958","35959","35960","35961","35962","35963","35964","35965","35966","35967","35968","35969","35970","35971","35972","35973","35974","35975","35976","35977","35978","35979","35980","35981","35982","35983","35984","35985","35986","35987","35988","35989","35990","35991","35992","35993","35994","35995","35996","35997","35998","35999","36000","36001","36002","36003","36004","36005","36006","36007","36008","36009","36010","36011","36012","36013","36014","36015","36016","36017","36018","36019","36020","36021","36022","36023","36024","36025","36026","36027","36028","36029","36030","36031","36032","36033","36034","36035","36036","36037","36038","36039","36040","36041","36042","36043","36044","36045","36046","36047","36048","36049","36050","36051","36052","36053","36054","36055","36056","36057","36058","36059","36060","36061","36062","36063","36064","36065","36066","36067","36068","36069","36070","36071","36072","36073","36074","36075","36076","36077","36078","36079","36080","36081","36082","36083","36084","36085","36086","36087","36088","36089","36090","36091","36092","36093","36094","36095","36096","36097","36098","36099","36100","36101","36102","36103","36104","36105","36106","36107","36108","36109","36110","36111","36112","36113","36114","36115","36116","36117","36118","36119","36120","36121","36122","36123","36124","36125","36126","36127","36128","36129","36130","36131","36132","36133","36134","36135","36136","36137","36138","36139","36140","36141","36142","36143","36144","36145","36146","36147","36148","36149","36150","36151","36152","36153","36154","36155","36156","36157","36158","36159","36160","36161","36162","36163","36164","36165","36166","36167","36168","36169","36170","36171","36172","36173","36174","36175","36176","36177","36178","36179","36180","36181","36182","36183","36184","36185","36186","36187","36188","36189","36190","36191","36192","36193","36194","36195","36196","36197","36198","36199","36200","36201","36202","36203","36204","36205","36206","36207","36208","36209","36210","36211","36212","36213","36214","36215","36216","36217","36218","36219","36220","36221","36222","36223","36224","36225","36226","36227","36228","36229","36230","36231","36232","36233","36234","36235","36236","36237","36238","36239","36240","36241","36242","36243","36244","36245","36246","36247","36248","36249","36250","36251","36252","36253","36254","36255","36256","36257","36258","36259","36260","36261","36262","36263","36264","36265","36266","36267","36268","36269","36270","36271","36272","36273","36274","36275","36276","36277","36278","36279","36280","36281","36282","36283","36284","36285","36286","36287","36288","36289","36290","36291","36292","36293","36294","36295","36296","36297","36298","36299","36300","36301","36302","36303","36304","36305","36306","36307","36308","36309","36310","36311","36312","36313","36314","36315","36316","36317","36318","36319","36320","36321","36322","36323","36324","36325","36326","36327","36328","36329","36330","36331","36332","36333","36334","36335","36336","36337","36338","36339","36340","36341","36342","36343","36344","36345","36346","36347","36348","36349","36350","36351","36352","36353","36354","36355","36356","36357","36358","36359","36360","36361","36362","36363","36364","36365","36366","36367","36368","36369","36370","36371","36372","36373","36374","36375","36376","36377","36378","36379","36380","36381","36382","36383","36384","36385","36386","36387","36388","36389","36390","36391","36392","36393","36394","36395","36396","36397","36398","36399","36400","36401","36402","36403","36404","36405","36406","36407","36408","36409","36410","36411","36412","36413","36414","36415","36416","36417","36418","36419","36420","36421","36422","36423","36424","36425","36426","36427","36428","36429","36430","36431","36432","36433","36434","36435","36436","36437","36438","36439","36440","36441","36442","36443","36444","36445","36446","36447","36448","36449","36450","36451","36452","36453","36454","36455","36456","36457","36458","36459","36460","36461","36462","36463","36464","36465","36466","36467","36468","36469","36470","36471","36472","36473","36474","36475","36476","36477","36478","36479","36480","36481","36482","36483","36484","36485","36486","36487","36488","36489","36490","36491","36492","36493","36494","36495","36496","36497","36498","36499","36500","36501","36502","36503","36504","36505","36506","36507","36508","36509","36510","36511","36512","36513","36514","36515","36516","36517","36518","36519","36520","36521","36522","36523","36524","36525","36526","36527","36528","36529","36530","36531","36532","36533","36534","36535","36536","36537","36538","36539","36540","36541","36542","36543","36544","36545","36546","36547","36548","36549","36550","36551","36552","36553","36554","36555","36556","36557","36558","36559","36560","36561","36562","36563","36564","36565","36566","36567","36568","36569","36570","36571","36572","36573","36574","36575","36576","36577","36578","36579","36580","36581","36582","36583","36584","36585","36586","36587","36588","36589","36590","36591","36592","36593","36594","36595","36596","36597","36598","36599","36600","36601","36602","36603","36604","36605","36606","36607","36608","36609","36610","36611","36612","36613","36614","36615","36616","36617","36618","36619","36620","36621","36622","36623","36624","36625","36626","36627","36628","36629","36630","36631","36632","36633","36634","36635","36636","36637","36638","36639","36640","36641","36642","36643","36644","36645","36646","36647","36648","36649","36650","36651","36652","36653","36654","36655","36656","36657","36658","36659","36660","36661","36662","36663","36664","36665","36666","36667","36668","36669","36670","36671","36672","36673","36674","36675","36676","36677","36678","36679","36680","36681","36682","36683","36684","36685","36686","36687","36688","36689","36690","36691","36692","36693","36694","36695","36696","36697","36698","36699","36700","36701","36702","36703","36704","36705","36706","36707","36708","36709","36710","36711","36712","36713","36714","36715","36716","36717","36718","36719","36720","36721","36722","36723","36724","36725","36726","36727","36728","36729","36730","36731","36732","36733","36734","36735","36736","36737","36738","36739","36740","36741","36742","36743","36744","36745","36746","36747","36748","36749","36750","36751","36752","36753","36754","36755","36756","36757","36758","36759","36760","36761","36762","36763","36764","36765","36766","36767","36768","36769","36770","36771","36772","36773","36774","36775","36776","36777","36778","36779","36780","36781","36782","36783","36784","36785","36786","36787","36788","36789","36790","36791","36792","36793","36794","36795","36796","36797","36798","36799","36800","36801","36802","36803","36804","36805","36806","36807","36808","36809","36810","36811","36812","36813","36814","36815","36816","36817","36818","36819","36820","36821","36822","36823","36824","36825","36826","36827","36828","36829","36830","36831","36832","36833","36834","36835","36836","36837","36838","36839","36840","36841","36842","36843","36844","36845","36846","36847","36848","36849","36850","36851","36852","36853","36854","36855","36856","36857","36858","36859","36860","36861","36862","36863","36864","36865","36866","36867","36868","36869","36870","36871","36872","36873","36874","36875","36876","36877","36878","36879","36880","36881","36882","36883","36884","36885","36886","36887","36888","36889","36890","36891","36892","36893","36894","36895","36896","36897","36898","36899","36900","36901","36902","36903","36904","36905","36906","36907","36908","36909","36910","36911","36912","36913","36914","36915","36916","36917","36918","36919","36920","36921","36922","36923","36924","36925","36926","36927","36928","36929","36930","36931","36932","36933","36934","36935","36936","36937","36938","36939","36940","36941","36942","36943","36944","36945","36946","36947","36948","36949","36950","36951","36952","36953","36954","36955","36956","36957","36958","36959","36960","36961","36962","36963","36964","36965","36966","36967","36968","36969","36970","36971","36972","36973","36974","36975","36976","36977","36978","36979","36980","36981","36982","36983","36984","36985","36986","36987","36988","36989","36990","36991","36992","36993","36994","36995","36996","36997","36998","36999","37000","37001","37002","37003","37004","37005","37006","37007","37008","37009","37010","37011","37012","37013","37014","37015","37016","37017","37018","37019","37020","37021","37022","37023","37024","37025","37026","37027","37028","37029","37030","37031","37032","37033","37034","37035","37036","37037","37038","37039","37040","37041","37042","37043","37044","37045","37046","37047","37048","37049","37050","37051","37052","37053","37054","37055","37056","37057","37058","37059","37060","37061","37062","37063","37064","37065","37066","37067","37068","37069","37070","37071","37072","37073","37074","37075","37076","37077","37078","37079","37080","37081","37082","37083","37084","37085","37086","37087","37088","37089","37090","37091","37092","37093","37094","37095","37096","37097","37098","37099","37100","37101","37102","37103","37104","37105","37106","37107","37108","37109","37110","37111","37112","37113","37114","37115","37116","37117","37118","37119","37120","37121","37122","37123","37124","37125","37126","37127","37128","37129","37130","37131","37132","37133","37134","37135","37136","37137","37138","37139","37140","37141","37142","37143","37144","37145","37146","37147","37148","37149","37150","37151","37152","37153","37154","37155","37156","37157","37158","37159","37160","37161","37162","37163","37164","37165","37166","37167","37168","37169","37170","37171","37172","37173","37174","37175","37176","37177","37178","37179","37180","37181","37182","37183","37184","37185","37186","37187","37188","37189","37190","37191","37192","37193","37194","37195","37196","37197","37198","37199","37200","37201","37202","37203","37204","37205","37206","37207","37208","37209","37210","37211","37212","37213","37214","37215","37216","37217","37218","37219","37220","37221","37222","37223","37224","37225","37226","37227","37228","37229","37230","37231","37232","37233","37234","37235","37236","37237","37238","37239","37240","37241","37242","37243","37244","37245","37246","37247","37248","37249","37250","37251","37252","37253","37254","37255","37256","37257","37258","37259","37260","37261","37262","37263","37264","37265","37266","37267","37268","37269","37270","37271","37272","37273","37274","37275","37276","37277","37278","37279","37280","37281","37282","37283","37284","37285","37286","37287","37288","37289","37290","37291","37292","37293","37294","37295","37296","37297","37298","37299","37300","37301","37302","37303","37304","37305","37306","37307","37308","37309","37310","37311","37312","37313","37314","37315","37316","37317","37318","37319","37320","37321","37322","37323","37324","37325","37326","37327","37328","37329","37330","37331","37332","37333","37334","37335","37336","37337","37338","37339","37340","37341","37342","37343","37344","37345","37346","37347","37348","37349","37350","37351","37352","37353","37354","37355","37356","37357","37358","37359","37360","37361","37362","37363","37364","37365","37366","37367","37368","37369","37370","37371","37372","37373","37374","37375","37376","37377","37378","37379","37380","37381","37382","37383","37384","37385","37386","37387","37388","37389","37390","37391","37392","37393","37394","37395","37396","37397","37398","37399","37400","37401","37402","37403","37404","37405","37406","37407","37408","37409","37410","37411","37412","37413","37414","37415","37416","37417","37418","37419","37420","37421","37422","37423","37424","37425","37426","37427","37428","37429","37430","37431","37432","37433","37434","37435","37436","37437","37438","37439","37440","37441","37442","37443","37444","37445","37446","37447","37448","37449","37450","37451","37452","37453","37454","37455","37456","37457","37458","37459","37460","37461","37462","37463","37464","37465","37466","37467","37468","37469","37470","37471","37472","37473","37474","37475","37476","37477","37478","37479","37480","37481","37482","37483","37484","37485","37486","37487","37488","37489","37490","37491","37492","37493","37494","37495","37496","37497","37498","37499","37500","37501","37502","37503","37504","37505","37506","37507","37508","37509","37510","37511","37512","37513","37514","37515","37516","37517","37518","37519","37520","37521","37522","37523","37524","37525","37526","37527","37528","37529","37530","37531","37532","37533","37534","37535","37536","37537","37538","37539","37540","37541","37542","37543","37544","37545","37546","37547","37548","37549","37550","37551","37552","37553","37554","37555","37556","37557","37558","37559","37560","37561","37562","37563","37564","37565","37566","37567","37568","37569","37570","37571","37572","37573","37574","37575","37576","37577","37578","37579","37580","37581","37582","37583","37584","37585","37586","37587","37588","37589","37590","37591","37592","37593","37594","37595","37596","37597","37598","37599","37600","37601","37602","37603","37604","37605","37606","37607","37608","37609","37610","37611","37612","37613","37614","37615","37616","37617","37618","37619","37620","37621","37622","37623","37624","37625","37626","37627","37628","37629","37630","37631","37632","37633","37634","37635","37636","37637","37638","37639","37640","37641","37642","37643","37644","37645","37646","37647","37648","37649","37650","37651","37652","37653","37654","37655","37656","37657","37658","37659","37660","37661","37662","37663","37664","37665","37666","37667","37668","37669","37670","37671","37672","37673","37674","37675","37676","37677","37678","37679","37680","37681","37682","37683","37684","37685","37686","37687","37688","37689","37690","37691","37692","37693","37694","37695","37696","37697","37698","37699","37700","37701","37702","37703","37704","37705","37706","37707","37708","37709","37710","37711","37712","37713","37714","37715","37716","37717","37718","37719","37720","37721","37722","37723","37724","37725","37726","37727","37728","37729","37730","37731","37732","37733","37734","37735","37736","37737","37738","37739","37740","37741","37742","37743","37744","37745","37746","37747","37748","37749","37750","37751","37752","37753","37754","37755","37756","37757","37758","37759","37760","37761","37762","37763","37764","37765","37766","37767","37768","37769","37770","37771","37772","37773","37774","37775","37776","37777","37778","37779","37780","37781","37782","37783","37784","37785","37786","37787","37788","37789","37790","37791","37792","37793","37794","37795","37796","37797","37798","37799","37800","37801","37802","37803","37804","37805","37806","37807","37808","37809","37810","37811","37812","37813","37814","37815","37816","37817","37818","37819","37820","37821","37822","37823","37824","37825","37826","37827","37828","37829","37830","37831","37832","37833","37834","37835","37836","37837","37838","37839","37840","37841","37842","37843","37844","37845","37846","37847","37848","37849","37850","37851","37852","37853","37854","37855","37856","37857","37858","37859","37860","37861","37862","37863","37864","37865","37866","37867","37868","37869","37870","37871","37872","37873","37874","37875","37876","37877","37878","37879","37880","37881","37882","37883","37884","37885","37886","37887","37888","37889","37890","37891","37892","37893","37894","37895","37896","37897","37898","37899","37900","37901","37902","37903","37904","37905","37906","37907","37908","37909","37910","37911","37912","37913","37914","37915","37916","37917","37918","37919","37920","37921","37922","37923","37924","37925","37926","37927","37928","37929","37930","37931","37932","37933","37934","37935","37936","37937","37938","37939","37940","37941","37942","37943","37944","37945","37946","37947","37948","37949","37950","37951","37952","37953","37954","37955","37956","37957","37958","37959","37960","37961","37962","37963","37964","37965","37966","37967","37968","37969","37970","37971","37972","37973","37974","37975","37976","37977","37978","37979","37980","37981","37982","37983","37984","37985","37986","37987","37988","37989","37990","37991","37992","37993","37994","37995","37996","37997","37998","37999","38000","38001","38002","38003","38004","38005","38006","38007","38008","38009","38010","38011","38012","38013","38014","38015","38016","38017","38018","38019","38020","38021","38022","38023","38024","38025","38026","38027","38028","38029","38030","38031","38032","38033","38034","38035","38036","38037","38038","38039","38040","38041","38042","38043","38044","38045","38046","38047","38048","38049","38050","38051","38052","38053","38054","38055","38056","38057","38058","38059","38060","38061","38062","38063","38064","38065","38066","38067","38068","38069","38070","38071","38072","38073","38074","38075","38076","38077","38078","38079","38080","38081","38082","38083","38084","38085","38086","38087","38088","38089","38090","38091","38092","38093","38094","38095","38096","38097","38098","38099","38100","38101","38102","38103","38104","38105","38106","38107","38108","38109","38110","38111","38112","38113","38114","38115","38116","38117","38118","38119","38120","38121","38122","38123","38124","38125","38126","38127","38128","38129","38130","38131","38132","38133","38134","38135","38136","38137","38138","38139","38140","38141","38142","38143","38144","38145","38146","38147","38148","38149","38150","38151","38152","38153","38154","38155","38156","38157","38158","38159","38160","38161","38162","38163","38164","38165","38166","38167","38168","38169","38170","38171","38172","38173","38174","38175","38176","38177","38178","38179","38180","38181","38182","38183","38184","38185","38186","38187","38188","38189","38190","38191","38192","38193","38194","38195","38196","38197","38198","38199","38200","38201","38202","38203","38204","38205","38206","38207","38208","38209","38210","38211","38212","38213","38214","38215","38216","38217","38218","38219","38220","38221","38222","38223","38224","38225","38226","38227","38228","38229","38230","38231","38232","38233","38234","38235","38236","38237","38238","38239","38240","38241","38242","38243","38244","38245","38246","38247","38248","38249","38250","38251","38252","38253","38254","38255","38256","38257","38258","38259","38260","38261","38262","38263","38264","38265","38266","38267","38268","38269","38270","38271","38272","38273","38274","38275","38276","38277","38278","38279","38280","38281","38282","38283","38284","38285","38286","38287","38288","38289","38290","38291","38292","38293","38294","38295","38296","38297","38298","38299","38300","38301","38302","38303","38304","38305","38306","38307","38308","38309","38310","38311","38312","38313","38314","38315","38316","38317","38318","38319","38320","38321","38322","38323","38324","38325","38326","38327","38328","38329","38330","38331","38332","38333","38334","38335","38336","38337","38338","38339","38340","38341","38342","38343","38344","38345","38346","38347","38348","38349","38350","38351","38352","38353","38354","38355","38356","38357","38358","38359","38360","38361","38362","38363","38364","38365","38366","38367","38368","38369","38370","38371","38372","38373","38374","38375","38376","38377","38378","38379","38380","38381","38382","38383","38384","38385","38386","38387","38388","38389","38390","38391","38392","38393","38394","38395","38396","38397","38398","38399","38400","38401","38402","38403","38404","38405","38406","38407","38408","38409","38410","38411","38412","38413","38414","38415","38416","38417","38418","38419","38420","38421","38422","38423","38424","38425","38426","38427","38428","38429","38430","38431","38432","38433","38434","38435","38436","38437","38438","38439","38440","38441","38442","38443","38444","38445","38446","38447","38448","38449","38450","38451","38452","38453","38454","38455","38456","38457","38458","38459","38460","38461","38462","38463","38464","38465","38466","38467","38468","38469","38470","38471","38472","38473","38474","38475","38476","38477","38478","38479","38480","38481","38482","38483","38484","38485","38486","38487","38488","38489","38490","38491","38492","38493","38494","38495","38496","38497","38498","38499","38500","38501","38502","38503","38504","38505","38506","38507","38508","38509","38510","38511","38512","38513","38514","38515","38516","38517","38518","38519","38520","38521","38522","38523","38524","38525","38526","38527","38528","38529","38530","38531","38532","38533","38534","38535","38536","38537","38538","38539","38540","38541","38542","38543","38544","38545","38546","38547","38548","38549","38550","38551","38552","38553","38554","38555","38556","38557","38558","38559","38560","38561","38562","38563","38564","38565","38566","38567","38568","38569","38570","38571","38572","38573","38574","38575","38576","38577","38578","38579","38580","38581","38582","38583","38584","38585","38586","38587","38588","38589","38590","38591","38592","38593","38594","38595","38596","38597","38598","38599","38600","38601","38602","38603","38604","38605","38606","38607","38608","38609","38610","38611","38612","38613","38614","38615","38616","38617","38618","38619","38620","38621","38622","38623","38624","38625","38626","38627","38628","38629","38630","38631","38632","38633","38634","38635","38636","38637","38638","38639","38640","38641","38642","38643","38644","38645","38646","38647","38648","38649","38650","38651","38652","38653","38654","38655","38656","38657","38658","38659","38660","38661","38662","38663","38664","38665","38666","38667","38668","38669","38670","38671","38672","38673","38674","38675","38676","38677","38678","38679","38680","38681","38682","38683","38684","38685","38686","38687","38688","38689","38690","38691","38692","38693","38694","38695","38696","38697","38698","38699","38700","38701","38702","38703","38704","38705","38706","38707","38708","38709","38710","38711","38712","38713","38714","38715","38716","38717","38718","38719","38720","38721","38722","38723","38724","38725","38726","38727","38728","38729","38730","38731","38732","38733","38734","38735","38736","38737","38738","38739","38740","38741","38742","38743","38744","38745","38746","38747","38748","38749","38750","38751","38752","38753","38754","38755","38756","38757","38758","38759","38760","38761","38762","38763","38764","38765","38766","38767","38768","38769","38770","38771","38772","38773","38774","38775","38776","38777","38778","38779","38780","38781","38782","38783","38784","38785","38786","38787","38788","38789","38790","38791","38792","38793","38794","38795","38796","38797","38798","38799","38800","38801","38802","38803","38804","38805","38806","38807","38808","38809","38810","38811","38812","38813","38814","38815","38816","38817","38818","38819","38820","38821","38822","38823","38824","38825","38826","38827","38828","38829","38830","38831","38832","38833","38834","38835","38836","38837","38838","38839","38840","38841","38842","38843","38844","38845","38846","38847","38848","38849","38850","38851","38852","38853","38854","38855","38856","38857","38858","38859","38860","38861","38862","38863","38864","38865","38866","38867","38868","38869","38870","38871","38872","38873","38874","38875","38876","38877","38878","38879","38880","38881","38882","38883","38884","38885","38886","38887","38888","38889","38890","38891","38892","38893","38894","38895","38896","38897","38898","38899","38900","38901","38902","38903","38904","38905","38906","38907","38908","38909","38910","38911","38912","38913","38914","38915","38916","38917","38918","38919","38920","38921","38922","38923","38924","38925","38926","38927","38928","38929","38930","38931","38932","38933","38934","38935","38936","38937","38938","38939","38940","38941","38942","38943","38944","38945","38946","38947","38948","38949","38950","38951","38952","38953","38954","38955","38956","38957","38958","38959","38960","38961","38962","38963","38964","38965","38966","38967","38968","38969","38970","38971","38972","38973","38974","38975","38976","38977","38978","38979","38980","38981","38982","38983","38984","38985","38986","38987","38988","38989","38990","38991","38992","38993","38994","38995","38996","38997","38998","38999","39000","39001","39002","39003","39004","39005","39006","39007","39008","39009","39010","39011","39012","39013","39014","39015","39016","39017","39018","39019","39020","39021","39022","39023","39024","39025","39026","39027","39028","39029","39030","39031","39032","39033","39034","39035","39036","39037","39038","39039","39040","39041","39042","39043","39044","39045","39046","39047","39048","39049","39050","39051","39052","39053","39054","39055","39056","39057","39058","39059","39060","39061","39062","39063","39064","39065","39066","39067","39068","39069","39070","39071","39072","39073","39074","39075","39076","39077","39078","39079","39080","39081","39082","39083","39084","39085","39086","39087","39088","39089","39090","39091","39092","39093","39094","39095","39096","39097","39098","39099","39100","39101","39102","39103","39104","39105","39106","39107","39108","39109","39110","39111","39112","39113","39114","39115","39116","39117","39118","39119","39120","39121","39122","39123","39124","39125","39126","39127","39128","39129","39130","39131","39132","39133","39134","39135","39136","39137","39138","39139","39140","39141","39142","39143","39144","39145","39146","39147","39148","39149","39150","39151","39152","39153","39154","39155","39156","39157","39158","39159","39160","39161","39162","39163","39164","39165","39166","39167","39168","39169","39170","39171","39172","39173","39174","39175","39176","39177","39178","39179","39180","39181","39182","39183","39184","39185","39186","39187","39188","39189","39190","39191","39192","39193","39194","39195","39196","39197","39198","39199","39200","39201","39202","39203","39204","39205","39206","39207","39208","39209","39210","39211","39212","39213","39214","39215","39216","39217","39218","39219","39220","39221","39222","39223","39224","39225","39226","39227","39228","39229","39230","39231","39232","39233","39234","39235","39236","39237","39238","39239","39240","39241","39242","39243","39244","39245","39246","39247","39248","39249","39250","39251","39252","39253","39254","39255","39256","39257","39258","39259","39260","39261","39262","39263","39264","39265","39266","39267","39268","39269","39270","39271","39272","39273","39274","39275","39276","39277","39278","39279","39280","39281","39282","39283","39284","39285","39286","39287","39288","39289","39290","39291","39292","39293","39294","39295","39296","39297","39298","39299","39300","39301","39302","39303","39304","39305","39306","39307","39308","39309","39310","39311","39312","39313","39314","39315","39316","39317","39318","39319","39320","39321","39322","39323","39324","39325","39326","39327","39328","39329","39330","39331","39332","39333","39334","39335","39336","39337","39338","39339","39340","39341","39342","39343","39344","39345","39346","39347","39348","39349","39350","39351","39352","39353","39354","39355","39356","39357","39358","39359","39360","39361","39362","39363","39364","39365","39366","39367","39368","39369","39370","39371","39372","39373","39374","39375","39376","39377","39378","39379","39380","39381","39382","39383","39384","39385","39386","39387","39388","39389","39390","39391","39392","39393","39394","39395","39396","39397","39398","39399","39400","39401","39402","39403","39404","39405","39406","39407","39408","39409","39410","39411","39412","39413","39414","39415","39416","39417","39418","39419","39420","39421","39422","39423","39424","39425","39426","39427","39428","39429","39430","39431","39432","39433","39434","39435","39436","39437","39438","39439","39440","39441","39442","39443","39444","39445","39446","39447","39448","39449","39450","39451","39452","39453","39454","39455","39456","39457","39458","39459","39460","39461","39462","39463","39464","39465","39466","39467","39468","39469","39470","39471","39472","39473","39474","39475","39476","39477","39478","39479","39480","39481","39482","39483","39484","39485","39486","39487","39488","39489","39490","39491","39492","39493","39494","39495","39496","39497","39498","39499","39500","39501","39502","39503","39504","39505","39506","39507","39508","39509","39510","39511","39512","39513","39514","39515","39516","39517","39518","39519","39520","39521","39522","39523","39524","39525","39526","39527","39528","39529","39530","39531","39532","39533","39534","39535","39536","39537","39538","39539","39540","39541","39542","39543","39544","39545","39546","39547","39548","39549","39550","39551","39552","39553","39554","39555","39556","39557","39558","39559","39560","39561","39562","39563","39564","39565","39566","39567","39568","39569","39570","39571","39572","39573","39574","39575","39576","39577","39578","39579","39580","39581","39582","39583","39584","39585","39586","39587","39588","39589","39590","39591","39592","39593","39594","39595","39596","39597","39598","39599","39600","39601","39602","39603","39604","39605","39606","39607","39608","39609","39610","39611","39612","39613","39614","39615","39616","39617","39618","39619","39620","39621","39622","39623","39624","39625","39626","39627","39628","39629","39630","39631","39632","39633","39634","39635","39636","39637","39638","39639","39640","39641","39642","39643","39644","39645","39646","39647","39648","39649","39650","39651","39652","39653","39654","39655","39656","39657","39658","39659","39660","39661","39662","39663","39664","39665","39666","39667","39668","39669","39670","39671","39672","39673","39674","39675","39676","39677","39678","39679","39680","39681","39682","39683","39684","39685","39686","39687","39688","39689","39690","39691","39692","39693","39694","39695","39696","39697","39698","39699","39700","39701","39702","39703","39704","39705","39706","39707","39708","39709","39710","39711","39712","39713","39714","39715","39716","39717","39718","39719","39720","39721","39722","39723","39724","39725","39726","39727","39728","39729","39730","39731","39732","39733","39734","39735","39736","39737","39738","39739","39740","39741","39742","39743","39744","39745","39746","39747","39748","39749","39750","39751","39752","39753","39754","39755","39756","39757","39758","39759","39760","39761","39762","39763","39764","39765","39766","39767","39768","39769","39770","39771","39772","39773","39774","39775","39776","39777","39778","39779","39780","39781","39782","39783","39784","39785","39786","39787","39788","39789","39790","39791","39792","39793","39794","39795","39796","39797","39798","39799","39800","39801","39802","39803","39804","39805","39806","39807","39808","39809","39810","39811","39812","39813","39814","39815","39816","39817","39818","39819","39820","39821","39822","39823","39824","39825","39826","39827","39828","39829","39830","39831","39832","39833","39834","39835","39836","39837","39838","39839","39840","39841","39842","39843","39844","39845","39846","39847","39848","39849","39850","39851","39852","39853","39854","39855","39856","39857","39858","39859","39860","39861","39862","39863","39864","39865","39866","39867","39868","39869","39870","39871","39872","39873","39874","39875","39876","39877","39878","39879","39880","39881","39882","39883","39884","39885","39886","39887","39888","39889","39890","39891","39892","39893","39894","39895","39896","39897","39898","39899","39900","39901","39902","39903","39904","39905","39906","39907","39908","39909","39910","39911","39912","39913","39914","39915","39916","39917","39918","39919","39920","39921","39922","39923","39924","39925","39926","39927","39928","39929","39930","39931","39932","39933","39934","39935","39936","39937","39938","39939","39940","39941","39942","39943","39944","39945","39946","39947","39948","39949","39950","39951","39952","39953","39954","39955","39956","39957","39958","39959","39960","39961","39962","39963","39964","39965","39966","39967","39968","39969","39970","39971","39972","39973","39974","39975","39976","39977","39978","39979","39980","39981","39982","39983","39984","39985","39986","39987","39988","39989","39990","39991","39992","39993","39994","39995","39996","39997","39998","39999","40000","40001","40002","40003","40004","40005","40006","40007","40008","40009","40010","40011","40012","40013","40014","40015","40016","40017","40018","40019","40020","40021","40022","40023","40024","40025","40026","40027","40028","40029","40030","40031","40032","40033","40034","40035","40036","40037","40038","40039","40040","40041","40042","40043","40044","40045","40046","40047","40048","40049","40050","40051","40052","40053","40054","40055","40056","40057","40058","40059","40060","40061","40062","40063","40064","40065","40066","40067","40068","40069","40070","40071","40072","40073","40074","40075","40076","40077","40078","40079","40080","40081","40082","40083","40084","40085","40086","40087","40088","40089","40090","40091","40092","40093","40094","40095","40096","40097","40098","40099","40100","40101","40102","40103","40104","40105","40106","40107","40108","40109","40110","40111","40112","40113","40114","40115","40116","40117","40118","40119","40120","40121","40122","40123","40124","40125","40126","40127","40128","40129","40130","40131","40132","40133","40134","40135","40136","40137","40138","40139","40140","40141","40142","40143","40144","40145","40146","40147","40148","40149","40150","40151","40152","40153","40154","40155","40156","40157","40158","40159","40160","40161","40162","40163","40164","40165","40166","40167","40168","40169","40170","40171","40172","40173","40174","40175","40176","40177","40178","40179","40180","40181","40182","40183","40184","40185","40186","40187","40188","40189","40190","40191","40192","40193","40194","40195","40196","40197","40198","40199","40200","40201","40202","40203","40204","40205","40206","40207","40208","40209","40210","40211","40212","40213","40214","40215","40216","40217","40218","40219","40220","40221","40222","40223","40224","40225","40226","40227","40228","40229","40230","40231","40232","40233","40234","40235","40236","40237","40238","40239","40240","40241","40242","40243","40244","40245","40246","40247","40248","40249","40250","40251","40252","40253","40254","40255","40256","40257","40258","40259","40260","40261","40262","40263","40264","40265","40266","40267","40268","40269","40270","40271","40272","40273","40274","40275","40276","40277","40278","40279","40280","40281","40282","40283","40284","40285","40286","40287","40288","40289","40290","40291","40292","40293","40294","40295","40296","40297","40298","40299","40300","40301","40302","40303","40304","40305","40306","40307","40308","40309","40310","40311","40312","40313","40314","40315","40316","40317","40318","40319","40320","40321","40322","40323","40324","40325","40326","40327","40328","40329","40330","40331","40332","40333","40334","40335","40336","40337","40338","40339","40340","40341","40342","40343","40344","40345","40346","40347","40348","40349","40350","40351","40352","40353","40354","40355","40356","40357","40358","40359","40360","40361","40362","40363","40364","40365","40366","40367","40368","40369","40370","40371","40372","40373","40374","40375","40376","40377","40378","40379","40380","40381","40382","40383","40384","40385","40386","40387","40388","40389","40390","40391","40392","40393","40394","40395","40396","40397","40398","40399","40400","40401","40402","40403","40404","40405","40406","40407","40408","40409","40410","40411","40412","40413","40414","40415","40416","40417","40418","40419","40420","40421","40422","40423","40424","40425","40426","40427","40428","40429","40430","40431","40432","40433","40434","40435","40436","40437","40438","40439","40440","40441","40442","40443","40444","40445","40446","40447","40448","40449","40450","40451","40452","40453","40454","40455","40456","40457","40458","40459","40460","40461","40462","40463","40464","40465","40466","40467","40468","40469","40470","40471","40472","40473","40474","40475","40476","40477","40478","40479","40480","40481","40482","40483","40484","40485","40486","40487","40488","40489","40490","40491","40492","40493","40494","40495","40496","40497","40498","40499","40500","40501","40502","40503","40504","40505","40506","40507","40508","40509","40510","40511","40512","40513","40514","40515","40516","40517","40518","40519","40520","40521","40522","40523","40524","40525","40526","40527","40528","40529","40530","40531","40532","40533","40534","40535","40536","40537","40538","40539","40540","40541","40542","40543","40544","40545","40546","40547","40548","40549","40550","40551","40552","40553","40554","40555","40556","40557","40558","40559","40560","40561","40562","40563","40564","40565","40566","40567","40568","40569","40570","40571","40572","40573","40574","40575","40576","40577","40578","40579","40580","40581","40582","40583","40584","40585","40586","40587","40588","40589","40590","40591","40592","40593","40594","40595","40596","40597","40598","40599","40600","40601","40602","40603","40604","40605","40606","40607","40608","40609","40610","40611","40612","40613","40614","40615","40616","40617","40618","40619","40620","40621","40622","40623","40624","40625","40626","40627","40628","40629","40630","40631","40632","40633","40634","40635","40636","40637","40638","40639","40640","40641","40642","40643","40644","40645","40646","40647","40648","40649","40650","40651","40652","40653","40654","40655","40656","40657","40658","40659","40660","40661","40662","40663","40664","40665","40666","40667","40668","40669","40670","40671","40672","40673","40674","40675","40676","40677","40678","40679","40680","40681","40682","40683","40684","40685","40686","40687","40688","40689","40690","40691","40692","40693","40694","40695","40696","40697","40698","40699","40700","40701","40702","40703","40704","40705","40706","40707","40708","40709","40710","40711","40712","40713","40714","40715","40716","40717","40718","40719","40720","40721","40722","40723","40724","40725","40726","40727","40728","40729","40730","40731","40732","40733","40734","40735","40736","40737","40738","40739","40740","40741","40742","40743","40744","40745","40746","40747","40748","40749","40750","40751","40752","40753","40754","40755","40756","40757","40758","40759","40760","40761","40762","40763","40764","40765","40766","40767","40768","40769","40770","40771","40772","40773","40774","40775","40776","40777","40778","40779","40780","40781","40782","40783","40784","40785","40786","40787","40788","40789","40790","40791","40792","40793","40794","40795","40796","40797","40798","40799","40800","40801","40802","40803","40804","40805","40806","40807","40808","40809","40810","40811","40812","40813","40814","40815","40816","40817","40818","40819","40820","40821","40822","40823","40824","40825","40826","40827","40828","40829","40830","40831","40832","40833","40834","40835","40836","40837","40838","40839","40840","40841","40842","40843","40844","40845","40846","40847","40848","40849","40850","40851","40852","40853","40854","40855","40856","40857","40858","40859","40860","40861","40862","40863","40864","40865","40866","40867","40868","40869","40870","40871","40872","40873","40874","40875","40876","40877","40878","40879","40880","40881","40882","40883","40884","40885","40886","40887","40888","40889","40890","40891","40892","40893","40894","40895","40896","40897","40898","40899","40900","40901","40902","40903","40904","40905","40906","40907","40908","40909","40910","40911","40912","40913","40914","40915","40916","40917","40918","40919","40920","40921","40922","40923","40924","40925","40926","40927","40928","40929","40930","40931","40932","40933","40934","40935","40936","40937","40938","40939","40940","40941","40942","40943","40944","40945","40946","40947","40948","40949","40950","40951","40952","40953","40954","40955","40956","40957","40958","40959","40960","40961","40962","40963","40964","40965","40966","40967","40968","40969","40970","40971","40972","40973","40974","40975","40976","40977","40978","40979","40980","40981","40982","40983","40984","40985","40986","40987","40988","40989","40990","40991","40992","40993","40994","40995","40996","40997","40998","40999","41000","41001","41002","41003","41004","41005","41006","41007","41008","41009","41010","41011","41012","41013","41014","41015","41016","41017","41018","41019","41020","41021","41022","41023","41024","41025","41026","41027","41028","41029","41030","41031","41032","41033","41034","41035","41036","41037","41038","41039","41040","41041","41042","41043","41044","41045","41046","41047","41048","41049","41050","41051","41052","41053","41054","41055","41056","41057","41058","41059","41060","41061","41062","41063","41064","41065","41066","41067","41068","41069","41070","41071","41072","41073","41074","41075","41076","41077","41078","41079","41080","41081","41082","41083","41084","41085","41086","41087","41088","41089","41090","41091","41092","41093","41094","41095","41096","41097","41098","41099","41100","41101","41102","41103","41104","41105","41106","41107","41108","41109","41110","41111","41112","41113","41114","41115","41116","41117","41118","41119","41120","41121","41122","41123","41124","41125","41126","41127","41128","41129","41130","41131","41132","41133","41134","41135","41136","41137","41138","41139","41140","41141","41142","41143","41144","41145","41146","41147","41148","41149","41150","41151","41152","41153","41154","41155","41156","41157","41158","41159","41160","41161","41162","41163","41164","41165","41166","41167","41168","41169","41170","41171","41172","41173","41174","41175","41176","41177","41178","41179","41180","41181","41182","41183","41184","41185","41186","41187","41188","41189","41190","41191","41192","41193","41194","41195","41196","41197","41198","41199","41200","41201","41202","41203","41204","41205","41206","41207","41208","41209","41210","41211","41212","41213","41214","41215","41216","41217","41218","41219","41220","41221","41222","41223","41224","41225","41226","41227","41228","41229","41230","41231","41232","41233","41234","41235","41236","41237","41238","41239","41240","41241","41242","41243","41244","41245","41246","41247","41248","41249","41250","41251","41252","41253","41254","41255","41256","41257","41258","41259","41260","41261","41262","41263","41264","41265","41266","41267","41268","41269","41270","41271","41272","41273","41274","41275","41276","41277","41278","41279","41280","41281","41282","41283","41284","41285","41286","41287","41288","41289","41290","41291","41292","41293","41294","41295","41296","41297","41298","41299","41300","41301","41302","41303","41304","41305","41306","41307","41308","41309","41310","41311","41312","41313","41314","41315","41316","41317","41318","41319","41320","41321","41322","41323","41324","41325","41326","41327","41328","41329","41330","41331","41332","41333","41334","41335","41336","41337","41338","41339","41340","41341","41342","41343","41344","41345","41346","41347","41348","41349","41350","41351","41352","41353","41354","41355","41356","41357","41358","41359","41360","41361","41362","41363","41364","41365","41366","41367","41368","41369","41370","41371","41372","41373","41374","41375","41376","41377","41378","41379","41380","41381","41382","41383","41384","41385","41386","41387","41388","41389","41390","41391","41392","41393","41394","41395","41396","41397","41398","41399","41400","41401","41402","41403","41404","41405","41406","41407","41408","41409","41410","41411","41412","41413","41414","41415","41416","41417","41418","41419","41420","41421","41422","41423","41424","41425","41426","41427","41428","41429","41430","41431","41432","41433","41434","41435","41436","41437","41438","41439","41440","41441","41442","41443","41444","41445","41446","41447","41448","41449","41450","41451","41452","41453","41454","41455","41456","41457","41458","41459","41460","41461","41462","41463","41464","41465","41466","41467","41468","41469","41470","41471","41472","41473","41474","41475","41476","41477","41478","41479","41480","41481","41482","41483","41484","41485","41486","41487","41488","41489","41490","41491","41492","41493","41494","41495","41496","41497","41498","41499","41500","41501","41502","41503","41504","41505","41506","41507","41508","41509","41510","41511","41512","41513","41514","41515","41516","41517","41518","41519","41520","41521","41522","41523","41524","41525","41526","41527","41528","41529","41530","41531","41532","41533","41534","41535","41536","41537","41538","41539","41540","41541","41542","41543","41544","41545","41546","41547","41548","41549","41550","41551","41552","41553","41554","41555","41556","41557","41558","41559","41560","41561","41562","41563","41564","41565","41566","41567","41568","41569","41570","41571","41572","41573","41574","41575","41576","41577","41578","41579","41580","41581","41582","41583","41584","41585","41586","41587","41588","41589","41590","41591","41592","41593","41594","41595","41596","41597","41598","41599","41600","41601","41602","41603","41604","41605","41606","41607","41608","41609","41610","41611","41612","41613","41614","41615","41616","41617","41618","41619","41620","41621","41622","41623","41624","41625","41626","41627","41628","41629","41630","41631","41632","41633","41634","41635","41636","41637","41638","41639","41640","41641","41642","41643","41644","41645","41646","41647","41648","41649","41650","41651","41652","41653","41654","41655","41656","41657","41658","41659","41660","41661","41662","41663","41664","41665","41666","41667","41668","41669","41670","41671","41672","41673","41674","41675","41676","41677","41678","41679","41680","41681","41682","41683","41684","41685","41686","41687","41688","41689","41690","41691","41692","41693","41694","41695","41696","41697","41698","41699","41700","41701","41702","41703","41704","41705","41706","41707","41708","41709","41710","41711","41712","41713","41714","41715","41716","41717","41718","41719","41720","41721","41722","41723","41724","41725","41726","41727","41728","41729","41730","41731","41732","41733","41734","41735","41736","41737","41738","41739","41740","41741","41742","41743","41744","41745","41746","41747","41748","41749","41750","41751","41752","41753","41754","41755","41756","41757","41758","41759","41760","41761","41762","41763","41764","41765","41766","41767","41768","41769","41770","41771","41772","41773","41774","41775","41776","41777","41778","41779","41780","41781","41782","41783","41784","41785","41786","41787","41788","41789","41790","41791","41792","41793","41794","41795","41796","41797","41798","41799","41800","41801","41802","41803","41804","41805","41806","41807","41808","41809","41810","41811","41812","41813","41814","41815","41816","41817","41818","41819","41820","41821","41822","41823","41824","41825","41826","41827","41828","41829","41830","41831","41832","41833","41834","41835","41836","41837","41838","41839","41840","41841","41842","41843","41844","41845","41846","41847","41848","41849","41850","41851","41852","41853","41854","41855","41856","41857","41858","41859","41860","41861","41862","41863","41864","41865","41866","41867","41868","41869","41870","41871","41872","41873","41874","41875","41876","41877","41878","41879","41880","41881","41882","41883","41884","41885","41886","41887","41888","41889","41890","41891","41892","41893","41894","41895","41896","41897","41898","41899","41900","41901","41902","41903","41904","41905","41906","41907","41908","41909","41910","41911","41912","41913","41914","41915","41916","41917","41918","41919","41920","41921","41922","41923","41924","41925","41926","41927","41928","41929","41930","41931","41932","41933","41934","41935","41936","41937","41938","41939","41940","41941","41942","41943","41944","41945","41946","41947","41948","41949","41950","41951","41952","41953","41954","41955","41956","41957","41958","41959","41960","41961","41962","41963","41964","41965","41966","41967","41968","41969","41970","41971","41972","41973","41974","41975","41976","41977","41978","41979","41980","41981","41982","41983","41984","41985","41986","41987","41988","41989","41990","41991","41992","41993","41994","41995","41996","41997","41998","41999","42000","42001","42002","42003","42004","42005","42006","42007","42008","42009","42010","42011","42012","42013","42014","42015","42016","42017","42018","42019","42020","42021","42022","42023","42024","42025","42026","42027","42028","42029","42030","42031","42032","42033","42034","42035","42036","42037","42038","42039","42040","42041","42042","42043","42044","42045","42046","42047","42048","42049","42050","42051","42052","42053","42054","42055","42056","42057","42058","42059","42060","42061","42062","42063","42064","42065","42066","42067","42068","42069","42070","42071","42072","42073","42074","42075","42076","42077","42078","42079","42080","42081","42082","42083","42084","42085","42086","42087","42088","42089","42090","42091","42092","42093","42094","42095","42096","42097","42098","42099","42100","42101","42102","42103","42104","42105","42106","42107","42108","42109","42110","42111","42112","42113","42114","42115","42116","42117","42118","42119","42120","42121","42122","42123","42124","42125","42126","42127","42128","42129","42130","42131","42132","42133","42134","42135","42136","42137","42138","42139","42140","42141","42142","42143","42144","42145","42146","42147","42148","42149","42150","42151","42152","42153","42154","42155","42156","42157","42158","42159","42160","42161","42162","42163","42164","42165","42166","42167","42168","42169","42170","42171","42172","42173","42174","42175","42176","42177","42178","42179","42180","42181","42182","42183","42184","42185","42186","42187","42188","42189","42190","42191","42192","42193","42194","42195","42196","42197","42198","42199","42200","42201","42202","42203","42204","42205","42206","42207","42208","42209","42210","42211","42212","42213","42214","42215","42216","42217","42218","42219","42220","42221","42222","42223","42224","42225","42226","42227","42228","42229","42230","42231","42232","42233","42234","42235","42236","42237","42238","42239","42240","42241","42242","42243","42244","42245","42246","42247","42248","42249","42250","42251","42252","42253","42254","42255","42256","42257","42258","42259","42260","42261","42262","42263","42264","42265","42266","42267","42268","42269","42270","42271","42272","42273","42274","42275","42276","42277","42278","42279","42280","42281","42282","42283","42284","42285","42286","42287","42288","42289","42290","42291","42292","42293","42294","42295","42296","42297","42298","42299","42300","42301","42302","42303","42304","42305","42306","42307","42308","42309","42310","42311","42312","42313","42314","42315","42316","42317","42318","42319","42320","42321","42322","42323","42324","42325","42326","42327","42328","42329","42330","42331","42332","42333","42334","42335","42336","42337","42338","42339","42340","42341","42342","42343","42344","42345","42346","42347","42348","42349","42350","42351","42352","42353","42354","42355","42356","42357","42358","42359","42360","42361","42362","42363","42364","42365","42366","42367","42368","42369","42370","42371","42372","42373","42374","42375","42376","42377","42378","42379","42380","42381","42382","42383","42384","42385","42386","42387","42388","42389","42390","42391","42392","42393","42394","42395","42396","42397","42398","42399","42400","42401","42402","42403","42404","42405","42406","42407","42408","42409","42410","42411","42412","42413","42414","42415","42416","42417","42418","42419","42420","42421","42422","42423","42424","42425","42426","42427","42428","42429","42430","42431","42432","42433","42434","42435","42436","42437","42438","42439","42440","42441","42442","42443","42444","42445","42446","42447","42448","42449","42450","42451","42452","42453","42454","42455","42456","42457","42458","42459","42460","42461","42462","42463","42464","42465","42466","42467","42468","42469","42470","42471","42472","42473","42474","42475","42476","42477","42478","42479","42480","42481","42482","42483","42484","42485","42486","42487","42488","42489","42490","42491","42492","42493","42494","42495","42496","42497","42498","42499","42500","42501","42502","42503","42504","42505","42506","42507","42508","42509","42510","42511","42512","42513","42514","42515","42516","42517","42518","42519","42520","42521","42522","42523","42524","42525","42526","42527","42528","42529","42530","42531","42532","42533","42534","42535","42536","42537","42538","42539","42540","42541","42542","42543","42544","42545","42546","42547","42548","42549","42550","42551","42552","42553","42554","42555","42556","42557","42558","42559","42560","42561","42562","42563","42564","42565","42566","42567","42568","42569","42570","42571","42572","42573","42574","42575","42576","42577","42578","42579","42580","42581","42582","42583","42584","42585","42586","42587","42588","42589","42590","42591","42592","42593","42594","42595","42596","42597","42598","42599","42600","42601","42602","42603","42604","42605","42606","42607","42608","42609","42610","42611","42612","42613","42614","42615","42616","42617","42618","42619","42620","42621","42622","42623","42624","42625","42626","42627","42628","42629","42630","42631","42632","42633","42634","42635","42636","42637","42638","42639","42640","42641","42642","42643","42644","42645","42646","42647","42648","42649","42650","42651","42652","42653","42654","42655","42656","42657","42658","42659","42660","42661","42662","42663","42664","42665","42666","42667","42668","42669","42670","42671","42672","42673","42674","42675","42676","42677","42678","42679","42680","42681","42682","42683","42684","42685","42686","42687","42688","42689","42690","42691","42692","42693","42694","42695","42696","42697","42698","42699","42700","42701","42702","42703","42704","42705","42706","42707","42708","42709","42710","42711","42712","42713","42714","42715","42716","42717","42718","42719","42720","42721","42722","42723","42724","42725","42726","42727","42728","42729","42730","42731","42732","42733","42734","42735","42736","42737","42738","42739","42740","42741","42742","42743","42744","42745","42746","42747","42748","42749","42750","42751","42752","42753","42754","42755","42756","42757","42758","42759","42760","42761","42762","42763","42764","42765","42766","42767","42768","42769","42770","42771","42772","42773","42774","42775","42776","42777","42778","42779","42780","42781","42782","42783","42784","42785","42786","42787","42788","42789","42790","42791","42792","42793","42794","42795","42796","42797","42798","42799","42800","42801","42802","42803","42804","42805","42806","42807","42808","42809","42810","42811","42812","42813","42814","42815","42816","42817","42818","42819","42820","42821","42822","42823","42824","42825","42826","42827","42828","42829","42830","42831","42832","42833","42834","42835","42836","42837","42838","42839","42840","42841","42842","42843","42844","42845","42846","42847","42848","42849","42850","42851","42852","42853","42854","42855","42856","42857","42858","42859","42860","42861","42862","42863","42864","42865","42866","42867","42868","42869","42870","42871","42872","42873","42874","42875","42876","42877","42878","42879","42880","42881","42882","42883","42884","42885","42886","42887","42888","42889","42890","42891","42892","42893","42894","42895","42896","42897","42898","42899","42900","42901","42902","42903","42904","42905","42906","42907","42908","42909","42910","42911","42912","42913","42914","42915","42916","42917","42918","42919","42920","42921","42922","42923","42924","42925","42926","42927","42928","42929","42930","42931","42932","42933","42934","42935","42936","42937","42938","42939","42940","42941","42942","42943","42944","42945","42946","42947","42948","42949","42950","42951","42952","42953","42954","42955","42956","42957","42958","42959","42960","42961","42962","42963","42964","42965","42966","42967","42968","42969","42970","42971","42972","42973","42974","42975","42976","42977","42978","42979","42980","42981","42982","42983","42984","42985","42986","42987","42988","42989","42990","42991","42992","42993","42994","42995","42996","42997","42998","42999","43000","43001","43002","43003","43004","43005","43006","43007","43008","43009","43010","43011","43012","43013","43014","43015","43016","43017","43018","43019","43020","43021","43022","43023","43024","43025","43026","43027","43028","43029","43030","43031","43032","43033","43034","43035","43036","43037","43038","43039","43040","43041","43042","43043","43044","43045","43046","43047","43048","43049","43050","43051","43052","43053","43054","43055","43056","43057","43058","43059","43060","43061","43062","43063","43064","43065","43066","43067","43068","43069","43070","43071","43072","43073","43074","43075","43076","43077","43078","43079","43080","43081","43082","43083","43084","43085","43086","43087","43088","43089","43090","43091","43092","43093","43094","43095","43096","43097","43098","43099","43100","43101","43102","43103","43104","43105","43106","43107","43108","43109","43110","43111","43112","43113","43114","43115","43116","43117","43118","43119","43120","43121","43122","43123","43124","43125","43126","43127","43128","43129","43130","43131","43132","43133","43134","43135","43136","43137","43138","43139","43140","43141","43142","43143","43144","43145","43146","43147","43148","43149","43150","43151","43152","43153","43154","43155","43156","43157","43158","43159","43160","43161","43162","43163","43164","43165","43166","43167","43168","43169","43170","43171","43172","43173","43174","43175","43176","43177","43178","43179","43180","43181","43182","43183","43184","43185","43186","43187","43188","43189","43190","43191","43192","43193","43194","43195","43196","43197","43198","43199","43200","43201","43202","43203","43204","43205","43206","43207","43208","43209","43210","43211","43212","43213","43214","43215","43216","43217","43218","43219","43220","43221","43222","43223","43224","43225","43226","43227","43228","43229","43230","43231","43232","43233","43234","43235","43236","43237","43238","43239","43240","43241","43242","43243","43244","43245","43246","43247","43248","43249","43250","43251","43252","43253","43254","43255","43256","43257","43258","43259","43260","43261","43262","43263","43264","43265","43266","43267","43268","43269","43270","43271","43272","43273","43274","43275","43276","43277","43278","43279","43280","43281","43282","43283","43284","43285","43286","43287","43288","43289","43290","43291","43292","43293","43294","43295","43296","43297","43298","43299","43300","43301","43302","43303","43304","43305","43306","43307","43308","43309","43310","43311","43312","43313","43314","43315","43316","43317","43318","43319","43320","43321","43322","43323","43324","43325","43326","43327","43328","43329","43330","43331","43332","43333","43334","43335","43336","43337","43338","43339","43340","43341","43342","43343","43344","43345","43346","43347","43348","43349","43350","43351","43352","43353","43354","43355","43356","43357","43358","43359","43360","43361","43362","43363","43364","43365","43366","43367","43368","43369","43370","43371","43372","43373","43374","43375","43376","43377","43378","43379","43380","43381","43382","43383","43384","43385","43386","43387","43388","43389","43390","43391","43392","43393","43394","43395","43396","43397","43398","43399","43400","43401","43402","43403","43404","43405","43406","43407","43408","43409","43410","43411","43412","43413","43414","43415","43416","43417","43418","43419","43420","43421","43422","43423","43424","43425","43426","43427","43428","43429","43430","43431","43432","43433","43434","43435","43436","43437","43438","43439","43440","43441","43442","43443","43444","43445","43446","43447","43448","43449","43450","43451","43452","43453","43454","43455","43456","43457","43458","43459","43460","43461","43462","43463","43464","43465","43466","43467","43468","43469","43470","43471","43472","43473","43474","43475","43476","43477","43478","43479","43480","43481","43482","43483","43484","43485","43486","43487","43488","43489","43490","43491","43492","43493","43494","43495","43496","43497","43498","43499","43500","43501","43502","43503","43504","43505","43506","43507","43508","43509","43510","43511","43512","43513","43514","43515","43516","43517","43518","43519","43520","43521","43522","43523","43524","43525","43526","43527","43528","43529","43530","43531","43532","43533","43534","43535","43536","43537","43538","43539","43540","43541","43542","43543","43544","43545","43546","43547","43548","43549","43550","43551","43552","43553","43554","43555","43556","43557","43558","43559","43560","43561","43562","43563","43564","43565","43566","43567","43568","43569","43570","43571","43572","43573","43574","43575","43576","43577","43578","43579","43580","43581","43582","43583","43584","43585","43586","43587","43588","43589","43590","43591","43592","43593","43594","43595","43596","43597","43598","43599","43600","43601","43602","43603","43604","43605","43606","43607","43608","43609","43610","43611","43612","43613","43614","43615","43616","43617","43618","43619","43620","43621","43622","43623","43624","43625","43626","43627","43628","43629","43630","43631","43632","43633","43634","43635","43636","43637","43638","43639","43640","43641","43642","43643","43644","43645","43646","43647","43648","43649","43650","43651","43652","43653","43654","43655","43656","43657","43658","43659","43660","43661","43662","43663","43664","43665","43666","43667","43668","43669","43670","43671","43672","43673","43674","43675","43676","43677","43678","43679","43680","43681","43682","43683","43684","43685","43686","43687","43688","43689","43690","43691","43692","43693","43694","43695","43696","43697","43698","43699","43700","43701","43702","43703","43704","43705","43706","43707","43708","43709","43710","43711","43712","43713","43714","43715","43716","43717","43718","43719","43720","43721","43722","43723","43724","43725","43726","43727","43728","43729","43730","43731","43732","43733","43734","43735","43736","43737","43738","43739","43740","43741","43742","43743","43744","43745","43746","43747","43748","43749","43750","43751","43752","43753","43754","43755","43756","43757","43758","43759","43760","43761","43762","43763","43764","43765","43766","43767","43768","43769","43770","43771","43772","43773","43774","43775","43776","43777","43778","43779","43780","43781","43782","43783","43784","43785","43786","43787","43788","43789","43790","43791","43792","43793","43794","43795","43796","43797","43798","43799","43800","43801","43802","43803","43804","43805","43806","43807","43808","43809","43810","43811","43812","43813","43814","43815","43816","43817","43818","43819","43820","43821","43822","43823","43824","43825","43826","43827","43828","43829","43830","43831","43832","43833","43834","43835","43836","43837","43838","43839","43840","43841","43842","43843","43844","43845","43846","43847","43848","43849","43850","43851","43852","43853","43854","43855","43856","43857","43858","43859","43860","43861","43862","43863","43864","43865","43866","43867","43868","43869","43870","43871","43872","43873","43874","43875","43876","43877","43878","43879","43880","43881","43882","43883","43884","43885","43886","43887","43888","43889","43890","43891","43892","43893","43894","43895","43896","43897","43898","43899","43900","43901","43902","43903","43904","43905","43906","43907","43908","43909","43910","43911","43912","43913","43914","43915","43916","43917","43918","43919","43920","43921","43922","43923","43924","43925","43926","43927","43928","43929","43930","43931","43932","43933","43934","43935","43936","43937","43938","43939","43940","43941","43942","43943","43944","43945","43946","43947","43948","43949","43950","43951","43952","43953","43954","43955","43956","43957","43958","43959","43960","43961","43962","43963","43964","43965","43966","43967","43968","43969","43970","43971","43972","43973","43974","43975","43976","43977","43978","43979","43980","43981","43982","43983","43984","43985","43986","43987","43988","43989","43990","43991","43992","43993","43994","43995","43996","43997","43998","43999","44000","44001","44002","44003","44004","44005","44006","44007","44008","44009","44010","44011","44012","44013","44014","44015","44016","44017","44018","44019","44020","44021","44022","44023","44024","44025","44026","44027","44028","44029","44030","44031","44032","44033","44034","44035","44036","44037","44038","44039","44040","44041","44042","44043","44044","44045","44046","44047","44048","44049","44050","44051","44052","44053","44054","44055","44056","44057","44058","44059","44060","44061","44062","44063","44064","44065","44066","44067","44068","44069","44070","44071","44072","44073","44074","44075","44076","44077","44078","44079","44080","44081","44082","44083","44084","44085","44086","44087","44088","44089","44090","44091","44092","44093","44094","44095","44096","44097","44098","44099","44100","44101","44102","44103","44104","44105","44106","44107","44108","44109","44110","44111","44112","44113","44114","44115","44116","44117","44118","44119","44120","44121","44122","44123","44124","44125","44126","44127","44128","44129","44130","44131","44132","44133","44134","44135","44136","44137","44138","44139","44140","44141","44142","44143","44144","44145","44146","44147","44148","44149","44150","44151","44152","44153","44154","44155","44156","44157","44158","44159","44160","44161","44162","44163","44164","44165","44166","44167","44168","44169","44170","44171","44172","44173","44174","44175","44176","44177","44178","44179","44180","44181","44182","44183","44184","44185","44186","44187","44188","44189","44190","44191","44192","44193","44194","44195","44196","44197","44198","44199","44200","44201","44202","44203","44204","44205","44206","44207","44208","44209","44210","44211","44212","44213","44214","44215","44216","44217","44218","44219","44220","44221","44222","44223","44224","44225","44226","44227","44228","44229","44230","44231","44232","44233","44234","44235","44236","44237","44238","44239","44240","44241","44242","44243","44244","44245","44246","44247","44248","44249","44250","44251","44252","44253","44254","44255","44256","44257","44258","44259","44260","44261","44262","44263","44264","44265","44266","44267","44268","44269","44270","44271","44272","44273","44274","44275","44276","44277","44278","44279","44280","44281","44282","44283","44284","44285","44286","44287","44288","44289","44290","44291","44292","44293","44294","44295","44296","44297","44298","44299","44300","44301","44302","44303","44304","44305","44306","44307","44308","44309","44310","44311","44312","44313","44314","44315","44316","44317","44318","44319","44320","44321","44322","44323","44324","44325","44326","44327","44328","44329","44330","44331","44332","44333","44334","44335","44336","44337","44338","44339","44340","44341","44342","44343","44344","44345","44346","44347","44348","44349","44350","44351","44352","44353","44354","44355","44356","44357","44358","44359","44360","44361","44362","44363","44364","44365","44366","44367","44368","44369","44370","44371","44372","44373","44374","44375","44376","44377","44378","44379","44380","44381","44382","44383","44384","44385","44386","44387","44388","44389","44390","44391","44392","44393","44394","44395","44396","44397","44398","44399","44400","44401","44402","44403","44404","44405","44406","44407","44408","44409","44410","44411","44412","44413","44414","44415","44416","44417","44418","44419","44420","44421","44422","44423","44424","44425","44426","44427","44428","44429","44430","44431","44432","44433","44434","44435","44436","44437","44438","44439","44440","44441","44442","44443","44444","44445","44446","44447","44448","44449","44450","44451","44452","44453","44454","44455","44456","44457","44458","44459","44460","44461","44462","44463","44464","44465","44466","44467","44468","44469","44470","44471","44472","44473","44474","44475","44476","44477","44478","44479","44480","44481","44482","44483","44484","44485","44486","44487","44488","44489","44490","44491","44492","44493","44494","44495","44496","44497","44498","44499","44500","44501","44502","44503","44504","44505","44506","44507","44508","44509","44510","44511","44512","44513","44514","44515","44516","44517","44518","44519","44520","44521","44522","44523","44524","44525","44526","44527","44528","44529","44530","44531","44532","44533","44534","44535","44536","44537","44538","44539","44540","44541","44542","44543","44544","44545","44546","44547","44548","44549","44550","44551","44552","44553","44554","44555","44556","44557","44558","44559","44560","44561","44562","44563","44564","44565","44566","44567","44568","44569","44570","44571","44572","44573","44574","44575","44576","44577","44578","44579","44580","44581","44582","44583","44584","44585","44586","44587","44588","44589","44590","44591","44592","44593","44594","44595","44596","44597","44598","44599","44600","44601","44602","44603","44604","44605","44606","44607","44608","44609","44610","44611","44612","44613","44614","44615","44616","44617","44618","44619","44620","44621","44622","44623","44624","44625","44626","44627","44628","44629","44630","44631","44632","44633","44634","44635","44636","44637","44638","44639","44640","44641","44642","44643","44644","44645","44646","44647","44648","44649","44650","44651","44652","44653","44654","44655","44656","44657","44658","44659","44660","44661","44662","44663","44664","44665","44666","44667","44668","44669","44670","44671","44672","44673","44674","44675","44676","44677","44678","44679","44680","44681","44682","44683","44684","44685","44686","44687","44688","44689","44690","44691","44692","44693","44694","44695","44696","44697","44698","44699","44700","44701","44702","44703","44704","44705","44706","44707","44708","44709","44710","44711","44712","44713","44714","44715","44716","44717","44718","44719","44720","44721","44722","44723","44724","44725","44726","44727","44728","44729","44730","44731","44732","44733","44734","44735","44736","44737","44738","44739","44740","44741","44742","44743","44744","44745","44746","44747","44748","44749","44750","44751","44752","44753","44754","44755","44756","44757","44758","44759","44760","44761","44762","44763","44764","44765","44766","44767","44768","44769","44770","44771","44772","44773","44774","44775","44776","44777","44778","44779","44780","44781","44782","44783","44784","44785","44786","44787","44788","44789","44790","44791","44792","44793","44794","44795","44796","44797","44798","44799","44800","44801","44802","44803","44804","44805","44806","44807","44808","44809","44810","44811","44812","44813","44814","44815","44816","44817","44818","44819","44820","44821","44822","44823","44824","44825","44826","44827","44828","44829","44830","44831","44832","44833","44834","44835","44836","44837","44838","44839","44840","44841","44842","44843","44844","44845","44846","44847","44848","44849","44850","44851","44852","44853","44854","44855","44856","44857","44858","44859","44860","44861","44862","44863","44864","44865","44866","44867","44868","44869","44870","44871","44872","44873","44874","44875","44876","44877","44878","44879","44880","44881","44882","44883","44884","44885","44886","44887","44888","44889","44890","44891","44892","44893","44894","44895","44896","44897","44898","44899","44900","44901","44902","44903","44904","44905","44906","44907","44908","44909","44910","44911","44912","44913","44914","44915","44916","44917","44918","44919","44920","44921","44922","44923","44924","44925","44926","44927","44928","44929","44930","44931","44932","44933","44934","44935","44936","44937","44938","44939","44940","44941","44942","44943","44944","44945","44946","44947","44948","44949","44950","44951","44952","44953","44954","44955","44956","44957","44958","44959","44960","44961","44962","44963","44964","44965","44966","44967","44968","44969","44970","44971","44972","44973","44974","44975","44976","44977","44978","44979","44980","44981","44982","44983","44984","44985","44986","44987","44988","44989","44990","44991","44992","44993","44994","44995","44996","44997","44998","44999","45000","45001","45002","45003","45004","45005","45006","45007","45008","45009","45010","45011","45012","45013","45014","45015","45016","45017","45018","45019","45020","45021","45022","45023","45024","45025","45026","45027","45028","45029","45030","45031","45032","45033","45034","45035","45036","45037","45038","45039","45040","45041","45042","45043","45044","45045","45046","45047","45048","45049","45050","45051","45052","45053","45054","45055","45056","45057","45058","45059","45060","45061","45062","45063","45064","45065","45066","45067","45068","45069","45070","45071","45072","45073","45074","45075","45076","45077","45078","45079","45080","45081","45082","45083","45084","45085","45086","45087","45088","45089","45090","45091","45092","45093","45094","45095","45096","45097","45098","45099","45100","45101","45102","45103","45104","45105","45106","45107","45108","45109","45110","45111","45112","45113","45114","45115","45116","45117","45118","45119","45120","45121","45122","45123","45124","45125","45126","45127","45128","45129","45130","45131","45132","45133","45134","45135","45136","45137","45138","45139","45140","45141","45142","45143","45144","45145","45146","45147","45148","45149","45150","45151","45152","45153","45154","45155","45156","45157","45158","45159","45160","45161","45162","45163","45164","45165","45166","45167","45168","45169","45170","45171","45172","45173","45174","45175","45176","45177","45178","45179","45180","45181","45182","45183","45184","45185","45186","45187","45188","45189","45190","45191","45192","45193","45194","45195","45196","45197","45198","45199","45200","45201","45202","45203","45204","45205","45206","45207","45208","45209","45210","45211","45212","45213","45214","45215","45216","45217","45218","45219","45220","45221","45222","45223","45224","45225","45226","45227","45228","45229","45230","45231","45232","45233","45234","45235","45236","45237","45238","45239","45240","45241","45242","45243","45244","45245","45246","45247","45248","45249","45250","45251","45252","45253","45254","45255","45256","45257","45258","45259","45260","45261","45262","45263","45264","45265","45266","45267","45268","45269","45270","45271","45272","45273","45274","45275","45276","45277","45278","45279","45280","45281","45282","45283","45284","45285","45286","45287","45288","45289","45290","45291","45292","45293","45294","45295","45296","45297","45298","45299","45300","45301","45302","45303","45304","45305","45306","45307","45308","45309","45310","45311","45312","45313","45314","45315","45316","45317","45318","45319","45320","45321","45322","45323","45324","45325","45326","45327","45328","45329","45330","45331","45332","45333","45334","45335","45336","45337","45338","45339","45340","45341","45342","45343","45344","45345","45346","45347","45348","45349","45350","45351","45352","45353","45354","45355","45356","45357","45358","45359","45360","45361","45362","45363","45364","45365","45366","45367","45368","45369","45370","45371","45372","45373","45374","45375","45376","45377","45378","45379","45380","45381","45382","45383","45384","45385","45386","45387","45388","45389","45390","45391","45392","45393","45394","45395","45396","45397","45398","45399","45400","45401","45402","45403","45404","45405","45406","45407","45408","45409","45410","45411","45412","45413","45414","45415","45416","45417","45418","45419","45420","45421","45422","45423","45424","45425","45426","45427","45428","45429","45430","45431","45432","45433","45434","45435","45436","45437","45438","45439","45440","45441","45442","45443","45444","45445","45446","45447","45448","45449","45450","45451","45452","45453","45454","45455","45456","45457","45458","45459","45460","45461","45462","45463","45464","45465","45466","45467","45468","45469","45470","45471","45472","45473","45474","45475","45476","45477","45478","45479","45480","45481","45482","45483","45484","45485","45486","45487","45488","45489","45490","45491","45492","45493","45494","45495","45496","45497","45498","45499","45500","45501","45502","45503","45504","45505","45506","45507","45508","45509","45510","45511","45512","45513","45514","45515","45516","45517","45518","45519","45520","45521","45522","45523","45524","45525","45526","45527","45528","45529","45530","45531","45532","45533","45534","45535","45536","45537","45538","45539","45540","45541","45542","45543","45544","45545","45546","45547","45548","45549","45550","45551","45552","45553","45554","45555","45556","45557","45558","45559","45560","45561","45562","45563","45564","45565","45566","45567","45568","45569","45570","45571","45572","45573","45574","45575","45576","45577","45578","45579","45580","45581","45582","45583","45584","45585","45586","45587","45588","45589","45590","45591","45592","45593","45594","45595","45596","45597","45598","45599","45600","45601","45602","45603","45604","45605","45606","45607","45608","45609","45610","45611","45612","45613","45614","45615","45616","45617","45618","45619","45620","45621","45622","45623","45624","45625","45626","45627","45628","45629","45630","45631","45632","45633","45634","45635","45636","45637","45638","45639","45640","45641","45642","45643","45644","45645","45646","45647","45648","45649","45650","45651","45652","45653","45654","45655","45656","45657","45658","45659","45660","45661","45662","45663","45664","45665","45666","45667","45668","45669","45670","45671","45672","45673","45674","45675","45676","45677","45678","45679","45680","45681","45682","45683","45684","45685","45686","45687","45688","45689","45690","45691","45692","45693","45694","45695","45696","45697","45698","45699","45700","45701","45702","45703","45704","45705","45706","45707","45708","45709","45710","45711","45712","45713","45714","45715","45716","45717","45718","45719","45720","45721","45722","45723","45724","45725","45726","45727","45728","45729","45730","45731","45732","45733","45734","45735","45736","45737","45738","45739","45740","45741","45742","45743","45744","45745","45746","45747","45748","45749","45750","45751","45752","45753","45754","45755","45756","45757","45758","45759","45760","45761","45762","45763","45764","45765","45766","45767","45768","45769","45770","45771","45772","45773","45774","45775","45776","45777","45778","45779","45780","45781","45782","45783","45784","45785","45786","45787","45788","45789","45790","45791","45792","45793","45794","45795","45796","45797","45798","45799","45800","45801","45802","45803","45804","45805","45806","45807","45808","45809","45810","45811","45812","45813","45814","45815","45816","45817","45818","45819","45820","45821","45822","45823","45824","45825","45826","45827","45828","45829","45830","45831","45832","45833","45834","45835","45836","45837","45838","45839","45840","45841","45842","45843","45844","45845","45846","45847","45848","45849","45850","45851","45852","45853","45854","45855","45856","45857","45858","45859","45860","45861","45862","45863","45864","45865","45866","45867","45868","45869","45870","45871","45872","45873","45874","45875","45876","45877","45878","45879","45880","45881","45882","45883","45884","45885","45886","45887","45888","45889","45890","45891","45892","45893","45894","45895","45896","45897","45898","45899","45900","45901","45902","45903","45904","45905","45906","45907","45908","45909","45910","45911","45912","45913","45914","45915","45916","45917","45918","45919","45920","45921","45922","45923","45924","45925","45926","45927","45928","45929","45930","45931","45932","45933","45934","45935","45936","45937","45938","45939","45940","45941","45942","45943","45944","45945","45946","45947","45948","45949","45950","45951","45952","45953","45954","45955","45956","45957","45958","45959","45960","45961","45962","45963","45964","45965","45966","45967","45968","45969","45970","45971","45972","45973","45974","45975","45976","45977","45978","45979","45980","45981","45982","45983","45984","45985","45986","45987","45988","45989","45990","45991","45992","45993","45994","45995","45996","45997","45998","45999","46000","46001","46002","46003","46004","46005","46006","46007","46008","46009","46010","46011","46012","46013","46014","46015","46016","46017","46018","46019","46020","46021","46022","46023","46024","46025","46026","46027","46028","46029","46030","46031","46032","46033","46034","46035","46036","46037","46038","46039","46040","46041","46042","46043","46044","46045","46046","46047","46048","46049","46050","46051","46052","46053","46054","46055","46056","46057","46058","46059","46060","46061","46062","46063","46064","46065","46066","46067","46068","46069","46070","46071","46072","46073","46074","46075","46076","46077","46078","46079","46080","46081","46082","46083","46084","46085","46086","46087","46088","46089","46090","46091","46092","46093","46094","46095","46096","46097","46098","46099","46100","46101","46102","46103","46104","46105","46106","46107","46108","46109","46110","46111","46112","46113","46114","46115","46116","46117","46118","46119","46120","46121","46122","46123","46124","46125","46126","46127","46128","46129","46130","46131","46132","46133","46134","46135","46136","46137","46138","46139","46140","46141","46142","46143","46144","46145","46146","46147","46148","46149","46150","46151","46152","46153","46154","46155","46156","46157","46158","46159","46160","46161","46162","46163","46164","46165","46166","46167","46168","46169","46170","46171","46172","46173","46174","46175","46176","46177","46178","46179","46180","46181","46182","46183","46184","46185","46186","46187","46188","46189","46190","46191","46192","46193","46194","46195","46196","46197","46198","46199","46200","46201","46202","46203","46204","46205","46206","46207","46208","46209","46210","46211","46212","46213","46214","46215","46216","46217","46218","46219","46220","46221","46222","46223","46224","46225","46226","46227","46228","46229","46230","46231","46232","46233","46234","46235","46236","46237","46238","46239","46240","46241","46242","46243","46244","46245","46246","46247","46248","46249","46250","46251","46252","46253","46254","46255","46256","46257","46258","46259","46260","46261","46262","46263","46264","46265","46266","46267","46268","46269","46270","46271","46272","46273","46274","46275","46276","46277","46278","46279","46280","46281","46282","46283","46284","46285","46286","46287","46288","46289","46290","46291","46292","46293","46294","46295","46296","46297","46298","46299","46300","46301","46302","46303","46304","46305","46306","46307","46308","46309","46310","46311","46312","46313","46314","46315","46316","46317","46318","46319","46320","46321","46322","46323","46324","46325","46326","46327","46328","46329","46330","46331","46332","46333","46334","46335","46336","46337","46338","46339","46340","46341","46342","46343","46344","46345","46346","46347","46348","46349","46350","46351","46352","46353","46354","46355","46356","46357","46358","46359","46360","46361","46362","46363","46364","46365","46366","46367","46368","46369","46370","46371","46372","46373","46374","46375","46376","46377","46378","46379","46380","46381","46382","46383","46384","46385","46386","46387","46388","46389","46390","46391","46392","46393","46394","46395","46396","46397","46398","46399","46400","46401","46402","46403","46404","46405","46406","46407","46408","46409","46410","46411","46412","46413","46414","46415","46416","46417","46418","46419","46420","46421","46422","46423","46424","46425","46426","46427","46428","46429","46430","46431","46432","46433","46434","46435","46436","46437","46438","46439","46440","46441","46442","46443","46444","46445","46446","46447","46448","46449","46450","46451","46452","46453","46454","46455","46456","46457","46458","46459","46460","46461","46462","46463","46464","46465","46466","46467","46468","46469","46470","46471","46472","46473","46474","46475","46476","46477","46478","46479","46480","46481","46482","46483","46484","46485","46486","46487","46488","46489","46490","46491","46492","46493","46494","46495","46496","46497","46498","46499","46500","46501","46502","46503","46504","46505","46506","46507","46508","46509","46510","46511","46512","46513","46514","46515","46516","46517","46518","46519","46520","46521","46522","46523","46524","46525","46526","46527","46528","46529","46530","46531","46532","46533","46534","46535","46536","46537","46538","46539","46540","46541","46542","46543","46544","46545","46546","46547","46548","46549","46550","46551","46552","46553","46554","46555","46556","46557","46558","46559","46560","46561","46562","46563","46564","46565","46566","46567","46568","46569","46570","46571","46572","46573","46574","46575","46576","46577","46578","46579","46580","46581","46582","46583","46584","46585","46586","46587","46588","46589","46590","46591","46592","46593","46594","46595","46596","46597","46598","46599","46600","46601","46602","46603","46604","46605","46606","46607","46608","46609","46610","46611","46612","46613","46614","46615","46616","46617","46618","46619","46620","46621","46622","46623","46624","46625","46626","46627","46628","46629","46630","46631","46632","46633","46634","46635","46636","46637","46638","46639","46640","46641","46642","46643","46644","46645","46646","46647","46648","46649","46650","46651","46652","46653","46654","46655","46656","46657","46658","46659","46660","46661","46662","46663","46664","46665","46666","46667","46668","46669","46670","46671","46672","46673","46674","46675","46676","46677","46678","46679","46680","46681","46682","46683","46684","46685","46686","46687","46688","46689","46690","46691","46692","46693","46694","46695","46696","46697","46698","46699","46700","46701","46702","46703","46704","46705","46706","46707","46708","46709","46710","46711","46712","46713","46714","46715","46716","46717","46718","46719","46720","46721","46722","46723","46724","46725","46726","46727","46728","46729","46730","46731","46732","46733","46734","46735","46736","46737","46738","46739","46740","46741","46742","46743","46744","46745","46746","46747","46748","46749","46750","46751","46752","46753","46754","46755","46756","46757","46758","46759","46760","46761","46762","46763","46764","46765","46766","46767","46768","46769","46770","46771","46772","46773","46774","46775","46776","46777","46778","46779","46780","46781","46782","46783","46784","46785","46786","46787","46788","46789","46790","46791","46792","46793","46794","46795","46796","46797","46798","46799","46800","46801","46802","46803","46804","46805","46806","46807","46808","46809","46810","46811","46812","46813","46814","46815","46816","46817","46818","46819","46820","46821","46822","46823","46824","46825","46826","46827","46828","46829","46830","46831","46832","46833","46834","46835","46836","46837","46838","46839","46840","46841","46842","46843","46844","46845","46846","46847","46848","46849","46850","46851","46852","46853","46854","46855","46856","46857","46858","46859","46860","46861","46862","46863","46864","46865","46866","46867","46868","46869","46870","46871","46872","46873","46874","46875","46876","46877","46878","46879","46880","46881","46882","46883","46884","46885","46886","46887","46888","46889","46890","46891","46892","46893","46894","46895","46896","46897","46898","46899","46900","46901","46902","46903","46904","46905","46906","46907","46908","46909","46910","46911","46912","46913","46914","46915","46916","46917","46918","46919","46920","46921","46922","46923","46924","46925","46926","46927","46928","46929","46930","46931","46932","46933","46934","46935","46936","46937","46938","46939","46940","46941","46942","46943","46944","46945","46946","46947","46948","46949","46950","46951","46952","46953","46954","46955","46956","46957","46958","46959","46960","46961","46962","46963","46964","46965","46966","46967","46968","46969","46970","46971","46972","46973","46974","46975","46976","46977","46978","46979","46980","46981","46982","46983","46984","46985","46986","46987","46988","46989","46990","46991","46992","46993","46994","46995","46996","46997","46998","46999","47000","47001","47002","47003","47004","47005","47006","47007","47008","47009","47010","47011","47012","47013","47014","47015","47016","47017","47018","47019","47020","47021","47022","47023","47024","47025","47026","47027","47028","47029","47030","47031","47032","47033","47034","47035","47036","47037","47038","47039","47040","47041","47042","47043","47044","47045","47046","47047","47048","47049","47050","47051","47052","47053","47054","47055","47056","47057","47058","47059","47060","47061","47062","47063","47064","47065","47066","47067","47068","47069","47070","47071","47072","47073","47074","47075","47076","47077","47078","47079","47080","47081","47082","47083","47084","47085","47086","47087","47088","47089","47090","47091","47092","47093","47094","47095","47096","47097","47098","47099","47100","47101","47102","47103","47104","47105","47106","47107","47108","47109","47110","47111","47112","47113","47114","47115","47116","47117","47118","47119","47120","47121","47122","47123","47124","47125","47126","47127","47128","47129","47130","47131","47132","47133","47134","47135","47136","47137","47138","47139","47140","47141","47142","47143","47144","47145","47146","47147","47148","47149","47150","47151","47152","47153","47154","47155","47156","47157","47158","47159","47160","47161","47162","47163","47164","47165","47166","47167","47168","47169","47170","47171","47172","47173","47174","47175","47176","47177","47178","47179","47180","47181","47182","47183","47184","47185","47186","47187","47188","47189","47190","47191","47192","47193","47194","47195","47196","47197","47198","47199","47200","47201","47202","47203","47204","47205","47206","47207","47208","47209","47210","47211","47212","47213","47214","47215","47216","47217","47218","47219","47220","47221","47222","47223","47224","47225","47226","47227","47228","47229","47230","47231","47232","47233","47234","47235","47236","47237","47238","47239","47240","47241","47242","47243","47244","47245","47246","47247","47248","47249","47250","47251","47252","47253","47254","47255","47256","47257","47258","47259","47260","47261","47262","47263","47264","47265","47266","47267","47268","47269","47270","47271","47272","47273","47274","47275","47276","47277","47278","47279","47280","47281","47282","47283","47284","47285","47286","47287","47288","47289","47290","47291","47292","47293","47294","47295","47296","47297","47298","47299","47300","47301","47302","47303","47304","47305","47306","47307","47308","47309","47310","47311","47312","47313","47314","47315","47316","47317","47318","47319","47320","47321","47322","47323","47324","47325","47326","47327","47328","47329","47330","47331","47332","47333","47334","47335","47336","47337","47338","47339","47340","47341","47342","47343","47344","47345","47346","47347","47348","47349","47350","47351","47352","47353","47354","47355","47356","47357","47358","47359","47360","47361","47362","47363","47364","47365","47366","47367","47368","47369","47370","47371","47372","47373","47374","47375","47376","47377","47378","47379","47380","47381","47382","47383","47384","47385","47386","47387","47388","47389","47390","47391","47392","47393","47394","47395","47396","47397","47398","47399","47400","47401","47402","47403","47404","47405","47406","47407","47408","47409","47410","47411","47412","47413","47414","47415","47416","47417","47418","47419","47420","47421","47422","47423","47424","47425","47426","47427","47428","47429","47430","47431","47432","47433","47434","47435","47436","47437","47438","47439","47440","47441","47442","47443","47444","47445","47446","47447","47448","47449","47450","47451","47452","47453","47454","47455","47456","47457","47458","47459","47460","47461","47462","47463","47464","47465","47466","47467","47468","47469","47470","47471","47472","47473","47474","47475","47476","47477","47478","47479","47480","47481","47482","47483","47484","47485","47486","47487","47488","47489","47490","47491","47492","47493","47494","47495","47496","47497","47498","47499","47500","47501","47502","47503","47504","47505","47506","47507","47508","47509","47510","47511","47512","47513","47514","47515","47516","47517","47518","47519","47520","47521","47522","47523","47524","47525","47526","47527","47528","47529","47530","47531","47532","47533","47534","47535","47536","47537","47538","47539","47540","47541","47542","47543","47544","47545","47546","47547","47548","47549","47550","47551","47552","47553","47554","47555","47556","47557","47558","47559","47560","47561","47562","47563","47564","47565","47566","47567","47568","47569","47570","47571","47572","47573","47574","47575","47576","47577","47578","47579","47580","47581","47582","47583","47584","47585","47586","47587","47588","47589","47590","47591","47592","47593","47594","47595","47596","47597","47598","47599","47600","47601","47602","47603","47604","47605","47606","47607","47608","47609","47610","47611","47612","47613","47614","47615","47616","47617","47618","47619","47620","47621","47622","47623","47624","47625","47626","47627","47628","47629","47630","47631","47632","47633","47634","47635","47636","47637","47638","47639","47640","47641","47642","47643","47644","47645","47646","47647","47648","47649","47650","47651","47652","47653","47654","47655","47656","47657","47658","47659","47660","47661","47662","47663","47664","47665","47666","47667","47668","47669","47670","47671","47672","47673","47674","47675","47676","47677","47678","47679","47680","47681","47682","47683","47684","47685","47686","47687","47688","47689","47690","47691","47692","47693","47694","47695","47696","47697","47698","47699","47700","47701","47702","47703","47704","47705","47706","47707","47708","47709","47710","47711","47712","47713","47714","47715","47716","47717","47718","47719","47720","47721","47722","47723","47724","47725","47726","47727","47728","47729","47730","47731","47732","47733","47734","47735","47736","47737","47738","47739","47740","47741","47742","47743","47744","47745","47746","47747","47748","47749","47750","47751","47752","47753","47754","47755","47756","47757","47758","47759","47760","47761","47762","47763","47764","47765","47766","47767","47768","47769","47770","47771","47772","47773","47774","47775","47776","47777","47778","47779","47780","47781","47782","47783","47784","47785","47786","47787","47788","47789","47790","47791","47792","47793","47794","47795","47796","47797","47798","47799","47800","47801","47802","47803","47804","47805","47806","47807","47808","47809","47810","47811","47812","47813","47814","47815","47816","47817","47818","47819","47820","47821","47822","47823","47824","47825","47826","47827","47828","47829","47830","47831","47832","47833","47834","47835","47836","47837","47838","47839","47840","47841","47842","47843","47844","47845","47846","47847","47848","47849","47850","47851","47852","47853","47854","47855","47856","47857","47858","47859","47860","47861","47862","47863","47864","47865","47866","47867","47868","47869","47870","47871","47872","47873","47874","47875","47876","47877","47878","47879","47880","47881","47882","47883","47884","47885","47886","47887","47888","47889","47890","47891","47892","47893","47894","47895","47896","47897","47898","47899","47900","47901","47902","47903","47904","47905","47906","47907","47908","47909","47910","47911","47912","47913","47914","47915","47916","47917","47918","47919","47920","47921","47922","47923","47924","47925","47926","47927","47928","47929","47930","47931","47932","47933","47934","47935","47936","47937","47938","47939","47940","47941","47942","47943","47944","47945","47946","47947","47948","47949","47950","47951","47952","47953","47954","47955","47956","47957","47958","47959","47960","47961","47962","47963","47964","47965","47966","47967","47968","47969","47970","47971","47972","47973","47974","47975","47976","47977","47978","47979","47980","47981","47982","47983","47984","47985","47986","47987","47988","47989","47990","47991","47992","47993","47994","47995","47996","47997","47998","47999","48000","48001","48002","48003","48004","48005","48006","48007","48008","48009","48010","48011","48012","48013","48014","48015","48016","48017","48018","48019","48020","48021","48022","48023","48024","48025","48026","48027","48028","48029","48030","48031","48032","48033","48034","48035","48036","48037","48038","48039","48040","48041","48042","48043","48044","48045","48046","48047","48048","48049","48050","48051","48052","48053","48054","48055","48056","48057","48058","48059","48060","48061","48062","48063","48064","48065","48066","48067","48068","48069","48070","48071","48072","48073","48074","48075","48076","48077","48078","48079","48080","48081","48082","48083","48084","48085","48086","48087","48088","48089","48090","48091","48092","48093","48094","48095","48096","48097","48098","48099","48100","48101","48102","48103","48104","48105","48106","48107","48108","48109","48110","48111","48112","48113","48114","48115","48116","48117","48118","48119","48120","48121","48122","48123","48124","48125","48126","48127","48128","48129","48130","48131","48132","48133","48134","48135","48136","48137","48138","48139","48140","48141","48142","48143","48144","48145","48146","48147","48148","48149","48150","48151","48152","48153","48154","48155","48156","48157","48158","48159","48160","48161","48162","48163","48164","48165","48166","48167","48168","48169","48170","48171","48172","48173","48174","48175","48176","48177","48178","48179","48180","48181","48182","48183","48184","48185","48186","48187","48188","48189","48190","48191","48192","48193","48194","48195","48196","48197","48198","48199","48200","48201","48202","48203","48204","48205","48206","48207","48208","48209","48210","48211","48212","48213","48214","48215","48216","48217","48218","48219","48220","48221","48222","48223","48224","48225","48226","48227","48228","48229","48230","48231","48232","48233","48234","48235","48236","48237","48238","48239","48240","48241","48242","48243","48244","48245","48246","48247","48248","48249","48250","48251","48252","48253","48254","48255","48256","48257","48258","48259","48260","48261","48262","48263","48264","48265","48266","48267","48268","48269","48270","48271","48272","48273","48274","48275","48276","48277","48278","48279","48280","48281","48282","48283","48284","48285","48286","48287","48288","48289","48290","48291","48292","48293","48294","48295","48296","48297","48298","48299","48300","48301","48302","48303","48304","48305","48306","48307","48308","48309","48310","48311","48312","48313","48314","48315","48316","48317","48318","48319","48320","48321","48322","48323","48324","48325","48326","48327","48328","48329","48330","48331","48332","48333","48334","48335","48336","48337","48338","48339","48340","48341","48342","48343","48344","48345","48346","48347","48348","48349","48350","48351","48352","48353","48354","48355","48356","48357","48358","48359","48360","48361","48362","48363","48364","48365","48366","48367","48368","48369","48370","48371","48372","48373","48374","48375","48376","48377","48378","48379","48380","48381","48382","48383","48384","48385","48386","48387","48388","48389","48390","48391","48392","48393","48394","48395","48396","48397","48398","48399","48400","48401","48402","48403","48404","48405","48406","48407","48408","48409","48410","48411","48412","48413","48414","48415","48416","48417","48418","48419","48420","48421","48422","48423","48424","48425","48426","48427","48428","48429","48430","48431","48432","48433","48434","48435","48436","48437","48438","48439","48440","48441","48442","48443","48444","48445","48446","48447","48448","48449","48450","48451","48452","48453","48454","48455","48456","48457","48458","48459","48460","48461","48462","48463","48464","48465","48466","48467","48468","48469","48470","48471","48472","48473","48474","48475","48476","48477","48478","48479","48480","48481","48482","48483","48484","48485","48486","48487","48488","48489","48490","48491","48492","48493","48494","48495","48496","48497","48498","48499","48500","48501","48502","48503","48504","48505","48506","48507","48508","48509","48510","48511","48512","48513","48514","48515","48516","48517","48518","48519","48520","48521","48522","48523","48524","48525","48526","48527","48528","48529","48530","48531","48532","48533","48534","48535","48536","48537","48538","48539","48540","48541","48542","48543","48544","48545","48546","48547","48548","48549","48550","48551","48552","48553","48554","48555","48556","48557","48558","48559","48560","48561","48562","48563","48564","48565","48566","48567","48568","48569","48570","48571","48572","48573","48574","48575","48576","48577","48578","48579","48580","48581","48582","48583","48584","48585","48586","48587","48588","48589","48590","48591","48592","48593","48594","48595","48596","48597","48598","48599","48600","48601","48602","48603","48604","48605","48606","48607","48608","48609","48610","48611","48612","48613","48614","48615","48616","48617","48618","48619","48620","48621","48622","48623","48624","48625","48626","48627","48628","48629","48630","48631","48632","48633","48634","48635","48636","48637","48638","48639","48640","48641","48642","48643","48644","48645","48646","48647","48648","48649","48650","48651","48652","48653","48654","48655","48656","48657","48658","48659","48660","48661","48662","48663","48664","48665","48666","48667","48668","48669","48670","48671","48672","48673","48674","48675","48676","48677","48678","48679","48680","48681","48682","48683","48684","48685","48686","48687","48688","48689","48690","48691","48692","48693","48694","48695","48696","48697","48698","48699","48700","48701","48702","48703","48704","48705","48706","48707","48708","48709","48710","48711","48712","48713","48714","48715","48716","48717","48718","48719","48720","48721","48722","48723","48724","48725","48726","48727","48728","48729","48730","48731","48732","48733","48734","48735","48736","48737","48738","48739","48740","48741","48742","48743","48744","48745","48746","48747","48748","48749","48750","48751","48752","48753","48754","48755","48756","48757","48758","48759","48760","48761","48762","48763","48764","48765","48766","48767","48768","48769","48770","48771","48772","48773","48774","48775","48776","48777","48778","48779","48780","48781","48782","48783","48784","48785","48786","48787","48788","48789","48790","48791","48792","48793","48794","48795","48796","48797","48798","48799","48800","48801","48802","48803","48804","48805","48806","48807","48808","48809","48810","48811","48812","48813","48814","48815","48816","48817","48818","48819","48820","48821","48822","48823","48824","48825","48826","48827","48828","48829","48830","48831","48832","48833","48834","48835","48836","48837","48838","48839","48840","48841","48842","48843","48844","48845","48846","48847","48848","48849","48850","48851","48852","48853","48854","48855","48856","48857","48858","48859","48860","48861","48862","48863","48864","48865","48866","48867","48868","48869","48870","48871","48872","48873","48874","48875","48876","48877","48878","48879","48880","48881","48882","48883","48884","48885","48886","48887","48888","48889","48890","48891","48892","48893","48894","48895","48896","48897","48898","48899","48900","48901","48902","48903","48904","48905","48906","48907","48908","48909","48910","48911","48912","48913","48914","48915","48916","48917","48918","48919","48920","48921","48922","48923","48924","48925","48926","48927","48928","48929","48930","48931","48932","48933","48934","48935","48936","48937","48938","48939","48940","48941","48942","48943","48944","48945","48946","48947","48948","48949","48950","48951","48952","48953","48954","48955","48956","48957","48958","48959","48960","48961","48962","48963","48964","48965","48966","48967","48968","48969","48970","48971","48972","48973","48974","48975","48976","48977","48978","48979","48980","48981","48982","48983","48984","48985","48986","48987","48988","48989","48990","48991","48992","48993","48994","48995","48996","48997","48998","48999","49000","49001","49002","49003","49004","49005","49006","49007","49008","49009","49010","49011","49012","49013","49014","49015","49016","49017","49018","49019","49020","49021","49022","49023","49024","49025","49026","49027","49028","49029","49030","49031","49032","49033","49034","49035","49036","49037","49038","49039","49040","49041","49042","49043","49044","49045","49046","49047","49048","49049","49050","49051","49052","49053","49054","49055","49056","49057","49058","49059","49060","49061","49062","49063","49064","49065","49066","49067","49068","49069","49070","49071","49072","49073","49074","49075","49076","49077","49078","49079","49080","49081","49082","49083","49084","49085","49086","49087","49088","49089","49090","49091","49092","49093","49094","49095","49096","49097","49098","49099","49100","49101","49102","49103","49104","49105","49106","49107","49108","49109","49110","49111","49112","49113","49114","49115","49116","49117","49118","49119","49120","49121","49122","49123","49124","49125","49126","49127","49128","49129","49130","49131","49132","49133","49134","49135","49136","49137","49138","49139","49140","49141","49142","49143","49144","49145","49146","49147","49148","49149","49150","49151","49152","49153","49154","49155","49156","49157","49158","49159","49160","49161","49162","49163","49164","49165","49166","49167","49168","49169","49170","49171","49172","49173","49174","49175","49176","49177","49178","49179","49180","49181","49182","49183","49184","49185","49186","49187","49188","49189","49190","49191","49192","49193","49194","49195","49196","49197","49198","49199","49200","49201","49202","49203","49204","49205","49206","49207","49208","49209","49210","49211","49212","49213","49214","49215","49216","49217","49218","49219","49220","49221","49222","49223","49224","49225","49226","49227","49228","49229","49230","49231","49232","49233","49234","49235","49236","49237","49238","49239","49240","49241","49242","49243","49244","49245","49246","49247","49248","49249","49250","49251","49252","49253","49254","49255","49256","49257","49258","49259","49260","49261","49262","49263","49264","49265","49266","49267","49268","49269","49270","49271","49272","49273","49274","49275","49276","49277","49278","49279","49280","49281","49282","49283","49284","49285","49286","49287","49288","49289","49290","49291","49292","49293","49294","49295","49296","49297","49298","49299","49300","49301","49302","49303","49304","49305","49306","49307","49308","49309","49310","49311","49312","49313","49314","49315","49316","49317","49318","49319","49320","49321","49322","49323","49324","49325","49326","49327","49328","49329","49330","49331","49332","49333","49334","49335","49336","49337","49338","49339","49340","49341","49342","49343","49344","49345","49346","49347","49348","49349","49350","49351","49352","49353","49354","49355","49356","49357","49358","49359","49360","49361","49362","49363","49364","49365","49366","49367","49368","49369","49370","49371","49372","49373","49374","49375","49376","49377","49378","49379","49380","49381","49382","49383","49384","49385","49386","49387","49388","49389","49390","49391","49392","49393","49394","49395","49396","49397","49398","49399","49400","49401","49402","49403","49404","49405","49406","49407","49408","49409","49410","49411","49412","49413","49414","49415","49416","49417","49418","49419","49420","49421","49422","49423","49424","49425","49426","49427","49428","49429","49430","49431","49432","49433","49434","49435","49436","49437","49438","49439","49440","49441","49442","49443","49444","49445","49446","49447","49448","49449","49450","49451","49452","49453","49454","49455","49456","49457","49458","49459","49460","49461","49462","49463","49464","49465","49466","49467","49468","49469","49470","49471","49472","49473","49474","49475","49476","49477","49478","49479","49480","49481","49482","49483","49484","49485","49486","49487","49488","49489","49490","49491","49492","49493","49494","49495","49496","49497","49498","49499","49500","49501","49502","49503","49504","49505","49506","49507","49508","49509","49510","49511","49512","49513","49514","49515","49516","49517","49518","49519","49520","49521","49522","49523","49524","49525","49526","49527","49528","49529","49530","49531","49532","49533","49534","49535","49536","49537","49538","49539","49540","49541","49542","49543","49544","49545","49546","49547","49548","49549","49550","49551","49552","49553","49554","49555","49556","49557","49558","49559","49560","49561","49562","49563","49564","49565","49566","49567","49568","49569","49570","49571","49572","49573","49574","49575","49576","49577","49578","49579","49580","49581","49582","49583","49584","49585","49586","49587","49588","49589","49590","49591","49592","49593","49594","49595","49596","49597","49598","49599","49600","49601","49602","49603","49604","49605","49606","49607","49608","49609","49610","49611","49612","49613","49614","49615","49616","49617","49618","49619","49620","49621","49622","49623","49624","49625","49626","49627","49628","49629","49630","49631","49632","49633","49634","49635","49636","49637","49638","49639","49640","49641","49642","49643","49644","49645","49646","49647","49648","49649","49650","49651","49652","49653","49654","49655","49656","49657","49658","49659","49660","49661","49662","49663","49664","49665","49666","49667","49668","49669","49670","49671","49672","49673","49674","49675","49676","49677","49678","49679","49680","49681","49682","49683","49684","49685","49686","49687","49688","49689","49690","49691","49692","49693","49694","49695","49696","49697","49698","49699","49700","49701","49702","49703","49704","49705","49706","49707","49708","49709","49710","49711","49712","49713","49714","49715","49716","49717","49718","49719","49720","49721","49722","49723","49724","49725","49726","49727","49728","49729","49730","49731","49732","49733","49734","49735","49736","49737","49738","49739","49740","49741","49742","49743","49744","49745","49746","49747","49748","49749","49750","49751","49752","49753","49754","49755","49756","49757","49758","49759","49760","49761","49762","49763","49764","49765","49766","49767","49768","49769","49770","49771","49772","49773","49774","49775","49776","49777","49778","49779","49780","49781","49782","49783","49784","49785","49786","49787","49788","49789","49790","49791","49792","49793","49794","49795","49796","49797","49798","49799","49800","49801","49802","49803","49804","49805","49806","49807","49808","49809","49810","49811","49812","49813","49814","49815","49816","49817","49818","49819","49820","49821","49822","49823","49824","49825","49826","49827","49828","49829","49830","49831","49832","49833","49834","49835","49836","49837","49838","49839","49840","49841","49842","49843","49844","49845","49846","49847","49848","49849","49850","49851","49852","49853","49854","49855","49856","49857","49858","49859","49860","49861","49862","49863","49864","49865","49866","49867","49868","49869","49870","49871","49872","49873","49874","49875","49876","49877","49878","49879","49880","49881","49882","49883","49884","49885","49886","49887","49888","49889","49890","49891","49892","49893","49894","49895","49896","49897","49898","49899","49900","49901","49902","49903","49904","49905","49906","49907","49908","49909","49910","49911","49912","49913","49914","49915","49916","49917","49918","49919","49920","49921","49922","49923","49924","49925","49926","49927","49928","49929","49930","49931","49932","49933","49934","49935","49936","49937","49938","49939","49940","49941","49942","49943","49944","49945","49946","49947","49948","49949","49950","49951","49952","49953","49954","49955","49956","49957","49958","49959","49960","49961","49962","49963","49964","49965","49966","49967","49968","49969","49970","49971","49972","49973","49974","49975","49976","49977","49978","49979","49980","49981","49982","49983","49984","49985","49986","49987","49988","49989","49990","49991","49992","49993","49994","49995","49996","49997","49998","49999","50000","50001","50002","50003","50004","50005","50006","50007","50008","50009","50010","50011","50012","50013","50014","50015","50016","50017","50018","50019","50020","50021","50022","50023","50024","50025","50026","50027","50028","50029","50030","50031","50032","50033","50034","50035","50036","50037","50038","50039","50040","50041","50042","50043","50044","50045","50046","50047","50048","50049","50050","50051","50052","50053","50054","50055","50056","50057","50058","50059","50060","50061","50062","50063","50064","50065","50066","50067","50068","50069","50070","50071","50072","50073","50074","50075","50076","50077","50078","50079","50080","50081","50082","50083","50084","50085","50086","50087","50088","50089","50090","50091","50092","50093","50094","50095","50096","50097","50098","50099","50100","50101","50102","50103","50104","50105","50106","50107","50108","50109","50110","50111","50112","50113","50114","50115","50116","50117","50118","50119","50120","50121","50122","50123","50124","50125","50126","50127","50128","50129","50130","50131","50132","50133","50134","50135","50136","50137","50138","50139","50140","50141","50142","50143","50144","50145","50146","50147","50148","50149","50150","50151","50152","50153","50154","50155","50156","50157","50158","50159","50160","50161","50162","50163","50164","50165","50166","50167","50168","50169","50170","50171","50172","50173","50174","50175","50176","50177","50178","50179","50180","50181","50182","50183","50184","50185","50186","50187","50188","50189","50190","50191","50192","50193","50194","50195","50196","50197","50198","50199","50200","50201","50202","50203","50204","50205","50206","50207","50208","50209","50210","50211","50212","50213","50214","50215","50216","50217","50218","50219","50220","50221","50222","50223","50224","50225","50226","50227","50228","50229","50230","50231","50232","50233","50234","50235","50236","50237","50238","50239","50240","50241","50242","50243","50244","50245","50246","50247","50248","50249","50250","50251","50252","50253","50254","50255","50256","50257","50258","50259","50260","50261","50262","50263","50264","50265","50266","50267","50268","50269","50270","50271","50272","50273","50274","50275","50276","50277","50278","50279","50280","50281","50282","50283","50284","50285","50286","50287","50288","50289","50290","50291","50292","50293","50294","50295","50296","50297","50298","50299","50300","50301","50302","50303","50304","50305","50306","50307","50308","50309","50310","50311","50312","50313","50314","50315","50316","50317","50318","50319","50320","50321","50322","50323","50324","50325","50326","50327","50328","50329","50330","50331","50332","50333","50334","50335","50336","50337","50338","50339","50340","50341","50342","50343","50344","50345","50346","50347","50348","50349","50350","50351","50352","50353","50354","50355","50356","50357","50358","50359","50360","50361","50362","50363","50364","50365","50366","50367","50368","50369","50370","50371","50372","50373","50374","50375","50376","50377","50378","50379","50380","50381","50382","50383","50384","50385","50386","50387","50388","50389","50390","50391","50392","50393","50394","50395","50396","50397","50398","50399","50400","50401","50402","50403","50404","50405","50406","50407","50408","50409","50410","50411","50412","50413","50414","50415","50416","50417","50418","50419","50420","50421","50422","50423","50424","50425","50426","50427","50428","50429","50430","50431","50432","50433","50434","50435","50436","50437","50438","50439","50440","50441","50442","50443","50444","50445","50446","50447","50448","50449","50450","50451","50452","50453","50454","50455","50456","50457","50458","50459","50460","50461","50462","50463","50464","50465","50466","50467","50468","50469","50470","50471","50472","50473","50474","50475","50476","50477","50478","50479","50480","50481","50482","50483","50484","50485","50486","50487","50488","50489","50490","50491","50492","50493","50494","50495","50496","50497","50498","50499","50500","50501","50502","50503","50504","50505","50506","50507","50508","50509","50510","50511","50512","50513","50514","50515","50516","50517","50518","50519","50520","50521","50522","50523","50524","50525","50526","50527","50528","50529","50530","50531","50532","50533","50534","50535","50536","50537","50538","50539","50540","50541","50542","50543","50544","50545","50546","50547","50548","50549","50550","50551","50552","50553","50554","50555","50556","50557","50558","50559","50560","50561","50562","50563","50564","50565","50566","50567","50568","50569","50570","50571","50572","50573","50574","50575","50576","50577","50578","50579","50580","50581","50582","50583","50584","50585","50586","50587","50588","50589","50590","50591","50592","50593","50594","50595","50596","50597","50598","50599","50600","50601","50602","50603","50604","50605","50606","50607","50608","50609","50610","50611","50612","50613","50614","50615","50616","50617","50618","50619","50620","50621","50622","50623","50624","50625","50626","50627","50628","50629","50630","50631","50632","50633","50634","50635","50636","50637","50638","50639","50640","50641","50642","50643","50644","50645","50646","50647","50648","50649","50650","50651","50652","50653","50654","50655","50656","50657","50658","50659","50660","50661","50662","50663","50664","50665","50666","50667","50668","50669","50670","50671","50672","50673","50674","50675","50676","50677","50678","50679","50680","50681","50682","50683","50684","50685","50686","50687","50688","50689","50690","50691","50692","50693","50694","50695","50696","50697","50698","50699","50700","50701","50702","50703","50704","50705","50706","50707","50708","50709","50710","50711","50712","50713","50714","50715","50716","50717","50718","50719","50720","50721","50722","50723","50724","50725","50726","50727","50728","50729","50730","50731","50732","50733","50734","50735","50736","50737","50738","50739","50740","50741","50742","50743","50744","50745","50746","50747","50748","50749","50750","50751","50752","50753","50754","50755","50756","50757","50758","50759","50760","50761","50762","50763","50764","50765","50766","50767","50768","50769","50770","50771","50772","50773","50774","50775","50776","50777","50778","50779","50780","50781","50782","50783","50784","50785","50786","50787","50788","50789","50790","50791","50792","50793","50794","50795","50796","50797","50798","50799","50800","50801","50802","50803","50804","50805","50806","50807","50808","50809","50810","50811","50812","50813","50814","50815","50816","50817","50818","50819","50820","50821","50822","50823","50824","50825","50826","50827","50828","50829","50830","50831","50832","50833","50834","50835","50836","50837","50838","50839","50840","50841","50842","50843","50844","50845","50846","50847","50848","50849","50850","50851","50852","50853","50854","50855","50856","50857","50858","50859","50860","50861","50862","50863","50864","50865","50866","50867","50868","50869","50870","50871","50872","50873","50874","50875","50876","50877","50878","50879","50880","50881","50882","50883","50884","50885","50886","50887","50888","50889","50890","50891","50892","50893","50894","50895","50896","50897","50898","50899","50900","50901","50902","50903","50904","50905","50906","50907","50908","50909","50910","50911","50912","50913","50914","50915","50916","50917","50918","50919","50920","50921","50922","50923","50924","50925","50926","50927","50928","50929","50930","50931","50932","50933","50934","50935","50936","50937","50938","50939","50940","50941","50942","50943","50944","50945","50946","50947","50948","50949","50950","50951","50952","50953","50954","50955","50956","50957","50958","50959","50960","50961","50962","50963","50964","50965","50966","50967","50968","50969","50970","50971","50972","50973","50974","50975","50976","50977","50978","50979","50980","50981","50982","50983","50984","50985","50986","50987","50988","50989","50990","50991","50992","50993","50994","50995","50996","50997","50998","50999","51000","51001","51002","51003","51004","51005","51006","51007","51008","51009","51010","51011","51012","51013","51014","51015","51016","51017","51018","51019","51020","51021","51022","51023","51024","51025","51026","51027","51028","51029","51030","51031","51032","51033","51034","51035","51036","51037","51038","51039","51040","51041","51042","51043","51044","51045","51046","51047","51048","51049","51050","51051","51052","51053","51054","51055","51056","51057","51058","51059","51060","51061","51062","51063","51064","51065","51066","51067","51068","51069","51070","51071","51072","51073","51074","51075","51076","51077","51078","51079","51080","51081","51082","51083","51084","51085","51086","51087","51088","51089","51090","51091","51092","51093","51094","51095","51096","51097","51098","51099","51100","51101","51102","51103","51104","51105","51106","51107","51108","51109","51110","51111","51112","51113","51114","51115","51116","51117","51118","51119","51120","51121","51122","51123","51124","51125","51126","51127","51128","51129","51130","51131","51132","51133","51134","51135","51136","51137","51138","51139","51140","51141","51142","51143","51144","51145","51146","51147","51148","51149","51150","51151","51152","51153","51154","51155","51156","51157","51158","51159","51160","51161","51162","51163","51164","51165","51166","51167","51168","51169","51170","51171","51172","51173","51174","51175","51176","51177","51178","51179","51180","51181","51182","51183","51184","51185","51186","51187","51188","51189","51190","51191","51192","51193","51194","51195","51196","51197","51198","51199","51200","51201","51202","51203","51204","51205","51206","51207","51208","51209","51210","51211","51212","51213","51214","51215","51216","51217","51218","51219","51220","51221","51222","51223","51224","51225","51226","51227","51228","51229","51230","51231","51232","51233","51234","51235","51236","51237","51238","51239","51240","51241","51242","51243","51244","51245","51246","51247","51248","51249","51250","51251","51252","51253","51254","51255","51256","51257","51258","51259","51260","51261","51262","51263","51264","51265","51266","51267","51268","51269","51270","51271","51272","51273","51274","51275","51276","51277","51278","51279","51280","51281","51282","51283","51284","51285","51286","51287","51288","51289","51290","51291","51292","51293","51294","51295","51296","51297","51298","51299","51300","51301","51302","51303","51304","51305","51306","51307","51308","51309","51310","51311","51312","51313","51314","51315","51316","51317","51318","51319","51320","51321","51322","51323","51324","51325","51326","51327","51328","51329","51330","51331","51332","51333","51334","51335","51336","51337","51338","51339","51340","51341","51342","51343","51344","51345","51346","51347","51348","51349","51350","51351","51352","51353","51354","51355","51356","51357","51358","51359","51360","51361","51362","51363","51364","51365","51366","51367","51368","51369","51370","51371","51372","51373","51374","51375","51376","51377","51378","51379","51380","51381","51382","51383","51384","51385","51386","51387","51388","51389","51390","51391","51392","51393","51394","51395","51396","51397","51398","51399","51400","51401","51402","51403","51404","51405","51406","51407","51408","51409","51410","51411","51412","51413","51414","51415","51416","51417","51418","51419","51420","51421","51422","51423","51424","51425","51426","51427","51428","51429","51430","51431","51432","51433","51434","51435","51436","51437","51438","51439","51440","51441","51442","51443","51444","51445","51446","51447","51448","51449","51450","51451","51452","51453","51454","51455","51456","51457","51458","51459","51460","51461","51462","51463","51464","51465","51466","51467","51468","51469","51470","51471","51472","51473","51474","51475","51476","51477","51478","51479","51480","51481","51482","51483","51484","51485","51486","51487","51488","51489","51490","51491","51492","51493","51494","51495","51496","51497","51498","51499","51500","51501","51502","51503","51504","51505","51506","51507","51508","51509","51510","51511","51512","51513","51514","51515","51516","51517","51518","51519","51520","51521","51522","51523","51524","51525","51526","51527","51528","51529","51530","51531","51532","51533","51534","51535","51536","51537","51538","51539","51540","51541","51542","51543","51544","51545","51546","51547","51548","51549","51550","51551","51552","51553","51554","51555","51556","51557","51558","51559","51560","51561","51562","51563","51564","51565","51566","51567","51568","51569","51570","51571","51572","51573","51574","51575","51576","51577","51578","51579","51580","51581","51582","51583","51584","51585","51586","51587","51588","51589","51590","51591","51592","51593","51594","51595","51596","51597","51598","51599","51600","51601","51602","51603","51604","51605","51606","51607","51608","51609","51610","51611","51612","51613","51614","51615","51616","51617","51618","51619","51620","51621","51622","51623","51624","51625","51626","51627","51628","51629","51630","51631","51632","51633","51634","51635","51636","51637","51638","51639","51640","51641","51642","51643","51644","51645","51646","51647","51648","51649","51650","51651","51652","51653","51654","51655","51656","51657","51658","51659","51660","51661","51662","51663","51664","51665","51666","51667","51668","51669","51670","51671","51672","51673","51674","51675","51676","51677","51678","51679","51680","51681","51682","51683","51684","51685","51686","51687","51688","51689","51690","51691","51692","51693","51694","51695","51696","51697","51698","51699","51700","51701","51702","51703","51704","51705","51706","51707","51708","51709","51710","51711","51712","51713","51714","51715","51716","51717","51718","51719","51720","51721","51722","51723","51724","51725","51726","51727","51728","51729","51730","51731","51732","51733","51734","51735","51736","51737","51738","51739","51740","51741","51742","51743","51744","51745","51746","51747","51748","51749","51750","51751","51752","51753","51754","51755","51756","51757","51758","51759","51760","51761","51762","51763","51764","51765","51766","51767","51768","51769","51770","51771","51772","51773","51774","51775","51776","51777","51778","51779","51780","51781","51782","51783","51784","51785","51786","51787","51788","51789","51790","51791","51792","51793","51794","51795","51796","51797","51798","51799","51800","51801","51802","51803","51804","51805","51806","51807","51808","51809","51810","51811","51812","51813","51814","51815","51816","51817","51818","51819","51820","51821","51822","51823","51824","51825","51826","51827","51828","51829","51830","51831","51832","51833","51834","51835","51836","51837","51838","51839","51840","51841","51842","51843","51844","51845","51846","51847","51848","51849","51850","51851","51852","51853","51854","51855","51856","51857","51858","51859","51860","51861","51862","51863","51864","51865","51866","51867","51868","51869","51870","51871","51872","51873","51874","51875","51876","51877","51878","51879","51880","51881","51882","51883","51884","51885","51886","51887","51888","51889","51890","51891","51892","51893","51894","51895","51896","51897","51898","51899","51900","51901","51902","51903","51904","51905","51906","51907","51908","51909","51910","51911","51912","51913","51914","51915","51916","51917","51918","51919","51920","51921","51922","51923","51924","51925","51926","51927","51928","51929","51930","51931","51932","51933","51934","51935","51936","51937","51938","51939","51940","51941","51942","51943","51944","51945","51946","51947","51948","51949","51950","51951","51952","51953","51954","51955","51956","51957","51958","51959","51960","51961","51962","51963","51964","51965","51966","51967","51968","51969","51970","51971","51972","51973","51974","51975","51976","51977","51978","51979","51980","51981","51982","51983","51984","51985","51986","51987","51988","51989","51990","51991","51992","51993","51994","51995","51996","51997","51998","51999","52000","52001","52002","52003","52004","52005","52006","52007","52008","52009","52010","52011","52012","52013","52014","52015","52016","52017","52018","52019","52020","52021","52022","52023","52024","52025","52026","52027","52028","52029","52030","52031","52032","52033","52034","52035","52036","52037","52038","52039","52040","52041","52042","52043","52044","52045","52046","52047","52048","52049","52050","52051","52052","52053","52054","52055","52056","52057","52058","52059","52060","52061","52062","52063","52064","52065","52066","52067","52068","52069","52070","52071","52072","52073","52074","52075","52076","52077","52078","52079","52080","52081","52082","52083","52084","52085","52086","52087","52088","52089","52090","52091","52092","52093","52094","52095","52096","52097","52098","52099","52100","52101","52102","52103","52104","52105","52106","52107","52108","52109","52110","52111","52112","52113","52114","52115","52116","52117","52118","52119","52120","52121","52122","52123","52124","52125","52126","52127","52128","52129","52130","52131","52132","52133","52134","52135","52136","52137","52138","52139","52140","52141","52142","52143","52144","52145","52146","52147","52148","52149","52150","52151","52152","52153","52154","52155","52156","52157","52158","52159","52160","52161","52162","52163","52164","52165","52166","52167","52168","52169","52170","52171","52172","52173","52174","52175","52176","52177","52178","52179","52180","52181","52182","52183","52184","52185","52186","52187","52188","52189","52190","52191","52192","52193","52194","52195","52196","52197","52198","52199","52200","52201","52202","52203","52204","52205","52206","52207","52208","52209","52210","52211","52212","52213","52214","52215","52216","52217","52218","52219","52220","52221","52222","52223","52224","52225","52226","52227","52228","52229","52230","52231","52232","52233","52234","52235","52236","52237","52238","52239","52240","52241","52242","52243","52244","52245","52246","52247","52248","52249","52250","52251","52252","52253","52254","52255","52256","52257","52258","52259","52260","52261","52262","52263","52264","52265","52266","52267","52268","52269","52270","52271","52272","52273","52274","52275","52276","52277","52278","52279","52280","52281","52282","52283","52284","52285","52286","52287","52288","52289","52290","52291","52292","52293","52294","52295","52296","52297","52298","52299","52300","52301","52302","52303","52304","52305","52306","52307","52308","52309","52310","52311","52312","52313","52314","52315","52316","52317","52318","52319","52320","52321","52322","52323","52324","52325","52326","52327","52328","52329","52330","52331","52332","52333","52334","52335","52336","52337","52338","52339","52340","52341","52342","52343","52344","52345","52346","52347","52348","52349","52350","52351","52352","52353","52354","52355","52356","52357","52358","52359","52360","52361","52362","52363","52364","52365","52366","52367","52368","52369","52370","52371","52372","52373","52374","52375","52376","52377","52378","52379","52380","52381","52382","52383","52384","52385","52386","52387","52388","52389","52390","52391","52392","52393","52394","52395","52396","52397","52398","52399","52400","52401","52402","52403","52404","52405","52406","52407","52408","52409","52410","52411","52412","52413","52414","52415","52416","52417","52418","52419","52420","52421","52422","52423","52424","52425","52426","52427","52428","52429","52430","52431","52432","52433","52434","52435","52436","52437","52438","52439","52440","52441","52442","52443","52444","52445","52446","52447","52448","52449","52450","52451","52452","52453","52454","52455","52456","52457","52458","52459","52460","52461","52462","52463","52464","52465","52466","52467","52468","52469","52470","52471","52472","52473","52474","52475","52476","52477","52478","52479","52480","52481","52482","52483","52484","52485","52486","52487","52488","52489","52490","52491","52492","52493","52494","52495","52496","52497","52498","52499","52500","52501","52502","52503","52504","52505","52506","52507","52508","52509","52510","52511","52512","52513","52514","52515","52516","52517","52518","52519","52520","52521","52522","52523","52524","52525","52526","52527","52528","52529","52530","52531","52532","52533","52534","52535","52536","52537","52538","52539","52540","52541","52542","52543","52544","52545","52546","52547","52548","52549","52550","52551","52552","52553","52554","52555","52556","52557","52558","52559","52560","52561","52562","52563","52564","52565","52566","52567","52568","52569","52570","52571","52572","52573","52574","52575","52576","52577","52578","52579","52580","52581","52582","52583","52584","52585","52586","52587","52588","52589","52590","52591","52592","52593","52594","52595","52596","52597","52598","52599","52600","52601","52602","52603","52604","52605","52606","52607","52608","52609","52610","52611","52612","52613","52614","52615","52616","52617","52618","52619","52620","52621","52622","52623","52624","52625","52626","52627","52628","52629","52630","52631","52632","52633","52634","52635","52636","52637","52638","52639","52640","52641","52642","52643","52644","52645","52646","52647","52648","52649","52650","52651","52652","52653","52654","52655","52656","52657","52658","52659","52660","52661","52662","52663","52664","52665","52666","52667","52668","52669","52670","52671","52672","52673","52674","52675","52676","52677","52678","52679","52680","52681","52682","52683","52684","52685","52686","52687","52688","52689","52690","52691","52692","52693","52694","52695","52696","52697","52698","52699","52700","52701","52702","52703","52704","52705","52706","52707","52708","52709","52710","52711","52712","52713","52714","52715","52716","52717","52718","52719","52720","52721","52722","52723","52724","52725","52726","52727","52728","52729","52730","52731","52732","52733","52734","52735","52736","52737","52738","52739","52740","52741","52742","52743","52744","52745","52746","52747","52748","52749","52750","52751","52752","52753","52754","52755","52756","52757","52758","52759","52760","52761","52762","52763","52764","52765","52766","52767","52768","52769","52770","52771","52772","52773","52774","52775","52776","52777","52778","52779","52780","52781","52782","52783","52784","52785","52786","52787","52788","52789","52790","52791","52792","52793","52794","52795","52796","52797","52798","52799","52800","52801","52802","52803","52804","52805","52806","52807","52808","52809","52810","52811","52812","52813","52814","52815","52816","52817","52818","52819","52820","52821","52822","52823","52824","52825","52826","52827","52828","52829","52830","52831","52832","52833","52834","52835","52836","52837","52838","52839","52840","52841","52842","52843","52844","52845","52846","52847","52848","52849","52850","52851","52852","52853","52854","52855","52856","52857","52858","52859","52860","52861","52862","52863","52864","52865","52866","52867","52868","52869","52870","52871","52872","52873","52874","52875","52876","52877","52878","52879","52880","52881","52882","52883","52884","52885","52886","52887","52888","52889","52890","52891","52892","52893","52894","52895","52896","52897","52898","52899","52900","52901","52902","52903","52904","52905","52906","52907","52908","52909","52910","52911","52912","52913","52914","52915","52916","52917","52918","52919","52920","52921","52922","52923","52924","52925","52926","52927","52928","52929","52930","52931","52932","52933","52934","52935","52936","52937","52938","52939","52940","52941","52942","52943","52944","52945","52946","52947","52948","52949","52950","52951","52952","52953","52954","52955","52956","52957","52958","52959","52960","52961","52962","52963","52964","52965","52966","52967","52968","52969","52970","52971","52972","52973","52974","52975","52976","52977","52978","52979","52980","52981","52982","52983","52984","52985","52986","52987","52988","52989","52990","52991","52992","52993","52994","52995","52996","52997","52998","52999","53000","53001","53002","53003","53004","53005","53006","53007","53008","53009","53010","53011","53012","53013","53014","53015","53016","53017","53018","53019","53020","53021","53022","53023","53024","53025","53026","53027","53028","53029","53030","53031","53032","53033","53034","53035","53036","53037","53038","53039","53040","53041","53042","53043","53044","53045","53046","53047","53048","53049","53050","53051","53052","53053","53054","53055","53056","53057","53058","53059","53060","53061","53062","53063","53064","53065","53066","53067","53068","53069","53070","53071","53072","53073","53074","53075","53076","53077","53078","53079","53080","53081","53082","53083","53084","53085","53086","53087","53088","53089","53090","53091","53092","53093","53094","53095","53096","53097","53098","53099","53100","53101","53102","53103","53104","53105","53106","53107","53108","53109","53110","53111","53112","53113","53114","53115","53116","53117","53118","53119","53120","53121","53122","53123","53124","53125","53126","53127","53128","53129","53130","53131","53132","53133","53134","53135","53136","53137","53138","53139","53140","53141","53142","53143","53144","53145","53146","53147","53148","53149","53150","53151","53152","53153","53154","53155","53156","53157","53158","53159","53160","53161","53162","53163","53164","53165","53166","53167","53168","53169","53170","53171","53172","53173","53174","53175","53176","53177","53178","53179","53180","53181","53182","53183","53184","53185","53186","53187","53188","53189","53190","53191","53192","53193","53194","53195","53196","53197","53198","53199","53200","53201","53202","53203","53204","53205","53206","53207","53208","53209","53210","53211","53212","53213","53214","53215","53216","53217","53218","53219","53220","53221","53222","53223","53224","53225","53226","53227","53228","53229","53230","53231","53232","53233","53234","53235","53236","53237","53238","53239","53240","53241","53242","53243","53244","53245","53246","53247","53248","53249","53250","53251","53252","53253","53254","53255","53256","53257","53258","53259","53260","53261","53262","53263","53264","53265","53266","53267","53268","53269","53270","53271","53272","53273","53274","53275","53276","53277","53278","53279","53280","53281","53282","53283","53284","53285","53286","53287","53288","53289","53290","53291","53292","53293","53294","53295","53296","53297","53298","53299","53300","53301","53302","53303","53304","53305","53306","53307","53308","53309","53310","53311","53312","53313","53314","53315","53316","53317","53318","53319","53320","53321","53322","53323","53324","53325","53326","53327","53328","53329","53330","53331","53332","53333","53334","53335","53336","53337","53338","53339","53340","53341","53342","53343","53344","53345","53346","53347","53348","53349","53350","53351","53352","53353","53354","53355","53356","53357","53358","53359","53360","53361","53362","53363","53364","53365","53366","53367","53368","53369","53370","53371","53372","53373","53374","53375","53376","53377","53378","53379","53380","53381","53382","53383","53384","53385","53386","53387","53388","53389","53390","53391","53392","53393","53394","53395","53396","53397","53398","53399","53400","53401","53402","53403","53404","53405","53406","53407","53408","53409","53410","53411","53412","53413","53414","53415","53416","53417","53418","53419","53420","53421","53422","53423","53424","53425","53426","53427","53428","53429","53430","53431","53432","53433","53434","53435","53436","53437","53438","53439","53440","53441","53442","53443","53444","53445","53446","53447","53448","53449","53450","53451","53452","53453","53454","53455","53456","53457","53458","53459","53460","53461","53462","53463","53464","53465","53466","53467","53468","53469","53470","53471","53472","53473","53474","53475","53476","53477","53478","53479","53480","53481","53482","53483","53484","53485","53486","53487","53488","53489","53490","53491","53492","53493","53494","53495","53496","53497","53498","53499","53500","53501","53502","53503","53504","53505","53506","53507","53508","53509","53510","53511","53512","53513","53514","53515","53516","53517","53518","53519","53520","53521","53522","53523","53524","53525","53526","53527","53528","53529","53530","53531","53532","53533","53534","53535","53536","53537","53538","53539","53540","53541","53542","53543","53544","53545","53546","53547","53548","53549","53550","53551","53552","53553","53554","53555","53556","53557","53558","53559","53560","53561","53562","53563","53564","53565","53566","53567","53568","53569","53570","53571","53572","53573","53574","53575","53576","53577","53578","53579","53580","53581","53582","53583","53584","53585","53586","53587","53588","53589","53590","53591","53592","53593","53594","53595","53596","53597","53598","53599","53600","53601","53602","53603","53604","53605","53606","53607","53608","53609","53610","53611","53612","53613","53614","53615","53616","53617","53618","53619","53620","53621","53622","53623","53624","53625","53626","53627","53628","53629","53630","53631","53632","53633","53634","53635","53636","53637","53638","53639","53640","53641","53642","53643","53644","53645","53646","53647","53648","53649","53650","53651","53652","53653","53654","53655","53656","53657","53658","53659","53660","53661","53662","53663","53664","53665","53666","53667","53668","53669","53670","53671","53672","53673","53674","53675","53676","53677","53678","53679","53680","53681","53682","53683","53684","53685","53686","53687","53688","53689","53690","53691","53692","53693","53694","53695","53696","53697","53698","53699","53700","53701","53702","53703","53704","53705","53706","53707","53708","53709","53710","53711","53712","53713","53714","53715","53716","53717","53718","53719","53720","53721","53722","53723","53724","53725","53726","53727","53728","53729","53730","53731","53732","53733","53734","53735","53736","53737","53738","53739","53740","53741","53742","53743","53744","53745","53746","53747","53748","53749","53750","53751","53752","53753","53754","53755","53756","53757","53758","53759","53760","53761","53762","53763","53764","53765","53766","53767","53768","53769","53770","53771","53772","53773","53774","53775","53776","53777","53778","53779","53780","53781","53782","53783","53784","53785","53786","53787","53788","53789","53790","53791","53792","53793","53794","53795","53796","53797","53798","53799","53800","53801","53802","53803","53804","53805","53806","53807","53808","53809","53810","53811","53812","53813","53814","53815","53816","53817","53818","53819","53820","53821","53822","53823","53824","53825","53826","53827","53828","53829","53830","53831","53832","53833","53834","53835","53836","53837","53838","53839","53840","53841","53842","53843","53844","53845","53846","53847","53848","53849","53850","53851","53852","53853","53854","53855","53856","53857","53858","53859","53860","53861","53862","53863","53864","53865","53866","53867","53868","53869","53870","53871","53872","53873","53874","53875","53876","53877","53878","53879","53880","53881","53882","53883","53884","53885","53886","53887","53888","53889","53890","53891","53892","53893","53894","53895","53896","53897","53898","53899","53900","53901","53902","53903","53904","53905","53906","53907","53908","53909","53910","53911","53912","53913","53914","53915","53916","53917","53918","53919","53920","53921","53922","53923","53924","53925","53926","53927","53928","53929","53930","53931","53932","53933","53934","53935","53936","53937","53938","53939","53940","53941","53942","53943","53944","53945","53946","53947","53948","53949","53950","53951","53952","53953","53954","53955","53956","53957","53958","53959","53960","53961","53962","53963","53964","53965","53966","53967","53968","53969","53970","53971","53972","53973","53974","53975","53976","53977","53978","53979","53980","53981","53982","53983","53984","53985","53986","53987","53988","53989","53990","53991","53992","53993","53994","53995","53996","53997","53998","53999","54000","54001","54002","54003","54004","54005","54006","54007","54008","54009","54010","54011","54012","54013","54014","54015","54016","54017","54018","54019","54020","54021","54022","54023","54024","54025","54026","54027","54028","54029","54030","54031","54032","54033","54034","54035","54036","54037","54038","54039","54040","54041","54042","54043","54044","54045","54046","54047","54048","54049","54050","54051","54052","54053","54054","54055","54056","54057","54058","54059","54060","54061","54062","54063","54064","54065","54066","54067","54068","54069","54070","54071","54072","54073","54074","54075","54076","54077","54078","54079","54080","54081","54082","54083","54084","54085","54086","54087","54088","54089","54090","54091","54092","54093","54094","54095","54096","54097","54098","54099","54100","54101","54102","54103","54104","54105","54106","54107","54108","54109","54110","54111","54112","54113","54114","54115","54116","54117","54118","54119","54120","54121","54122","54123","54124","54125","54126","54127","54128","54129","54130","54131","54132","54133","54134","54135","54136","54137","54138","54139","54140","54141","54142","54143","54144","54145","54146","54147","54148","54149","54150","54151","54152","54153","54154","54155","54156","54157","54158","54159","54160","54161","54162","54163","54164","54165","54166","54167","54168","54169","54170","54171","54172","54173","54174","54175","54176","54177","54178","54179","54180","54181","54182","54183","54184","54185","54186","54187","54188","54189","54190","54191","54192","54193","54194","54195","54196","54197","54198","54199","54200","54201","54202","54203","54204","54205","54206","54207","54208","54209","54210","54211","54212","54213","54214","54215","54216","54217","54218","54219","54220","54221","54222","54223","54224","54225","54226","54227","54228","54229","54230","54231","54232","54233","54234","54235","54236","54237","54238","54239","54240","54241","54242","54243","54244","54245","54246","54247","54248","54249","54250","54251","54252","54253","54254","54255","54256","54257","54258","54259","54260","54261","54262","54263","54264","54265","54266","54267","54268","54269","54270","54271","54272","54273","54274","54275","54276","54277","54278","54279","54280","54281","54282","54283","54284","54285","54286","54287","54288","54289","54290","54291","54292","54293","54294","54295","54296","54297","54298","54299","54300","54301","54302","54303","54304","54305","54306","54307","54308","54309","54310","54311","54312","54313","54314","54315","54316","54317","54318","54319","54320","54321","54322","54323","54324","54325","54326","54327","54328","54329","54330","54331","54332","54333","54334","54335","54336","54337","54338","54339","54340","54341","54342","54343","54344","54345","54346","54347","54348","54349","54350","54351","54352","54353","54354","54355","54356","54357","54358","54359","54360","54361","54362","54363","54364","54365","54366","54367","54368","54369","54370","54371","54372","54373","54374","54375","54376","54377","54378","54379","54380","54381","54382","54383","54384","54385","54386","54387","54388","54389","54390","54391","54392","54393","54394","54395","54396","54397","54398","54399","54400","54401","54402","54403","54404","54405","54406","54407","54408","54409","54410","54411","54412","54413","54414","54415","54416","54417","54418","54419","54420","54421","54422","54423","54424","54425","54426","54427","54428","54429","54430","54431","54432","54433","54434","54435","54436","54437","54438","54439","54440","54441","54442","54443","54444","54445","54446","54447","54448","54449","54450","54451","54452","54453","54454","54455","54456","54457","54458","54459","54460","54461","54462","54463","54464","54465","54466","54467","54468","54469","54470","54471","54472","54473","54474","54475","54476","54477","54478","54479","54480","54481","54482","54483","54484","54485","54486","54487","54488","54489","54490","54491","54492","54493","54494","54495","54496","54497","54498","54499","54500","54501","54502","54503","54504","54505","54506","54507","54508","54509","54510","54511","54512","54513","54514","54515","54516","54517","54518","54519","54520","54521","54522","54523","54524","54525","54526","54527","54528","54529","54530","54531","54532","54533","54534","54535","54536","54537","54538","54539","54540","54541","54542","54543","54544","54545","54546","54547","54548","54549","54550","54551","54552","54553","54554","54555","54556","54557","54558","54559","54560","54561","54562","54563","54564","54565","54566","54567","54568","54569","54570","54571","54572","54573","54574","54575","54576","54577","54578","54579","54580","54581","54582","54583","54584","54585","54586","54587","54588","54589","54590","54591","54592","54593","54594","54595","54596","54597","54598","54599","54600","54601","54602","54603","54604","54605","54606","54607","54608","54609","54610","54611","54612","54613","54614","54615","54616","54617","54618","54619","54620","54621","54622","54623","54624","54625","54626","54627","54628","54629","54630","54631","54632","54633","54634","54635","54636","54637","54638","54639","54640","54641","54642","54643","54644","54645","54646","54647","54648","54649","54650","54651","54652","54653","54654","54655","54656","54657","54658","54659","54660","54661","54662","54663","54664","54665","54666","54667","54668","54669","54670","54671","54672","54673","54674","54675","54676","54677","54678","54679","54680","54681","54682","54683","54684","54685","54686","54687","54688","54689","54690","54691","54692","54693","54694","54695","54696","54697","54698","54699","54700","54701","54702","54703","54704","54705","54706","54707","54708","54709","54710","54711","54712","54713","54714","54715","54716","54717","54718","54719","54720","54721","54722","54723","54724","54725","54726","54727","54728","54729","54730","54731","54732","54733","54734","54735","54736","54737","54738","54739","54740","54741","54742","54743","54744","54745","54746","54747","54748","54749","54750","54751","54752","54753","54754","54755","54756","54757","54758","54759","54760","54761","54762","54763","54764","54765","54766","54767","54768","54769","54770","54771","54772","54773","54774","54775","54776","54777","54778","54779","54780","54781","54782","54783","54784","54785","54786","54787","54788","54789","54790","54791","54792","54793","54794","54795","54796","54797","54798","54799","54800","54801","54802","54803","54804","54805","54806","54807","54808","54809","54810","54811","54812","54813","54814","54815","54816","54817","54818","54819","54820","54821","54822","54823","54824","54825","54826","54827","54828","54829","54830","54831","54832","54833","54834","54835","54836","54837","54838","54839","54840","54841","54842","54843","54844","54845","54846","54847","54848","54849","54850","54851","54852","54853","54854","54855","54856","54857","54858","54859","54860","54861","54862","54863","54864","54865","54866","54867","54868","54869","54870","54871","54872","54873","54874","54875","54876","54877","54878","54879","54880","54881","54882","54883","54884","54885","54886","54887","54888","54889","54890","54891","54892","54893","54894","54895","54896","54897","54898","54899","54900","54901","54902","54903","54904","54905","54906","54907","54908","54909","54910","54911","54912","54913","54914","54915","54916","54917","54918","54919","54920","54921","54922","54923","54924","54925","54926","54927","54928","54929","54930","54931","54932","54933","54934","54935","54936","54937","54938","54939","54940","54941","54942","54943","54944","54945","54946","54947","54948","54949","54950","54951","54952","54953","54954","54955","54956","54957","54958","54959","54960","54961","54962","54963","54964","54965","54966","54967","54968","54969","54970","54971","54972","54973","54974","54975","54976","54977","54978","54979","54980","54981","54982","54983","54984","54985","54986","54987","54988","54989","54990","54991","54992","54993","54994","54995","54996","54997","54998","54999","55000","55001","55002","55003","55004","55005","55006","55007","55008","55009","55010","55011","55012","55013","55014","55015","55016","55017","55018","55019","55020","55021","55022","55023","55024","55025","55026","55027","55028","55029","55030","55031","55032","55033","55034","55035","55036","55037","55038","55039","55040","55041","55042","55043","55044","55045","55046","55047","55048","55049","55050","55051","55052","55053","55054","55055","55056","55057","55058","55059","55060","55061","55062","55063","55064","55065","55066","55067","55068","55069","55070","55071","55072","55073","55074","55075","55076","55077","55078","55079","55080","55081","55082","55083","55084","55085","55086","55087","55088","55089","55090","55091","55092","55093","55094","55095","55096","55097","55098","55099","55100","55101","55102","55103","55104","55105","55106","55107","55108","55109","55110","55111","55112","55113","55114","55115","55116","55117","55118","55119","55120","55121","55122","55123","55124","55125","55126","55127","55128","55129","55130","55131","55132","55133","55134","55135","55136","55137","55138","55139","55140","55141","55142","55143","55144","55145","55146","55147","55148","55149","55150","55151","55152","55153","55154","55155","55156","55157","55158","55159","55160","55161","55162","55163","55164","55165","55166","55167","55168","55169","55170","55171","55172","55173","55174","55175","55176","55177","55178","55179","55180","55181","55182","55183","55184","55185","55186","55187","55188","55189","55190","55191","55192","55193","55194","55195","55196","55197","55198","55199","55200","55201","55202","55203","55204","55205","55206","55207","55208","55209","55210","55211","55212","55213","55214","55215","55216","55217","55218","55219","55220","55221","55222","55223","55224","55225","55226","55227","55228","55229","55230","55231","55232","55233","55234","55235","55236","55237","55238","55239","55240","55241","55242","55243","55244","55245","55246","55247","55248","55249","55250","55251","55252","55253","55254","55255","55256","55257","55258","55259","55260","55261","55262","55263","55264","55265","55266","55267","55268","55269","55270","55271","55272","55273","55274","55275","55276","55277","55278","55279","55280","55281","55282","55283","55284","55285","55286","55287","55288","55289","55290","55291","55292","55293","55294","55295","55296","55297","55298","55299","55300","55301","55302","55303","55304","55305","55306","55307","55308","55309","55310","55311","55312","55313","55314","55315","55316","55317","55318","55319","55320","55321","55322","55323","55324","55325","55326","55327","55328","55329","55330","55331","55332","55333","55334","55335","55336","55337","55338","55339","55340","55341","55342","55343","55344","55345","55346","55347","55348","55349","55350","55351","55352","55353","55354","55355","55356","55357","55358","55359","55360","55361","55362","55363","55364","55365","55366","55367","55368","55369","55370","55371","55372","55373","55374","55375","55376","55377","55378","55379","55380","55381","55382","55383","55384","55385","55386","55387","55388","55389","55390","55391","55392","55393","55394","55395","55396","55397","55398","55399","55400","55401","55402","55403","55404","55405","55406","55407","55408","55409","55410","55411","55412","55413","55414","55415","55416","55417","55418","55419","55420","55421","55422","55423","55424","55425","55426","55427","55428","55429","55430","55431","55432","55433","55434","55435","55436","55437","55438","55439","55440","55441","55442","55443","55444","55445","55446","55447","55448","55449","55450","55451","55452","55453","55454","55455","55456","55457","55458","55459","55460","55461","55462","55463","55464","55465","55466","55467","55468","55469","55470","55471","55472","55473","55474","55475","55476","55477","55478","55479","55480","55481","55482","55483","55484","55485","55486","55487","55488","55489","55490","55491","55492","55493","55494","55495","55496","55497","55498","55499","55500","55501","55502","55503","55504","55505","55506","55507","55508","55509","55510","55511","55512","55513","55514","55515","55516","55517","55518","55519","55520","55521","55522","55523","55524","55525","55526","55527","55528","55529","55530","55531","55532","55533","55534","55535","55536","55537","55538","55539","55540","55541","55542","55543","55544","55545","55546","55547","55548","55549","55550","55551","55552","55553","55554","55555","55556","55557","55558","55559","55560","55561","55562","55563","55564","55565","55566","55567","55568","55569","55570","55571","55572","55573","55574","55575","55576","55577","55578","55579","55580","55581","55582","55583","55584","55585","55586","55587","55588","55589","55590","55591","55592","55593","55594","55595","55596","55597","55598","55599","55600","55601","55602","55603","55604","55605","55606","55607","55608","55609","55610","55611","55612","55613","55614","55615","55616","55617","55618","55619","55620","55621","55622","55623","55624","55625","55626","55627","55628","55629","55630","55631","55632","55633","55634","55635","55636","55637","55638","55639","55640","55641","55642","55643","55644","55645","55646","55647","55648","55649","55650","55651","55652","55653","55654","55655","55656","55657","55658","55659","55660","55661","55662","55663","55664","55665","55666","55667","55668","55669","55670","55671","55672","55673","55674","55675","55676","55677","55678","55679","55680","55681","55682","55683","55684","55685","55686","55687","55688","55689","55690","55691","55692","55693","55694","55695","55696","55697","55698","55699","55700","55701","55702","55703","55704","55705","55706","55707","55708","55709","55710","55711","55712","55713","55714","55715","55716","55717","55718","55719","55720","55721","55722","55723","55724","55725","55726","55727","55728","55729","55730","55731","55732","55733","55734","55735","55736","55737","55738","55739","55740","55741","55742","55743","55744","55745","55746","55747","55748","55749","55750","55751","55752","55753","55754","55755","55756","55757","55758","55759","55760","55761","55762","55763","55764","55765","55766","55767","55768","55769","55770","55771","55772","55773","55774","55775","55776","55777","55778","55779","55780","55781","55782","55783","55784","55785","55786","55787","55788","55789","55790","55791","55792","55793","55794","55795","55796","55797","55798","55799","55800","55801","55802","55803","55804","55805","55806","55807","55808","55809","55810","55811","55812","55813","55814","55815","55816","55817","55818","55819","55820","55821","55822","55823","55824","55825","55826","55827","55828","55829","55830","55831","55832","55833","55834","55835","55836","55837","55838","55839","55840","55841","55842","55843","55844","55845","55846","55847","55848","55849","55850","55851","55852","55853","55854","55855","55856","55857","55858","55859","55860","55861","55862","55863","55864","55865","55866","55867","55868","55869","55870","55871","55872","55873","55874","55875","55876","55877","55878","55879","55880","55881","55882","55883","55884","55885","55886","55887","55888","55889","55890","55891","55892","55893","55894","55895","55896","55897","55898","55899","55900","55901","55902","55903","55904","55905","55906","55907","55908","55909","55910","55911","55912","55913","55914","55915","55916","55917","55918","55919","55920","55921","55922","55923","55924","55925","55926","55927","55928","55929","55930","55931","55932","55933","55934","55935","55936","55937","55938","55939","55940","55941","55942","55943","55944","55945","55946","55947","55948","55949","55950","55951","55952","55953","55954","55955","55956","55957","55958","55959","55960","55961","55962","55963","55964","55965","55966","55967","55968","55969","55970","55971","55972","55973","55974","55975","55976","55977","55978","55979","55980","55981","55982","55983","55984","55985","55986","55987","55988","55989","55990","55991","55992","55993","55994","55995","55996","55997","55998","55999","56000","56001","56002","56003","56004","56005","56006","56007","56008","56009","56010","56011","56012","56013","56014","56015","56016","56017","56018","56019","56020","56021","56022","56023","56024","56025","56026","56027","56028","56029","56030","56031","56032","56033","56034","56035","56036","56037","56038","56039","56040","56041","56042","56043","56044","56045","56046","56047","56048","56049","56050","56051","56052","56053","56054","56055","56056","56057","56058","56059","56060","56061","56062","56063","56064","56065","56066","56067","56068","56069","56070","56071","56072","56073","56074","56075","56076","56077","56078","56079","56080","56081","56082","56083","56084","56085","56086","56087","56088","56089","56090","56091","56092","56093","56094","56095","56096","56097","56098","56099","56100","56101","56102","56103","56104","56105","56106","56107","56108","56109","56110","56111","56112","56113","56114","56115","56116","56117","56118","56119","56120","56121","56122","56123","56124","56125","56126","56127","56128","56129","56130","56131","56132","56133","56134","56135","56136","56137","56138","56139","56140","56141","56142","56143","56144","56145","56146","56147","56148","56149","56150","56151","56152","56153","56154","56155","56156","56157","56158","56159","56160","56161","56162","56163","56164","56165","56166","56167","56168","56169","56170","56171","56172","56173","56174","56175","56176","56177","56178","56179","56180","56181","56182","56183","56184","56185","56186","56187","56188","56189","56190","56191","56192","56193","56194","56195","56196","56197","56198","56199","56200","56201","56202","56203","56204","56205","56206","56207","56208","56209","56210","56211","56212","56213","56214","56215","56216","56217","56218","56219","56220","56221","56222","56223","56224","56225","56226","56227","56228","56229","56230","56231","56232","56233","56234","56235","56236","56237","56238","56239","56240","56241","56242","56243","56244","56245","56246","56247","56248","56249","56250","56251","56252","56253","56254","56255","56256","56257","56258","56259","56260","56261","56262","56263","56264","56265","56266","56267","56268","56269","56270","56271","56272","56273","56274","56275","56276","56277","56278","56279","56280","56281","56282","56283","56284","56285","56286","56287","56288","56289","56290","56291","56292","56293","56294","56295","56296","56297","56298","56299","56300","56301","56302","56303","56304","56305","56306","56307","56308","56309","56310","56311","56312","56313","56314","56315","56316","56317","56318","56319","56320","56321","56322","56323","56324","56325","56326","56327","56328","56329","56330","56331","56332","56333","56334","56335","56336","56337","56338","56339","56340","56341","56342","56343","56344","56345","56346","56347","56348","56349","56350","56351","56352","56353","56354","56355","56356","56357","56358","56359","56360","56361","56362","56363","56364","56365","56366","56367","56368","56369","56370","56371","56372","56373","56374","56375","56376","56377","56378","56379","56380","56381","56382","56383","56384","56385","56386","56387","56388","56389","56390","56391","56392","56393","56394","56395","56396","56397","56398","56399","56400","56401","56402","56403","56404","56405","56406","56407","56408","56409","56410","56411","56412","56413","56414","56415","56416","56417","56418","56419","56420","56421","56422","56423","56424","56425","56426","56427","56428","56429","56430","56431","56432","56433","56434","56435","56436","56437","56438","56439","56440","56441","56442","56443","56444","56445","56446","56447","56448","56449","56450","56451","56452","56453","56454","56455","56456","56457","56458","56459","56460","56461","56462","56463","56464","56465","56466","56467","56468","56469","56470","56471","56472","56473","56474","56475","56476","56477","56478","56479","56480","56481","56482","56483","56484","56485","56486","56487","56488","56489","56490","56491","56492","56493","56494","56495","56496","56497","56498","56499","56500","56501","56502","56503","56504","56505","56506","56507","56508","56509","56510","56511","56512","56513","56514","56515","56516","56517","56518","56519","56520","56521","56522","56523","56524","56525","56526","56527","56528","56529","56530","56531","56532","56533","56534","56535","56536","56537","56538","56539","56540","56541","56542","56543","56544","56545","56546","56547","56548","56549","56550","56551","56552","56553","56554","56555","56556","56557","56558","56559","56560","56561","56562","56563","56564","56565","56566","56567","56568","56569","56570","56571","56572","56573","56574","56575","56576","56577","56578","56579","56580","56581","56582","56583","56584","56585","56586","56587","56588","56589","56590","56591","56592","56593","56594","56595","56596","56597","56598","56599","56600","56601","56602","56603","56604","56605","56606","56607","56608","56609","56610","56611","56612","56613","56614","56615","56616","56617","56618","56619","56620","56621","56622","56623","56624","56625","56626","56627","56628","56629","56630","56631","56632","56633","56634","56635","56636","56637","56638","56639","56640","56641","56642","56643","56644","56645","56646","56647","56648","56649","56650","56651","56652","56653","56654","56655","56656","56657","56658","56659","56660","56661","56662","56663","56664","56665","56666","56667","56668","56669","56670","56671","56672","56673","56674","56675","56676","56677","56678","56679","56680","56681","56682","56683","56684","56685","56686","56687","56688","56689","56690","56691","56692","56693","56694","56695","56696","56697","56698","56699","56700","56701","56702","56703","56704","56705","56706","56707","56708","56709","56710","56711","56712","56713","56714","56715","56716","56717","56718","56719","56720","56721","56722","56723","56724","56725","56726","56727","56728","56729","56730","56731","56732","56733","56734","56735","56736","56737","56738","56739","56740","56741","56742","56743","56744","56745","56746","56747","56748","56749","56750","56751","56752","56753","56754","56755","56756","56757","56758","56759","56760","56761","56762","56763","56764","56765","56766","56767","56768","56769","56770","56771","56772","56773","56774","56775","56776","56777","56778","56779","56780","56781","56782","56783","56784","56785","56786","56787","56788","56789","56790","56791","56792","56793","56794","56795","56796","56797","56798","56799","56800","56801","56802","56803","56804","56805","56806","56807","56808","56809","56810","56811","56812","56813","56814","56815","56816","56817","56818","56819","56820","56821","56822","56823","56824","56825","56826","56827","56828","56829","56830","56831","56832","56833","56834","56835","56836","56837","56838","56839","56840","56841","56842","56843","56844","56845","56846","56847","56848","56849","56850","56851","56852","56853","56854","56855","56856","56857","56858","56859","56860","56861","56862","56863","56864","56865","56866","56867","56868","56869","56870","56871","56872","56873","56874","56875","56876","56877","56878","56879","56880","56881","56882","56883","56884","56885","56886","56887","56888","56889","56890","56891","56892","56893","56894","56895","56896","56897","56898","56899","56900","56901","56902","56903","56904","56905","56906","56907","56908","56909","56910","56911","56912","56913","56914","56915","56916","56917","56918","56919","56920","56921","56922","56923","56924","56925","56926","56927","56928","56929","56930","56931","56932","56933","56934","56935","56936","56937","56938","56939","56940","56941","56942","56943","56944","56945","56946","56947","56948","56949","56950","56951","56952","56953","56954","56955","56956","56957","56958","56959","56960","56961","56962","56963","56964","56965","56966","56967","56968","56969","56970","56971","56972","56973","56974","56975","56976","56977","56978","56979","56980","56981","56982","56983","56984","56985","56986","56987","56988","56989","56990","56991","56992","56993","56994","56995","56996","56997","56998","56999","57000","57001","57002","57003","57004","57005","57006","57007","57008","57009","57010","57011","57012","57013","57014","57015","57016","57017","57018","57019","57020","57021","57022","57023","57024","57025","57026","57027","57028","57029","57030","57031","57032","57033","57034","57035","57036","57037","57038","57039","57040","57041","57042","57043","57044","57045","57046","57047","57048","57049","57050","57051","57052","57053","57054","57055","57056","57057","57058","57059","57060","57061","57062","57063","57064","57065","57066","57067","57068","57069","57070","57071","57072","57073","57074","57075","57076","57077","57078","57079","57080","57081","57082","57083","57084","57085","57086","57087","57088","57089","57090","57091","57092","57093","57094","57095","57096","57097","57098","57099","57100","57101","57102","57103","57104","57105","57106","57107","57108","57109","57110","57111","57112","57113","57114","57115","57116","57117","57118","57119","57120","57121","57122","57123","57124","57125","57126","57127","57128","57129","57130","57131","57132","57133","57134","57135","57136","57137","57138","57139","57140","57141","57142","57143","57144","57145","57146","57147","57148","57149","57150","57151","57152","57153","57154","57155","57156","57157","57158","57159","57160","57161","57162","57163","57164","57165","57166","57167","57168","57169","57170","57171","57172","57173","57174","57175","57176","57177","57178","57179","57180","57181","57182","57183","57184","57185","57186","57187","57188","57189","57190","57191","57192","57193","57194","57195","57196","57197","57198","57199","57200","57201","57202","57203","57204","57205","57206","57207","57208","57209","57210","57211","57212","57213","57214","57215","57216","57217","57218","57219","57220","57221","57222","57223","57224","57225","57226","57227","57228","57229","57230","57231","57232","57233","57234","57235","57236","57237","57238","57239","57240","57241","57242","57243","57244","57245","57246","57247","57248","57249","57250","57251","57252","57253","57254","57255","57256","57257","57258","57259","57260","57261","57262","57263","57264","57265","57266","57267","57268","57269","57270","57271","57272","57273","57274","57275","57276","57277","57278","57279","57280","57281","57282","57283","57284","57285","57286","57287","57288","57289","57290","57291","57292","57293","57294","57295","57296","57297","57298","57299","57300","57301","57302","57303","57304","57305","57306","57307","57308","57309","57310","57311","57312","57313","57314","57315","57316","57317","57318","57319","57320","57321","57322","57323","57324","57325","57326","57327","57328","57329","57330","57331","57332","57333","57334","57335","57336","57337","57338","57339","57340","57341","57342","57343","57344","57345","57346","57347","57348","57349","57350","57351","57352","57353","57354","57355","57356","57357","57358","57359","57360","57361","57362","57363","57364","57365","57366","57367","57368","57369","57370","57371","57372","57373","57374","57375","57376","57377","57378","57379","57380","57381","57382","57383","57384","57385","57386","57387","57388","57389","57390","57391","57392","57393","57394","57395","57396","57397","57398","57399","57400","57401","57402","57403","57404","57405","57406","57407","57408","57409","57410","57411","57412","57413","57414","57415","57416","57417","57418","57419","57420","57421","57422","57423","57424","57425","57426","57427","57428","57429","57430","57431","57432","57433","57434","57435","57436","57437","57438","57439","57440","57441","57442","57443","57444","57445","57446","57447","57448","57449","57450","57451","57452","57453","57454","57455","57456","57457","57458","57459","57460","57461","57462","57463","57464","57465","57466","57467","57468","57469","57470","57471","57472","57473","57474","57475","57476","57477","57478","57479","57480","57481","57482","57483","57484","57485","57486","57487","57488","57489","57490","57491","57492","57493","57494","57495","57496","57497","57498","57499","57500","57501","57502","57503","57504","57505","57506","57507","57508","57509","57510","57511","57512","57513","57514","57515","57516","57517","57518","57519","57520","57521","57522","57523","57524","57525","57526","57527","57528","57529","57530","57531","57532","57533","57534","57535","57536","57537","57538","57539","57540","57541","57542","57543","57544","57545","57546","57547","57548","57549","57550","57551","57552","57553","57554","57555","57556","57557","57558","57559","57560","57561","57562","57563","57564","57565","57566","57567","57568","57569","57570","57571","57572","57573","57574","57575","57576","57577","57578","57579","57580","57581","57582","57583","57584","57585","57586","57587","57588","57589","57590","57591","57592","57593","57594","57595","57596","57597","57598","57599","57600","57601","57602","57603","57604","57605","57606","57607","57608","57609","57610","57611","57612","57613","57614","57615","57616","57617","57618","57619","57620","57621","57622","57623","57624","57625","57626","57627","57628","57629","57630","57631","57632","57633","57634","57635","57636","57637","57638","57639","57640","57641","57642","57643","57644","57645","57646","57647","57648","57649","57650","57651","57652","57653","57654","57655","57656","57657","57658","57659","57660","57661","57662","57663","57664","57665","57666","57667","57668","57669","57670","57671","57672","57673","57674","57675","57676","57677","57678","57679","57680","57681","57682","57683","57684","57685","57686","57687","57688","57689","57690","57691","57692","57693","57694","57695","57696","57697","57698","57699","57700","57701","57702","57703","57704","57705","57706","57707","57708","57709","57710","57711","57712","57713","57714","57715","57716","57717","57718","57719","57720","57721","57722","57723","57724","57725","57726","57727","57728","57729","57730","57731","57732","57733","57734","57735","57736","57737","57738","57739","57740","57741","57742","57743","57744","57745","57746","57747","57748","57749","57750","57751","57752","57753","57754","57755","57756","57757","57758","57759","57760","57761","57762","57763","57764","57765","57766","57767","57768","57769","57770","57771","57772","57773","57774","57775","57776","57777","57778","57779","57780","57781","57782","57783","57784","57785","57786","57787","57788","57789","57790","57791","57792","57793","57794","57795","57796","57797","57798","57799","57800","57801","57802","57803","57804","57805","57806","57807","57808","57809","57810","57811","57812","57813","57814","57815","57816","57817","57818","57819","57820","57821","57822","57823","57824","57825","57826","57827","57828","57829","57830","57831","57832","57833","57834","57835","57836","57837","57838","57839","57840","57841","57842","57843","57844","57845","57846","57847","57848","57849","57850","57851","57852","57853","57854","57855","57856","57857","57858","57859","57860","57861","57862","57863","57864","57865","57866","57867","57868","57869","57870","57871","57872","57873","57874","57875","57876","57877","57878","57879","57880","57881","57882","57883","57884","57885","57886","57887","57888","57889","57890","57891","57892","57893","57894","57895","57896","57897","57898","57899","57900","57901","57902","57903","57904","57905","57906","57907","57908","57909","57910","57911","57912","57913","57914","57915","57916","57917","57918","57919","57920","57921","57922","57923","57924","57925","57926","57927","57928","57929","57930","57931","57932","57933","57934","57935","57936","57937","57938","57939","57940","57941","57942","57943","57944","57945","57946","57947","57948","57949","57950","57951","57952","57953","57954","57955","57956","57957","57958","57959","57960","57961","57962","57963","57964","57965","57966","57967","57968","57969","57970","57971","57972","57973","57974","57975","57976","57977","57978","57979","57980","57981","57982","57983","57984","57985","57986","57987","57988","57989","57990","57991","57992","57993","57994","57995","57996","57997","57998","57999","58000","58001","58002","58003","58004","58005","58006","58007","58008","58009","58010","58011","58012","58013","58014","58015","58016","58017","58018","58019","58020","58021","58022","58023","58024","58025","58026","58027","58028","58029","58030","58031","58032","58033","58034","58035","58036","58037","58038","58039","58040","58041","58042","58043","58044","58045","58046","58047","58048","58049","58050","58051","58052","58053","58054","58055","58056","58057","58058","58059","58060","58061","58062","58063","58064","58065","58066","58067","58068","58069","58070","58071","58072","58073","58074","58075","58076","58077","58078","58079","58080","58081","58082","58083","58084","58085","58086","58087","58088","58089","58090","58091","58092","58093","58094","58095","58096","58097","58098","58099","58100","58101","58102","58103","58104","58105","58106","58107","58108","58109","58110","58111","58112","58113","58114","58115","58116","58117","58118","58119","58120","58121","58122","58123","58124","58125","58126","58127","58128","58129","58130","58131","58132","58133","58134","58135","58136","58137","58138","58139","58140","58141","58142","58143","58144","58145","58146","58147","58148","58149","58150","58151","58152","58153","58154","58155","58156","58157","58158","58159","58160","58161","58162","58163","58164","58165","58166","58167","58168","58169","58170","58171","58172","58173","58174","58175","58176","58177","58178","58179","58180","58181","58182","58183","58184","58185","58186","58187","58188","58189","58190","58191","58192","58193","58194","58195","58196","58197","58198","58199","58200","58201","58202","58203","58204","58205","58206","58207","58208","58209","58210","58211","58212","58213","58214","58215","58216","58217","58218","58219","58220","58221","58222","58223","58224","58225","58226","58227","58228","58229","58230","58231","58232","58233","58234","58235","58236","58237","58238","58239","58240","58241","58242","58243","58244","58245","58246","58247","58248","58249","58250","58251","58252","58253","58254","58255","58256","58257","58258","58259","58260","58261","58262","58263","58264","58265","58266","58267","58268","58269","58270","58271","58272","58273","58274","58275","58276","58277","58278","58279","58280","58281","58282","58283","58284","58285","58286","58287","58288","58289","58290","58291","58292","58293","58294","58295","58296","58297","58298","58299","58300","58301","58302","58303","58304","58305","58306","58307","58308","58309","58310","58311","58312","58313","58314","58315","58316","58317","58318","58319","58320","58321","58322","58323","58324","58325","58326","58327","58328","58329","58330","58331","58332","58333","58334","58335","58336","58337","58338","58339","58340","58341","58342","58343","58344","58345","58346","58347","58348","58349","58350","58351","58352","58353","58354","58355","58356","58357","58358","58359","58360","58361","58362","58363","58364","58365","58366","58367","58368","58369","58370","58371","58372","58373","58374","58375","58376","58377","58378","58379","58380","58381","58382","58383","58384","58385","58386","58387","58388","58389","58390","58391","58392","58393","58394","58395","58396","58397","58398","58399","58400","58401","58402","58403","58404","58405","58406","58407","58408","58409","58410","58411","58412","58413","58414","58415","58416","58417","58418","58419","58420","58421","58422","58423","58424","58425","58426","58427","58428","58429","58430","58431","58432","58433","58434","58435","58436","58437","58438","58439","58440","58441","58442","58443","58444","58445","58446","58447","58448","58449","58450","58451","58452","58453","58454","58455","58456","58457","58458","58459","58460","58461","58462","58463","58464","58465","58466","58467","58468","58469","58470","58471","58472","58473","58474","58475","58476","58477","58478","58479","58480","58481","58482","58483","58484","58485","58486","58487","58488","58489","58490","58491","58492","58493","58494","58495","58496","58497","58498","58499","58500","58501","58502","58503","58504","58505","58506","58507","58508","58509","58510","58511","58512","58513","58514","58515","58516","58517","58518","58519","58520","58521","58522","58523","58524","58525","58526","58527","58528","58529","58530","58531","58532","58533","58534","58535","58536","58537","58538","58539","58540","58541","58542","58543","58544","58545","58546","58547","58548","58549","58550","58551","58552","58553","58554","58555","58556","58557","58558","58559","58560","58561","58562","58563","58564","58565","58566","58567","58568","58569","58570","58571","58572","58573","58574","58575","58576","58577","58578","58579","58580","58581","58582","58583","58584","58585","58586","58587","58588","58589","58590","58591","58592","58593","58594","58595","58596","58597","58598","58599","58600","58601","58602","58603","58604","58605","58606","58607","58608","58609","58610","58611","58612","58613","58614","58615","58616","58617","58618","58619","58620","58621","58622","58623","58624","58625","58626","58627","58628","58629","58630","58631","58632","58633","58634","58635","58636","58637","58638","58639","58640","58641","58642","58643","58644","58645","58646","58647","58648","58649","58650","58651","58652","58653","58654","58655","58656","58657","58658","58659","58660","58661","58662","58663","58664","58665","58666","58667","58668","58669","58670","58671","58672","58673","58674","58675","58676","58677","58678","58679","58680","58681","58682","58683","58684","58685","58686","58687","58688","58689","58690","58691","58692","58693","58694","58695","58696","58697","58698","58699","58700","58701","58702","58703","58704","58705","58706","58707","58708","58709","58710","58711","58712","58713","58714","58715","58716","58717","58718","58719","58720","58721","58722","58723","58724","58725","58726","58727","58728","58729","58730","58731","58732","58733","58734","58735","58736","58737","58738","58739","58740","58741","58742","58743","58744","58745","58746","58747","58748","58749","58750","58751","58752","58753","58754","58755","58756","58757","58758","58759","58760","58761","58762","58763","58764","58765","58766","58767","58768","58769","58770","58771","58772","58773","58774","58775","58776","58777","58778","58779","58780","58781","58782","58783","58784","58785","58786","58787","58788","58789","58790","58791","58792","58793","58794","58795","58796","58797","58798","58799","58800","58801","58802","58803","58804","58805","58806","58807","58808","58809","58810","58811","58812","58813","58814","58815","58816","58817","58818","58819","58820","58821","58822","58823","58824","58825","58826","58827","58828","58829","58830","58831","58832","58833","58834","58835","58836","58837","58838","58839","58840","58841","58842","58843","58844","58845","58846","58847","58848","58849","58850","58851","58852","58853","58854","58855","58856","58857","58858","58859","58860","58861","58862","58863","58864","58865","58866","58867","58868","58869","58870","58871","58872","58873","58874","58875","58876","58877","58878","58879","58880","58881","58882","58883","58884","58885","58886","58887","58888","58889","58890","58891","58892","58893","58894","58895","58896","58897","58898","58899","58900","58901","58902","58903","58904","58905","58906","58907","58908","58909","58910","58911","58912","58913","58914","58915","58916","58917","58918","58919","58920","58921","58922","58923","58924","58925","58926","58927","58928","58929","58930","58931","58932","58933","58934","58935","58936","58937","58938","58939","58940","58941","58942","58943","58944","58945","58946","58947","58948","58949","58950","58951","58952","58953","58954","58955","58956","58957","58958","58959","58960","58961","58962","58963","58964","58965","58966","58967","58968","58969","58970","58971","58972","58973","58974","58975","58976","58977","58978","58979","58980","58981","58982","58983","58984","58985","58986","58987","58988","58989","58990","58991","58992","58993","58994","58995","58996","58997","58998","58999","59000","59001","59002","59003","59004","59005","59006","59007","59008","59009","59010","59011","59012","59013","59014","59015","59016","59017","59018","59019","59020","59021","59022","59023","59024","59025","59026","59027","59028","59029","59030","59031","59032","59033","59034","59035","59036","59037","59038","59039","59040","59041","59042","59043","59044","59045","59046","59047","59048","59049","59050","59051","59052","59053","59054","59055","59056","59057","59058","59059","59060","59061","59062","59063","59064","59065","59066","59067","59068","59069","59070","59071","59072","59073","59074","59075","59076","59077","59078","59079","59080","59081","59082","59083","59084","59085","59086","59087","59088","59089","59090","59091","59092","59093","59094","59095","59096","59097","59098","59099","59100","59101","59102","59103","59104","59105","59106","59107","59108","59109","59110","59111","59112","59113","59114","59115","59116","59117","59118","59119","59120","59121","59122","59123","59124","59125","59126","59127","59128","59129","59130","59131","59132","59133","59134","59135","59136","59137","59138","59139","59140","59141","59142","59143","59144","59145","59146","59147","59148","59149","59150","59151","59152","59153","59154","59155","59156","59157","59158","59159","59160","59161","59162","59163","59164","59165","59166","59167","59168","59169","59170","59171","59172","59173","59174","59175","59176","59177","59178","59179","59180","59181","59182","59183","59184","59185","59186","59187","59188","59189","59190","59191","59192","59193","59194","59195","59196","59197","59198","59199","59200","59201","59202","59203","59204","59205","59206","59207","59208","59209","59210","59211","59212","59213","59214","59215","59216","59217","59218","59219","59220","59221","59222","59223","59224","59225","59226","59227","59228","59229","59230","59231","59232","59233","59234","59235","59236","59237","59238","59239","59240","59241","59242","59243","59244","59245","59246","59247","59248","59249","59250","59251","59252","59253","59254","59255","59256","59257","59258","59259","59260","59261","59262","59263","59264","59265","59266","59267","59268","59269","59270","59271","59272","59273","59274","59275","59276","59277","59278","59279","59280","59281","59282","59283","59284","59285","59286","59287","59288","59289","59290","59291","59292","59293","59294","59295","59296","59297","59298","59299","59300","59301","59302","59303","59304","59305","59306","59307","59308","59309","59310","59311","59312","59313","59314","59315","59316","59317","59318","59319","59320","59321","59322","59323","59324","59325","59326","59327","59328","59329","59330","59331","59332","59333","59334","59335","59336","59337","59338","59339","59340","59341","59342","59343","59344","59345","59346","59347","59348","59349","59350","59351","59352","59353","59354","59355","59356","59357","59358","59359","59360","59361","59362","59363","59364","59365","59366","59367","59368","59369","59370","59371","59372","59373","59374","59375","59376","59377","59378","59379","59380","59381","59382","59383","59384","59385","59386","59387","59388","59389","59390","59391","59392","59393","59394","59395","59396","59397","59398","59399","59400","59401","59402","59403","59404","59405","59406","59407","59408","59409","59410","59411","59412","59413","59414","59415","59416","59417","59418","59419","59420","59421","59422","59423","59424","59425","59426","59427","59428","59429","59430","59431","59432","59433","59434","59435","59436","59437","59438","59439","59440","59441","59442","59443","59444","59445","59446","59447","59448","59449","59450","59451","59452","59453","59454","59455","59456","59457","59458","59459","59460","59461","59462","59463","59464","59465","59466","59467","59468","59469","59470","59471","59472","59473","59474","59475","59476","59477","59478","59479","59480","59481","59482","59483","59484","59485","59486","59487","59488","59489","59490","59491","59492","59493","59494","59495","59496","59497","59498","59499","59500","59501","59502","59503","59504","59505","59506","59507","59508","59509","59510","59511","59512","59513","59514","59515","59516","59517","59518","59519","59520","59521","59522","59523","59524","59525","59526","59527","59528","59529","59530","59531","59532","59533","59534","59535","59536","59537","59538","59539","59540","59541","59542","59543","59544","59545","59546","59547","59548","59549","59550","59551","59552","59553","59554","59555","59556","59557","59558","59559","59560","59561","59562","59563","59564","59565","59566","59567","59568","59569","59570","59571","59572","59573","59574","59575","59576","59577","59578","59579","59580","59581","59582","59583","59584","59585","59586","59587","59588","59589","59590","59591","59592","59593","59594","59595","59596","59597","59598","59599","59600","59601","59602","59603","59604","59605","59606","59607","59608","59609","59610","59611","59612","59613","59614","59615","59616","59617","59618","59619","59620","59621","59622","59623","59624","59625","59626","59627","59628","59629","59630","59631","59632","59633","59634","59635","59636","59637","59638","59639","59640","59641","59642","59643","59644","59645","59646","59647","59648","59649","59650","59651","59652","59653","59654","59655","59656","59657","59658","59659","59660","59661","59662","59663","59664","59665","59666","59667","59668","59669","59670","59671","59672","59673","59674","59675","59676","59677","59678","59679","59680","59681","59682","59683","59684","59685","59686","59687","59688","59689","59690","59691","59692","59693","59694","59695","59696","59697","59698","59699","59700","59701","59702","59703","59704","59705","59706","59707","59708","59709","59710","59711","59712","59713","59714","59715","59716","59717","59718","59719","59720","59721","59722","59723","59724","59725","59726","59727","59728","59729","59730","59731","59732","59733","59734","59735","59736","59737","59738","59739","59740","59741","59742","59743","59744","59745","59746","59747","59748","59749","59750","59751","59752","59753","59754","59755","59756","59757","59758","59759","59760","59761","59762","59763","59764","59765","59766","59767","59768","59769","59770","59771","59772","59773","59774","59775","59776","59777","59778","59779","59780","59781","59782","59783","59784","59785","59786","59787","59788","59789","59790","59791","59792","59793","59794","59795","59796","59797","59798","59799","59800","59801","59802","59803","59804","59805","59806","59807","59808","59809","59810","59811","59812","59813","59814","59815","59816","59817","59818","59819","59820","59821","59822","59823","59824","59825","59826","59827","59828","59829","59830","59831","59832","59833","59834","59835","59836","59837","59838","59839","59840","59841","59842","59843","59844","59845","59846","59847","59848","59849","59850","59851","59852","59853","59854","59855","59856","59857","59858","59859","59860","59861","59862","59863","59864","59865","59866","59867","59868","59869","59870","59871","59872","59873","59874","59875","59876","59877","59878","59879","59880","59881","59882","59883","59884","59885","59886","59887","59888","59889","59890","59891","59892","59893","59894","59895","59896","59897","59898","59899","59900","59901","59902","59903","59904","59905","59906","59907","59908","59909","59910","59911","59912","59913","59914","59915","59916","59917","59918","59919","59920","59921","59922","59923","59924","59925","59926","59927","59928","59929","59930","59931","59932","59933","59934","59935","59936","59937","59938","59939","59940","59941","59942","59943","59944","59945","59946","59947","59948","59949","59950","59951","59952","59953","59954","59955","59956","59957","59958","59959","59960","59961","59962","59963","59964","59965","59966","59967","59968","59969","59970","59971","59972","59973","59974","59975","59976","59977","59978","59979","59980","59981","59982","59983","59984","59985","59986","59987","59988","59989","59990","59991","59992","59993","59994","59995","59996","59997","59998","59999","60000","60001","60002","60003","60004","60005","60006","60007","60008","60009","60010","60011","60012","60013","60014","60015","60016","60017","60018","60019","60020","60021","60022","60023","60024","60025","60026","60027","60028","60029","60030","60031","60032","60033","60034","60035","60036","60037","60038","60039","60040","60041","60042","60043","60044","60045","60046","60047","60048","60049","60050","60051","60052","60053","60054","60055","60056","60057","60058","60059","60060","60061","60062","60063","60064","60065","60066","60067","60068","60069","60070","60071","60072","60073","60074","60075","60076","60077","60078","60079","60080","60081","60082","60083","60084","60085","60086","60087","60088","60089","60090","60091","60092","60093","60094","60095","60096","60097","60098","60099","60100","60101","60102","60103","60104","60105","60106","60107","60108","60109","60110","60111","60112","60113","60114","60115","60116","60117","60118","60119","60120","60121","60122","60123","60124","60125","60126","60127","60128","60129","60130","60131","60132","60133","60134","60135","60136","60137","60138","60139","60140","60141","60142","60143","60144","60145","60146","60147","60148","60149","60150","60151","60152","60153","60154","60155","60156","60157","60158","60159","60160","60161","60162","60163","60164","60165","60166","60167","60168","60169","60170","60171","60172","60173","60174","60175","60176","60177","60178","60179","60180","60181","60182","60183","60184","60185","60186","60187","60188","60189","60190","60191","60192","60193","60194","60195","60196","60197","60198","60199","60200","60201","60202","60203","60204","60205","60206","60207","60208","60209","60210","60211","60212","60213","60214","60215","60216","60217","60218","60219","60220","60221","60222","60223","60224","60225","60226","60227","60228","60229","60230","60231","60232","60233","60234","60235","60236","60237","60238","60239","60240","60241","60242","60243","60244","60245","60246","60247","60248","60249","60250","60251","60252","60253","60254","60255","60256","60257","60258","60259","60260","60261","60262","60263","60264","60265","60266","60267","60268","60269","60270","60271","60272","60273","60274","60275","60276","60277","60278","60279","60280","60281","60282","60283","60284","60285","60286","60287","60288","60289","60290","60291","60292","60293","60294","60295","60296","60297","60298","60299","60300","60301","60302","60303","60304","60305","60306","60307","60308","60309","60310","60311","60312","60313","60314","60315","60316","60317","60318","60319","60320","60321","60322","60323","60324","60325","60326","60327","60328","60329","60330","60331","60332","60333","60334","60335","60336","60337","60338","60339","60340","60341","60342","60343","60344","60345","60346","60347","60348","60349","60350","60351","60352","60353","60354","60355","60356","60357","60358","60359","60360","60361","60362","60363","60364","60365","60366","60367","60368","60369","60370","60371","60372","60373","60374","60375","60376","60377","60378","60379","60380","60381","60382","60383","60384","60385","60386","60387","60388","60389","60390","60391","60392","60393","60394","60395","60396","60397","60398","60399","60400","60401","60402","60403","60404","60405","60406","60407","60408","60409","60410","60411","60412","60413","60414","60415","60416","60417","60418","60419","60420","60421","60422","60423","60424","60425","60426","60427","60428","60429","60430","60431","60432","60433","60434","60435","60436","60437","60438","60439","60440","60441","60442","60443","60444","60445","60446","60447","60448","60449","60450","60451","60452","60453","60454","60455","60456","60457","60458","60459","60460","60461","60462","60463","60464","60465","60466","60467","60468","60469","60470","60471","60472","60473","60474","60475","60476","60477","60478","60479","60480","60481","60482","60483","60484","60485","60486","60487","60488","60489","60490","60491","60492","60493","60494","60495","60496","60497","60498","60499","60500","60501","60502","60503","60504","60505","60506","60507","60508","60509","60510","60511","60512","60513","60514","60515","60516","60517","60518","60519","60520","60521","60522","60523","60524","60525","60526","60527","60528","60529","60530","60531","60532","60533","60534","60535","60536","60537","60538","60539","60540","60541","60542","60543","60544","60545","60546","60547","60548","60549","60550","60551","60552","60553","60554","60555","60556","60557","60558","60559","60560","60561","60562","60563","60564","60565","60566","60567","60568","60569","60570","60571","60572","60573","60574","60575","60576","60577","60578","60579","60580","60581","60582","60583","60584","60585","60586","60587","60588","60589","60590","60591","60592","60593","60594","60595","60596","60597","60598","60599","60600","60601","60602","60603","60604","60605","60606","60607","60608","60609","60610","60611","60612","60613","60614","60615","60616","60617","60618","60619","60620","60621","60622","60623","60624","60625","60626","60627","60628","60629","60630","60631","60632","60633","60634","60635","60636","60637","60638","60639","60640","60641","60642","60643","60644","60645","60646","60647","60648","60649","60650","60651","60652","60653","60654","60655","60656","60657","60658","60659","60660","60661","60662","60663","60664","60665","60666","60667","60668","60669","60670","60671","60672","60673","60674","60675","60676","60677","60678","60679","60680","60681","60682","60683","60684","60685","60686","60687","60688","60689","60690","60691","60692","60693","60694","60695","60696","60697","60698","60699","60700","60701","60702","60703","60704","60705","60706","60707","60708","60709","60710","60711","60712","60713","60714","60715","60716","60717","60718","60719","60720","60721","60722","60723","60724","60725","60726","60727","60728","60729","60730","60731","60732","60733","60734","60735","60736","60737","60738","60739","60740","60741","60742","60743","60744","60745","60746","60747","60748","60749","60750","60751","60752","60753","60754","60755","60756","60757","60758","60759","60760","60761","60762","60763","60764","60765","60766","60767","60768","60769","60770","60771","60772","60773","60774","60775","60776","60777","60778","60779","60780","60781","60782","60783","60784","60785","60786","60787","60788","60789","60790","60791","60792","60793","60794","60795","60796","60797","60798","60799","60800","60801","60802","60803","60804","60805","60806","60807","60808","60809","60810","60811","60812","60813","60814","60815","60816","60817","60818","60819","60820","60821","60822","60823","60824","60825","60826","60827","60828","60829","60830","60831","60832","60833","60834","60835","60836","60837","60838","60839","60840","60841","60842","60843","60844","60845","60846","60847","60848","60849","60850","60851","60852","60853","60854","60855","60856","60857","60858","60859","60860","60861","60862","60863","60864","60865","60866","60867","60868","60869","60870","60871","60872","60873","60874","60875","60876","60877","60878","60879","60880","60881","60882","60883","60884","60885","60886","60887","60888","60889","60890","60891","60892","60893","60894","60895","60896","60897","60898","60899","60900","60901","60902","60903","60904","60905","60906","60907","60908","60909","60910","60911","60912","60913","60914","60915","60916","60917","60918","60919","60920","60921","60922","60923","60924","60925","60926","60927","60928","60929","60930","60931","60932","60933","60934","60935","60936","60937","60938","60939","60940","60941","60942","60943","60944","60945","60946","60947","60948","60949","60950","60951","60952","60953","60954","60955","60956","60957","60958","60959","60960","60961","60962","60963","60964","60965","60966","60967","60968","60969","60970","60971","60972","60973","60974","60975","60976","60977","60978","60979","60980","60981","60982","60983","60984","60985","60986","60987","60988","60989","60990","60991","60992","60993","60994","60995","60996","60997","60998","60999","61000","61001","61002","61003","61004","61005","61006","61007","61008","61009","61010","61011","61012","61013","61014","61015","61016","61017","61018","61019","61020","61021","61022","61023","61024","61025","61026","61027","61028","61029","61030","61031","61032","61033","61034","61035","61036","61037","61038","61039","61040","61041","61042","61043","61044","61045","61046","61047","61048","61049","61050","61051","61052","61053","61054","61055","61056","61057","61058","61059","61060","61061","61062","61063","61064","61065","61066","61067","61068","61069","61070","61071","61072","61073","61074","61075","61076","61077","61078","61079","61080","61081","61082","61083","61084","61085","61086","61087","61088","61089","61090","61091","61092","61093","61094","61095","61096","61097","61098","61099","61100","61101","61102","61103","61104","61105","61106","61107","61108","61109","61110","61111","61112","61113","61114","61115","61116","61117","61118","61119","61120","61121","61122","61123","61124","61125","61126","61127","61128","61129","61130","61131","61132","61133","61134","61135","61136","61137","61138","61139","61140","61141","61142","61143","61144","61145","61146","61147","61148","61149","61150","61151","61152","61153","61154","61155","61156","61157","61158","61159","61160","61161","61162","61163","61164","61165","61166","61167","61168","61169","61170","61171","61172","61173","61174","61175","61176","61177","61178","61179","61180","61181","61182","61183","61184","61185","61186","61187","61188","61189","61190","61191","61192","61193","61194","61195","61196","61197","61198","61199","61200","61201","61202","61203","61204","61205","61206","61207","61208","61209","61210","61211","61212","61213","61214","61215","61216","61217","61218","61219","61220","61221","61222","61223","61224","61225","61226","61227","61228","61229","61230","61231","61232","61233","61234","61235","61236","61237","61238","61239","61240","61241","61242","61243","61244","61245","61246","61247","61248","61249","61250","61251","61252","61253","61254","61255","61256","61257","61258","61259","61260","61261","61262","61263","61264","61265","61266","61267","61268","61269","61270","61271","61272","61273","61274","61275","61276","61277","61278","61279","61280","61281","61282","61283","61284","61285","61286","61287","61288","61289","61290","61291","61292","61293","61294","61295","61296","61297","61298","61299","61300","61301","61302","61303","61304","61305","61306","61307","61308","61309","61310","61311","61312","61313","61314","61315","61316","61317","61318","61319","61320","61321","61322","61323","61324","61325","61326","61327","61328","61329","61330","61331","61332","61333","61334","61335","61336","61337","61338","61339","61340","61341","61342","61343","61344","61345","61346","61347","61348","61349","61350","61351","61352","61353","61354","61355","61356","61357","61358","61359","61360","61361","61362","61363","61364","61365","61366","61367","61368","61369","61370","61371","61372","61373","61374","61375","61376","61377","61378","61379","61380","61381","61382","61383","61384","61385","61386","61387","61388","61389","61390","61391","61392","61393","61394","61395","61396","61397","61398","61399","61400","61401","61402","61403","61404","61405","61406","61407","61408","61409","61410","61411","61412","61413","61414","61415","61416","61417","61418","61419","61420","61421","61422","61423","61424","61425","61426","61427","61428","61429","61430","61431","61432","61433","61434","61435","61436","61437","61438","61439","61440","61441","61442","61443","61444","61445","61446","61447","61448","61449","61450","61451","61452","61453","61454","61455","61456","61457","61458","61459","61460","61461","61462","61463","61464","61465","61466","61467","61468","61469","61470","61471","61472","61473","61474","61475","61476","61477","61478","61479","61480","61481","61482","61483","61484","61485","61486","61487","61488","61489","61490","61491","61492","61493","61494","61495","61496","61497","61498","61499","61500","61501","61502","61503","61504","61505","61506","61507","61508","61509","61510","61511","61512","61513","61514","61515","61516","61517","61518","61519","61520","61521","61522","61523","61524","61525","61526","61527","61528","61529","61530","61531","61532","61533","61534","61535","61536","61537","61538","61539","61540","61541","61542","61543","61544","61545","61546","61547","61548","61549","61550","61551","61552","61553","61554","61555","61556","61557","61558","61559","61560","61561","61562","61563","61564","61565","61566","61567","61568","61569","61570","61571","61572","61573","61574","61575","61576","61577","61578","61579","61580","61581","61582","61583","61584","61585","61586","61587","61588","61589","61590","61591","61592","61593","61594","61595","61596","61597","61598","61599","61600","61601","61602","61603","61604","61605","61606","61607","61608","61609","61610","61611","61612","61613","61614","61615","61616","61617","61618","61619","61620","61621","61622","61623","61624","61625","61626","61627","61628","61629","61630","61631","61632","61633","61634","61635","61636","61637","61638","61639","61640","61641","61642","61643","61644","61645","61646","61647","61648","61649","61650","61651","61652","61653","61654","61655","61656","61657","61658","61659","61660","61661","61662","61663","61664","61665","61666","61667","61668","61669","61670","61671","61672","61673","61674","61675","61676","61677","61678","61679","61680","61681","61682","61683","61684","61685","61686","61687","61688","61689","61690","61691","61692","61693","61694","61695","61696","61697","61698","61699","61700","61701","61702","61703","61704","61705","61706","61707","61708","61709","61710","61711","61712","61713","61714","61715","61716","61717","61718","61719","61720","61721","61722","61723","61724","61725","61726","61727","61728","61729","61730","61731","61732","61733","61734","61735","61736","61737","61738","61739","61740","61741","61742","61743","61744","61745","61746","61747","61748","61749","61750","61751","61752","61753","61754","61755","61756","61757","61758","61759","61760","61761","61762","61763","61764","61765","61766","61767","61768","61769","61770","61771","61772","61773","61774","61775","61776","61777","61778","61779","61780","61781","61782","61783","61784","61785","61786","61787","61788","61789","61790","61791","61792","61793","61794","61795","61796","61797","61798","61799","61800","61801","61802","61803","61804","61805","61806","61807","61808","61809","61810","61811","61812","61813","61814","61815","61816","61817","61818","61819","61820","61821","61822","61823","61824","61825","61826","61827","61828","61829","61830","61831","61832","61833","61834","61835","61836","61837","61838","61839","61840","61841","61842","61843","61844","61845","61846","61847","61848","61849","61850","61851","61852","61853","61854","61855","61856","61857","61858","61859","61860","61861","61862","61863","61864","61865","61866","61867","61868","61869","61870","61871","61872","61873","61874","61875","61876","61877","61878","61879","61880","61881","61882","61883","61884","61885","61886","61887","61888","61889","61890","61891","61892","61893","61894","61895","61896","61897","61898","61899","61900","61901","61902","61903","61904","61905","61906","61907","61908","61909","61910","61911","61912","61913","61914","61915","61916","61917","61918","61919","61920","61921","61922","61923","61924","61925","61926","61927","61928","61929","61930","61931","61932","61933","61934","61935","61936","61937","61938","61939","61940","61941","61942","61943","61944","61945","61946","61947","61948","61949","61950","61951","61952","61953","61954","61955","61956","61957","61958","61959","61960","61961","61962","61963","61964","61965","61966","61967","61968","61969","61970","61971","61972","61973","61974","61975","61976","61977","61978","61979","61980","61981","61982","61983","61984","61985","61986","61987","61988","61989","61990","61991","61992","61993","61994","61995","61996","61997","61998","61999","62000","62001","62002","62003","62004","62005","62006","62007","62008","62009","62010","62011","62012","62013","62014","62015","62016","62017","62018","62019","62020","62021","62022","62023","62024","62025","62026","62027","62028","62029","62030","62031","62032","62033","62034","62035","62036","62037","62038","62039","62040","62041","62042","62043","62044","62045","62046","62047","62048","62049","62050","62051","62052","62053","62054","62055","62056","62057","62058","62059","62060","62061","62062","62063","62064","62065","62066","62067","62068","62069","62070","62071","62072","62073","62074","62075","62076","62077","62078","62079","62080","62081","62082","62083","62084","62085","62086","62087","62088","62089","62090","62091","62092","62093","62094","62095","62096","62097","62098","62099","62100","62101","62102","62103","62104","62105","62106","62107","62108","62109","62110","62111","62112","62113","62114","62115","62116","62117","62118","62119","62120","62121","62122","62123","62124","62125","62126","62127","62128","62129","62130","62131","62132","62133","62134","62135","62136","62137","62138","62139","62140","62141","62142","62143","62144","62145","62146","62147","62148","62149","62150","62151","62152","62153","62154","62155","62156","62157","62158","62159","62160","62161","62162","62163","62164","62165","62166","62167","62168","62169","62170","62171","62172","62173","62174","62175","62176","62177","62178","62179","62180","62181","62182","62183","62184","62185","62186","62187","62188","62189","62190","62191","62192","62193","62194","62195","62196","62197","62198","62199","62200","62201","62202","62203","62204","62205","62206","62207","62208","62209","62210","62211","62212","62213","62214","62215","62216","62217","62218","62219","62220","62221","62222","62223","62224","62225","62226","62227","62228","62229","62230","62231","62232","62233","62234","62235","62236","62237","62238","62239","62240","62241","62242","62243","62244","62245","62246","62247","62248","62249","62250","62251","62252","62253","62254","62255","62256","62257","62258","62259","62260","62261","62262","62263","62264","62265","62266","62267","62268","62269","62270","62271","62272","62273","62274","62275","62276","62277","62278","62279","62280","62281","62282","62283","62284","62285","62286","62287","62288","62289","62290","62291","62292","62293","62294","62295","62296","62297","62298","62299","62300","62301","62302","62303","62304","62305","62306","62307","62308","62309","62310","62311","62312","62313","62314","62315","62316","62317","62318","62319","62320","62321","62322","62323","62324","62325","62326","62327","62328","62329","62330","62331","62332","62333","62334","62335","62336","62337","62338","62339","62340","62341","62342","62343","62344","62345","62346","62347","62348","62349","62350","62351","62352","62353","62354","62355","62356","62357","62358","62359","62360","62361","62362","62363","62364","62365","62366","62367","62368","62369","62370","62371","62372","62373","62374","62375","62376","62377","62378","62379","62380","62381","62382","62383","62384","62385","62386","62387","62388","62389","62390","62391","62392","62393","62394","62395","62396","62397","62398","62399","62400","62401","62402","62403","62404","62405","62406","62407","62408","62409","62410","62411","62412","62413","62414","62415","62416","62417","62418","62419","62420","62421","62422","62423","62424","62425","62426","62427","62428","62429","62430","62431","62432","62433","62434","62435","62436","62437","62438","62439","62440","62441","62442","62443","62444","62445","62446","62447","62448","62449","62450","62451","62452","62453","62454","62455","62456","62457","62458","62459","62460","62461","62462","62463","62464","62465","62466","62467","62468","62469","62470","62471","62472","62473","62474","62475","62476","62477","62478","62479","62480","62481","62482","62483","62484","62485","62486","62487","62488","62489","62490","62491","62492","62493","62494","62495","62496","62497","62498","62499","62500","62501","62502","62503","62504","62505","62506","62507","62508","62509","62510","62511","62512","62513","62514","62515","62516","62517","62518","62519","62520","62521","62522","62523","62524","62525","62526","62527","62528","62529","62530","62531","62532","62533","62534","62535","62536","62537","62538","62539","62540","62541","62542","62543","62544","62545","62546","62547","62548","62549","62550","62551","62552","62553","62554","62555","62556","62557","62558","62559","62560","62561","62562","62563","62564","62565","62566","62567","62568","62569","62570","62571","62572","62573","62574","62575","62576","62577","62578","62579","62580","62581","62582","62583","62584","62585","62586","62587","62588","62589","62590","62591","62592","62593","62594","62595","62596","62597","62598","62599","62600","62601","62602","62603","62604","62605","62606","62607","62608","62609","62610","62611","62612","62613","62614","62615","62616","62617","62618","62619","62620","62621","62622","62623","62624","62625","62626","62627","62628","62629","62630","62631","62632","62633","62634","62635","62636","62637","62638","62639","62640","62641","62642","62643","62644","62645","62646","62647","62648","62649","62650","62651","62652","62653","62654","62655","62656","62657","62658","62659","62660","62661","62662","62663","62664","62665","62666","62667","62668","62669","62670","62671","62672","62673","62674","62675","62676","62677","62678","62679","62680","62681","62682","62683","62684","62685","62686","62687","62688","62689","62690","62691","62692","62693","62694","62695","62696","62697","62698","62699","62700","62701","62702","62703","62704","62705","62706","62707","62708","62709","62710","62711","62712","62713","62714","62715","62716","62717","62718","62719","62720","62721","62722","62723","62724","62725","62726","62727","62728","62729","62730","62731","62732","62733","62734","62735","62736","62737","62738","62739","62740","62741","62742","62743","62744","62745","62746","62747","62748","62749","62750","62751","62752","62753","62754","62755","62756","62757","62758","62759","62760","62761","62762","62763","62764","62765","62766","62767","62768","62769","62770","62771","62772","62773","62774","62775","62776","62777","62778","62779","62780","62781","62782","62783","62784","62785","62786","62787","62788","62789","62790","62791","62792","62793","62794","62795","62796","62797","62798","62799","62800","62801","62802","62803","62804","62805","62806","62807","62808","62809","62810","62811","62812","62813","62814","62815","62816","62817","62818","62819","62820","62821","62822","62823","62824","62825","62826","62827","62828","62829","62830","62831","62832","62833","62834","62835","62836","62837","62838","62839","62840","62841","62842","62843","62844","62845","62846","62847","62848","62849","62850","62851","62852","62853","62854","62855","62856","62857","62858","62859","62860","62861","62862","62863","62864","62865","62866","62867","62868","62869","62870","62871","62872","62873","62874","62875","62876","62877","62878","62879","62880","62881","62882","62883","62884","62885","62886","62887","62888","62889","62890","62891","62892","62893","62894","62895","62896","62897","62898","62899","62900","62901","62902","62903","62904","62905","62906","62907","62908","62909","62910","62911","62912","62913","62914","62915","62916","62917","62918","62919","62920","62921","62922","62923","62924","62925","62926","62927","62928","62929","62930","62931","62932","62933","62934","62935","62936","62937","62938","62939","62940","62941","62942","62943","62944","62945","62946","62947","62948","62949","62950","62951","62952","62953","62954","62955","62956","62957","62958","62959","62960","62961","62962","62963","62964","62965","62966","62967","62968","62969","62970","62971","62972","62973","62974","62975","62976","62977","62978","62979","62980","62981","62982","62983","62984","62985","62986","62987","62988","62989","62990","62991","62992","62993","62994","62995","62996","62997","62998","62999","63000","63001","63002","63003","63004","63005","63006","63007","63008","63009","63010","63011","63012","63013","63014","63015","63016","63017","63018","63019","63020","63021","63022","63023","63024","63025","63026","63027","63028","63029","63030","63031","63032","63033","63034","63035","63036","63037","63038","63039","63040","63041","63042","63043","63044","63045","63046","63047","63048","63049","63050","63051","63052","63053","63054","63055","63056","63057","63058","63059","63060","63061","63062","63063","63064","63065","63066","63067","63068","63069","63070","63071","63072","63073","63074","63075","63076","63077","63078","63079","63080","63081","63082","63083","63084","63085","63086","63087","63088","63089","63090","63091","63092","63093","63094","63095","63096","63097","63098","63099","63100","63101","63102","63103","63104","63105","63106","63107","63108","63109","63110","63111","63112","63113","63114","63115","63116","63117","63118","63119","63120","63121","63122","63123","63124","63125","63126","63127","63128","63129","63130","63131","63132","63133","63134","63135","63136","63137","63138","63139","63140","63141","63142","63143","63144","63145","63146","63147","63148","63149","63150","63151","63152","63153","63154","63155","63156","63157","63158","63159","63160","63161","63162","63163","63164","63165","63166","63167","63168","63169","63170","63171","63172","63173","63174","63175","63176","63177","63178","63179","63180","63181","63182","63183","63184","63185","63186","63187","63188","63189","63190","63191","63192","63193","63194","63195","63196","63197","63198","63199","63200","63201","63202","63203","63204","63205","63206","63207","63208","63209","63210","63211","63212","63213","63214","63215","63216","63217","63218","63219","63220","63221","63222","63223","63224","63225","63226","63227","63228","63229","63230","63231","63232","63233","63234","63235","63236","63237","63238","63239","63240","63241","63242","63243","63244","63245","63246","63247","63248","63249","63250","63251","63252","63253","63254","63255","63256","63257","63258","63259","63260","63261","63262","63263","63264","63265","63266","63267","63268","63269","63270","63271","63272","63273","63274","63275","63276","63277","63278","63279","63280","63281","63282","63283","63284","63285","63286","63287","63288","63289","63290","63291","63292","63293","63294","63295","63296","63297","63298","63299","63300","63301","63302","63303","63304","63305","63306","63307","63308","63309","63310","63311","63312","63313","63314","63315","63316","63317","63318","63319","63320","63321","63322","63323","63324","63325","63326","63327","63328","63329","63330","63331","63332","63333","63334","63335","63336","63337","63338","63339","63340","63341","63342","63343","63344","63345","63346","63347","63348","63349","63350","63351","63352","63353","63354","63355","63356","63357","63358","63359","63360","63361","63362","63363","63364","63365","63366","63367","63368","63369","63370","63371","63372","63373","63374","63375","63376","63377","63378","63379","63380","63381","63382","63383","63384","63385","63386","63387","63388","63389","63390","63391","63392","63393","63394","63395","63396","63397","63398","63399","63400","63401","63402","63403","63404","63405","63406","63407","63408","63409","63410","63411","63412","63413","63414","63415","63416","63417","63418","63419","63420","63421","63422","63423","63424","63425","63426","63427","63428","63429","63430","63431","63432","63433","63434","63435","63436","63437","63438","63439","63440","63441","63442","63443","63444","63445","63446","63447","63448","63449","63450","63451","63452","63453","63454","63455","63456","63457","63458","63459","63460","63461","63462","63463","63464","63465","63466","63467","63468","63469","63470","63471","63472","63473","63474","63475","63476","63477","63478","63479","63480","63481","63482","63483","63484","63485","63486","63487","63488","63489","63490","63491","63492","63493","63494","63495","63496","63497","63498","63499","63500","63501","63502","63503","63504","63505","63506","63507","63508","63509","63510","63511","63512","63513","63514","63515","63516","63517","63518","63519","63520","63521","63522","63523","63524","63525","63526","63527","63528","63529","63530","63531","63532","63533","63534","63535","63536","63537","63538","63539","63540","63541","63542","63543","63544","63545","63546","63547","63548","63549","63550","63551","63552","63553","63554","63555","63556","63557","63558","63559","63560","63561","63562","63563","63564","63565","63566","63567","63568","63569","63570","63571","63572","63573","63574","63575","63576","63577","63578","63579","63580","63581","63582","63583","63584","63585","63586","63587","63588","63589","63590","63591","63592","63593","63594","63595","63596","63597","63598","63599","63600","63601","63602","63603","63604","63605","63606","63607","63608","63609","63610","63611","63612","63613","63614","63615","63616","63617","63618","63619","63620","63621","63622","63623","63624","63625","63626","63627","63628","63629","63630","63631","63632","63633","63634","63635","63636","63637","63638","63639","63640","63641","63642","63643","63644","63645","63646","63647","63648","63649","63650","63651","63652","63653","63654","63655","63656","63657","63658","63659","63660","63661","63662","63663","63664","63665","63666","63667","63668","63669","63670","63671","63672","63673","63674","63675","63676","63677","63678","63679","63680","63681","63682","63683","63684","63685","63686","63687","63688","63689","63690","63691","63692","63693","63694","63695","63696","63697","63698","63699","63700","63701","63702","63703","63704","63705","63706","63707","63708","63709","63710","63711","63712","63713","63714","63715","63716","63717","63718","63719","63720","63721","63722","63723","63724","63725","63726","63727","63728","63729","63730","63731","63732","63733","63734","63735","63736","63737","63738","63739","63740","63741","63742","63743","63744","63745","63746","63747","63748","63749","63750","63751","63752","63753","63754","63755","63756","63757","63758","63759","63760","63761","63762","63763","63764","63765","63766","63767","63768","63769","63770","63771","63772","63773","63774","63775","63776","63777","63778","63779","63780","63781","63782","63783","63784","63785","63786","63787","63788","63789","63790","63791","63792","63793","63794","63795","63796","63797","63798","63799","63800","63801","63802","63803","63804","63805","63806","63807","63808","63809","63810","63811","63812","63813","63814","63815","63816","63817","63818","63819","63820","63821","63822","63823","63824","63825","63826","63827","63828","63829","63830","63831","63832","63833","63834","63835","63836","63837","63838","63839","63840","63841","63842","63843","63844","63845","63846","63847","63848","63849","63850","63851","63852","63853","63854","63855","63856","63857","63858","63859","63860","63861","63862","63863","63864","63865","63866","63867","63868","63869","63870","63871","63872","63873","63874","63875","63876","63877","63878","63879","63880","63881","63882","63883","63884","63885","63886","63887","63888","63889","63890","63891","63892","63893","63894","63895","63896","63897","63898","63899","63900","63901","63902","63903","63904","63905","63906","63907","63908","63909","63910","63911","63912","63913","63914","63915","63916","63917","63918","63919","63920","63921","63922","63923","63924","63925","63926","63927","63928","63929","63930","63931","63932","63933","63934","63935","63936","63937","63938","63939","63940","63941","63942","63943","63944","63945","63946","63947","63948","63949","63950","63951","63952","63953","63954","63955","63956","63957","63958","63959","63960","63961","63962","63963","63964","63965","63966","63967","63968","63969","63970","63971","63972","63973","63974","63975","63976","63977","63978","63979","63980","63981","63982","63983","63984","63985","63986","63987","63988","63989","63990","63991","63992","63993","63994","63995","63996","63997","63998","63999","64000","64001","64002","64003","64004","64005","64006","64007","64008","64009","64010","64011","64012","64013","64014","64015","64016","64017","64018","64019","64020","64021","64022","64023","64024","64025","64026","64027","64028","64029","64030","64031","64032","64033","64034","64035","64036","64037","64038","64039","64040","64041","64042","64043","64044","64045","64046","64047","64048","64049","64050","64051","64052","64053","64054","64055","64056","64057","64058","64059","64060","64061","64062","64063","64064","64065","64066","64067","64068","64069","64070","64071","64072","64073","64074","64075","64076","64077","64078","64079","64080","64081","64082","64083","64084","64085","64086","64087","64088","64089","64090","64091","64092","64093","64094","64095","64096","64097","64098","64099","64100","64101","64102","64103","64104","64105","64106","64107","64108","64109","64110","64111","64112","64113","64114","64115","64116","64117","64118","64119","64120","64121","64122","64123","64124","64125","64126","64127","64128","64129","64130","64131","64132","64133","64134","64135","64136","64137","64138","64139","64140","64141","64142","64143","64144","64145","64146","64147","64148","64149","64150","64151","64152","64153","64154","64155","64156","64157","64158","64159","64160","64161","64162","64163","64164","64165","64166","64167","64168","64169","64170","64171","64172","64173","64174","64175","64176","64177","64178","64179","64180","64181","64182","64183","64184","64185","64186","64187","64188","64189","64190","64191","64192","64193","64194","64195","64196","64197","64198","64199","64200","64201","64202","64203","64204","64205","64206","64207","64208","64209","64210","64211","64212","64213","64214","64215","64216","64217","64218","64219","64220","64221","64222","64223","64224","64225","64226","64227","64228","64229","64230","64231","64232","64233","64234","64235","64236","64237","64238","64239","64240","64241","64242","64243","64244","64245","64246","64247","64248","64249","64250","64251","64252","64253","64254","64255","64256","64257","64258","64259","64260","64261","64262","64263","64264","64265","64266","64267","64268","64269","64270","64271","64272","64273","64274","64275","64276","64277","64278","64279","64280","64281","64282","64283","64284","64285","64286","64287","64288","64289","64290","64291","64292","64293","64294","64295","64296","64297","64298","64299","64300","64301","64302","64303","64304","64305","64306","64307","64308","64309","64310","64311","64312","64313","64314","64315","64316","64317","64318","64319","64320","64321","64322","64323","64324","64325","64326","64327","64328","64329","64330","64331","64332","64333","64334","64335","64336","64337","64338","64339","64340","64341","64342","64343","64344","64345","64346","64347","64348","64349","64350","64351","64352","64353","64354","64355","64356","64357","64358","64359","64360","64361","64362","64363","64364","64365","64366","64367","64368","64369","64370","64371","64372","64373","64374","64375","64376","64377","64378","64379","64380","64381","64382","64383","64384","64385","64386","64387","64388","64389","64390","64391","64392","64393","64394","64395","64396","64397","64398","64399","64400","64401","64402","64403","64404","64405","64406","64407","64408","64409","64410","64411","64412","64413","64414","64415","64416","64417","64418","64419","64420","64421","64422","64423","64424","64425","64426","64427","64428","64429","64430","64431","64432","64433","64434","64435","64436","64437","64438","64439","64440","64441","64442","64443","64444","64445","64446","64447","64448","64449","64450","64451","64452","64453","64454","64455","64456","64457","64458","64459","64460","64461","64462","64463","64464","64465","64466","64467","64468","64469","64470","64471","64472","64473","64474","64475","64476","64477","64478","64479","64480","64481","64482","64483","64484","64485","64486","64487","64488","64489","64490","64491","64492","64493","64494","64495","64496","64497","64498","64499","64500","64501","64502","64503","64504","64505","64506","64507","64508","64509","64510","64511","64512","64513","64514","64515","64516","64517","64518","64519","64520","64521","64522","64523","64524","64525","64526","64527","64528","64529","64530","64531","64532","64533","64534","64535","64536","64537","64538","64539","64540","64541","64542","64543","64544","64545","64546","64547","64548","64549","64550","64551","64552","64553","64554","64555","64556","64557","64558","64559","64560","64561","64562","64563","64564","64565","64566","64567","64568","64569","64570","64571","64572","64573","64574","64575","64576","64577","64578","64579","64580","64581","64582","64583","64584","64585","64586","64587","64588","64589","64590","64591","64592","64593","64594","64595","64596","64597","64598","64599","64600","64601","64602","64603","64604","64605","64606","64607","64608","64609","64610","64611","64612","64613","64614","64615","64616","64617","64618","64619","64620","64621","64622","64623","64624","64625","64626","64627","64628","64629","64630","64631","64632","64633","64634","64635","64636","64637","64638","64639","64640","64641","64642","64643","64644","64645","64646","64647","64648","64649","64650","64651","64652","64653","64654","64655","64656","64657","64658","64659","64660","64661","64662","64663","64664","64665","64666","64667","64668","64669","64670","64671","64672","64673","64674","64675","64676","64677","64678","64679","64680","64681","64682","64683","64684","64685","64686","64687","64688","64689","64690","64691","64692","64693","64694","64695","64696","64697","64698","64699","64700","64701","64702","64703","64704","64705","64706","64707","64708","64709","64710","64711","64712","64713","64714","64715","64716","64717","64718","64719","64720","64721","64722","64723","64724","64725","64726","64727","64728","64729","64730","64731","64732","64733","64734","64735","64736","64737","64738","64739","64740","64741","64742","64743","64744","64745","64746","64747","64748","64749","64750","64751","64752","64753","64754","64755","64756","64757","64758","64759","64760","64761","64762","64763","64764","64765","64766","64767","64768","64769","64770","64771","64772","64773","64774","64775","64776","64777","64778","64779","64780","64781","64782","64783","64784","64785","64786","64787","64788","64789","64790","64791","64792","64793","64794","64795","64796","64797","64798","64799","64800","64801","64802","64803","64804","64805","64806","64807","64808","64809","64810","64811","64812","64813","64814","64815","64816","64817","64818","64819","64820","64821","64822","64823","64824","64825","64826","64827","64828","64829","64830","64831","64832","64833","64834","64835","64836","64837","64838","64839","64840","64841","64842","64843","64844","64845","64846","64847","64848","64849","64850","64851","64852","64853","64854","64855","64856","64857","64858","64859","64860","64861","64862","64863","64864","64865","64866","64867","64868","64869","64870","64871","64872","64873","64874","64875","64876","64877","64878","64879","64880","64881","64882","64883","64884","64885","64886","64887","64888","64889","64890","64891","64892","64893","64894","64895","64896","64897","64898","64899","64900","64901","64902","64903","64904","64905","64906","64907","64908","64909","64910","64911","64912","64913","64914","64915","64916","64917","64918","64919","64920","64921","64922","64923","64924","64925","64926","64927","64928","64929","64930","64931","64932","64933","64934","64935","64936","64937","64938","64939","64940","64941","64942","64943","64944","64945","64946","64947","64948","64949","64950","64951","64952","64953","64954","64955","64956","64957","64958","64959","64960","64961","64962","64963","64964","64965","64966","64967","64968","64969","64970","64971","64972","64973","64974","64975","64976","64977","64978","64979","64980","64981","64982","64983","64984","64985","64986","64987","64988","64989","64990","64991","64992","64993","64994","64995","64996","64997","64998","64999","65000","65001","65002","65003","65004","65005","65006","65007","65008","65009","65010","65011","65012","65013","65014","65015","65016","65017","65018","65019","65020","65021","65022","65023","65024","65025","65026","65027","65028","65029","65030","65031","65032","65033","65034","65035","65036","65037","65038","65039","65040","65041","65042","65043","65044","65045","65046","65047","65048","65049","65050","65051","65052","65053","65054","65055","65056","65057","65058","65059","65060","65061","65062","65063","65064","65065","65066","65067","65068","65069","65070","65071","65072","65073","65074","65075","65076","65077","65078","65079","65080","65081","65082","65083","65084","65085","65086","65087","65088","65089","65090","65091","65092","65093","65094","65095","65096","65097","65098","65099","65100","65101","65102","65103","65104","65105","65106","65107","65108","65109","65110","65111","65112","65113","65114","65115","65116","65117","65118","65119","65120","65121","65122","65123","65124","65125","65126","65127","65128","65129","65130","65131","65132","65133","65134","65135","65136","65137","65138","65139","65140","65141","65142","65143","65144","65145","65146","65147","65148","65149","65150","65151","65152","65153","65154","65155","65156","65157","65158","65159","65160","65161","65162","65163","65164","65165","65166","65167","65168","65169","65170","65171","65172","65173","65174","65175","65176","65177","65178","65179","65180","65181","65182","65183","65184","65185","65186","65187","65188","65189","65190","65191","65192","65193","65194","65195","65196","65197","65198","65199","65200","65201","65202","65203","65204","65205","65206","65207","65208","65209","65210","65211","65212","65213","65214","65215","65216","65217","65218","65219","65220","65221","65222","65223","65224","65225","65226","65227","65228","65229","65230","65231","65232","65233","65234","65235","65236","65237","65238","65239","65240","65241","65242","65243","65244","65245","65246","65247","65248","65249","65250","65251","65252","65253","65254","65255","65256","65257","65258","65259","65260","65261","65262","65263","65264","65265","65266","65267","65268","65269","65270","65271","65272","65273","65274","65275","65276","65277","65278","65279","65280","65281","65282","65283","65284","65285","65286","65287","65288","65289","65290","65291","65292","65293","65294","65295","65296","65297","65298","65299","65300","65301","65302","65303","65304","65305","65306","65307","65308","65309","65310","65311","65312","65313","65314","65315","65316","65317","65318","65319","65320","65321","65322","65323","65324","65325","65326","65327","65328","65329","65330","65331","65332","65333","65334","65335","65336","65337","65338","65339","65340","65341","65342","65343","65344","65345","65346","65347","65348","65349","65350","65351","65352","65353","65354","65355","65356","65357","65358","65359","65360","65361","65362","65363","65364","65365","65366","65367","65368","65369","65370","65371","65372","65373","65374","65375","65376","65377","65378","65379","65380","65381","65382","65383","65384","65385","65386","65387","65388","65389","65390","65391","65392","65393","65394","65395","65396","65397","65398","65399","65400","65401","65402","65403","65404","65405","65406","65407","65408","65409","65410","65411","65412","65413","65414","65415","65416","65417","65418","65419","65420","65421","65422","65423","65424","65425","65426","65427","65428","65429","65430","65431","65432","65433","65434","65435","65436","65437","65438","65439","65440","65441","65442","65443","65444","65445","65446","65447","65448","65449","65450","65451","65452","65453","65454","65455","65456","65457","65458","65459","65460","65461","65462","65463","65464","65465","65466","65467","65468","65469","65470","65471","65472","65473","65474","65475","65476","65477","65478","65479","65480","65481","65482","65483","65484","65485","65486","65487","65488","65489","65490","65491","65492","65493","65494","65495","65496","65497","65498","65499","65500","65501"]}
diff --git a/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/0/0.json b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/0/0.json
new file mode 100644
index 0000000..7ad337b
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/0/0.json
@@ -0,0 +1 @@
+{"keys": ["", "71", "24", "245", "207", "238", "82", "132", "205", "51", "65", "242", "231", "186", "165", "114", "120", "2", "13", "235", "116", "113", "41", "234", "34", "90", "78", "48", "173", "215", "150", "75", "79", "224", "42", "181", "158", "38", "225", "211", "11", "208", "212", "167", "76", "39", "164", "77", "183", "104", "87", "95", "157", "59", "49", "21"], "data": {"150": {"NAME": "United States Minor Outlying Islands", "POP2005": 0}, "215": {"NAME": "United States Virgin Islands", "POP2005": 111408}, "212": {"NAME": "Venezuela", "POP2005": 26725573}, "157": {"NAME": "Suriname", "POP2005": 452468}, "211": {"NAME": "Saint Vincent and the Grenadines", "POP2005": 119137}, "158": {"NAME": "Nicaragua", "POP2005": 5462539}, "132": {"NAME": "Faroe Islands", "POP2005": 48205}, "116": {"NAME": "Mauritania", "POP2005": 2963105}, "238": {"NAME": "Svalbard", "POP2005": 0}, "65": {"NAME": "France", "POP2005": 60990544}, "113": {"NAME": "Mali", "POP2005": 1161109}, "90": {"NAME": "Jamaica", "POP2005": 2682469}, "234": {"NAME": "Turks and Caicos Islands", "POP2005": 24459}, "235": {"NAME": "Western Sahara", "POP2005": 440428}, "173": {"NAME": "Puerto Rico", "POP2005": 3946779}, "231": {"NAME": "Saint Pierre and Miquelon", "POP2005": 6346}, "24": {"NAME": "Canada", "POP2005": 32270507}, "224": {"NAME": "Guadeloupe", "POP2005": 438403}, "21": {"NAME": "Brazil", "POP2005": 186830759}, "48": {"NAME": "Dominican Republic", "POP2005": 9469601}, "49": {"NAME": "Ecuador", "POP2005": 13060993}, "82": {"NAME": "Iceland", "POP2005": 295732}, "42": {"NAME": "Cape Verde", "POP2005": 506807}, "41": {"NAME": "Cuba", "POP2005": 11259905}, "183": {"NAME": "Sierra Leone", "POP2005": 5586403}, "181": {"NAME": "Senegal", "POP2005": 1177034}, "186": {"NAME": "Spain", "POP2005": 43397491}, "79": {"NAME": "Honduras", "POP2005": 683411}, "87": {"NAME": "Cote d'Ivoire", "POP2005": 18584701}, "205": {"NAME": "United Kingdom", "POP2005": 60244834}, "207": {"NAME": "United States", "POP2005": 299846449}, "208": {"NAME": "Burkina Faso", "POP2005": 13933363}, "39": {"NAME": "Costa Rica", "POP2005": 4327228}, "120": {"NAME": "Mexico", "POP2005": 104266392}, "76": {"NAME": "Guinea", "POP2005": 9002656}, "2": {"NAME": "Algeria", "POP2005": 32854159}, "71": {"NAME": "Greenland", "POP2005": 57475}, "242": {"NAME": "Jersey", "POP2005": 0}, "164": {"NAME": "Panama", "POP2005": 3231502}, "165": {"NAME": "Portugal", "POP2005": 10528226}, "225": {"NAME": "Netherlands Antilles", "POP2005": 186392}, "167": {"NAME": "Guinea-Bissau", "POP2005": 1596929}, "95": {"NAME": "Kiribati", "POP2005": 92003}, "104": {"NAME": "Liberia", "POP2005": 3441796}, "78": {"NAME": "Haiti", "POP2005": 9296291}, "11": {"NAME": "Barbados", "POP2005": 291933}, "245": {"NAME": "Russia", "POP2005": 143953092}, "13": {"NAME": "Bahamas", "POP2005": 323295}, "38": {"NAME": "Colombia", "POP2005": 4494579}, "59": {"NAME": "French Guiana", "POP2005": 192099}, "114": {"NAME": "Morocco", "POP2005": 30494991}, "51": {"NAME": "Ireland", "POP2005": 4143294}, "75": {"NAME": "Guatemala", "POP2005": 12709564}, "34": {"NAME": "Cayman Islands", "POP2005": 45591}, "77": {"NAME": "Guyana", "POP2005": 739472}}, "grid": [" ", " ", " ", " ", " ", " ", " !!!!!!! ", " ###### !!!!!!!!! ", " ####### !!!!!!!!!! ", " ########## !!!!!!!!!!!! ! ", " ##########!!!!!!!!!!!!!!!!! ", " # ##########!!!!!!!!!!!!!!!!!! ", " ##########!!!!!!!!!!!!!!!!!!! ", " ##########!!!!!!!!!!!!!!!!!!! ", " # ##########!!!!!!!!!!!!!!!!!!! ", " ########## !!!!!!!!!!!!!!!!! ", " ### ####### !!!!!!!!!!!!!!!!!! ", " ## ###########!!!!!!!!!!!!!!!!!!!! ", " #### ### #####!!!!!!!!!!!!!!!!!!!!! ", " ### # ##### !!!!!!!!!!!!!!!!!!!! ", " ### # ######### !!!!!!!!!!!!!!!!!!! ", " ############# ## !!!!!!!!!!!!!!!!!! ", " ############## !!!!!!!!!!!!!!! ", " ## ######## !!!!!!!!!!!!!!! ", " #### ##### # !!!!!!!!!!!!!! ", " ####### ########## !!!!!!!!!!!!! ", " ######## ### ###### !!!!!!!!!!!!! ", " $ % ######## ########## !!!!!!!!!!!!! & ", " %%%%% # ####### ## ######## !!!!!!!!!!!! ", " %%%%%%%%######## ########## ####### !!!!!!!!!!!! ", " %%%%%%%%%########################### !!!!!!!!!! ", " %%%%%%%%###################### ###### !!!!!!!! ", " %%%%%%%%%###################### ##### !!!!!!! ''''' ", " %%%%%%%%%##################### ####### !!!!!! ''''' ", " % %%%%%%%%############################ !!!!! ''' ", " %%%%%%%%%################## # ###### !!!! ( ", " %%%%%%%%%################# #### # !!!! ", " %%%%%%%%%%%%################ #### # !!! )", " %%%%% %%############### ####### ))", " % %%% %%############### ######## )))", " %% %%%######################### *))", " % %################# ######### *)))", "%%%% ############################ **))", " ########################### ))", " #%%%%%%%%%%%%############## +,", " %%%%%%%%%%%%%%%####%%## -# +", " %%%%%%%%%%%%%%%##%%%%## ", " %%%%%%%%%%%%%%%#%%%% ...", " %%%%%%%%%%%%%%%%%%%% /..", " %%%%%%%%%%%%%%%%%% / ...", " %%%%%%%%%%%%%%%%% ..", " %%%%%%%%%%%%%%%% 000", " 1%%%%%%%%%%%% 000", " 11111%%%%%%%%% .0022", " % 111111% %3 44422", " 111111 %33 445566", " %% 1111 1177778 445566", " % 1 1111111 9:%;<=> 555556", " ? 111@AA B CC DD5556", " 1@EEF G HI DDD66J", " E FKKKKK LMM6JJ", " NOOFKKKKKP QQRSS", " T FFFKKKPUV RSS", " T WFFFKKXPUVX "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/0/1.json b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/0/1.json
new file mode 100644
index 0000000..549b5e6
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/0/1.json
@@ -0,0 +1 @@
+{"keys": ["", "150", "49", "161", "38", "21", "95", "195", "64", "43", "17", "218", "61", "196", "33", "160", "8", "209", "159", "62", "243"], "data": {"150": {"NAME": "United States Minor Outlying Islands", "POP2005": 0}, "38": {"NAME": "Colombia", "POP2005": 4494579}, "21": {"NAME": "Brazil", "POP2005": 186830759}, "17": {"NAME": "Bolivia", "POP2005": 9182015}, "49": {"NAME": "Ecuador", "POP2005": 13060993}, "159": {"NAME": "New Zealand", "POP2005": 4097112}, "95": {"NAME": "Kiribati", "POP2005": 92003}, "196": {"NAME": "Tonga", "POP2005": 99361}, "61": {"NAME": "Fiji", "POP2005": 828046}, "43": {"NAME": "Cook Islands", "POP2005": 13984}, "218": {"NAME": "Samoa", "POP2005": 183845}, "195": {"NAME": "Tokelau", "POP2005": 1401}, "62": {"NAME": "Falkland Islands (Malvinas)", "POP2005": 2975}, "209": {"NAME": "Uruguay", "POP2005": 3325727}, "243": {"NAME": "South Georgia South Sandwich Islands", "POP2005": 0}, "8": {"NAME": "Argentina", "POP2005": 38747148}, "64": {"NAME": "French Polynesia", "POP2005": 255632}, "160": {"NAME": "Paraguay", "POP2005": 5904342}, "161": {"NAME": "Peru", "POP2005": 27274266}, "33": {"NAME": "Chile", "POP2005": 16295102}}, "grid": [" ! ! ## ##$%%&&&&&&&&& ", " ' $$$$$&&&&&&&&&&& ", " ' $$$$&&&&&&&&&&&&& ", " ( ) $$$$&&&&&&&&&&&& ", " * ' $$$$+&&&&&&&&&& ", " , $$$+++&&&&&&&& ", "- * ) $$+++&&&&&&&& ", "- . * /++00&&&&&& & ", " ) ) //+000&&&&& ", " /11100&&& ", " ) / /11100&& ", " /1111&&& ", " /111222 ", " //111222 ", " /11111 ", " /11111 ", " /111 ", " 3 //111 ", " 3 //11 ", " //11 ", " //11 ", " //1 44 ", " //// 4 ", " //11 55 ", " / 5 ", " ", " 5 ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/0/2.json b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/0/2.json
new file mode 100644
index 0000000..a0e62f4
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/0/2.json
@@ -0,0 +1 @@
+{"keys": [""], "data": {}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/1/0.json b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/1/0.json
new file mode 100644
index 0000000..1be321e
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/1/0.json
@@ -0,0 +1 @@
+{"keys": ["", "245", "238", "154", "189", "60", "142", "53", "45", "101", "103", "102", "205", "153", "72", "163", "98", "206", "30", "207", "129", "65", "105", "110", "191", "86", "182", "81", "170", "80", "236", "171", "210", "89", "186", "22", "202", "112", "200", "5", "3", "93", "94", "74", "84", "194", "96", "2", "199", "117", "88", "162", "190", "31", "83", "107", "50", "139", "175", "155", "168", "226", "244", "14", "18", "113", "126", "36", "188", "118", "99", "214", "220", "193", "172", "150", "54", "131", "208", "152", "56", "25", "185", "230", "197", "35", "40", "119", "26", "229", "63", "69", "223", "121", "67", "27", "28", "204", "92", "95"], "data": {"214": {"NAME": "Viet Nam", "POP2005": 85028643}, "210": {"NAME": "Uzbekistan", "POP2005": 26593123}, "131": {"NAME": "Northern Mariana Islands", "POP2005": 80258}, "139": {"NAME": "Palestine", "POP2005": 3762005}, "25": {"NAME": "Cambodia", "POP2005": 13955507}, "26": {"NAME": "Sri Lanka", "POP2005": 19120763}, "27": {"NAME": "Congo", "POP2005": 3609851}, "22": {"NAME": "Bulgaria", "POP2005": 7744591}, "95": {"NAME": "Kiribati", "POP2005": 92003}, "28": {"NAME": "Democratic Republic of the Congo", "POP2005": 58740547}, "220": {"NAME": "Yemen", "POP2005": 21095679}, "121": {"NAME": "Malaysia", "POP2005": 25652985}, "126": {"NAME": "Niger", "POP2005": 1326419}, "129": {"NAME": "Belgium", "POP2005": 10398049}, "54": {"NAME": "Eritrea", "POP2005": 4526722}, "56": {"NAME": "Ethiopia", "POP2005": 78985857}, "50": {"NAME": "Egypt", "POP2005": 72849793}, "53": {"NAME": "Estonia", "POP2005": 1344312}, "199": {"NAME": "Tunisia", "POP2005": 10104685}, "194": {"NAME": "Tajikistan", "POP2005": 6550213}, "197": {"NAME": "Togo", "POP2005": 6238572}, "191": {"NAME": "Switzerland", "POP2005": 7424389}, "190": {"NAME": "Syrian Arab Republic", "POP2005": 18893881}, "193": {"NAME": "Thailand", "POP2005": 63002911}, "117": {"NAME": "Malta", "POP2005": 402617}, "89": {"NAME": "Japan", "POP2005": 127896740}, "110": {"NAME": "Mongolia", "POP2005": 2580704}, "113": {"NAME": "Mali", "POP2005": 1161109}, "112": {"NAME": "The former Yugoslav Republic of Macedonia", "POP2005": 2033655}, "205": {"NAME": "United Kingdom", "POP2005": 60244834}, "80": {"NAME": "Croatia", "POP2005": 455149}, "81": {"NAME": "Hungary", "POP2005": 10086387}, "119": {"NAME": "Maldives", "POP2005": 295297}, "118": {"NAME": "Oman", "POP2005": 2507042}, "84": {"NAME": "Iran (Islamic Republic of)", "POP2005": 69420607}, "3": {"NAME": "Azerbaijan", "POP2005": 8352021}, "245": {"NAME": "Russia", "POP2005": 143953092}, "244": {"NAME": "Taiwan", "POP2005": 0}, "102": {"NAME": "Belarus", "POP2005": 9795287}, "103": {"NAME": "Lithuania", "POP2005": 3425077}, "101": {"NAME": "Latvia", "POP2005": 2301793}, "107": {"NAME": "Libyan Arab Jamahiriya", "POP2005": 5918217}, "105": {"NAME": "Slovakia", "POP2005": 5386995}, "31": {"NAME": "Afghanistan", "POP2005": 25067407}, "30": {"NAME": "China", "POP2005": 1312978855}, "36": {"NAME": "Chad", "POP2005": 10145609}, "35": {"NAME": "Cameroon", "POP2005": 17795149}, "60": {"NAME": "Finland", "POP2005": 5246004}, "63": {"NAME": "Micronesia, Federated States of", "POP2005": 110058}, "65": {"NAME": "France", "POP2005": 60990544}, "67": {"NAME": "Gabon", "POP2005": 1290693}, "69": {"NAME": "Ghana", "POP2005": 2253501}, "175": {"NAME": "Saudi Arabia", "POP2005": 2361236}, "172": {"NAME": "Philippines", "POP2005": 84566163}, "171": {"NAME": "Republic of Moldova", "POP2005": 3876661}, "170": {"NAME": "Romania", "POP2005": 21627557}, "182": {"NAME": "Slovenia", "POP2005": 1999425}, "96": {"NAME": "Korea, Republic of", "POP2005": 47869837}, "2": {"NAME": "Algeria", "POP2005": 32854159}, "186": {"NAME": "Spain", "POP2005": 43397491}, "185": {"NAME": "Somalia", "POP2005": 8196395}, "188": {"NAME": "Sudan", "POP2005": 36899747}, "189": {"NAME": "Sweden", "POP2005": 9038049}, "99": {"NAME": "Lao People's Democratic Republic", "POP2005": 566391}, "98": {"NAME": "Kazakhstan", "POP2005": 15210609}, "168": {"NAME": "Qatar", "POP2005": 796186}, "229": {"NAME": "Palau", "POP2005": 20127}, "226": {"NAME": "United Arab Emirates", "POP2005": 4104291}, "93": {"NAME": "Kyrgyzstan", "POP2005": 5203547}, "92": {"NAME": "Kenya", "POP2005": 35598952}, "223": {"NAME": "Indonesia", "POP2005": 226063044}, "94": {"NAME": "Korea, Democratic People's Republic of", "POP2005": 23615611}, "162": {"NAME": "Pakistan", "POP2005": 158080591}, "163": {"NAME": "Poland", "POP2005": 38195558}, "14": {"NAME": "Bangladesh", "POP2005": 15328112}, "18": {"NAME": "Burma", "POP2005": 47967266}, "88": {"NAME": "Iraq", "POP2005": 27995984}, "150": {"NAME": "United States Minor Outlying Islands", "POP2005": 0}, "153": {"NAME": "Netherlands", "POP2005": 1632769}, "152": {"NAME": "Nigeria", "POP2005": 141356083}, "155": {"NAME": "Nepal", "POP2005": 27093656}, "154": {"NAME": "Norway", "POP2005": 4638836}, "238": {"NAME": "Svalbard", "POP2005": 0}, "83": {"NAME": "India", "POP2005": 1134403141}, "236": {"NAME": "Serbia", "POP2005": 9863026}, "230": {"NAME": "Marshall Islands", "POP2005": 5672}, "86": {"NAME": "Italy", "POP2005": 5864636}, "45": {"NAME": "Denmark", "POP2005": 5416945}, "40": {"NAME": "Central African Republic", "POP2005": 4191429}, "5": {"NAME": "Armenia", "POP2005": 3017661}, "200": {"NAME": "Turkey", "POP2005": 72969723}, "202": {"NAME": "Turkmenistan", "POP2005": 4833266}, "142": {"NAME": "\ufffdland Islands", "POP2005": 0}, "204": {"NAME": "Uganda", "POP2005": 28947181}, "207": {"NAME": "United States", "POP2005": 299846449}, "206": {"NAME": "Ukraine", "POP2005": 46917544}, "208": {"NAME": "Burkina Faso", "POP2005": 13933363}, "74": {"NAME": "Greece", "POP2005": 11099737}, "72": {"NAME": "Germany", "POP2005": 82652369}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ! ", " !! ! ", " !!!!!! !!! ", " ## !!!!!!! !! ", " ######## !!!!! !!!! ", " ###### ! !!!! ", " ##### # !!!!! ", " #### # !! ", " ## # ! ! ", " #### ! !!! ", " # # ! ! !!!! ! ", " !!! ! !!!!!!!! !!! ! ", " !!! ! !!!!!!!!!! !!!!!! ", " !! ! !!!!!!!!!!! ! ! ", " !! !!!!!!!!!!!! ! ! ! ", " !! !!!!!!!!!!!!!!!!!!!!!! !! ", " ! !!!!!!!!!!!!!!!!!!!!!!! !!!!! ", " !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ", " $$$$$ !!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ", " $$$$$$!! ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", " $$%%$!!!! ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", " $%%%&&!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", " %%%%&&!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", " $%%%%&&!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", " $$%%%&&&!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", " $$%%% &&&!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", " $$%%% &&!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ", " $$%%%'&&!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!! ", " $$%%%%((!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! !! ", " )%%% **!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!! ", " ))% ++,!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!! ! ", "- .//000!+,!!!!!!!!!!!!!1!!!!!!!!!!!!!!!!!!!!!!!!!! !! ! ", "- .//00002,!!!!!!!!!!!111111!!!!!!!!!!!!!!!!3!!!!!!! ! 444", "-5.//00002222!!!!!11!!1111111!!!!!!!!!!!!!!33!!!!!!! ! ", "666///70222222!!!11111111111111!88888888883333!!!! ! ! ", "6699:;;<==222!!!!!111111111111138888888888833333!! ! ! ", "66:::>>??=@ 2!!!!!11AA1111111133888888888333333!! BB! ", "CC6:::>??DD !!!!1EAAAA1111133333888888333333!! BB ", "C : ::FFGGGGGGGH!IEEEAAAAAJ3333333333333333KKK B ", "C : ::LLGGGGGGGGM EEEEAANN33333333333333333KO BB ", "PPPQ:R LGGGGGGSSMMMMMEENTTT3333333333333333 OOBBBB ", "PPPQQ UUSSMMMMMVVVTTWW33333333333333 OBBBB ", "PPPQQXXXXXYYZ[[[SSMMMMMVTTTWW333333333333333 B ", "PPPPXXXXXXYYY[[[[[MMMMTTTTTWW]]33WW333333333 B ", "PPPPXXXXXXYYY[[[[[^M_MTTTTWWWW]]WWW33333333` B B ", "PPPPXXXXXXYYY[[[[[___ TTWWWWWWaWb3333333``BB ", "cPPdddeXXfffff[[[[[[gg WWWWWWWaWb3hii333 ` ", "ccddddeeefffff [[[j[g WWWWW bbkki 3 l m ", "cdddddeeefffffnjjjjj WWWW bkkki l o ", "pdqqqqeeffffffrjjj WW W kksii lll o ", "pqqqqqeefffffrrtttt WWW W k iii lll uu ", "vqqqqwxxfffffrrrttt yWzz W k i ll { |||| uu ", "} qqwxxxxxfffrrrtt y ~ k \u007flll~ { | ", " \u0080w\u0081\u0082\u0082\u0082\u0082\u0083\u0083\u0084\u0084tt y ~~~\u007f ~\u007f~~ ~ \u0085 "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/1/1.json b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/1/1.json
new file mode 100644
index 0000000..66a2faf
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/1/1.json
@@ -0,0 +1 @@
+{"keys": ["", "52", "67", "28", "204", "92", "185", "119", "223", "156", "95", "174", "203", "166", "177", "148", "201", "6", "221", "227", "20", "123", "37", "9", "122", "108", "151", "216", "222", "146", "61", "180", "115", "124", "178", "219", "179", "159", "147", "145"], "data": {"151": {"NAME": "Vanuatu", "POP2005": 215366}, "201": {"NAME": "Tuvalu", "POP2005": 10441}, "156": {"NAME": "Nauru", "POP2005": 10111}, "159": {"NAME": "New Zealand", "POP2005": 4097112}, "67": {"NAME": "Gabon", "POP2005": 1290693}, "219": {"NAME": "Swaziland", "POP2005": 1124529}, "115": {"NAME": "Mauritius", "POP2005": 1241173}, "61": {"NAME": "Fiji", "POP2005": 828046}, "179": {"NAME": "Lesotho", "POP2005": 1980831}, "178": {"NAME": "South Africa", "POP2005": 47938663}, "177": {"NAME": "Seychelles", "POP2005": 85532}, "174": {"NAME": "Rwanda", "POP2005": 9233793}, "119": {"NAME": "Maldives", "POP2005": 295297}, "20": {"NAME": "Solomon Islands", "POP2005": 472419}, "95": {"NAME": "Kiribati", "POP2005": 92003}, "28": {"NAME": "Democratic Republic of the Congo", "POP2005": 58740547}, "180": {"NAME": "Botswana", "POP2005": 1835938}, "185": {"NAME": "Somalia", "POP2005": 8196395}, "9": {"NAME": "Australia", "POP2005": 20310208}, "146": {"NAME": "French Southern and Antarctic Lands", "POP2005": 0}, "147": {"NAME": "Heard Island and McDonald Islands", "POP2005": 0}, "203": {"NAME": "United Republic of Tanzania", "POP2005": 38477873}, "145": {"NAME": "Bouvet Island", "POP2005": 0}, "204": {"NAME": "Uganda", "POP2005": 28947181}, "6": {"NAME": "Angola", "POP2005": 16095214}, "148": {"NAME": "British Indian Ocean Territory", "POP2005": 0}, "122": {"NAME": "Mozambique", "POP2005": 20532675}, "123": {"NAME": "Malawi", "POP2005": 13226091}, "124": {"NAME": "New Caledonia", "POP2005": 234185}, "227": {"NAME": "Timor-Leste", "POP2005": 1067285}, "166": {"NAME": "Papua New Guinea", "POP2005": 6069715}, "92": {"NAME": "Kenya", "POP2005": 35598952}, "223": {"NAME": "Indonesia", "POP2005": 226063044}, "222": {"NAME": "Zimbabwe", "POP2005": 13119679}, "221": {"NAME": "Zambia", "POP2005": 11478317}, "37": {"NAME": "Comoros", "POP2005": 797902}, "108": {"NAME": "Madagascar", "POP2005": 18642586}, "52": {"NAME": "Equatorial Guinea", "POP2005": 484098}, "216": {"NAME": "Namibia", "POP2005": 2019677}}, "grid": [" !###$$$$$%%&&' ( ))) ))) )))))) * + ", " ##$$$$,---& )))))))) ))))))..... ", " $$$$$$$---- / 0 ))) ) ) ))))..... 11", " 2$2$$33--- / ))))44 )).... 555 ", " 222$3336--7 888 . 555 ", " 22223333399 : 8888888 ;; ", " <<<233==699>:: 888888888 ; ?", " <<<@===9 :: A 8888888888888 B ; ?", " <<<@@==9 ::: 8888888888888 BB ", " <<<@@CC9 : 888888888888888 ", " <<CCCD 88888888888888 ", " CCCCEC 88888888888888 ", " CCCC 88888888888888 ", " CCC 88 888888 FF ", " 8888 F ", " 88 FF", " 8 FF ", " 8 FF ", " FFF ", " C ", " >> ", " F ", " G F ", " H ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/1/2.json b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/1/2.json
new file mode 100644
index 0000000..a0e62f4
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/1/2.json
@@ -0,0 +1 @@
+{"keys": [""], "data": {}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/2/0.json b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/2/0.json
new file mode 100644
index 0000000..a0e62f4
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/2/0.json
@@ -0,0 +1 @@
+{"keys": [""], "data": {}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/2/1.json b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/2/1.json
new file mode 100644
index 0000000..a0e62f4
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/2/1.json
@@ -0,0 +1 @@
+{"keys": [""], "data": {}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/2/2.json b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/2/2.json
new file mode 100644
index 0000000..a0e62f4
--- /dev/null
+++ b/misc/openlayers/tests/data/utfgrid/world_utfgrid/1/2/2.json
@@ -0,0 +1 @@
+{"keys": [""], "data": {}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} \ No newline at end of file
diff --git a/misc/openlayers/tests/data_Layer_Text_textfile.txt b/misc/openlayers/tests/data_Layer_Text_textfile.txt
new file mode 100644
index 0000000..8250988
--- /dev/null
+++ b/misc/openlayers/tests/data_Layer_Text_textfile.txt
@@ -0,0 +1,3 @@
+point image
+10,20 http://boston.openguides.org/markers/ORANGE.png
+15,25 http://boston.openguides.org/markers/ORANGE.png
diff --git a/misc/openlayers/tests/data_Layer_Text_textfile_2.txt b/misc/openlayers/tests/data_Layer_Text_textfile_2.txt
new file mode 100644
index 0000000..91a8093
--- /dev/null
+++ b/misc/openlayers/tests/data_Layer_Text_textfile_2.txt
@@ -0,0 +1,3 @@
+point title description image
+10,20 a b http://boston.openguides.org/markers/ORANGE.png
+15,25 c d http://boston.openguides.org/markers/ORANGE.png
diff --git a/misc/openlayers/tests/data_Layer_Text_textfile_overflow.txt b/misc/openlayers/tests/data_Layer_Text_textfile_overflow.txt
new file mode 100644
index 0000000..bb4768e
--- /dev/null
+++ b/misc/openlayers/tests/data_Layer_Text_textfile_overflow.txt
@@ -0,0 +1,3 @@
+overflow point title description image
+auto 10,20 a b http://boston.openguides.org/markers/ORANGE.png
+hidden 15,25 c d http://boston.openguides.org/markers/ORANGE.png
diff --git a/misc/openlayers/tests/deprecated/Ajax.html b/misc/openlayers/tests/deprecated/Ajax.html
new file mode 100644
index 0000000..e73e80c
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Ajax.html
@@ -0,0 +1,28 @@
+<html>
+<head>
+ <script src="../OLLoader.js"></script>
+ <script src="../../lib/deprecated.js"></script>
+ <script type="text/javascript">
+
+ function test_Ajax_loadUrl(t) {
+ t.plan(5);
+ var _get = OpenLayers.Request.GET;
+ var caller = {};
+ var onComplete = function() {};
+ var onFailure = function() {};
+ var params = {};
+ OpenLayers.Request.GET = function(config) {
+ t.eq(config.url, "http://example.com/?format=image+kml", "correct url")
+ t.eq(config.params, params, "correct params");
+ t.eq(config.scope, caller, "correct scope");
+ t.ok(config.success === onComplete, "correct success callback");
+ t.ok(config.failure === onFailure, "correct failure callback");
+ };
+ OpenLayers.loadURL("http://example.com/?format=image+kml", params, caller, onComplete, onFailure);
+ OpenLayers.Request.GET = _get;
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/deprecated/BaseTypes/Class.html b/misc/openlayers/tests/deprecated/BaseTypes/Class.html
new file mode 100644
index 0000000..572765d
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/BaseTypes/Class.html
@@ -0,0 +1,142 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script src="../../../lib/deprecated.js"></script>
+ <script type="text/javascript">
+ // remove this next line at 3.0
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+
+ // Remove this at 3.0
+ function test_Class_backwards(t) {
+ t.plan(4);
+ // test that a new style class supports old style inheritance
+ var NewClass = OpenLayers.Class({
+ newProp: "new",
+ initialize: function() {
+ t.ok(false, "the base class is never instantiated");
+ },
+ toString: function() {
+ return "new style";
+ }
+ });
+
+ var OldClass = OpenLayers.Class.create();
+ OldClass.prototype = OpenLayers.Class.inherit(NewClass, {
+ oldProp: "old",
+ initialize: function() {
+ t.ok(true, "only the child class constructor is called");
+ }
+ });
+
+ var oldObj = new OldClass();
+ t.eq(oldObj.oldProp, "old",
+ "old style classes can still be instantiated");
+ t.eq(oldObj.newProp, "new",
+ "old style inheritance of properties works with new style base");
+ t.eq(oldObj.toString(), "new style",
+ "toString inheritance works with backwards style");
+
+ }
+
+ // Remove this at 3.0
+ function test_Class_create (t) {
+ t.plan( 3 );
+ var cls = OpenLayers.Class.create();
+ cls.prototype = {
+ initialize: function () {
+ if (isMozilla)
+ t.ok(this instanceof cls,
+ "initialize is called on the right class");
+ else
+ t.ok(true, "initialize is called");
+ }
+ };
+ var obj = new cls();
+ t.eq(typeof obj, "object", "obj is an object");
+ if (isMozilla)
+ t.ok(obj instanceof cls,
+ "object is of the right class");
+ else
+ t.ok(true, "this test doesn't work in IE");
+ }
+
+ // Remove this at 3.0
+ function test_Class_inherit (t) {
+ t.plan( 20 );
+ var A = OpenLayers.Class.create();
+ var initA = 0;
+ A.prototype = {
+ count: 0,
+ mixed: false,
+ initialize: function () {
+ initA++;
+ this.count++;
+ }
+ };
+
+ var B = OpenLayers.Class.create();
+ var initB = 0;
+ B.prototype = OpenLayers.Class.inherit( A, {
+ initialize: function () {
+ A.prototype.initialize.apply(this, arguments);
+ initB++;
+ this.count++;
+ }
+ });
+
+ var mixin = OpenLayers.Class.create()
+ mixin.prototype = {
+ mixed: true
+ };
+
+ t.eq( initA, 0, "class A not inited" );
+ t.eq( initB, 0, "class B not inited" );
+
+ var objA = new A();
+ t.eq( objA.count, 1, "object A init" );
+ t.eq( initA, 1, "class A init" );
+ if (isMozilla)
+ t.ok( objA instanceof A, "obj A isa A" );
+ else
+ t.ok( true, "IE sucks" );
+
+ var objB = new B();
+ t.eq( initA, 2, "class A init" );
+ t.eq( initB, 1, "class B init" );
+ t.eq( objB.count, 2, "object B init twice" );
+ if (isMozilla) {
+ t.ok( objB instanceof A, "obj B isa A" );
+ t.ok( objB instanceof B, "obj B isa B" );
+ } else {
+ t.ok( true, "IE sucks" );
+ t.ok( true, "IE sucks" );
+ }
+
+ var C = OpenLayers.Class.create();
+ C.prototype = OpenLayers.Class.inherit( B, mixin, {count: 0} );
+ t.eq( initA, 2, "class A init unchanged" );
+ t.eq( initB, 1, "class B init unchanged" );
+
+ var objC = new C();
+ t.eq( initA, 3, "class A init changed" );
+ t.eq( initB, 2, "class B init changed" );
+ t.eq( objC.count, 2, "object C init changed" );
+ if (isMozilla) {
+ t.ok( objC instanceof A, "obj C isa A" );
+ t.ok( objC instanceof B, "obj C isa B" );
+ t.ok( objC instanceof C, "obj C isa C" );
+ t.ok( !(objC instanceof mixin), "obj C isn'ta mixin" );
+ } else {
+ t.ok( true, "IE sucks" );
+ t.ok( true, "IE sucks" );
+ t.ok( true, "IE sucks" );
+ t.ok( true, "IE sucks" );
+ }
+ t.eq( objC.mixed, true, "class C has mixin properties" );
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/deprecated/BaseTypes/Element.html b/misc/openlayers/tests/deprecated/BaseTypes/Element.html
new file mode 100644
index 0000000..bd7e074
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/BaseTypes/Element.html
@@ -0,0 +1,56 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script src="../../../lib/deprecated.js"></script>
+
+ <script type="text/javascript">
+
+ function test_Element_hide(t) {
+ t.plan(2);
+
+ var elem1 = {
+ style: {
+ 'display': "none"
+ }
+ };
+
+ var elem2 = {
+ style: {
+ 'display': ""
+ }
+ };
+
+ OpenLayers.Element.hide(elem1, elem2, "do-not-exists");
+
+ t.eq(elem1.style.display, "none", "hidden element stays hidden");
+ t.eq(elem2.style.display, "none", "shown element hidden");
+ }
+
+ function test_Element_show(t) {
+ t.plan(2);
+
+ var elem1 = {
+ style: {
+ 'display': "none"
+ }
+ };
+
+ var elem2 = {
+ style: {
+ 'display': ""
+ }
+ };
+
+ OpenLayers.Element.show(elem1, "do-not-exists", elem2);
+
+ t.eq(elem1.style.display, "", "hidden element shown");
+ t.eq(elem2.style.display, "", "shown element stays shown");
+ }
+
+ </script>
+</head>
+<body>
+ <div id="elemID" style="width:50px; height:100px; background-color:red">test</div>
+</body>
+</html>
+ \ No newline at end of file
diff --git a/misc/openlayers/tests/deprecated/Control/MouseToolbar.html b/misc/openlayers/tests/deprecated/Control/MouseToolbar.html
new file mode 100644
index 0000000..f66a18b
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Control/MouseToolbar.html
@@ -0,0 +1,60 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script src="../../../lib/deprecated.js"></script>
+ <script type="text/javascript">
+ var map;
+ function test_Control_MouseToolbar_constructor (t) {
+ t.plan( 1 );
+
+ control = new OpenLayers.Control.MouseToolbar();
+ t.ok( control instanceof OpenLayers.Control.MouseToolbar, "new OpenLayers.Control.MouseToolbar returns object" );
+ }
+ function test_Control_MouseToolbar_addControl (t) {
+ t.plan( 8 );
+ map = new OpenLayers.Map('map');
+ control = new OpenLayers.Control.MouseToolbar();
+ t.ok( control instanceof OpenLayers.Control.MouseToolbar, "new OpenLayers.Control.MouseToolbar returns object" );
+ t.ok( map instanceof OpenLayers.Map, "new OpenLayers.Map creates map" );
+ map.addControl(control);
+ t.ok( control.map === map, "Control.map is set to the map object" );
+ t.ok( map.controls[4] === control, "map.controls contains control" );
+ t.eq( parseInt(control.div.style.zIndex), map.Z_INDEX_BASE['Control'] + 5, "Control div zIndexed properly" );
+ t.eq( parseInt(map.viewPortDiv.lastChild.style.zIndex), map.Z_INDEX_BASE['Control'] + 5, "Viewport div contains control div" );
+ t.eq( control.div.style.left, "6px", "Control div left located correctly by default");
+ t.eq( control.div.style.top, "300px", "Control div top located correctly by default");
+
+ }
+ function test_Control_MouseToolbar_control_events (t) {
+ t.plan( 1 );
+ if ((navigator.userAgent.indexOf("compatible") == -1)) {
+ var evt = {which: 1}; // control expects left-click
+ map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS("Test Layer",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"});
+ map.addLayer(layer);
+
+ control = new OpenLayers.Control.MouseToolbar();
+ map.addControl(control);
+
+ var centerLL = new OpenLayers.LonLat(0,0);
+ map.setCenter(centerLL, 5);
+
+ evt.shiftKey = true;
+ evt.xy = new OpenLayers.Size(5,5);
+ control.defaultMouseDown(evt);
+ evt.xy = new OpenLayers.Size(15,15);
+ control.defaultMouseUp(evt);
+ t.eq(map.getZoom(), 6, "Map zoom set correctly after zoombox");
+ } else {
+ t.ok(true, "IE does not run this test.")
+ }
+ }
+
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
diff --git a/misc/openlayers/tests/deprecated/Geometry/Rectangle.html b/misc/openlayers/tests/deprecated/Geometry/Rectangle.html
new file mode 100644
index 0000000..75778e8
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Geometry/Rectangle.html
@@ -0,0 +1,77 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script src="../../../lib/deprecated.js"></script>
+ <script type="text/javascript">
+
+ function test_Rectangle_constructor (t) {
+ t.plan( 8 );
+
+ //empty
+ var rect = new OpenLayers.Geometry.Rectangle();
+ t.ok( rect instanceof OpenLayers.Geometry.Rectangle, "new OpenLayers.Geometry.Rectangle returns Rectangle object" );
+ t.eq( rect.CLASS_NAME, "OpenLayers.Geometry.Rectangle", "Rectangle.CLASS_NAME is set correctly");
+ t.ok( rect.id != null, "rect.id is set");
+ t.ok( ! (rect.x || rect.y || rect.width || rect.height), "empty construct leaves properties empty");
+
+ //good
+ var x = {};
+ var y = {};
+ var w = {};
+ var h = {};
+ var rect = new OpenLayers.Geometry.Rectangle(x, y, w, h);
+ t.eq( rect.x, x, "good init correctly sets x property");
+ t.eq( rect.y, y, "good init correctly sets y property");
+ t.eq( rect.width, w, "good init correctly sets width property");
+ t.eq( rect.height, h, "good init correctly sets height property");
+ }
+
+ function test_Rectangle_calculateBounds(t) {
+ t.plan(1);
+
+ var x = 1;
+ var y = 2;
+ var w = 10;
+ var h = 20;
+ var rect = new OpenLayers.Geometry.Rectangle(x, y, w, h);
+ rect.calculateBounds();
+
+ var testBounds = new OpenLayers.Bounds(x, y, x + w, y + h)
+
+ t.ok( rect.bounds.equals(testBounds), "calculateBounds works correctly");
+ }
+
+ function test_Rectangle_getLength(t) {
+ t.plan(1);
+
+ var x = 1;
+ var y = 2;
+ var w = 10;
+ var h = 20;
+ var rect = new OpenLayers.Geometry.Rectangle(x, y, w, h);
+
+ var testLength = (2 * w) + (2 * h);
+
+ t.eq(rect.getLength(), testLength, "getLength() works");
+ }
+
+ function test_Rectangle_getArea(t) {
+ t.plan(1);
+
+ var x = 1;
+ var y = 2;
+ var w = 10;
+ var h = 20;
+ var rect = new OpenLayers.Geometry.Rectangle(x, y, w, h);
+
+ var testArea = w * h;
+ t.eq(rect.getArea(), testArea, "testArea() works");
+ }
+
+
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/deprecated/Layer/GML.html b/misc/openlayers/tests/deprecated/Layer/GML.html
new file mode 100644
index 0000000..daf5917
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Layer/GML.html
@@ -0,0 +1,61 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script src="../../../lib/deprecated.js"></script>
+ <script type="text/javascript">
+
+ var name = "GML Layer";
+
+ var gml = "./owls.xml";
+ var gml2 = "./mice.xml";
+
+ // if this test is running online, different rules apply
+ var isMSIE = (navigator.userAgent.indexOf("MSIE") > -1);
+ if (isMSIE) {
+ gml = "." + gml;
+ gml2 = "." + gml2;
+ }
+
+ function test_Layer_GML_constructor(t) {
+ t.plan(3);
+
+ var layer = new OpenLayers.Layer.GML(name);
+ t.ok(layer instanceof OpenLayers.Layer.GML, "new OpenLayers.Layer.GML returns correct object" );
+ t.eq(layer.name, name, "layer name is correctly set");
+ t.ok(layer.renderer.CLASS_NAME, "layer has a renderer");
+
+ }
+ function test_Layer_GML_events(t) {
+ t.plan(3);
+
+ var layer = new OpenLayers.Layer.GML(name, gml, {isBaseLayer: true});
+ layer.events.register("loadstart", layer, function() {
+ t.ok(true, "loadstart called.")
+ });
+ layer.events.register("loadend", layer, function() {
+ t.ok(true, "loadend called.")
+ });
+ var map = new OpenLayers.Map("map");
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ t.delay_call(3, function() {
+ t.ok(true, "waited for 3s");
+ });
+
+ }
+ function test_GML_setUrl(t) {
+ t.plan(2);
+ var layer = new OpenLayers.Layer.GML(name, gml);
+ var map = new OpenLayers.Map("map");
+ map.addLayer(layer);
+ t.eq(layer.url, gml, "layer has correct original url");
+ layer.setUrl(gml2);
+ t.eq(layer.url, gml2, "layer has correctly changed url");
+ }
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
+
diff --git a/misc/openlayers/tests/deprecated/Layer/MapServer.html b/misc/openlayers/tests/deprecated/Layer/MapServer.html
new file mode 100644
index 0000000..d65fef6
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Layer/MapServer.html
@@ -0,0 +1,59 @@
+<html>
+<head>
+<script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhR_wWLPFku8Ix9i2SXYRVK3e45q1BQUd_beF8dtzKET_EteAjPdGDwqpQ'></script>
+
+<script src="../../OLLoader.js"></script>
+<script src="../../../lib/deprecated.js"></script>
+<script type="text/javascript">
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var layer;
+
+ var name = 'Test Layer';
+ var url = "http://labs.metacarta.com/cgi-bin/mapserv";
+ var params = { map: '/mapdata/vmap_wms.map',
+ layers: 'basic'};
+
+ function test_Layer_MapServer_Reproject (t) {
+ var validkey = (window.location.protocol == "file:") ||
+ (window.location.host == "localhost") ||
+ (window.location.host == "openlayers.org");
+
+ if (OpenLayers.BROWSER_NAME == "opera" || OpenLayers.BROWSER_NAME == "safari") {
+ t.plan(1);
+ t.debug_print("Can't test google reprojection in Opera or Safari.");
+ } else if(validkey) {
+ t.plan(5);
+ var map = new OpenLayers.Map('map', {tileManager: null});
+ var layer = new OpenLayers.Layer.Google("Google");
+ map.addLayer(layer);
+ layer = new OpenLayers.Layer.MapServer(name, url, params, {reproject: true, isBaseLayer: false, buffer: 2});
+ layer.isBaseLayer=false;
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+ var tile = layer.grid[0][0];
+ t.eq( tile.bounds.left, -22.5, "left side matches" );
+ t.eq( tile.bounds.right, -11.25, "right side matches" );
+ t.eq( tile.bounds.bottom.toFixed(6), '11.781325', "bottom side matches" );
+ t.eq( tile.bounds.top.toFixed(6), '22.512557', "top side matches" );
+ map.destroy();
+ } else {
+ t.plan(1);
+ t.debug_print("can't test google layer from " +
+ window.location.host);
+ }
+
+ var map = new OpenLayers.Map('map', {tileManager: null});
+ layer = new OpenLayers.Layer.MapServer(name, url, params, {buffer:2});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+ var tile = layer.grid[0][0];
+ t.ok( tile.bounds.equals(new OpenLayers.Bounds(-33.75, 33.75, -22.5, 45)), "okay");
+ map.destroy();
+ }
+
+</script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/deprecated/Layer/MapServer/Untiled.html b/misc/openlayers/tests/deprecated/Layer/MapServer/Untiled.html
new file mode 100644
index 0000000..f235492
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Layer/MapServer/Untiled.html
@@ -0,0 +1,158 @@
+<html>
+<head>
+
+ <script src="../../../OLLoader.js"></script>
+ <script src="../../../../lib/deprecated.js"></script>
+ <script type="text/javascript">
+
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var layer;
+
+ var name = 'Test Layer';
+ var url = "http://labs.metacarta.com/cgi-bin/mapserv";
+ var params = { map: '/mapdata/vmap_wms.map',
+ layers: 'basic'};
+
+ function test_Layer_MapServer_Untiled_constructor (t) {
+ t.plan( 4 );
+
+ var url = "http://labs.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.MapServer.Untiled(name, url, params);
+ t.ok( layer instanceof OpenLayers.Layer.MapServer.Untiled, "new OpenLayers.Layer.MapServer returns object" );
+ t.eq( layer.url, "http://labs.metacarta.com/cgi-bin/mapserv", "layer.url is correct (HTTPRequest inited)" );
+
+ t.eq( layer.params.mode, "map", "default mode param correctly copied");
+ t.eq( layer.params.map_imagetype, "png", "default imagetype correctly copied");
+
+
+ }
+
+ function test_Layer_MapServer_Untiled_clone (t) {
+ t.plan(3);
+
+ var url = "http://labs.metacarta.com/cgi-bin/mapserv";
+ var map = new OpenLayers.Map('map', {});
+ layer = new OpenLayers.Layer.MapServer.Untiled(name, url, params);
+ map.addLayer(layer);
+
+ var clone = layer.clone();
+ layer.tile = [[1,2],[3,4]];
+
+ t.ok( clone.tile != layer.tile, "clone does not copy tile");
+
+ layer.ratio += 1;
+
+ t.eq( clone.ratio, 1.5, "changing layer.ratio does not change clone.ratio -- a fresh copy was made, not just copied reference");
+
+ t.eq( clone.alpha, layer.alpha, "alpha copied correctly");
+
+ layer.tile = null;
+ map.destroy();
+ }
+
+ function test_Layer_MapServer_Untiled_isBaseLayer(t) {
+ t.plan(3);
+
+ var url = "http://labs.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.MapServer.Untiled(name, url, params);
+ t.ok( layer.isBaseLayer, "baselayer is true by default");
+
+ var newParams = OpenLayers.Util.extend({}, params);
+ newParams.transparent = "true";
+ layer = new OpenLayers.Layer.MapServer.Untiled(name, url, newParams);
+ t.ok( !layer.isBaseLayer, "baselayer is false when transparent is set to true");
+
+ layer = new OpenLayers.Layer.MapServer.Untiled(name, url, params, {isBaseLayer: false});
+ t.ok( !layer.isBaseLayer, "baselayer is false when option is set to false" );
+ }
+
+ function test_Layer_MapServer_Untiled_mergeNewParams (t) {
+ t.plan( 5 );
+
+ var map = new OpenLayers.Map("map", {tileManager: null});
+ var url = "http://labs.metacarta.com/cgi-bin/mapserv";
+ layer = new OpenLayers.Layer.MapServer.Untiled(name, url, params);
+
+ var newParams = { layers: 'sooper',
+ chickpeas: 'image/png'};
+
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ t.ok( !layer.grid[0][0].url.match("chickpeas"), "chickpeas is not in URL of first tile in grid" );
+
+ layer.mergeNewParams(newParams);
+
+ t.eq( layer.params.layers, "sooper", "mergeNewParams() overwrites well");
+ t.eq( layer.params.chickpeas, "image/png", "mergeNewParams() adds well");
+ t.ok( layer.grid[0][0].url.match("chickpeas"), "chickpeas is in URL of first tile in grid" );
+
+ newParams.chickpeas = 151;
+
+ t.eq( layer.params.chickpeas, "image/png", "mergeNewParams() makes clean copy of hashtable");
+ map.destroy();
+ }
+
+ function test_Layer_MapServer_Untiled_getFullRequestString (t) {
+
+
+ t.plan( 1 );
+ var map = new OpenLayers.Map('map');
+ tUrl = "http://labs.metacarta.com/cgi-bin/mapserv";
+ tParams = { layers: 'basic',
+ format: 'png'};
+ var tLayer = new OpenLayers.Layer.MapServer.Untiled(name, tUrl, tParams);
+ map.addLayer(tLayer);
+ str = tLayer.getFullRequestString();
+ var tParams = {
+ layers: 'basic',
+ format: 'png',
+ mode: 'map',
+ map_imagetype: 'png'
+ };
+
+ var sStr = tUrl + "?" + OpenLayers.Util.getParameterString(tParams);
+ sStr = sStr.replace(/,/g, "+");
+
+ t.eq(str, sStr , "getFullRequestString() works");
+ map.destroy();
+
+ }
+
+ // DEPRECATED -- REMOVE IN 3.0
+ function test_Layer_Untiled_MapServer(t) {
+ t.plan(1);
+
+ var layer = new OpenLayers.Layer.MapServer.Untiled();
+
+ var clone = layer.clone();
+
+ t.ok(clone.singleTile, "regression test: clone works. this is for #1013");
+ }
+
+ function test_Layer_MapServer_Untiled_destroy (t) {
+
+ t.plan( 1 );
+
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.MapServer.Untiled(name, url, params);
+ map.addLayer(layer);
+
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+
+ //grab a reference to one of the tiles
+ var tile = layer.tile;
+
+ layer.destroy();
+
+ // checks to make sure superclass (grid) destroy() was called
+
+ t.ok( layer.tile == null, "tile set to null");
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/deprecated/Layer/WFS.html b/misc/openlayers/tests/deprecated/Layer/WFS.html
new file mode 100644
index 0000000..09b6a54
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Layer/WFS.html
@@ -0,0 +1,178 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script src="../../../lib/deprecated.js"></script>
+ <script type="text/javascript">
+
+ var name = "Vector Layer";
+
+ function test_Layer_WFS_constructor(t) {
+ t.plan(3);
+
+ var layer = new OpenLayers.Layer.WFS(name, "url", {});
+ t.ok(layer instanceof OpenLayers.Layer.WFS, "new OpenLayers.Layer.Vector returns correct object" );
+ t.eq(layer.name, name, "layer name is correctly set");
+ t.ok(layer.renderer.CLASS_NAME, "layer has a renderer");
+
+ }
+
+ function test_Layer_WFS_getDataExtent(t) {
+ t.plan(1);
+
+ var layer = new OpenLayers.Layer.WFS(name, "url", {});
+ layer.addFeatures(new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0, 0)));
+ layer.addFeatures(new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0, 1)));
+ t.eq(layer.getDataExtent().toBBOX(), "0,0,0,1", "bbox is correctly pulled from vectors.");
+
+ }
+
+ function test_Layer_WFS_setOpacity(t) {
+ t.plan(3);
+
+ var layer = new OpenLayers.Layer.WFS(name, "url", {});
+ layer.setOpacity(.5);
+ t.eq(layer.opacity, 0.5, "vector setOpacity didn't fail");
+ var layer = new OpenLayers.Layer.WFS(name, "url", {}, {'featureClass': OpenLayers.Feature.WFS});
+ var marker = new OpenLayers.Marker(new OpenLayers.LonLat(0,0));
+ marker.setOpacity = function() {
+ t.ok(true, "Marker setOpacity was called");
+ }
+ layer.addMarker(marker);
+ layer.setOpacity(.6);
+ t.eq(layer.opacity, 0.6, "setOpacity didn't fail on markers");
+ }
+
+ function test_Layer_WFS_destroy(t) {
+ t.plan(13);
+
+ var tVectorDestroy = OpenLayers.Layer.Vector.prototype.destroy;
+ OpenLayers.Layer.Vector.prototype.destroy = function() {
+ g_VectorDestroyed = true;
+ }
+
+ var tMarkersDestroy = OpenLayers.Layer.Markers.prototype.destroy;
+ OpenLayers.Layer.Markers.prototype.destroy = function() {
+ g_MarkersDestroyed = true;
+ }
+
+ var layer = {
+ 'vectorMode': true,
+ 'tile': {
+ 'destroy': function() {
+ t.ok(true, "wfs layer's tile is destroyed");
+ }
+ },
+ 'ratio': {},
+ 'featureClass': {},
+ 'format': {},
+ 'formatObject': {
+ 'destroy': function() {
+ t.ok(true, "wfs layer's format object is destroyed");
+ }
+ },
+ 'formatOptions': {},
+ 'encodeBBOX': {},
+ 'extractAttributes': {}
+ };
+
+ //this call should set off two tests (destroys for tile and format object)
+ g_VectorDestroyed = null;
+ g_MarkersDestroyed = null;
+ OpenLayers.Layer.WFS.prototype.destroy.apply(layer, []);
+
+ t.ok(g_VectorDestroyed && !g_MarkersDestroyed, "when vector mode is set to true, the default vector layer's destroy() method is called");
+ t.eq(layer.vectorMode, null, "'vectorMode' property nullified");
+ t.eq(layer.tile, null, "'tile' property nullified");
+ t.eq(layer.ratio, null, "'ratio' property nullified");
+ t.eq(layer.featureClass, null, "'featureClass' property nullified");
+ t.eq(layer.format, null, "'format' property nullified");
+ t.eq(layer.formatObject, null, "'formatObject' property nullified");
+ t.eq(layer.formatOptions, null, "'formatOptions' property nullified");
+ t.eq(layer.encodeBBOX, null, "'encodeBBOX' property nullified");
+ t.eq(layer.extractAttributes, null, "'extractAttributes' property nullified");
+
+ layer.vectorMode = false;
+
+ //this call will *not* set off two tests (tile and format object are null)
+ g_VectorDestroyed = null;
+ g_MarkersDestroyed = null;
+ OpenLayers.Layer.WFS.prototype.destroy.apply(layer, []);
+ t.ok(!g_VectorDestroyed && g_MarkersDestroyed, "when vector mode is set to false, the default markers layer's destroy() method is called");
+
+ OpenLayers.Layer.Vector.prototype.destroy = tVectorDestroy;
+ OpenLayers.Layer.Markers.prototype.destroy = tMarkersDestroy;
+ }
+
+ function test_Layer_WFS_mapresizevector(t) {
+ t.plan(2);
+
+ var map = new OpenLayers.Map("map");
+ map.addLayer(new OpenLayers.Layer.WMS("WMS", "url", {}));
+ var layer = new OpenLayers.Layer.WFS(name, "url", {});
+ t.ok(layer.renderer.CLASS_NAME, "layer has a renderer");
+ map.addLayer(layer);
+ setSize = false;
+ layer.renderer.setSize = function() { setSize = true; }
+ layer.onMapResize();
+ t.eq(setSize, true, "Renderer resize called on map size change.");
+ map.destroy();
+
+ }
+ function test_Layer_WFS_drawmap(t) {
+ t.plan(2);
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
+ map.addLayer(layer);
+
+ layer = new OpenLayers.Layer.WFS( "Owl Survey",
+ "http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp?",
+ {typename: "OWLS", maxfeatures: 10},
+ { featureClass: OpenLayers.Feature.WFS});
+ map.addLayer(layer);
+ map.addControl(new OpenLayers.Control.LayerSwitcher());
+ try {
+ map.setCenter(new OpenLayers.LonLat(-100, 60), 3);
+ } catch (Exception) {
+ }
+ t.eq(layer.tile.url, "http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp?TYPENAME=OWLS&MAXFEATURES=10&SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&SRS=EPSG%3A4326&BBOX=-187.890625,-36.6796875,-12.109375,156.6796875", "Tile URL is set correctly when not encoded");
+ map.destroy();
+ var map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'}
+ );
+ map.addLayer(layer);
+
+ layer = new OpenLayers.Layer.WFS( "Owl Survey",
+ "http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp?",
+ {typename: "OWLS", maxfeatures: 10},
+ { featureClass: OpenLayers.Feature.WFS, 'encodeBBOX': true});
+ map.addLayer(layer);
+ map.addControl(new OpenLayers.Control.LayerSwitcher());
+ try {
+ map.setCenter(new OpenLayers.LonLat(-100, 60), 3);
+ } catch (Exception) {
+ }
+ t.eq(layer.tile.url, "http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp?TYPENAME=OWLS&MAXFEATURES=10&SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&SRS=EPSG%3A4326&BBOX=-187.890625%2C-36.679687%2C-12.109375%2C156.679688", "Tile URL is set correctly when not encoded");
+ map.destroy();
+ }
+ function test_projection_srs(t) {
+ t.plan(1);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer("",{isBaseLayer: true} ));
+ // we use an empty moveTo function because we don't want to request tiles
+ var layer = new OpenLayers.Layer.WFS("","/wfs",{},{'projection': new OpenLayers.Projection("EPSG:900913"),
+ moveTo: function() {}});
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ var params = OpenLayers.Util.getParameters(layer.getFullRequestString());
+ t.eq(params.SRS, "EPSG:900913", "SRS represents projection of WFS layer, instead of map (#1537)");
+ }
+
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/deprecated/Layer/WMS.html b/misc/openlayers/tests/deprecated/Layer/WMS.html
new file mode 100644
index 0000000..43977c8
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Layer/WMS.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <!-- this gmaps key generated for http://openlayers.org/dev/ -->
+ <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAA9XNhd8q0UdwNC7YSO4YZghSPUCi5aRYVveCcVYxzezM4iaj_gxQ9t-UajFL70jfcpquH5l1IJ-Zyyw'></script>
+ <script src="../../OLLoader.js"></script>
+ <script src="../../../lib/deprecated.js"></script>
+ <script type="text/javascript">
+
+ var name = 'Test Layer';
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ var params = { map: '/mapdata/vmap_wms.map',
+ layers: 'basic',
+ format: 'image/jpeg'};
+
+ function test_Layer_WMS_Reproject (t) {
+ var validkey = (window.location.protocol == "file:") ||
+ (window.location.host == "localhost") ||
+ (window.location.host == "openlayers.org");
+ if (OpenLayers.BROWSER_NAME == "opera" || OpenLayers.BROWSER_NAME == "safari") {
+ t.plan(1);
+ t.debug_print("Can't test google reprojection in Opera or Safari.");
+ } else if(validkey) {
+ t.plan(5);
+
+ var map = new OpenLayers.Map('map', {tileManager: null});
+ var layer = new OpenLayers.Layer.Google("Google");
+ map.addLayer(layer);
+ var wmslayer = new OpenLayers.Layer.WMS(name, url, params,
+ {isBaseLayer: false, reproject:true, buffer: 2});
+ wmslayer.isBaseLayer=false;
+ map.addLayer(wmslayer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+ var tile = wmslayer.grid[0][0];
+ t.eq( tile.bounds.left, -22.5, "left side matches" );
+ t.eq( tile.bounds.right, -11.25, "right side matches" );
+ t.eq( tile.bounds.bottom.toFixed(6), '11.781325', "bottom side matches" );
+ t.eq( tile.bounds.top.toFixed(6), '22.512557', "top side matches" );
+ map.destroy();
+ } else {
+ t.plan(1);
+ t.debug_print("can't test google layer from " +
+ window.location.host);
+ }
+
+ var map = new OpenLayers.Map('map', {tileManager: null});
+ layer = new OpenLayers.Layer.WMS(name, url, params, {buffer: 2});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+ var tile = layer.grid[0][0];
+ t.ok( tile.bounds.equals(new OpenLayers.Bounds(-33.75, 33.75, -22.5, 45)), "okay");
+
+ map.destroy();
+ }
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/deprecated/Layer/WMS/Post.html b/misc/openlayers/tests/deprecated/Layer/WMS/Post.html
new file mode 100644
index 0000000..d79aec5
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Layer/WMS/Post.html
@@ -0,0 +1,89 @@
+<html>
+<head>
+ <script src="../../../OLLoader.js"></script>
+ <script src="../../../../lib/deprecated.js"></script>
+ <script type="text/javascript">
+ var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+ var isOpera = (navigator.userAgent.indexOf("Opera") != -1);
+ var layer;
+
+ var name = 'Test Layer';
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ var params = { map: '/mapdata/vmap_wms.map',
+ layers: 'basic',
+ format: 'image/jpeg'};
+
+ function test_Layer_WMS_Post_constructor (t) {
+ t.plan( 2 );
+
+ var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+ var options = {unsupportedBrowsers: []};
+ layer = new OpenLayers.Layer.WMS.Post(name, url, params, options);
+
+ t.eq(
+ layer.usePost, true,
+ "Supported browsers use IFrame tiles.");
+
+ layer.destroy();
+
+ var options = { unsupportedBrowsers: [OpenLayers.BROWSER_NAME]};
+ layer = new OpenLayers.Layer.WMS.Post(name, url, params, options);
+ t.eq(
+ layer.usePost, false,
+ "unsupported browsers use Image tiles.");
+ layer.destroy();
+ }
+
+ function test_Layer_WMS_Post_addtile (t) {
+ t.plan( 3 );
+
+ layer = new OpenLayers.Layer.WMS.Post(name, url, params);
+ var map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var pixel = new OpenLayers.Pixel(5,6);
+ var tile = layer.addTile(bounds, pixel);
+
+ if(isMozilla || isOpera) {
+ t.ok(
+ tile instanceof OpenLayers.Tile.Image,
+ "tile is an instance of OpenLayers.Tile.Image");
+ }
+ else {
+ t.ok(
+ tile.useIFrame !== undefined,
+ "tile is created with the OpenLayers.Tile.Image.IFrame mixin");
+ }
+ map.destroy();
+
+ // test the unsupported browser
+ layer = new OpenLayers.Layer.WMS.Post(name, url, params, {
+ unsupportedBrowsers: [OpenLayers.BROWSER_NAME]
+ });
+ map = new OpenLayers.Map('map');
+ map.addLayer(layer);
+ tile = layer.addTile(bounds, pixel);
+ t.ok(
+ tile instanceof OpenLayers.Tile.Image,
+ "unsupported browser: tile is an instance of Tile.Image");
+ layer.destroy();
+
+ // test a supported browser
+ layer = new OpenLayers.Layer.WMS.Post(name, url, params, {
+ unsupportedBrowsers: []
+ });
+ map.addLayer(layer);
+ var tile2 = layer.addTile(bounds, pixel);
+ tile2.draw();
+ t.eq(
+ tile2.useIFrame, true,
+ "supported browser: tile is created with the Tile.Image.IFrame mixin");
+ map.destroy();
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/deprecated/Layer/Yahoo.html b/misc/openlayers/tests/deprecated/Layer/Yahoo.html
new file mode 100755
index 0000000..f7c67c0
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Layer/Yahoo.html
@@ -0,0 +1,121 @@
+<html>
+<head>
+ <script src="http://api.maps.yahoo.com/ajaxymap?v=3.0&appid=euzuro-openlayers"></script>
+ <script src="../../OLLoader.js"></script>
+ <script src="../../../lib/deprecated.js"></script>
+ <script type="text/javascript">
+ var layer;
+
+ function test_Layer_Yahoo_constructor (t) {
+ t.plan( 4 );
+
+ var tempEventPane = OpenLayers.Layer.EventPane.prototype.initialize;
+ OpenLayers.Layer.EventPane.prototype.initialize = function(name, options) {
+ t.ok(name == g_Name, "EventPane initialize() called with correct name");
+ t.ok(options == g_Options, "EventPane initialize() called with correct options");
+ }
+
+ var tempFixedZoomLevels = OpenLayers.Layer.FixedZoomLevels.prototype.initialize;
+ OpenLayers.Layer.FixedZoomLevels.prototype.initialize = function(name, options) {
+ t.ok(name == g_Name, "FixedZoomLevels initialize() called with correct name");
+ t.ok(options == g_Options, "FixedZoomLevels initialize() called with correct options");
+ }
+
+
+ g_Name = {};
+ g_Options = {};
+ var l = new OpenLayers.Layer.Yahoo(g_Name, g_Options);
+
+ OpenLayers.Layer.EventPane.prototype.initialize = tempEventPane;
+ OpenLayers.Layer.FixedZoomLevels.prototype.initialize = tempFixedZoomLevels;
+ }
+
+ function test_Layer_Yahoo_loadMapObject(t) {
+ t.plan(5);
+
+ var temp = YMap;
+ YMap = OpenLayers.Class({
+ initialize: function(div, type, size) {
+ t.ok(div == g_Div, "correct div passed to YMap constructor");
+ t.ok(type == g_Type, "correct type passed to YMap constructor");
+ t.ok(size == g_YMapSize, "correct size passed to YMap constructor");
+ },
+ disableKeyControls: function() {
+ t.ok(true, "disableKeyControls called on map object");
+ }
+ });
+
+ g_Div = {};
+ g_Type = {};
+ g_MapSize = {};
+ g_YMapSize = {};
+
+ var l = new OpenLayers.Layer.Yahoo();
+ l.div = g_Div;
+ l.type = g_Type;
+ l.map = {
+ 'getSize': function() {
+ return g_MapSize;
+ }
+ };
+ l.getMapObjectSizeFromOLSize = function(mapSize) {
+ t.ok(mapSize == g_MapSize, "correctly translating map size from ol to YSize");
+ return g_YMapSize;
+ };
+
+ l.loadMapObject();
+
+ YMap = temp;
+ }
+
+ function test_Layer_Yahoo_onMapResize(t) {
+ t.plan(2);
+
+ g_MapSize = {};
+ g_YMapSize = {};
+
+ var l = new OpenLayers.Layer.Yahoo();
+ l.mapObject = {
+ 'resizeTo': function(size) {
+ t.ok(size == g_YMapSize, "correct YSize passed to reiszeTo on map object");
+ }
+ }
+ l.map = {
+ 'getSize': function() {
+ return g_MapSize;
+ }
+ };
+ l.getMapObjectSizeFromOLSize = function(mapSize) {
+ t.ok(mapSize == g_MapSize, "correctly translating map size from ol to YSize");
+ return g_YMapSize;
+ };
+
+ l.onMapResize();
+ }
+
+ function test_Layer_Yahoo_getMapObjectSizeFromOLSize(t) {
+ t.plan(2);
+
+ var temp = YSize;
+ YSize = function(w, h) {
+ t.ok(w == g_Size.w, "correct width passed to YSize constructor");
+ t.ok(h == g_Size.h, "correct height passed to YSize constructor");
+ }
+
+ g_Size = {
+ 'w': {},
+ 'h': {}
+ };
+
+ OpenLayers.Layer.Yahoo.prototype.getMapObjectSizeFromOLSize(g_Size);
+
+ YSize = temp;
+ }
+
+
+ </script>
+</head>
+<body>
+ <div id="map"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/deprecated/Layer/mice.xml b/misc/openlayers/tests/deprecated/Layer/mice.xml
new file mode 100644
index 0000000..4a001ec
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Layer/mice.xml
@@ -0,0 +1,156 @@
+<?xml version='1.0' encoding="ISO-8859-1" ?>
+<wfs:FeatureCollection
+ xmlns:bsc="http://www.bsc-eoc.org/bsc"
+ xmlns:wfs="http://www.opengis.net/wfs"
+ xmlns:gml="http://www.opengis.net/gml"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengeospatial.net//wfs/1.0.0/WFS-basic.xsd
+ http://www.bsc-eoc.org/bsc http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp?SERVICE=WFS&amp;VERSION=1.0.0&amp;REQUEST=DescribeFeatureType&amp;TYPENAME=OWLS&amp;OUTPUTFORMAT=XMLSCHEMA">
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-89.817223,45.005555 -74.755001,51.701388</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <gml:featureMember><bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-79.771668,45.891110 -79.771668,45.891110</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-79.771668,45.891110</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.755834,46.365277 -83.755834,46.365277</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:owlname>owl</bsc:owlname>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.755834,46.365277</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.808612,46.175277 -83.808612,46.175277</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.808612,46.175277</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-84.111112,46.309166 -84.111112,46.309166</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-84.111112,46.309166</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.678612,46.821110 -83.678612,46.821110</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.678612,46.821110</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.664445,46.518888 -83.664445,46.518888</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.664445,46.518888</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-80.613334,46.730277 -80.613334,46.730277</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-80.613334,46.730277</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-79.676946,45.428054 -79.676946,45.428054</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-79.676946,45.428054</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.853056,46.236944 -83.853056,46.236944</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.853056,46.236944</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-82.289167,45.896388 -82.289167,45.896388</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-82.289167,45.896388</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+</wfs:FeatureCollection>
+
diff --git a/misc/openlayers/tests/deprecated/Layer/owls.xml b/misc/openlayers/tests/deprecated/Layer/owls.xml
new file mode 100644
index 0000000..4a001ec
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Layer/owls.xml
@@ -0,0 +1,156 @@
+<?xml version='1.0' encoding="ISO-8859-1" ?>
+<wfs:FeatureCollection
+ xmlns:bsc="http://www.bsc-eoc.org/bsc"
+ xmlns:wfs="http://www.opengis.net/wfs"
+ xmlns:gml="http://www.opengis.net/gml"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengeospatial.net//wfs/1.0.0/WFS-basic.xsd
+ http://www.bsc-eoc.org/bsc http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp?SERVICE=WFS&amp;VERSION=1.0.0&amp;REQUEST=DescribeFeatureType&amp;TYPENAME=OWLS&amp;OUTPUTFORMAT=XMLSCHEMA">
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-89.817223,45.005555 -74.755001,51.701388</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <gml:featureMember><bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-79.771668,45.891110 -79.771668,45.891110</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-79.771668,45.891110</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.755834,46.365277 -83.755834,46.365277</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:owlname>owl</bsc:owlname>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.755834,46.365277</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.808612,46.175277 -83.808612,46.175277</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.808612,46.175277</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-84.111112,46.309166 -84.111112,46.309166</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-84.111112,46.309166</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.678612,46.821110 -83.678612,46.821110</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.678612,46.821110</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.664445,46.518888 -83.664445,46.518888</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.664445,46.518888</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-80.613334,46.730277 -80.613334,46.730277</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-80.613334,46.730277</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-79.676946,45.428054 -79.676946,45.428054</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-79.676946,45.428054</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.853056,46.236944 -83.853056,46.236944</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.853056,46.236944</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-82.289167,45.896388 -82.289167,45.896388</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-82.289167,45.896388</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+</wfs:FeatureCollection>
+
diff --git a/misc/openlayers/tests/deprecated/Popup/AnchoredBubble.html b/misc/openlayers/tests/deprecated/Popup/AnchoredBubble.html
new file mode 100644
index 0000000..ffad069
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Popup/AnchoredBubble.html
@@ -0,0 +1,61 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script src="../../../lib/Rico/Corner.js"></script>
+ <script src="../../../lib/deprecated.js"></script>
+ <script type="text/javascript">
+
+ function test_Popup_Anchored_setOpacity(t) {
+ t.plan(5);
+ var opacity = 0.5;
+ var id = "chicken";
+ var w = 500;
+ var h = 400;
+ var sz = new OpenLayers.Size(w,h);
+ var lon = 5;
+ var lat = 40;
+ var ll = new OpenLayers.LonLat(lon, lat);
+ var content = "foo";
+ var x = 50;
+ var y = 100;
+
+ var map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer('name', {'isBaseLayer':true}));
+ map.zoomToMaxExtent();
+
+ var popup = new OpenLayers.Popup.AnchoredBubble(id,
+ ll,
+ sz,
+ content,
+ null,
+ false);
+ map.addPopup(popup);
+
+ popup.setOpacity(opacity);
+ popup.draw(new OpenLayers.Pixel(x, y));
+
+ if (navigator.appName.indexOf("Microsoft") == -1 || new RegExp(/msie 10/).test(navigator.userAgent.toLowerCase())) {
+ t.eq(parseFloat(popup.div.style.opacity), opacity, "good default popup.opacity");
+ } else {
+ t.eq(popup.div.style.filter, "alpha(opacity=" + opacity*100 + ")", "good default popup.opacity");
+ }
+
+ t.ok(popup.groupDiv!=null, "popup.groupDiv exists");
+ t.ok(popup.groupDiv.parentNode!=null, "popup.groupDiv.parentNode exists");
+ t.ok(popup.groupDiv.parentNode.getElementsByTagName("span").length > 0, "popup.groupDiv.parentNode has SPAN children");
+
+ var ricoCornerDiv = popup.groupDiv.parentNode.getElementsByTagName("span")[0];
+ if (navigator.appName.indexOf("Microsoft") == -1 || new RegExp(/msie 10/).test(navigator.userAgent.toLowerCase())) {
+ t.eq(parseFloat(ricoCornerDiv.style.opacity), opacity, "good default ricoCornerDiv.opacity");
+ } else {
+ t.eq(ricoCornerDiv.style.filter, "alpha(opacity=" + opacity*100 + ")", "good default ricoCornerDiv.opacity");
+ }
+
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:512px; height:256px"> </div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/deprecated/Protocol/SQL.html b/misc/openlayers/tests/deprecated/Protocol/SQL.html
new file mode 100644
index 0000000..f45203d
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Protocol/SQL.html
@@ -0,0 +1,24 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script src="../../../lib/deprecated.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ t.plan(3);
+ var options = {tableName: 'my_features',
+ databaseName: 'my_database_name'}
+ var protocol = new OpenLayers.Protocol.SQL(options);
+
+ t.ok(protocol instanceof OpenLayers.Protocol.SQL,
+ "new OpenLayers.Protocol.SQL returns object");
+
+ t.eq(protocol.tableName, options.tableName, "tableName property is set");
+ t.eq(protocol.databaseName, options.databaseName, "databaseName property is set");
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/deprecated/Protocol/SQL/Gears.html b/misc/openlayers/tests/deprecated/Protocol/SQL/Gears.html
new file mode 100644
index 0000000..0909fb4
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Protocol/SQL/Gears.html
@@ -0,0 +1,474 @@
+<html>
+<head>
+ <script src="../../../OLLoader.js"></script>
+ <script src="../../../../lib/deprecated.js"></script>
+ <script type="text/javascript">
+
+ function test_initialize(t) {
+ var protocol = new OpenLayers.Protocol.SQL.Gears();
+ if (!protocol.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(5);
+
+ t.eq(protocol.CLASS_NAME, "OpenLayers.Protocol.SQL.Gears",
+ "ctor returns correct value");
+
+ t.eq(protocol.jsonParser.CLASS_NAME,
+ "OpenLayers.Format.JSON",
+ "ctor creates a JSON parser");
+
+ t.eq(protocol.wktParser.CLASS_NAME,
+ "OpenLayers.Format.WKT",
+ "ctor creates a WKT parser");
+
+ var str = protocol.FID_PREFIX + "foo_bar";
+ t.ok(str.match(protocol.fidRegExp),
+ "ctor creates correct regexp");
+
+ t.ok(typeof protocol.db == "object",
+ "ctor creates a db object");
+
+ protocol.clear();
+ protocol.destroy();
+ }
+
+ function test_destroy(t) {
+ var protocol = new OpenLayers.Protocol.SQL.Gears();
+ if (!protocol.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(3);
+
+ protocol.destroy();
+
+ t.eq(protocol.db, null,
+ "destroy nullifies db");
+ t.eq(protocol.jsonParser, null,
+ "destroy nullifies jsonParser");
+ t.eq(protocol.wktParser, null,
+ "destroy nullifies wktParser");
+ }
+
+ function test_read(t) {
+ var protocolCallback, readCallback;
+ var protocolOptions = {callback: protocolCallback};
+ var readOptions = {callback: readCallback};
+
+ var protocol = new OpenLayers.Protocol.SQL.Gears(protocolOptions);
+ if (!protocol.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ function okCallback(resp) {
+ t.eq(resp.CLASS_NAME, "OpenLayers.Protocol.Response",
+ "read calls correct callback with a response object");
+ }
+
+ function failCallback(resp) {
+ t.fail("read calls incorrect callback");
+ }
+
+ t.plan(4);
+
+ var resp;
+
+ // 2 tests
+ protocolOptions.callback = okCallback;
+ readOptions.callback = failCallback;
+ resp = protocol.read();
+ t.eq(resp.CLASS_NAME, "OpenLayers.Protocol.Response",
+ "read returns a response object");
+
+ // 2 test
+ protocolOptions.callback = failCallback;
+ readOptions.callback = okCallback;
+ resp = protocol.read(readOptions);
+ t.eq(resp.CLASS_NAME, "OpenLayers.Protocol.Response",
+ "read returns a response object");
+
+ protocol.clear();
+ protocol.destroy();
+ }
+
+ function test_unfreezeFeature(t) {
+ var protocol = new OpenLayers.Protocol.SQL.Gears();
+ if (!protocol.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(10);
+
+ var feature;
+ var wkt, json, fid, state;
+
+ json = "{\"fake\":\"properties\"}";
+ fid = "1000";
+ state = OpenLayers.State.INSERT;
+
+ var row = {
+ fieldByName: function(str) {
+ if (str == "geometry") {
+ return wkt;
+ }
+ if (str == "properties") {
+ return json;
+ }
+ if (str == "fid") {
+ return fid;
+ }
+ if (str == "state") {
+ return state;
+ }
+ }
+ };
+
+ // 5 tests
+ wkt = "POINT(1 2)";
+ feature = protocol.unfreezeFeature(row);
+ t.eq(feature.CLASS_NAME, "OpenLayers.Feature.Vector",
+ "unfreezeFeature returns an OpenLayers.Feature.Vector");
+ t.ok(feature.geometry.x == 1 && feature.geometry.y == 2,
+ "unfreezeFeature returns a feature with correct geometry");
+ t.eq(feature.attributes.fake, "properties",
+ "unfreezeFeature returns a feature with correct attributes");
+ t.eq(feature.fid, fid,
+ "unfreezeFeature returns a feature with fid");
+ t.eq(feature.state, state,
+ "unfreezeFeature returns a feature with state");
+
+ // 5 tests
+ wkt = protocol.NULL_GEOMETRY;
+ state = protocol.NULL_FEATURE_STATE;
+ feature = protocol.unfreezeFeature(row);
+ t.eq(feature.CLASS_NAME, "OpenLayers.Feature.Vector",
+ "unfreezeFeature returns an OpenLayers.Feature.Vector");
+ t.eq(feature.geometry, null,
+ "unfreezeFeature returns a feature with correct geometry");
+ t.eq(feature.attributes.fake, "properties",
+ "unfreezeFeature returns a feature with correct attributes");
+ t.eq(feature.fid, fid,
+ "unfreezeFeature returns a feature with fid");
+ t.eq(feature.state, null,
+ "unfreezeFeature returns a feature with state");
+
+ protocol.clear();
+ protocol.destroy();
+ }
+
+ function test_extractFidFromField(t) {
+ var protocol = new OpenLayers.Protocol.SQL.Gears();
+ if (!protocol.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(4);
+
+ var field, fid;
+
+ // fid is a string, field is not prefixed with FID_PREFIX
+ // 1 test
+ field = "10";
+ res = protocol.extractFidFromField(field);
+ t.eq(res, "10",
+ "extractFidFromField returns expected string");
+
+ // fid is a string, field is prefixed with FID_PREFIX
+ // 1 test
+ field = protocol.FIX_PREFIX + "10";
+ res = protocol.extractFidFromField(field);
+ t.eq(res, protocol.FIX_PREFIX + "10",
+ "extractFidFromField returns expected prefixed string");
+
+ // fid is a number, field is not prefixed with FIX_PREFIX
+ // 1 test
+ protocol.typeOfFid = "number";
+ field = "10";
+ res = protocol.extractFidFromField(field);
+ t.eq(res, 10,
+ "extractFidFromField returns expected number");
+
+ // fid is a number, field is prefixed with FIX_PREFIX
+ // 1 test
+ protocol.typeOfFid = "number";
+ field = protocol.FID_PREFIX + "10";
+ res = protocol.extractFidFromField(field);
+ t.eq(res, protocol.FID_PREFIX + "10",
+ "extractFidFromField returns expected prefixed string");
+ }
+
+ function test_freezeFeature(t) {
+ var protocol = new OpenLayers.Protocol.SQL.Gears();
+ if (!protocol.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(8);
+
+ var feature, res;
+
+ // 4 tests
+ feature = new OpenLayers.Feature.Vector();
+ feature.geometry = new OpenLayers.Geometry.Point(1, 2);
+ feature.attributes.fake = "properties";
+ feature.fid = "1000";
+ feature.state = OpenLayers.State.INSERT;
+ res = protocol.freezeFeature(feature);
+ t.eq(res[0], feature.fid,
+ "freezeFeature returns correct fid");
+ t.eq(res[1], "POINT(1 2)",
+ "freezeFeature returns correct WKT");
+ t.eq(res[2], "{\"fake\":\"properties\"}",
+ "freezeFeature returns correct JSON");
+ t.eq(res[3], feature.state,
+ "freezeFeature returns correct feature state");
+
+ // 4 tests
+ protocol.saveFeatureState = false;
+ feature = new OpenLayers.Feature.Vector();
+ feature.attributes.fake = "properties";
+ feature.fid = "1000";
+ feature.state = OpenLayers.State.INSERT;
+ res = protocol.freezeFeature(feature);
+ t.eq(res[0], feature.fid,
+ "freezeFeature returns correct fid");
+ t.eq(res[1], protocol.NULL_GEOMETRY,
+ "freezeFeature returns expected null geom string");
+ t.eq(res[2], "{\"fake\":\"properties\"}",
+ "freezeFeature returns correct JSON");
+ t.eq(res[3], protocol.NULL_FEATURE_STATE,
+ "freezeFeature returns expected null feature state string");
+
+ protocol.clear();
+ protocol.destroy();
+ }
+
+ function test_create(t) {
+ var protocol = new OpenLayers.Protocol.SQL.Gears();
+ if (!protocol.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(8);
+
+ var resp;
+ var scope = {"fake": "scope"};
+
+ var options = {
+ callback: function(resp) {
+ t.eq(resp.CLASS_NAME, "OpenLayers.Protocol.Response",
+ "user callback is passed a response");
+ t.eq(resp.requestType, "create",
+ "user callback is passed correct request type in resp");
+ t.ok(this == scope,
+ "user callback called with correct scope");
+ },
+ scope: scope
+ };
+
+ // 4 tests
+ var feature = new OpenLayers.Feature.Vector();
+ feature.fid = "1000";
+ feature.attributes.fake = "properties";
+ feature.state = OpenLayers.State.INSERT;
+ resp = protocol.create([feature], options);
+ t.eq(resp.CLASS_NAME, "OpenLayers.Protocol.Response",
+ "create returns a response");
+
+ // check what we have in the DB
+ // 4 tests
+ resp = protocol.read({"noFeatureStateReset": true});
+ t.eq(resp.features.length, 1,
+ "create inserts feature in the DB");
+ t.eq(resp.features[0].fid, feature.fid,
+ "create inserts feature with correct fid");
+ t.eq(resp.features[0].attributes.fake, feature.attributes.fake,
+ "create inserts feature with correct attributes");
+ t.eq(resp.features[0].state, feature.state,
+ "create inserts feature with correct state");
+
+ protocol.clear();
+ protocol.destroy();
+ }
+
+ function test_createOrUpdate(t) {
+ var protocol = new OpenLayers.Protocol.SQL.Gears();
+ if (!protocol.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(5);
+
+ // 1 test
+ var feature = new OpenLayers.Feature.Vector();
+ feature.fid = "1000";
+ feature.attributes.fake = "properties";
+ feature.state = OpenLayers.State.INSERT;
+ resp = protocol.createOrUpdate([feature]);
+ t.eq(resp.CLASS_NAME, "OpenLayers.Protocol.Response",
+ "createOrUpdate returns a response");
+
+ // check what we have in the DB
+ // 4 tests
+ resp = protocol.read({"noFeatureStateReset": true});
+ t.eq(resp.features.length, 1,
+ "createOrUpdate inserts feature in the DB");
+ t.eq(resp.features[0].fid, feature.fid,
+ "createOrUpdate inserts feature with correct fid");
+ t.eq(resp.features[0].attributes.fake, feature.attributes.fake,
+ "createOrUpdate inserts feature with correct attributes");
+ t.eq(resp.features[0].state, feature.state,
+ "createOrUpdate inserts feature with correct state");
+
+ protocol.clear();
+ protocol.destroy();
+ }
+
+ function test_delete(t) {
+ var protocol = new OpenLayers.Protocol.SQL.Gears();
+ if (!protocol.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(4);
+
+ function createOneAndDeleteOne(fid, deleteOptions) {
+ var feature = new OpenLayers.Feature.Vector();
+ feature.fid = fid;
+ feature.attributes.fake = "properties";
+ feature.state = OpenLayers.State.INSERT;
+ var r = protocol.create([feature]);
+ protocol["delete"](r.reqFeatures, deleteOptions);
+ }
+
+ var resp, fid;
+
+ // 1 test
+ fid = 1000;
+ protocol.saveFeatureState = false;
+ createOneAndDeleteOne(fid)
+ resp = protocol.read();
+ t.eq(resp.features.length, 0,
+ "delete deletes feature if saveFeatureState is false");
+ protocol.clear();
+
+ // 1 test
+ fid = 1000;
+ protocol.saveFeatureState = true;
+ createOneAndDeleteOne(fid);
+ resp = protocol.read();
+ t.eq(resp.features.length, 1,
+ "delete does not delete feature if saveFeatureState is true");
+ protocol.clear();
+
+ // 1 test
+ fid = "1000";
+ protocol.saveFeatureState = true;
+ createOneAndDeleteOne(fid);
+ resp = protocol.read();
+ t.eq(resp.features.length, 1,
+ "delete does not delete feature if saveFeatureState is true");
+ protocol.clear();
+
+ // 1 test
+ fid = protocol.FID_PREFIX + "1000";
+ protocol.saveFeatureState = true;
+ createOneAndDeleteOne(fid, {dontDelete: true});
+ resp = protocol.read();
+ t.eq(resp.features.length, 0,
+ "delete deletes feature if saveFeatureState is true and fid is prefixed");
+ protocol.clear();
+
+ protocol.destroy();
+ }
+
+ function test_callUserCallback(t) {
+ var protocol = new OpenLayers.Protocol.SQL.Gears();
+ if (!protocol.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(6);
+
+ var options, resp;
+ var scope = {'fake': 'scope'};
+
+ // test commit callback
+ // 1 tests
+ options = {
+ 'callback': function() {
+ t.ok(this == scope, 'callback called with correct scope');
+ },
+ 'scope': scope
+ };
+ resp = {'requestType': 'create', 'last': true};
+ protocol.callUserCallback(options, resp);
+ // 0 test
+ resp = {'requestType': 'create', 'last': false};
+ protocol.callUserCallback(options, resp);
+
+ // test create callback
+ // 2 tests
+ options = {
+ 'create': {
+ 'callback': function(r) {
+ t.ok(this == scope, 'callback called with correct scope');
+ t.ok(r == resp, 'callback called with correct response');
+ },
+ 'scope': scope
+ }
+ };
+ resp = {'requestType': 'create'};
+ protocol.callUserCallback(options, resp);
+
+ // test with both callbacks set
+ // 3 tests
+ options = {
+ 'create': {
+ 'callback': function(r) {
+ t.ok(this == scope, 'callback called with correct scope');
+ t.ok(r == resp, 'callback called with correct response');
+ },
+ 'scope': scope
+ },
+ 'callback': function() {
+ t.ok(this == scope, 'callback called with correct scope');
+ },
+ 'scope': scope
+ };
+ resp = {'requestType': 'create', 'last': true};
+ protocol.callUserCallback(options, resp);
+
+ // no callback set
+ // 0 test
+ options = {
+ 'delete': {
+ 'callback': function(resp) {
+ t.fail('callback should not get called');
+ }
+ }
+ };
+ resp = {'requestType': 'create'};
+ protocol.callUserCallback(options, resp);
+
+ // cleanup
+ protocol.destroy();
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/misc/openlayers/tests/deprecated/Renderer/SVG2.html b/misc/openlayers/tests/deprecated/Renderer/SVG2.html
new file mode 100644
index 0000000..c23b95c
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Renderer/SVG2.html
@@ -0,0 +1,399 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script src="../../../lib/deprecated.js"></script>
+ <script type="text/javascript">
+
+ var geometry = null, node = null;
+
+ function test_SVG_constructor(t) {
+ if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+ var r = new OpenLayers.Renderer.SVG2(document.body);
+ t.ok(r instanceof OpenLayers.Renderer.SVG2, "new OpenLayers.Renderer.SVG2 returns SVG object" );
+ }
+
+ function test_SVG_destroy(t) {
+ if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+
+ var g_Destroy = false;
+
+ OpenLayers.Renderer.Elements.prototype._destroy =
+ OpenLayers.Renderer.Elements.prototype.destroy;
+
+ OpenLayers.Renderer.prototype.destroy = function() {
+ g_Destroy = true;
+ }
+
+ var r = new OpenLayers.Renderer.SVG2(document.body);
+ r.destroy();
+
+ t.eq(g_Destroy, true, "OpenLayers.Renderer.Elements.destroy() called");
+
+ OpenLayers.Renderer.prototype.destroy =
+ OpenLayers.Renderer.prototype._destroy;
+ }
+
+ function test_SVG_updateDimensions(t) {
+ if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(7);
+
+ OpenLayers.Renderer.SVG2.prototype._setExtent =
+ OpenLayers.Renderer.SVG2.prototype.setExtent;
+
+ var g_SetExtent = false;
+ OpenLayers.Renderer.SVG2.prototype.setExtent = function() {
+ g_SetExtent = true;
+ OpenLayers.Renderer.SVG2.prototype._setExtent.apply(this, arguments);
+ }
+
+ var r = new OpenLayers.Renderer.SVG2(document.body);
+ var extent = new OpenLayers.Bounds(1,2,3,4);
+ r.map = {
+ getResolution: function() {
+ return 0.5;
+ },
+ getExtent: function() {
+ return extent;
+ },
+ getMaxExtent: function() {
+ return extent;
+ }
+ }
+ r.updateDimensions();
+
+ t.eq(g_SetExtent, true, "Elements.setExtent() called");
+
+ t.eq(r.extent.toString(), extent.scale(3).toString(), "renderer's extent is correct");
+ t.eq(r.rendererRoot.getAttributeNS(null, "width"), "12", "width is correct");
+ t.eq(r.rendererRoot.getAttributeNS(null, "height"), "12", "height is correct");
+ t.eq(r.rendererRoot.getAttributeNS(null, "viewBox"), "-1 -6 6 6", "rendererRoot viewBox is correct");
+
+ // test extent changes
+ extent = new OpenLayers.Bounds(2,3,5,6);
+ r.updateDimensions();
+ t.eq(r.extent.toString(), extent.scale(3).toString(), "renderer's extent changed after updateDimensions");
+ t.eq(r.rendererRoot.getAttributeNS(null, "viewBox"), "-1 -9 9 9", "rendererRoot viewBox is correct after a new setExtent");
+
+ OpenLayers.Renderer.SVG2.prototype.setExtent =
+ OpenLayers.Renderer.SVG2.prototype._setExtent;
+ }
+
+ function test_SVG_drawpoint(t) {
+ if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+
+ var r = new OpenLayers.Renderer.SVG2(document.body);
+
+ var properDraw = false;
+ var g_Radius = null;
+ r.drawCircle = function(n, g, r) {
+ properDraw = true;
+ g_Radius = 1;
+ }
+ r.drawPoint();
+
+ t.ok(properDraw && g_Radius == 1, "drawPoint called drawCircle with radius set to 1");
+ }
+
+ function test_SVG_drawcircle(t) {
+ if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(5);
+
+ var r = new OpenLayers.Renderer.SVG2(document.body);
+ r.resolution = 0.5;
+ r.left = 0;
+ r.top = 0;
+
+ var node = document.createElement('div');
+
+ var geometry = {
+ x: 1,
+ y: 2
+ }
+
+ r.drawCircle(node, geometry, 3);
+
+ t.eq(node.getAttributeNS(null, 'cx'), '1', "cx is correct");
+ t.eq(node.getAttributeNS(null, 'cy'), '-2', "cy is correct");
+ t.eq(node._radius, 3, "radius preset is correct");
+
+ // #1274: out of bound node fails when first added
+ var geometry = {
+ x: 10000000,
+ y: 200000000,
+ CLASS_NAME: "OpenLayers.Geometry.Point",
+ id: "foo",
+ getBounds: function() {return {bottom: 0}}
+ }
+ node.id = geometry.id;
+ r.root.appendChild(node);
+
+ var drawCircleCalled = false;
+ r.drawCircle = function() {
+ drawCircleCalled = true;
+ return OpenLayers.Renderer.SVG2.prototype.drawCircle.apply(r, arguments);
+ }
+
+ r.drawGeometry(geometry, {pointRadius: 3}, "blah_4000");
+ t.eq(drawCircleCalled, true, "drawCircle called on drawGeometry for a point geometry.")
+ t.ok(node.parentNode != r.root, "circle will not be drawn when coordinates are outside the valid range");
+ }
+
+ function test_SVG_drawlinestring(t) {
+ if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(2);
+
+ var r = new OpenLayers.Renderer.SVG2(document.body);
+
+ var node = document.createElement('div');
+
+ var geometry = {
+ components: "foo"
+ }
+ g_GetString = false;
+ g_Components = null;
+ r.getComponentsString = function(c) {
+ g_GetString = true;
+ g_Components = c;
+ return "bar";
+ }
+
+ r.drawLineString(node, geometry);
+
+ t.ok(g_GetString && g_Components == "foo", "getComponentString is called with valid arguments");
+ t.eq(node.getAttributeNS(null, "points"), "bar", "points attribute is correct");
+ }
+
+ function test_SVG_drawlinearring(t) {
+ if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(2);
+
+ var r = new OpenLayers.Renderer.SVG2(document.body);
+
+ var node = document.createElement('div');
+
+ var geometry = {
+ components: "foo"
+ }
+ g_GetString = false;
+ g_Components = null;
+ r.getComponentsString = function(c) {
+ g_GetString = true;
+ g_Components = c;
+ return "bar";
+ }
+
+ r.drawLinearRing(node, geometry);
+
+ t.ok(g_GetString, "getComponentString is called with valid arguments");
+ t.eq(node.getAttributeNS(null, "points"), "bar", "points attribute is correct");
+ }
+
+ function test_SVG_drawpolygon(t) {
+ if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(3);
+
+ var r = new OpenLayers.Renderer.SVG2(document.body);
+
+ var node = document.createElement('div');
+
+ var linearRings = [{
+ components: ["foo"]
+ },{
+ components: ["bar"]
+ }]
+
+ var geometry = {
+ components: linearRings
+ }
+ g_GetString = false;
+ r.getShortString = function(c) {
+ g_GetString = true;
+ return c;
+ }
+
+ r.drawPolygon(node, geometry);
+
+ t.ok(g_GetString, "getShortString is called");
+ t.eq(node.getAttributeNS(null, "d"), "M foo M bar z", "d attribute is correctly set");
+ t.eq(node.getAttributeNS(null, "fill-rule"), "evenodd", "fill-rule attribute is correctly set");
+ }
+
+ function test_SVG_drawrectangle(t) {
+ if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(4);
+
+ var r = new OpenLayers.Renderer.SVG2(document.body);
+ r.resolution = 0.5;
+ r.left = 0;
+ r.top = 0;
+
+ var node = document.createElement('div');
+
+ var geometry = {
+ x: 1,
+ y: 2,
+ width: 3,
+ height: 4
+ }
+
+ r.drawRectangle(node, geometry);
+
+ t.eq(node.getAttributeNS(null, "x"), "1", "x attribute is correctly set");
+ t.eq(node.getAttributeNS(null, "y"), "-2", "y attribute is correctly set");
+ t.eq(node.getAttributeNS(null, "width"), "3", "width attribute is correctly set");
+ t.eq(node.getAttributeNS(null, "height"), "4", "height attribute is correctly set");
+ }
+
+ function test_SVG_getcomponentsstring(t) {
+ if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+
+ var components = ['foo', 'bar'];
+
+ OpenLayers.Renderer.SVG2.prototype._getShortString =
+ OpenLayers.Renderer.SVG2.prototype.getShortString;
+
+ OpenLayers.Renderer.SVG2.prototype.getShortString = function(p) {
+ return p;
+ };
+
+ var string = OpenLayers.Renderer.SVG2.prototype.getComponentsString(components);
+ t.eq(string, "foo,bar", "returned string is correct");
+
+ OpenLayers.Renderer.SVG2.prototype.getShortString =
+ OpenLayers.Renderer.SVG2.prototype._getShortString;
+ }
+
+
+
+ function test_SVG_getshortstring(t) {
+ if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+
+ var r = new OpenLayers.Renderer.SVG2(document.body);
+ r.resolution = 0.5;
+ r.left = 0;
+ r.top = 0;
+
+ var point = {
+ x: 1,
+ y: 2
+ };
+
+ var string = r.getShortString(point);
+ t.eq(string, "1,-2", "returned string is correct");
+ }
+
+ function test_svg_getnodetype(t) {
+ if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(1);
+
+ var r = new OpenLayers.Renderer.SVG2(document.body);
+
+ var g = {CLASS_NAME: "OpenLayers.Geometry.Point"}
+ var s = {graphicName: "square"};
+
+ t.eq(r.getNodeType(g, s), "svg", "Correct node type for well known symbols");
+ }
+
+ function test_svg_importsymbol(t) {
+ if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(2);
+
+ var r = new OpenLayers.Renderer.SVG2(document.body);
+
+ r.importSymbol("square");
+
+ var polygon = document.getElementById(r.container.id + "_defs").firstChild.firstChild;
+
+ var pass = false;
+ for (var i = 0; i < polygon.points.numberOfItems; i++) {
+ var p = polygon.points.getItem(i);
+ pass = p.x === OpenLayers.Renderer.symbol.square[2*i] &&
+ p.y === OpenLayers.Renderer.symbol.square[2*i+1];
+ if (!pass) {
+ break;
+ }
+ }
+ t.ok(pass, "Square symbol rendered correctly");
+ t.ok(r.symbolMetrics["-square"], "Symbol metrics cached correctly.");
+ }
+
+ function test_svg_dashstyle(t) {
+ if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+ t.plan(0);
+ return;
+ }
+
+ t.plan(5);
+
+ var r = new OpenLayers.Renderer.SVG2(document.body);
+
+ t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "dot"}, 1), "1,4", "dot dasharray created correctly");
+ t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "dash"}, 1), "4,4", "dash dasharray created correctly");
+ t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "longdash"}, 1), "8,4", "longdash dasharray created correctly");
+ t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "dashdot"}, 1), "4,4,1,4", "dashdot dasharray created correctly");
+ t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "longdashdot"}, 1), "8,4,1,4", "dashdot dasharray created correctly");
+ }
+
+ </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/deprecated/Tile/WFS.html b/misc/openlayers/tests/deprecated/Tile/WFS.html
new file mode 100644
index 0000000..3dee1c7
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Tile/WFS.html
@@ -0,0 +1,215 @@
+<html>
+<head>
+ <script src="../../OLLoader.js"></script>
+ <script src="../../../lib/deprecated.js"></script>
+ <script type="text/javascript">
+ var tile;
+
+ var map, layer;
+ function setUp() {
+ map = new OpenLayers.Map("map");
+ layer = new OpenLayers.Layer(null, {
+ isBaseLayer: true
+ });
+ map.addLayer(layer)
+ map.setCenter(new OpenLayers.LonLat(0, 0));
+ }
+
+ function tearDown() {
+ map.destroy();
+ map = null;
+ layer = null;
+ }
+
+ function test_Tile_WFS_constructor (t) {
+ t.plan( 8 );
+ setUp();
+
+ var position = new OpenLayers.Pixel(10,20);
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var url = "bobob";
+ var size = new OpenLayers.Size(5,6);
+
+ tile = new OpenLayers.Tile.WFS(layer, position, bounds, url, size);
+
+ t.ok( tile instanceof OpenLayers.Tile.WFS, "new OpenLayers.Tile.WFS returns Tile.WFS object" );
+ t.ok( tile.layer === layer, "tile.layer set correctly");
+ t.ok( tile.position.equals(position), "tile.position set correctly");
+ t.ok( tile.bounds.equals(bounds), "tile.bounds set correctly");
+ t.eq( tile.url, url, "tile.url set correctly");
+ t.ok( tile.size.equals(size), "tile.size is set correctly" );
+
+ t.ok( tile.id != null, "tile is given an id");
+ t.ok( tile.events != null, "tile's events intitialized");
+
+ tearDown();
+ }
+
+ function test_Tile_WFS_requestSuccess(t) {
+ t.plan(2);
+ setUp();
+
+ var tile = {
+ 'request': {}
+ };
+
+ OpenLayers.Tile.WFS.prototype.requestSuccess.apply(tile, []);
+
+ t.ok(tile.request == null, "request property on tile set to null");
+
+ var position = new OpenLayers.Pixel(10,20);
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var url = "bobob";
+ var size = new OpenLayers.Size(5,6);
+
+ tile = new OpenLayers.Tile.WFS(layer, position, bounds, url, size);
+ tile.destroy();
+ tile.requestSuccess({'requestText': '<xml><foo /></xml>'});
+ t.ok(true, "Didn't fail after calling requestSuccess on destroyed tile.");
+
+ tearDown();
+ }
+
+ function test_Tile_WFS_loadFeaturesForRegion(t) {
+ t.plan(9);
+
+ var tile = {
+ 'url': {}
+ };
+
+ var g_Success = {};
+
+ var _get = OpenLayers.Request.GET;
+ OpenLayers.Request.GET = function(config) {
+ t.ok(config.url == tile.url, "tile's url correctly passed");
+ t.ok(config.params == null, "null params");
+ t.ok(config.scope == tile, "tile passed as scope");
+ t.ok(config.success == g_Success, "success passed");
+ };
+
+ //no running request -- 4 tests
+ OpenLayers.Tile.WFS.prototype.loadFeaturesForRegion.apply(tile, [g_Success]);
+
+ //running request (cancelled) -- 4 tests + 1 test (for request abort)
+ tile.request = {
+ 'abort': function() {
+ t.ok(true, "request aborted");
+ }
+ };
+ OpenLayers.Tile.WFS.prototype.loadFeaturesForRegion.apply(tile, [g_Success]);
+
+ OpenLayers.Request.GET = _get;
+ }
+
+ function test_Tile_WFS_destroy(t) {
+ t.plan(9);
+ setUp();
+
+ var position = new OpenLayers.Pixel(10,20);
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var url = "bobob";
+ var size = new OpenLayers.Size(5,6);
+
+ tile = new OpenLayers.Tile.WFS(layer, position, bounds, url, size);
+ tile.events.destroy = function() {
+ t.ok(true, "tile events destroy() called");
+ };
+
+
+ var _gAbort = false;
+ tile.request = {
+ abort: function() {
+ _gAbort = true;
+ }
+ }
+
+
+ tile.destroy();
+
+ t.ok(tile.layer == null, "tile.layer set to null");
+ t.ok(tile.bounds == null, "tile.bounds set to null");
+ t.ok(tile.size == null, "tile.size set to null");
+ t.ok(tile.position == null, "tile.position set to null");
+ t.ok(_gAbort, "request transport is aborted");
+ t.ok(tile.request == null, "tile.request set to null");
+
+ t.ok(tile.events == null, "tile.events set to null");
+
+ tile.requestSuccess({'requestText': '<xml><foo /></xml>'});
+ t.ok(true, "Didn't fail after calling requestSuccess on destroyed tile.");
+
+ tearDown();
+ }
+ function test_nonxml_format(t) {
+ t.plan(2);
+
+ setUp();
+
+ var data = '{"type":"Feature", "id":"OpenLayers.Feature.Vector_135", "properties":{}, "geometry":{"type":"Point", "coordinates":[118.125, -18.6328125]}, "crs":{"type":"OGC", "properties":{"urn":"urn:ogc:def:crs:OGC:1.3:CRS84"}}}'
+ var position = new OpenLayers.Pixel(10,20);
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var url = "bobob";
+ var size = new OpenLayers.Size(5,6);
+
+ var log = [];
+
+ var l = new OpenLayers.Layer(null, {
+ vectorMode: true,
+ formatObject: new OpenLayers.Format.GeoJSON(),
+ addFeatures: function(features) {
+ log.push(features);
+ }
+ })
+ map.addLayer(l);
+
+ var tile = new OpenLayers.Tile.WFS(l, position, bounds, url, size);
+
+ tile.requestSuccess({responseText: data});
+
+ t.eq(log.length, 1, "one call logged")
+ t.eq(log[0] && log[0].length, 1, "GeoJSON format returned a single feature which was added.");
+
+ tearDown();
+ }
+
+ function test_xml_string_and_dom(t) {
+ t.plan(4);
+ setUp();
+
+ var data = '<?xml version="1.0" encoding="ISO-8859-1" ?><wfs:FeatureCollection xmlns:bsc="http://www.bsc-eoc.org/bsc" xmlns:wfs="http://www.opengis.net/wfs" xmlns:gml="http://www.opengis.net/gml" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengeospatial.net//wfs/1.0.0/WFS-basic.xsd http://www.bsc-eoc.org/bsc http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp?SERVICE=WFS&amp;VERSION=1.0.0&amp;REQUEST=DescribeFeatureType&amp;TYPENAME=OWLS&amp;OUTPUTFORMAT=XMLSCHEMA"> <gml:boundedBy> <gml:Box srsName="EPSG:4326"> <gml:coordinates>-94.989723,43.285833 -74.755001,51.709520</gml:coordinates> </gml:Box> </gml:boundedBy> <gml:featureMember> <bsc:OWLS> <gml:boundedBy> <gml:Box srsName="EPSG:4326"> <gml:coordinates>-94.142500,50.992777 -94.142500,50.992777</gml:coordinates> </gml:Box> </gml:boundedBy> <bsc:msGeometry> <gml:Point srsName="EPSG:4326"> <gml:coordinates>-94.142500,50.992777</gml:coordinates> </gml:Point> </bsc:msGeometry> <bsc:ROUTEID>ON_2</bsc:ROUTEID> <bsc:ROUTE_NAME>Suffel Road</bsc:ROUTE_NAME> <bsc:LATITUDE>50.9927770</bsc:LATITUDE> <bsc:LONGITUDE>-94.1425000</bsc:LONGITUDE> </bsc:OWLS> </gml:featureMember></wfs:FeatureCollection>';
+ var position = new OpenLayers.Pixel(10,20);
+ var bounds = new OpenLayers.Bounds(1,2,3,4);
+ var url = "bobob";
+ var size = new OpenLayers.Size(5,6);
+
+ var l = new OpenLayers.Layer();
+ map.addLayer(l);
+
+ var tile = new OpenLayers.Tile.WFS(l, position, bounds, url, size);
+
+ var log = [];
+ tile.addResults = function(results) {
+ log.push(results);
+ }
+ tile.requestSuccess({responseText: data});
+
+ t.eq(log.length, 1, "first call logged");
+ t.eq(log[0] && log[0].length, 1, "results count is correct when passing in XML as a string into non-vectormode");
+
+ log.length = 0;
+ tile.addResults = function(results) {
+ log.push(results);
+ }
+ tile.requestSuccess({responseXML: OpenLayers.Format.XML.prototype.read(data)});
+
+ t.eq(log.length, 1, "second call logged");
+ t.eq(log[0] && log[0].length, 1, "results count is correct when passing in XML as DOM into non-vectormode");
+
+ tearDown();
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
+
diff --git a/misc/openlayers/tests/deprecated/Util.html b/misc/openlayers/tests/deprecated/Util.html
new file mode 100644
index 0000000..e65340b
--- /dev/null
+++ b/misc/openlayers/tests/deprecated/Util.html
@@ -0,0 +1,20 @@
+<html>
+ <head>
+ <script>
+var custom$ = function() {};
+window.$ = custom$;
+ </script>
+ <script src="../OLLoader.js"></script>
+ <script src="../../lib/deprecated.js"></script>
+ <script>
+
+function test_$(t) {
+ t.plan(1);
+ t.ok($ === custom$, "OpenLayers doesn't clobber existing definition of $.");
+}
+
+ </script>
+ </head>
+ <body>
+ </body>
+</html> \ No newline at end of file
diff --git a/misc/openlayers/tests/georss.txt b/misc/openlayers/tests/georss.txt
new file mode 100644
index 0000000..053749b
--- /dev/null
+++ b/misc/openlayers/tests/georss.txt
@@ -0,0 +1,378 @@
+<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/css" href="/css/rss.css" ?>
+
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://purl.org/rss/1.0/"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:georss="http://www.georss.org/georss">
+<docs>This is an RSS file. Copy the URL into your aggregator of choice. If you don't know what this means and want to learn more, please see: <span>http://platial.typepad.com/news/2006/04/really_simple_t.html</span> for more info.</docs><channel rdf:about="http://platial.com">
+<link>http://platial.com</link>
+<title>Crschmidt's Places At Platial</title>
+<description></description>
+<items>
+<rdf:Seq>
+<rdf:li resource="http://platial.com/place/90306"/>
+<rdf:li resource="http://platial.com/place/67230"/>
+<rdf:li resource="http://platial.com/place/65645"/>
+<rdf:li resource="http://platial.com/place/62200"/>
+<rdf:li resource="http://platial.com/place/28232"/>
+<rdf:li resource="http://platial.com/place/43666"/>
+<rdf:li resource="http://platial.com/place/28394"/>
+<rdf:li resource="http://platial.com/place/28251"/>
+<rdf:li resource="http://platial.com/place/28392"/>
+<rdf:li resource="http://platial.com/place/28391"/>
+<rdf:li resource="http://platial.com/place/28231"/>
+<rdf:li resource="http://platial.com/place/28393"/>
+<rdf:li resource="http://platial.com/place/31685"/>
+<rdf:li resource="http://platial.com/place/28596"/>
+<rdf:li resource="http://platial.com/place/28595"/>
+<rdf:li resource="http://platial.com/place/28594"/>
+<rdf:li resource="http://platial.com/place/28593"/>
+<rdf:li resource="http://platial.com/place/28592"/>
+<rdf:li resource="http://platial.com/place/28591"/>
+<rdf:li resource="http://platial.com/place/28590"/>
+<rdf:li resource="http://platial.com/place/28589"/>
+<rdf:li resource="http://platial.com/place/28588"/>
+<rdf:li resource="http://platial.com/place/28587"/>
+<rdf:li resource="http://platial.com/place/28586"/>
+<rdf:li resource="http://platial.com/place/28585"/>
+<rdf:li resource="http://platial.com/place/28584"/>
+<rdf:li resource="http://platial.com/place/28583"/>
+<rdf:li resource="http://platial.com/place/28582"/>
+<rdf:li resource="http://platial.com/place/28581"/>
+<rdf:li resource="http://platial.com/place/28580"/>
+<rdf:li resource="http://platial.com/place/28579"/>
+<rdf:li resource="http://platial.com/place/28578"/>
+<rdf:li resource="http://platial.com/place/28577"/>
+<rdf:li resource="http://platial.com/place/28576"/>
+<rdf:li resource="http://platial.com/place/28575"/>
+<rdf:li resource="http://platial.com/place/28574"/>
+<rdf:li resource="http://platial.com/place/28573"/>
+<rdf:li resource="http://platial.com/place/28572"/>
+<rdf:li resource="http://platial.com/place/28571"/>
+<rdf:li resource="http://platial.com/place/28570"/>
+</rdf:Seq>
+</items>
+</channel>
+<item rdf:about="http://platial.com/place/90306">
+<link>http://platial.com/place/90306</link>
+<title>Knitting Room</title>
+<description><![CDATA[This little shop is jammed full. Yarn, yarn everywhere. They make the most of every possible nook and cranny. I like this place also because they have a lot of different kinds of knitting needles in all different sizes. Also, the people who work here are younger and hipper than in the other stores I go to. I reccomend buying supplies here and then knitting your way through a good documentary at the Capitol Theater across the street.<br/>Address: 2 lake St, Arlington, MA <br/>Tags: knitting, yarn, pins and needles, handspun, hand dyed, novelty yarn, fancy, simple, young, hip, friendly, needles, addy, cute hats<br /><br /><a href="http://platial.com/place/90306">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/90306">Grab this on Platial</a> ]]></description>
+<georss:point>42.405696 -71.142197</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-06-08T17:35:01.942452+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/67230">
+<link>http://platial.com/place/67230</link>
+<title>Knitting Room</title>
+<description><![CDATA[This little shop is jammed full. Yarn, yarn everywhere. They make the most of every possible nook and cranny. I like this place also because they have a lot of different kinds of knitting needles in all different sizes. Also, the people who work here are younger and hipper than in the other stores I go to. I reccomend buying supplies here and then knitting your way through a good documentary at the Capitol Theater across the street.<br/>Address: 2 lake St, Arlington, MA <br/>Tags: knitting, yarn, pins and needles, handspun, hand dyed, novelty yarn, fancy, simple, young, hip, friendly, needles, addy, cute hats<br /><br /><a href="http://platial.com/place/67230">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/67230">Grab this on Platial</a> ]]></description>
+<georss:point>42.405524 -71.142273</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-04-24T11:35:26.733857+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/65645">
+<link>http://platial.com/place/65645</link>
+<title>†¢¢™£ˆøœ</title>
+<description><![CDATA[ijeª£∆µˆ˚î<br/>Address: 151 Erie St., Cambridge, MA<br/>Tags: platial graffiti<br /><br /><a href="http://platial.com/place/65645">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/65645">Grab this on Platial</a> ]]></description>
+<georss:point>42.352455 -71.110210</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-04-20T08:56:12.696224+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/62200">
+<link>http://platial.com/place/62200</link>
+<title>Allen Hall</title>
+<description><![CDATA[My dorm at UIUC.<br/>Address: 1301 W Gregory Dr, Urbana, IL<br/>Tags: dorm, uiuc, college<br/><a href="http://platial.com/place/62200"><img src="http://platial.comhttp://static.flickr.com/4/8576450_0d59cc2531_s.jpg"/></a><br/><br /><br /><a href="http://platial.com/place/62200">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/62200">Grab this on Platial</a> ]]></description>
+<georss:point>40.104172 -88.220623</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-04-14T08:01:01.872873+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28232">
+<link>http://platial.com/place/28232</link>
+<title>Bagby Hot Springs, OR</title>
+<description><![CDATA[Hot spring, temperature: 136 degress F, 58 degress C. However, the area around the springs are not exactly well looked upon by people who know the place.
+
+<br/>Tags: 20s, rosalie, romance, childhood, hike, camping, soak, relax, beautiful, hot springs, bathhouse, favorite, popular, crowded, organized, honeymoon tub, plumbing made from hollowed out trees, hot springs, mt hood, notorious car break in spot, rash, bacteria<br /><br /><a href="http://platial.com/place/28232">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28232">Grab this on Platial</a> ]]></description>
+<georss:point>44.936000 -122.173000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:10:18.553063+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/43666">
+<link>http://platial.com/place/43666</link>
+<title>Shooting Location for "The Field of Dreams" Film</title>
+<description><![CDATA[1989's Field of Dreams was a Best Picture Academy Award nominee, and the baseball field in the cornfield still stands today, and has become quite a tourist destination. Games are occasionally played at the field, re-enacting professional baseball at the turn of the 20th Century.<br/>Address: Dyersville, Iowa<br/>Tags: iowa, baseball, movie locations, field of dreams, kevin costner, costner, dyersville, kinsella, james earl jones, chicago black sox, shoeless joe, joe jackson, famous farms, film, movie, cinema, shooting location<br /><br /><a href="http://platial.com/place/43666">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/43666">Grab this on Platial</a> ]]></description>
+<georss:point>42.481213 -91.111679</georss:point>
+<dc:creator>echinodermata</dc:creator>
+<dc:date>2006-03-23T11:40:17.654061+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28394">
+<link>http://platial.com/place/28394</link>
+<title>Moffetts (Bonneville) Hot Springs, WA</title>
+<description><![CDATA[Hot spring, temperature: 97 degress F, 36 degress C<br/>Tags: soak, hot springs, relax, nature<br /><br /><a href="http://platial.com/place/28394">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28394">Grab this on Platial</a> ]]></description>
+<georss:point>45.658000 -121.962000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:16:27.329816+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28251">
+<link>http://platial.com/place/28251</link>
+<title>Austin Hot Springs, OR</title>
+<description><![CDATA[Hot spring, temperature: 186 degress F, 86 degress C<br/>Tags: soak, hot springs, relax, nature, popular, crowded<br /><br /><a href="http://platial.com/place/28251">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28251">Grab this on Platial</a> ]]></description>
+<georss:point>45.021000 -122.009000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:11:04.489886+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28392">
+<link>http://platial.com/place/28392</link>
+<title>Rock Creek Hot Springs, WA</title>
+<description><![CDATA[Hot spring, temperature: Hot degress F, Hot degress C<br/>Tags: soak, hot springs, relax, nature<br /><br /><a href="http://platial.com/place/28392">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28392">Grab this on Platial</a> ]]></description>
+<georss:point>45.723000 -121.927000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:16:22.636855+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28391">
+<link>http://platial.com/place/28391</link>
+<title>St. Martins (Wind River) Hot Springs, WA</title>
+<description><![CDATA[Hot spring, temperature: 120 degress F, 49 degress C<br/>Tags: hot springs, soak, relax, nature, wonderful<br /><br /><a href="http://platial.com/place/28391">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28391">Grab this on Platial</a> ]]></description>
+<georss:point>45.728000 -121.800000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:16:20.383244+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28231">
+<link>http://platial.com/place/28231</link>
+<title>Breitenbush Hot Springs, OR</title>
+<description><![CDATA[Hot spring, temperature: 198 degress F, 92 degress C<br/>Tags: hot springs, resort, relax, nature, beautiful, http:www.breitenbush.com, soaking<br /><br /><a href="http://platial.com/place/28231">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28231">Grab this on Platial</a> ]]></description>
+<georss:point>44.782000 -121.975000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:10:16.529195+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28393">
+<link>http://platial.com/place/28393</link>
+<title>Collins Hot Springs, WA</title>
+<description><![CDATA[Hot spring, temperature: 122 degress F, 50 degress C<br/>Tags: portland, nice, hot springs, soak<br /><br /><a href="http://platial.com/place/28393">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28393">Grab this on Platial</a> ]]></description>
+<georss:point>45.701000 -121.728000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:16:24.648745+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/31685">
+<link>http://platial.com/place/31685</link>
+<title>Darwin's Ltd.</title>
+<description><![CDATA[Nice little coffee shop/cafe, free Wifi, close enough to walk from Harvard Square.<br/>Address: 148 Mount Auburn St, Cambridge, MA<br/>Tags: coffee, beer, sandwiches, freewifi<br/><a href="http://platial.com/place/31685"><img src="http://platial.comhttp://static.flickr.com/38/84885937_74fd3d1025_s.jpg"/></a><br/><br /><br /><a href="http://platial.com/place/31685">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/31685">Grab this on Platial</a> ]]></description>
+<georss:point>42.373974 -71.125053</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-10T09:24:08.152985+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28596">
+<link>http://platial.com/place/28596</link>
+<title>Huckleberry Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: Boiling degress F, Boiling degress C<br /><br /><a href="http://platial.com/place/28596">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28596">Grab this on Platial</a> ]]></description>
+<georss:point>44.115000 -110.684000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:32.283094+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28595">
+<link>http://platial.com/place/28595</link>
+<title>South Entrance Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 156 degress F, 69 degress C<br/><a href="http://platial.com/place/28595"><img src="http://platial.comhttp://static.flickr.com/52/130989872_f1457f68b5_s.jpg"/></a><br/><br /><br /><a href="http://platial.com/place/28595">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28595">Grab this on Platial</a> ]]></description>
+<georss:point>44.142000 -110.656000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:30.279497+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28594">
+<link>http://platial.com/place/28594</link>
+<title>Crawfish Creek Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 136 degress F, 58 degress C<br/><a href="http://platial.com/place/28594"><img src="http://platial.comhttp://static.flickr.com/52/128312256_d6a879924c_s.jpg"/></a><br/><br /><br /><a href="http://platial.com/place/28594">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28594">Grab this on Platial</a> ]]></description>
+<georss:point>44.157000 -110.699000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:28.280271+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28593">
+<link>http://platial.com/place/28593</link>
+<title>Crawfish Creek Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 138 degress F, 59 degress C<br /><br /><a href="http://platial.com/place/28593">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28593">Grab this on Platial</a> ]]></description>
+<georss:point>44.165000 -110.723000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:20.364077+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28592">
+<link>http://platial.com/place/28592</link>
+<title>Snake Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 136 degress F, 58 degress C<br /><br /><a href="http://platial.com/place/28592">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28592">Grab this on Platial</a> ]]></description>
+<georss:point>44.169000 -110.583000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:12.234974+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28591">
+<link>http://platial.com/place/28591</link>
+<title>Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 142 degress F, 61 degress C<br /><br /><a href="http://platial.com/place/28591">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28591">Grab this on Platial</a> ]]></description>
+<georss:point>44.187000 -110.726000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:10.027857+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28590">
+<link>http://platial.com/place/28590</link>
+<title>Hot Springs on Upper Snake River, WY</title>
+<description><![CDATA[Hot spring, temperature: 167 degress F, 75 degress C<br /><br /><a href="http://platial.com/place/28590">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28590">Grab this on Platial</a> ]]></description>
+<georss:point>44.204000 -110.486000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:07.79658+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28589">
+<link>http://platial.com/place/28589</link>
+<title>Hot Springs on lewis Lake, WY</title>
+<description><![CDATA[Hot spring, temperature: 154 degress F, 68 degress C<br /><br /><a href="http://platial.com/place/28589">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28589">Grab this on Platial</a> ]]></description>
+<georss:point>44.276000 -110.636000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:05.683418+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28588">
+<link>http://platial.com/place/28588</link>
+<title>Rustic Geyser, WY</title>
+<description><![CDATA[Hot spring, temperature: 199 degress F, 93 degress C<br /><br /><a href="http://platial.com/place/28588">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28588">Grab this on Platial</a> ]]></description>
+<georss:point>44.282000 -110.506000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:03.66329+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28587">
+<link>http://platial.com/place/28587</link>
+<title>Bechler River Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 194 degress F, 90 degress C<br /><br /><a href="http://platial.com/place/28587">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28587">Grab this on Platial</a> ]]></description>
+<georss:point>44.285000 -110.900000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:01.611442+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28586">
+<link>http://platial.com/place/28586</link>
+<title>Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: Boiling degress F, 201 degress C<br /><br /><a href="http://platial.com/place/28586">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28586">Grab this on Platial</a> ]]></description>
+<georss:point>44.290000 -110.504000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:59.658699+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28585">
+<link>http://platial.com/place/28585</link>
+<title>Heart Lake Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: Middle Group degress F, 174 degress C<br /><br /><a href="http://platial.com/place/28585">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28585">Grab this on Platial</a> ]]></description>
+<georss:point>44.299000 -110.517000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:57.181801+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28584">
+<link>http://platial.com/place/28584</link>
+<title>Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: Boiling degress F, 201 degress C<br /><br /><a href="http://platial.com/place/28584">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28584">Grab this on Platial</a> ]]></description>
+<georss:point>44.307000 -110.526000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:55.240485+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28583">
+<link>http://platial.com/place/28583</link>
+<title>Hot Springs on lewis Lake, WY</title>
+<description><![CDATA[Hot spring, temperature: 199 degress F, 93 degress C<br /><br /><a href="http://platial.com/place/28583">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28583">Grab this on Platial</a> ]]></description>
+<georss:point>44.309000 -110.654000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:53.22295+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28582">
+<link>http://platial.com/place/28582</link>
+<title>Shoshone Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: 203 degress F, 95 degress C<br /><br /><a href="http://platial.com/place/28582">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28582">Grab this on Platial</a> ]]></description>
+<georss:point>44.354000 -110.800000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:51.179049+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28581">
+<link>http://platial.com/place/28581</link>
+<title>Hot Springs on Continental Divide, WY</title>
+<description><![CDATA[Hot spring, temperature: 189 degress F, 87 degress C<br /><br /><a href="http://platial.com/place/28581">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28581">Grab this on Platial</a> ]]></description>
+<georss:point>44.401000 -110.936000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:49.077176+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28580">
+<link>http://platial.com/place/28580</link>
+<title>Hot Springs on Upper Firehole River, WY</title>
+<description><![CDATA[Hot spring, temperature: Hot degress F, Hot degress C<br /><br /><a href="http://platial.com/place/28580">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28580">Grab this on Platial</a> ]]></description>
+<georss:point>44.404000 -110.824000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:47.054664+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28579">
+<link>http://platial.com/place/28579</link>
+<title>Summit Lake Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 162 degress F, 72 degress C<br /><br /><a href="http://platial.com/place/28579">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28579">Grab this on Platial</a> ]]></description>
+<georss:point>44.410000 -110.953000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:45.039394+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28578">
+<link>http://platial.com/place/28578</link>
+<title>Lone Star Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: Footbridge degress F, 183 degress C<br /><br /><a href="http://platial.com/place/28578">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28578">Grab this on Platial</a> ]]></description>
+<georss:point>44.414000 -110.817000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:42.938808+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28577">
+<link>http://platial.com/place/28577</link>
+<title>West. Thumb Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: 203 degress F, 95 degress C<br /><br /><a href="http://platial.com/place/28577">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28577">Grab this on Platial</a> ]]></description>
+<georss:point>44.417000 -110.570000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:40.90238+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28576">
+<link>http://platial.com/place/28576</link>
+<title>Lone Star Geyser, WY</title>
+<description><![CDATA[Hot spring, temperature: 199 degress F, 93 degress C<br /><br /><a href="http://platial.com/place/28576">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28576">Grab this on Platial</a> ]]></description>
+<georss:point>44.418000 -110.805000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:38.844625+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28575">
+<link>http://platial.com/place/28575</link>
+<title>Smoke Jumper Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 198 degress F, 92 degress C<br /><br /><a href="http://platial.com/place/28575">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28575">Grab this on Platial</a> ]]></description>
+<georss:point>44.421000 -110.952000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:36.818513+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28574">
+<link>http://platial.com/place/28574</link>
+<title>West. Thumb Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: 196 degress F, 91 degress C<br /><br /><a href="http://platial.com/place/28574">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28574">Grab this on Platial</a> ]]></description>
+<georss:point>44.422000 -110.574000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:34.767729+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28573">
+<link>http://platial.com/place/28573</link>
+<title>Potts Hot Spring Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: 203 degress F, 95 degress C<br /><br /><a href="http://platial.com/place/28573">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28573">Grab this on Platial</a> ]]></description>
+<georss:point>44.433000 -110.581000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:32.749915+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28572">
+<link>http://platial.com/place/28572</link>
+<title>Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: Hot degress F, Hot degress C<br /><br /><a href="http://platial.com/place/28572">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28572">Grab this on Platial</a> ]]></description>
+<georss:point>44.433000 -110.813000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:30.829745+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28571">
+<link>http://platial.com/place/28571</link>
+<title>Hot Springs on Continental Divide, WY</title>
+<description><![CDATA[Hot spring, temperature: Hot degress F, Hot degress C<br /><br /><a href="http://platial.com/place/28571">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28571">Grab this on Platial</a> ]]></description>
+<georss:point>44.438000 -110.977000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:28.730401+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28570">
+<link>http://platial.com/place/28570</link>
+<title>SouthEastern Group, WY</title>
+<description><![CDATA[Hot spring, temperature: 198 degress F, 92 degress C<br /><br /><a href="http://platial.com/place/28570">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28570">Grab this on Platial</a> ]]></description>
+<georss:point>44.459000 -110.817000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:26.706763+00:00</dc:date>
+</item>
+</rdf:RDF> \ No newline at end of file
diff --git a/misc/openlayers/tests/grid_inittiles.html b/misc/openlayers/tests/grid_inittiles.html
new file mode 100644
index 0000000..40035a2
--- /dev/null
+++ b/misc/openlayers/tests/grid_inittiles.html
@@ -0,0 +1,30 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <style type="text/css">
+ #map {
+ width: 800px;
+ height: 475px;
+ border: 1px solid black;
+ }
+ </style>
+ <script src="../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ function init(){
+ var map = new OpenLayers.Map('map', {'maxResolution': 1.40625/2, tileSize: new OpenLayers.Size(256,256)});
+ ww = new OpenLayers.Layer.WMS( "Basic",
+ "http://labs.metacarta.com/wms-c/Basic.py?",
+ {layers:"basic"});
+ map.addLayers([ww]);
+ map.zoomToMaxExtent();
+ map.zoomIn();
+ map.zoomOut();
+ map.zoomOut();
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1>Grid Test</h1>
+ <p>Map should display with two centered tiles. If there appear to be a combination of two zoom levels, then this test is failed, and something is broken in OpenLayers.</p>
+ <div id="map"></div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/index.html b/misc/openlayers/tests/index.html
new file mode 100644
index 0000000..86ec617
--- /dev/null
+++ b/misc/openlayers/tests/index.html
@@ -0,0 +1,6 @@
+<html>
+ <head>
+ <meta http-equiv="refresh" content="0;url=run-tests.html">
+ </head>
+ <body></body>
+</html> \ No newline at end of file
diff --git a/misc/openlayers/tests/list-tests.html b/misc/openlayers/tests/list-tests.html
new file mode 100644
index 0000000..f59c477
--- /dev/null
+++ b/misc/openlayers/tests/list-tests.html
@@ -0,0 +1,260 @@
+<ul id="testlist">
+ <li>Animation.html</li>
+ <li>BaseTypes.html</li>
+ <li>BaseTypes/Bounds.html</li>
+ <li>BaseTypes/Class.html</li>
+ <li>BaseTypes/Date.html</li>
+ <li>BaseTypes/Element.html</li>
+ <li>BaseTypes/LonLat.html</li>
+ <li>BaseTypes/Pixel.html</li>
+ <li>BaseTypes/Size.html</li>
+ <li>Console.html</li>
+ <li>Control.html</li>
+ <li>Control/Attribution.html</li>
+ <li>Control/ArgParser.html</li>
+ <li>Control/Button.html</li>
+ <li>Control/CacheRead.html</li>
+ <li>Control/CacheWrite.html</li>
+ <li>Control/DragFeature.html</li>
+ <li>Control/DragPan.html</li>
+ <li>Control/DrawFeature.html</li>
+ <li>Control/EditingToolbar.html</li>
+ <li>Control/Geolocate.html</li>
+ <li>Control/GetFeature.html</li>
+ <li>Control/Graticule.html</li>
+ <li>Control/KeyboardDefaults.html</li>
+ <li>Control/LayerSwitcher.html</li>
+ <li>Control/Measure.html</li>
+ <li>Control/ModifyFeature.html</li>
+ <li>Control/MousePosition.html</li>
+ <li>Control/Navigation.html</li>
+ <li>Control/NavigationHistory.html</li>
+ <li>Control/NavToolbar.html</li>
+ <li>Control/OverviewMap.html</li>
+ <li>Control/Panel.html</li>
+ <li>Control/PanZoom.html</li>
+ <li>Control/PanZoomBar.html</li>
+ <li>Control/Permalink.html</li>
+ <li>Control/PinchZoom.html</li>
+ <li>Control/Scale.html</li>
+ <li>Control/ScaleLine.html</li>
+ <li>Control/SelectFeature.html</li>
+ <li>Control/Snapping.html</li>
+ <li>Control/Split.html</li>
+ <li>Control/TouchNavigation.html</li>
+ <li>Control/TransformFeature.html</li>
+ <li>Control/UTFGrid.html</li>
+ <li>Control/WMSGetFeatureInfo.html</li>
+ <li>Control/WMTSGetFeatureInfo.html</li>
+ <li>Control/PanPanel.html</li>
+ <li>Control/SLDSelect.html</li>
+ <li>Control/Zoom.html</li>
+ <li>Control/ZoomBox.html</li>
+ <li>Events.html</li>
+ <li>Events/buttonclick.html</li>
+ <li>Events/featureclick.html?visible</li>
+ <li>Extras.html</li>
+ <li>Feature.html</li>
+ <li>Feature/Vector.html</li>
+ <li>Filter.html</li>
+ <li>Filter/Comparison.html</li>
+ <li>Filter/FeatureId.html</li>
+ <li>Filter/Logical.html</li>
+ <li>Filter/Spatial.html</li>
+ <li>Format.html</li>
+ <li>Format/Atom.html</li>
+ <li>Format/ArcXML.html</li>
+ <li>Format/XML/VersionedOGC.html</li>
+ <li>Format/ArcXML/Features.html</li>
+ <li>Format/CQL.html</li>
+ <li>Format/EncodedPolyline.html</li>
+ <li>Format/GeoJSON.html</li>
+ <li>Format/GeoRSS.html</li>
+ <li>Format/GML.html</li>
+ <li>Format/GML/v2.html</li>
+ <li>Format/GML/v3.html</li>
+ <li>Format/GPX.html</li>
+ <li>Format/JSON.html</li>
+ <li>Format/KML.html</li>
+ <li>Format/OSM.html</li>
+ <li>Format/Text.html</li>
+ <li>Format/SLD.html</li>
+ <li>Format/SLD/v1_0_0.html</li>
+ <li>Format/SLD/v1_0_0_GeoServer.html</li>
+ <li>Format/Filter.html</li>
+ <li>Format/Filter/v1.html</li>
+ <li>Format/Filter/v1_0_0.html</li>
+ <li>Format/Filter/v1_1_0.html</li>
+ <li>Format/QueryStringFilter.html</li>
+ <li>Format/WCSCapabilities.html</li>
+ <li>Format/WCSCapabilities/v1.html</li>
+ <li>Format/WFS.html</li>
+ <li>Format/WFSCapabilities.html</li>
+ <li>Format/WFSCapabilities/v1.html</li>
+ <li>Format/WFSDescribeFeatureType.html</li>
+ <li>Format/WFST.html</li>
+ <li>Format/WFST/v1.html</li>
+ <li>Format/WFST/v1_0_0.html</li>
+ <li>Format/WFST/v1_1_0.html</li>
+ <li>Format/WKT.html</li>
+ <li>Format/WMC.html</li>
+ <li>Format/WMC/v1_1_0.html</li>
+ <li>Format/WMC/v1.html</li>
+ <li>Format/WMSCapabilities.html</li>
+ <li>Format/WMSCapabilities/v1_1_1.html</li>
+ <li>Format/WMSCapabilities/v1_1_1_WMSC.html</li>
+ <li>Format/WMSCapabilities/v1_3_0.html</li>
+ <li>Format/WMSDescribeLayer.html</li>
+ <li>Format/WMSGetFeatureInfo.html</li>
+ <li>Format/WMTSCapabilities.html</li>
+ <li>Format/WMTSCapabilities/v1_0_0.html</li>
+ <li>Format/WPSCapabilities/v1_0_0.html</li>
+ <li>Format/WPSDescribeProcess.html</li>
+ <li>Format/WPSExecute.html</li>
+ <li>Format/CSWGetDomain.html</li>
+ <li>Format/CSWGetDomain/v2_0_2.html</li>
+ <li>Format/CSWGetRecords.html</li>
+ <li>Format/CSWGetRecords/v2_0_2.html</li>
+ <li>Format/SOSCapabilities/v1_0_0.html</li>
+ <li>Format/SOSGetObservation.html</li>
+ <li>Format/SOSGetFeatureOfInterest.html</li>
+ <li>Format/OWSContext/v0_3_1.html</li>
+ <li>Format/OWSCommon/v1_0_0.html</li>
+ <li>Format/OWSCommon/v1_1_0.html</li>
+ <li>Format/OGCExceptionReport.html</li>
+ <li>Format/XLS/v1_1_0.html</li>
+ <li>Format/WCSGetCoverage.html</li>
+ <li>Format/XML.html</li>
+ <li>Geometry.html</li>
+ <li>Geometry/Collection.html</li>
+ <li>Geometry/Curve.html</li>
+ <li>Geometry/LinearRing.html</li>
+ <li>Geometry/LineString.html</li>
+ <li>Geometry/MultiLineString.html</li>
+ <li>Geometry/MultiPoint.html</li>
+ <li>Geometry/MultiPolygon.html</li>
+ <li>Geometry/Point.html</li>
+ <li>Geometry/Polygon.html</li>
+ <li>Handler.html</li>
+ <li>Handler/Box.html</li>
+ <li>Handler/Click.html</li>
+ <li>Handler/Drag.html</li>
+ <li>Handler/Pinch.html</li>
+ <li>Handler/Feature.html</li>
+ <li>Handler/Hover.html</li>
+ <li>Handler/Keyboard.html</li>
+ <li>Handler/MouseWheel.html</li>
+ <li>Handler/Path.html</li>
+ <li>Handler/Point.html</li>
+ <li>Handler/Polygon.html</li>
+ <li>Handler/RegularPolygon.html</li>
+ <li>Icon.html</li>
+ <li>Lang.html</li>
+ <li>Layer.html</li>
+ <li>Layer/ArcIMS.html</li>
+ <li>Layer/ArcGIS93Rest.html</li>
+ <li>Layer/ArcGISCache.html</li>
+ <li>Layer/Bing.html</li>
+ <li>Layer/EventPane.html</li>
+ <li>Layer/FixedZoomLevels.html</li>
+ <li>Layer/GeoRSS.html</li>
+ <li>Layer/Google.html</li>
+ <li>Layer/Google/v3.html</li>
+ <li>Layer/Grid.html</li>
+ <li>Layer/HTTPRequest.html</li>
+ <li>Layer/Image.html</li>
+ <li>Layer/KaMap.html</li>
+ <li>Layer/MapGuide.html</li>
+ <li>Layer/MapServer.html</li>
+ <li>Layer/Markers.html</li>
+ <li>Layer/PointGrid.html</li>
+ <li>Layer/PointTrack.html</li>
+ <li>Layer/SphericalMercator.html</li>
+ <li>Layer/Text.html</li>
+ <li>Layer/TileCache.html</li>
+ <li>Layer/TMS.html</li>
+ <li>Layer/UTFGrid.html</li>
+ <li>Layer/Vector.html</li>
+ <li>Layer/Vector/RootContainer.html</li>
+ <li>Layer/WMS.html</li>
+ <li>Layer/WMTS.html</li>
+ <li>Layer/WrapDateLine.html</li>
+ <li>Layer/XYZ.html</li>
+ <li>Layer/OSM.html</li>
+ <li>Map.html</li>
+ <li>Marker.html</li>
+ <li>Marker/Box.html</li>
+ <li>OpenLayers1.html</li>
+ <li>OpenLayers2.html</li>
+ <li>OpenLayers3.html</li>
+ <li>OpenLayers4.html</li>
+ <li>OpenLayersJsFiles.html</li>
+ <li>SingleFile1.html</html>
+ <li>SingleFile2.html</html>
+ <li>SingleFile3.html</html>
+ <li>Popup.html?visible</li>
+ <li>Popup/Anchored.html</li>
+ <li>Popup/FramedCloud.html</li>
+ <li>Projection.html</li>
+ <li>Protocol.html</li>
+ <li>Protocol/HTTP.html</li>
+ <li>Protocol/Script.html</li>
+ <li>Protocol/WFS.html</li>
+ <li>Protocol/CSW.html</li>
+ <li>Protocol/SOS.html</li>
+ <li>Renderer.html</li>
+ <li>Renderer/Canvas.html</li>
+ <li>Renderer/Elements.html</li>
+ <li>Renderer/SVG.html</li>
+ <li>Renderer/VML.html</li>
+ <li>Request.html</li>
+ <li>Request/XMLHttpRequest.html</li>
+ <li>Rule.html</li>
+ <li>Strategy.html</li>
+ <li>Strategy/BBOX.html</li>
+ <li>Strategy/Cluster.html</li>
+ <li>Strategy/Filter.html</li>
+ <li>Strategy/Fixed.html</li>
+ <li>Strategy/Paging.html</li>
+ <li>Strategy/Save.html</li>
+ <li>Strategy/Refresh.html</li>
+ <li>Style.html</li>
+ <li>Style2.html</li>
+ <li>StyleMap.html</li>
+ <li>Symbolizer.html</li>
+ <li>Symbolizer/Line.html</li>
+ <li>Symbolizer/Point.html</li>
+ <li>Symbolizer/Polygon.html</li>
+ <li>Symbolizer/Raster.html</li>
+ <li>Symbolizer/Text.html</li>
+ <li>Tile.html</li>
+ <li>Tile/Image.html</li>
+ <li>Tile/Image/IFrame.html</li>
+ <li>Tile/UTFGrid.html</li>
+ <li>TileManager.html</li>
+ <li>Tween.html</li>
+ <li>Kinetic.html</li>
+ <li>Util.html?visible</li>
+ <li>Util_w3c.html?visible</li>
+ <li>Util/vendorPrefix.html</li>
+ <li>WPSClient.html</li>
+ <li>WPSProcess.html</li>
+ <li>deprecated/Ajax.html</li>
+ <li>deprecated/Util.html</li>
+ <li>deprecated/BaseTypes/Class.html</li>
+ <li>deprecated/BaseTypes/Element.html</li>
+ <li>deprecated/Control/MouseToolbar.html</li>
+ <li>deprecated/Geometry/Rectangle.html</li>
+ <li>deprecated/Layer/GML.html</li>
+ <li>deprecated/Layer/MapServer.html</li>
+ <li>deprecated/Layer/MapServer/Untiled.html</li>
+ <li>deprecated/Layer/WFS.html</li>
+ <li>deprecated/Layer/WMS.html</li>
+ <li>deprecated/Layer/WMS/Post.html</li>
+ <li>deprecated/Popup/AnchoredBubble.html</li>
+ <li>deprecated/Protocol/SQL.html</li>
+ <li>deprecated/Protocol/SQL/Gears.html</li>
+ <li>deprecated/Renderer/SVG2.html</li>
+ <li>deprecated/Layer/Yahoo.html</li>
+ <li>deprecated/Tile/WFS.html</li>
+</ul>
diff --git a/misc/openlayers/tests/manual/ajax.html b/misc/openlayers/tests/manual/ajax.html
new file mode 100644
index 0000000..be038ad
--- /dev/null
+++ b/misc/openlayers/tests/manual/ajax.html
@@ -0,0 +1,49 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>XHR Acceptance Test</title>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var url = "ajax.txt";
+ function sendSynchronous(){
+ var request = OpenLayers.Request.GET({
+ url: url,
+ async: false,
+ callback: function() {
+ document.getElementById('send_sync').value += 'request completed\n';
+ }
+ });
+ document.getElementById('send_sync').value += 'other processing\n';
+ }
+ function sendAsynchronous(){
+ var request = OpenLayers.Request.GET({
+ url: url,
+ callback: function() {
+ document.getElementById('send_sync').value += 'request completed\n';
+ }
+ });
+ document.getElementById('send_sync').value += 'other processing\n';
+ }
+ function sendAndAbort(){
+ var request = OpenLayers.Request.GET({
+ url: url,
+ callback: function() {
+ document.getElementById('send_sync').value += 'never called\n';
+ }
+ });
+ request.abort();
+ document.getElementById('send_sync').value += 'other processing\n';
+ }
+
+ </script>
+ </head>
+ <body >
+ <button onclick="sendSynchronous()">synchronous</button>
+ expected output: "request completed" then "other processing"<br />
+ <button onclick="sendAsynchronous()">asynchronous</button>
+ expected output: "other processing" then "request completed"<br />
+ <button onclick="sendAndAbort()">send and abort</button>
+ expected output: "other processing" (and not "never called")<br />
+ <textarea id="send_sync" rows="6"></textarea><br />
+ <button onclick="document.getElementById('send_sync').value = ''">Clear</button>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/ajax.txt b/misc/openlayers/tests/manual/ajax.txt
new file mode 100644
index 0000000..b10a427
--- /dev/null
+++ b/misc/openlayers/tests/manual/ajax.txt
@@ -0,0 +1 @@
+one fake text file \ No newline at end of file
diff --git a/misc/openlayers/tests/manual/alloverlays-mixed.html b/misc/openlayers/tests/manual/alloverlays-mixed.html
new file mode 100644
index 0000000..2f8f959
--- /dev/null
+++ b/misc/openlayers/tests/manual/alloverlays-mixed.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <title>OpenLayers Mixed allOverlays Test</title>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css">
+ <link rel="stylesheet" href="../../theme/default/google.css" type="text/css">
+ <link rel="stylesheet" href="../../examples/style.css" type="text/css">
+ <script src="http://maps.google.com/maps/api/js?sensor=false&amp;v=3.6"></script>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map;
+
+ function init() {
+ map = new OpenLayers.Map('map', {allOverlays: true});
+ map.addControl(new OpenLayers.Control.LayerSwitcher());
+
+ var osm = new OpenLayers.Layer.OSM("OSM", null, {
+ visibility: false,
+ maxResolution: 78271.516953125,
+ serverResolutions: [156543.03390625, 78271.516953125, 39135.7584765625, 19567.87923828125, 9783.939619140625, 4891.9698095703125, 2445.9849047851562, 1222.9924523925781, 611.4962261962891, 305.74811309814453, 152.87405654907226, 76.43702827453613, 38.218514137268066, 19.109257068634033, 9.554628534317017, 4.777314267158508, 2.388657133579254, 1.194328566789627, 0.5971642833948135]
+ });
+ var google = new OpenLayers.Layer.Google("Google");
+ var wms = new OpenLayers.Layer.WMS("WMS",
+ "http://vmap0.tiles.osgeo.org/wms/vmap0",
+ {layers: 'basic'}, {
+ opacity: .5,
+ maxExtent: new OpenLayers.Bounds(
+ -20037508.34, -20037508.34, 20037508.34, 20037508.34
+ ),
+ wrapDateLine: true
+ }
+ );
+
+ map.addLayers([osm, google, wms]);
+
+ map.setCenter(new OpenLayers.LonLat(0, 0), 3);
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">Mixed allOverlays Test</h1>
+
+ <div id="map" class="smallmap"></div>
+
+ <div id="docs">
+ <p>
+ The map image aboved should show a Google layer and an opaque WMS
+ layer. They both should align (look at the border of West Africa)
+ </p>
+ </div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/arcims-2117.html b/misc/openlayers/tests/manual/arcims-2117.html
new file mode 100644
index 0000000..08dc4aa
--- /dev/null
+++ b/misc/openlayers/tests/manual/arcims-2117.html
@@ -0,0 +1,103 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>ArcIMS Test Ticket #2117</title>
+ <link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
+ <link rel="stylesheet" href="../../examples/style.css" type="text/css" />
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var lon = 0;
+ var lat = 0;
+ var zoom = 1;
+ var map, layer;
+
+ function init(){
+ OpenLayers.ProxyHost = "../../examples/proxy.cgi?url=";
+
+ map = new OpenLayers.Map( 'map' );
+
+ var options = {
+ serviceName: "OpenLayers_Sample",
+ async: true,
+ layers: [{
+ id:1,
+ visible:'true',
+ /*query:{
+ where: '1=1',
+ spatialfilter: true
+ },*/
+ renderer:{
+ type: 'valuemaplabel',
+ lookupfield: 'FIPS_ID',
+ labelfield: 'FIPS_CNTRY',
+ exacts:[{
+ value: '227',
+ symbol: {
+ type: 'text',
+ antialiasing: 'true',
+ interval: 6,
+ blockout: '255,255,255',
+ font: 'Arial',
+ fontcolor: '0,0,0',
+ fontsize: 11,
+ transparency: 0.7
+ }
+ },{
+ value: '150',
+ symbol: {
+ type: 'text',
+ antialiasing: 'true',
+ interval: 6,
+ blockout: '255,255,255',
+ font: 'Arial',
+ fontcolor: '0,0,0',
+ fontsize: 11,
+ transparency: 0.7
+ }
+ },{
+ value: '75',
+ symbol: {
+ type: 'text',
+ antialiasing: 'true',
+ interval: 6,
+ blockout: '255,255,255',
+ font: 'Arial',
+ fontcolor: '0,0,0',
+ fontsize: 11,
+ transparency: 0.7
+ }
+ }]
+ }
+ }]
+ };
+
+ layer = new OpenLayers.Layer.ArcIMS( "Global Sample Map",
+ "http://sample.avencia.com/servlet/com.esri.esrimap.Esrimap", options );
+ map.addLayer(layer);
+
+ map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+ map.addControl( new OpenLayers.Control.LayerSwitcher() );
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">ArcIMS Test Ticket #2117</h1>
+
+ <div id="tags">
+ </div>
+ <p id="shortdesc">
+ <a href="http://trac.openlayers.org/ticket/2117">Testing ticket #2117</a>
+ </p>
+
+ <div id="map" class="smallmap"></div>
+
+ <div id="docs">
+ This is an example of a bug in the ArcXML format writer.
+ If you don't see a map, it's broken.
+ </div>
+
+ </body>
+</html>
+
+
+
+
diff --git a/misc/openlayers/tests/manual/arkansas.rss b/misc/openlayers/tests/manual/arkansas.rss
new file mode 100644
index 0000000..926d357
--- /dev/null
+++ b/misc/openlayers/tests/manual/arkansas.rss
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:georss="http://www.georss.org/georss" version="2.0"><channel><title>topp:states</title><description>Feed auto-generated by GeoServer</description><link><![CDATA[http://localhost:8080/geoserver/wms?format_options=ENCODING:simple;&width=512&featureid=states.21&transparent=false&version=1.1.1&service=wms&srs=EPSG:4326&styles=population&height=216&bbox=-124.731422,24.955967,-66.969849,49.371735&request=GetMap&layers=topp:states&format=application/rss+xml]]></link><atom:link href="http://localhost:8080/geoserver/wms?format_options=ENCODING:simple;&amp;width=512&amp;featureid=states.21&amp;transparent=false&amp;version=1.1.1&amp;service=wms&amp;srs=EPSG:4326&amp;styles=population&amp;height=216&amp;bbox=-124.731422,24.955967,-66.969849,49.371735&amp;request=GetMap&amp;layers=topp:states&amp;format=application/rss+xml" rel="self"/><item><title>Arkansas
+</title><link><![CDATA[http://localhost:8080/geoserver/wfs?request=getfeature&service=wfs&version=1.0.0&featureid=states.21]]></link><guid><![CDATA[http://localhost:8080/geoserver/wfs?request=getfeature&service=wfs&version=1.0.0&featureid=states.21]]></guid><description><![CDATA[This is the state of Arkansas.
+2350725.0 people live in an area of 134875.075 square
+kilometers, and only 5096.0 take public transportation.
+<br>
+<br>
+
+Map by:<br> <a href="http://topp.openplans.org/geoserver"><img alt="TOPP" src="http://topp.openplans.org/images/logo.jpg"></a>
+]]></description><georss:polygon>34.19665500000001 -94.461479 34.508326999999994 -94.452408 34.735504000000006 -94.44574 34.929050000000004 -94.439102 35.400454999999994 -94.428337 35.641003 -94.468269 35.760227 -94.485718 36.106753999999995 -94.542198 36.164444 -94.552895 36.478714 -94.607231 36.489338000000004 -94.617035 36.49095199999999 -94.080849 36.489716 -93.857323 36.489891 -93.59626 36.490196 -93.328163 36.490616 -93.297142 36.489819 -92.852104 36.489918 -92.777466 36.490855999999994 -92.522888 36.491596 -92.146164 36.491371 -92.127487 36.490955 -91.688416 36.490376 -91.45285 36.491039 -91.411659 36.487953000000005 -91.133827 36.489204 -90.804314 36.490962999999994 -90.581619 36.492751999999996 -90.224373 36.491814000000005 -90.150162 36.45741700000001 -90.137276 36.453896 -90.117226 36.422565000000006 -90.123833 36.404915 -90.116829 36.39738800000001 -90.080177 36.382553 -90.052063 36.362606 -90.050201 36.325333 -90.067635 36.300472 -90.049751 36.272273999999996 -90.066093 36.257996000000006 -90.109917 36.21207 -90.131218 36.196940999999995 -90.161308 36.172565000000006 -90.219223 36.161148 -90.232224 36.137089 -90.23484 36.118763 -90.263702 36.115905999999995 -90.284752 36.091656 -90.315239 35.989586 -90.37896 35.991158 -90.283455 35.996838 -89.963203 35.999877999999995 -89.721756 35.966324 -89.713135 35.93782 -89.664192 35.913799 -89.645401 35.894287000000006 -89.649338 35.885647000000006 -89.66465 35.911427 -89.714684 35.915012000000004 -89.737976 35.896812 -89.762909 35.884102 -89.766273 35.871418000000006 -89.757713 35.842037000000005 -89.701439 35.827515000000005 -89.700829 35.807036999999994 -89.735939 35.817420999999996 -89.759796 35.805553 -89.790382 35.774223000000006 -89.799904 35.758269999999996 -89.827042 35.748192 -89.859871 35.754836999999995 -89.909782 35.734268 -89.951035 35.712486 -89.952034 35.676266 -89.929741 35.655972000000006 -89.893402 35.673306 -89.865181 35.671062000000006 -89.857246 35.645222000000004 -89.849197 35.629745 -89.863838 35.633335 -89.877441 35.603104 -89.957047 35.578593999999995 -89.958031 35.546059 -89.921661 35.52923199999999 -89.931175 35.526900999999995 -89.947548 35.532291 -89.962273 35.561676000000006 -89.989586 35.552414 -90.033051 35.542846999999995 -90.040901 35.51244 -90.041817 35.445454 -89.999565 35.417103 -90.046783 35.41341 -90.060295 35.426506 -90.073936 35.472342999999995 -90.074844 35.478207 -90.08223 35.473568 -90.101959 35.442524000000006 -90.137276 35.423716999999996 -90.172676 35.384254 -90.167816 35.383044999999996 -90.140167 35.4076 -90.132469 35.41768999999999 -90.112244 35.418282000000005 -90.085159 35.406527999999994 -90.075478 35.381508 -90.087135 35.365982 -90.105621 35.345591999999996 -90.098701 35.314685999999995 -90.106346 35.30624400000001 -90.15699 35.282566 -90.169746 35.264056999999994 -90.152122 35.263847 -90.105942 35.254397999999995 -90.090103 35.212738 -90.068962 35.191833 -90.073303 35.166916 -90.062431 35.147385 -90.064537 35.12505 -90.082924 35.13653600000001 -90.14373 35.129611999999995 -90.164474 35.10864599999999 -90.178345 35.077827 -90.169083 35.040897 -90.195709 35.048458 -90.291809 35.000693999999996 -90.305351 34.978481 -90.299507 34.94976 -90.248169 34.938903999999994 -90.241898 34.920731 -90.242844 34.896511000000004 -90.266708 34.88269 -90.296272 34.864959999999996 -90.299446 34.851776 -90.301552 34.850266000000005 -90.322823 34.860577000000006 -90.341423 34.841038 -90.403931 34.832268 -90.42231 34.835353999999995 -90.433548 34.872643 -90.427841 34.88618099999999 -90.438087 34.88092399999999 -90.470528 34.857727 -90.474716 34.82521800000001 -90.451904 34.79966400000001 -90.466705 34.76075 -90.448868 34.741198999999995 -90.451431 34.726833 -90.485924 34.729855 -90.504417 34.748371000000006 -90.516968 34.765784999999994 -90.498734 34.789833 -90.501282 34.805603000000005 -90.516045 34.807323 -90.52726 34.790336999999994 -90.547745 34.713252999999995 -90.533279 34.702068 -90.513565 34.704254000000006 -90.469978 34.672039 -90.466225 34.638065 -90.508812 34.636894 -90.538963 34.65180599999999 -90.547546 34.685947 -90.539062 34.700287 -90.561058 34.645611 -90.5793 34.627815 -90.58799 34.604744 -90.577614 34.555649 -90.530617 34.543327000000005 -90.537148 34.532509000000005 -90.565681 34.520222000000004 -90.580345 34.496506 -90.59005 34.453945000000004 -90.574402 34.432998999999995 -90.579124 34.40459799999999 -90.60379 34.36591 -90.657242 34.330006 -90.657814 34.31797400000001 -90.679337 34.320145 -90.689377 34.3634 -90.681137 34.377871999999996 -90.687485 34.37216600000001 -90.75531 34.363913999999994 -90.761856 34.317719 -90.747757 34.278976 -90.758255 34.299957000000006 -90.792526 34.299347 -90.806419 34.277339999999995 -90.823837 34.229534 -90.83136 34.219162 -90.863411 34.250195000000005 -90.928436 34.23467599999999 -90.933708 34.20483 -90.921486 34.190544 -90.822922 34.166172 -90.80751 34.148658999999995 -90.828865 34.147544999999994 -90.846611 34.185649999999995 -90.928917 34.155804 -90.953346 34.125941999999995 -90.942245 34.102749 -90.906311 34.10095200000001 -90.866333 34.040710000000004 -90.886345 34.031288 -90.95047 34.010998 -90.973366 33.994606000000005 -90.975273 33.978874000000005 -90.961128 33.967461 -90.96479 33.960815 -90.986816 33.968372 -91.000259 33.990528 -91.009544 33.985699 -91.031059 34.006096 -91.069695 33.994468999999995 -91.088852 33.974644 -91.075706 33.936306 -91.018463 33.867081 -91.061371 33.843525 -91.054817 33.816586 -91.028854 33.798897 -90.990372 33.78533899999999 -90.984039 33.77154899999999 -90.995377 33.76397299999999 -91.018433 33.769554 -91.043404 33.786525999999995 -91.066391 33.776439999999994 -91.105415 33.780086999999995 -91.136986 33.771820000000005 -91.142891 33.723225 -91.138 33.71244 -91.128944 33.70823300000001 -91.104568 33.719357 -91.056213 33.70549 -91.038826 33.683216 -91.037834 33.662586000000005 -91.083771 33.677527999999995 -91.121048 33.718315000000004 -91.1633 33.708965000000006 -91.211678 33.690723000000006 -91.215141 33.669945 -91.205307 33.637032000000005 -91.154404 33.616161000000005 -91.15065 33.57724399999999 -91.168022 33.57468 -91.187805 33.590481 -91.22673 33.55635100000001 -91.227631 33.539276 -91.213486 33.538506 -91.204102 33.52334999999999 -91.182846 33.512012 -91.180405 33.473395999999994 -91.207642 33.459453999999994 -91.22715 33.443443 -91.232849 33.447388000000004 -91.18177 33.466644 -91.17173 33.504368 -91.174179 33.51178 -91.164978 33.493190999999996 -91.128799 33.469673 -91.118958 33.452831 -91.119713 33.443123 -91.130516 33.422112 -91.19883 33.414299 -91.204163 33.39183 -91.184982 33.388878000000005 -91.137634 33.414897999999994 -91.099129 33.462856 -91.0858 33.466206 -91.073761 33.460010999999994 -91.060982 33.431797 -91.061516 33.410179 -91.07869 33.393406 -91.106941 33.359402 -91.130424 33.322384 -91.141747 33.268505000000005 -91.122505 33.249210000000005 -91.102905 33.29235799999999 -91.076324 33.293575000000004 -91.053833 33.281921 -91.040428 33.245780999999994 -91.054543 33.225697 -91.092003 33.161602 -91.086319 33.145084 -91.095856 33.131069 -91.12133 33.150288 -91.177628 33.140465000000006 -91.195503 33.113224 -91.190536 33.090652000000006 -91.14679 33.065571000000006 -91.11779 33.04727200000001 -91.123985 33.040431999999996 -91.156685 33.021709 -91.160675 33.013039000000006 -91.162132 33.013476999999995 -91.254616 33.013419999999996 -91.427528 33.013874 -91.454353 33.010025 -92.063309 33.016701 -92.717079 33.018135 -92.978828 33.019238 -93.232376 33.021393 -93.478897 33.021152 -93.511742 33.022594 -93.809753 33.023289000000005 -94.03875 33.270325 -94.036507 33.555912000000006 -94.035927 33.577213 -94.061432 33.583954000000006 -94.086655 33.572998 -94.098701 33.567085000000006 -94.155167 33.593773 -94.159515 33.58507899999999 -94.205345 33.557987 -94.210884 33.561535000000006 -94.235367 33.585719999999995 -94.223038 33.592422 -94.237236 33.561736999999994 -94.274544 33.584605999999994 -94.272079 33.589332999999996 -94.278984 33.579853 -94.29882 33.556934 -94.302383 33.57313499999999 -94.328751 33.547684000000004 -94.370758 33.560303000000005 -94.395264 33.572661999999994 -94.372307 33.590042 -94.370628 33.593327 -94.379112 33.57495900000001 -94.393417 33.573486 -94.40657 33.59714099999999 -94.428467 33.596503999999996 -94.443329 33.604347000000004 -94.451553 33.616844 -94.436333 33.636444 -94.435913 33.631966000000006 -94.476486 33.939198000000005 -94.468376 34.19665500000001 -94.461479</georss:polygon></item></channel></rss> \ No newline at end of file
diff --git a/misc/openlayers/tests/manual/big-georss.html b/misc/openlayers/tests/manual/big-georss.html
new file mode 100644
index 0000000..7e2f2b5
--- /dev/null
+++ b/misc/openlayers/tests/manual/big-georss.html
@@ -0,0 +1,33 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>OpenLayers GML Layer Example</title>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+ <link rel="stylesheet" href="../../examples/style.css" type="text/css" />
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var lon = 5;
+ var lat = 40;
+ var zoom = 5;
+ var map, layer;
+
+ function init(){
+ map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
+ map.addLayer(layer);
+ map.zoomToExtent(new OpenLayers.Bounds(-94.617035,33.010025,-89.645401,36.492752));
+ map.addLayer(new OpenLayers.Layer.Vector("arkansas", {
+ protocol: new OpenLayers.Protocol.HTTP({
+ url: "arkansas.rss",
+ format: new OpenLayers.Format.GeoRSS()
+ }),
+ strategies: [new OpenLayers.Strategy.Fixed()]
+ }));
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <p>Does this map look like arkansas?</p>
+ <div id="map" class="smallmap"></div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/box-quirks.html b/misc/openlayers/tests/manual/box-quirks.html
new file mode 100644
index 0000000..eb74bed
--- /dev/null
+++ b/misc/openlayers/tests/manual/box-quirks.html
@@ -0,0 +1,52 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Box Handler Quirks Mode Test</title>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+ <link rel="stylesheet" href="../../examples/style.css" type="text/css" />
+ <style type="text/css">
+ /* simulate quirks mode (traditional box model) in browsers other than IE */
+ div {
+ box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ }
+
+ .olHandlerBoxZoomBox {
+ border: 20px solid red;
+ border-left-width: 10px;
+ border-bottom-width: 30px;
+ }
+ </style>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map, layer;
+ function init(){
+ map = new OpenLayers.Map( 'map' );
+ layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://vmap0.tiles.osgeo.org/wms/vmap0",
+ {layers: 'basic'} );
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">Box handler Quirks Mode Test</h1>
+
+ <div id="shortdesc">Test the correct appearance of the ZoomBox in quirks mode</div>
+
+ <div id="map" class="smallmap"></div>
+
+ <div id="docs">
+ <p>For the box to be positioned correctly, we need to know the
+ width of the borders.</p>
+ <p>Shift-click on the map. A red box should be visible around the mouse
+ cursor position, with 20 pixels to the top and right, 10 pixels to
+ the left and 30 pixels to the bottom edge of the box.</p>
+ <p>Drag the box both to the top-left and the bottom-right. The cursor
+ should always be at the top-left or bottom-right inner corner of
+ the box.</p>
+ </div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/box-strict.html b/misc/openlayers/tests/manual/box-strict.html
new file mode 100644
index 0000000..5b38ea5
--- /dev/null
+++ b/misc/openlayers/tests/manual/box-strict.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+ <head>
+ <title>Box Handler Strict Mode Test</title>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+ <link rel="stylesheet" href="../../examples/style.css" type="text/css" />
+ <style type="text/css">
+ .olHandlerBoxZoomBox {
+ border: 20px solid red;
+ border-left-width: 10px;
+ border-bottom-width: 30px;
+ }
+ </style>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map, layer;
+ function init(){
+ map = new OpenLayers.Map( 'map' );
+ layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://vmap0.tiles.osgeo.org/wms/vmap0",
+ {layers: 'basic'} );
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">Box Handler Strict Mode Test</h1>
+
+ <div id="shortdesc">Test the correct appearance of the ZoomBox in strict mode</div>
+
+ <div id="map" class="smallmap"></div>
+
+ <div id="docs">
+ <p>For the box to be positioned correctly, we need to know the
+ width of the borders.</p>
+ <p>Shift-click on the map. A red box should be visible around the mouse
+ cursor position, with 20 pixels to the top and right, 10 pixels to
+ the left and 30 pixels to the bottom edge of the box.</p>
+ <p>Drag the box both to the top-left and the bottom-right. The cursor
+ should always be at the top-left or bottom-right inner corner of
+ the box.</p>
+ </div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/clip-features-svg.html b/misc/openlayers/tests/manual/clip-features-svg.html
new file mode 100644
index 0000000..f4137ea
--- /dev/null
+++ b/misc/openlayers/tests/manual/clip-features-svg.html
@@ -0,0 +1,128 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>SVG inValidRange Test Case</title><link
+href="../../theme/default/style.css"
+rel="stylesheet" type="text/css">
+<style>
+ #map {
+ width: 512px;
+ height: 512px;
+ border: 1px solid #4B3624;
+ background: White;
+ }
+
+ /* avoid pink tiles */
+ .olImageLoadError {
+ background-color: transparent !important;
+ }
+
+ .olControlAttribution { bottom: 0px!important }
+</style>
+<script src="../../lib/OpenLayers.js"
+type="text/javascript"></script>
+<script type="text/javascript">var map;
+
+ // increase reload attempts
+ OpenLayers.IMAGE_RELOAD_ATTEMPTS = 3;
+
+ var vectorLayer;
+ var markerLayer, boxes, newPoint;
+
+ function init(){
+ var options = {
+ projection: new OpenLayers.Projection("EPSG:900913"),
+ units: "m",
+ numZoomLevels: 19,
+ maxResolution: 156543.0339,
+ maxExtent: new OpenLayers.Bounds(-20037508, -20037508,
+ 20037508, 20037508.34)
+ };
+ map = new OpenLayers.Map('map', options);
+
+ map.addControl(new OpenLayers.Control.MousePosition());
+
+ vectorLayer = new OpenLayers.Layer.Vector("Trails", {isBaseLayer: true});
+ markerLayer = new OpenLayers.Layer.Markers("WayPoints");
+
+ map.addLayers([vectorLayer,markerLayer]);
+
+ var style_trail = OpenLayers.Util.extend({},
+ OpenLayers.Feature.Vector.style['default']);
+ style_trail.strokeColor = "green";
+ style_trail.strokeWidth = 5;
+
+ var pointList = [];
+
+ newPoint = new OpenLayers.Geometry.Point(-13653735.8487833,5726045.3578081);
+ pointList.push(newPoint);
+ newPoint = new OpenLayers.Geometry.Point(-13653731.3960036,5726056.5070679);
+ pointList.push(newPoint);
+ newPoint = new OpenLayers.Geometry.Point(-13653730.8394062,5726044.7207079);
+ pointList.push(newPoint);
+ newPoint = new OpenLayers.Geometry.Point(-13653743.1958697,5726043.9243328);
+ pointList.push(newPoint);
+ newPoint = new OpenLayers.Geometry.Point(-13653754.1051798,5726046.9505586);
+ pointList.push(newPoint);
+ newPoint = new OpenLayers.Geometry.Point(-13653760.4503907,5726056.5070679);
+ pointList.push(newPoint);
+ newPoint = new OpenLayers.Geometry.Point(-13653767.4635187,5726065.5857612);
+ pointList.push(newPoint);
+ newPoint = new OpenLayers.Geometry.Point(-13653830.136392,5726052.2066375);
+ pointList.push(newPoint);
+ newPoint = new OpenLayers.Geometry.Point(-13653846.5003571,5726042.3315828);
+ pointList.push(newPoint);
+
+ var lineFeature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LineString(pointList));
+ lineFeature.fid = 52730;
+ vectorLayer.addFeatures(lineFeature);
+
+ pointList = [];
+
+ newPoint = new OpenLayers.Geometry.Point(-12250153.3626406,4852001.6114048);
+ pointList.push(newPoint);
+ newPoint = new OpenLayers.Geometry.Point(-12194315.5060664,4800503.5113048);
+ pointList.push(newPoint);
+ newPoint = new OpenLayers.Geometry.Point(-12180445.0975155,4873109.008858);
+ pointList.push(newPoint);
+
+ lineFeature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LineString(pointList),null,style_trail);
+ lineFeature.fid = 52751;
+ vectorLayer.addFeatures([lineFeature]);
+
+ var size = new OpenLayers.Size(15, 15);
+ var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
+ var icon = new OpenLayers.Icon('../../img/marker.png', size, offset);
+ markerLayer.addMarker(new OpenLayers.Marker(
+ new OpenLayers.LonLat((newPoint.x + 400), (newPoint.y - 400)), icon));
+
+ map.setCenter(new OpenLayers.LonLat(newPoint.x, newPoint.y), 13)
+ }
+
+ function zoomToScale(zoom) {
+ if (zoom == 8) map.zoomToScale(3385.5001275);
+ else if(zoom == 7) map.zoomToScale(6771.000255);
+ else if (zoom == 6) map.zoomToScale(13542);
+ else if (zoom == 5) map.zoomToScale(27084.001020);
+ else if (zoom == 4) map.zoomToScale(54168.001020);
+ else if (zoom == 3) map.zoomToScale(108337);
+ else if (zoom == 2) map.zoomToScale(3466752.1306573446);
+ else if (zoom == 1) map.zoomToScale(13867008.522629378);
+ else if (zoom == 0) map.zoomToScale(55468034.09051751);
+ }
+
+</script>
+</head>
+<body onLoad="init()">
+<h1 id="title">SVG inValidRange Clipping Test Case</h1>
+<p>Behavior before fixing #1631: Push Zoom 5. You see lines. Push
+Zoom 6. No lines.</p>
+ <div id="map">
+ </div>
+ <button onClick="zoomToScale(5);">Zoom 5</button>
+ <button onClick="zoomToScale(6);">Zoom 6</button>
+ <button onClick="zoomToScale(7);">Zoom 7</button>
+ <button onClick="zoomToScale(8);">Zoom 8</button>
+</body>
+</html>
diff --git a/misc/openlayers/tests/manual/dateline-sketch.html b/misc/openlayers/tests/manual/dateline-sketch.html
new file mode 100644
index 0000000..1be1f36
--- /dev/null
+++ b/misc/openlayers/tests/manual/dateline-sketch.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <title>OpenLayers: Sketch handlers crossing the dateline</title>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css">
+ <link rel="stylesheet" href="../../examples/style.css" type="text/css">
+ <style type="text/css">
+ #map {
+ height: 512px;
+ }
+ </style>
+
+ <script src="../../lib/OpenLayers.js"></script>
+ </head>
+ <body>
+ <h1 id="title">OpenLayers sketch handlers crossing the dateline example</h1>
+
+ <div id="tags">
+ international date line, dateline, sketch
+ </div>
+ <p id="shortdesc">
+ Start digitizing a polygon or line
+ on one side of the international dateline, and then cross the dateline
+ whilst digitizing. The feature should behave like digitizing on any
+ other location.
+ </p>
+ <div id="map" class="smallmap"></div>
+
+ <div id="docs">
+ </div>
+ <script type="text/javascript">
+
+ var map = new OpenLayers.Map('map');
+
+ var base = new OpenLayers.Layer.WMS("marble",
+ "http://demo.opengeo.org/geoserver/wms",
+ {layers: "topp:naturalearth"},
+ {wrapDateLine: true}
+ );
+
+ // allow testing of specific renderers via "?renderer=Canvas", etc
+ var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
+ renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
+
+ var vector = new OpenLayers.Layer.Vector("Editable Vectors", {renderers: renderer});
+
+ map.addLayers([base, vector]);
+
+ var wkt = new OpenLayers.Format.WKT();
+ var f = wkt.read("POLYGON((210.8828125 39.7265625,151.8203125 36.2109375,152.171875 -9.4921875,219.3203125 -10.546875,210.8828125 39.7265625))");
+
+ var f2 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(-190, 0));
+
+ vector.addFeatures([f, f2]);
+
+ map.addControl(new OpenLayers.Control.EditingToolbar(vector));
+
+ map.setCenter(new OpenLayers.LonLat(-179, 0), 2);
+
+ </script>
+
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/dateline-smallextent.html b/misc/openlayers/tests/manual/dateline-smallextent.html
new file mode 100644
index 0000000..1d05e84
--- /dev/null
+++ b/misc/openlayers/tests/manual/dateline-smallextent.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <title>OpenLayers: Overlay layer extents crossing the dateline</title>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css">
+ <link rel="stylesheet" href="../../examples/style.css" type="text/css">
+ <style type="text/css">
+ #map {
+ height: 512px;
+ }
+ </style>
+
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+
+// make map available for easy debugging
+var map;
+
+function init(){
+ map = new OpenLayers.Map('map');
+
+ var base = new OpenLayers.Layer.WMS("marble",
+ "http://demo.opengeo.org/geoserver/wms",
+ {layers: "topp:naturalearth"},
+ {wrapDateLine: true}
+ );
+ var extent = new OpenLayers.Bounds(142.3828125,-70.902270266175,233.6171875,-12.039326531729);
+ var wms = new OpenLayers.Layer.WMS( "world",
+ "http://demo.opengeo.org/geoserver/wms",
+ {layers: 'world', transparent: true},
+ {maxExtent: extent}
+ );
+
+ var vector = new OpenLayers.Layer.Vector();
+ vector.addFeatures([
+ new OpenLayers.Feature.Vector(extent.toGeometry())
+ ]);
+
+ map.addLayers([base, wms, vector]);
+
+ map.addControl(new OpenLayers.Control.LayerSwitcher());
+ map.zoomToExtent(extent);
+}
+
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">OpenLayers overlays crossing the dateline test</h1>
+
+ <p id="shortdesc">
+ The overlay has an extent smaller than the world extent, but exceeds
+ the world extent. The base layer is configured with wrapDateLine set to
+ true. The area inside the orange rectangle should always contain tiles
+ from the world layer, regardless of the zoom level.
+ </p>
+ <div id="map" class="smallmap"></div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/draw-feature.html b/misc/openlayers/tests/manual/draw-feature.html
new file mode 100644
index 0000000..8872b63
--- /dev/null
+++ b/misc/openlayers/tests/manual/draw-feature.html
@@ -0,0 +1,73 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Draw Feature Acceptance Test</title>
+ <style type="text/css">
+
+ body {
+ font-size: 0.8em;
+ }
+ p {
+ padding-top: 1em;
+ }
+
+ .map {
+ margin: 1em;
+ float: left;
+ width: 256px;
+ height: 256px;
+ }
+
+ </style>
+
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+
+ var map1, map2;
+
+ function init(){
+ var wms, vector, ctrl;
+ var goodMaxRes = OpenLayers.Map.prototype.maxResolution;
+ var badMaxRes = 0.00000001;
+
+ map1 = new OpenLayers.Map('map1');
+ wms = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'});
+ vector = new OpenLayers.Layer.Vector("vector1");
+ map1.addLayers([wms, vector]);
+ ctrl = new OpenLayers.Control.DrawFeature(vector,
+ OpenLayers.Handler.Path);
+ map1.addControl(ctrl);
+ ctrl.activate();
+
+ map2 = new OpenLayers.Map('map2',
+ {maxResolution: badMaxRes});
+ wms = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'},
+ {maxResolution: goodMaxRes});
+ vector = new OpenLayers.Layer.Vector("vector2",
+ {maxResolution: goodMaxRes});
+ map2.addLayers([wms, vector]);
+ ctrl = new OpenLayers.Control.DrawFeature(vector,
+ OpenLayers.Handler.Path);
+ map2.addControl(ctrl);
+ ctrl.activate();
+
+ map1.setCenter(new OpenLayers.LonLat(0, 0), 3);
+ map2.setCenter(new OpenLayers.LonLat(0, 0), 3);
+ }
+
+ </script>
+ </head>
+ <body onload="init()">
+ <div id="map1" class="map"></div>
+ <p><b>Resolution properties set at the map level.</b></p>
+ <p>Points should draw as you draw lines. Click to start
+ drawing and double-click to draw the last point.</p>
+ <br style="clear: both;" />
+
+ <div id="map2" class="map"></div>
+ <p><b>Resolution properties set at the layer level.</b></p>
+ <p>Points should draw as you draw lines. Click to start
+ drawing and double-click to draw the last point.</p>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/feature-handler.html b/misc/openlayers/tests/manual/feature-handler.html
new file mode 100644
index 0000000..f9b8892
--- /dev/null
+++ b/misc/openlayers/tests/manual/feature-handler.html
@@ -0,0 +1,126 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Feature Handler Acceptance Test</title>
+ <style type="text/css">
+
+ body {
+ font-size: 0.8em;
+ }
+ p {
+ padding-top: 1em;
+ }
+ li {
+ list-style: none;
+ }
+ #output {
+ width: 300px;
+ height: 300px;
+ }
+ #west {
+ width: 425px;
+ }
+
+ #east {
+ position: absolute;
+ left: 450px;
+ top: 5px;
+ }
+ #map {
+ width: 400px;
+ height: 400px;
+ border: 1px solid gray;
+ }
+
+ </style>
+
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+
+ var map, draw, handler, controls;
+ OpenLayers.Feature.Vector.style['default']['strokeWidth'] = '2';
+
+ function init(){
+ map = new OpenLayers.Map('map');
+
+ var vectors = new OpenLayers.Layer.Vector(
+ "Vector Layer",
+ {isBaseLayer: true}
+ );
+ map.addLayer(vectors);
+
+
+ draw = new OpenLayers.Control.DrawFeature(
+ vectors, OpenLayers.Handler.Polygon
+ );
+ map.addControl(draw);
+
+ var callbacks = {
+ "over": function(feature) {
+ log("over " + feature.id);
+ },
+ "out": function(feature) {
+ log("out " + feature.id);
+ },
+ "click": function(feature) {
+ log("click " + feature.id);
+ },
+ "dblclick": function(feature) {
+ log("dblclick " + feature.id);
+ },
+ "clickout": function(feature) {
+ log("clickout " + feature.id);
+ }
+ };
+
+ handler = new OpenLayers.Handler.Feature(
+ {map: map}, vectors, callbacks
+ );
+
+ map.setCenter(new OpenLayers.LonLat(0, 0), 3);
+
+ }
+
+ function log(msg) {
+ document.getElementById('output').value += msg + "\n";
+ }
+
+ function clearLog() {
+ document.getElementById('output').value = "";
+ }
+
+ </script>
+ </head>
+ <body onload="init()">
+ <div id="west">
+ <div id="map"></div>
+ <p>
+ Draw a few polygons on the map. Some overlapping. Activate the
+ feature handler and ensure that "over" and "out" are called only
+ when mousing over/out of a feature for the first time. The
+ "click" callback should be called for every click on a feature.
+ The "clickout" callback should be called when?
+ </p>
+ </div>
+ <div id="east">
+ <ul>
+ <li>
+ <input type="radio" name="type" value="none" id="noneToggle"
+ onclick="draw.deactivate();handler.deactivate();" checked="checked" />
+ <label for="noneToggle">navigate</label>
+ </li>
+ <li>
+ <input type="radio" name="type" value="polygon" id="polygonToggle"
+ onclick="draw.activate();handler.deactivate();" />
+ <label for="polygonToggle">draw polygon</label>
+ </li>
+ <li>
+ <input type="radio" name="type" value="feature" id="featureToggle"
+ onclick="draw.deactivate();handler.activate();" />
+ <label for="featureToggle">activate feature handler</label>
+ </li>
+ </ul>
+ <button onclick="clearLog();">clear log</button><br />
+ <textarea id="output"></textarea>
+ </div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/geodesic.html b/misc/openlayers/tests/manual/geodesic.html
new file mode 100644
index 0000000..e642558
--- /dev/null
+++ b/misc/openlayers/tests/manual/geodesic.html
@@ -0,0 +1,160 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+ <style type="text/css">
+ #controlToggle li {
+ list-style: none;
+ }
+ #options {
+ position: relative;
+ width: 512px;
+ }
+ #output {
+ float: right;
+ }
+
+ /* avoid pink tiles */
+ .olImageLoadError {
+ background-color: transparent !important;
+ }
+ </style>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map, measureControls;
+ function init(){
+ map = new OpenLayers.Map('map');
+
+ var wmsLayer = new OpenLayers.Layer.OSM();
+
+ map.addLayers([wmsLayer]);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+ map.addControl(new OpenLayers.Control.LayerSwitcher());
+ map.addControl(new OpenLayers.Control.MousePosition());
+ map.addControl(new OpenLayers.Control.ScaleLine({geodesic: true}))
+
+ // style the sketch fancy
+ var sketchSymbolizers = {
+ "Point": {
+ pointRadius: 4,
+ graphicName: "square",
+ fillColor: "white",
+ fillOpacity: 1,
+ strokeWidth: 1,
+ strokeOpacity: 1,
+ strokeColor: "#333333"
+ },
+ "Line": {
+ strokeWidth: 3,
+ strokeOpacity: 1,
+ strokeColor: "#666666",
+ strokeDashstyle: "dash"
+ },
+ "Polygon": {
+ strokeWidth: 2,
+ strokeOpacity: 1,
+ strokeColor: "#666666",
+ fillColor: "white",
+ fillOpacity: 0.3
+ }
+ };
+ var style = new OpenLayers.Style();
+ style.addRules([
+ new OpenLayers.Rule({symbolizer: sketchSymbolizers})
+ ]);
+ var styleMap = new OpenLayers.StyleMap({"default": style});
+
+ measureControls = {
+ line: new OpenLayers.Control.Measure(
+ OpenLayers.Handler.Path, {
+ geodesic: true,
+ persist: true,
+ handlerOptions: {
+ layerOptions: {styleMap: styleMap}
+ }
+ }
+ ),
+ polygon: new OpenLayers.Control.Measure(
+ OpenLayers.Handler.Polygon, {
+ geodesic: true,
+ persist: true,
+ handlerOptions: {
+ layerOptions: {styleMap: styleMap}
+ }
+ }
+ )
+ };
+
+ var control;
+ for(var key in measureControls) {
+ control = measureControls[key];
+ control.events.on({
+ "measure": handleMeasurements,
+ "measurepartial": handleMeasurements
+ });
+ map.addControl(control);
+ }
+
+ map.setCenter(new OpenLayers.LonLat(0, 0), 3);
+
+ document.getElementById('noneToggle').checked = true;
+ }
+
+ function handleMeasurements(event) {
+ var geometry = event.geometry;
+ var units = event.units;
+ var order = event.order;
+ var measure = event.measure;
+ var element = document.getElementById('output');
+ var out = "";
+ if(order == 1) {
+ out += "measure: " + measure.toFixed(3) + " " + units;
+ } else {
+ out += "measure: " + measure.toFixed(3) + " " + units + "<sup>2</" + "sup>";
+ }
+ element.innerHTML = out;
+ }
+
+ function toggleControl(element) {
+ for(key in measureControls) {
+ var control = measureControls[key];
+ if(element.value == key && element.checked) {
+ control.activate();
+ } else {
+ control.deactivate();
+ }
+ }
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">OpenLayers Geodesic Measurement & ScaleLine</h1>
+ <p id="shortdesc">
+ Tests geodesic measurement of distances and areas against a geodesic ScaleLine.
+ </p>
+ <div id="map" style="width: 512px; height: 300px;"></div>
+ <div id="options">
+ <div id="output">
+ </div>
+ <ul id="controlToggle">
+ <li>
+ <input type="radio" name="type" value="none" id="noneToggle"
+ onclick="toggleControl(this);" checked="checked" />
+ <label for="noneToggle">navigate</label>
+ </li>
+ <li>
+ <input type="radio" name="type" value="line" id="lineToggle" onclick="toggleControl(this);" />
+ <label for="lineToggle">measure distance</label>
+ </li>
+ <li>
+ <input type="radio" name="type" value="polygon" id="polygonToggle" onclick="toggleControl(this);" />
+ <label for="polygonToggle">measure area</label>
+ </li>
+ </ul>
+ </div>
+ <p>Zoom in so the ScaleLine shows units in the range of 10-100 km. Measure
+ the length of the ScaleLine. The result should be approximately the same
+ as the distance printed on the ScaleLine.</p>
+ <p>Zoom out so the ScaleLine shows units in the range of 100-500 km. Drag
+ the map to the South or North and see how the ScaleLine length changes.</p>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/geojson-geomcoll-reprojection.html b/misc/openlayers/tests/manual/geojson-geomcoll-reprojection.html
new file mode 100644
index 0000000..e82e08a
--- /dev/null
+++ b/misc/openlayers/tests/manual/geojson-geomcoll-reprojection.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head>
+ <style type="text/css" media="screen">
+ #map { height: 500px; }
+ </style>
+ <script src="../../lib/OpenLayers.js" type="text/javascript" charset="utf-8"></script>
+ <script src="http://www.openstreetmap.org/openlayers/OpenStreetMap.js" type="text/javascript" charset="utf-8"></script>
+ <script type="text/javascript" charset="utf-8">
+ function init(){
+ var map = new OpenLayers.Map ("map", {
+ controls: [
+ new OpenLayers.Control.Navigation(),
+ new OpenLayers.Control.Attribution()
+ ],
+ maxExtent: new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34),
+ maxResolution: 156543.0399,
+ numZoomLevels: 19,
+ units: 'm',
+ projection: new OpenLayers.Projection("EPSG:900913"),
+ displayProjection: new OpenLayers.Projection("EPSG:4326")
+ });
+
+ var osm = new OpenLayers.Layer.OSM.Mapnik('OSM');
+ map.addLayer(osm);
+ var lonLat = new OpenLayers.LonLat(5, 40).transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject());
+ map.setCenter (lonLat, 5);
+
+ var featurecollection = {
+ "type": "FeatureCollection",
+ "features": [{
+ "geometry": {
+ "type": "GeometryCollection",
+ "geometries": [
+ {
+ "type": "LineString",
+ "coordinates":
+ [[11.0878902207, 45.1602390564],
+ [15.01953125, 48.1298828125]]
+ },
+ {
+ "type": "Polygon",
+ "coordinates":
+ [[[11.0878902207, 45.1602390564],
+ [14.931640625, 40.9228515625],
+ [0.8251953125, 41.0986328125],
+ [7.63671875, 48.96484375],
+ [11.0878902207, 45.1602390564]]]
+ },
+ {
+ "type":"Point",
+ "coordinates":[15.87646484375, 44.1748046875]
+ }
+ ]
+ },
+ "type": "Feature",
+ "properties": {}
+ }]
+ };
+ var geojson_format = new OpenLayers.Format.GeoJSON({
+ 'internalProjection': new OpenLayers.Projection("EPSG:900913"),
+ 'externalProjection': new OpenLayers.Projection("EPSG:4326")
+ });
+ var vector_layer = new OpenLayers.Layer.Vector();
+ map.addLayer(vector_layer);
+ vector_layer.addFeatures(geojson_format.read(featurecollection));
+ };
+ </script>
+ </head>
+ <body onload="init()">
+ <div id="map"></div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/google-fullscreen-overlay.html b/misc/openlayers/tests/manual/google-fullscreen-overlay.html
new file mode 100644
index 0000000..80a8fd4
--- /dev/null
+++ b/misc/openlayers/tests/manual/google-fullscreen-overlay.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Google v3 with Overlay Test</title>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+ <link rel="stylesheet" href="../../examples/style.css" type="text/css">
+ <style type="text/css">
+ html, body, #map {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ }
+ #text {
+ position: absolute;
+ top: 1em;
+ right: 1em;
+ width: 512px;
+ z-index: 20000;
+ background-color: white;
+ padding: 0 0.5em 0.5em 0.5em;
+ }
+ </style>
+ <script src="http://maps.google.com/maps/api/js?v=3.6&amp;sensor=false"></script>
+ <script src="../../lib/OpenLayers.js"></script>
+ </head>
+ <body>
+ <div id="map"></div>
+ <div id="text">
+ <h1 id="title">Google v3 with Overlay Test</h1>
+
+ <div id="docs">
+ <p>This test shows that the Google layer and overlays are not in sync while dragging or zooming.</p>
+ </div>
+ </div>
+ <script type="text/javascript">
+ var options = {
+ projection: new OpenLayers.Projection("EPSG:900913"),
+ units: "m",
+ maxResolution: 156543.0339,
+ maxExtent: new OpenLayers.Bounds(-20037508, -20037508, 20037508, 20037508)
+ };
+ var map = new OpenLayers.Map('map', options);
+ var gmap = new OpenLayers.Layer.Google(
+ "Google Streets", {sphericalMercator: true}
+ );
+ var states = new OpenLayers.Layer.WMS(
+ "USA States", "http://demo.opengeo.org/geoserver/wms",
+ {layers: "topp:states", transparent: true}
+ );
+ map.addLayers([gmap, states]);
+ map.setCenter(new OpenLayers.LonLat(-10028537.429619, 4598451.0222853), 5);
+ </script>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/google-panning.html b/misc/openlayers/tests/manual/google-panning.html
new file mode 100644
index 0000000..0ccdaf2
--- /dev/null
+++ b/misc/openlayers/tests/manual/google-panning.html
@@ -0,0 +1,122 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Google Panning Acceptance Test</title>
+ <style type="text/css">
+
+ body {
+ font-size: 0.8em;
+ }
+ p {
+ padding-top: 1em;
+ }
+
+ #evenmap {
+ margin: 1em;
+ float: left;
+ width: 256px;
+ height: 256px;
+ }
+
+ #oddmap {
+ margin: 1em;
+ float: left;
+ width: 255px;
+ height: 255px;
+ }
+
+ /* avoid pink tiles */
+ .olImageLoadError {
+ background-color: transparent !important;
+ }
+
+ </style>
+
+ <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhR_wWLPFku8Ix9i2SXYRVK3e45q1BQUd_beF8dtzKET_EteAjPdGDwqpQ'></script>
+
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+
+ var evenmap, oddmap;
+
+ // increase reload attempts
+ OpenLayers.IMAGE_RELOAD_ATTEMPTS = 3;
+
+ function init(){
+ evenmap = new OpenLayers.Map('evenmap');
+ var evenlayer = new OpenLayers.Layer.Google(
+ "Imagery",
+ {type: G_SATELLITE_MAP}
+ );
+ evenmap.addLayer(evenlayer);
+ var epc = document.getElementById("epc");
+ var emc = document.getElementById("emc");
+ var ee = document.getElementById("ee");
+ evenmap.events.register("moveend", null, function() {
+ var px = new OpenLayers.Pixel(evenmap.size.w / 2,
+ evenmap.size.h / 2);
+ var pc = evenmap.getLonLatFromViewPortPx(px);
+ pc.lon = parseFloat(pc.lon.toPrecision(6));
+ pc.lat = parseFloat(pc.lat.toPrecision(6));
+ var mc = evenmap.baseLayer.getOLLonLatFromMapObjectLonLat(
+ evenmap.baseLayer.mapObject.getCenter()
+ );
+ mc.lon = parseFloat(mc.lon.toPrecision(6));
+ mc.lat = parseFloat(mc.lat.toPrecision(6));
+ epc.innerHTML = "(" + pc.lon + ", " + pc.lat + ")";
+ emc.innerHTML = "(" + mc.lon + ", " + mc.lat + ")";
+ ee.innerHTML = pc.equals(mc);
+ });
+ evenmap.zoomToMaxExtent();
+
+ oddmap = new OpenLayers.Map('oddmap');
+ var oddlayer = new OpenLayers.Layer.Google(
+ "Imagery",
+ {type: G_SATELLITE_MAP}
+ );
+ oddmap.addLayer(oddlayer);
+ var opc = document.getElementById("opc");
+ var omc = document.getElementById("omc");
+ var oe = document.getElementById("oe");
+ oddmap.events.register("moveend", null, function() {
+ var px = new OpenLayers.Pixel(oddmap.size.w / 2,
+ oddmap.size.h / 2);
+ var pc = oddmap.getLonLatFromViewPortPx(px);
+ pc.lon = parseFloat(pc.lon.toPrecision(6));
+ pc.lat = parseFloat(pc.lat.toPrecision(6));
+ var mc = oddmap.baseLayer.getOLLonLatFromMapObjectLonLat(
+ oddmap.baseLayer.mapObject.getCenter()
+ );
+ mc.lon = parseFloat(mc.lon.toPrecision(6));
+ mc.lat = parseFloat(mc.lat.toPrecision(6));
+ opc.innerHTML = "(" + pc.lon + ", " + pc.lat + ")";
+ omc.innerHTML = "(" + mc.lon + ", " + mc.lat + ")";
+ oe.innerHTML = pc.equals(mc);
+ });
+ oddmap.zoomToMaxExtent();
+
+ }
+
+ </script>
+ </head>
+ <body onload="init()">
+ <div id="evenmap"></div>
+ <p><b>Even sized map.</b> The map on the left should pan regularly, and the
+ two centers below should be equivalent. Both dragging and panning with
+ buttons should maintain the same center.</p>
+ <p><b>pixel center:</b> <span id="epc"></span>
+ <br /><b>map center:</b> <span id="emc"></span>
+ <br /><b>equvalent:</b> <span id="ee"></span>
+ </p>
+ <br style="clear: both;" />
+
+ <div id="oddmap"></div>
+ <p><b>Odd sized map.</b> The map on the left should pan regularly, and the
+ two centers below should be equivalent. Both dragging and panning with
+ buttons should maintain the same center.</p>
+ <p><b>pixel center:</b> <span id="opc"></span>
+ <br /><b>map center:</b> <span id="omc"></span>
+ <br /><b>equvalent:</b> <span id="oe"></span>
+ </p>
+ </ul>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/google-resize.html b/misc/openlayers/tests/manual/google-resize.html
new file mode 100644
index 0000000..c1384df
--- /dev/null
+++ b/misc/openlayers/tests/manual/google-resize.html
@@ -0,0 +1,55 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>OpenLayers Google Layer Example</title>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+ <link rel="stylesheet" href="../../examples/style.css" type="text/css" />
+ <!-- this gmaps key generated for http://openlayers.org/dev/ -->
+ <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhR_wWLPFku8Ix9i2SXYRVK3e45q1BQUd_beF8dtzKET_EteAjPdGDwqpQ'></script>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map;
+
+ function init() {
+ var mapOptions = {
+ projection: "EPSG:900913",
+ displayProjection: new OpenLayers.Projection("EPSG:4326"), //Pour afficher les coord lat long
+ units: "m",
+ maxResolution: 156543.0339,
+ maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34),
+ controls: [new OpenLayers.Control.LayerSwitcher()],
+ numZoomLevels: 20
+ };
+ map = new OpenLayers.Map('map', mapOptions);
+
+ var dummy = new OpenLayers.Layer(
+ "Dummy",
+ {isBaseLayer: true}
+ );
+ var gmap = new OpenLayers.Layer.Google(
+ "Google Streets", {sphericalMercator: true}
+ );
+
+ map.addLayers([dummy, gmap]);
+
+ map.setCenter(new OpenLayers.LonLat(-7712190.388467473, 6567469.498697457), 6);
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">Google Layer Resize Issue</h1>
+
+ <div id="tags"></div>
+
+ <p id="shortdesc">
+ <ol>
+ <li>Click
+ <button onclick="var m = document.getElementById('map').style; m.height = '400px'; m.width = '800px'; map.updateSize(); return false;">Resize</button></li>
+ <li>Open the LayerSwitcher and switch to Google Streets</li>
+ <li>Confirm that the whole map area is populated with tiles</li>
+ </ol>
+ </p>
+
+ <div id="map" style="width: 200px; height: 200px"></div>
+
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/google-tilt.html b/misc/openlayers/tests/manual/google-tilt.html
new file mode 100644
index 0000000..d0b2ed6
--- /dev/null
+++ b/misc/openlayers/tests/manual/google-tilt.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <title>OpenLayers Google (v3) Layer Example</title>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css">
+ <link rel="stylesheet" href="../../examples/style.css" type="text/css">
+ <script src="http://maps.google.com/maps/api/js?v=3.6&amp;sensor=false"></script>
+ <script src="../../lib/OpenLayers.js"></script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">Google (v3) Unexpected Tilt Test</h1>
+ <div id="map" class="smallmap"></div>
+ <div id="docs">
+ <p>
+ OpenLayers uses the disableDefaultUI option of the GMaps API.
+ Despite that, the tilt feature is active. To see it, zoom in
+ once and see the buildings from a 45° angle instead of from the
+ top as you would expect from aerial imagery.
+ </p>
+ </div>
+ <script>
+ var map = new OpenLayers.Map('map');
+
+ var ghyb = new OpenLayers.Layer.Google(
+ "Google Hybrid",
+ {type: google.maps.MapTypeId.HYBRID, numZoomLevels: 20}
+ );
+
+ map.addLayers([ghyb]);
+
+ map.setCenter(new OpenLayers.LonLat(-13635213, 4544641), 17);
+ </script>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/google-v3-resize.html b/misc/openlayers/tests/manual/google-v3-resize.html
new file mode 100644
index 0000000..6949ddc
--- /dev/null
+++ b/misc/openlayers/tests/manual/google-v3-resize.html
@@ -0,0 +1,54 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Google v3 Resize Test</title>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+ <link rel="stylesheet" href="../../examples/style.css" type="text/css" />
+ <script src="http://maps.google.com/maps/api/js?sensor=false&amp;v=3.6"></script>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map;
+
+ function init() {
+ var mapOptions = {
+ projection: "EPSG:900913",
+ displayProjection: new OpenLayers.Projection("EPSG:4326"), //Pour afficher les coord lat long
+ units: "m",
+ maxResolution: 156543.0339,
+ maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34),
+ controls: [new OpenLayers.Control.Navigation(), new OpenLayers.Control.LayerSwitcher()],
+ numZoomLevels: 20
+ };
+ map = new OpenLayers.Map('map', mapOptions);
+
+ var dummy = new OpenLayers.Layer(
+ "Dummy",
+ {isBaseLayer: true}
+ );
+ var gmap = new OpenLayers.Layer.Google(
+ "Google Streets", {sphericalMercator: true}
+ );
+
+ map.addLayers([dummy, gmap]);
+
+ map.setCenter(new OpenLayers.LonLat(-7712190.388467473, 6567469.498697457), 6);
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">Google Layer Resize Issue</h1>
+
+ <div id="tags"></div>
+
+ <p id="shortdesc">
+ <ol>
+ <li>Click
+ <button onclick="var m = document.getElementById('map').style; m.height = '400px'; m.width = '800px';map.updateSize(); return false;">Resize</button></li>
+ <li>Open the LayerSwitcher and switch to Google Streets</li>
+ <li>Confirm that the whole map area is populated with tiles</li>
+ </ol>
+ </p>
+
+ <div id="map" style="width: 350px; height: 200px"></div>
+
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/loadend.html b/misc/openlayers/tests/manual/loadend.html
new file mode 100644
index 0000000..0536b75
--- /dev/null
+++ b/misc/openlayers/tests/manual/loadend.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css">
+ <link rel="stylesheet" href="../../examples/style.css" type="text/css">
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var lon = 5;
+ var lat = 40;
+ var zoom = 5;
+ var map, layer;
+
+ var numLoadingLayers = 0;
+
+ function init(){
+ map = new OpenLayers.Map( 'map' );
+ layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://vmap0.tiles.osgeo.org/wms/vmap0", {layers: 'basic'});
+
+ layer.events.register('loadstart', this, onloadstart);
+ layer.events.register('loadend', this, onloadend);
+
+ map.addLayer(layer);
+
+ map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+ }
+
+ function log(msg) {
+ document.getElementById("output").innerHTML += msg + "<br />";
+ }
+
+ function onloadstart(evt) {
+ numLoadingLayers++;
+ var msg = ['loadstart', '# layers loading:', numLoadingLayers].join(' ');
+ log (msg);
+ };
+
+ function onloadend(evt) {
+ numLoadingLayers--;
+ var msg = ['loadend ', '# layers loading:', numLoadingLayers].join(' ');
+ log (msg);
+ };
+
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">WMS loadstart/loadend events</h1>
+
+ <div id="tags">
+ wms, layer, singletile
+ </div>
+ <p id="shortdesc">
+ Shows the loadstart and loadend events of a WMS layer
+ </p>
+
+ <div id="map" class="smallmap"></div>
+
+ <div id="docs">
+ <p>
+ This example is helpful in testing whether all loadstart events are followed
+ by a loadend event.
+ Test by using scroll-wheel up and down.
+ </p>
+ </div>
+
+ <h1>loadstart and loadend events</h1>
+ <pre id="output"></pre>
+
+ </body>
+</html> \ No newline at end of file
diff --git a/misc/openlayers/tests/manual/map-events.html b/misc/openlayers/tests/manual/map-events.html
new file mode 100644
index 0000000..3695e82
--- /dev/null
+++ b/misc/openlayers/tests/manual/map-events.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <title>map.div Events Acceptance Test</title>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css">
+ <link rel="stylesheet" href="../../examples/style.css" type="text/css">
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map, layer;
+ function init() {
+ map = new OpenLayers.Map('map');
+ layer = new OpenLayers.Layer.OSM( "Simple OSM Map");
+ map.addLayer(layer);
+ map.setCenter(
+ new OpenLayers.LonLat(-71.147, 42.472).transform(
+ new OpenLayers.Projection("EPSG:4326"),
+ map.getProjectionObject()
+ ), 12
+ );
+
+ var element = document.getElementById('map');
+ element.addEventListener('mousedown', function(evt) {
+ alert('mousedown on map div');
+ });
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">map.div Events Acceptance Test</h1>
+
+ <div id="map" class="smallmap"></div>
+
+ <p><b>Test 1</b> : mousedown the map; an alert must be displayed.</p>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/memory/Marker-2258.html b/misc/openlayers/tests/manual/memory/Marker-2258.html
new file mode 100644
index 0000000..b2d8a37
--- /dev/null
+++ b/misc/openlayers/tests/manual/memory/Marker-2258.html
@@ -0,0 +1,60 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Memory Test - Layer.Markers / Marker</title>
+ <style type="text/css">
+ body {
+ font-size: 0.8em;
+ }
+ p {
+ padding-top: 1em;
+ }
+ #map {
+ width: 256px;
+ height: 256px;
+ border: 1px solid black;
+ }
+ </style>
+
+ <script src="../../../lib/Firebug/firebug.js"></script>
+ <script src="../../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map, layer, marker;
+
+ function init(){
+ map = new OpenLayers.Map('map');
+ map.addLayer(new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} ));
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ layer = new OpenLayers.Layer.Markers( "Markers" );
+ map.addLayer(layer);
+
+ marker = new OpenLayers.Marker(new OpenLayers.LonLat(0,0));
+ layer.addMarker(marker);
+
+ window.setTimeout(function() {
+ layer.removeMarker(marker);
+ layer.addMarker(marker);
+
+ // people SHOULD call marker.destroy(). But if they don't
+ // we leak memory.
+ //marker.destroy();
+
+ window.alert("Setup - hit STOP in the leak detector now");
+ }, 100);
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">Memory Test - Layer.Markers / Marker</h1>
+ <pre id="status"></pre>
+ <div id="map"></div>
+ <p>
+ This test is a memory leak test for usage of Layer.Markers / Marker.
+ </p>
+ <p>
+ Run this test in IE6/7 with <a href="http://blogs.msdn.com/gpde/pages/javascript-memory-leak-detector-v2.aspx">JavaScript Memory Leak Detector v2</a>
+ and watch it identify a leak unless this is fixed.
+ </p>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/memory/PanZoom-2323.html b/misc/openlayers/tests/manual/memory/PanZoom-2323.html
new file mode 100644
index 0000000..de629a6
--- /dev/null
+++ b/misc/openlayers/tests/manual/memory/PanZoom-2323.html
@@ -0,0 +1,41 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Memory Test - PanZoom.getSlideFactor</title>
+ <style type="text/css">
+ body {
+ font-size: 0.8em;
+ }
+ p {
+ padding-top: 1em;
+ }
+ #map {
+ width: 256px;
+ height: 256px;
+ border: 1px solid black;
+ }
+ </style>
+
+ <script src="../../../lib/Firebug/firebug.js"></script>
+ <script src="../../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map;
+ var layer;
+
+ function init(){
+ map = new OpenLayers.Map('map');
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">Memory Test - PanZoom.getSlideFactor</h1>
+ <pre id="status"></pre>
+ <div id="map"></div>
+ <p>
+ This test is a memory leak test for: PanZoom.getSlideFactor.
+ </p>
+ <p>
+ Run this test in IE6/7 with <a href="http://blogs.msdn.com/gpde/pages/javascript-memory-leak-detector-v2.aspx">JavaScript Memory Leak Detector v2</a>
+ and watch it identify a leak unless this is fixed.
+ </p>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/memory/RemoveChild-2170.html b/misc/openlayers/tests/manual/memory/RemoveChild-2170.html
new file mode 100644
index 0000000..abe9249
--- /dev/null
+++ b/misc/openlayers/tests/manual/memory/RemoveChild-2170.html
@@ -0,0 +1,56 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Memory Test - DOMNode.removeChild</title>
+ <style type="text/css">
+ body {
+ font-size: 0.8em;
+ }
+ p {
+ padding-top: 1em;
+ }
+ #map {
+ width: 512px;
+ height: 512px;
+ border: 1px solid black;
+ }
+ </style>
+
+ <script src="../../../lib/Firebug/firebug.js"></script>
+ <script src="../../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map, layer;
+
+ function tearDown() {
+ layer.events.unregister("loadend", layer, tearDown);
+ window.setTimeout(function() {
+ map.removeLayer(layer);
+ //map.addLayer(layer);
+ layer.destroy();
+ window.alert("Setup - hit STOP in the leak detector now");
+ }, 100);
+ }
+
+ function init(){
+ map = new OpenLayers.Map( 'map', {maxResolution:1.40625/2} );
+ layer = new OpenLayers.Layer.TMS( "TMS",
+ "http://labs.metacarta.com/wms-c/Basic.py/", {layername: 'basic', type:'png'} );
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(5, 40), 5);
+
+ layer.events.register("loadend", layer, tearDown);
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">Memory Test - DOMNode.removeChild</h1>
+ <pre id="status"></pre>
+ <div id="map"></div>
+ <p>
+ This test is a memory leak test for usage of DOMNode.removeChild
+ </p>
+ <p>
+ Run this test in IE6/7 with <a href="http://blogs.msdn.com/gpde/pages/javascript-memory-leak-detector-v2.aspx">JavaScript Memory Leak Detector v2</a>
+ and watch it identify a leak unless this is fixed.
+ </p>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/memory/VML-2170.html b/misc/openlayers/tests/manual/memory/VML-2170.html
new file mode 100644
index 0000000..2f72300
--- /dev/null
+++ b/misc/openlayers/tests/manual/memory/VML-2170.html
@@ -0,0 +1,49 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Memory Test - Renderer.VML - onselectstart</title>
+ <style type="text/css">
+ body {
+ font-size: 0.8em;
+ }
+ p {
+ padding-top: 1em;
+ }
+ #map {
+ width: 256px;
+ height: 256px;
+ border: 1px solid black;
+ }
+ </style>
+
+ <script src="../../../lib/Firebug/firebug.js"></script>
+ <script src="../../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map;
+ var layer;
+
+ function init(){
+ map = new OpenLayers.Map('map');
+
+ layer = new OpenLayers.Layer.Vector("Test-VML", {renderers:['VML']});
+ map.addLayers([layer]);
+
+ window.setTimeout(function() {
+ layer.redraw();
+ window.alert("Setup - hit STOP in the leak detector now");
+ }, 100);
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">Memory Test - Renderer.VML - onselectstart</h1>
+ <pre id="status"></pre>
+ <div id="map"></div>
+ <p>
+ This test is a memory leak test for usage of "onselectstart" event handler in Renderer.VML
+ </p>
+ <p>
+ Run this test in IE6/7 with <a href="http://blogs.msdn.com/gpde/pages/javascript-memory-leak-detector-v2.aspx">JavaScript Memory Leak Detector v2</a>
+ and watch it identify a leak unless this is fixed.
+ </p>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/multiple-google-layers.html b/misc/openlayers/tests/manual/multiple-google-layers.html
new file mode 100644
index 0000000..df5c4f0
--- /dev/null
+++ b/misc/openlayers/tests/manual/multiple-google-layers.html
@@ -0,0 +1,135 @@
+<html>
+ <head>
+ <title>Multiple Google Layers Acceptance Test</title>
+ <script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhR_wWLPFku8Ix9i2SXYRVK3e45q1BQUd_beF8dtzKET_EteAjPdGDwqpQ"></script>
+ <script src="../../lib/OpenLayers.js"></script>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+ <link rel="stylesheet" href="../../theme/default/google.css" type="text/css" />
+ <style>
+ .col {
+ position: relative;
+ width: 50%;
+ }
+ #col1 {
+ float: left;
+ }
+ #col2 {
+ float: right;
+ }
+ .map {
+ position: relative;
+ height: 300px;
+ }
+ .wrap {
+ position: relative;
+ padding: 10px;
+ }
+ ul {
+ padding: 0;
+ }
+ ul li {
+ list-style: none;
+ }
+ p.clear {
+ clear: both;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="col1" class="col">
+ <div class="wrap">
+ <div id="map1" class="map"></div>
+ layers for map1
+ <ul>
+ <li><input type="checkbox" checked="checked" name="streets1" id="streets1"><label for="streets1">streets</label></li>
+ <li><input type="checkbox" checked="checked" name="sat1" id="sat1"><label for="sat1">imagery</label></li>
+ <li><input type="checkbox" checked="checked" name="topo1" id="topo1"><label for="topo1">topography</label></li>
+ </ul>
+ </div>
+ </div>
+ <div id="col2" class="col">
+ <div class="wrap">
+ <div id="map2" class="map"></div>
+ layers for map2
+ <ul>
+ <li><input type="checkbox" name="streets2" id="streets2"><label for="streets2">streets</label></li>
+ <li><input type="checkbox" name="sat2" id="sat2"><label for="sat2">imagery</label></li>
+ <li><input type="checkbox" name="topo2" id="topo2"><label for="topo2">topography</label></li>
+ </ul>
+ </div>
+ </div>
+ <p class="clear">
+ This example is used to confirm that resizable maps with multiple
+ Google layers work properly. Click the checkboxes to add/remove
+ layers from the maps. Use the layer switcher to change the map's
+ base layer. You should be able to confirm the following:
+ <ol>
+ <li>Adding and removing layers doesn't raise any errors
+ (regardless of how many times the same layer is added/removed).</li>
+ <li>The Google "Powered By" link is always visible and clickable
+ when a Google layer is displayed.</li>
+ <li>The Google "Terms of Use" link is always visible and clickable
+ when a Google layer is displayed.</li>
+ <li>Resizing a map (by resizing the browser window) and then
+ changing base layer works well. That is, the center & scale are
+ preserved and all tiles are well aligned.</li>
+ <li>Setting the base layer to the "Dummy Layer" hides all other
+ Google base layers, "Powered By" link, and "Terms of Use" link.</li>
+ </ol>
+ </p>
+ <script>
+
+ var map1 = new OpenLayers.Map("map1");
+ var streets1 = new OpenLayers.Layer.Google("Streets", {
+ type: G_NORMAL_MAP
+ });
+ var sat1 = new OpenLayers.Layer.Google("Imagery", {
+ type: G_SATELLITE_MAP
+ });
+ var topo1 = new OpenLayers.Layer.Google("Topography", {
+ type: G_PHYSICAL_MAP
+ });
+ var dummy1 = new OpenLayers.Layer("Dummy Layer", {
+ isBaseLayer: true
+ });
+ map1.addLayers([streets1, sat1, topo1, dummy1]);
+ map1.addControl(new OpenLayers.Control.LayerSwitcher);
+ map1.zoomToMaxExtent();
+
+ var map2 = new OpenLayers.Map("map2");
+ var streets2 = new OpenLayers.Layer.Google("Streets", {
+ type: G_NORMAL_MAP
+ });
+ var sat2 = new OpenLayers.Layer.Google("Imagery", {
+ type: G_SATELLITE_MAP
+ });
+ var topo2 = new OpenLayers.Layer.Google("Topography", {
+ type: G_PHYSICAL_MAP
+ });
+ var dummy2 = new OpenLayers.Layer("Dummy Layer", {
+ isBaseLayer: true
+ });
+ map2.addLayer(dummy2);
+ map2.addControl(new OpenLayers.Control.LayerSwitcher);
+ map2.zoomToMaxExtent();
+
+ // add behavior to checkboxes
+ var check, inputs = document.getElementsByTagName("input");
+ for (var i=0, len=inputs.length; i<len; ++i) {
+ check = inputs[i];
+ check.onclick = function() {
+ var name = this.name;
+ var num = name.match(/\d$/)[0];
+ var layer = window[name];
+ var map = window["map" + num];
+ if (this.checked) {
+ map.addLayer(layer);
+ } else {
+ map.removeLayer(layer);
+ }
+ }
+ }
+
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/misc/openlayers/tests/manual/overviewmap-projection.html b/misc/openlayers/tests/manual/overviewmap-projection.html
new file mode 100644
index 0000000..bb15c9f
--- /dev/null
+++ b/misc/openlayers/tests/manual/overviewmap-projection.html
@@ -0,0 +1,70 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+ <link rel="stylesheet" href="../../examples/style.css" type="text/css" />
+ <style type="text/css">
+ .olControlAttribution { bottom: 0px!important }
+ #map {
+ height: 512px;
+ }
+ </style>
+
+ <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhR_wWLPFku8Ix9i2SXYRVK3e45q1BQUd_beF8dtzKET_EteAjPdGDwqpQ'></script>
+
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+
+ // make map available for easy debugging
+ var map;
+
+ function init(){
+ var options = {
+ projection: new OpenLayers.Projection("EPSG:900913"),
+ displayProjection: new OpenLayers.Projection("EPSG:4326"),
+ units: "m",
+ maxResolution: 156543.0339,
+ maxExtent: new OpenLayers.Bounds(-20037508, -20037508,
+ 20037508, 20037508.34)
+ };
+ map = new OpenLayers.Map('map', options);
+
+ // create Google Mercator layers
+ var gmap = new OpenLayers.Layer.Google(
+ "Google Streets",
+ {'sphericalMercator': true}
+ );
+ map.addLayers([gmap]);
+ map.addControl(new OpenLayers.Control.LayerSwitcher());
+ map.addControl(new OpenLayers.Control.Permalink());
+ map.addControl(new OpenLayers.Control.MousePosition());
+ var ovmap = new OpenLayers.Control.OverviewMap({
+ maxRatio: 16,
+ layers: [new OpenLayers.Layer.WMS("OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'})]
+ });
+ map.addControl(ovmap);
+ ovmap.maximizeControl();
+ if (!map.getCenter()) {map.zoomToMaxExtent()};
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">OpenLayers Overview Map Projection Test</h1>
+
+ <div id="tags">
+ </div>
+ <p id="shortdesc">
+ Acceptance test for different projections in map and overview map.
+ The map uses EPSG:900913, the overview map EPSG:4326. Zoom the map and
+ drag both the map and the overview map to see it in action.
+ </p>
+ <div id="map" class="smallmap"></div>
+
+ <div id="docs">
+ </div>
+ </body>
+</html>
+
+
+
diff --git a/misc/openlayers/tests/manual/page-position.html b/misc/openlayers/tests/manual/page-position.html
new file mode 100644
index 0000000..a59dfde
--- /dev/null
+++ b/misc/openlayers/tests/manual/page-position.html
@@ -0,0 +1,103 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Page Position Test</title>
+
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+ <link rel="stylesheet" href="../../examples/style.css" type="text/css" />
+ <style type="text/css">
+ #mapwrap {
+ border: 10px solid red;
+ width: 532px;
+ height: 276px;
+ }
+ #map {
+ position: absolute;
+ border: 10px solid #ccc;
+ width: 512px;
+ height: 256px;
+ }
+ #controlToggle li {
+ list-style: none;
+ }
+ p {
+ width: 512px;
+ }
+ #scrollspace {
+ height: 500px;
+ }
+ </style>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map, drawControls;
+ function init(){
+ map = new OpenLayers.Map('map');
+
+ var wmsLayer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://vmap0.tiles.osgeo.org/wms/vmap0?", {layers: 'basic'});
+
+ var lineLayer = new OpenLayers.Layer.Vector("Line Layer");
+
+ map.addLayers([wmsLayer, lineLayer]);
+ map.addControl(new OpenLayers.Control.LayerSwitcher());
+ map.addControl(new OpenLayers.Control.MousePosition());
+
+ drawControl = new OpenLayers.Control.DrawFeature(lineLayer,
+ OpenLayers.Handler.Path);
+
+ map.addControl(drawControl);
+
+ map.setCenter(new OpenLayers.LonLat(0, 0), 3);
+
+ document.getElementById('noneToggle').checked = true;
+ }
+
+ function toggleControl(element) {
+ var control = drawControl;
+ if(element.value == "draw" && element.checked) {
+ control.activate();
+ } else {
+ control.deactivate();
+ }
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">OpenLayers Page Position Test</h1>
+
+ <p id="shortdesc">
+ Test if borders and scroll position cause unwanted offsets on the
+ mouse positions reported by map events.
+ </p>
+ <div id="mapwrap">
+ <div id="map"></div>
+ </div>
+
+ <ul id="controlToggle">
+ <li>
+ <input type="radio" name="type" value="none" id="noneToggle"
+ onclick="toggleControl(this);" checked="checked" />
+ <label for="noneToggle">navigate</label>
+ </li>
+ <li>
+ <input type="radio" name="type" value="draw" id="lineToggle" onclick="toggleControl(this);" />
+ <label for="lineToggle">draw line</label>
+ </li>
+ </ul>
+
+ <div id="docs">
+ <p>This map's div has a border and absolute positioning, wrapped
+ by a container which also has a border. The page is also
+ scrollable. Neither the borders nor scrolling the page should
+ result in unwanted offsets on pixel positions reported by map
+ events.</p>
+ <p>With the line drawing control active, click on the map to add a
+ point. The point should be drawn at the exact mouse location.</p>
+ <p>With the navigation control active, shift-drag a zoom rectangle.
+ The rectangle's corner should align exactly with the mouse
+ cursor.</p>
+ <p>Scroll the page and repeat the above tests.</p>
+ <div id="scrollspace"><div>
+ </div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/pan-redraw-svg.html b/misc/openlayers/tests/manual/pan-redraw-svg.html
new file mode 100644
index 0000000..ec1126b
--- /dev/null
+++ b/misc/openlayers/tests/manual/pan-redraw-svg.html
@@ -0,0 +1,58 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+ <style type="text/css">
+ #map {
+ width: 512px;
+ height: 512px;
+ border: 1px solid gray;
+ }
+ </style>
+
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map, point;
+
+ function init(){
+ var options = {
+ projection: new OpenLayers.Projection("EPSG:900913"),
+ displayProjection: new OpenLayers.Projection("EPSG:4326"),
+ units: "m",
+ maxResolution: 20, //0.07464553542137146,
+ maxExtent: new OpenLayers.Bounds(-20037508, -20037508,
+ 20037508, 20037508.34)
+ };
+ map = new OpenLayers.Map('map', options);
+ var vector = new OpenLayers.Layer.Vector(
+ "Vectors",
+ {isBaseLayer: true}
+ );
+ map.addLayer(vector);
+
+ var x = -20000;//4.33791754;
+ var y = 20000;
+ point = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(x, y)
+ );
+
+ map.addLayer(vector);
+ vector.addFeatures([point]);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 5);
+ }
+
+ function pan(){
+ map.panTo(point.geometry.getBounds().getCenterLonLat());
+ }
+
+ </script>
+ </head>
+ <body onload="init()">
+ <h3 id="title">SVG inValidRange Redraw Test Case</h3>
+ <p>Before fixing #1631, after klicking Go! no point would have appeared. The Go! button
+ pans the map over a long distance. Before dragging, the point would have been
+ outside the valid range, and the pan operation would not have triggered the SVG
+ coordinate system to be recreated. The new vector rendering takes care of all this. </p>
+ <div id="map"></div>
+ <input type="button" value="Go!" onclick="pan();"></input>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/popup-keepInMap.html b/misc/openlayers/tests/manual/popup-keepInMap.html
new file mode 100644
index 0000000..4ba1c18
--- /dev/null
+++ b/misc/openlayers/tests/manual/popup-keepInMap.html
@@ -0,0 +1,100 @@
+<html xmlns="http://www.w3.org/1999/xhtml" debug="true">
+ <head>
+ <title>OpenLayers: Popup Mayhem</title>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+ <style type="text/css">
+ #map {
+ width: 900px;
+ height: 500px;
+ border: 1px solid black;
+ }
+ </style>
+
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map;
+ var layer, markers;
+
+ var currentPopup;
+
+
+ AutoSizeFramedCloud = OpenLayers.Class(OpenLayers.Popup.FramedCloud, {
+ 'autoSize': true,
+ 'panMapIfOutOfView': false
+ });
+
+ function init(){
+ map = new OpenLayers.Map('map');
+
+ markers = new OpenLayers.Layer.Markers("zibo", {isBaseLayer: true});
+ map.addLayer(markers);
+
+ addMarkers();
+ map.zoomToMaxExtent();
+ }
+
+ function addMarkers() {
+
+ var ll, popupClass, popupContentHTML;
+
+ //anchored bubble popup small contents autosize closebox
+ ll = new OpenLayers.LonLat(-35,-15);
+ popupClass = AutoSizeFramedCloud;
+ popupContentHTML = "<div>This popup can't be panned to fit in view, so its popup text should fit inside the map. If it doens't, instead of expaning outside, it will simply make the content scroll. Scroll scroll scroll your boat, gently down the stream! Chicken chicken says the popup text is really boring to write. Did you ever see a popup a popup a popup did you ever see a popup a popup right now. With this way and that way and this way and that way did you ever see a popup a popup right now. I wonder if this is long enough. it might be, but maybe i should throw in some other content. <ul><li>one</li><li>two</li><li>three</li><li>four</li></ul>(get your booty on the floor) </div>";
+ addMarker(ll, popupClass, popupContentHTML, true, true);
+
+
+ }
+
+ /**
+ * Function: addMarker
+ * Add a new marker to the markers layer given the following lonlat,
+ * popupClass, and popup contents HTML. Also allow specifying
+ * whether or not to give the popup a close box.
+ *
+ * Parameters:
+ * ll - {<OpenLayers.LonLat>} Where to place the marker
+ * popupClass - {<OpenLayers.Class>} Which class of popup to bring up
+ * when the marker is clicked.
+ * popupContentHTML - {String} What to put in the popup
+ * closeBox - {Boolean} Should popup have a close box?
+ * overflow - {Boolean} Let the popup overflow scrollbars?
+ */
+ function addMarker(ll, popupClass, popupContentHTML, closeBox, overflow) {
+
+ var feature = new OpenLayers.Feature(markers, ll);
+ feature.closeBox = closeBox;
+ feature.popupClass = popupClass;
+ feature.data.popupContentHTML = popupContentHTML;
+ feature.data.overflow = (overflow) ? "auto" : "hidden";
+
+ var marker = feature.createMarker();
+
+ var markerClick = function (evt) {
+ if (this.popup == null) {
+ this.popup = this.createPopup(this.closeBox);
+ map.addPopup(this.popup);
+ this.popup.show();
+ } else {
+ this.popup.toggle();
+ this.popup.updateSize();
+ }
+ currentPopup = this.popup;
+ OpenLayers.Event.stop(evt);
+ };
+ marker.events.register("mousedown", feature, markerClick);
+
+ markers.addMarker(marker);
+ }
+
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">Popup KeepInMap</h1>
+
+ <div id="map" class="smallmap"></div>
+ </div>
+ Click on popup, and the content should scroll rather than expanding outside the map.
+ </div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/reflow.html b/misc/openlayers/tests/manual/reflow.html
new file mode 100644
index 0000000..bb9585e
--- /dev/null
+++ b/misc/openlayers/tests/manual/reflow.html
@@ -0,0 +1,59 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <style type="text/css">
+ #map {
+ width: 800px;
+ height: 475px;
+ border: 1px solid black;
+ }
+ </style>
+ <script src="../../lib/OpenLayers.js" type="text/javascript"></script>
+ <script type="text/javascript">
+
+ var map;
+ var vectors;
+
+ function init(){
+ map = new OpenLayers.Map('map');
+ var wms = new OpenLayers.Layer.WMS(
+ "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'}
+ );
+
+ vectors = new OpenLayers.Layer.Vector(
+ "Simple Geometry",
+ {
+ styleMap: new OpenLayers.StyleMap({
+ externalGraphic: "../../img/marker-gold.png",
+ pointRadius: 10
+ })
+ }
+ );
+
+ map.addLayers([wms, vectors]);
+
+ var features = [];
+ var x = -111.04;
+ var y = 45.68;
+ for(var i = 0; i < 10; i++){
+ x += i * .5;
+ y += i * .1;
+ features.push(
+ new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(x, y)
+ )
+ );
+ }
+
+ map.setCenter(new OpenLayers.LonLat(x, y), 5);
+ vectors.addFeatures(features);
+ };
+
+ </script>
+ </head>
+ <body onload="init()">
+ <div id="map"></div>
+ <p>Use the pan buttons. See flicker at end of animated pan.</p>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/renderedDimensions.html b/misc/openlayers/tests/manual/renderedDimensions.html
new file mode 100644
index 0000000..b01624b
--- /dev/null
+++ b/misc/openlayers/tests/manual/renderedDimensions.html
@@ -0,0 +1,113 @@
+<html xmlns="http://www.w3.org/1999/xhtml" debug="true">
+ <head>
+ <title>OpenLayers: Popup Mayhem</title>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+ <style type="text/css">
+ #map {
+ width: 900px;
+ height: 500px;
+ border: 1px solid black;
+ }
+ </style>
+
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map;
+ var layer, markers;
+
+ var currentPopup;
+
+
+// different popup types
+
+
+ //disable the autosize for the purpose of our matrix
+ OpenLayers.Popup.FramedCloud.prototype.autoSize = false;
+
+ AutoSizeFramedCloud = OpenLayers.Class(OpenLayers.Popup.FramedCloud, {
+ 'autoSize': true
+ });
+
+ function init(){
+ map = new OpenLayers.Map('map');
+
+ layer = new OpenLayers.Layer(
+ "popupMatrix",
+ {isBaseLayer: true}
+ );
+ map.addLayer(layer);
+
+ markers = new OpenLayers.Layer.Markers("zibo");
+ map.addLayer(markers);
+
+ addMarkers();
+ map.zoomToMaxExtent();
+ }
+
+ function addMarkers() {
+
+ var ll, popupClass, popupContentHTML;
+
+ //anchored bubble popup small contents autosize closebox
+ ll = new OpenLayers.LonLat(-35,-15);
+ popupClass = AutoSizeFramedCloud;
+ popupContentHTML = "<div>This text's line-height is affected<br/>by it's parents. Thus we have to<br/>place the content inside<br/>the correct container to get<br/>the rendered size.</div>";
+ addMarker(ll, popupClass, popupContentHTML, true);
+
+
+ }
+
+ /**
+ * Function: addMarker
+ * Add a new marker to the markers layer given the following lonlat,
+ * popupClass, and popup contents HTML. Also allow specifying
+ * whether or not to give the popup a close box.
+ *
+ * Parameters:
+ * ll - {<OpenLayers.LonLat>} Where to place the marker
+ * popupClass - {<OpenLayers.Class>} Which class of popup to bring up
+ * when the marker is clicked.
+ * popupContentHTML - {String} What to put in the popup
+ * closeBox - {Boolean} Should popup have a close box?
+ * overflow - {Boolean} Let the popup overflow scrollbars?
+ */
+ function addMarker(ll, popupClass, popupContentHTML, closeBox, overflow) {
+
+ var feature = new OpenLayers.Feature(markers, ll);
+ feature.closeBox = closeBox;
+ feature.popupClass = popupClass;
+ feature.data.popupContentHTML = popupContentHTML;
+ feature.data.overflow = (overflow) ? "auto" : "hidden";
+
+ var marker = feature.createMarker();
+
+ var markerClick = function (evt) {
+ if (this.popup == null) {
+ this.popup = this.createPopup(this.closeBox);
+ map.addPopup(this.popup);
+ this.popup.show();
+ } else {
+ this.popup.toggle();
+ }
+ currentPopup = this.popup;
+ OpenLayers.Event.stop(evt);
+ };
+ marker.events.register("mousedown", feature, markerClick);
+
+ markers.addMarker(marker);
+ }
+
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">Popup Matrix</h1>
+
+ <div id="tags">
+ </div>
+ <div style="line-height: 40px;">
+ <div id="map" class="smallmap"></div>
+ </div>
+ Click on popup, should be able to read a full sentence, not just two lines.
+ </div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/select-feature-right-click.html b/misc/openlayers/tests/manual/select-feature-right-click.html
new file mode 100644
index 0000000..edd79d6
--- /dev/null
+++ b/misc/openlayers/tests/manual/select-feature-right-click.html
@@ -0,0 +1,86 @@
+<html>
+ <head>
+ <title>OpenLayers Ticket 3404</title>
+ <script src="../../lib/OpenLayers.js"></script>
+ </head>
+ <body>
+ <table cellpadding="10px">
+ <tr>
+ <td width="600">
+ <p><a href="http://trac.osgeo.org/openlayers/ticket/3404">Ticket 3404</a> Test Page</p>
+ <p>This bug is only triggered by physical right mouse clicks so it is not possible to write
+ an automated js unit test</p>
+ <p>When a SelectFeature control and a Navigation control are added to a map the left-click
+ mousedown events are stopped by a Drag handler before reaching the Feature handler. However,
+ right-click mousedown events so pass through and reach the Feature handler.</p>
+ <p>The Feature handler records the xy of
+ each mousedown and mouseup events so they can be compared in the click event. Because only right-click
+ mousedown event are received the location of future left-click mouseup events are compared
+ to the location of the 'stale' right-click mousedown event resulting in the feature not being selected.</p>
+ <p>Steps to recreate the bug:
+ <ol>
+ <li>Left-click a point to select it.</li>
+ <li>Left-click the map to deselect the point.</li>
+ <li>Left-click a different point to select it.</li>
+ <li>Left-click the map to deselect the second point.</li>
+ <li>Right-click the map then left-click to close the browser context menu.</li>
+ <li>Left-click a point.</li>
+ </ol>
+ </p>
+ <p>Expected: The point is selected.</p>
+ </td>
+ <td>
+ <div style="width:300; height:400" id="map"></div>
+ </td>
+ </tr>
+ </table>
+
+ <script defer="defer" type="text/javascript">
+ var map = new OpenLayers.Map('map');
+
+ var wmsLayer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://vmap0.tiles.osgeo.org/wms/vmap0", {layers: 'basic'} );
+
+ // allow testing of specific renderers via "?renderer=Canvas", etc
+ var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
+ renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
+
+ var vectorLayer = new OpenLayers.Layer.Vector("Vector Layer", {
+ renderers: renderer
+ });
+
+ map.addLayers([wmsLayer, vectorLayer]);
+ map.addControl(new OpenLayers.Control.LayerSwitcher());
+
+ var selectControl = new OpenLayers.Control.SelectFeature(
+ vectorLayer,
+ {
+ clickout: true, toggle: false,
+ multiple: false, hover: false,
+ toggleKey: "ctrlKey", // ctrl key removes from selection
+ multipleKey: "shiftKey", // shift key adds to selection
+ }
+ );
+
+ map.addControl(selectControl);
+ selectControl.activate();
+
+ map.addControl(new OpenLayers.Control.Navigation());
+ map.setCenter(new OpenLayers.LonLat(-75.1641667, 39.9522222), 10);
+
+ var createRandomFeatures = function() {
+ var extent = map.getExtent();
+ var features = [];
+ for(var i=0; i<10; ++i) {
+ features.push(new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(extent.left + (extent.right - extent.left) * Math.random(),
+ extent.bottom + (extent.top - extent.bottom) * Math.random()
+ )));
+ }
+ return features;
+ }
+
+ vectorLayer.addFeatures(createRandomFeatures());
+ </script>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/select-feature.html b/misc/openlayers/tests/manual/select-feature.html
new file mode 100644
index 0000000..6e1fba0
--- /dev/null
+++ b/misc/openlayers/tests/manual/select-feature.html
@@ -0,0 +1,75 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Select Feature Test</title>
+ <style type="text/css">
+ body {
+ font-size: 0.8em;
+ }
+ p {
+ padding-top: 1em;
+ }
+ #map {
+ margin: 1em;
+ width: 512px;
+ height: 512px;
+ }
+ </style>
+
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map, selectControl1, selectControl2;
+
+ function init() {
+ map = new OpenLayers.Map('map');
+ var wmsLayer = new OpenLayers.Layer.WMS(
+ "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'}
+ );
+ var vectorLayer = new OpenLayers.Layer.Vector("Vector Layer");
+ var pointFeature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(-50, -45)
+ );
+ var polygonFeature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(-50,-50),
+ new OpenLayers.Geometry.Point(-40,-50),
+ new OpenLayers.Geometry.Point(-40,-40),
+ new OpenLayers.Geometry.Point(-50,-50)
+ ])
+ ])
+ );
+ vectorLayer.addFeatures([pointFeature, polygonFeature]);
+ map.addLayers([wmsLayer, vectorLayer]);
+ selectControl1 = new OpenLayers.Control.SelectFeature(
+ vectorLayer, {geometryTypes: ['OpenLayers.Geometry.Point']}
+ );
+ selectControl2 = new OpenLayers.Control.SelectFeature(
+ vectorLayer, {
+ geometryTypes: ['OpenLayers.Geometry.Polygon'],
+ hover: true
+ });
+ map.addControl(new OpenLayers.Control.MousePosition());
+ map.addControl(selectControl1);
+ map.addControl(selectControl2);
+ selectControl1.activate();
+ selectControl2.activate();
+ map.setCenter(new OpenLayers.LonLat(-45, -45), 4);
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">Select Feature Test</h1>
+ <div id="map"></div>
+ <p>
+
+ The map includes two select feature controls. The first one operates on
+ geometries of type OpenLayers.Geometry.Point only and works on clicks. The
+ second one operates on geometries of type OpenLayers.Geometry.Polygon and
+ works on mouseover's. If you select the point geometry by clicking on it,
+ it shouldn't be unselected when the mouse moves out if it.
+
+ </p>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/tiles-loading.html b/misc/openlayers/tests/manual/tiles-loading.html
new file mode 100644
index 0000000..cbd0e10
--- /dev/null
+++ b/misc/openlayers/tests/manual/tiles-loading.html
@@ -0,0 +1,122 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Tiles Loading Acceptance Test</title>
+ <style type="text/css">
+ body {
+ font-size: 0.8em;
+ }
+ p {
+ padding-top: 1em;
+ }
+ #map {
+ margin: 1em;
+ float: left;
+ width: 512px;
+ height: 512px;
+ }
+
+ </style>
+
+ <script src='http://maps.google.com/maps?file=api&amp;v=2.82&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhR_wWLPFku8Ix9i2SXYRVK3e45q1BQUd_beF8dtzKET_EteAjPdGDwqpQ'></script>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ // make map available for easy debugging
+ var map;
+
+ // increase reload attempts
+ OpenLayers.IMAGE_RELOAD_ATTEMPTS = 3;
+
+ function init(){
+ var options = {
+ controls: [],
+ projection: "EPSG:900913",
+ units: "m",
+ maxResolution: 156543.0339,
+ maxExtent: new OpenLayers.Bounds(-20037508, -20037508,
+ 20037508, 20037508.34)
+ };
+ map = new OpenLayers.Map('map', options);
+ // create Google Mercator layers
+ var gmap = new OpenLayers.Layer.Google(
+ "Google Streets",
+ {'sphericalMercator': true}
+ );
+ // create WMS layer
+ var wmsMaxResolution = 78271.51695;
+ var wms = new OpenLayers.Layer.WMS(
+ "World Map",
+ "http://world.freemap.in/tiles/",
+ {'layers': 'factbook-overlay', 'format':'png'},
+ {
+ 'opacity': 0.4,
+ 'isBaseLayer': false,
+ 'wrapDateLine': true,
+ 'buffer': 0,
+ 'maxResolution' : wmsMaxResolution
+ }
+ );
+ map.addLayers([gmap, wms]);
+ map.addControl(new OpenLayers.Control.Navigation());
+ map.addControl(new OpenLayers.Control.LayerSwitcher());
+ map.addControl(new OpenLayers.Control.PanZoomBar());
+
+ function onLayerChanged() {
+ var html = '<p>WMS Layer state - in range: '
+ + this.inRange
+ + ', visibility: '
+ + this.visibility;
+ + '</p>';
+ document.getElementById('layerstate').innerHTML = html;
+ }
+ map.events.register('changelayer', wms, onLayerChanged);
+
+ function onTileLoaded() {
+ var html = '<p>Message: ';
+ if (this.numLoadingTiles > 0) {
+ html += 'Loading tiles...';
+ } else {
+ html += 'Done loading tiles';
+ }
+ html += '</p>';
+ document.getElementById('tilesloading').innerHTML = html;
+ }
+ wms.events.register('tileloaded', wms, onTileLoaded);
+
+ map.zoomToMaxExtent()
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <div id="map"></div>
+ <p>
+
+ <b>Test 0</b> : at the initial zoom the WMS layer is in range, you should
+ therefore see the 'Loading tiles...' message when loading the page for
+ the first time.
+
+ </p>
+ <p>
+
+ <b>Test 1</b> : If you zoom out by one level (using the zoombar), the WMS
+ layer becomes out of range. No tile should be loaded so you shouldn't see
+ the 'Loading tiles...' message.
+
+ </p>
+ <p>
+
+ <b>Test 2</b> : Zoom in by one level to go back to initial state (the WMS
+ is back). Open the layer switcher and turn off the WMS layer. No tile
+ should be loaded so you shouldn't see the 'Loading tiles...' message.
+
+ </p>
+ <p>
+
+ <b>Test 3</b> : Keep the WMS layer turned off in the layer switcher. Zoom
+ out by one level again. The layer is both invisible and out of range, so
+ you shouldn't see the 'Loading tiles...' message.
+
+ </p>
+ <div id="layerstate"><p>WMS Layer state - in range: true, visibility: true</p></div>
+ <div id="tilesloading"><p>Message:</p></div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/tween.html b/misc/openlayers/tests/manual/tween.html
new file mode 100644
index 0000000..88606cb
--- /dev/null
+++ b/misc/openlayers/tests/manual/tween.html
@@ -0,0 +1,82 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Tween Example</title>
+ <style type="text/css">
+ #viewport {
+ width: 500px;
+ height: 300px;
+ border: 1px solid black;
+ }
+ #block {
+ width: 10px;
+ height: 10px;
+ background-color: red;
+ position: absolute;
+ }
+ </style>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var tween, events;
+
+ function init(){
+ tween = new OpenLayers.Tween(OpenLayers.Easing.Linear.easeIn);
+
+ events = new OpenLayers.Events(null, OpenLayers.Util.getElement('viewport'), null, true);
+ events.register("mousedown", null, moveBlock);
+
+ changeTween();
+ }
+ function moveBlock(e) {
+ var block = OpenLayers.Util.getElement('block');
+ var viewport = OpenLayers.Util.getElement('viewport');
+ var blockPosition = OpenLayers.Util.pagePosition(block);
+ var viewportPosition = OpenLayers.Util.pagePosition(viewport);
+ e.xy = events.getMousePosition(e);
+ var from = {
+ x: blockPosition[0] - viewportPosition[0],
+ y: blockPosition[1] - viewportPosition[1]
+ };
+ var to = {
+ x: e.xy.x,
+ y: e.xy.y
+ }
+ var duration = OpenLayers.Util.getElement('duration').value;
+ var callbacks = {
+ eachStep: function(value) {
+ block.style.left = (value.x + viewportPosition[0]) + 'px';
+ block.style.top = (value.y + viewportPosition[1]) + 'px';
+ }
+ }
+ tween.start(from, to, duration, {callbacks: callbacks});
+
+ }
+ function changeTween() {
+ var transition = OpenLayers.Util.getElement('transition').value;
+ var easing = OpenLayers.Util.getElement('easing').value;
+ tween.stop();
+ tween.easing = OpenLayers.Easing[transition][easing];
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <div id="title">Tween Example</div>
+ <div id="tags"></div>
+ <div id="shortdesc">Show transition effects</div>
+ <select name="transition" id="transition" onchange="changeTween()">
+ <option value="Linear">Linear</option>
+ <option value="Expo">Expo</option>
+ <option value="Quad">Quad</option>
+ </select>
+ <select name="easing" id="easing" onchange="changeTween()">
+ <option value="easeIn">EaseIn</option>
+ <option value="easeOut">EaseOut</option>
+ </select>
+ <input type="text" name="duration" id="duration" value="100"></input>
+ <div id="viewport">
+ <div id="block"></div>
+ </div>
+ <div id="docs">
+ This is an example of transition effects.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/misc/openlayers/tests/manual/vector-features-performance.html b/misc/openlayers/tests/manual/vector-features-performance.html
new file mode 100644
index 0000000..7990379
--- /dev/null
+++ b/misc/openlayers/tests/manual/vector-features-performance.html
@@ -0,0 +1,149 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Vector Features Performance Test</title>
+ <style type="text/css">
+ body {
+ font-size: 0.8em;
+ }
+ p {
+ padding-top: 1em;
+ }
+ #map {
+ width: 512px;
+ height: 512px;
+ border: 1px solid black;
+ }
+ </style>
+
+ <script src="../../lib/Firebug/firebug.js"></script>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map, vectorLayer, drawFeature, features
+
+ var run = 0;
+
+ function nextRun() {
+ window.setTimeout(function() {
+ if (run < 5) {
+ vectorLayer.removeFeatures(features);
+ }
+ }, 900);
+
+ window.setTimeout(function(){
+ run++;
+
+ switch(run) {
+ case 1:
+ console.log("First run - feature bboxes will be cached");
+ map.setCenter(new OpenLayers.LonLat(-22.5, -22.5), 3);
+ vectorTestNew()
+ break;
+ case 2:
+ console.log("Test with all features inside extent");
+ vectorTestOld();
+ break;
+ case 3:
+ vectorTestNew();
+ break;
+ case 4:
+ console.log("Test with some features outside extent");
+ map.setCenter(new OpenLayers.LonLat(-22.5, -22.5), 5);
+ vectorTestOld();
+ break;
+ case 5:
+ vectorTestNew();
+ break;
+ }
+ }, 1000);
+ }
+
+ function vectorTestOld(){
+ vectorLayer.renderer.drawFeature = function(feature, style) {
+ if(style == null) {
+ style = feature.style;
+ }
+ if (feature.geometry) {
+ this.drawGeometry(feature.geometry, style, feature.id);
+ }
+ };
+
+ console.time("addFeaturesOld");
+ vectorLayer.addFeatures(features);
+ console.timeEnd("addFeaturesOld");
+ }
+
+ function vectorTestNew() {
+ vectorLayer.renderer.drawFeature = OpenLayers.Renderer[vectorLayer.renderer.CLASS_NAME.split(".")[2]].prototype.drawFeature;
+
+ console.time("addFeatures");
+ vectorLayer.addFeatures(features);
+ console.timeEnd("addFeatures");
+ }
+
+ function init(){
+ // allow testing of specific renderers via "?renderer=Canvas", etc
+ var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
+ renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
+
+ map = new OpenLayers.Map('map');
+
+ vectorLayer = new OpenLayers.Layer.Vector("Vector Layer", {
+ isBaseLayer: true,
+ renderers: renderer
+ });
+
+ map.addLayers([vectorLayer]);
+ map.addControl(new OpenLayers.Control.MousePosition());
+ map.setCenter(new OpenLayers.LonLat(-22.5, -22.5), 3);
+
+ vectorLayer.events.register("featuresadded", this, nextRun);
+
+ features = new Array(200);
+ var x, y
+ for (var i = 0; i < 200; i++) {
+ x = -Math.random()*45;
+ y = -Math.random()*45;
+ features[i] = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(
+ -Math.random()*5+x, -Math.random()*5+y),
+ new OpenLayers.Geometry.Point(
+ -Math.random()*5+x, -Math.random()*5+y),
+ new OpenLayers.Geometry.Point(
+ -Math.random()*5+x, -Math.random()*5+y),
+ new OpenLayers.Geometry.Point(
+ -Math.random()*5+x, -Math.random()*5+y),
+ new OpenLayers.Geometry.Point(
+ -Math.random()*5+x, -Math.random()*5+y),
+ new OpenLayers.Geometry.Point(
+ -Math.random()*5+x, -Math.random()*5+y),
+ new OpenLayers.Geometry.Point(
+ -Math.random()*5+x, -Math.random()*5+y),
+ new OpenLayers.Geometry.Point(
+ -Math.random()*5+x, -Math.random()*5+y),
+ new OpenLayers.Geometry.Point(
+ -Math.random()*5+x, -Math.random()*5+y),
+ new OpenLayers.Geometry.Point(
+ -Math.random()*5+x, -Math.random()*5+y)
+ ]));
+
+ }
+
+ nextRun();
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">New Rendering - Vector Features Performance Test</h1>
+ <div id="map"></div>
+ <p>
+ This test examines if checking for a feature being inside the visible
+ extent before rendering it has an impact on performance. Open the Firebug
+ console after running this test (hit F12) to see the results.
+ <br/>
+ After the performance test, you can drag around the map to see how the new
+ vector rendering feels where features get rendered only when they are visible
+ inside the map extent.
+ </p>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/manual/vector-layer-zindex.html b/misc/openlayers/tests/manual/vector-layer-zindex.html
new file mode 100644
index 0000000..d33c853
--- /dev/null
+++ b/misc/openlayers/tests/manual/vector-layer-zindex.html
@@ -0,0 +1,143 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Vector Layer ZIndex Test</title>
+ <style type="text/css">
+ body {
+ font-size: 0.8em;
+ }
+ p {
+ padding-top: 1em;
+ }
+ #map {
+ margin: 1em;
+ width: 512px;
+ height: 512px;
+ }
+ </style>
+
+ <script src="../../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map, layerA, layerB, layerV, selectControl1, selectControl2;
+
+ function init() {
+ map = new OpenLayers.Map('map');
+ var wmsLayer = new OpenLayers.Layer.WMS(
+ "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'}
+ );
+
+ layerV = new OpenLayers.Layer.Vector('v');
+ var feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(-110, 60),
+ new OpenLayers.Geometry.Point(-110, 30),
+ new OpenLayers.Geometry.Point(-80, 30),
+ new OpenLayers.Geometry.Point(-110, 60)
+ ])
+ ])
+ );
+ layerV.addFeatures([feature]);
+ selectControl1 = new OpenLayers.Control.SelectFeature(layerV);
+ selectControl2 = new OpenLayers.Control.SelectFeature(layerV, {
+ hover: true,
+ selectStyle: OpenLayers.Util.applyDefaults({fillColor: "red"}, OpenLayers.Feature.Vector.style["select"]),
+ onSelect: function(feature) {
+ selectControl2.unselect(feature);
+ layerV.drawFeature(feature, selectControl2.selectStyle);
+ }
+ });
+ selectControl2.events.register("beforefeatureselected", null, function(feature) {
+ layerV.drawFeature(feature, selectControl2.selectStyle);
+ return false;
+ })
+
+ layerA = new OpenLayers.Layer('a', {'isBaseLayer': false});
+ layerB = new OpenLayers.Layer.WMS(
+ 'b', 'http://www2.dmsolutions.ca/cgi-bin/mswms_gmap', {
+ 'layers': [
+ 'bathymetry', 'land_fn', 'park', 'drain_fn', 'drainage',
+ 'prov_bound', 'fedlimit', 'rail', 'road', 'popplace'
+ ],
+ 'transparent': 'true',
+ 'format': 'image/png'
+ }, {
+ 'reproject': false
+ });
+
+ map.addLayers([wmsLayer, layerV, layerA, layerB]);
+ map.addControl(selectControl2);
+ map.addControl(selectControl1);
+ map.addControl(new OpenLayers.Control.MousePosition());
+ selectControl2.activate();
+ selectControl1.activate();
+
+ map.setCenter(new OpenLayers.LonLat(-90, 20), 2);
+ }
+
+ function removeLayerA() {
+ if (OpenLayers.Util.indexOf(map.layers, layerA) > -1) {
+ map.removeLayer(layerA);
+ }
+ }
+
+ function toggleSelectControl1() {
+ if (selectControl1.active) {
+ selectControl1.deactivate();
+ alert("SelectFeature control for clicks deactivated.");
+ } else {
+ selectControl1.activate();
+ alert("SelectFeature control for clicks activated.");
+ }
+ }
+
+ function toggleSelectControl2() {
+ if (selectControl2.active) {
+ selectControl2.deactivate();
+ alert("SelectFeature control for hover deactivated.");
+ } else {
+ selectControl2.activate();
+ alert("SelectFeature control for hover activated.");
+ }
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">Vector Layer ZIndex Test</h1>
+ <div id="map"></div>
+ <p>
+
+ The map includes one base layer (vmap0) and three overlays, namely a vector
+ layer, a fake layer with no images, and a dmsolutions layer. The overlays are
+ added to the map in this order: the vector layer, the fake layer, and the
+ dmsolutions layer. The map also includes a select feature control, which
+ when activated bumped the vector layer z-index to some high value. This
+ makes feature selection work, even though other overlays were added after
+ the vector layer.
+
+ </p>
+ <p>
+
+ If the fake layer is removed from the map (first link below), the vector layer's
+ z-index must not be reset, so the vector layer must not go under the
+ dmsolutions layer and feature selection must continue to function as
+ expected.
+
+ </p>
+ <p>
+
+ If one of the SelectFeature controls is deactivated or activated (second
+ and third link below), the vector layer should change it's position in the
+ layer stack: on top if at least one of the controls is activated, covered
+ by other layers if both are deactivated.
+
+ </p>
+
+ <p>
+ <a href="javascript:removeLayerA()">Remove the fake layer</a>
+ <br/><a href="javascript:toggleSelectControl1()">Toggle the click SelectFeature control's active status</a>
+ <br/><a href="javascript:toggleSelectControl2()">Toggle the hover SelectFeature control's active status</a>
+ </p>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/mice.xml b/misc/openlayers/tests/mice.xml
new file mode 100644
index 0000000..4a001ec
--- /dev/null
+++ b/misc/openlayers/tests/mice.xml
@@ -0,0 +1,156 @@
+<?xml version='1.0' encoding="ISO-8859-1" ?>
+<wfs:FeatureCollection
+ xmlns:bsc="http://www.bsc-eoc.org/bsc"
+ xmlns:wfs="http://www.opengis.net/wfs"
+ xmlns:gml="http://www.opengis.net/gml"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengeospatial.net//wfs/1.0.0/WFS-basic.xsd
+ http://www.bsc-eoc.org/bsc http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp?SERVICE=WFS&amp;VERSION=1.0.0&amp;REQUEST=DescribeFeatureType&amp;TYPENAME=OWLS&amp;OUTPUTFORMAT=XMLSCHEMA">
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-89.817223,45.005555 -74.755001,51.701388</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <gml:featureMember><bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-79.771668,45.891110 -79.771668,45.891110</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-79.771668,45.891110</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.755834,46.365277 -83.755834,46.365277</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:owlname>owl</bsc:owlname>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.755834,46.365277</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.808612,46.175277 -83.808612,46.175277</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.808612,46.175277</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-84.111112,46.309166 -84.111112,46.309166</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-84.111112,46.309166</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.678612,46.821110 -83.678612,46.821110</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.678612,46.821110</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.664445,46.518888 -83.664445,46.518888</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.664445,46.518888</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-80.613334,46.730277 -80.613334,46.730277</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-80.613334,46.730277</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-79.676946,45.428054 -79.676946,45.428054</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-79.676946,45.428054</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.853056,46.236944 -83.853056,46.236944</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.853056,46.236944</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-82.289167,45.896388 -82.289167,45.896388</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-82.289167,45.896388</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+</wfs:FeatureCollection>
+
diff --git a/misc/openlayers/tests/node.js/mockdom.js b/misc/openlayers/tests/node.js/mockdom.js
new file mode 100644
index 0000000..68c088a
--- /dev/null
+++ b/misc/openlayers/tests/node.js/mockdom.js
@@ -0,0 +1,104 @@
+XMLHttpRequest = function() {
+ return {
+ 'open': function() { },
+ 'send': function() { }
+ }
+};
+
+navigator = {
+ 'appName': 'mockdom',
+ 'userAgent': 'mockdom',
+ 'appVersion': '0.1',
+ 'language': 'en',
+ 'userLanguage': 'en'
+}
+
+element = function(type) {
+ type = type || "";
+
+ return {
+ 'childNodes': [],
+ 'className': '',
+ 'tagName': type.toUpperCase(),
+ 'style': {},
+ 'setAttribute': function(attr, value) {
+ this[attr] = value;
+ },
+ 'appendChild': function(element) {
+ if (this.childNodes.length) {
+ this.childNodes[this.childNodes.length - 1].nextSibling = element;
+ } else {
+ this.firstChild = element;
+ }
+ element.parentNode = this;
+ this.childNodes.push(element);
+
+ },
+ 'removeChild': function(element) {
+ var i = this.childNodes.indexOf(element);
+ this.childNodes.splice(i, 1);
+ },
+ 'addEventListener': function() {
+ },
+ 'removeEventListener': function() {
+ },
+ 'getElementsByTagName': function(name, externalList) {
+ var uc = name.toUpperCase();
+ var list = externalList || [];
+ for(var i = 0; i < this.childNodes.length; i++) {
+ if (this.childNodes[i].tagName == uc) {
+ list.push(this.childNodes[i]);
+ }
+ this.childNodes[i].getElementsByTagName(name, list);
+ }
+ return list;
+ },
+ 'getElementById': function(id) {
+ for(var i = 0; i < this.childNodes.length; i++) {
+ if (this.childNodes[i].id == id) {
+ return this.childNodes[i];
+ } else {
+ var elem = this.childNodes[i].getElementById(id);
+ if (elem) {
+ return elem
+ }
+ }
+ }
+ }
+ }
+};
+
+document = element();
+document.createElement = function(type) {
+ return element(type);
+};
+document.createTextNode = function(text) {
+ var e = element("Text");
+ e.innerHTML = text;
+}
+
+document.appendChild(element("head"));
+document.body = element("body");
+document.appendChild(document.body);
+
+window = {
+ 'addEventListener': function() {
+ },
+ 'getSelection': function() {
+ return {
+ collapseToStart: function() {}
+ }
+ },
+ document: document,
+ navigator: navigator,
+ location: {
+ href: '#',
+ port: '',
+ hostname: 'openlayers.org',
+ host: 'openlayers.org',
+ proto: 'https'
+ }
+};
+document.location = window.location;
+
+window.Function = Function;
diff --git a/misc/openlayers/tests/node.js/node-tests.cfg b/misc/openlayers/tests/node.js/node-tests.cfg
new file mode 100644
index 0000000..bc79baa
--- /dev/null
+++ b/misc/openlayers/tests/node.js/node-tests.cfg
@@ -0,0 +1,12 @@
+# This build config is supposed to be used for the units tests with "mode=build"
+
+[first]
+mockdom.js
+[last]
+node.js
+
+[include]
+
+[exclude]
+OpenLayers.js
+Firebug/firebug.js
diff --git a/misc/openlayers/tests/node.js/node.js b/misc/openlayers/tests/node.js/node.js
new file mode 100644
index 0000000..32249d1
--- /dev/null
+++ b/misc/openlayers/tests/node.js/node.js
@@ -0,0 +1 @@
+exports.OpenLayers = OpenLayers;
diff --git a/misc/openlayers/tests/node.js/run-test.js b/misc/openlayers/tests/node.js/run-test.js
new file mode 100644
index 0000000..7b0dd8b
--- /dev/null
+++ b/misc/openlayers/tests/node.js/run-test.js
@@ -0,0 +1,26 @@
+// Requires:
+/// 0. nodejs
+// 1. jsdom installed (npm install jsdom)
+// 2. A build profile with mockdom.js included in [first], and node.js
+// inclded in [last], at ../../build/OpenLayers.js , like node-tests.js.
+// 3. Run with node run-tests.js
+//
+// Missing: integration with a solid node.js testrunner.
+var jsdom = require('jsdom');
+jsdom.env('<html><body></body></html>', function(errors, window) {
+ for (var i in window) {
+ if (i == "console") {
+ continue;
+ }
+ eval(i+"=window['"+i+"'];");
+ }
+ OpenLayers = require("../../build/OpenLayers.js")['OpenLayers'];
+ var map = new OpenLayers.Map(document.createElement("map"));
+ map.addLayer(new OpenLayers.Layer("", {isBaseLayer:true}));
+ map.setCenter(new OpenLayers.LonLat(-71,42), 10);
+ var px = map.getPixelFromLonLat(map.getLonLatFromPixel(new OpenLayers.Pixel(100,100)));
+ console.log(px);
+ var px = map.getLonLatFromPixel(map.getPixelFromLonLat(new OpenLayers.LonLat(10,10)));
+ console.log(px);
+
+});
diff --git a/misc/openlayers/tests/node.js/run.sh b/misc/openlayers/tests/node.js/run.sh
new file mode 100755
index 0000000..1434dd4
--- /dev/null
+++ b/misc/openlayers/tests/node.js/run.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+cp mockdom.js node.js ../../lib
+cp node-tests.cfg ../../build
+cd ../../build
+python build.py -c none node-tests
+cd ../tests/node.js/
+
+node run-test.js
+rm ../../lib/mockdom.js
+rm ../../lib/node.js
diff --git a/misc/openlayers/tests/owls.xml b/misc/openlayers/tests/owls.xml
new file mode 100644
index 0000000..4a001ec
--- /dev/null
+++ b/misc/openlayers/tests/owls.xml
@@ -0,0 +1,156 @@
+<?xml version='1.0' encoding="ISO-8859-1" ?>
+<wfs:FeatureCollection
+ xmlns:bsc="http://www.bsc-eoc.org/bsc"
+ xmlns:wfs="http://www.opengis.net/wfs"
+ xmlns:gml="http://www.opengis.net/gml"
+ xmlns:ogc="http://www.opengis.net/ogc"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengeospatial.net//wfs/1.0.0/WFS-basic.xsd
+ http://www.bsc-eoc.org/bsc http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp?SERVICE=WFS&amp;VERSION=1.0.0&amp;REQUEST=DescribeFeatureType&amp;TYPENAME=OWLS&amp;OUTPUTFORMAT=XMLSCHEMA">
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-89.817223,45.005555 -74.755001,51.701388</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <gml:featureMember><bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-79.771668,45.891110 -79.771668,45.891110</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-79.771668,45.891110</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.755834,46.365277 -83.755834,46.365277</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:owlname>owl</bsc:owlname>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.755834,46.365277</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.808612,46.175277 -83.808612,46.175277</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.808612,46.175277</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-84.111112,46.309166 -84.111112,46.309166</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-84.111112,46.309166</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.678612,46.821110 -83.678612,46.821110</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.678612,46.821110</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.664445,46.518888 -83.664445,46.518888</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.664445,46.518888</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-80.613334,46.730277 -80.613334,46.730277</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-80.613334,46.730277</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-79.676946,45.428054 -79.676946,45.428054</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-79.676946,45.428054</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-83.853056,46.236944 -83.853056,46.236944</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-83.853056,46.236944</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+ <gml:featureMember>
+ <bsc:OWLS>
+ <gml:boundedBy>
+ <gml:Box srsName="EPSG:4326">
+ <gml:coordinates>-82.289167,45.896388 -82.289167,45.896388</gml:coordinates>
+ </gml:Box>
+ </gml:boundedBy>
+ <bsc:msGeometry>
+ <gml:Point srsName="EPSG:4326">
+ <gml:coordinates>-82.289167,45.896388</gml:coordinates>
+ </gml:Point>
+ </bsc:msGeometry>
+ </bsc:OWLS>
+ </gml:featureMember>
+</wfs:FeatureCollection>
+
diff --git a/misc/openlayers/tests/run-tests.html b/misc/openlayers/tests/run-tests.html
new file mode 100644
index 0000000..d1517e4
--- /dev/null
+++ b/misc/openlayers/tests/run-tests.html
@@ -0,0 +1,155 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Run the testsuite</title>
+ <noscript>
+ Javascript is disabled in your browser. This page cannot be
+ displayed correctly without Javascript. Sorry.
+ <br/>
+ If you want to view this page, please change your browser settings
+ so that Javascript is enabled.
+ </noscript>
+ <!--
+
+ Test.AnotherWay version 0.5
+
+ Copyright (c) 2005 Artem Khodush, http://straytree.org
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ -->
+ <link rel="stylesheet" href="Test.AnotherWay.css" />
+ <script type="text/javascript" src="Test.AnotherWay.js"></script>
+ <script type="text/javascript" src="Test.AnotherWay.baseadditions.js"></script>
+ <script type="text/javascript" src="Test.AnotherWay.xml_eq.js"></script>
+ <script type="text/javascript" src="Test.AnotherWay.geom_eq.js"></script>
+ </head>
+ <body>
+ <div id="col1">
+ <div id="col1_header">
+ Test pages:
+ <input id="quickfilter" placeholder="quick filter">
+ </div>
+ <div id="scroller">
+ <table id="testtable">
+ </table>
+ </div>
+ <div id="run_buttons">
+ <input type="button" value=" clear " id="clear_btn" /><input type="button" value=" run all " id="run_all" /><input type="button" value=" run selected " id="run_selected" /><input type="button" value=" unselect all " id="unselect_all" />
+ </div>
+ <div id="running-time">
+ </div>
+ <input type="checkbox" id="dont_close_test_windows" /> do not close windows opened by tests
+ <div id="error">
+ </div>
+ <div id="record_div">
+ <p id="record_not_supported" style="display:none">
+ </p>
+ <p>
+ Record mouse input for the page:
+ </p>
+ <p>
+ <input type="radio" name="record_choose" value="select" checked="checked" />
+ <select id="record_select">
+ <option selected="selected">-- select a page: --</option>
+ </select>
+ </p>
+ <p>
+ <input type="radio" name="record_choose" value="input" /> or enter page url: <input type="text" id="record_input" />
+ </p>
+ <p>
+ <input type="button" value=" record " id="record_start" />
+ </p>
+ </div>
+ </div>
+ <div id="col2">
+ <div id="right_header">
+ <span id="results_count">Results: <span id="total"></span></span>
+ <span id="results_tab" class="active_tab" style="visibility:hidden">Results</span>
+ <span id="debug_tab" class="inactive_tab" style="visibility:hidden">Debug</span>
+ </div>
+ <div id="right_frame">
+ <div id="results">
+ </div>
+ <div id="debug">
+ </div>
+ </div>
+ </div>
+ <span>
+ <iframe id="test_iframe_el" style="display:none" name="test_iframe" onload="Test.AnotherWay._test_page_onload();">
+ </iframe>
+ </span>
+ <span style="display:none">
+ <iframe name="list_iframe" onload="Test.AnotherWay._list_iframe_onload();">
+ </iframe>
+ <!-- record_control div is to be imported into other documents, so all its styles are inline -->-
+ <div id="record_control" style="position:absolute;bottom:0;left:0;margin:0;padding:0.5em;width:22em;height:22em;border:1px solid;background:#ffd;font: normal normal 8pt sans-serif; color:#000; text-align: left">
+ <p style="margin:0 0 0 0; padding:0">
+ &nbsp;<span style="display:none;font-weight:bold;color:#408" id="record_indicator">recording. <span style="font-weight:normal">time: <span id="record_time"></span></span><span id="record_pause_indicator">paused</span></span>
+ </p>
+ <div id="record_cursor_over" style="margin:0;padding:2px;width:14em;height:1.1em;overflow:hidden;float:right;border:1px solid #777;background:#fff;font: normal normal 8pt sans-serif;position:relative;top:3px;color:#000;text-align:left;">
+ &nbsp;
+ </div>
+ <p style="margin:2px 0 0 0; padding:0">
+ cursor is over
+ </p>
+ <p style="margin:8px 0 0 0; padding:0;">
+ keyboard control: press<span id="record_ctrl_key" style="border:1px solid #226;background:#adf;padding:0 0.5em">ctrl</span>
+ -<span id="record_shift_key" style="border:1px solid #226;background:#adf;padding:0 0.5em">shift</span>
+ -
+ </p>
+ <p style="margin:4px 0 0 0; padding:0">
+ <span id="record_s" style="border:1px solid #226;background:#adf;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">s</span>
+ <span id="record_on">to <b>start</b> recording</span>
+ <span id="record_off" style="display:none">to <b>stop</b> recording</span>
+ </p>
+ <p style="margin:4px 0 0 0; padding:0">
+ <span id="record_h" style="border:1px solid #226;background:#adf;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">h</span>
+ <span>to <b>hide/show</b> this window</span>
+ </p>
+ <p style="margin:4px 0 0 0; padding:0">
+ <span id="record_m" style="border:1px solid #226;background:#adf;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">m</span>
+ <span id="record_include_mousemove">to <b>record</b> mousemove</span>
+ <span id="record_omit_mousemove" style="display:none">to <b>omit</b> mousemove</span>
+ </p>
+ <p style="margin:4px 0 0 0; padding:0">
+ <span id="record_p" style="border:1px solid #226;background:#aaa;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">p</span>
+ <span id="record_pause_on">to <b>pause</b> recording</span>
+ <span id="record_pause_off" style="display:none">to <b>continue</b> recording</span>
+ </p>
+ <p style="margin:4px 0 0 0; padding:0">
+ <span id="record_c" style="border:1px solid #226;background:#aaa;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">c</span>
+ <span>to add checkpoint</span>
+ </p>
+ <p style="margin:6px 0 0 0; padding:0">
+ checkpoints:
+ </p>
+ <div id="record_checkpoints" style="position:relative;width:100%;height:6em;overflow:auto;font: normal normal 8pt sans-serif; color:#000; text-align: left">
+ </div>
+ </div>
+ </span>
+ <script>
+ if (/noscroll/.test(location.href)) {
+ document.getElementById('scroller').style.height = 'auto';
+ document.getElementById('right_frame').style.height = 'auto';
+ }
+ </script>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/selenium/remotecontrol/config.cfg b/misc/openlayers/tests/selenium/remotecontrol/config.cfg
new file mode 100644
index 0000000..764bd3d
--- /dev/null
+++ b/misc/openlayers/tests/selenium/remotecontrol/config.cfg
@@ -0,0 +1,48 @@
+[config]
+server=http://openlayers.org/
+url=/dev/tests/run-tests.html?run=all&windows=none
+
+[local_ff]
+host=localhost
+browserCmd=firefox
+comment=Firefox on localhost
+
+[local_safari]
+host=localhost
+browserCmd=safari
+comment=Safari on localhost
+
+#[ie6-winxp]
+#host=208.80.142.184
+#browserCmd=iexploreproxy C:\Program Files\MultipleIEs\IE6\iexplore.exe
+#comment=IE6 on WinXP
+
+[ie7-winxp]
+host=208.80.142.184
+browserCmd=iexploreproxy
+comment=IE7 on WinXP
+
+# Running on alta: debian etch
+[opera-winxp]
+host=208.80.142.184
+browserCmd=opera C:\Program Files\Opera 9\Opera.exe
+comment=Opera on WinXP
+
+# Running on alta: debian etch
+[opera]
+host=208.80.142.140
+browserCmd=opera
+comment=Opera on Debian Etch
+
+# Running on alta: Debian Etch
+[firefox2]
+host=208.80.142.140
+browserCmd=firefox /usr/lib/iceweasel/firefox-bin
+comment=Iceweasel 2 on Debian Etch
+
+# Running on alta: Debian Etch
+[firefox3]
+host=208.80.142.105
+browserCmd=firefox /usr/lib/firefox-3.0.1/firefox-bin
+comment=FF3 on Ubuntu
+
diff --git a/misc/openlayers/tests/selenium/remotecontrol/selenium.py b/misc/openlayers/tests/selenium/remotecontrol/selenium.py
new file mode 100644
index 0000000..e5505ed
--- /dev/null
+++ b/misc/openlayers/tests/selenium/remotecontrol/selenium.py
@@ -0,0 +1,1846 @@
+
+"""
+Copyright 2006 ThoughtWorks, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+__docformat__ = "restructuredtext en"
+
+# This file has been automatically generated via XSL
+
+import httplib
+import urllib
+import re
+
+class selenium:
+ """
+ Defines an object that runs Selenium commands.
+
+ Element Locators
+ ~~~~~~~~~~~~~~~~
+
+ Element Locators tell Selenium which HTML element a command refers to.
+ The format of a locator is:
+
+ \ *locatorType*\ **=**\ \ *argument*
+
+
+ We support the following strategies for locating elements:
+
+
+ * \ **identifier**\ =\ *id*:
+ Select the element with the specified @id attribute. If no match is
+ found, select the first element whose @name attribute is \ *id*.
+ (This is normally the default; see below.)
+ * \ **id**\ =\ *id*:
+ Select the element with the specified @id attribute.
+ * \ **name**\ =\ *name*:
+ Select the first element with the specified @name attribute.
+
+ * username
+ * name=username
+
+
+ The name may optionally be followed by one or more \ *element-filters*, separated from the name by whitespace. If the \ *filterType* is not specified, \ **value**\ is assumed.
+
+ * name=flavour value=chocolate
+
+
+ * \ **dom**\ =\ *javascriptExpression*:
+
+ Find an element by evaluating the specified string. This allows you to traverse the HTML Document Object
+ Model using JavaScript. Note that you must not return a value in this string; simply make it the last expression in the block.
+
+ * dom=document.forms['myForm'].myDropdown
+ * dom=document.images[56]
+ * dom=function foo() { return document.links[1]; }; foo();
+
+
+ * \ **xpath**\ =\ *xpathExpression*:
+ Locate an element using an XPath expression.
+
+ * xpath=//img[@alt='The image alt text']
+ * xpath=//table[@id='table1']//tr[4]/td[2]
+ * xpath=//a[contains(@href,'#id1')]
+ * xpath=//a[contains(@href,'#id1')]/@class
+ * xpath=(//table[@class='stylee'])//th[text()='theHeaderText']/../td
+ * xpath=//input[@name='name2' and @value='yes']
+ * xpath=//\*[text()="right"]
+
+
+ * \ **link**\ =\ *textPattern*:
+ Select the link (anchor) element which contains text matching the
+ specified \ *pattern*.
+
+ * link=The link text
+
+
+ * \ **css**\ =\ *cssSelectorSyntax*:
+ Select the element using css selectors. Please refer to CSS2 selectors, CSS3 selectors for more information. You can also check the TestCssLocators test in the selenium test suite for an example of usage, which is included in the downloaded selenium core package.
+
+ * css=a[href="#id3"]
+ * css=span#firstChild + span
+
+
+ Currently the css selector locator supports all css1, css2 and css3 selectors except namespace in css3, some pseudo classes(:nth-of-type, :nth-last-of-type, :first-of-type, :last-of-type, :only-of-type, :visited, :hover, :active, :focus, :indeterminate) and pseudo elements(::first-line, ::first-letter, ::selection, ::before, ::after).
+
+
+
+
+ Without an explicit locator prefix, Selenium uses the following default
+ strategies:
+
+
+ * \ **dom**\ , for locators starting with "document."
+ * \ **xpath**\ , for locators starting with "//"
+ * \ **identifier**\ , otherwise
+
+ Element Filters
+ ~~~~~~~~~~~~~~~
+
+ Element filters can be used with a locator to refine a list of candidate elements. They are currently used only in the 'name' element-locator.
+
+ Filters look much like locators, ie.
+
+ \ *filterType*\ **=**\ \ *argument*
+
+ Supported element-filters are:
+
+ \ **value=**\ \ *valuePattern*
+
+
+ Matches elements based on their values. This is particularly useful for refining a list of similarly-named toggle-buttons.
+
+ \ **index=**\ \ *index*
+
+
+ Selects a single element based on its position in the list (offset from zero).
+
+ String-match Patterns
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ Various Pattern syntaxes are available for matching string values:
+
+
+ * \ **glob:**\ \ *pattern*:
+ Match a string against a "glob" (aka "wildmat") pattern. "Glob" is a
+ kind of limited regular-expression syntax typically used in command-line
+ shells. In a glob pattern, "\*" represents any sequence of characters, and "?"
+ represents any single character. Glob patterns match against the entire
+ string.
+ * \ **regexp:**\ \ *regexp*:
+ Match a string using a regular-expression. The full power of JavaScript
+ regular-expressions is available.
+ * \ **regexpi:**\ \ *regexpi*:
+ Match a string using a case-insensitive regular-expression.
+ * \ **exact:**\ \ *string*:
+
+ Match a string exactly, verbatim, without any of that fancy wildcard
+ stuff.
+
+
+
+ If no pattern prefix is specified, Selenium assumes that it's a "glob"
+ pattern.
+
+
+
+ For commands that return multiple values (such as verifySelectOptions),
+ the string being matched is a comma-separated list of the return values,
+ where both commas and backslashes in the values are backslash-escaped.
+ When providing a pattern, the optional matching syntax (i.e. glob,
+ regexp, etc.) is specified once, as usual, at the beginning of the
+ pattern.
+
+
+ """
+
+### This part is hard-coded in the XSL
+ def __init__(self, host, port, browserStartCommand, browserURL):
+ self.host = host
+ self.port = port
+ self.browserStartCommand = browserStartCommand
+ self.browserURL = browserURL
+ self.sessionId = None
+
+ def start(self):
+ result = self.get_string("getNewBrowserSession", [self.browserStartCommand, self.browserURL])
+ try:
+ self.sessionId = result
+ except ValueError:
+ raise Exception, result
+
+ def stop(self):
+ self.do_command("testComplete", [])
+ self.sessionId = None
+
+ def do_command(self, verb, args):
+ conn = httplib.HTTPConnection(self.host, self.port)
+ commandString = u'/selenium-server/driver/?cmd=' + urllib.quote_plus(unicode(verb).encode('utf-8'))
+ for i in range(len(args)):
+ commandString = commandString + '&' + unicode(i+1) + '=' + urllib.quote_plus(unicode(args[i]).encode('utf-8'))
+ if (None != self.sessionId):
+ commandString = commandString + "&sessionId=" + unicode(self.sessionId)
+ conn.request("GET", commandString)
+
+ response = conn.getresponse()
+ #print response.status, response.reason
+ data = unicode(response.read(), "UTF-8")
+ result = response.reason
+ #print "Selenium Result: " + repr(data) + "\n\n"
+ if (not data.startswith('OK')):
+ raise Exception, data
+ return data
+
+ def get_string(self, verb, args):
+ result = self.do_command(verb, args)
+ return result[3:]
+
+ def get_string_array(self, verb, args):
+ csv = self.get_string(verb, args)
+ token = ""
+ tokens = []
+ escape = False
+ for i in range(len(csv)):
+ letter = csv[i]
+ if (escape):
+ token = token + letter
+ escape = False
+ continue
+ if (letter == '\\'):
+ escape = True
+ elif (letter == ','):
+ tokens.append(token)
+ token = ""
+ else:
+ token = token + letter
+ tokens.append(token)
+ return tokens
+
+ def get_number(self, verb, args):
+ # Is there something I need to do here?
+ return self.get_string(verb, args)
+
+ def get_number_array(self, verb, args):
+ # Is there something I need to do here?
+ return self.get_string_array(verb, args)
+
+ def get_boolean(self, verb, args):
+ boolstr = self.get_string(verb, args)
+ if ("true" == boolstr):
+ return True
+ if ("false" == boolstr):
+ return False
+ raise ValueError, "result is neither 'true' nor 'false': " + boolstr
+
+ def get_boolean_array(self, verb, args):
+ boolarr = self.get_string_array(verb, args)
+ for i in range(len(boolarr)):
+ if ("true" == boolstr):
+ boolarr[i] = True
+ continue
+ if ("false" == boolstr):
+ boolarr[i] = False
+ continue
+ raise ValueError, "result is neither 'true' nor 'false': " + boolarr[i]
+ return boolarr
+
+
+
+### From here on, everything's auto-generated from XML
+
+
+ def click(self,locator):
+ """
+ Clicks on a link, button, checkbox or radio button. If the click action
+ causes a new page to load (like a link usually does), call
+ waitForPageToLoad.
+
+ 'locator' is an element locator
+ """
+ self.do_command("click", [locator,])
+
+
+ def double_click(self,locator):
+ """
+ Double clicks on a link, button, checkbox or radio button. If the double click action
+ causes a new page to load (like a link usually does), call
+ waitForPageToLoad.
+
+ 'locator' is an element locator
+ """
+ self.do_command("doubleClick", [locator,])
+
+
+ def context_menu(self,locator):
+ """
+ Simulates opening the context menu for the specified element (as might happen if the user "right-clicked" on the element).
+
+ 'locator' is an element locator
+ """
+ self.do_command("contextMenu", [locator,])
+
+
+ def click_at(self,locator,coordString):
+ """
+ Clicks on a link, button, checkbox or radio button. If the click action
+ causes a new page to load (like a link usually does), call
+ waitForPageToLoad.
+
+ 'locator' is an element locator
+ 'coordString' is specifies the x,y position (i.e. - 10,20) of the mouse event relative to the element returned by the locator.
+ """
+ self.do_command("clickAt", [locator,coordString,])
+
+
+ def double_click_at(self,locator,coordString):
+ """
+ Doubleclicks on a link, button, checkbox or radio button. If the action
+ causes a new page to load (like a link usually does), call
+ waitForPageToLoad.
+
+ 'locator' is an element locator
+ 'coordString' is specifies the x,y position (i.e. - 10,20) of the mouse event relative to the element returned by the locator.
+ """
+ self.do_command("doubleClickAt", [locator,coordString,])
+
+
+ def context_menu_at(self,locator,coordString):
+ """
+ Simulates opening the context menu for the specified element (as might happen if the user "right-clicked" on the element).
+
+ 'locator' is an element locator
+ 'coordString' is specifies the x,y position (i.e. - 10,20) of the mouse event relative to the element returned by the locator.
+ """
+ self.do_command("contextMenuAt", [locator,coordString,])
+
+
+ def fire_event(self,locator,eventName):
+ """
+ Explicitly simulate an event, to trigger the corresponding "on\ *event*"
+ handler.
+
+ 'locator' is an element locator
+ 'eventName' is the event name, e.g. "focus" or "blur"
+ """
+ self.do_command("fireEvent", [locator,eventName,])
+
+
+ def focus(self,locator):
+ """
+ Move the focus to the specified element; for example, if the element is an input field, move the cursor to that field.
+
+ 'locator' is an element locator
+ """
+ self.do_command("focus", [locator,])
+
+
+ def key_press(self,locator,keySequence):
+ """
+ Simulates a user pressing and releasing a key.
+
+ 'locator' is an element locator
+ 'keySequence' is Either be a string("\" followed by the numeric keycode of the key to be pressed, normally the ASCII value of that key), or a single character. For example: "w", "\119".
+ """
+ self.do_command("keyPress", [locator,keySequence,])
+
+
+ def shift_key_down(self):
+ """
+ Press the shift key and hold it down until doShiftUp() is called or a new page is loaded.
+
+ """
+ self.do_command("shiftKeyDown", [])
+
+
+ def shift_key_up(self):
+ """
+ Release the shift key.
+
+ """
+ self.do_command("shiftKeyUp", [])
+
+
+ def meta_key_down(self):
+ """
+ Press the meta key and hold it down until doMetaUp() is called or a new page is loaded.
+
+ """
+ self.do_command("metaKeyDown", [])
+
+
+ def meta_key_up(self):
+ """
+ Release the meta key.
+
+ """
+ self.do_command("metaKeyUp", [])
+
+
+ def alt_key_down(self):
+ """
+ Press the alt key and hold it down until doAltUp() is called or a new page is loaded.
+
+ """
+ self.do_command("altKeyDown", [])
+
+
+ def alt_key_up(self):
+ """
+ Release the alt key.
+
+ """
+ self.do_command("altKeyUp", [])
+
+
+ def control_key_down(self):
+ """
+ Press the control key and hold it down until doControlUp() is called or a new page is loaded.
+
+ """
+ self.do_command("controlKeyDown", [])
+
+
+ def control_key_up(self):
+ """
+ Release the control key.
+
+ """
+ self.do_command("controlKeyUp", [])
+
+
+ def key_down(self,locator,keySequence):
+ """
+ Simulates a user pressing a key (without releasing it yet).
+
+ 'locator' is an element locator
+ 'keySequence' is Either be a string("\" followed by the numeric keycode of the key to be pressed, normally the ASCII value of that key), or a single character. For example: "w", "\119".
+ """
+ self.do_command("keyDown", [locator,keySequence,])
+
+
+ def key_up(self,locator,keySequence):
+ """
+ Simulates a user releasing a key.
+
+ 'locator' is an element locator
+ 'keySequence' is Either be a string("\" followed by the numeric keycode of the key to be pressed, normally the ASCII value of that key), or a single character. For example: "w", "\119".
+ """
+ self.do_command("keyUp", [locator,keySequence,])
+
+
+ def mouse_over(self,locator):
+ """
+ Simulates a user hovering a mouse over the specified element.
+
+ 'locator' is an element locator
+ """
+ self.do_command("mouseOver", [locator,])
+
+
+ def mouse_out(self,locator):
+ """
+ Simulates a user moving the mouse pointer away from the specified element.
+
+ 'locator' is an element locator
+ """
+ self.do_command("mouseOut", [locator,])
+
+
+ def mouse_down(self,locator):
+ """
+ Simulates a user pressing the mouse button (without releasing it yet) on
+ the specified element.
+
+ 'locator' is an element locator
+ """
+ self.do_command("mouseDown", [locator,])
+
+
+ def mouse_down_at(self,locator,coordString):
+ """
+ Simulates a user pressing the mouse button (without releasing it yet) at
+ the specified location.
+
+ 'locator' is an element locator
+ 'coordString' is specifies the x,y position (i.e. - 10,20) of the mouse event relative to the element returned by the locator.
+ """
+ self.do_command("mouseDownAt", [locator,coordString,])
+
+
+ def mouse_up(self,locator):
+ """
+ Simulates the event that occurs when the user releases the mouse button (i.e., stops
+ holding the button down) on the specified element.
+
+ 'locator' is an element locator
+ """
+ self.do_command("mouseUp", [locator,])
+
+
+ def mouse_up_at(self,locator,coordString):
+ """
+ Simulates the event that occurs when the user releases the mouse button (i.e., stops
+ holding the button down) at the specified location.
+
+ 'locator' is an element locator
+ 'coordString' is specifies the x,y position (i.e. - 10,20) of the mouse event relative to the element returned by the locator.
+ """
+ self.do_command("mouseUpAt", [locator,coordString,])
+
+
+ def mouse_move(self,locator):
+ """
+ Simulates a user pressing the mouse button (without releasing it yet) on
+ the specified element.
+
+ 'locator' is an element locator
+ """
+ self.do_command("mouseMove", [locator,])
+
+
+ def mouse_move_at(self,locator,coordString):
+ """
+ Simulates a user pressing the mouse button (without releasing it yet) on
+ the specified element.
+
+ 'locator' is an element locator
+ 'coordString' is specifies the x,y position (i.e. - 10,20) of the mouse event relative to the element returned by the locator.
+ """
+ self.do_command("mouseMoveAt", [locator,coordString,])
+
+
+ def type(self,locator,value):
+ """
+ Sets the value of an input field, as though you typed it in.
+
+
+ Can also be used to set the value of combo boxes, check boxes, etc. In these cases,
+ value should be the value of the option selected, not the visible text.
+
+
+ 'locator' is an element locator
+ 'value' is the value to type
+ """
+ self.do_command("type", [locator,value,])
+
+
+ def type_keys(self,locator,value):
+ """
+ Simulates keystroke events on the specified element, as though you typed the value key-by-key.
+
+
+ This is a convenience method for calling keyDown, keyUp, keyPress for every character in the specified string;
+ this is useful for dynamic UI widgets (like auto-completing combo boxes) that require explicit key events.
+
+ Unlike the simple "type" command, which forces the specified value into the page directly, this command
+ may or may not have any visible effect, even in cases where typing keys would normally have a visible effect.
+ For example, if you use "typeKeys" on a form element, you may or may not see the results of what you typed in
+ the field.
+
+ In some cases, you may need to use the simple "type" command to set the value of the field and then the "typeKeys" command to
+ send the keystroke events corresponding to what you just typed.
+
+
+ 'locator' is an element locator
+ 'value' is the value to type
+ """
+ self.do_command("typeKeys", [locator,value,])
+
+
+ def set_speed(self,value):
+ """
+ Set execution speed (i.e., set the millisecond length of a delay which will follow each selenium operation). By default, there is no such delay, i.e.,
+ the delay is 0 milliseconds.
+
+ 'value' is the number of milliseconds to pause after operation
+ """
+ self.do_command("setSpeed", [value,])
+
+
+ def get_speed(self):
+ """
+ Get execution speed (i.e., get the millisecond length of the delay following each selenium operation). By default, there is no such delay, i.e.,
+ the delay is 0 milliseconds.
+
+ See also setSpeed.
+
+ """
+ return self.get_string("getSpeed", [])
+
+
+ def check(self,locator):
+ """
+ Check a toggle-button (checkbox/radio)
+
+ 'locator' is an element locator
+ """
+ self.do_command("check", [locator,])
+
+
+ def uncheck(self,locator):
+ """
+ Uncheck a toggle-button (checkbox/radio)
+
+ 'locator' is an element locator
+ """
+ self.do_command("uncheck", [locator,])
+
+
+ def select(self,selectLocator,optionLocator):
+ """
+ Select an option from a drop-down using an option locator.
+
+
+
+ Option locators provide different ways of specifying options of an HTML
+ Select element (e.g. for selecting a specific option, or for asserting
+ that the selected option satisfies a specification). There are several
+ forms of Select Option Locator.
+
+
+ * \ **label**\ =\ *labelPattern*:
+ matches options based on their labels, i.e. the visible text. (This
+ is the default.)
+
+ * label=regexp:^[Oo]ther
+
+
+ * \ **value**\ =\ *valuePattern*:
+ matches options based on their values.
+
+ * value=other
+
+
+ * \ **id**\ =\ *id*:
+
+ matches options based on their ids.
+
+ * id=option1
+
+
+ * \ **index**\ =\ *index*:
+ matches an option based on its index (offset from zero).
+
+ * index=2
+
+
+
+
+
+ If no option locator prefix is provided, the default behaviour is to match on \ **label**\ .
+
+
+
+ 'selectLocator' is an element locator identifying a drop-down menu
+ 'optionLocator' is an option locator (a label by default)
+ """
+ self.do_command("select", [selectLocator,optionLocator,])
+
+
+ def add_selection(self,locator,optionLocator):
+ """
+ Add a selection to the set of selected options in a multi-select element using an option locator.
+
+ @see #doSelect for details of option locators
+
+ 'locator' is an element locator identifying a multi-select box
+ 'optionLocator' is an option locator (a label by default)
+ """
+ self.do_command("addSelection", [locator,optionLocator,])
+
+
+ def remove_selection(self,locator,optionLocator):
+ """
+ Remove a selection from the set of selected options in a multi-select element using an option locator.
+
+ @see #doSelect for details of option locators
+
+ 'locator' is an element locator identifying a multi-select box
+ 'optionLocator' is an option locator (a label by default)
+ """
+ self.do_command("removeSelection", [locator,optionLocator,])
+
+
+ def remove_all_selections(self,locator):
+ """
+ Unselects all of the selected options in a multi-select element.
+
+ 'locator' is an element locator identifying a multi-select box
+ """
+ self.do_command("removeAllSelections", [locator,])
+
+
+ def submit(self,formLocator):
+ """
+ Submit the specified form. This is particularly useful for forms without
+ submit buttons, e.g. single-input "Search" forms.
+
+ 'formLocator' is an element locator for the form you want to submit
+ """
+ self.do_command("submit", [formLocator,])
+
+
+ def open(self,url):
+ """
+ Opens an URL in the test frame. This accepts both relative and absolute
+ URLs.
+
+ The "open" command waits for the page to load before proceeding,
+ ie. the "AndWait" suffix is implicit.
+
+ \ *Note*: The URL must be on the same domain as the runner HTML
+ due to security restrictions in the browser (Same Origin Policy). If you
+ need to open an URL on another domain, use the Selenium Server to start a
+ new browser session on that domain.
+
+ 'url' is the URL to open; may be relative or absolute
+ """
+ self.do_command("open", [url,])
+
+
+ def open_window(self,url,windowID):
+ """
+ Opens a popup window (if a window with that ID isn't already open).
+ After opening the window, you'll need to select it using the selectWindow
+ command.
+
+
+ This command can also be a useful workaround for bug SEL-339. In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example).
+ In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using
+ an empty (blank) url, like this: openWindow("", "myFunnyWindow").
+
+
+ 'url' is the URL to open, which can be blank
+ 'windowID' is the JavaScript window ID of the window to select
+ """
+ self.do_command("openWindow", [url,windowID,])
+
+
+ def select_window(self,windowID):
+ """
+ Selects a popup window using a window locator; once a popup window has been selected, all
+ commands go to that window. To select the main window again, use null
+ as the target.
+
+
+
+
+ Window locators provide different ways of specifying the window object:
+ by title, by internal JavaScript "name," or by JavaScript variable.
+
+
+ * \ **title**\ =\ *My Special Window*:
+ Finds the window using the text that appears in the title bar. Be careful;
+ two windows can share the same title. If that happens, this locator will
+ just pick one.
+
+ * \ **name**\ =\ *myWindow*:
+ Finds the window using its internal JavaScript "name" property. This is the second
+ parameter "windowName" passed to the JavaScript method window.open(url, windowName, windowFeatures, replaceFlag)
+ (which Selenium intercepts).
+
+ * \ **var**\ =\ *variableName*:
+ Some pop-up windows are unnamed (anonymous), but are associated with a JavaScript variable name in the current
+ application window, e.g. "window.foo = window.open(url);". In those cases, you can open the window using
+ "var=foo".
+
+
+
+
+ If no window locator prefix is provided, we'll try to guess what you mean like this:
+
+ 1.) if windowID is null, (or the string "null") then it is assumed the user is referring to the original window instantiated by the browser).
+
+ 2.) if the value of the "windowID" parameter is a JavaScript variable name in the current application window, then it is assumed
+ that this variable contains the return value from a call to the JavaScript window.open() method.
+
+ 3.) Otherwise, selenium looks in a hash it maintains that maps string names to window "names".
+
+ 4.) If \ *that* fails, we'll try looping over all of the known windows to try to find the appropriate "title".
+ Since "title" is not necessarily unique, this may have unexpected behavior.
+
+ If you're having trouble figuring out the name of a window that you want to manipulate, look at the Selenium log messages
+ which identify the names of windows created via window.open (and therefore intercepted by Selenium). You will see messages
+ like the following for each window as it is opened:
+
+ ``debug: window.open call intercepted; window ID (which you can use with selectWindow()) is "myNewWindow"``
+
+ In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example).
+ (This is bug SEL-339.) In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using
+ an empty (blank) url, like this: openWindow("", "myFunnyWindow").
+
+
+ 'windowID' is the JavaScript window ID of the window to select
+ """
+ self.do_command("selectWindow", [windowID,])
+
+
+ def select_frame(self,locator):
+ """
+ Selects a frame within the current window. (You may invoke this command
+ multiple times to select nested frames.) To select the parent frame, use
+ "relative=parent" as a locator; to select the top frame, use "relative=top".
+ You can also select a frame by its 0-based index number; select the first frame with
+ "index=0", or the third frame with "index=2".
+
+
+ You may also use a DOM expression to identify the frame you want directly,
+ like this: ``dom=frames["main"].frames["subframe"]``
+
+
+ 'locator' is an element locator identifying a frame or iframe
+ """
+ self.do_command("selectFrame", [locator,])
+
+
+ def get_whether_this_frame_match_frame_expression(self,currentFrameString,target):
+ """
+ Determine whether current/locator identify the frame containing this running code.
+
+
+ This is useful in proxy injection mode, where this code runs in every
+ browser frame and window, and sometimes the selenium server needs to identify
+ the "current" frame. In this case, when the test calls selectFrame, this
+ routine is called for each frame to figure out which one has been selected.
+ The selected frame will return true, while all others will return false.
+
+
+ 'currentFrameString' is starting frame
+ 'target' is new frame (which might be relative to the current one)
+ """
+ return self.get_boolean("getWhetherThisFrameMatchFrameExpression", [currentFrameString,target,])
+
+
+ def get_whether_this_window_match_window_expression(self,currentWindowString,target):
+ """
+ Determine whether currentWindowString plus target identify the window containing this running code.
+
+
+ This is useful in proxy injection mode, where this code runs in every
+ browser frame and window, and sometimes the selenium server needs to identify
+ the "current" window. In this case, when the test calls selectWindow, this
+ routine is called for each window to figure out which one has been selected.
+ The selected window will return true, while all others will return false.
+
+
+ 'currentWindowString' is starting window
+ 'target' is new window (which might be relative to the current one, e.g., "_parent")
+ """
+ return self.get_boolean("getWhetherThisWindowMatchWindowExpression", [currentWindowString,target,])
+
+
+ def wait_for_pop_up(self,windowID,timeout):
+ """
+ Waits for a popup window to appear and load up.
+
+ 'windowID' is the JavaScript window "name" of the window that will appear (not the text of the title bar)
+ 'timeout' is a timeout in milliseconds, after which the action will return with an error
+ """
+ self.do_command("waitForPopUp", [windowID,timeout,])
+
+
+ def choose_cancel_on_next_confirmation(self):
+ """
+ By default, Selenium's overridden window.confirm() function will
+ return true, as if the user had manually clicked OK; after running
+ this command, the next call to confirm() will return false, as if
+ the user had clicked Cancel. Selenium will then resume using the
+ default behavior for future confirmations, automatically returning
+ true (OK) unless/until you explicitly call this command for each
+ confirmation.
+
+ """
+ self.do_command("chooseCancelOnNextConfirmation", [])
+
+
+ def choose_ok_on_next_confirmation(self):
+ """
+ Undo the effect of calling chooseCancelOnNextConfirmation. Note
+ that Selenium's overridden window.confirm() function will normally automatically
+ return true, as if the user had manually clicked OK, so you shouldn't
+ need to use this command unless for some reason you need to change
+ your mind prior to the next confirmation. After any confirmation, Selenium will resume using the
+ default behavior for future confirmations, automatically returning
+ true (OK) unless/until you explicitly call chooseCancelOnNextConfirmation for each
+ confirmation.
+
+ """
+ self.do_command("chooseOkOnNextConfirmation", [])
+
+
+ def answer_on_next_prompt(self,answer):
+ """
+ Instructs Selenium to return the specified answer string in response to
+ the next JavaScript prompt [window.prompt()].
+
+ 'answer' is the answer to give in response to the prompt pop-up
+ """
+ self.do_command("answerOnNextPrompt", [answer,])
+
+
+ def go_back(self):
+ """
+ Simulates the user clicking the "back" button on their browser.
+
+ """
+ self.do_command("goBack", [])
+
+
+ def refresh(self):
+ """
+ Simulates the user clicking the "Refresh" button on their browser.
+
+ """
+ self.do_command("refresh", [])
+
+
+ def close(self):
+ """
+ Simulates the user clicking the "close" button in the titlebar of a popup
+ window or tab.
+
+ """
+ self.do_command("close", [])
+
+
+ def is_alert_present(self):
+ """
+ Has an alert occurred?
+
+
+
+ This function never throws an exception
+
+
+
+ """
+ return self.get_boolean("isAlertPresent", [])
+
+
+ def is_prompt_present(self):
+ """
+ Has a prompt occurred?
+
+
+
+ This function never throws an exception
+
+
+
+ """
+ return self.get_boolean("isPromptPresent", [])
+
+
+ def is_confirmation_present(self):
+ """
+ Has confirm() been called?
+
+
+
+ This function never throws an exception
+
+
+
+ """
+ return self.get_boolean("isConfirmationPresent", [])
+
+
+ def get_alert(self):
+ """
+ Retrieves the message of a JavaScript alert generated during the previous action, or fail if there were no alerts.
+
+
+ Getting an alert has the same effect as manually clicking OK. If an
+ alert is generated but you do not get/verify it, the next Selenium action
+ will fail.
+
+ NOTE: under Selenium, JavaScript alerts will NOT pop up a visible alert
+ dialog.
+
+ NOTE: Selenium does NOT support JavaScript alerts that are generated in a
+ page's onload() event handler. In this case a visible dialog WILL be
+ generated and Selenium will hang until someone manually clicks OK.
+
+
+ """
+ return self.get_string("getAlert", [])
+
+
+ def get_confirmation(self):
+ """
+ Retrieves the message of a JavaScript confirmation dialog generated during
+ the previous action.
+
+
+
+ By default, the confirm function will return true, having the same effect
+ as manually clicking OK. This can be changed by prior execution of the
+ chooseCancelOnNextConfirmation command. If an confirmation is generated
+ but you do not get/verify it, the next Selenium action will fail.
+
+
+
+ NOTE: under Selenium, JavaScript confirmations will NOT pop up a visible
+ dialog.
+
+
+
+ NOTE: Selenium does NOT support JavaScript confirmations that are
+ generated in a page's onload() event handler. In this case a visible
+ dialog WILL be generated and Selenium will hang until you manually click
+ OK.
+
+
+
+ """
+ return self.get_string("getConfirmation", [])
+
+
+ def get_prompt(self):
+ """
+ Retrieves the message of a JavaScript question prompt dialog generated during
+ the previous action.
+
+
+ Successful handling of the prompt requires prior execution of the
+ answerOnNextPrompt command. If a prompt is generated but you
+ do not get/verify it, the next Selenium action will fail.
+
+ NOTE: under Selenium, JavaScript prompts will NOT pop up a visible
+ dialog.
+
+ NOTE: Selenium does NOT support JavaScript prompts that are generated in a
+ page's onload() event handler. In this case a visible dialog WILL be
+ generated and Selenium will hang until someone manually clicks OK.
+
+
+ """
+ return self.get_string("getPrompt", [])
+
+
+ def get_location(self):
+ """
+ Gets the absolute URL of the current page.
+
+ """
+ return self.get_string("getLocation", [])
+
+
+ def get_title(self):
+ """
+ Gets the title of the current page.
+
+ """
+ return self.get_string("getTitle", [])
+
+
+ def get_body_text(self):
+ """
+ Gets the entire text of the page.
+
+ """
+ return self.get_string("getBodyText", [])
+
+
+ def get_value(self,locator):
+ """
+ Gets the (whitespace-trimmed) value of an input field (or anything else with a value parameter).
+ For checkbox/radio elements, the value will be "on" or "off" depending on
+ whether the element is checked or not.
+
+ 'locator' is an element locator
+ """
+ return self.get_string("getValue", [locator,])
+
+
+ def get_text(self,locator):
+ """
+ Gets the text of an element. This works for any element that contains
+ text. This command uses either the textContent (Mozilla-like browsers) or
+ the innerText (IE-like browsers) of the element, which is the rendered
+ text shown to the user.
+
+ 'locator' is an element locator
+ """
+ return self.get_string("getText", [locator,])
+
+
+ def highlight(self,locator):
+ """
+ Briefly changes the backgroundColor of the specified element yellow. Useful for debugging.
+
+ 'locator' is an element locator
+ """
+ self.do_command("highlight", [locator,])
+
+
+ def get_eval(self,script):
+ """
+ Gets the result of evaluating the specified JavaScript snippet. The snippet may
+ have multiple lines, but only the result of the last line will be returned.
+
+
+ Note that, by default, the snippet will run in the context of the "selenium"
+ object itself, so ``this`` will refer to the Selenium object. Use ``window`` to
+ refer to the window of your application, e.g. ``window.document.getElementById('foo')``
+
+ If you need to use
+ a locator to refer to a single element in your application page, you can
+ use ``this.browserbot.findElement("id=foo")`` where "id=foo" is your locator.
+
+
+ 'script' is the JavaScript snippet to run
+ """
+ return self.get_string("getEval", [script,])
+
+
+ def is_checked(self,locator):
+ """
+ Gets whether a toggle-button (checkbox/radio) is checked. Fails if the specified element doesn't exist or isn't a toggle-button.
+
+ 'locator' is an element locator pointing to a checkbox or radio button
+ """
+ return self.get_boolean("isChecked", [locator,])
+
+
+ def get_table(self,tableCellAddress):
+ """
+ Gets the text from a cell of a table. The cellAddress syntax
+ tableLocator.row.column, where row and column start at 0.
+
+ 'tableCellAddress' is a cell address, e.g. "foo.1.4"
+ """
+ return self.get_string("getTable", [tableCellAddress,])
+
+
+ def get_selected_labels(self,selectLocator):
+ """
+ Gets all option labels (visible text) for selected options in the specified select or multi-select element.
+
+ 'selectLocator' is an element locator identifying a drop-down menu
+ """
+ return self.get_string_array("getSelectedLabels", [selectLocator,])
+
+
+ def get_selected_label(self,selectLocator):
+ """
+ Gets option label (visible text) for selected option in the specified select element.
+
+ 'selectLocator' is an element locator identifying a drop-down menu
+ """
+ return self.get_string("getSelectedLabel", [selectLocator,])
+
+
+ def get_selected_values(self,selectLocator):
+ """
+ Gets all option values (value attributes) for selected options in the specified select or multi-select element.
+
+ 'selectLocator' is an element locator identifying a drop-down menu
+ """
+ return self.get_string_array("getSelectedValues", [selectLocator,])
+
+
+ def get_selected_value(self,selectLocator):
+ """
+ Gets option value (value attribute) for selected option in the specified select element.
+
+ 'selectLocator' is an element locator identifying a drop-down menu
+ """
+ return self.get_string("getSelectedValue", [selectLocator,])
+
+
+ def get_selected_indexes(self,selectLocator):
+ """
+ Gets all option indexes (option number, starting at 0) for selected options in the specified select or multi-select element.
+
+ 'selectLocator' is an element locator identifying a drop-down menu
+ """
+ return self.get_string_array("getSelectedIndexes", [selectLocator,])
+
+
+ def get_selected_index(self,selectLocator):
+ """
+ Gets option index (option number, starting at 0) for selected option in the specified select element.
+
+ 'selectLocator' is an element locator identifying a drop-down menu
+ """
+ return self.get_string("getSelectedIndex", [selectLocator,])
+
+
+ def get_selected_ids(self,selectLocator):
+ """
+ Gets all option element IDs for selected options in the specified select or multi-select element.
+
+ 'selectLocator' is an element locator identifying a drop-down menu
+ """
+ return self.get_string_array("getSelectedIds", [selectLocator,])
+
+
+ def get_selected_id(self,selectLocator):
+ """
+ Gets option element ID for selected option in the specified select element.
+
+ 'selectLocator' is an element locator identifying a drop-down menu
+ """
+ return self.get_string("getSelectedId", [selectLocator,])
+
+
+ def is_something_selected(self,selectLocator):
+ """
+ Determines whether some option in a drop-down menu is selected.
+
+ 'selectLocator' is an element locator identifying a drop-down menu
+ """
+ return self.get_boolean("isSomethingSelected", [selectLocator,])
+
+
+ def get_select_options(self,selectLocator):
+ """
+ Gets all option labels in the specified select drop-down.
+
+ 'selectLocator' is an element locator identifying a drop-down menu
+ """
+ return self.get_string_array("getSelectOptions", [selectLocator,])
+
+
+ def get_attribute(self,attributeLocator):
+ """
+ Gets the value of an element attribute. The value of the attribute may
+ differ across browsers (this is the case for the "style" attribute, for
+ example).
+
+ 'attributeLocator' is an element locator followed by an @ sign and then the name of the attribute, e.g. "foo@bar"
+ """
+ return self.get_string("getAttribute", [attributeLocator,])
+
+
+ def is_text_present(self,pattern):
+ """
+ Verifies that the specified text pattern appears somewhere on the rendered page shown to the user.
+
+ 'pattern' is a pattern to match with the text of the page
+ """
+ return self.get_boolean("isTextPresent", [pattern,])
+
+
+ def is_element_present(self,locator):
+ """
+ Verifies that the specified element is somewhere on the page.
+
+ 'locator' is an element locator
+ """
+ return self.get_boolean("isElementPresent", [locator,])
+
+
+ def is_visible(self,locator):
+ """
+ Determines if the specified element is visible. An
+ element can be rendered invisible by setting the CSS "visibility"
+ property to "hidden", or the "display" property to "none", either for the
+ element itself or one if its ancestors. This method will fail if
+ the element is not present.
+
+ 'locator' is an element locator
+ """
+ return self.get_boolean("isVisible", [locator,])
+
+
+ def is_editable(self,locator):
+ """
+ Determines whether the specified input element is editable, ie hasn't been disabled.
+ This method will fail if the specified element isn't an input element.
+
+ 'locator' is an element locator
+ """
+ return self.get_boolean("isEditable", [locator,])
+
+
+ def get_all_buttons(self):
+ """
+ Returns the IDs of all buttons on the page.
+
+
+ If a given button has no ID, it will appear as "" in this array.
+
+
+ """
+ return self.get_string_array("getAllButtons", [])
+
+
+ def get_all_links(self):
+ """
+ Returns the IDs of all links on the page.
+
+
+ If a given link has no ID, it will appear as "" in this array.
+
+
+ """
+ return self.get_string_array("getAllLinks", [])
+
+
+ def get_all_fields(self):
+ """
+ Returns the IDs of all input fields on the page.
+
+
+ If a given field has no ID, it will appear as "" in this array.
+
+
+ """
+ return self.get_string_array("getAllFields", [])
+
+
+ def get_attribute_from_all_windows(self,attributeName):
+ """
+ Returns every instance of some attribute from all known windows.
+
+ 'attributeName' is name of an attribute on the windows
+ """
+ return self.get_string_array("getAttributeFromAllWindows", [attributeName,])
+
+
+ def dragdrop(self,locator,movementsString):
+ """
+ deprecated - use dragAndDrop instead
+
+ 'locator' is an element locator
+ 'movementsString' is offset in pixels from the current location to which the element should be moved, e.g., "+70,-300"
+ """
+ self.do_command("dragdrop", [locator,movementsString,])
+
+
+ def set_mouse_speed(self,pixels):
+ """
+ Configure the number of pixels between "mousemove" events during dragAndDrop commands (default=10).
+
+ Setting this value to 0 means that we'll send a "mousemove" event to every single pixel
+ in between the start location and the end location; that can be very slow, and may
+ cause some browsers to force the JavaScript to timeout.
+
+ If the mouse speed is greater than the distance between the two dragged objects, we'll
+ just send one "mousemove" at the start location and then one final one at the end location.
+
+
+ 'pixels' is the number of pixels between "mousemove" events
+ """
+ self.do_command("setMouseSpeed", [pixels,])
+
+
+ def get_mouse_speed(self):
+ """
+ Returns the number of pixels between "mousemove" events during dragAndDrop commands (default=10).
+
+ """
+ return self.get_number("getMouseSpeed", [])
+
+
+ def drag_and_drop(self,locator,movementsString):
+ """
+ Drags an element a certain distance and then drops it
+
+ 'locator' is an element locator
+ 'movementsString' is offset in pixels from the current location to which the element should be moved, e.g., "+70,-300"
+ """
+ self.do_command("dragAndDrop", [locator,movementsString,])
+
+
+ def drag_and_drop_to_object(self,locatorOfObjectToBeDragged,locatorOfDragDestinationObject):
+ """
+ Drags an element and drops it on another element
+
+ 'locatorOfObjectToBeDragged' is an element to be dragged
+ 'locatorOfDragDestinationObject' is an element whose location (i.e., whose center-most pixel) will be the point where locatorOfObjectToBeDragged is dropped
+ """
+ self.do_command("dragAndDropToObject", [locatorOfObjectToBeDragged,locatorOfDragDestinationObject,])
+
+
+ def window_focus(self):
+ """
+ Gives focus to the currently selected window
+
+ """
+ self.do_command("windowFocus", [])
+
+
+ def window_maximize(self):
+ """
+ Resize currently selected window to take up the entire screen
+
+ """
+ self.do_command("windowMaximize", [])
+
+
+ def get_all_window_ids(self):
+ """
+ Returns the IDs of all windows that the browser knows about.
+
+ """
+ return self.get_string_array("getAllWindowIds", [])
+
+
+ def get_all_window_names(self):
+ """
+ Returns the names of all windows that the browser knows about.
+
+ """
+ return self.get_string_array("getAllWindowNames", [])
+
+
+ def get_all_window_titles(self):
+ """
+ Returns the titles of all windows that the browser knows about.
+
+ """
+ return self.get_string_array("getAllWindowTitles", [])
+
+
+ def get_html_source(self):
+ """
+ Returns the entire HTML source between the opening and
+ closing "html" tags.
+
+ """
+ return self.get_string("getHtmlSource", [])
+
+
+ def set_cursor_position(self,locator,position):
+ """
+ Moves the text cursor to the specified position in the given input element or textarea.
+ This method will fail if the specified element isn't an input element or textarea.
+
+ 'locator' is an element locator pointing to an input element or textarea
+ 'position' is the numerical position of the cursor in the field; position should be 0 to move the position to the beginning of the field. You can also set the cursor to -1 to move it to the end of the field.
+ """
+ self.do_command("setCursorPosition", [locator,position,])
+
+
+ def get_element_index(self,locator):
+ """
+ Get the relative index of an element to its parent (starting from 0). The comment node and empty text node
+ will be ignored.
+
+ 'locator' is an element locator pointing to an element
+ """
+ return self.get_number("getElementIndex", [locator,])
+
+
+ def is_ordered(self,locator1,locator2):
+ """
+ Check if these two elements have same parent and are ordered siblings in the DOM. Two same elements will
+ not be considered ordered.
+
+ 'locator1' is an element locator pointing to the first element
+ 'locator2' is an element locator pointing to the second element
+ """
+ return self.get_boolean("isOrdered", [locator1,locator2,])
+
+
+ def get_element_position_left(self,locator):
+ """
+ Retrieves the horizontal position of an element
+
+ 'locator' is an element locator pointing to an element OR an element itself
+ """
+ return self.get_number("getElementPositionLeft", [locator,])
+
+
+ def get_element_position_top(self,locator):
+ """
+ Retrieves the vertical position of an element
+
+ 'locator' is an element locator pointing to an element OR an element itself
+ """
+ return self.get_number("getElementPositionTop", [locator,])
+
+
+ def get_element_width(self,locator):
+ """
+ Retrieves the width of an element
+
+ 'locator' is an element locator pointing to an element
+ """
+ return self.get_number("getElementWidth", [locator,])
+
+
+ def get_element_height(self,locator):
+ """
+ Retrieves the height of an element
+
+ 'locator' is an element locator pointing to an element
+ """
+ return self.get_number("getElementHeight", [locator,])
+
+
+ def get_cursor_position(self,locator):
+ """
+ Retrieves the text cursor position in the given input element or textarea; beware, this may not work perfectly on all browsers.
+
+
+ Specifically, if the cursor/selection has been cleared by JavaScript, this command will tend to
+ return the position of the last location of the cursor, even though the cursor is now gone from the page. This is filed as SEL-243.
+
+ This method will fail if the specified element isn't an input element or textarea, or there is no cursor in the element.
+
+ 'locator' is an element locator pointing to an input element or textarea
+ """
+ return self.get_number("getCursorPosition", [locator,])
+
+
+ def get_expression(self,expression):
+ """
+ Returns the specified expression.
+
+
+ This is useful because of JavaScript preprocessing.
+ It is used to generate commands like assertExpression and waitForExpression.
+
+
+ 'expression' is the value to return
+ """
+ return self.get_string("getExpression", [expression,])
+
+
+ def get_xpath_count(self,xpath):
+ """
+ Returns the number of nodes that match the specified xpath, eg. "//table" would give
+ the number of tables.
+
+ 'xpath' is the xpath expression to evaluate. do NOT wrap this expression in a 'count()' function; we will do that for you.
+ """
+ return self.get_number("getXpathCount", [xpath,])
+
+
+ def assign_id(self,locator,identifier):
+ """
+ Temporarily sets the "id" attribute of the specified element, so you can locate it in the future
+ using its ID rather than a slow/complicated XPath. This ID will disappear once the page is
+ reloaded.
+
+ 'locator' is an element locator pointing to an element
+ 'identifier' is a string to be used as the ID of the specified element
+ """
+ self.do_command("assignId", [locator,identifier,])
+
+
+ def allow_native_xpath(self,allow):
+ """
+ Specifies whether Selenium should use the native in-browser implementation
+ of XPath (if any native version is available); if you pass "false" to
+ this function, we will always use our pure-JavaScript xpath library.
+ Using the pure-JS xpath library can improve the consistency of xpath
+ element locators between different browser vendors, but the pure-JS
+ version is much slower than the native implementations.
+
+ 'allow' is boolean, true means we'll prefer to use native XPath; false means we'll only use JS XPath
+ """
+ self.do_command("allowNativeXpath", [allow,])
+
+
+ def ignore_attributes_without_value(self,ignore):
+ """
+ Specifies whether Selenium will ignore xpath attributes that have no
+ value, i.e. are the empty string, when using the non-native xpath
+ evaluation engine. You'd want to do this for performance reasons in IE.
+ However, this could break certain xpaths, for example an xpath that looks
+ for an attribute whose value is NOT the empty string.
+
+ The hope is that such xpaths are relatively rare, but the user should
+ have the option of using them. Note that this only influences xpath
+ evaluation when using the ajaxslt engine (i.e. not "javascript-xpath").
+
+ 'ignore' is boolean, true means we'll ignore attributes without value at the expense of xpath "correctness"; false means we'll sacrifice speed for correctness.
+ """
+ self.do_command("ignoreAttributesWithoutValue", [ignore,])
+
+
+ def wait_for_condition(self,script,timeout):
+ """
+ Runs the specified JavaScript snippet repeatedly until it evaluates to "true".
+ The snippet may have multiple lines, but only the result of the last line
+ will be considered.
+
+
+ Note that, by default, the snippet will be run in the runner's test window, not in the window
+ of your application. To get the window of your application, you can use
+ the JavaScript snippet ``selenium.browserbot.getCurrentWindow()``, and then
+ run your JavaScript in there
+
+
+ 'script' is the JavaScript snippet to run
+ 'timeout' is a timeout in milliseconds, after which this command will return with an error
+ """
+ self.do_command("waitForCondition", [script,timeout,])
+
+
+ def set_timeout(self,timeout):
+ """
+ Specifies the amount of time that Selenium will wait for actions to complete.
+
+
+ Actions that require waiting include "open" and the "waitFor\*" actions.
+
+ The default timeout is 30 seconds.
+
+ 'timeout' is a timeout in milliseconds, after which the action will return with an error
+ """
+ self.do_command("setTimeout", [timeout,])
+
+
+ def wait_for_page_to_load(self,timeout):
+ """
+ Waits for a new page to load.
+
+
+ You can use this command instead of the "AndWait" suffixes, "clickAndWait", "selectAndWait", "typeAndWait" etc.
+ (which are only available in the JS API).
+
+ Selenium constantly keeps track of new pages loading, and sets a "newPageLoaded"
+ flag when it first notices a page load. Running any other Selenium command after
+ turns the flag to false. Hence, if you want to wait for a page to load, you must
+ wait immediately after a Selenium command that caused a page-load.
+
+
+ 'timeout' is a timeout in milliseconds, after which this command will return with an error
+ """
+ self.do_command("waitForPageToLoad", [timeout,])
+
+
+ def wait_for_frame_to_load(self,frameAddress,timeout):
+ """
+ Waits for a new frame to load.
+
+
+ Selenium constantly keeps track of new pages and frames loading,
+ and sets a "newPageLoaded" flag when it first notices a page load.
+
+
+ See waitForPageToLoad for more information.
+
+ 'frameAddress' is FrameAddress from the server side
+ 'timeout' is a timeout in milliseconds, after which this command will return with an error
+ """
+ self.do_command("waitForFrameToLoad", [frameAddress,timeout,])
+
+
+ def get_cookie(self):
+ """
+ Return all cookies of the current page under test.
+
+ """
+ return self.get_string("getCookie", [])
+
+
+ def get_cookie_by_name(self,name):
+ """
+ Returns the value of the cookie with the specified name, or throws an error if the cookie is not present.
+
+ 'name' is the name of the cookie
+ """
+ return self.get_string("getCookieByName", [name,])
+
+
+ def is_cookie_present(self,name):
+ """
+ Returns true if a cookie with the specified name is present, or false otherwise.
+
+ 'name' is the name of the cookie
+ """
+ return self.get_boolean("isCookiePresent", [name,])
+
+
+ def create_cookie(self,nameValuePair,optionsString):
+ """
+ Create a new cookie whose path and domain are same with those of current page
+ under test, unless you specified a path for this cookie explicitly.
+
+ 'nameValuePair' is name and value of the cookie in a format "name=value"
+ 'optionsString' is options for the cookie. Currently supported options include 'path', 'max_age' and 'domain'. the optionsString's format is "path=/path/, max_age=60, domain=.foo.com". The order of options are irrelevant, the unit of the value of 'max_age' is second. Note that specifying a domain that isn't a subset of the current domain will usually fail.
+ """
+ self.do_command("createCookie", [nameValuePair,optionsString,])
+
+
+ def delete_cookie(self,name,optionsString):
+ """
+ Delete a named cookie with specified path and domain. Be careful; to delete a cookie, you
+ need to delete it using the exact same path and domain that were used to create the cookie.
+ If the path is wrong, or the domain is wrong, the cookie simply won't be deleted. Also
+ note that specifying a domain that isn't a subset of the current domain will usually fail.
+
+ Since there's no way to discover at runtime the original path and domain of a given cookie,
+ we've added an option called 'recurse' to try all sub-domains of the current domain with
+ all paths that are a subset of the current path. Beware; this option can be slow. In
+ big-O notation, it operates in O(n\*m) time, where n is the number of dots in the domain
+ name and m is the number of slashes in the path.
+
+ 'name' is the name of the cookie to be deleted
+ 'optionsString' is options for the cookie. Currently supported options include 'path', 'domain' and 'recurse.' The optionsString's format is "path=/path/, domain=.foo.com, recurse=true". The order of options are irrelevant. Note that specifying a domain that isn't a subset of the current domain will usually fail.
+ """
+ self.do_command("deleteCookie", [name,optionsString,])
+
+
+ def delete_all_visible_cookies(self):
+ """
+ Calls deleteCookie with recurse=true on all cookies visible to the current page.
+ As noted on the documentation for deleteCookie, recurse=true can be much slower
+ than simply deleting the cookies using a known domain/path.
+
+ """
+ self.do_command("deleteAllVisibleCookies", [])
+
+
+ def set_browser_log_level(self,logLevel):
+ """
+ Sets the threshold for browser-side logging messages; log messages beneath this threshold will be discarded.
+ Valid logLevel strings are: "debug", "info", "warn", "error" or "off".
+ To see the browser logs, you need to
+ either show the log window in GUI mode, or enable browser-side logging in Selenium RC.
+
+ 'logLevel' is one of the following: "debug", "info", "warn", "error" or "off"
+ """
+ self.do_command("setBrowserLogLevel", [logLevel,])
+
+
+ def run_script(self,script):
+ """
+ Creates a new "script" tag in the body of the current test window, and
+ adds the specified text into the body of the command. Scripts run in
+ this way can often be debugged more easily than scripts executed using
+ Selenium's "getEval" command. Beware that JS exceptions thrown in these script
+ tags aren't managed by Selenium, so you should probably wrap your script
+ in try/catch blocks if there is any chance that the script will throw
+ an exception.
+
+ 'script' is the JavaScript snippet to run
+ """
+ self.do_command("runScript", [script,])
+
+
+ def add_location_strategy(self,strategyName,functionDefinition):
+ """
+ Defines a new function for Selenium to locate elements on the page.
+ For example,
+ if you define the strategy "foo", and someone runs click("foo=blah"), we'll
+ run your function, passing you the string "blah", and click on the element
+ that your function
+ returns, or throw an "Element not found" error if your function returns null.
+
+ We'll pass three arguments to your function:
+
+ * locator: the string the user passed in
+ * inWindow: the currently selected window
+ * inDocument: the currently selected document
+
+
+ The function must return null if the element can't be found.
+
+ 'strategyName' is the name of the strategy to define; this should use only letters [a-zA-Z] with no spaces or other punctuation.
+ 'functionDefinition' is a string defining the body of a function in JavaScript. For example: ``return inDocument.getElementById(locator);``
+ """
+ self.do_command("addLocationStrategy", [strategyName,functionDefinition,])
+
+
+ def capture_entire_page_screenshot(self,filename):
+ """
+ Saves the entire contents of the current window canvas to a PNG file.
+ Currently this only works in Mozilla and when running in chrome mode.
+ Contrast this with the captureScreenshot command, which captures the
+ contents of the OS viewport (i.e. whatever is currently being displayed
+ on the monitor), and is implemented in the RC only. Implementation
+ mostly borrowed from the Screengrab! Firefox extension. Please see
+ http://www.screengrab.org for details.
+
+ 'filename' is the path to the file to persist the screenshot as. No filename extension will be appended by default. Directories will not be created if they do not exist, and an exception will be thrown, possibly by native code.
+ """
+ self.do_command("captureEntirePageScreenshot", [filename,])
+
+
+ def set_context(self,context):
+ """
+ Writes a message to the status bar and adds a note to the browser-side
+ log.
+
+ 'context' is the message to be sent to the browser
+ """
+ self.do_command("setContext", [context,])
+
+
+ def attach_file(self,fieldLocator,fileLocator):
+ """
+ Sets a file input (upload) field to the file listed in fileLocator
+
+ 'fieldLocator' is an element locator
+ 'fileLocator' is a URL pointing to the specified file. Before the file can be set in the input field (fieldLocator), Selenium RC may need to transfer the file to the local machine before attaching the file in a web page form. This is common in selenium grid configurations where the RC server driving the browser is not the same machine that started the test. Supported Browsers: Firefox ("\*chrome") only.
+ """
+ self.do_command("attachFile", [fieldLocator,fileLocator,])
+
+
+ def capture_screenshot(self,filename):
+ """
+ Captures a PNG screenshot to the specified file.
+
+ 'filename' is the absolute path to the file to be written, e.g. "c:\blah\screenshot.png"
+ """
+ self.do_command("captureScreenshot", [filename,])
+
+
+ def shut_down_selenium_server(self):
+ """
+ Kills the running Selenium Server and all browser sessions. After you run this command, you will no longer be able to send
+ commands to the server; you can't remotely start the server once it has been stopped. Normally
+ you should prefer to run the "stop" command, which terminates the current browser session, rather than
+ shutting down the entire server.
+
+ """
+ self.do_command("shutDownSeleniumServer", [])
+
+
+ def key_down_native(self,keycode):
+ """
+ Simulates a user pressing a key (without releasing it yet) by sending a native operating system keystroke.
+ This function uses the java.awt.Robot class to send a keystroke; this more accurately simulates typing
+ a key on the keyboard. It does not honor settings from the shiftKeyDown, controlKeyDown, altKeyDown and
+ metaKeyDown commands, and does not target any particular HTML element. To send a keystroke to a particular
+ element, focus on the element first before running this command.
+
+ 'keycode' is an integer keycode number corresponding to a java.awt.event.KeyEvent; note that Java keycodes are NOT the same thing as JavaScript keycodes!
+ """
+ self.do_command("keyDownNative", [keycode,])
+
+
+ def key_up_native(self,keycode):
+ """
+ Simulates a user releasing a key by sending a native operating system keystroke.
+ This function uses the java.awt.Robot class to send a keystroke; this more accurately simulates typing
+ a key on the keyboard. It does not honor settings from the shiftKeyDown, controlKeyDown, altKeyDown and
+ metaKeyDown commands, and does not target any particular HTML element. To send a keystroke to a particular
+ element, focus on the element first before running this command.
+
+ 'keycode' is an integer keycode number corresponding to a java.awt.event.KeyEvent; note that Java keycodes are NOT the same thing as JavaScript keycodes!
+ """
+ self.do_command("keyUpNative", [keycode,])
+
+
+ def key_press_native(self,keycode):
+ """
+ Simulates a user pressing and releasing a key by sending a native operating system keystroke.
+ This function uses the java.awt.Robot class to send a keystroke; this more accurately simulates typing
+ a key on the keyboard. It does not honor settings from the shiftKeyDown, controlKeyDown, altKeyDown and
+ metaKeyDown commands, and does not target any particular HTML element. To send a keystroke to a particular
+ element, focus on the element first before running this command.
+
+ 'keycode' is an integer keycode number corresponding to a java.awt.event.KeyEvent; note that Java keycodes are NOT the same thing as JavaScript keycodes!
+ """
+ self.do_command("keyPressNative", [keycode,])
+
diff --git a/misc/openlayers/tests/selenium/remotecontrol/setup.txt b/misc/openlayers/tests/selenium/remotecontrol/setup.txt
new file mode 100644
index 0000000..d349491
--- /dev/null
+++ b/misc/openlayers/tests/selenium/remotecontrol/setup.txt
@@ -0,0 +1,8 @@
+ * Install selenium remote control from the latest snapshot:
+
+ http://nexus.openqa.org/content/repositories/snapshots/org/seleniumhq/selenium/selenium-remote-control/1.0-SNAPSHOT/
+
+ * Run the server with java -jar selenium-server.jar
+ * Run the python script
+
+
diff --git a/misc/openlayers/tests/selenium/remotecontrol/test_ol.py b/misc/openlayers/tests/selenium/remotecontrol/test_ol.py
new file mode 100644
index 0000000..cb8ad98
--- /dev/null
+++ b/misc/openlayers/tests/selenium/remotecontrol/test_ol.py
@@ -0,0 +1,95 @@
+from selenium import selenium
+import time
+import sys
+from ConfigParser import ConfigParser
+
+MAX_TEST_LENGTH = 300
+if len(sys.argv) > 2:
+ filename = sys.argv[2]
+else:
+ filename = "config.cfg"
+
+c = ConfigParser()
+c.read(filename)
+
+targets = {}
+
+server = c.get('config', 'server')
+url= c.get('config', 'url')
+if c.has_option('config', 'timeout'):
+ MAX_TEST_LENGTH = int(c.get('config', 'timeout'))
+
+
+sections = c.sections()
+for s in sections:
+ if s == 'config':
+ continue
+ targets[s] = dict(c.items(s))
+ targets[s]['name'] = s
+
+if sys.argv[1] == "all":
+ browsers = list(targets.values())
+elif sys.argv[1] not in targets:
+ print "Invalid target"
+ sys.exit()
+else:
+ browsers = [targets[sys.argv[1]]]
+
+keep_going = True
+
+if 1:
+ for b in browsers:
+ if not keep_going:
+ continue
+
+ print "Running %s on %s" % (b['name'], b['host'])
+ s = selenium(b['host'], 4444, "*%s" % b['browsercmd'], server)
+ s.start()
+ try:
+ s.open_window(url, "test_running")
+ time.sleep(2)
+ s.select_window("test_running")
+ time.sleep(2)
+ s.refresh()
+
+ count = 0
+ while count == 0:
+ count = int(s.get_eval("window.document.getElementById('testtable').getElementsByTagName('tr').length"))
+ time.sleep(5)
+
+ ok = 0
+ fail = 0
+ last_change = time.time()
+ while True:
+ new_ok = int(s.get_eval('window.Test.AnotherWay._g_ok_pages'))
+ new_fail = int(s.get_eval('window.Test.AnotherWay._g_fail_pages'))
+ if new_ok != ok or new_fail != fail:
+ ok = new_ok
+ fail = new_fail
+ last_change = time.time()
+
+ if (ok + fail) >= count:
+ break
+ if time.time() - last_change > MAX_TEST_LENGTH:
+ raise Exception("Failed: with %s okay and %s failed, ran out of time: %s is more than %s" % (ok, fail, (time.time() - last_change), MAX_TEST_LENGTH))
+ time.sleep(10)
+
+ if fail:
+ print "Failed: %s" % fail
+ html = s.get_eval("window.document.getElementById('results').innerHTML").encode("utf-8")
+ all_html = """<html>
+ <head>
+ <meta content="text/html; charset=utf-8" http-equiv="content-type" />
+ </head>
+ <body>%s</body></html>""" % html
+
+ f = open("fail.%s.%s.html" % (time.time(), b['name']), "w")
+ f.write(all_html)
+ f.close()
+ except KeyboardInterrupt, E:
+ keep_going = False
+ print "Stopped by keyboard interrupt"
+ except Exception, E:
+ print "Error: ", E
+ s.stop()
+
diff --git a/misc/openlayers/tests/speed/geometry.html b/misc/openlayers/tests/speed/geometry.html
new file mode 100644
index 0000000..bc2b488
--- /dev/null
+++ b/misc/openlayers/tests/speed/geometry.html
@@ -0,0 +1,43 @@
+<html>
+<script src="../../lib/OpenLayers.js"></script>
+<script>
+
+var test_data = {};
+
+function setup_test() {
+ if (test_data['polygon']) { return; }
+ var f = new OpenLayers.Format.WKT();
+ var list = f.read("POLYGON((-78.046875 5.9765625, -78.75 5.9765625, -79.453125 5.2734375, -79.453125 4.5703125, -80.15625 3.8671875, -80.15625 2.4609375, -80.15625 1.0546875, -80.15625 -1.0546875, -80.15625 -2.4609375, -80.15625 -3.8671875, -80.15625 -4.5703125, -80.15625 -5.2734375, -80.15625 -5.9765625, -80.15625 -6.6796875, -80.15625 -8.0859375, -80.15625 -8.7890625, -80.15625 -9.4921875, -80.15625 -10.8984375, -79.453125 -13.0078125, -78.046875 -13.7109375, -78.046875 -15.1171875, -76.640625 -15.1171875, -76.640625 -16.5234375, -75.9375 -16.5234375, -75.9375 -17.2265625, -75.234375 -17.9296875, -75.234375 -18.6328125, -74.53125 -19.3359375, -74.53125 -20.0390625, -74.53125 -20.7421875, -73.828125 -20.7421875, -73.828125 -22.8515625, -73.828125 -24.2578125, -72.421875 -25.6640625, -71.015625 -25.6640625, -71.015625 -26.3671875, -69.609375 -27.7734375, -69.609375 -28.4765625, -68.90625 -29.1796875, -68.203125 -29.8828125, -67.5 -30.5859375, -67.5 -31.9921875, -67.5 -32.6953125, -67.5 -33.3984375, -67.5 -34.8046875, -67.5 -36.9140625, -68.203125 -39.0234375, -68.203125 -39.7265625, -70.3125 -41.1328125, -71.015625 -41.8359375, -71.015625 -43.2421875, -71.015625 -43.9453125, -71.015625 -44.6484375, -71.71875 -48.8671875, -71.71875 -50.2734375, -71.71875 -50.9765625, -72.421875 -52.3828125, -72.421875 -53.0859375, -73.828125 -53.7890625, -73.828125 -55.1953125, -73.828125 -55.8984375, -73.828125 -56.6015625, -73.828125 -57.3046875, -73.828125 -58.0078125, -72.421875 -59.4140625, -71.71875 -60.1171875, -71.015625 -60.1171875, -70.3125 -60.1171875, -68.90625 -60.8203125, -68.203125 -60.8203125, -67.5 -60.8203125, -66.796875 -60.8203125, -66.796875 -59.4140625, -65.390625 -58.0078125, -65.390625 -57.3046875, -65.390625 -55.8984375, -65.390625 -55.1953125, -65.390625 -54.4921875, -65.390625 -53.7890625, -65.390625 -53.0859375, -64.6875 -52.3828125, -60.46875 -50.2734375, -57.65625 -48.8671875, -55.546875 -47.4609375, -54.84375 -47.4609375, -54.84375 -46.7578125, -54.140625 -46.7578125, -54.140625 -46.0546875, -54.140625 -45.3515625, -54.140625 -43.2421875, -54.140625 -41.1328125, -54.140625 -39.0234375, -53.4375 -37.6171875, -52.734375 -36.2109375, -51.328125 -36.2109375, -49.921875 -35.5078125, -48.515625 -34.8046875, -45 -33.3984375, -41.484375 -31.9921875, -35.15625 -28.4765625, -33.046875 -27.0703125, -33.046875 -24.9609375, -33.046875 -23.5546875, -33.046875 -22.1484375, -33.046875 -19.3359375, -32.34375 -13.0078125, -31.640625 -11.6015625, -31.640625 -10.8984375, -31.640625 -10.1953125, -31.640625 -8.0859375, -31.640625 -7.3828125, -31.640625 -5.9765625, -31.640625 -4.5703125, -31.640625 -2.4609375, -32.34375 -2.4609375, -34.453125 0.3515625, -37.265625 3.1640625, -41.484375 6.6796875, -45.703125 8.7890625, -53.4375 12.3046875, -55.546875 14.4140625, -56.953125 14.4140625, -59.0625 15.1171875, -61.875 13.7109375, -65.390625 13.0078125, -71.015625 12.3046875, -75.234375 11.6015625, -78.75 11.6015625, -80.859375 11.6015625, -81.5625 11.6015625, -81.5625 10.8984375, -81.5625 10.1953125, -81.5625 9.4921875, -81.5625 8.7890625, -80.15625 8.7890625, -80.15625 6.6796875, -80.15625 5.2734375, -80.15625 4.5703125, -80.15625 3.8671875, -79.453125 3.8671875, -78.046875 5.9765625))");
+ test_data['polygon'] = list.geometry;
+ var list = f.read("POLYGON((-125.15625 48.1640625, -125.859375 48.1640625, -125.859375 46.7578125, -125.15625 46.7578125, -124.453125 46.0546875, -123.75 46.0546875, -123.046875 46.0546875, -122.34375 46.0546875, -120.9375 46.0546875, -116.71875 46.7578125, -113.90625 46.7578125, -113.203125 46.7578125, -112.5 46.7578125, -111.796875 46.7578125, -111.09375 46.7578125, -110.390625 46.7578125, -109.6875 46.7578125, -106.875 46.7578125, -102.65625 46.7578125, -95.625 47.4609375, -91.40625 48.1640625, -87.890625 48.1640625, -87.1875 48.1640625, -86.484375 48.1640625, -85.78125 48.1640625, -84.375 48.1640625, -82.96875 48.1640625, -81.5625 48.1640625, -80.15625 48.1640625, -79.453125 47.4609375, -79.453125 46.7578125, -78.75 46.7578125, -78.75 45.3515625, -78.046875 45.3515625, -77.34375 45.3515625, -76.640625 45.3515625, -75.234375 45.3515625, -71.015625 46.7578125, -69.609375 47.4609375, -68.90625 47.4609375, -68.203125 48.1640625, -67.5 48.1640625, -66.09375 48.1640625, -65.390625 49.5703125, -63.984375 49.5703125, -63.28125 49.5703125, -62.578125 49.5703125, -61.875 48.8671875, -61.875 48.1640625, -61.875 46.7578125, -61.171875 46.0546875, -61.171875 43.2421875, -61.171875 41.8359375, -61.171875 40.4296875, -61.171875 39.7265625, -61.875 39.7265625, -62.578125 39.7265625, -63.28125 39.7265625, -63.984375 39.7265625, -66.796875 39.7265625, -67.5 39.7265625, -68.203125 39.7265625, -68.90625 39.7265625, -68.90625 39.0234375, -69.609375 38.3203125, -69.609375 36.9140625, -71.015625 34.8046875, -71.015625 32.6953125, -72.421875 31.2890625, -73.125 30.5859375, -73.828125 29.8828125, -74.53125 29.1796875, -75.234375 28.4765625, -76.640625 26.3671875, -76.640625 25.6640625, -76.640625 24.9609375, -77.34375 24.2578125, -77.34375 23.5546875, -78.046875 23.5546875, -79.453125 23.5546875, -80.15625 23.5546875, -80.859375 23.5546875, -81.5625 23.5546875, -82.265625 23.5546875, -82.96875 23.5546875, -83.671875 23.5546875, -83.671875 24.2578125, -85.078125 24.2578125, -85.78125 24.9609375, -85.78125 25.6640625, -86.484375 25.6640625, -87.1875 26.3671875, -87.1875 27.7734375, -87.1875 28.4765625, -87.890625 28.4765625, -88.59375 28.4765625, -89.296875 28.4765625, -90 28.4765625, -90.703125 28.4765625, -91.40625 28.4765625, -91.40625 27.7734375, -92.8125 27.7734375, -92.8125 27.0703125, -92.8125 26.3671875, -92.8125 25.6640625, -92.8125 24.9609375, -92.8125 24.2578125, -93.515625 24.2578125, -94.921875 23.5546875, -95.625 23.5546875, -97.03125 23.5546875, -97.734375 23.5546875, -98.4375 23.5546875, -99.140625 23.5546875, -99.84375 24.2578125, -101.25 24.2578125, -104.0625 24.9609375, -106.171875 25.6640625, -106.875 26.3671875, -107.578125 26.3671875, -108.28125 26.3671875, -108.984375 26.3671875, -110.390625 26.3671875, -113.90625 26.3671875, -116.015625 26.3671875, -116.71875 27.0703125, -118.125 27.0703125, -118.828125 27.7734375, -120.234375 27.7734375, -120.234375 28.4765625, -120.9375 28.4765625, -120.9375 29.1796875, -121.640625 29.8828125, -121.640625 30.5859375, -121.640625 31.2890625, -123.046875 31.2890625, -123.75 32.6953125, -124.453125 33.3984375, -125.15625 34.1015625, -126.5625 34.8046875, -127.265625 34.8046875, -127.96875 35.5078125, -125.15625 48.1640625))");
+ test_data['miss_polygon'] = list.geometry;
+ var list = f.read("POLYGON((-32.34375 13.0078125, -33.046875 13.0078125, -33.75 13.0078125, -34.453125 13.0078125, -35.15625 13.0078125, -35.859375 13.0078125, -36.5625 13.0078125, -37.265625 13.0078125, -37.96875 13.0078125, -37.96875 12.3046875, -38.671875 12.3046875, -39.375 12.3046875, -40.078125 12.3046875, -40.078125 11.6015625, -41.484375 11.6015625, -42.890625 10.8984375, -43.59375 10.8984375, -44.296875 10.1953125, -44.296875 9.4921875, -44.296875 8.7890625, -44.296875 8.0859375, -44.296875 6.6796875, -44.296875 5.9765625, -44.296875 5.2734375, -43.59375 5.2734375, -43.59375 4.5703125, -43.59375 3.8671875, -42.890625 3.1640625, -42.890625 2.4609375, -42.890625 1.7578125, -42.890625 1.0546875, -42.1875 -0.3515625, -41.484375 -1.0546875, -41.484375 -2.4609375, -41.484375 -3.1640625, -42.890625 -3.1640625, -44.296875 -3.8671875, -44.296875 -4.5703125, -45.703125 -5.2734375, -46.40625 -5.2734375, -47.109375 -5.2734375, -47.109375 -5.9765625, -47.109375 -6.6796875, -47.109375 -7.3828125, -47.109375 -8.0859375, -47.109375 -9.4921875, -47.109375 -10.1953125, -47.109375 -10.8984375, -47.109375 -11.6015625, -47.109375 -12.3046875, -47.109375 -13.7109375, -47.109375 -15.1171875, -47.109375 -15.8203125, -47.8125 -15.8203125, -47.8125 -16.5234375, -46.40625 -16.5234375, -45.703125 -16.5234375, -44.296875 -17.2265625, -43.59375 -17.9296875, -42.890625 -18.6328125, -42.1875 -18.6328125, -41.484375 -18.6328125, -40.78125 -18.6328125, -39.375 -18.6328125, -37.265625 -18.6328125, -35.859375 -18.6328125, -34.453125 -17.9296875, -33.75 -17.9296875, -33.046875 -17.9296875, -32.34375 -17.9296875, -32.34375 -18.6328125, -32.34375 -19.3359375, -32.34375 -20.0390625, -32.34375 -21.4453125, -31.640625 -22.1484375, -30.9375 -22.8515625, -28.828125 -22.8515625, -26.71875 -22.8515625, -23.90625 -20.0390625, -23.203125 -19.3359375, -22.5 -19.3359375, -21.796875 -17.2265625, -21.09375 -16.5234375, -21.09375 -15.8203125, -21.09375 -15.1171875, -21.09375 -14.4140625, -19.6875 -10.8984375, -19.6875 -8.0859375, -19.6875 -7.3828125, -19.6875 -5.9765625, -19.6875 -5.2734375, -19.6875 -3.1640625, -19.6875 -1.7578125, -19.6875 -0.3515625, -19.6875 0.3515625, -19.6875 1.0546875, -19.6875 1.7578125, -19.6875 2.4609375, -19.6875 3.1640625, -20.390625 4.5703125, -20.390625 5.9765625, -20.390625 6.6796875, -21.09375 6.6796875, -22.5 8.0859375, -23.90625 8.7890625, -25.3125 8.7890625, -26.015625 9.4921875, -26.71875 9.4921875, -27.421875 9.4921875, -28.125 9.4921875, -32.34375 13.0078125))");
+ test_data['hit_polygon'] = list.geometry;
+
+}
+
+function run_test() {
+ test_data['polygon'].intersects(test_data['hit_polygon']);
+ test_data['polygon'].intersects(test_data['miss_polygon']);
+}
+
+function run_many(x) {
+ var elapsed = 0;
+ for (var i = 0; i < x; i++) {
+ var time = new Date();
+ run_test();
+ elapsed += (new Date() - time);
+ }
+ return elapsed;
+}
+
+function print_results(x) {
+ var t = run_many(x);
+ document.getElementById("out").innerHTML += (t +"ms to run " + x + " times<br />");
+}
+ setup_test()
+</script>
+<body>
+<input type="submit" value="Run" onclick="print_results(5)" />
+<div id="out"></div>
+</body>
diff --git a/misc/openlayers/tests/speed/string_format.html b/misc/openlayers/tests/speed/string_format.html
new file mode 100644
index 0000000..39146b7
--- /dev/null
+++ b/misc/openlayers/tests/speed/string_format.html
@@ -0,0 +1,29 @@
+<html>
+<head>
+<script src="../../lib/OpenLayers.js"></script>
+<script>
+
+function stringformat() {
+ var string = OpenLayers.String.format("${abc} 123 ${def}", {'abc': 456, 'def': 789})
+}
+function run(x) {
+ var date = new Date();
+ for (var i = 0; i < x; i++) {
+ stringformat();
+ }
+ var elapsed = (new Date() - date);
+ return elapsed;
+}
+
+function show_time(x)
+{
+ var t = run(x);
+ document.getElementById("out").innerHTML = t + "ms for " + x + " runs";
+}
+</script>
+</head>
+<body>
+<a onclick="javascript:show_time(100000); return false" href="#">Run</a>
+<div id="out"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/speed/vector-renderers.html b/misc/openlayers/tests/speed/vector-renderers.html
new file mode 100644
index 0000000..4d88dfc
--- /dev/null
+++ b/misc/openlayers/tests/speed/vector-renderers.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Vector Features Performance Test</title>
+ <script type="text/javascript" src="https://getfirebug.com/firebug-lite.js#startOpened=true"></script>
+ <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+ <link rel="stylesheet" href="../../examples/style.css" type="text/css" />
+ </head>
+ <body>
+ <h1 id="title">Vector Rendering Performance</h1>
+ <div id="map" class="smallmap"></div>
+ <p>
+ This is a benchmark for vector rendering performance. Test results are
+ written to the debug console.
+ Select a renderer here:
+ <br/>
+ <select id="renderers"></select>
+ </p><p>
+ The benchmark shows the time needed to render the features, and how long a
+ move (drag or zoom) takes. Drag and zoom around to produce move results.
+ </p>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script src="vector-renderers.js"></script>
+ </body>
+</html> \ No newline at end of file
diff --git a/misc/openlayers/tests/speed/vector-renderers.js b/misc/openlayers/tests/speed/vector-renderers.js
new file mode 100644
index 0000000..a11b361
--- /dev/null
+++ b/misc/openlayers/tests/speed/vector-renderers.js
@@ -0,0 +1,70 @@
+var map, vectorLayer, drawFeature, features
+
+map = new OpenLayers.Map('map', {
+ eventListeners: {
+ movestart: function() {
+ console.time("move");
+ },
+ moveend: function() {
+ console.timeEnd("move");
+ }
+ }
+});
+
+// allow testing of specific renderers via "?renderer=Canvas", etc
+var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
+renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
+
+vectorLayer = new OpenLayers.Layer.Vector("Vector Layer", {
+ isBaseLayer: true,
+ renderers: renderer,
+ eventListeners: {
+ beforefeaturesadded: function() {
+ console.time("addFeatures");
+ },
+ featuresadded: function() {
+ console.timeEnd("addFeatures");
+ }
+ }
+});
+
+map.addLayers([vectorLayer]);
+map.addControl(new OpenLayers.Control.MousePosition());
+map.setCenter(new OpenLayers.LonLat(0, 0), 2);
+
+features = new Array(500);
+var x, y, points
+for (var i = 0; i < 500; i++) {
+ x = 90-Math.random()*180;
+ y = 45-Math.random()*90;
+ var pointList = [];
+ for(var p=0; p<19; ++p) {
+ var a = p * (2 * Math.PI) / 20;
+ var r = Math.random() * 3 + 1;
+ var newPoint = new OpenLayers.Geometry.Point(x + (r * Math.cos(a)),
+ y + (r * Math.sin(a)));
+ pointList.push(newPoint);
+ }
+ pointList.push(pointList[0]);
+ features[i] = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LinearRing(pointList));
+
+}
+vectorLayer.addFeatures(features);
+
+var select = document.getElementById("renderers");
+var renderers = OpenLayers.Layer.Vector.prototype.renderers;
+var option;
+for (var i=0, len=renderers.length; i<len; i++) {
+ if (OpenLayers.Renderer[renderers[i]].prototype.supported()) {
+ option = document.createElement("option");
+ option.textContent = renderers[i];
+ option.value = renderers[i];
+ option.selected = renderers[i] == vectorLayer.renderer.CLASS_NAME.split(".").pop();
+ select.appendChild(option);
+ }
+}
+select.onchange = function() {
+ window.location.href = window.location.href.split("?")[0] +
+ "?renderer=" + select.options[select.selectedIndex].value;
+}
diff --git a/misc/openlayers/tests/speed/wmc_speed.html b/misc/openlayers/tests/speed/wmc_speed.html
new file mode 100644
index 0000000..0dd0ce8
--- /dev/null
+++ b/misc/openlayers/tests/speed/wmc_speed.html
@@ -0,0 +1,30 @@
+<html>
+<head>
+<script src="../../lib/OpenLayers.js"></script>
+<script>
+var context = '<ViewContext xmlns="http://www.opengis.net/context" version="1.1.0" id="OpenLayers_Context_232" xsi:schemaLocation="http://www.opengis.net/context http://schemas.opengis.net/context/1.1.0/context.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><General><Window width="512" height="256"/><BoundingBox minx="-109.9709708" miny="27.01451459" maxx="-80.02902918" maxy="41.98548541" SRS="EPSG:4326"/><Title/><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-130.0000000" miny="14.00000000" maxx="-60.00000000" maxy="55.00000000"/></Extension></General><LayerList><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://t1.hypercube.telascience.org/cgi-bin/landsat7"/></Server><Name>landsat7</Name><Title>NASA Global Mosaic</Title><sld:MinScaleDenominator xmlns:sld="http://www.opengis.net/sld">6299645.760</sld:MinScaleDenominator><sld:MaxScaleDenominator xmlns:sld="http://www.opengis.net/sld">31498228.80</sld:MaxScaleDenominator><FormatList><Format current="1">image/jpeg</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-130.0000000" miny="14.00000000" maxx="-60.00000000" maxy="55.00000000"/><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">true</ol:isBaseLayer><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">true</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">false</ol:singleTile></Extension></Layer><Layer queryable="1" hidden="1"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://labs.metacarta.com/wms/vmap0"/></Server><Name>basic</Name><Title>OpenLayers WMS</Title><sld:MinScaleDenominator xmlns:sld="http://www.opengis.net/sld">6299645.760</sld:MinScaleDenominator><sld:MaxScaleDenominator xmlns:sld="http://www.opengis.net/sld">31498228.80</sld:MaxScaleDenominator><FormatList><Format current="1">image/jpeg</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-130.0000000" miny="14.00000000" maxx="-60.00000000" maxy="55.00000000"/><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">true</ol:isBaseLayer><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">true</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">false</ol:singleTile></Extension></Layer><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://lioapp.lrc.gov.on.ca/cubeserv/cubeserv.pl"/></Server><Name>na_road:CCRS</Name><Title>Transportation Network</Title><sld:MinScaleDenominator xmlns:sld="http://www.opengis.net/sld">6200000.000</sld:MinScaleDenominator><sld:MaxScaleDenominator xmlns:sld="http://www.opengis.net/sld">32000000.00</sld:MaxScaleDenominator><FormatList><Format current="1">image/png</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-166.5320000" miny="4.050460000" maxx="-0.2068180000" maxy="70.28700000"/><ol:transparent xmlns:ol="http://openlayers.org/context">TRUE</ol:transparent><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">false</ol:isBaseLayer><ol:opacity xmlns:ol="http://openlayers.org/context">0.6</ol:opacity><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">false</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">false</ol:singleTile></Extension></Layer><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://columbo.nrlssc.navy.mil/ogcwms/servlet/WMSServlet/AccuWeather_Maps.wms"/></Server><Name>3:1</Name><Title>Radar 3:1</Title><sld:MinScaleDenominator xmlns:sld="http://www.opengis.net/sld">6299645.760</sld:MinScaleDenominator><sld:MaxScaleDenominator xmlns:sld="http://www.opengis.net/sld">31498228.80</sld:MaxScaleDenominator><FormatList><Format current="1">image/png</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-131.0294952" miny="14.56289673" maxx="-61.02950287" maxy="54.56289673"/><ol:transparent xmlns:ol="http://openlayers.org/context">TRUE</ol:transparent><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">false</ol:isBaseLayer><ol:opacity xmlns:ol="http://openlayers.org/context">0.8</ol:opacity><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">true</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">true</ol:singleTile></Extension></Layer></LayerList></ViewContext>';
+function parse_wmc() {
+ var format = new OpenLayers.Format.WMC.v1()
+ var data = format.read(context);
+}
+function run(x) {
+ var date = new Date();
+ for (var i = 0; i < x; i++) {
+ parse_wmc();
+ }
+ var elapsed = (new Date() - date);
+ return elapsed;
+}
+
+function show_time(x)
+{
+ var t = run(x);
+ document.getElementById("out").innerHTML = t + "ms for " + x + " runs";
+}
+</script>
+</head>
+<body>
+<a onclick="javascript:show_time(100); return false" href="#">Run</a>
+<div id="out"></div>
+</body>
+</html>
diff --git a/misc/openlayers/tests/speed/wmscaps.html b/misc/openlayers/tests/speed/wmscaps.html
new file mode 100644
index 0000000..3cab542
--- /dev/null
+++ b/misc/openlayers/tests/speed/wmscaps.html
@@ -0,0 +1,52 @@
+<html>
+ <head>
+ <title>WMS Capabilities Speed Test</title>
+ <script src="../../lib/OpenLayers.js"></script>
+ <script src="wmscaps.js"></script>
+ <script>
+ var data;
+ var stats = [];
+
+ function parseCaps(id, done) {
+ var format = new OpenLayers.Format.WMSCapabilities();
+ data = format.read(caps);
+ done(id);
+ }
+
+ function run(func, x) {
+ document.getElementById("out").innerHTML = "running ...";
+ var starts = {};
+ var elapsed = 0;
+ completed = 0;
+ function callback(id) {
+ elapsed += new Date() - starts[id];
+ ++completed;
+ if (completed === x) {
+ report(x, elapsed);
+ }
+ }
+ var runner;
+ for (var i=0; i<x; i++) {
+ runner = createRunner(i, starts, func, callback);
+ window.setTimeout(runner, 0);
+ }
+ }
+
+ function createRunner(id, starts, func, done) {
+ return function() {
+ starts[id] = new Date();
+ func(id, done);
+ }
+ }
+
+ function report(x, elapsed) {
+ document.getElementById("out").innerHTML = elapsed + " ms for " + x + " runs (" + elapsed/x + " ms average)";
+ }
+
+ </script>
+ </head>
+ <body>
+ <a onclick="javascript:run(parseCaps, 5); return false" href="#">Run</a>
+ <div id="out"></div>
+ </body>
+</html>
diff --git a/misc/openlayers/tests/speed/wmscaps.js b/misc/openlayers/tests/speed/wmscaps.js
new file mode 100644
index 0000000..a186779
--- /dev/null
+++ b/misc/openlayers/tests/speed/wmscaps.js
@@ -0,0 +1,4956 @@
+var caps =
+'<?xml version="1.0" encoding="UTF-8"?>' +
+'<!DOCTYPE WMT_MS_Capabilities SYSTEM "http://demo.opengeo.org/geoserver/schemas/wms/1.1.1/WMS_MS_Capabilities.dtd">' +
+'<WMT_MS_Capabilities version="1.1.1" updateSequence="145">' +
+' <Service>' +
+' <Name>OGC:WMS</Name>' +
+' <Title>GeoServer Web Map Service</Title>' +
+' <Abstract>A compliant implementation of WMS 1.1.1 plus most of the SLD 1.0 extension (dynamic styling). Can also generate PDF, SVG, KML, GeoRSS</Abstract>' +
+' <KeywordList>' +
+' <Keyword>WFS</Keyword>' +
+' <Keyword>WMS</Keyword>' +
+' <Keyword>GEOSERVER</Keyword>' +
+' </KeywordList>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms"/>' +
+' <ContactInformation>' +
+' <ContactPersonPrimary>' +
+' <ContactPerson>Claudius Ptolomaeus</ContactPerson>' +
+' <ContactOrganization>The ancient geographes INC</ContactOrganization>' +
+' </ContactPersonPrimary>' +
+' <ContactPosition>Chief geographer</ContactPosition>' +
+' <ContactAddress>' +
+' <AddressType>Work</AddressType>' +
+' <Address/>' +
+' <City>Alexandria</City>' +
+' <StateOrProvince/>' +
+' <PostCode/>' +
+' <Country>Egypt</Country>' +
+' </ContactAddress>' +
+' <ContactVoiceTelephone/>' +
+' <ContactFacsimileTelephone/>' +
+' <ContactElectronicMailAddress>claudius.ptolomaeus@gmail.com</ContactElectronicMailAddress>' +
+' </ContactInformation>' +
+' <Fees>NONE</Fees>' +
+' <AccessConstraints>NONE</AccessConstraints>' +
+' </Service>' +
+' <Capability>' +
+' <Request>' +
+' <GetCapabilities>' +
+' <Format>application/vnd.ogc.wms_xml</Format>' +
+' <DCPType>' +
+' <HTTP>' +
+' <Get>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms?SERVICE=WMS&amp;"/>' +
+' </Get>' +
+' <Post>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms?SERVICE=WMS&amp;"/>' +
+' </Post>' +
+' </HTTP>' +
+' </DCPType>' +
+' </GetCapabilities>' +
+' <GetMap>' +
+' <Format>image/png</Format>' +
+' <Format>application/atom xml</Format>' +
+' <Format>application/atom+xml</Format>' +
+' <Format>application/openlayers</Format>' +
+' <Format>application/pdf</Format>' +
+' <Format>application/rss xml</Format>' +
+' <Format>application/rss+xml</Format>' +
+' <Format>application/vnd.google-earth.kml</Format>' +
+' <Format>application/vnd.google-earth.kml xml</Format>' +
+' <Format>application/vnd.google-earth.kml+xml</Format>' +
+' <Format>application/vnd.google-earth.kmz</Format>' +
+' <Format>application/vnd.google-earth.kmz xml</Format>' +
+' <Format>application/vnd.google-earth.kmz+xml</Format>' +
+' <Format>atom</Format>' +
+' <Format>image/geotiff</Format>' +
+' <Format>image/geotiff8</Format>' +
+' <Format>image/gif</Format>' +
+' <Format>image/jpeg</Format>' +
+' <Format>image/png8</Format>' +
+' <Format>image/svg</Format>' +
+' <Format>image/svg xml</Format>' +
+' <Format>image/svg+xml</Format>' +
+' <Format>image/tiff</Format>' +
+' <Format>image/tiff8</Format>' +
+' <Format>kml</Format>' +
+' <Format>kmz</Format>' +
+' <Format>openlayers</Format>' +
+' <Format>rss</Format>' +
+' <DCPType>' +
+' <HTTP>' +
+' <Get>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms?SERVICE=WMS&amp;"/>' +
+' </Get>' +
+' </HTTP>' +
+' </DCPType>' +
+' </GetMap>' +
+' <GetFeatureInfo>' +
+' <Format>text/plain</Format>' +
+' <Format>text/html</Format>' +
+' <Format>application/vnd.ogc.gml</Format>' +
+' <DCPType>' +
+' <HTTP>' +
+' <Get>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms?SERVICE=WMS&amp;"/>' +
+' </Get>' +
+' <Post>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms?SERVICE=WMS&amp;"/>' +
+' </Post>' +
+' </HTTP>' +
+' </DCPType>' +
+' </GetFeatureInfo>' +
+' <DescribeLayer>' +
+' <Format>application/vnd.ogc.wms_xml</Format>' +
+' <DCPType>' +
+' <HTTP>' +
+' <Get>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms?SERVICE=WMS&amp;"/>' +
+' </Get>' +
+' </HTTP>' +
+' </DCPType>' +
+' </DescribeLayer>' +
+' <GetLegendGraphic>' +
+' <Format>image/png</Format>' +
+' <Format>image/jpeg</Format>' +
+' <Format>image/gif</Format>' +
+' <DCPType>' +
+' <HTTP>' +
+' <Get>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms?SERVICE=WMS&amp;"/>' +
+' </Get>' +
+' </HTTP>' +
+' </DCPType>' +
+' </GetLegendGraphic>' +
+' </Request>' +
+' <Exception>' +
+' <Format>application/vnd.ogc.se_xml</Format>' +
+' </Exception>' +
+' <UserDefinedSymbolization SupportSLD="1" UserLayer="1" UserStyle="1" RemoteWFS="1"/>' +
+' <Layer>' +
+' <Title>GeoServer Web Map Service</Title>' +
+' <Abstract>A compliant implementation of WMS 1.1.1 plus most of the SLD 1.0 extension (dynamic styling). Can also generate PDF, SVG, KML, GeoRSS</Abstract>' +
+' <!--All supported EPSG projections:-->' +
+' <SRS>EPSG:WGS84(DD)</SRS>' +
+' <SRS>EPSG:2000</SRS>' +
+' <SRS>EPSG:2001</SRS>' +
+' <SRS>EPSG:2002</SRS>' +
+' <SRS>EPSG:2003</SRS>' +
+' <SRS>EPSG:2004</SRS>' +
+' <SRS>EPSG:2005</SRS>' +
+' <SRS>EPSG:2006</SRS>' +
+' <SRS>EPSG:2007</SRS>' +
+' <SRS>EPSG:2008</SRS>' +
+' <SRS>EPSG:2009</SRS>' +
+' <SRS>EPSG:2010</SRS>' +
+' <SRS>EPSG:2011</SRS>' +
+' <SRS>EPSG:2012</SRS>' +
+' <SRS>EPSG:2013</SRS>' +
+' <SRS>EPSG:2014</SRS>' +
+' <SRS>EPSG:2015</SRS>' +
+' <SRS>EPSG:2016</SRS>' +
+' <SRS>EPSG:2017</SRS>' +
+' <SRS>EPSG:2018</SRS>' +
+' <SRS>EPSG:2019</SRS>' +
+' <SRS>EPSG:2020</SRS>' +
+' <SRS>EPSG:2021</SRS>' +
+' <SRS>EPSG:2022</SRS>' +
+' <SRS>EPSG:2023</SRS>' +
+' <SRS>EPSG:2024</SRS>' +
+' <SRS>EPSG:2025</SRS>' +
+' <SRS>EPSG:2026</SRS>' +
+' <SRS>EPSG:2027</SRS>' +
+' <SRS>EPSG:2028</SRS>' +
+' <SRS>EPSG:2029</SRS>' +
+' <SRS>EPSG:2030</SRS>' +
+' <SRS>EPSG:2031</SRS>' +
+' <SRS>EPSG:2032</SRS>' +
+' <SRS>EPSG:2033</SRS>' +
+' <SRS>EPSG:2034</SRS>' +
+' <SRS>EPSG:2035</SRS>' +
+' <SRS>EPSG:2036</SRS>' +
+' <SRS>EPSG:2037</SRS>' +
+' <SRS>EPSG:2038</SRS>' +
+' <SRS>EPSG:2039</SRS>' +
+' <SRS>EPSG:2040</SRS>' +
+' <SRS>EPSG:2041</SRS>' +
+' <SRS>EPSG:2042</SRS>' +
+' <SRS>EPSG:2043</SRS>' +
+' <SRS>EPSG:2044</SRS>' +
+' <SRS>EPSG:2045</SRS>' +
+' <SRS>EPSG:2046</SRS>' +
+' <SRS>EPSG:2047</SRS>' +
+' <SRS>EPSG:2048</SRS>' +
+' <SRS>EPSG:2049</SRS>' +
+' <SRS>EPSG:2050</SRS>' +
+' <SRS>EPSG:2051</SRS>' +
+' <SRS>EPSG:2052</SRS>' +
+' <SRS>EPSG:2053</SRS>' +
+' <SRS>EPSG:2054</SRS>' +
+' <SRS>EPSG:2055</SRS>' +
+' <SRS>EPSG:2056</SRS>' +
+' <SRS>EPSG:2057</SRS>' +
+' <SRS>EPSG:2058</SRS>' +
+' <SRS>EPSG:2059</SRS>' +
+' <SRS>EPSG:2060</SRS>' +
+' <SRS>EPSG:2061</SRS>' +
+' <SRS>EPSG:2062</SRS>' +
+' <SRS>EPSG:2063</SRS>' +
+' <SRS>EPSG:2064</SRS>' +
+' <SRS>EPSG:2065</SRS>' +
+' <SRS>EPSG:2066</SRS>' +
+' <SRS>EPSG:2067</SRS>' +
+' <SRS>EPSG:2068</SRS>' +
+' <SRS>EPSG:2069</SRS>' +
+' <SRS>EPSG:2070</SRS>' +
+' <SRS>EPSG:2071</SRS>' +
+' <SRS>EPSG:2072</SRS>' +
+' <SRS>EPSG:2073</SRS>' +
+' <SRS>EPSG:2074</SRS>' +
+' <SRS>EPSG:2075</SRS>' +
+' <SRS>EPSG:2076</SRS>' +
+' <SRS>EPSG:2077</SRS>' +
+' <SRS>EPSG:2078</SRS>' +
+' <SRS>EPSG:2079</SRS>' +
+' <SRS>EPSG:2080</SRS>' +
+' <SRS>EPSG:2081</SRS>' +
+' <SRS>EPSG:2082</SRS>' +
+' <SRS>EPSG:2083</SRS>' +
+' <SRS>EPSG:2084</SRS>' +
+' <SRS>EPSG:2085</SRS>' +
+' <SRS>EPSG:2086</SRS>' +
+' <SRS>EPSG:2087</SRS>' +
+' <SRS>EPSG:2088</SRS>' +
+' <SRS>EPSG:2089</SRS>' +
+' <SRS>EPSG:2090</SRS>' +
+' <SRS>EPSG:2091</SRS>' +
+' <SRS>EPSG:2092</SRS>' +
+' <SRS>EPSG:2093</SRS>' +
+' <SRS>EPSG:2094</SRS>' +
+' <SRS>EPSG:2095</SRS>' +
+' <SRS>EPSG:2096</SRS>' +
+' <SRS>EPSG:2097</SRS>' +
+' <SRS>EPSG:2098</SRS>' +
+' <SRS>EPSG:2099</SRS>' +
+' <SRS>EPSG:2100</SRS>' +
+' <SRS>EPSG:2101</SRS>' +
+' <SRS>EPSG:2102</SRS>' +
+' <SRS>EPSG:2103</SRS>' +
+' <SRS>EPSG:2104</SRS>' +
+' <SRS>EPSG:2105</SRS>' +
+' <SRS>EPSG:2106</SRS>' +
+' <SRS>EPSG:2107</SRS>' +
+' <SRS>EPSG:2108</SRS>' +
+' <SRS>EPSG:2109</SRS>' +
+' <SRS>EPSG:2110</SRS>' +
+' <SRS>EPSG:2111</SRS>' +
+' <SRS>EPSG:2112</SRS>' +
+' <SRS>EPSG:2113</SRS>' +
+' <SRS>EPSG:2114</SRS>' +
+' <SRS>EPSG:2115</SRS>' +
+' <SRS>EPSG:2116</SRS>' +
+' <SRS>EPSG:2117</SRS>' +
+' <SRS>EPSG:2118</SRS>' +
+' <SRS>EPSG:2119</SRS>' +
+' <SRS>EPSG:2120</SRS>' +
+' <SRS>EPSG:2121</SRS>' +
+' <SRS>EPSG:2122</SRS>' +
+' <SRS>EPSG:2123</SRS>' +
+' <SRS>EPSG:2124</SRS>' +
+' <SRS>EPSG:2125</SRS>' +
+' <SRS>EPSG:2126</SRS>' +
+' <SRS>EPSG:2127</SRS>' +
+' <SRS>EPSG:2128</SRS>' +
+' <SRS>EPSG:2129</SRS>' +
+' <SRS>EPSG:2130</SRS>' +
+' <SRS>EPSG:2131</SRS>' +
+' <SRS>EPSG:2132</SRS>' +
+' <SRS>EPSG:2133</SRS>' +
+' <SRS>EPSG:2134</SRS>' +
+' <SRS>EPSG:2135</SRS>' +
+' <SRS>EPSG:2136</SRS>' +
+' <SRS>EPSG:2137</SRS>' +
+' <SRS>EPSG:2138</SRS>' +
+' <SRS>EPSG:2139</SRS>' +
+' <SRS>EPSG:2140</SRS>' +
+' <SRS>EPSG:2141</SRS>' +
+' <SRS>EPSG:2142</SRS>' +
+' <SRS>EPSG:2143</SRS>' +
+' <SRS>EPSG:2144</SRS>' +
+' <SRS>EPSG:2145</SRS>' +
+' <SRS>EPSG:2146</SRS>' +
+' <SRS>EPSG:2147</SRS>' +
+' <SRS>EPSG:2148</SRS>' +
+' <SRS>EPSG:2149</SRS>' +
+' <SRS>EPSG:2150</SRS>' +
+' <SRS>EPSG:2151</SRS>' +
+' <SRS>EPSG:2152</SRS>' +
+' <SRS>EPSG:2153</SRS>' +
+' <SRS>EPSG:2154</SRS>' +
+' <SRS>EPSG:2155</SRS>' +
+' <SRS>EPSG:2156</SRS>' +
+' <SRS>EPSG:2157</SRS>' +
+' <SRS>EPSG:2158</SRS>' +
+' <SRS>EPSG:2159</SRS>' +
+' <SRS>EPSG:2160</SRS>' +
+' <SRS>EPSG:2161</SRS>' +
+' <SRS>EPSG:2162</SRS>' +
+' <SRS>EPSG:2163</SRS>' +
+' <SRS>EPSG:2164</SRS>' +
+' <SRS>EPSG:2165</SRS>' +
+' <SRS>EPSG:2166</SRS>' +
+' <SRS>EPSG:2167</SRS>' +
+' <SRS>EPSG:2168</SRS>' +
+' <SRS>EPSG:2169</SRS>' +
+' <SRS>EPSG:2170</SRS>' +
+' <SRS>EPSG:2171</SRS>' +
+' <SRS>EPSG:2172</SRS>' +
+' <SRS>EPSG:2173</SRS>' +
+' <SRS>EPSG:2174</SRS>' +
+' <SRS>EPSG:2175</SRS>' +
+' <SRS>EPSG:2176</SRS>' +
+' <SRS>EPSG:2177</SRS>' +
+' <SRS>EPSG:2178</SRS>' +
+' <SRS>EPSG:2179</SRS>' +
+' <SRS>EPSG:2180</SRS>' +
+' <SRS>EPSG:2188</SRS>' +
+' <SRS>EPSG:2189</SRS>' +
+' <SRS>EPSG:2190</SRS>' +
+' <SRS>EPSG:2191</SRS>' +
+' <SRS>EPSG:2192</SRS>' +
+' <SRS>EPSG:2193</SRS>' +
+' <SRS>EPSG:2194</SRS>' +
+' <SRS>EPSG:2195</SRS>' +
+' <SRS>EPSG:2196</SRS>' +
+' <SRS>EPSG:2197</SRS>' +
+' <SRS>EPSG:2198</SRS>' +
+' <SRS>EPSG:2199</SRS>' +
+' <SRS>EPSG:2200</SRS>' +
+' <SRS>EPSG:2201</SRS>' +
+' <SRS>EPSG:2202</SRS>' +
+' <SRS>EPSG:2203</SRS>' +
+' <SRS>EPSG:2204</SRS>' +
+' <SRS>EPSG:2205</SRS>' +
+' <SRS>EPSG:2206</SRS>' +
+' <SRS>EPSG:2207</SRS>' +
+' <SRS>EPSG:2208</SRS>' +
+' <SRS>EPSG:2209</SRS>' +
+' <SRS>EPSG:2210</SRS>' +
+' <SRS>EPSG:2211</SRS>' +
+' <SRS>EPSG:2212</SRS>' +
+' <SRS>EPSG:2213</SRS>' +
+' <SRS>EPSG:2214</SRS>' +
+' <SRS>EPSG:2215</SRS>' +
+' <SRS>EPSG:2216</SRS>' +
+' <SRS>EPSG:2217</SRS>' +
+' <SRS>EPSG:2218</SRS>' +
+' <SRS>EPSG:2219</SRS>' +
+' <SRS>EPSG:2220</SRS>' +
+' <SRS>EPSG:2221</SRS>' +
+' <SRS>EPSG:2222</SRS>' +
+' <SRS>EPSG:2223</SRS>' +
+' <SRS>EPSG:2224</SRS>' +
+' <SRS>EPSG:2225</SRS>' +
+' <SRS>EPSG:2226</SRS>' +
+' <SRS>EPSG:2227</SRS>' +
+' <SRS>EPSG:2228</SRS>' +
+' <SRS>EPSG:2229</SRS>' +
+' <SRS>EPSG:2230</SRS>' +
+' <SRS>EPSG:2231</SRS>' +
+' <SRS>EPSG:2232</SRS>' +
+' <SRS>EPSG:2233</SRS>' +
+' <SRS>EPSG:2234</SRS>' +
+' <SRS>EPSG:2235</SRS>' +
+' <SRS>EPSG:2236</SRS>' +
+' <SRS>EPSG:2237</SRS>' +
+' <SRS>EPSG:2238</SRS>' +
+' <SRS>EPSG:2239</SRS>' +
+' <SRS>EPSG:2240</SRS>' +
+' <SRS>EPSG:2241</SRS>' +
+' <SRS>EPSG:2242</SRS>' +
+' <SRS>EPSG:2243</SRS>' +
+' <SRS>EPSG:2244</SRS>' +
+' <SRS>EPSG:2245</SRS>' +
+' <SRS>EPSG:2246</SRS>' +
+' <SRS>EPSG:2247</SRS>' +
+' <SRS>EPSG:2248</SRS>' +
+' <SRS>EPSG:2249</SRS>' +
+' <SRS>EPSG:2250</SRS>' +
+' <SRS>EPSG:2251</SRS>' +
+' <SRS>EPSG:2252</SRS>' +
+' <SRS>EPSG:2253</SRS>' +
+' <SRS>EPSG:2254</SRS>' +
+' <SRS>EPSG:2255</SRS>' +
+' <SRS>EPSG:2256</SRS>' +
+' <SRS>EPSG:2257</SRS>' +
+' <SRS>EPSG:2258</SRS>' +
+' <SRS>EPSG:2259</SRS>' +
+' <SRS>EPSG:2260</SRS>' +
+' <SRS>EPSG:2261</SRS>' +
+' <SRS>EPSG:2262</SRS>' +
+' <SRS>EPSG:2263</SRS>' +
+' <SRS>EPSG:2264</SRS>' +
+' <SRS>EPSG:2265</SRS>' +
+' <SRS>EPSG:2266</SRS>' +
+' <SRS>EPSG:2267</SRS>' +
+' <SRS>EPSG:2268</SRS>' +
+' <SRS>EPSG:2269</SRS>' +
+' <SRS>EPSG:2270</SRS>' +
+' <SRS>EPSG:2271</SRS>' +
+' <SRS>EPSG:2272</SRS>' +
+' <SRS>EPSG:2273</SRS>' +
+' <SRS>EPSG:2274</SRS>' +
+' <SRS>EPSG:2275</SRS>' +
+' <SRS>EPSG:2276</SRS>' +
+' <SRS>EPSG:2277</SRS>' +
+' <SRS>EPSG:2278</SRS>' +
+' <SRS>EPSG:2279</SRS>' +
+' <SRS>EPSG:2280</SRS>' +
+' <SRS>EPSG:2281</SRS>' +
+' <SRS>EPSG:2282</SRS>' +
+' <SRS>EPSG:2283</SRS>' +
+' <SRS>EPSG:2284</SRS>' +
+' <SRS>EPSG:2285</SRS>' +
+' <SRS>EPSG:2286</SRS>' +
+' <SRS>EPSG:2287</SRS>' +
+' <SRS>EPSG:2288</SRS>' +
+' <SRS>EPSG:2289</SRS>' +
+' <SRS>EPSG:2290</SRS>' +
+' <SRS>EPSG:2291</SRS>' +
+' <SRS>EPSG:2292</SRS>' +
+' <SRS>EPSG:2294</SRS>' +
+' <SRS>EPSG:2295</SRS>' +
+' <SRS>EPSG:2296</SRS>' +
+' <SRS>EPSG:2297</SRS>' +
+' <SRS>EPSG:2298</SRS>' +
+' <SRS>EPSG:2299</SRS>' +
+' <SRS>EPSG:2300</SRS>' +
+' <SRS>EPSG:2301</SRS>' +
+' <SRS>EPSG:2302</SRS>' +
+' <SRS>EPSG:2303</SRS>' +
+' <SRS>EPSG:2304</SRS>' +
+' <SRS>EPSG:2305</SRS>' +
+' <SRS>EPSG:2306</SRS>' +
+' <SRS>EPSG:2307</SRS>' +
+' <SRS>EPSG:2308</SRS>' +
+' <SRS>EPSG:2309</SRS>' +
+' <SRS>EPSG:2310</SRS>' +
+' <SRS>EPSG:2311</SRS>' +
+' <SRS>EPSG:2312</SRS>' +
+' <SRS>EPSG:2313</SRS>' +
+' <SRS>EPSG:2314</SRS>' +
+' <SRS>EPSG:2315</SRS>' +
+' <SRS>EPSG:2316</SRS>' +
+' <SRS>EPSG:2317</SRS>' +
+' <SRS>EPSG:2318</SRS>' +
+' <SRS>EPSG:2319</SRS>' +
+' <SRS>EPSG:2320</SRS>' +
+' <SRS>EPSG:2321</SRS>' +
+' <SRS>EPSG:2322</SRS>' +
+' <SRS>EPSG:2323</SRS>' +
+' <SRS>EPSG:2324</SRS>' +
+' <SRS>EPSG:2325</SRS>' +
+' <SRS>EPSG:2326</SRS>' +
+' <SRS>EPSG:2327</SRS>' +
+' <SRS>EPSG:2328</SRS>' +
+' <SRS>EPSG:2329</SRS>' +
+' <SRS>EPSG:2330</SRS>' +
+' <SRS>EPSG:2331</SRS>' +
+' <SRS>EPSG:2332</SRS>' +
+' <SRS>EPSG:2333</SRS>' +
+' <SRS>EPSG:2334</SRS>' +
+' <SRS>EPSG:2335</SRS>' +
+' <SRS>EPSG:2336</SRS>' +
+' <SRS>EPSG:2337</SRS>' +
+' <SRS>EPSG:2338</SRS>' +
+' <SRS>EPSG:2339</SRS>' +
+' <SRS>EPSG:2340</SRS>' +
+' <SRS>EPSG:2341</SRS>' +
+' <SRS>EPSG:2342</SRS>' +
+' <SRS>EPSG:2343</SRS>' +
+' <SRS>EPSG:2344</SRS>' +
+' <SRS>EPSG:2345</SRS>' +
+' <SRS>EPSG:2346</SRS>' +
+' <SRS>EPSG:2347</SRS>' +
+' <SRS>EPSG:2348</SRS>' +
+' <SRS>EPSG:2349</SRS>' +
+' <SRS>EPSG:2350</SRS>' +
+' <SRS>EPSG:2351</SRS>' +
+' <SRS>EPSG:2352</SRS>' +
+' <SRS>EPSG:2353</SRS>' +
+' <SRS>EPSG:2354</SRS>' +
+' <SRS>EPSG:2355</SRS>' +
+' <SRS>EPSG:2356</SRS>' +
+' <SRS>EPSG:2357</SRS>' +
+' <SRS>EPSG:2358</SRS>' +
+' <SRS>EPSG:2359</SRS>' +
+' <SRS>EPSG:2360</SRS>' +
+' <SRS>EPSG:2361</SRS>' +
+' <SRS>EPSG:2362</SRS>' +
+' <SRS>EPSG:2363</SRS>' +
+' <SRS>EPSG:2364</SRS>' +
+' <SRS>EPSG:2365</SRS>' +
+' <SRS>EPSG:2366</SRS>' +
+' <SRS>EPSG:2367</SRS>' +
+' <SRS>EPSG:2368</SRS>' +
+' <SRS>EPSG:2369</SRS>' +
+' <SRS>EPSG:2370</SRS>' +
+' <SRS>EPSG:2371</SRS>' +
+' <SRS>EPSG:2372</SRS>' +
+' <SRS>EPSG:2373</SRS>' +
+' <SRS>EPSG:2374</SRS>' +
+' <SRS>EPSG:2375</SRS>' +
+' <SRS>EPSG:2376</SRS>' +
+' <SRS>EPSG:2377</SRS>' +
+' <SRS>EPSG:2378</SRS>' +
+' <SRS>EPSG:2379</SRS>' +
+' <SRS>EPSG:2380</SRS>' +
+' <SRS>EPSG:2381</SRS>' +
+' <SRS>EPSG:2382</SRS>' +
+' <SRS>EPSG:2383</SRS>' +
+' <SRS>EPSG:2384</SRS>' +
+' <SRS>EPSG:2385</SRS>' +
+' <SRS>EPSG:2386</SRS>' +
+' <SRS>EPSG:2387</SRS>' +
+' <SRS>EPSG:2388</SRS>' +
+' <SRS>EPSG:2389</SRS>' +
+' <SRS>EPSG:2390</SRS>' +
+' <SRS>EPSG:2391</SRS>' +
+' <SRS>EPSG:2392</SRS>' +
+' <SRS>EPSG:2393</SRS>' +
+' <SRS>EPSG:2394</SRS>' +
+' <SRS>EPSG:2395</SRS>' +
+' <SRS>EPSG:2396</SRS>' +
+' <SRS>EPSG:2397</SRS>' +
+' <SRS>EPSG:2398</SRS>' +
+' <SRS>EPSG:2399</SRS>' +
+' <SRS>EPSG:2400</SRS>' +
+' <SRS>EPSG:2401</SRS>' +
+' <SRS>EPSG:2402</SRS>' +
+' <SRS>EPSG:2403</SRS>' +
+' <SRS>EPSG:2404</SRS>' +
+' <SRS>EPSG:2405</SRS>' +
+' <SRS>EPSG:2406</SRS>' +
+' <SRS>EPSG:2407</SRS>' +
+' <SRS>EPSG:2408</SRS>' +
+' <SRS>EPSG:2409</SRS>' +
+' <SRS>EPSG:2410</SRS>' +
+' <SRS>EPSG:2411</SRS>' +
+' <SRS>EPSG:2412</SRS>' +
+' <SRS>EPSG:2413</SRS>' +
+' <SRS>EPSG:2414</SRS>' +
+' <SRS>EPSG:2415</SRS>' +
+' <SRS>EPSG:2416</SRS>' +
+' <SRS>EPSG:2417</SRS>' +
+' <SRS>EPSG:2418</SRS>' +
+' <SRS>EPSG:2419</SRS>' +
+' <SRS>EPSG:2420</SRS>' +
+' <SRS>EPSG:2421</SRS>' +
+' <SRS>EPSG:2422</SRS>' +
+' <SRS>EPSG:2423</SRS>' +
+' <SRS>EPSG:2424</SRS>' +
+' <SRS>EPSG:2425</SRS>' +
+' <SRS>EPSG:2426</SRS>' +
+' <SRS>EPSG:2427</SRS>' +
+' <SRS>EPSG:2428</SRS>' +
+' <SRS>EPSG:2429</SRS>' +
+' <SRS>EPSG:2430</SRS>' +
+' <SRS>EPSG:2431</SRS>' +
+' <SRS>EPSG:2432</SRS>' +
+' <SRS>EPSG:2433</SRS>' +
+' <SRS>EPSG:2434</SRS>' +
+' <SRS>EPSG:2435</SRS>' +
+' <SRS>EPSG:2436</SRS>' +
+' <SRS>EPSG:2437</SRS>' +
+' <SRS>EPSG:2438</SRS>' +
+' <SRS>EPSG:2439</SRS>' +
+' <SRS>EPSG:2440</SRS>' +
+' <SRS>EPSG:2441</SRS>' +
+' <SRS>EPSG:2442</SRS>' +
+' <SRS>EPSG:2443</SRS>' +
+' <SRS>EPSG:2444</SRS>' +
+' <SRS>EPSG:2445</SRS>' +
+' <SRS>EPSG:2446</SRS>' +
+' <SRS>EPSG:2447</SRS>' +
+' <SRS>EPSG:2448</SRS>' +
+' <SRS>EPSG:2449</SRS>' +
+' <SRS>EPSG:2450</SRS>' +
+' <SRS>EPSG:2451</SRS>' +
+' <SRS>EPSG:2452</SRS>' +
+' <SRS>EPSG:2453</SRS>' +
+' <SRS>EPSG:2454</SRS>' +
+' <SRS>EPSG:2455</SRS>' +
+' <SRS>EPSG:2456</SRS>' +
+' <SRS>EPSG:2457</SRS>' +
+' <SRS>EPSG:2458</SRS>' +
+' <SRS>EPSG:2459</SRS>' +
+' <SRS>EPSG:2460</SRS>' +
+' <SRS>EPSG:2461</SRS>' +
+' <SRS>EPSG:2462</SRS>' +
+' <SRS>EPSG:2463</SRS>' +
+' <SRS>EPSG:2464</SRS>' +
+' <SRS>EPSG:2465</SRS>' +
+' <SRS>EPSG:2466</SRS>' +
+' <SRS>EPSG:2467</SRS>' +
+' <SRS>EPSG:2468</SRS>' +
+' <SRS>EPSG:2469</SRS>' +
+' <SRS>EPSG:2470</SRS>' +
+' <SRS>EPSG:2471</SRS>' +
+' <SRS>EPSG:2472</SRS>' +
+' <SRS>EPSG:2473</SRS>' +
+' <SRS>EPSG:2474</SRS>' +
+' <SRS>EPSG:2475</SRS>' +
+' <SRS>EPSG:2476</SRS>' +
+' <SRS>EPSG:2477</SRS>' +
+' <SRS>EPSG:2478</SRS>' +
+' <SRS>EPSG:2479</SRS>' +
+' <SRS>EPSG:2480</SRS>' +
+' <SRS>EPSG:2481</SRS>' +
+' <SRS>EPSG:2482</SRS>' +
+' <SRS>EPSG:2483</SRS>' +
+' <SRS>EPSG:2484</SRS>' +
+' <SRS>EPSG:2485</SRS>' +
+' <SRS>EPSG:2486</SRS>' +
+' <SRS>EPSG:2487</SRS>' +
+' <SRS>EPSG:2488</SRS>' +
+' <SRS>EPSG:2489</SRS>' +
+' <SRS>EPSG:2490</SRS>' +
+' <SRS>EPSG:2491</SRS>' +
+' <SRS>EPSG:2492</SRS>' +
+' <SRS>EPSG:2493</SRS>' +
+' <SRS>EPSG:2494</SRS>' +
+' <SRS>EPSG:2495</SRS>' +
+' <SRS>EPSG:2496</SRS>' +
+' <SRS>EPSG:2497</SRS>' +
+' <SRS>EPSG:2498</SRS>' +
+' <SRS>EPSG:2499</SRS>' +
+' <SRS>EPSG:2500</SRS>' +
+' <SRS>EPSG:2501</SRS>' +
+' <SRS>EPSG:2502</SRS>' +
+' <SRS>EPSG:2503</SRS>' +
+' <SRS>EPSG:2504</SRS>' +
+' <SRS>EPSG:2505</SRS>' +
+' <SRS>EPSG:2506</SRS>' +
+' <SRS>EPSG:2507</SRS>' +
+' <SRS>EPSG:2508</SRS>' +
+' <SRS>EPSG:2509</SRS>' +
+' <SRS>EPSG:2510</SRS>' +
+' <SRS>EPSG:2511</SRS>' +
+' <SRS>EPSG:2512</SRS>' +
+' <SRS>EPSG:2513</SRS>' +
+' <SRS>EPSG:2514</SRS>' +
+' <SRS>EPSG:2515</SRS>' +
+' <SRS>EPSG:2516</SRS>' +
+' <SRS>EPSG:2517</SRS>' +
+' <SRS>EPSG:2518</SRS>' +
+' <SRS>EPSG:2519</SRS>' +
+' <SRS>EPSG:2520</SRS>' +
+' <SRS>EPSG:2521</SRS>' +
+' <SRS>EPSG:2522</SRS>' +
+' <SRS>EPSG:2523</SRS>' +
+' <SRS>EPSG:2524</SRS>' +
+' <SRS>EPSG:2525</SRS>' +
+' <SRS>EPSG:2526</SRS>' +
+' <SRS>EPSG:2527</SRS>' +
+' <SRS>EPSG:2528</SRS>' +
+' <SRS>EPSG:2529</SRS>' +
+' <SRS>EPSG:2530</SRS>' +
+' <SRS>EPSG:2531</SRS>' +
+' <SRS>EPSG:2532</SRS>' +
+' <SRS>EPSG:2533</SRS>' +
+' <SRS>EPSG:2534</SRS>' +
+' <SRS>EPSG:2535</SRS>' +
+' <SRS>EPSG:2536</SRS>' +
+' <SRS>EPSG:2537</SRS>' +
+' <SRS>EPSG:2538</SRS>' +
+' <SRS>EPSG:2539</SRS>' +
+' <SRS>EPSG:2540</SRS>' +
+' <SRS>EPSG:2541</SRS>' +
+' <SRS>EPSG:2542</SRS>' +
+' <SRS>EPSG:2543</SRS>' +
+' <SRS>EPSG:2544</SRS>' +
+' <SRS>EPSG:2545</SRS>' +
+' <SRS>EPSG:2546</SRS>' +
+' <SRS>EPSG:2547</SRS>' +
+' <SRS>EPSG:2548</SRS>' +
+' <SRS>EPSG:2549</SRS>' +
+' <SRS>EPSG:2550</SRS>' +
+' <SRS>EPSG:2551</SRS>' +
+' <SRS>EPSG:2552</SRS>' +
+' <SRS>EPSG:2553</SRS>' +
+' <SRS>EPSG:2554</SRS>' +
+' <SRS>EPSG:2555</SRS>' +
+' <SRS>EPSG:2556</SRS>' +
+' <SRS>EPSG:2557</SRS>' +
+' <SRS>EPSG:2558</SRS>' +
+' <SRS>EPSG:2559</SRS>' +
+' <SRS>EPSG:2560</SRS>' +
+' <SRS>EPSG:2561</SRS>' +
+' <SRS>EPSG:2562</SRS>' +
+' <SRS>EPSG:2563</SRS>' +
+' <SRS>EPSG:2564</SRS>' +
+' <SRS>EPSG:2565</SRS>' +
+' <SRS>EPSG:2566</SRS>' +
+' <SRS>EPSG:2567</SRS>' +
+' <SRS>EPSG:2568</SRS>' +
+' <SRS>EPSG:2569</SRS>' +
+' <SRS>EPSG:2570</SRS>' +
+' <SRS>EPSG:2571</SRS>' +
+' <SRS>EPSG:2572</SRS>' +
+' <SRS>EPSG:2573</SRS>' +
+' <SRS>EPSG:2574</SRS>' +
+' <SRS>EPSG:2575</SRS>' +
+' <SRS>EPSG:2576</SRS>' +
+' <SRS>EPSG:2577</SRS>' +
+' <SRS>EPSG:2578</SRS>' +
+' <SRS>EPSG:2579</SRS>' +
+' <SRS>EPSG:2580</SRS>' +
+' <SRS>EPSG:2581</SRS>' +
+' <SRS>EPSG:2582</SRS>' +
+' <SRS>EPSG:2583</SRS>' +
+' <SRS>EPSG:2584</SRS>' +
+' <SRS>EPSG:2585</SRS>' +
+' <SRS>EPSG:2586</SRS>' +
+' <SRS>EPSG:2587</SRS>' +
+' <SRS>EPSG:2588</SRS>' +
+' <SRS>EPSG:2589</SRS>' +
+' <SRS>EPSG:2590</SRS>' +
+' <SRS>EPSG:2591</SRS>' +
+' <SRS>EPSG:2592</SRS>' +
+' <SRS>EPSG:2593</SRS>' +
+' <SRS>EPSG:2594</SRS>' +
+' <SRS>EPSG:2595</SRS>' +
+' <SRS>EPSG:2596</SRS>' +
+' <SRS>EPSG:2597</SRS>' +
+' <SRS>EPSG:2598</SRS>' +
+' <SRS>EPSG:2599</SRS>' +
+' <SRS>EPSG:2600</SRS>' +
+' <SRS>EPSG:2601</SRS>' +
+' <SRS>EPSG:2602</SRS>' +
+' <SRS>EPSG:2603</SRS>' +
+' <SRS>EPSG:2604</SRS>' +
+' <SRS>EPSG:2605</SRS>' +
+' <SRS>EPSG:2606</SRS>' +
+' <SRS>EPSG:2607</SRS>' +
+' <SRS>EPSG:2608</SRS>' +
+' <SRS>EPSG:2609</SRS>' +
+' <SRS>EPSG:2610</SRS>' +
+' <SRS>EPSG:2611</SRS>' +
+' <SRS>EPSG:2612</SRS>' +
+' <SRS>EPSG:2613</SRS>' +
+' <SRS>EPSG:2614</SRS>' +
+' <SRS>EPSG:2615</SRS>' +
+' <SRS>EPSG:2616</SRS>' +
+' <SRS>EPSG:2617</SRS>' +
+' <SRS>EPSG:2618</SRS>' +
+' <SRS>EPSG:2619</SRS>' +
+' <SRS>EPSG:2620</SRS>' +
+' <SRS>EPSG:2621</SRS>' +
+' <SRS>EPSG:2622</SRS>' +
+' <SRS>EPSG:2623</SRS>' +
+' <SRS>EPSG:2624</SRS>' +
+' <SRS>EPSG:2625</SRS>' +
+' <SRS>EPSG:2626</SRS>' +
+' <SRS>EPSG:2627</SRS>' +
+' <SRS>EPSG:2628</SRS>' +
+' <SRS>EPSG:2629</SRS>' +
+' <SRS>EPSG:2630</SRS>' +
+' <SRS>EPSG:2631</SRS>' +
+' <SRS>EPSG:2632</SRS>' +
+' <SRS>EPSG:2633</SRS>' +
+' <SRS>EPSG:2634</SRS>' +
+' <SRS>EPSG:2635</SRS>' +
+' <SRS>EPSG:2636</SRS>' +
+' <SRS>EPSG:2637</SRS>' +
+' <SRS>EPSG:2638</SRS>' +
+' <SRS>EPSG:2639</SRS>' +
+' <SRS>EPSG:2640</SRS>' +
+' <SRS>EPSG:2641</SRS>' +
+' <SRS>EPSG:2642</SRS>' +
+' <SRS>EPSG:2643</SRS>' +
+' <SRS>EPSG:2644</SRS>' +
+' <SRS>EPSG:2645</SRS>' +
+' <SRS>EPSG:2646</SRS>' +
+' <SRS>EPSG:2647</SRS>' +
+' <SRS>EPSG:2648</SRS>' +
+' <SRS>EPSG:2649</SRS>' +
+' <SRS>EPSG:2650</SRS>' +
+' <SRS>EPSG:2651</SRS>' +
+' <SRS>EPSG:2652</SRS>' +
+' <SRS>EPSG:2653</SRS>' +
+' <SRS>EPSG:2654</SRS>' +
+' <SRS>EPSG:2655</SRS>' +
+' <SRS>EPSG:2656</SRS>' +
+' <SRS>EPSG:2657</SRS>' +
+' <SRS>EPSG:2658</SRS>' +
+' <SRS>EPSG:2659</SRS>' +
+' <SRS>EPSG:2660</SRS>' +
+' <SRS>EPSG:2661</SRS>' +
+' <SRS>EPSG:2662</SRS>' +
+' <SRS>EPSG:2663</SRS>' +
+' <SRS>EPSG:2664</SRS>' +
+' <SRS>EPSG:2665</SRS>' +
+' <SRS>EPSG:2666</SRS>' +
+' <SRS>EPSG:2667</SRS>' +
+' <SRS>EPSG:2668</SRS>' +
+' <SRS>EPSG:2669</SRS>' +
+' <SRS>EPSG:2670</SRS>' +
+' <SRS>EPSG:2671</SRS>' +
+' <SRS>EPSG:2672</SRS>' +
+' <SRS>EPSG:2673</SRS>' +
+' <SRS>EPSG:2674</SRS>' +
+' <SRS>EPSG:2675</SRS>' +
+' <SRS>EPSG:2676</SRS>' +
+' <SRS>EPSG:2677</SRS>' +
+' <SRS>EPSG:2678</SRS>' +
+' <SRS>EPSG:2679</SRS>' +
+' <SRS>EPSG:2680</SRS>' +
+' <SRS>EPSG:2681</SRS>' +
+' <SRS>EPSG:2682</SRS>' +
+' <SRS>EPSG:2683</SRS>' +
+' <SRS>EPSG:2684</SRS>' +
+' <SRS>EPSG:2685</SRS>' +
+' <SRS>EPSG:2686</SRS>' +
+' <SRS>EPSG:2687</SRS>' +
+' <SRS>EPSG:2688</SRS>' +
+' <SRS>EPSG:2689</SRS>' +
+' <SRS>EPSG:2690</SRS>' +
+' <SRS>EPSG:2691</SRS>' +
+' <SRS>EPSG:2692</SRS>' +
+' <SRS>EPSG:2693</SRS>' +
+' <SRS>EPSG:2694</SRS>' +
+' <SRS>EPSG:2695</SRS>' +
+' <SRS>EPSG:2696</SRS>' +
+' <SRS>EPSG:2697</SRS>' +
+' <SRS>EPSG:2698</SRS>' +
+' <SRS>EPSG:2699</SRS>' +
+' <SRS>EPSG:2700</SRS>' +
+' <SRS>EPSG:2701</SRS>' +
+' <SRS>EPSG:2702</SRS>' +
+' <SRS>EPSG:2703</SRS>' +
+' <SRS>EPSG:2704</SRS>' +
+' <SRS>EPSG:2705</SRS>' +
+' <SRS>EPSG:2706</SRS>' +
+' <SRS>EPSG:2707</SRS>' +
+' <SRS>EPSG:2708</SRS>' +
+' <SRS>EPSG:2709</SRS>' +
+' <SRS>EPSG:2710</SRS>' +
+' <SRS>EPSG:2711</SRS>' +
+' <SRS>EPSG:2712</SRS>' +
+' <SRS>EPSG:2713</SRS>' +
+' <SRS>EPSG:2714</SRS>' +
+' <SRS>EPSG:2715</SRS>' +
+' <SRS>EPSG:2716</SRS>' +
+' <SRS>EPSG:2717</SRS>' +
+' <SRS>EPSG:2718</SRS>' +
+' <SRS>EPSG:2719</SRS>' +
+' <SRS>EPSG:2720</SRS>' +
+' <SRS>EPSG:2721</SRS>' +
+' <SRS>EPSG:2722</SRS>' +
+' <SRS>EPSG:2723</SRS>' +
+' <SRS>EPSG:2724</SRS>' +
+' <SRS>EPSG:2725</SRS>' +
+' <SRS>EPSG:2726</SRS>' +
+' <SRS>EPSG:2727</SRS>' +
+' <SRS>EPSG:2728</SRS>' +
+' <SRS>EPSG:2729</SRS>' +
+' <SRS>EPSG:2730</SRS>' +
+' <SRS>EPSG:2731</SRS>' +
+' <SRS>EPSG:2732</SRS>' +
+' <SRS>EPSG:2733</SRS>' +
+' <SRS>EPSG:2734</SRS>' +
+' <SRS>EPSG:2735</SRS>' +
+' <SRS>EPSG:2736</SRS>' +
+' <SRS>EPSG:2737</SRS>' +
+' <SRS>EPSG:2738</SRS>' +
+' <SRS>EPSG:2739</SRS>' +
+' <SRS>EPSG:2740</SRS>' +
+' <SRS>EPSG:2741</SRS>' +
+' <SRS>EPSG:2742</SRS>' +
+' <SRS>EPSG:2743</SRS>' +
+' <SRS>EPSG:2744</SRS>' +
+' <SRS>EPSG:2745</SRS>' +
+' <SRS>EPSG:2746</SRS>' +
+' <SRS>EPSG:2747</SRS>' +
+' <SRS>EPSG:2748</SRS>' +
+' <SRS>EPSG:2749</SRS>' +
+' <SRS>EPSG:2750</SRS>' +
+' <SRS>EPSG:2751</SRS>' +
+' <SRS>EPSG:2752</SRS>' +
+' <SRS>EPSG:2753</SRS>' +
+' <SRS>EPSG:2754</SRS>' +
+' <SRS>EPSG:2755</SRS>' +
+' <SRS>EPSG:2756</SRS>' +
+' <SRS>EPSG:2757</SRS>' +
+' <SRS>EPSG:2758</SRS>' +
+' <SRS>EPSG:2759</SRS>' +
+' <SRS>EPSG:2760</SRS>' +
+' <SRS>EPSG:2761</SRS>' +
+' <SRS>EPSG:2762</SRS>' +
+' <SRS>EPSG:2763</SRS>' +
+' <SRS>EPSG:2764</SRS>' +
+' <SRS>EPSG:2765</SRS>' +
+' <SRS>EPSG:2766</SRS>' +
+' <SRS>EPSG:2767</SRS>' +
+' <SRS>EPSG:2768</SRS>' +
+' <SRS>EPSG:2769</SRS>' +
+' <SRS>EPSG:2770</SRS>' +
+' <SRS>EPSG:2771</SRS>' +
+' <SRS>EPSG:2772</SRS>' +
+' <SRS>EPSG:2773</SRS>' +
+' <SRS>EPSG:2774</SRS>' +
+' <SRS>EPSG:2775</SRS>' +
+' <SRS>EPSG:2776</SRS>' +
+' <SRS>EPSG:2777</SRS>' +
+' <SRS>EPSG:2778</SRS>' +
+' <SRS>EPSG:2779</SRS>' +
+' <SRS>EPSG:2780</SRS>' +
+' <SRS>EPSG:2781</SRS>' +
+' <SRS>EPSG:2782</SRS>' +
+' <SRS>EPSG:2783</SRS>' +
+' <SRS>EPSG:2784</SRS>' +
+' <SRS>EPSG:2785</SRS>' +
+' <SRS>EPSG:2786</SRS>' +
+' <SRS>EPSG:2787</SRS>' +
+' <SRS>EPSG:2788</SRS>' +
+' <SRS>EPSG:2789</SRS>' +
+' <SRS>EPSG:2790</SRS>' +
+' <SRS>EPSG:2791</SRS>' +
+' <SRS>EPSG:2792</SRS>' +
+' <SRS>EPSG:2793</SRS>' +
+' <SRS>EPSG:2794</SRS>' +
+' <SRS>EPSG:2795</SRS>' +
+' <SRS>EPSG:2796</SRS>' +
+' <SRS>EPSG:2797</SRS>' +
+' <SRS>EPSG:2798</SRS>' +
+' <SRS>EPSG:2799</SRS>' +
+' <SRS>EPSG:2800</SRS>' +
+' <SRS>EPSG:2801</SRS>' +
+' <SRS>EPSG:2802</SRS>' +
+' <SRS>EPSG:2803</SRS>' +
+' <SRS>EPSG:2804</SRS>' +
+' <SRS>EPSG:2805</SRS>' +
+' <SRS>EPSG:2806</SRS>' +
+' <SRS>EPSG:2807</SRS>' +
+' <SRS>EPSG:2808</SRS>' +
+' <SRS>EPSG:2809</SRS>' +
+' <SRS>EPSG:2810</SRS>' +
+' <SRS>EPSG:2811</SRS>' +
+' <SRS>EPSG:2812</SRS>' +
+' <SRS>EPSG:2813</SRS>' +
+' <SRS>EPSG:2814</SRS>' +
+' <SRS>EPSG:2815</SRS>' +
+' <SRS>EPSG:2816</SRS>' +
+' <SRS>EPSG:2817</SRS>' +
+' <SRS>EPSG:2818</SRS>' +
+' <SRS>EPSG:2819</SRS>' +
+' <SRS>EPSG:2820</SRS>' +
+' <SRS>EPSG:2821</SRS>' +
+' <SRS>EPSG:2822</SRS>' +
+' <SRS>EPSG:2823</SRS>' +
+' <SRS>EPSG:2824</SRS>' +
+' <SRS>EPSG:2825</SRS>' +
+' <SRS>EPSG:2826</SRS>' +
+' <SRS>EPSG:2827</SRS>' +
+' <SRS>EPSG:2828</SRS>' +
+' <SRS>EPSG:2829</SRS>' +
+' <SRS>EPSG:2830</SRS>' +
+' <SRS>EPSG:2831</SRS>' +
+' <SRS>EPSG:2832</SRS>' +
+' <SRS>EPSG:2833</SRS>' +
+' <SRS>EPSG:2834</SRS>' +
+' <SRS>EPSG:2835</SRS>' +
+' <SRS>EPSG:2836</SRS>' +
+' <SRS>EPSG:2837</SRS>' +
+' <SRS>EPSG:2838</SRS>' +
+' <SRS>EPSG:2839</SRS>' +
+' <SRS>EPSG:2840</SRS>' +
+' <SRS>EPSG:2841</SRS>' +
+' <SRS>EPSG:2842</SRS>' +
+' <SRS>EPSG:2843</SRS>' +
+' <SRS>EPSG:2844</SRS>' +
+' <SRS>EPSG:2845</SRS>' +
+' <SRS>EPSG:2846</SRS>' +
+' <SRS>EPSG:2847</SRS>' +
+' <SRS>EPSG:2848</SRS>' +
+' <SRS>EPSG:2849</SRS>' +
+' <SRS>EPSG:2850</SRS>' +
+' <SRS>EPSG:2851</SRS>' +
+' <SRS>EPSG:2852</SRS>' +
+' <SRS>EPSG:2853</SRS>' +
+' <SRS>EPSG:2854</SRS>' +
+' <SRS>EPSG:2855</SRS>' +
+' <SRS>EPSG:2856</SRS>' +
+' <SRS>EPSG:2857</SRS>' +
+' <SRS>EPSG:2858</SRS>' +
+' <SRS>EPSG:2859</SRS>' +
+' <SRS>EPSG:2860</SRS>' +
+' <SRS>EPSG:2861</SRS>' +
+' <SRS>EPSG:2862</SRS>' +
+' <SRS>EPSG:2863</SRS>' +
+' <SRS>EPSG:2864</SRS>' +
+' <SRS>EPSG:2865</SRS>' +
+' <SRS>EPSG:2866</SRS>' +
+' <SRS>EPSG:2867</SRS>' +
+' <SRS>EPSG:2868</SRS>' +
+' <SRS>EPSG:2869</SRS>' +
+' <SRS>EPSG:2870</SRS>' +
+' <SRS>EPSG:2871</SRS>' +
+' <SRS>EPSG:2872</SRS>' +
+' <SRS>EPSG:2873</SRS>' +
+' <SRS>EPSG:2874</SRS>' +
+' <SRS>EPSG:2875</SRS>' +
+' <SRS>EPSG:2876</SRS>' +
+' <SRS>EPSG:2877</SRS>' +
+' <SRS>EPSG:2878</SRS>' +
+' <SRS>EPSG:2879</SRS>' +
+' <SRS>EPSG:2880</SRS>' +
+' <SRS>EPSG:2881</SRS>' +
+' <SRS>EPSG:2882</SRS>' +
+' <SRS>EPSG:2883</SRS>' +
+' <SRS>EPSG:2884</SRS>' +
+' <SRS>EPSG:2885</SRS>' +
+' <SRS>EPSG:2886</SRS>' +
+' <SRS>EPSG:2887</SRS>' +
+' <SRS>EPSG:2888</SRS>' +
+' <SRS>EPSG:2889</SRS>' +
+' <SRS>EPSG:2890</SRS>' +
+' <SRS>EPSG:2891</SRS>' +
+' <SRS>EPSG:2892</SRS>' +
+' <SRS>EPSG:2893</SRS>' +
+' <SRS>EPSG:2894</SRS>' +
+' <SRS>EPSG:2895</SRS>' +
+' <SRS>EPSG:2896</SRS>' +
+' <SRS>EPSG:2897</SRS>' +
+' <SRS>EPSG:2898</SRS>' +
+' <SRS>EPSG:2899</SRS>' +
+' <SRS>EPSG:2900</SRS>' +
+' <SRS>EPSG:2901</SRS>' +
+' <SRS>EPSG:2902</SRS>' +
+' <SRS>EPSG:2903</SRS>' +
+' <SRS>EPSG:2904</SRS>' +
+' <SRS>EPSG:2905</SRS>' +
+' <SRS>EPSG:2906</SRS>' +
+' <SRS>EPSG:2907</SRS>' +
+' <SRS>EPSG:2908</SRS>' +
+' <SRS>EPSG:2909</SRS>' +
+' <SRS>EPSG:2910</SRS>' +
+' <SRS>EPSG:2911</SRS>' +
+' <SRS>EPSG:2912</SRS>' +
+' <SRS>EPSG:2913</SRS>' +
+' <SRS>EPSG:2914</SRS>' +
+' <SRS>EPSG:2915</SRS>' +
+' <SRS>EPSG:2916</SRS>' +
+' <SRS>EPSG:2917</SRS>' +
+' <SRS>EPSG:2918</SRS>' +
+' <SRS>EPSG:2919</SRS>' +
+' <SRS>EPSG:2920</SRS>' +
+' <SRS>EPSG:2921</SRS>' +
+' <SRS>EPSG:2922</SRS>' +
+' <SRS>EPSG:2923</SRS>' +
+' <SRS>EPSG:2924</SRS>' +
+' <SRS>EPSG:2925</SRS>' +
+' <SRS>EPSG:2926</SRS>' +
+' <SRS>EPSG:2927</SRS>' +
+' <SRS>EPSG:2928</SRS>' +
+' <SRS>EPSG:2929</SRS>' +
+' <SRS>EPSG:2930</SRS>' +
+' <SRS>EPSG:2931</SRS>' +
+' <SRS>EPSG:2932</SRS>' +
+' <SRS>EPSG:2933</SRS>' +
+' <SRS>EPSG:2934</SRS>' +
+' <SRS>EPSG:2935</SRS>' +
+' <SRS>EPSG:2936</SRS>' +
+' <SRS>EPSG:2937</SRS>' +
+' <SRS>EPSG:2938</SRS>' +
+' <SRS>EPSG:2939</SRS>' +
+' <SRS>EPSG:2940</SRS>' +
+' <SRS>EPSG:2941</SRS>' +
+' <SRS>EPSG:2942</SRS>' +
+' <SRS>EPSG:2943</SRS>' +
+' <SRS>EPSG:2944</SRS>' +
+' <SRS>EPSG:2945</SRS>' +
+' <SRS>EPSG:2946</SRS>' +
+' <SRS>EPSG:2947</SRS>' +
+' <SRS>EPSG:2948</SRS>' +
+' <SRS>EPSG:2949</SRS>' +
+' <SRS>EPSG:2950</SRS>' +
+' <SRS>EPSG:2951</SRS>' +
+' <SRS>EPSG:2952</SRS>' +
+' <SRS>EPSG:2953</SRS>' +
+' <SRS>EPSG:2954</SRS>' +
+' <SRS>EPSG:2955</SRS>' +
+' <SRS>EPSG:2956</SRS>' +
+' <SRS>EPSG:2957</SRS>' +
+' <SRS>EPSG:2958</SRS>' +
+' <SRS>EPSG:2959</SRS>' +
+' <SRS>EPSG:2960</SRS>' +
+' <SRS>EPSG:2961</SRS>' +
+' <SRS>EPSG:2962</SRS>' +
+' <SRS>EPSG:2963</SRS>' +
+' <SRS>EPSG:2964</SRS>' +
+' <SRS>EPSG:2965</SRS>' +
+' <SRS>EPSG:2966</SRS>' +
+' <SRS>EPSG:2967</SRS>' +
+' <SRS>EPSG:2968</SRS>' +
+' <SRS>EPSG:2969</SRS>' +
+' <SRS>EPSG:2970</SRS>' +
+' <SRS>EPSG:2971</SRS>' +
+' <SRS>EPSG:2972</SRS>' +
+' <SRS>EPSG:2973</SRS>' +
+' <SRS>EPSG:2975</SRS>' +
+' <SRS>EPSG:2976</SRS>' +
+' <SRS>EPSG:2977</SRS>' +
+' <SRS>EPSG:2978</SRS>' +
+' <SRS>EPSG:2979</SRS>' +
+' <SRS>EPSG:2980</SRS>' +
+' <SRS>EPSG:2981</SRS>' +
+' <SRS>EPSG:2982</SRS>' +
+' <SRS>EPSG:2983</SRS>' +
+' <SRS>EPSG:2984</SRS>' +
+' <SRS>EPSG:2985</SRS>' +
+' <SRS>EPSG:2986</SRS>' +
+' <SRS>EPSG:2987</SRS>' +
+' <SRS>EPSG:2988</SRS>' +
+' <SRS>EPSG:2989</SRS>' +
+' <SRS>EPSG:2990</SRS>' +
+' <SRS>EPSG:2991</SRS>' +
+' <SRS>EPSG:2992</SRS>' +
+' <SRS>EPSG:2993</SRS>' +
+' <SRS>EPSG:2994</SRS>' +
+' <SRS>EPSG:2995</SRS>' +
+' <SRS>EPSG:2996</SRS>' +
+' <SRS>EPSG:2997</SRS>' +
+' <SRS>EPSG:2998</SRS>' +
+' <SRS>EPSG:2999</SRS>' +
+' <SRS>EPSG:3000</SRS>' +
+' <SRS>EPSG:3001</SRS>' +
+' <SRS>EPSG:3002</SRS>' +
+' <SRS>EPSG:3003</SRS>' +
+' <SRS>EPSG:3004</SRS>' +
+' <SRS>EPSG:3005</SRS>' +
+' <SRS>EPSG:3006</SRS>' +
+' <SRS>EPSG:3007</SRS>' +
+' <SRS>EPSG:3008</SRS>' +
+' <SRS>EPSG:3009</SRS>' +
+' <SRS>EPSG:3010</SRS>' +
+' <SRS>EPSG:3011</SRS>' +
+' <SRS>EPSG:3012</SRS>' +
+' <SRS>EPSG:3013</SRS>' +
+' <SRS>EPSG:3014</SRS>' +
+' <SRS>EPSG:3015</SRS>' +
+' <SRS>EPSG:3016</SRS>' +
+' <SRS>EPSG:3017</SRS>' +
+' <SRS>EPSG:3018</SRS>' +
+' <SRS>EPSG:3019</SRS>' +
+' <SRS>EPSG:3020</SRS>' +
+' <SRS>EPSG:3021</SRS>' +
+' <SRS>EPSG:3022</SRS>' +
+' <SRS>EPSG:3023</SRS>' +
+' <SRS>EPSG:3024</SRS>' +
+' <SRS>EPSG:3025</SRS>' +
+' <SRS>EPSG:3026</SRS>' +
+' <SRS>EPSG:3027</SRS>' +
+' <SRS>EPSG:3028</SRS>' +
+' <SRS>EPSG:3029</SRS>' +
+' <SRS>EPSG:3030</SRS>' +
+' <SRS>EPSG:3031</SRS>' +
+' <SRS>EPSG:3032</SRS>' +
+' <SRS>EPSG:3033</SRS>' +
+' <SRS>EPSG:3034</SRS>' +
+' <SRS>EPSG:3035</SRS>' +
+' <SRS>EPSG:3036</SRS>' +
+' <SRS>EPSG:3037</SRS>' +
+' <SRS>EPSG:3038</SRS>' +
+' <SRS>EPSG:3039</SRS>' +
+' <SRS>EPSG:3040</SRS>' +
+' <SRS>EPSG:3041</SRS>' +
+' <SRS>EPSG:3042</SRS>' +
+' <SRS>EPSG:3043</SRS>' +
+' <SRS>EPSG:3044</SRS>' +
+' <SRS>EPSG:3045</SRS>' +
+' <SRS>EPSG:3046</SRS>' +
+' <SRS>EPSG:3047</SRS>' +
+' <SRS>EPSG:3048</SRS>' +
+' <SRS>EPSG:3049</SRS>' +
+' <SRS>EPSG:3050</SRS>' +
+' <SRS>EPSG:3051</SRS>' +
+' <SRS>EPSG:3052</SRS>' +
+' <SRS>EPSG:3053</SRS>' +
+' <SRS>EPSG:3054</SRS>' +
+' <SRS>EPSG:3055</SRS>' +
+' <SRS>EPSG:3056</SRS>' +
+' <SRS>EPSG:3057</SRS>' +
+' <SRS>EPSG:3058</SRS>' +
+' <SRS>EPSG:3059</SRS>' +
+' <SRS>EPSG:3060</SRS>' +
+' <SRS>EPSG:3061</SRS>' +
+' <SRS>EPSG:3062</SRS>' +
+' <SRS>EPSG:3063</SRS>' +
+' <SRS>EPSG:3064</SRS>' +
+' <SRS>EPSG:3065</SRS>' +
+' <SRS>EPSG:3066</SRS>' +
+' <SRS>EPSG:3067</SRS>' +
+' <SRS>EPSG:3068</SRS>' +
+' <SRS>EPSG:3069</SRS>' +
+' <SRS>EPSG:3070</SRS>' +
+' <SRS>EPSG:3071</SRS>' +
+' <SRS>EPSG:3072</SRS>' +
+' <SRS>EPSG:3073</SRS>' +
+' <SRS>EPSG:3074</SRS>' +
+' <SRS>EPSG:3075</SRS>' +
+' <SRS>EPSG:3076</SRS>' +
+' <SRS>EPSG:3077</SRS>' +
+' <SRS>EPSG:3078</SRS>' +
+' <SRS>EPSG:3079</SRS>' +
+' <SRS>EPSG:3080</SRS>' +
+' <SRS>EPSG:3081</SRS>' +
+' <SRS>EPSG:3082</SRS>' +
+' <SRS>EPSG:3083</SRS>' +
+' <SRS>EPSG:3084</SRS>' +
+' <SRS>EPSG:3085</SRS>' +
+' <SRS>EPSG:3086</SRS>' +
+' <SRS>EPSG:3087</SRS>' +
+' <SRS>EPSG:3088</SRS>' +
+' <SRS>EPSG:3089</SRS>' +
+' <SRS>EPSG:3090</SRS>' +
+' <SRS>EPSG:3091</SRS>' +
+' <SRS>EPSG:3092</SRS>' +
+' <SRS>EPSG:3093</SRS>' +
+' <SRS>EPSG:3094</SRS>' +
+' <SRS>EPSG:3095</SRS>' +
+' <SRS>EPSG:3096</SRS>' +
+' <SRS>EPSG:3097</SRS>' +
+' <SRS>EPSG:3098</SRS>' +
+' <SRS>EPSG:3099</SRS>' +
+' <SRS>EPSG:3100</SRS>' +
+' <SRS>EPSG:3101</SRS>' +
+' <SRS>EPSG:3102</SRS>' +
+' <SRS>EPSG:3103</SRS>' +
+' <SRS>EPSG:3104</SRS>' +
+' <SRS>EPSG:3105</SRS>' +
+' <SRS>EPSG:3106</SRS>' +
+' <SRS>EPSG:3107</SRS>' +
+' <SRS>EPSG:3108</SRS>' +
+' <SRS>EPSG:3109</SRS>' +
+' <SRS>EPSG:3110</SRS>' +
+' <SRS>EPSG:3111</SRS>' +
+' <SRS>EPSG:3112</SRS>' +
+' <SRS>EPSG:3113</SRS>' +
+' <SRS>EPSG:3114</SRS>' +
+' <SRS>EPSG:3115</SRS>' +
+' <SRS>EPSG:3116</SRS>' +
+' <SRS>EPSG:3117</SRS>' +
+' <SRS>EPSG:3118</SRS>' +
+' <SRS>EPSG:3119</SRS>' +
+' <SRS>EPSG:3120</SRS>' +
+' <SRS>EPSG:3121</SRS>' +
+' <SRS>EPSG:3122</SRS>' +
+' <SRS>EPSG:3123</SRS>' +
+' <SRS>EPSG:3124</SRS>' +
+' <SRS>EPSG:3125</SRS>' +
+' <SRS>EPSG:3126</SRS>' +
+' <SRS>EPSG:3127</SRS>' +
+' <SRS>EPSG:3128</SRS>' +
+' <SRS>EPSG:3129</SRS>' +
+' <SRS>EPSG:3130</SRS>' +
+' <SRS>EPSG:3131</SRS>' +
+' <SRS>EPSG:3132</SRS>' +
+' <SRS>EPSG:3133</SRS>' +
+' <SRS>EPSG:3134</SRS>' +
+' <SRS>EPSG:3135</SRS>' +
+' <SRS>EPSG:3136</SRS>' +
+' <SRS>EPSG:3137</SRS>' +
+' <SRS>EPSG:3138</SRS>' +
+' <SRS>EPSG:3139</SRS>' +
+' <SRS>EPSG:3140</SRS>' +
+' <SRS>EPSG:3141</SRS>' +
+' <SRS>EPSG:3142</SRS>' +
+' <SRS>EPSG:3143</SRS>' +
+' <SRS>EPSG:3144</SRS>' +
+' <SRS>EPSG:3145</SRS>' +
+' <SRS>EPSG:3146</SRS>' +
+' <SRS>EPSG:3147</SRS>' +
+' <SRS>EPSG:3148</SRS>' +
+' <SRS>EPSG:3149</SRS>' +
+' <SRS>EPSG:3150</SRS>' +
+' <SRS>EPSG:3151</SRS>' +
+' <SRS>EPSG:3152</SRS>' +
+' <SRS>EPSG:3153</SRS>' +
+' <SRS>EPSG:3154</SRS>' +
+' <SRS>EPSG:3155</SRS>' +
+' <SRS>EPSG:3156</SRS>' +
+' <SRS>EPSG:3157</SRS>' +
+' <SRS>EPSG:3158</SRS>' +
+' <SRS>EPSG:3159</SRS>' +
+' <SRS>EPSG:3160</SRS>' +
+' <SRS>EPSG:3161</SRS>' +
+' <SRS>EPSG:3162</SRS>' +
+' <SRS>EPSG:3163</SRS>' +
+' <SRS>EPSG:3164</SRS>' +
+' <SRS>EPSG:3165</SRS>' +
+' <SRS>EPSG:3166</SRS>' +
+' <SRS>EPSG:3167</SRS>' +
+' <SRS>EPSG:3168</SRS>' +
+' <SRS>EPSG:3169</SRS>' +
+' <SRS>EPSG:3170</SRS>' +
+' <SRS>EPSG:3171</SRS>' +
+' <SRS>EPSG:3172</SRS>' +
+' <SRS>EPSG:3173</SRS>' +
+' <SRS>EPSG:3174</SRS>' +
+' <SRS>EPSG:3175</SRS>' +
+' <SRS>EPSG:3176</SRS>' +
+' <SRS>EPSG:3177</SRS>' +
+' <SRS>EPSG:3178</SRS>' +
+' <SRS>EPSG:3179</SRS>' +
+' <SRS>EPSG:3180</SRS>' +
+' <SRS>EPSG:3181</SRS>' +
+' <SRS>EPSG:3182</SRS>' +
+' <SRS>EPSG:3183</SRS>' +
+' <SRS>EPSG:3184</SRS>' +
+' <SRS>EPSG:3185</SRS>' +
+' <SRS>EPSG:3186</SRS>' +
+' <SRS>EPSG:3187</SRS>' +
+' <SRS>EPSG:3188</SRS>' +
+' <SRS>EPSG:3189</SRS>' +
+' <SRS>EPSG:3190</SRS>' +
+' <SRS>EPSG:3191</SRS>' +
+' <SRS>EPSG:3192</SRS>' +
+' <SRS>EPSG:3193</SRS>' +
+' <SRS>EPSG:3194</SRS>' +
+' <SRS>EPSG:3195</SRS>' +
+' <SRS>EPSG:3196</SRS>' +
+' <SRS>EPSG:3197</SRS>' +
+' <SRS>EPSG:3198</SRS>' +
+' <SRS>EPSG:3199</SRS>' +
+' <SRS>EPSG:3200</SRS>' +
+' <SRS>EPSG:3201</SRS>' +
+' <SRS>EPSG:3202</SRS>' +
+' <SRS>EPSG:3203</SRS>' +
+' <SRS>EPSG:3204</SRS>' +
+' <SRS>EPSG:3205</SRS>' +
+' <SRS>EPSG:3206</SRS>' +
+' <SRS>EPSG:3207</SRS>' +
+' <SRS>EPSG:3208</SRS>' +
+' <SRS>EPSG:3209</SRS>' +
+' <SRS>EPSG:3210</SRS>' +
+' <SRS>EPSG:3211</SRS>' +
+' <SRS>EPSG:3212</SRS>' +
+' <SRS>EPSG:3213</SRS>' +
+' <SRS>EPSG:3214</SRS>' +
+' <SRS>EPSG:3215</SRS>' +
+' <SRS>EPSG:3216</SRS>' +
+' <SRS>EPSG:3217</SRS>' +
+' <SRS>EPSG:3218</SRS>' +
+' <SRS>EPSG:3219</SRS>' +
+' <SRS>EPSG:3220</SRS>' +
+' <SRS>EPSG:3221</SRS>' +
+' <SRS>EPSG:3222</SRS>' +
+' <SRS>EPSG:3223</SRS>' +
+' <SRS>EPSG:3224</SRS>' +
+' <SRS>EPSG:3225</SRS>' +
+' <SRS>EPSG:3226</SRS>' +
+' <SRS>EPSG:3227</SRS>' +
+' <SRS>EPSG:3228</SRS>' +
+' <SRS>EPSG:3229</SRS>' +
+' <SRS>EPSG:3230</SRS>' +
+' <SRS>EPSG:3231</SRS>' +
+' <SRS>EPSG:3232</SRS>' +
+' <SRS>EPSG:3233</SRS>' +
+' <SRS>EPSG:3234</SRS>' +
+' <SRS>EPSG:3235</SRS>' +
+' <SRS>EPSG:3236</SRS>' +
+' <SRS>EPSG:3237</SRS>' +
+' <SRS>EPSG:3238</SRS>' +
+' <SRS>EPSG:3239</SRS>' +
+' <SRS>EPSG:3240</SRS>' +
+' <SRS>EPSG:3241</SRS>' +
+' <SRS>EPSG:3242</SRS>' +
+' <SRS>EPSG:3243</SRS>' +
+' <SRS>EPSG:3244</SRS>' +
+' <SRS>EPSG:3245</SRS>' +
+' <SRS>EPSG:3246</SRS>' +
+' <SRS>EPSG:3247</SRS>' +
+' <SRS>EPSG:3248</SRS>' +
+' <SRS>EPSG:3249</SRS>' +
+' <SRS>EPSG:3250</SRS>' +
+' <SRS>EPSG:3251</SRS>' +
+' <SRS>EPSG:3252</SRS>' +
+' <SRS>EPSG:3253</SRS>' +
+' <SRS>EPSG:3254</SRS>' +
+' <SRS>EPSG:3255</SRS>' +
+' <SRS>EPSG:3256</SRS>' +
+' <SRS>EPSG:3257</SRS>' +
+' <SRS>EPSG:3258</SRS>' +
+' <SRS>EPSG:3259</SRS>' +
+' <SRS>EPSG:3260</SRS>' +
+' <SRS>EPSG:3261</SRS>' +
+' <SRS>EPSG:3262</SRS>' +
+' <SRS>EPSG:3263</SRS>' +
+' <SRS>EPSG:3264</SRS>' +
+' <SRS>EPSG:3265</SRS>' +
+' <SRS>EPSG:3266</SRS>' +
+' <SRS>EPSG:3267</SRS>' +
+' <SRS>EPSG:3268</SRS>' +
+' <SRS>EPSG:3269</SRS>' +
+' <SRS>EPSG:3270</SRS>' +
+' <SRS>EPSG:3271</SRS>' +
+' <SRS>EPSG:3272</SRS>' +
+' <SRS>EPSG:3273</SRS>' +
+' <SRS>EPSG:3274</SRS>' +
+' <SRS>EPSG:3275</SRS>' +
+' <SRS>EPSG:3276</SRS>' +
+' <SRS>EPSG:3277</SRS>' +
+' <SRS>EPSG:3278</SRS>' +
+' <SRS>EPSG:3279</SRS>' +
+' <SRS>EPSG:3280</SRS>' +
+' <SRS>EPSG:3281</SRS>' +
+' <SRS>EPSG:3282</SRS>' +
+' <SRS>EPSG:3283</SRS>' +
+' <SRS>EPSG:3284</SRS>' +
+' <SRS>EPSG:3285</SRS>' +
+' <SRS>EPSG:3286</SRS>' +
+' <SRS>EPSG:3287</SRS>' +
+' <SRS>EPSG:3288</SRS>' +
+' <SRS>EPSG:3289</SRS>' +
+' <SRS>EPSG:3290</SRS>' +
+' <SRS>EPSG:3291</SRS>' +
+' <SRS>EPSG:3292</SRS>' +
+' <SRS>EPSG:3293</SRS>' +
+' <SRS>EPSG:3294</SRS>' +
+' <SRS>EPSG:3295</SRS>' +
+' <SRS>EPSG:3296</SRS>' +
+' <SRS>EPSG:3297</SRS>' +
+' <SRS>EPSG:3298</SRS>' +
+' <SRS>EPSG:3299</SRS>' +
+' <SRS>EPSG:3300</SRS>' +
+' <SRS>EPSG:3301</SRS>' +
+' <SRS>EPSG:3302</SRS>' +
+' <SRS>EPSG:3303</SRS>' +
+' <SRS>EPSG:3304</SRS>' +
+' <SRS>EPSG:3305</SRS>' +
+' <SRS>EPSG:3306</SRS>' +
+' <SRS>EPSG:3307</SRS>' +
+' <SRS>EPSG:3308</SRS>' +
+' <SRS>EPSG:3309</SRS>' +
+' <SRS>EPSG:3310</SRS>' +
+' <SRS>EPSG:3311</SRS>' +
+' <SRS>EPSG:3312</SRS>' +
+' <SRS>EPSG:3313</SRS>' +
+' <SRS>EPSG:3314</SRS>' +
+' <SRS>EPSG:3315</SRS>' +
+' <SRS>EPSG:3316</SRS>' +
+' <SRS>EPSG:3317</SRS>' +
+' <SRS>EPSG:3318</SRS>' +
+' <SRS>EPSG:3319</SRS>' +
+' <SRS>EPSG:3320</SRS>' +
+' <SRS>EPSG:3321</SRS>' +
+' <SRS>EPSG:3322</SRS>' +
+' <SRS>EPSG:3323</SRS>' +
+' <SRS>EPSG:3324</SRS>' +
+' <SRS>EPSG:3325</SRS>' +
+' <SRS>EPSG:3326</SRS>' +
+' <SRS>EPSG:3327</SRS>' +
+' <SRS>EPSG:3328</SRS>' +
+' <SRS>EPSG:3329</SRS>' +
+' <SRS>EPSG:3330</SRS>' +
+' <SRS>EPSG:3331</SRS>' +
+' <SRS>EPSG:3332</SRS>' +
+' <SRS>EPSG:3333</SRS>' +
+' <SRS>EPSG:3334</SRS>' +
+' <SRS>EPSG:3335</SRS>' +
+' <SRS>EPSG:3336</SRS>' +
+' <SRS>EPSG:3337</SRS>' +
+' <SRS>EPSG:3338</SRS>' +
+' <SRS>EPSG:3339</SRS>' +
+' <SRS>EPSG:3340</SRS>' +
+' <SRS>EPSG:3341</SRS>' +
+' <SRS>EPSG:3342</SRS>' +
+' <SRS>EPSG:3343</SRS>' +
+' <SRS>EPSG:3344</SRS>' +
+' <SRS>EPSG:3345</SRS>' +
+' <SRS>EPSG:3346</SRS>' +
+' <SRS>EPSG:3347</SRS>' +
+' <SRS>EPSG:3348</SRS>' +
+' <SRS>EPSG:3349</SRS>' +
+' <SRS>EPSG:3350</SRS>' +
+' <SRS>EPSG:3351</SRS>' +
+' <SRS>EPSG:3352</SRS>' +
+' <SRS>EPSG:3353</SRS>' +
+' <SRS>EPSG:3354</SRS>' +
+' <SRS>EPSG:3355</SRS>' +
+' <SRS>EPSG:3356</SRS>' +
+' <SRS>EPSG:3357</SRS>' +
+' <SRS>EPSG:3358</SRS>' +
+' <SRS>EPSG:3359</SRS>' +
+' <SRS>EPSG:3360</SRS>' +
+' <SRS>EPSG:3361</SRS>' +
+' <SRS>EPSG:3362</SRS>' +
+' <SRS>EPSG:3363</SRS>' +
+' <SRS>EPSG:3364</SRS>' +
+' <SRS>EPSG:3365</SRS>' +
+' <SRS>EPSG:3366</SRS>' +
+' <SRS>EPSG:3367</SRS>' +
+' <SRS>EPSG:3368</SRS>' +
+' <SRS>EPSG:3369</SRS>' +
+' <SRS>EPSG:3370</SRS>' +
+' <SRS>EPSG:3371</SRS>' +
+' <SRS>EPSG:3372</SRS>' +
+' <SRS>EPSG:3373</SRS>' +
+' <SRS>EPSG:3374</SRS>' +
+' <SRS>EPSG:3375</SRS>' +
+' <SRS>EPSG:3376</SRS>' +
+' <SRS>EPSG:3377</SRS>' +
+' <SRS>EPSG:3378</SRS>' +
+' <SRS>EPSG:3379</SRS>' +
+' <SRS>EPSG:3380</SRS>' +
+' <SRS>EPSG:3381</SRS>' +
+' <SRS>EPSG:3382</SRS>' +
+' <SRS>EPSG:3383</SRS>' +
+' <SRS>EPSG:3384</SRS>' +
+' <SRS>EPSG:3385</SRS>' +
+' <SRS>EPSG:3386</SRS>' +
+' <SRS>EPSG:3387</SRS>' +
+' <SRS>EPSG:3388</SRS>' +
+' <SRS>EPSG:3389</SRS>' +
+' <SRS>EPSG:3390</SRS>' +
+' <SRS>EPSG:3391</SRS>' +
+' <SRS>EPSG:3392</SRS>' +
+' <SRS>EPSG:3393</SRS>' +
+' <SRS>EPSG:3394</SRS>' +
+' <SRS>EPSG:3395</SRS>' +
+' <SRS>EPSG:3396</SRS>' +
+' <SRS>EPSG:3397</SRS>' +
+' <SRS>EPSG:3398</SRS>' +
+' <SRS>EPSG:3399</SRS>' +
+' <SRS>EPSG:3400</SRS>' +
+' <SRS>EPSG:3401</SRS>' +
+' <SRS>EPSG:3402</SRS>' +
+' <SRS>EPSG:3403</SRS>' +
+' <SRS>EPSG:3404</SRS>' +
+' <SRS>EPSG:3405</SRS>' +
+' <SRS>EPSG:3406</SRS>' +
+' <SRS>EPSG:3407</SRS>' +
+' <SRS>EPSG:3408</SRS>' +
+' <SRS>EPSG:3409</SRS>' +
+' <SRS>EPSG:3410</SRS>' +
+' <SRS>EPSG:3411</SRS>' +
+' <SRS>EPSG:3412</SRS>' +
+' <SRS>EPSG:3413</SRS>' +
+' <SRS>EPSG:3414</SRS>' +
+' <SRS>EPSG:3415</SRS>' +
+' <SRS>EPSG:3416</SRS>' +
+' <SRS>EPSG:3417</SRS>' +
+' <SRS>EPSG:3418</SRS>' +
+' <SRS>EPSG:3419</SRS>' +
+' <SRS>EPSG:3420</SRS>' +
+' <SRS>EPSG:3421</SRS>' +
+' <SRS>EPSG:3422</SRS>' +
+' <SRS>EPSG:3423</SRS>' +
+' <SRS>EPSG:3424</SRS>' +
+' <SRS>EPSG:3425</SRS>' +
+' <SRS>EPSG:3426</SRS>' +
+' <SRS>EPSG:3427</SRS>' +
+' <SRS>EPSG:3428</SRS>' +
+' <SRS>EPSG:3429</SRS>' +
+' <SRS>EPSG:3430</SRS>' +
+' <SRS>EPSG:3431</SRS>' +
+' <SRS>EPSG:3432</SRS>' +
+' <SRS>EPSG:3433</SRS>' +
+' <SRS>EPSG:3434</SRS>' +
+' <SRS>EPSG:3435</SRS>' +
+' <SRS>EPSG:3436</SRS>' +
+' <SRS>EPSG:3437</SRS>' +
+' <SRS>EPSG:3438</SRS>' +
+' <SRS>EPSG:3439</SRS>' +
+' <SRS>EPSG:3440</SRS>' +
+' <SRS>EPSG:3441</SRS>' +
+' <SRS>EPSG:3442</SRS>' +
+' <SRS>EPSG:3443</SRS>' +
+' <SRS>EPSG:3444</SRS>' +
+' <SRS>EPSG:3445</SRS>' +
+' <SRS>EPSG:3446</SRS>' +
+' <SRS>EPSG:3447</SRS>' +
+' <SRS>EPSG:3448</SRS>' +
+' <SRS>EPSG:3449</SRS>' +
+' <SRS>EPSG:3450</SRS>' +
+' <SRS>EPSG:3451</SRS>' +
+' <SRS>EPSG:3452</SRS>' +
+' <SRS>EPSG:3453</SRS>' +
+' <SRS>EPSG:3454</SRS>' +
+' <SRS>EPSG:3455</SRS>' +
+' <SRS>EPSG:3456</SRS>' +
+' <SRS>EPSG:3457</SRS>' +
+' <SRS>EPSG:3458</SRS>' +
+' <SRS>EPSG:3459</SRS>' +
+' <SRS>EPSG:3460</SRS>' +
+' <SRS>EPSG:3461</SRS>' +
+' <SRS>EPSG:3462</SRS>' +
+' <SRS>EPSG:3463</SRS>' +
+' <SRS>EPSG:3464</SRS>' +
+' <SRS>EPSG:3560</SRS>' +
+' <SRS>EPSG:3561</SRS>' +
+' <SRS>EPSG:3562</SRS>' +
+' <SRS>EPSG:3563</SRS>' +
+' <SRS>EPSG:3564</SRS>' +
+' <SRS>EPSG:3565</SRS>' +
+' <SRS>EPSG:3566</SRS>' +
+' <SRS>EPSG:3567</SRS>' +
+' <SRS>EPSG:3568</SRS>' +
+' <SRS>EPSG:3569</SRS>' +
+' <SRS>EPSG:3570</SRS>' +
+' <SRS>EPSG:3571</SRS>' +
+' <SRS>EPSG:3572</SRS>' +
+' <SRS>EPSG:3573</SRS>' +
+' <SRS>EPSG:3574</SRS>' +
+' <SRS>EPSG:3575</SRS>' +
+' <SRS>EPSG:3576</SRS>' +
+' <SRS>EPSG:3577</SRS>' +
+' <SRS>EPSG:3920</SRS>' +
+' <SRS>EPSG:3991</SRS>' +
+' <SRS>EPSG:3992</SRS>' +
+' <SRS>EPSG:3993</SRS>' +
+' <SRS>EPSG:4001</SRS>' +
+' <SRS>EPSG:4002</SRS>' +
+' <SRS>EPSG:4003</SRS>' +
+' <SRS>EPSG:4004</SRS>' +
+' <SRS>EPSG:4005</SRS>' +
+' <SRS>EPSG:4006</SRS>' +
+' <SRS>EPSG:4007</SRS>' +
+' <SRS>EPSG:4008</SRS>' +
+' <SRS>EPSG:4009</SRS>' +
+' <SRS>EPSG:4010</SRS>' +
+' <SRS>EPSG:4011</SRS>' +
+' <SRS>EPSG:4012</SRS>' +
+' <SRS>EPSG:4013</SRS>' +
+' <SRS>EPSG:4014</SRS>' +
+' <SRS>EPSG:4015</SRS>' +
+' <SRS>EPSG:4016</SRS>' +
+' <SRS>EPSG:4018</SRS>' +
+' <SRS>EPSG:4019</SRS>' +
+' <SRS>EPSG:4020</SRS>' +
+' <SRS>EPSG:4021</SRS>' +
+' <SRS>EPSG:4022</SRS>' +
+' <SRS>EPSG:4024</SRS>' +
+' <SRS>EPSG:4025</SRS>' +
+' <SRS>EPSG:4027</SRS>' +
+' <SRS>EPSG:4028</SRS>' +
+' <SRS>EPSG:4029</SRS>' +
+' <SRS>EPSG:4030</SRS>' +
+' <SRS>EPSG:4031</SRS>' +
+' <SRS>EPSG:4032</SRS>' +
+' <SRS>EPSG:4033</SRS>' +
+' <SRS>EPSG:4034</SRS>' +
+' <SRS>EPSG:4035</SRS>' +
+' <SRS>EPSG:4036</SRS>' +
+' <SRS>EPSG:4041</SRS>' +
+' <SRS>EPSG:4042</SRS>' +
+' <SRS>EPSG:4043</SRS>' +
+' <SRS>EPSG:4044</SRS>' +
+' <SRS>EPSG:4045</SRS>' +
+' <SRS>EPSG:4047</SRS>' +
+' <SRS>EPSG:4052</SRS>' +
+' <SRS>EPSG:4053</SRS>' +
+' <SRS>EPSG:4054</SRS>' +
+' <SRS>EPSG:4120</SRS>' +
+' <SRS>EPSG:4121</SRS>' +
+' <SRS>EPSG:4122</SRS>' +
+' <SRS>EPSG:4123</SRS>' +
+' <SRS>EPSG:4124</SRS>' +
+' <SRS>EPSG:4125</SRS>' +
+' <SRS>EPSG:4126</SRS>' +
+' <SRS>EPSG:4127</SRS>' +
+' <SRS>EPSG:4128</SRS>' +
+' <SRS>EPSG:4129</SRS>' +
+' <SRS>EPSG:4130</SRS>' +
+' <SRS>EPSG:4131</SRS>' +
+' <SRS>EPSG:4132</SRS>' +
+' <SRS>EPSG:4133</SRS>' +
+' <SRS>EPSG:4134</SRS>' +
+' <SRS>EPSG:4135</SRS>' +
+' <SRS>EPSG:4136</SRS>' +
+' <SRS>EPSG:4137</SRS>' +
+' <SRS>EPSG:4138</SRS>' +
+' <SRS>EPSG:4139</SRS>' +
+' <SRS>EPSG:4140</SRS>' +
+' <SRS>EPSG:4141</SRS>' +
+' <SRS>EPSG:4142</SRS>' +
+' <SRS>EPSG:4143</SRS>' +
+' <SRS>EPSG:4144</SRS>' +
+' <SRS>EPSG:4145</SRS>' +
+' <SRS>EPSG:4146</SRS>' +
+' <SRS>EPSG:4147</SRS>' +
+' <SRS>EPSG:4148</SRS>' +
+' <SRS>EPSG:4149</SRS>' +
+' <SRS>EPSG:4150</SRS>' +
+' <SRS>EPSG:4151</SRS>' +
+' <SRS>EPSG:4152</SRS>' +
+' <SRS>EPSG:4153</SRS>' +
+' <SRS>EPSG:4154</SRS>' +
+' <SRS>EPSG:4155</SRS>' +
+' <SRS>EPSG:4156</SRS>' +
+' <SRS>EPSG:4157</SRS>' +
+' <SRS>EPSG:4158</SRS>' +
+' <SRS>EPSG:4159</SRS>' +
+' <SRS>EPSG:4160</SRS>' +
+' <SRS>EPSG:4161</SRS>' +
+' <SRS>EPSG:4162</SRS>' +
+' <SRS>EPSG:4163</SRS>' +
+' <SRS>EPSG:4164</SRS>' +
+' <SRS>EPSG:4165</SRS>' +
+' <SRS>EPSG:4166</SRS>' +
+' <SRS>EPSG:4167</SRS>' +
+' <SRS>EPSG:4168</SRS>' +
+' <SRS>EPSG:4169</SRS>' +
+' <SRS>EPSG:4170</SRS>' +
+' <SRS>EPSG:4171</SRS>' +
+' <SRS>EPSG:4172</SRS>' +
+' <SRS>EPSG:4173</SRS>' +
+' <SRS>EPSG:4174</SRS>' +
+' <SRS>EPSG:4175</SRS>' +
+' <SRS>EPSG:4176</SRS>' +
+' <SRS>EPSG:4178</SRS>' +
+' <SRS>EPSG:4179</SRS>' +
+' <SRS>EPSG:4180</SRS>' +
+' <SRS>EPSG:4181</SRS>' +
+' <SRS>EPSG:4182</SRS>' +
+' <SRS>EPSG:4183</SRS>' +
+' <SRS>EPSG:4184</SRS>' +
+' <SRS>EPSG:4185</SRS>' +
+' <SRS>EPSG:4188</SRS>' +
+' <SRS>EPSG:4189</SRS>' +
+' <SRS>EPSG:4190</SRS>' +
+' <SRS>EPSG:4191</SRS>' +
+' <SRS>EPSG:4192</SRS>' +
+' <SRS>EPSG:4193</SRS>' +
+' <SRS>EPSG:4194</SRS>' +
+' <SRS>EPSG:4195</SRS>' +
+' <SRS>EPSG:4196</SRS>' +
+' <SRS>EPSG:4197</SRS>' +
+' <SRS>EPSG:4198</SRS>' +
+' <SRS>EPSG:4199</SRS>' +
+' <SRS>EPSG:4200</SRS>' +
+' <SRS>EPSG:4201</SRS>' +
+' <SRS>EPSG:4202</SRS>' +
+' <SRS>EPSG:4203</SRS>' +
+' <SRS>EPSG:4204</SRS>' +
+' <SRS>EPSG:4205</SRS>' +
+' <SRS>EPSG:4206</SRS>' +
+' <SRS>EPSG:4207</SRS>' +
+' <SRS>EPSG:4208</SRS>' +
+' <SRS>EPSG:4209</SRS>' +
+' <SRS>EPSG:4210</SRS>' +
+' <SRS>EPSG:4211</SRS>' +
+' <SRS>EPSG:4212</SRS>' +
+' <SRS>EPSG:4213</SRS>' +
+' <SRS>EPSG:4214</SRS>' +
+' <SRS>EPSG:4215</SRS>' +
+' <SRS>EPSG:4216</SRS>' +
+' <SRS>EPSG:4218</SRS>' +
+' <SRS>EPSG:4219</SRS>' +
+' <SRS>EPSG:4220</SRS>' +
+' <SRS>EPSG:4221</SRS>' +
+' <SRS>EPSG:4222</SRS>' +
+' <SRS>EPSG:4223</SRS>' +
+' <SRS>EPSG:4224</SRS>' +
+' <SRS>EPSG:4225</SRS>' +
+' <SRS>EPSG:4226</SRS>' +
+' <SRS>EPSG:4227</SRS>' +
+' <SRS>EPSG:4228</SRS>' +
+' <SRS>EPSG:4229</SRS>' +
+' <SRS>EPSG:4230</SRS>' +
+' <SRS>EPSG:4231</SRS>' +
+' <SRS>EPSG:4232</SRS>' +
+' <SRS>EPSG:4233</SRS>' +
+' <SRS>EPSG:4234</SRS>' +
+' <SRS>EPSG:4235</SRS>' +
+' <SRS>EPSG:4236</SRS>' +
+' <SRS>EPSG:4237</SRS>' +
+' <SRS>EPSG:4238</SRS>' +
+' <SRS>EPSG:4239</SRS>' +
+' <SRS>EPSG:4240</SRS>' +
+' <SRS>EPSG:4241</SRS>' +
+' <SRS>EPSG:4242</SRS>' +
+' <SRS>EPSG:4243</SRS>' +
+' <SRS>EPSG:4244</SRS>' +
+' <SRS>EPSG:4245</SRS>' +
+' <SRS>EPSG:4246</SRS>' +
+' <SRS>EPSG:4247</SRS>' +
+' <SRS>EPSG:4248</SRS>' +
+' <SRS>EPSG:4249</SRS>' +
+' <SRS>EPSG:4250</SRS>' +
+' <SRS>EPSG:4251</SRS>' +
+' <SRS>EPSG:4252</SRS>' +
+' <SRS>EPSG:4253</SRS>' +
+' <SRS>EPSG:4254</SRS>' +
+' <SRS>EPSG:4255</SRS>' +
+' <SRS>EPSG:4256</SRS>' +
+' <SRS>EPSG:4257</SRS>' +
+' <SRS>EPSG:4258</SRS>' +
+' <SRS>EPSG:4259</SRS>' +
+' <SRS>EPSG:4260</SRS>' +
+' <SRS>EPSG:4261</SRS>' +
+' <SRS>EPSG:4262</SRS>' +
+' <SRS>EPSG:4263</SRS>' +
+' <SRS>EPSG:4264</SRS>' +
+' <SRS>EPSG:4265</SRS>' +
+' <SRS>EPSG:4266</SRS>' +
+' <SRS>EPSG:4267</SRS>' +
+' <SRS>EPSG:4268</SRS>' +
+' <SRS>EPSG:4269</SRS>' +
+' <SRS>EPSG:4270</SRS>' +
+' <SRS>EPSG:4271</SRS>' +
+' <SRS>EPSG:4272</SRS>' +
+' <SRS>EPSG:4273</SRS>' +
+' <SRS>EPSG:4274</SRS>' +
+' <SRS>EPSG:4275</SRS>' +
+' <SRS>EPSG:4276</SRS>' +
+' <SRS>EPSG:4277</SRS>' +
+' <SRS>EPSG:4278</SRS>' +
+' <SRS>EPSG:4279</SRS>' +
+' <SRS>EPSG:4280</SRS>' +
+' <SRS>EPSG:4281</SRS>' +
+' <SRS>EPSG:4282</SRS>' +
+' <SRS>EPSG:4283</SRS>' +
+' <SRS>EPSG:4284</SRS>' +
+' <SRS>EPSG:4285</SRS>' +
+' <SRS>EPSG:4286</SRS>' +
+' <SRS>EPSG:4287</SRS>' +
+' <SRS>EPSG:4288</SRS>' +
+' <SRS>EPSG:4289</SRS>' +
+' <SRS>EPSG:4291</SRS>' +
+' <SRS>EPSG:4292</SRS>' +
+' <SRS>EPSG:4293</SRS>' +
+' <SRS>EPSG:4294</SRS>' +
+' <SRS>EPSG:4295</SRS>' +
+' <SRS>EPSG:4296</SRS>' +
+' <SRS>EPSG:4297</SRS>' +
+' <SRS>EPSG:4298</SRS>' +
+' <SRS>EPSG:4299</SRS>' +
+' <SRS>EPSG:4300</SRS>' +
+' <SRS>EPSG:4301</SRS>' +
+' <SRS>EPSG:4302</SRS>' +
+' <SRS>EPSG:4303</SRS>' +
+' <SRS>EPSG:4304</SRS>' +
+' <SRS>EPSG:4306</SRS>' +
+' <SRS>EPSG:4307</SRS>' +
+' <SRS>EPSG:4308</SRS>' +
+' <SRS>EPSG:4309</SRS>' +
+' <SRS>EPSG:4310</SRS>' +
+' <SRS>EPSG:4311</SRS>' +
+' <SRS>EPSG:4312</SRS>' +
+' <SRS>EPSG:4313</SRS>' +
+' <SRS>EPSG:4314</SRS>' +
+' <SRS>EPSG:4315</SRS>' +
+' <SRS>EPSG:4316</SRS>' +
+' <SRS>EPSG:4317</SRS>' +
+' <SRS>EPSG:4318</SRS>' +
+' <SRS>EPSG:4319</SRS>' +
+' <SRS>EPSG:4322</SRS>' +
+' <SRS>EPSG:4324</SRS>' +
+' <SRS>EPSG:4326</SRS>' +
+' <SRS>EPSG:4327</SRS>' +
+' <SRS>EPSG:4328</SRS>' +
+' <SRS>EPSG:4329</SRS>' +
+' <SRS>EPSG:4330</SRS>' +
+' <SRS>EPSG:4331</SRS>' +
+' <SRS>EPSG:4332</SRS>' +
+' <SRS>EPSG:4333</SRS>' +
+' <SRS>EPSG:4334</SRS>' +
+' <SRS>EPSG:4335</SRS>' +
+' <SRS>EPSG:4336</SRS>' +
+' <SRS>EPSG:4337</SRS>' +
+' <SRS>EPSG:4338</SRS>' +
+' <SRS>EPSG:4339</SRS>' +
+' <SRS>EPSG:4340</SRS>' +
+' <SRS>EPSG:4341</SRS>' +
+' <SRS>EPSG:4342</SRS>' +
+' <SRS>EPSG:4343</SRS>' +
+' <SRS>EPSG:4344</SRS>' +
+' <SRS>EPSG:4345</SRS>' +
+' <SRS>EPSG:4346</SRS>' +
+' <SRS>EPSG:4347</SRS>' +
+' <SRS>EPSG:4348</SRS>' +
+' <SRS>EPSG:4349</SRS>' +
+' <SRS>EPSG:4350</SRS>' +
+' <SRS>EPSG:4351</SRS>' +
+' <SRS>EPSG:4352</SRS>' +
+' <SRS>EPSG:4353</SRS>' +
+' <SRS>EPSG:4354</SRS>' +
+' <SRS>EPSG:4355</SRS>' +
+' <SRS>EPSG:4356</SRS>' +
+' <SRS>EPSG:4357</SRS>' +
+' <SRS>EPSG:4358</SRS>' +
+' <SRS>EPSG:4359</SRS>' +
+' <SRS>EPSG:4360</SRS>' +
+' <SRS>EPSG:4361</SRS>' +
+' <SRS>EPSG:4362</SRS>' +
+' <SRS>EPSG:4363</SRS>' +
+' <SRS>EPSG:4364</SRS>' +
+' <SRS>EPSG:4365</SRS>' +
+' <SRS>EPSG:4366</SRS>' +
+' <SRS>EPSG:4367</SRS>' +
+' <SRS>EPSG:4368</SRS>' +
+' <SRS>EPSG:4369</SRS>' +
+' <SRS>EPSG:4370</SRS>' +
+' <SRS>EPSG:4371</SRS>' +
+' <SRS>EPSG:4372</SRS>' +
+' <SRS>EPSG:4373</SRS>' +
+' <SRS>EPSG:4374</SRS>' +
+' <SRS>EPSG:4375</SRS>' +
+' <SRS>EPSG:4376</SRS>' +
+' <SRS>EPSG:4377</SRS>' +
+' <SRS>EPSG:4378</SRS>' +
+' <SRS>EPSG:4379</SRS>' +
+' <SRS>EPSG:4380</SRS>' +
+' <SRS>EPSG:4381</SRS>' +
+' <SRS>EPSG:4382</SRS>' +
+' <SRS>EPSG:4383</SRS>' +
+' <SRS>EPSG:4384</SRS>' +
+' <SRS>EPSG:4385</SRS>' +
+' <SRS>EPSG:4386</SRS>' +
+' <SRS>EPSG:4387</SRS>' +
+' <SRS>EPSG:4388</SRS>' +
+' <SRS>EPSG:4389</SRS>' +
+' <SRS>EPSG:4600</SRS>' +
+' <SRS>EPSG:4601</SRS>' +
+' <SRS>EPSG:4602</SRS>' +
+' <SRS>EPSG:4603</SRS>' +
+' <SRS>EPSG:4604</SRS>' +
+' <SRS>EPSG:4605</SRS>' +
+' <SRS>EPSG:4606</SRS>' +
+' <SRS>EPSG:4607</SRS>' +
+' <SRS>EPSG:4608</SRS>' +
+' <SRS>EPSG:4609</SRS>' +
+' <SRS>EPSG:4610</SRS>' +
+' <SRS>EPSG:4611</SRS>' +
+' <SRS>EPSG:4612</SRS>' +
+' <SRS>EPSG:4613</SRS>' +
+' <SRS>EPSG:4614</SRS>' +
+' <SRS>EPSG:4615</SRS>' +
+' <SRS>EPSG:4616</SRS>' +
+' <SRS>EPSG:4617</SRS>' +
+' <SRS>EPSG:4618</SRS>' +
+' <SRS>EPSG:4619</SRS>' +
+' <SRS>EPSG:4620</SRS>' +
+' <SRS>EPSG:4621</SRS>' +
+' <SRS>EPSG:4622</SRS>' +
+' <SRS>EPSG:4623</SRS>' +
+' <SRS>EPSG:4624</SRS>' +
+' <SRS>EPSG:4625</SRS>' +
+' <SRS>EPSG:4626</SRS>' +
+' <SRS>EPSG:4627</SRS>' +
+' <SRS>EPSG:4628</SRS>' +
+' <SRS>EPSG:4629</SRS>' +
+' <SRS>EPSG:4630</SRS>' +
+' <SRS>EPSG:4631</SRS>' +
+' <SRS>EPSG:4632</SRS>' +
+' <SRS>EPSG:4633</SRS>' +
+' <SRS>EPSG:4634</SRS>' +
+' <SRS>EPSG:4635</SRS>' +
+' <SRS>EPSG:4636</SRS>' +
+' <SRS>EPSG:4637</SRS>' +
+' <SRS>EPSG:4638</SRS>' +
+' <SRS>EPSG:4639</SRS>' +
+' <SRS>EPSG:4640</SRS>' +
+' <SRS>EPSG:4641</SRS>' +
+' <SRS>EPSG:4642</SRS>' +
+' <SRS>EPSG:4643</SRS>' +
+' <SRS>EPSG:4644</SRS>' +
+' <SRS>EPSG:4645</SRS>' +
+' <SRS>EPSG:4646</SRS>' +
+' <SRS>EPSG:4657</SRS>' +
+' <SRS>EPSG:4658</SRS>' +
+' <SRS>EPSG:4659</SRS>' +
+' <SRS>EPSG:4660</SRS>' +
+' <SRS>EPSG:4661</SRS>' +
+' <SRS>EPSG:4662</SRS>' +
+' <SRS>EPSG:4663</SRS>' +
+' <SRS>EPSG:4664</SRS>' +
+' <SRS>EPSG:4665</SRS>' +
+' <SRS>EPSG:4666</SRS>' +
+' <SRS>EPSG:4667</SRS>' +
+' <SRS>EPSG:4668</SRS>' +
+' <SRS>EPSG:4669</SRS>' +
+' <SRS>EPSG:4670</SRS>' +
+' <SRS>EPSG:4671</SRS>' +
+' <SRS>EPSG:4672</SRS>' +
+' <SRS>EPSG:4673</SRS>' +
+' <SRS>EPSG:4674</SRS>' +
+' <SRS>EPSG:4675</SRS>' +
+' <SRS>EPSG:4676</SRS>' +
+' <SRS>EPSG:4677</SRS>' +
+' <SRS>EPSG:4678</SRS>' +
+' <SRS>EPSG:4679</SRS>' +
+' <SRS>EPSG:4680</SRS>' +
+' <SRS>EPSG:4681</SRS>' +
+' <SRS>EPSG:4682</SRS>' +
+' <SRS>EPSG:4683</SRS>' +
+' <SRS>EPSG:4684</SRS>' +
+' <SRS>EPSG:4685</SRS>' +
+' <SRS>EPSG:4686</SRS>' +
+' <SRS>EPSG:4687</SRS>' +
+' <SRS>EPSG:4688</SRS>' +
+' <SRS>EPSG:4689</SRS>' +
+' <SRS>EPSG:4690</SRS>' +
+' <SRS>EPSG:4691</SRS>' +
+' <SRS>EPSG:4692</SRS>' +
+' <SRS>EPSG:4693</SRS>' +
+' <SRS>EPSG:4694</SRS>' +
+' <SRS>EPSG:4695</SRS>' +
+' <SRS>EPSG:4696</SRS>' +
+' <SRS>EPSG:4697</SRS>' +
+' <SRS>EPSG:4698</SRS>' +
+' <SRS>EPSG:4699</SRS>' +
+' <SRS>EPSG:4700</SRS>' +
+' <SRS>EPSG:4701</SRS>' +
+' <SRS>EPSG:4702</SRS>' +
+' <SRS>EPSG:4703</SRS>' +
+' <SRS>EPSG:4704</SRS>' +
+' <SRS>EPSG:4705</SRS>' +
+' <SRS>EPSG:4706</SRS>' +
+' <SRS>EPSG:4707</SRS>' +
+' <SRS>EPSG:4708</SRS>' +
+' <SRS>EPSG:4709</SRS>' +
+' <SRS>EPSG:4710</SRS>' +
+' <SRS>EPSG:4711</SRS>' +
+' <SRS>EPSG:4712</SRS>' +
+' <SRS>EPSG:4713</SRS>' +
+' <SRS>EPSG:4714</SRS>' +
+' <SRS>EPSG:4715</SRS>' +
+' <SRS>EPSG:4716</SRS>' +
+' <SRS>EPSG:4717</SRS>' +
+' <SRS>EPSG:4718</SRS>' +
+' <SRS>EPSG:4719</SRS>' +
+' <SRS>EPSG:4720</SRS>' +
+' <SRS>EPSG:4721</SRS>' +
+' <SRS>EPSG:4722</SRS>' +
+' <SRS>EPSG:4723</SRS>' +
+' <SRS>EPSG:4724</SRS>' +
+' <SRS>EPSG:4725</SRS>' +
+' <SRS>EPSG:4726</SRS>' +
+' <SRS>EPSG:4727</SRS>' +
+' <SRS>EPSG:4728</SRS>' +
+' <SRS>EPSG:4729</SRS>' +
+' <SRS>EPSG:4730</SRS>' +
+' <SRS>EPSG:4731</SRS>' +
+' <SRS>EPSG:4732</SRS>' +
+' <SRS>EPSG:4733</SRS>' +
+' <SRS>EPSG:4734</SRS>' +
+' <SRS>EPSG:4735</SRS>' +
+' <SRS>EPSG:4736</SRS>' +
+' <SRS>EPSG:4737</SRS>' +
+' <SRS>EPSG:4738</SRS>' +
+' <SRS>EPSG:4739</SRS>' +
+' <SRS>EPSG:4740</SRS>' +
+' <SRS>EPSG:4741</SRS>' +
+' <SRS>EPSG:4742</SRS>' +
+' <SRS>EPSG:4743</SRS>' +
+' <SRS>EPSG:4744</SRS>' +
+' <SRS>EPSG:4745</SRS>' +
+' <SRS>EPSG:4746</SRS>' +
+' <SRS>EPSG:4747</SRS>' +
+' <SRS>EPSG:4748</SRS>' +
+' <SRS>EPSG:4749</SRS>' +
+' <SRS>EPSG:4750</SRS>' +
+' <SRS>EPSG:4751</SRS>' +
+' <SRS>EPSG:4752</SRS>' +
+' <SRS>EPSG:4753</SRS>' +
+' <SRS>EPSG:4754</SRS>' +
+' <SRS>EPSG:4755</SRS>' +
+' <SRS>EPSG:4756</SRS>' +
+' <SRS>EPSG:4757</SRS>' +
+' <SRS>EPSG:4758</SRS>' +
+' <SRS>EPSG:4801</SRS>' +
+' <SRS>EPSG:4802</SRS>' +
+' <SRS>EPSG:4803</SRS>' +
+' <SRS>EPSG:4804</SRS>' +
+' <SRS>EPSG:4805</SRS>' +
+' <SRS>EPSG:4806</SRS>' +
+' <SRS>EPSG:4807</SRS>' +
+' <SRS>EPSG:4808</SRS>' +
+' <SRS>EPSG:4809</SRS>' +
+' <SRS>EPSG:4810</SRS>' +
+' <SRS>EPSG:4811</SRS>' +
+' <SRS>EPSG:4813</SRS>' +
+' <SRS>EPSG:4814</SRS>' +
+' <SRS>EPSG:4815</SRS>' +
+' <SRS>EPSG:4816</SRS>' +
+' <SRS>EPSG:4817</SRS>' +
+' <SRS>EPSG:4818</SRS>' +
+' <SRS>EPSG:4819</SRS>' +
+' <SRS>EPSG:4820</SRS>' +
+' <SRS>EPSG:4821</SRS>' +
+' <SRS>EPSG:4894</SRS>' +
+' <SRS>EPSG:4895</SRS>' +
+' <SRS>EPSG:4896</SRS>' +
+' <SRS>EPSG:4897</SRS>' +
+' <SRS>EPSG:4898</SRS>' +
+' <SRS>EPSG:4899</SRS>' +
+' <SRS>EPSG:4900</SRS>' +
+' <SRS>EPSG:4901</SRS>' +
+' <SRS>EPSG:4902</SRS>' +
+' <SRS>EPSG:4903</SRS>' +
+' <SRS>EPSG:4904</SRS>' +
+' <SRS>EPSG:4906</SRS>' +
+' <SRS>EPSG:4907</SRS>' +
+' <SRS>EPSG:4908</SRS>' +
+' <SRS>EPSG:4909</SRS>' +
+' <SRS>EPSG:4910</SRS>' +
+' <SRS>EPSG:4911</SRS>' +
+' <SRS>EPSG:4912</SRS>' +
+' <SRS>EPSG:4913</SRS>' +
+' <SRS>EPSG:4914</SRS>' +
+' <SRS>EPSG:4915</SRS>' +
+' <SRS>EPSG:4916</SRS>' +
+' <SRS>EPSG:4917</SRS>' +
+' <SRS>EPSG:4918</SRS>' +
+' <SRS>EPSG:4919</SRS>' +
+' <SRS>EPSG:4920</SRS>' +
+' <SRS>EPSG:4921</SRS>' +
+' <SRS>EPSG:4922</SRS>' +
+' <SRS>EPSG:4923</SRS>' +
+' <SRS>EPSG:4924</SRS>' +
+' <SRS>EPSG:4925</SRS>' +
+' <SRS>EPSG:4926</SRS>' +
+' <SRS>EPSG:4927</SRS>' +
+' <SRS>EPSG:4928</SRS>' +
+' <SRS>EPSG:4929</SRS>' +
+' <SRS>EPSG:4930</SRS>' +
+' <SRS>EPSG:4931</SRS>' +
+' <SRS>EPSG:4932</SRS>' +
+' <SRS>EPSG:4933</SRS>' +
+' <SRS>EPSG:4934</SRS>' +
+' <SRS>EPSG:4935</SRS>' +
+' <SRS>EPSG:4936</SRS>' +
+' <SRS>EPSG:4937</SRS>' +
+' <SRS>EPSG:4938</SRS>' +
+' <SRS>EPSG:4939</SRS>' +
+' <SRS>EPSG:4940</SRS>' +
+' <SRS>EPSG:4941</SRS>' +
+' <SRS>EPSG:4942</SRS>' +
+' <SRS>EPSG:4943</SRS>' +
+' <SRS>EPSG:4944</SRS>' +
+' <SRS>EPSG:4945</SRS>' +
+' <SRS>EPSG:4946</SRS>' +
+' <SRS>EPSG:4947</SRS>' +
+' <SRS>EPSG:4948</SRS>' +
+' <SRS>EPSG:4949</SRS>' +
+' <SRS>EPSG:4950</SRS>' +
+' <SRS>EPSG:4951</SRS>' +
+' <SRS>EPSG:4952</SRS>' +
+' <SRS>EPSG:4953</SRS>' +
+' <SRS>EPSG:4954</SRS>' +
+' <SRS>EPSG:4955</SRS>' +
+' <SRS>EPSG:4956</SRS>' +
+' <SRS>EPSG:4957</SRS>' +
+' <SRS>EPSG:4958</SRS>' +
+' <SRS>EPSG:4959</SRS>' +
+' <SRS>EPSG:4960</SRS>' +
+' <SRS>EPSG:4961</SRS>' +
+' <SRS>EPSG:4962</SRS>' +
+' <SRS>EPSG:4963</SRS>' +
+' <SRS>EPSG:4964</SRS>' +
+' <SRS>EPSG:4965</SRS>' +
+' <SRS>EPSG:4966</SRS>' +
+' <SRS>EPSG:4967</SRS>' +
+' <SRS>EPSG:4968</SRS>' +
+' <SRS>EPSG:4969</SRS>' +
+' <SRS>EPSG:4970</SRS>' +
+' <SRS>EPSG:4971</SRS>' +
+' <SRS>EPSG:4972</SRS>' +
+' <SRS>EPSG:4973</SRS>' +
+' <SRS>EPSG:4974</SRS>' +
+' <SRS>EPSG:4975</SRS>' +
+' <SRS>EPSG:4976</SRS>' +
+' <SRS>EPSG:4977</SRS>' +
+' <SRS>EPSG:4978</SRS>' +
+' <SRS>EPSG:4979</SRS>' +
+' <SRS>EPSG:4980</SRS>' +
+' <SRS>EPSG:4981</SRS>' +
+' <SRS>EPSG:4982</SRS>' +
+' <SRS>EPSG:4983</SRS>' +
+' <SRS>EPSG:4984</SRS>' +
+' <SRS>EPSG:4985</SRS>' +
+' <SRS>EPSG:4986</SRS>' +
+' <SRS>EPSG:4987</SRS>' +
+' <SRS>EPSG:4988</SRS>' +
+' <SRS>EPSG:4989</SRS>' +
+' <SRS>EPSG:4990</SRS>' +
+' <SRS>EPSG:4991</SRS>' +
+' <SRS>EPSG:4992</SRS>' +
+' <SRS>EPSG:4993</SRS>' +
+' <SRS>EPSG:4994</SRS>' +
+' <SRS>EPSG:4995</SRS>' +
+' <SRS>EPSG:4996</SRS>' +
+' <SRS>EPSG:4997</SRS>' +
+' <SRS>EPSG:4998</SRS>' +
+' <SRS>EPSG:4999</SRS>' +
+' <SRS>EPSG:5600</SRS>' +
+' <SRS>EPSG:5601</SRS>' +
+' <SRS>EPSG:5602</SRS>' +
+' <SRS>EPSG:5603</SRS>' +
+' <SRS>EPSG:5604</SRS>' +
+' <SRS>EPSG:5605</SRS>' +
+' <SRS>EPSG:5606</SRS>' +
+' <SRS>EPSG:5607</SRS>' +
+' <SRS>EPSG:5608</SRS>' +
+' <SRS>EPSG:5609</SRS>' +
+' <SRS>EPSG:5701</SRS>' +
+' <SRS>EPSG:5702</SRS>' +
+' <SRS>EPSG:5703</SRS>' +
+' <SRS>EPSG:5704</SRS>' +
+' <SRS>EPSG:5705</SRS>' +
+' <SRS>EPSG:5706</SRS>' +
+' <SRS>EPSG:5709</SRS>' +
+' <SRS>EPSG:5710</SRS>' +
+' <SRS>EPSG:5711</SRS>' +
+' <SRS>EPSG:5712</SRS>' +
+' <SRS>EPSG:5713</SRS>' +
+' <SRS>EPSG:5714</SRS>' +
+' <SRS>EPSG:5715</SRS>' +
+' <SRS>EPSG:5716</SRS>' +
+' <SRS>EPSG:5717</SRS>' +
+' <SRS>EPSG:5718</SRS>' +
+' <SRS>EPSG:5719</SRS>' +
+' <SRS>EPSG:5720</SRS>' +
+' <SRS>EPSG:5721</SRS>' +
+' <SRS>EPSG:5722</SRS>' +
+' <SRS>EPSG:5723</SRS>' +
+' <SRS>EPSG:5724</SRS>' +
+' <SRS>EPSG:5725</SRS>' +
+' <SRS>EPSG:5726</SRS>' +
+' <SRS>EPSG:5727</SRS>' +
+' <SRS>EPSG:5728</SRS>' +
+' <SRS>EPSG:5729</SRS>' +
+' <SRS>EPSG:5730</SRS>' +
+' <SRS>EPSG:5731</SRS>' +
+' <SRS>EPSG:5732</SRS>' +
+' <SRS>EPSG:5733</SRS>' +
+' <SRS>EPSG:5734</SRS>' +
+' <SRS>EPSG:5735</SRS>' +
+' <SRS>EPSG:5736</SRS>' +
+' <SRS>EPSG:5737</SRS>' +
+' <SRS>EPSG:5738</SRS>' +
+' <SRS>EPSG:5739</SRS>' +
+' <SRS>EPSG:5740</SRS>' +
+' <SRS>EPSG:5741</SRS>' +
+' <SRS>EPSG:5742</SRS>' +
+' <SRS>EPSG:5743</SRS>' +
+' <SRS>EPSG:5744</SRS>' +
+' <SRS>EPSG:5745</SRS>' +
+' <SRS>EPSG:5746</SRS>' +
+' <SRS>EPSG:5747</SRS>' +
+' <SRS>EPSG:5748</SRS>' +
+' <SRS>EPSG:5749</SRS>' +
+' <SRS>EPSG:5750</SRS>' +
+' <SRS>EPSG:5751</SRS>' +
+' <SRS>EPSG:5752</SRS>' +
+' <SRS>EPSG:5753</SRS>' +
+' <SRS>EPSG:5754</SRS>' +
+' <SRS>EPSG:5755</SRS>' +
+' <SRS>EPSG:5756</SRS>' +
+' <SRS>EPSG:5757</SRS>' +
+' <SRS>EPSG:5758</SRS>' +
+' <SRS>EPSG:5759</SRS>' +
+' <SRS>EPSG:5760</SRS>' +
+' <SRS>EPSG:5761</SRS>' +
+' <SRS>EPSG:5762</SRS>' +
+' <SRS>EPSG:5763</SRS>' +
+' <SRS>EPSG:5764</SRS>' +
+' <SRS>EPSG:5765</SRS>' +
+' <SRS>EPSG:5766</SRS>' +
+' <SRS>EPSG:5767</SRS>' +
+' <SRS>EPSG:5768</SRS>' +
+' <SRS>EPSG:5769</SRS>' +
+' <SRS>EPSG:5770</SRS>' +
+' <SRS>EPSG:5771</SRS>' +
+' <SRS>EPSG:5772</SRS>' +
+' <SRS>EPSG:5773</SRS>' +
+' <SRS>EPSG:5774</SRS>' +
+' <SRS>EPSG:5775</SRS>' +
+' <SRS>EPSG:5776</SRS>' +
+' <SRS>EPSG:5777</SRS>' +
+' <SRS>EPSG:5778</SRS>' +
+' <SRS>EPSG:5779</SRS>' +
+' <SRS>EPSG:5780</SRS>' +
+' <SRS>EPSG:5781</SRS>' +
+' <SRS>EPSG:5782</SRS>' +
+' <SRS>EPSG:5783</SRS>' +
+' <SRS>EPSG:5784</SRS>' +
+' <SRS>EPSG:5785</SRS>' +
+' <SRS>EPSG:5786</SRS>' +
+' <SRS>EPSG:5787</SRS>' +
+' <SRS>EPSG:5788</SRS>' +
+' <SRS>EPSG:5789</SRS>' +
+' <SRS>EPSG:5790</SRS>' +
+' <SRS>EPSG:5791</SRS>' +
+' <SRS>EPSG:5792</SRS>' +
+' <SRS>EPSG:5793</SRS>' +
+' <SRS>EPSG:5794</SRS>' +
+' <SRS>EPSG:5795</SRS>' +
+' <SRS>EPSG:5796</SRS>' +
+' <SRS>EPSG:5797</SRS>' +
+' <SRS>EPSG:5798</SRS>' +
+' <SRS>EPSG:5799</SRS>' +
+' <SRS>EPSG:5800</SRS>' +
+' <SRS>EPSG:5801</SRS>' +
+' <SRS>EPSG:5802</SRS>' +
+' <SRS>EPSG:5803</SRS>' +
+' <SRS>EPSG:5804</SRS>' +
+' <SRS>EPSG:5805</SRS>' +
+' <SRS>EPSG:5806</SRS>' +
+' <SRS>EPSG:5807</SRS>' +
+' <SRS>EPSG:5808</SRS>' +
+' <SRS>EPSG:5809</SRS>' +
+' <SRS>EPSG:5810</SRS>' +
+' <SRS>EPSG:5811</SRS>' +
+' <SRS>EPSG:5812</SRS>' +
+' <SRS>EPSG:5813</SRS>' +
+' <SRS>EPSG:5814</SRS>' +
+' <SRS>EPSG:5815</SRS>' +
+' <SRS>EPSG:5816</SRS>' +
+' <SRS>EPSG:5817</SRS>' +
+' <SRS>EPSG:5818</SRS>' +
+' <SRS>EPSG:7400</SRS>' +
+' <SRS>EPSG:7401</SRS>' +
+' <SRS>EPSG:7402</SRS>' +
+' <SRS>EPSG:7403</SRS>' +
+' <SRS>EPSG:7404</SRS>' +
+' <SRS>EPSG:7405</SRS>' +
+' <SRS>EPSG:7406</SRS>' +
+' <SRS>EPSG:7407</SRS>' +
+' <SRS>EPSG:7408</SRS>' +
+' <SRS>EPSG:7409</SRS>' +
+' <SRS>EPSG:7410</SRS>' +
+' <SRS>EPSG:7411</SRS>' +
+' <SRS>EPSG:7412</SRS>' +
+' <SRS>EPSG:7413</SRS>' +
+' <SRS>EPSG:7414</SRS>' +
+' <SRS>EPSG:7415</SRS>' +
+' <SRS>EPSG:7416</SRS>' +
+' <SRS>EPSG:7417</SRS>' +
+' <SRS>EPSG:7418</SRS>' +
+' <SRS>EPSG:7419</SRS>' +
+' <SRS>EPSG:7420</SRS>' +
+' <SRS>EPSG:20004</SRS>' +
+' <SRS>EPSG:20005</SRS>' +
+' <SRS>EPSG:20006</SRS>' +
+' <SRS>EPSG:20007</SRS>' +
+' <SRS>EPSG:20008</SRS>' +
+' <SRS>EPSG:20009</SRS>' +
+' <SRS>EPSG:20010</SRS>' +
+' <SRS>EPSG:20011</SRS>' +
+' <SRS>EPSG:20012</SRS>' +
+' <SRS>EPSG:20013</SRS>' +
+' <SRS>EPSG:20014</SRS>' +
+' <SRS>EPSG:20015</SRS>' +
+' <SRS>EPSG:20016</SRS>' +
+' <SRS>EPSG:20017</SRS>' +
+' <SRS>EPSG:20018</SRS>' +
+' <SRS>EPSG:20019</SRS>' +
+' <SRS>EPSG:20020</SRS>' +
+' <SRS>EPSG:20021</SRS>' +
+' <SRS>EPSG:20022</SRS>' +
+' <SRS>EPSG:20023</SRS>' +
+' <SRS>EPSG:20024</SRS>' +
+' <SRS>EPSG:20025</SRS>' +
+' <SRS>EPSG:20026</SRS>' +
+' <SRS>EPSG:20027</SRS>' +
+' <SRS>EPSG:20028</SRS>' +
+' <SRS>EPSG:20029</SRS>' +
+' <SRS>EPSG:20030</SRS>' +
+' <SRS>EPSG:20031</SRS>' +
+' <SRS>EPSG:20032</SRS>' +
+' <SRS>EPSG:20064</SRS>' +
+' <SRS>EPSG:20065</SRS>' +
+' <SRS>EPSG:20066</SRS>' +
+' <SRS>EPSG:20067</SRS>' +
+' <SRS>EPSG:20068</SRS>' +
+' <SRS>EPSG:20069</SRS>' +
+' <SRS>EPSG:20070</SRS>' +
+' <SRS>EPSG:20071</SRS>' +
+' <SRS>EPSG:20072</SRS>' +
+' <SRS>EPSG:20073</SRS>' +
+' <SRS>EPSG:20074</SRS>' +
+' <SRS>EPSG:20075</SRS>' +
+' <SRS>EPSG:20076</SRS>' +
+' <SRS>EPSG:20077</SRS>' +
+' <SRS>EPSG:20078</SRS>' +
+' <SRS>EPSG:20079</SRS>' +
+' <SRS>EPSG:20080</SRS>' +
+' <SRS>EPSG:20081</SRS>' +
+' <SRS>EPSG:20082</SRS>' +
+' <SRS>EPSG:20083</SRS>' +
+' <SRS>EPSG:20084</SRS>' +
+' <SRS>EPSG:20085</SRS>' +
+' <SRS>EPSG:20086</SRS>' +
+' <SRS>EPSG:20087</SRS>' +
+' <SRS>EPSG:20088</SRS>' +
+' <SRS>EPSG:20089</SRS>' +
+' <SRS>EPSG:20090</SRS>' +
+' <SRS>EPSG:20091</SRS>' +
+' <SRS>EPSG:20092</SRS>' +
+' <SRS>EPSG:20135</SRS>' +
+' <SRS>EPSG:20136</SRS>' +
+' <SRS>EPSG:20137</SRS>' +
+' <SRS>EPSG:20138</SRS>' +
+' <SRS>EPSG:20248</SRS>' +
+' <SRS>EPSG:20249</SRS>' +
+' <SRS>EPSG:20250</SRS>' +
+' <SRS>EPSG:20251</SRS>' +
+' <SRS>EPSG:20252</SRS>' +
+' <SRS>EPSG:20253</SRS>' +
+' <SRS>EPSG:20254</SRS>' +
+' <SRS>EPSG:20255</SRS>' +
+' <SRS>EPSG:20256</SRS>' +
+' <SRS>EPSG:20257</SRS>' +
+' <SRS>EPSG:20258</SRS>' +
+' <SRS>EPSG:20348</SRS>' +
+' <SRS>EPSG:20349</SRS>' +
+' <SRS>EPSG:20350</SRS>' +
+' <SRS>EPSG:20351</SRS>' +
+' <SRS>EPSG:20352</SRS>' +
+' <SRS>EPSG:20353</SRS>' +
+' <SRS>EPSG:20354</SRS>' +
+' <SRS>EPSG:20355</SRS>' +
+' <SRS>EPSG:20356</SRS>' +
+' <SRS>EPSG:20357</SRS>' +
+' <SRS>EPSG:20358</SRS>' +
+' <SRS>EPSG:20436</SRS>' +
+' <SRS>EPSG:20437</SRS>' +
+' <SRS>EPSG:20438</SRS>' +
+' <SRS>EPSG:20439</SRS>' +
+' <SRS>EPSG:20440</SRS>' +
+' <SRS>EPSG:20499</SRS>' +
+' <SRS>EPSG:20538</SRS>' +
+' <SRS>EPSG:20539</SRS>' +
+' <SRS>EPSG:20790</SRS>' +
+' <SRS>EPSG:20791</SRS>' +
+' <SRS>EPSG:20822</SRS>' +
+' <SRS>EPSG:20823</SRS>' +
+' <SRS>EPSG:20824</SRS>' +
+' <SRS>EPSG:20934</SRS>' +
+' <SRS>EPSG:20935</SRS>' +
+' <SRS>EPSG:20936</SRS>' +
+' <SRS>EPSG:21035</SRS>' +
+' <SRS>EPSG:21036</SRS>' +
+' <SRS>EPSG:21037</SRS>' +
+' <SRS>EPSG:21095</SRS>' +
+' <SRS>EPSG:21096</SRS>' +
+' <SRS>EPSG:21097</SRS>' +
+' <SRS>EPSG:21100</SRS>' +
+' <SRS>EPSG:21148</SRS>' +
+' <SRS>EPSG:21149</SRS>' +
+' <SRS>EPSG:21150</SRS>' +
+' <SRS>EPSG:21291</SRS>' +
+' <SRS>EPSG:21292</SRS>' +
+' <SRS>EPSG:21413</SRS>' +
+' <SRS>EPSG:21414</SRS>' +
+' <SRS>EPSG:21415</SRS>' +
+' <SRS>EPSG:21416</SRS>' +
+' <SRS>EPSG:21417</SRS>' +
+' <SRS>EPSG:21418</SRS>' +
+' <SRS>EPSG:21419</SRS>' +
+' <SRS>EPSG:21420</SRS>' +
+' <SRS>EPSG:21421</SRS>' +
+' <SRS>EPSG:21422</SRS>' +
+' <SRS>EPSG:21423</SRS>' +
+' <SRS>EPSG:21453</SRS>' +
+' <SRS>EPSG:21454</SRS>' +
+' <SRS>EPSG:21455</SRS>' +
+' <SRS>EPSG:21456</SRS>' +
+' <SRS>EPSG:21457</SRS>' +
+' <SRS>EPSG:21458</SRS>' +
+' <SRS>EPSG:21459</SRS>' +
+' <SRS>EPSG:21460</SRS>' +
+' <SRS>EPSG:21461</SRS>' +
+' <SRS>EPSG:21462</SRS>' +
+' <SRS>EPSG:21463</SRS>' +
+' <SRS>EPSG:21473</SRS>' +
+' <SRS>EPSG:21474</SRS>' +
+' <SRS>EPSG:21475</SRS>' +
+' <SRS>EPSG:21476</SRS>' +
+' <SRS>EPSG:21477</SRS>' +
+' <SRS>EPSG:21478</SRS>' +
+' <SRS>EPSG:21479</SRS>' +
+' <SRS>EPSG:21480</SRS>' +
+' <SRS>EPSG:21481</SRS>' +
+' <SRS>EPSG:21482</SRS>' +
+' <SRS>EPSG:21483</SRS>' +
+' <SRS>EPSG:21500</SRS>' +
+' <SRS>EPSG:21780</SRS>' +
+' <SRS>EPSG:21781</SRS>' +
+' <SRS>EPSG:21817</SRS>' +
+' <SRS>EPSG:21818</SRS>' +
+' <SRS>EPSG:21891</SRS>' +
+' <SRS>EPSG:21892</SRS>' +
+' <SRS>EPSG:21893</SRS>' +
+' <SRS>EPSG:21894</SRS>' +
+' <SRS>EPSG:21896</SRS>' +
+' <SRS>EPSG:21897</SRS>' +
+' <SRS>EPSG:21898</SRS>' +
+' <SRS>EPSG:21899</SRS>' +
+' <SRS>EPSG:22032</SRS>' +
+' <SRS>EPSG:22033</SRS>' +
+' <SRS>EPSG:22091</SRS>' +
+' <SRS>EPSG:22092</SRS>' +
+' <SRS>EPSG:22171</SRS>' +
+' <SRS>EPSG:22172</SRS>' +
+' <SRS>EPSG:22173</SRS>' +
+' <SRS>EPSG:22174</SRS>' +
+' <SRS>EPSG:22175</SRS>' +
+' <SRS>EPSG:22176</SRS>' +
+' <SRS>EPSG:22177</SRS>' +
+' <SRS>EPSG:22181</SRS>' +
+' <SRS>EPSG:22182</SRS>' +
+' <SRS>EPSG:22183</SRS>' +
+' <SRS>EPSG:22184</SRS>' +
+' <SRS>EPSG:22185</SRS>' +
+' <SRS>EPSG:22186</SRS>' +
+' <SRS>EPSG:22187</SRS>' +
+' <SRS>EPSG:22191</SRS>' +
+' <SRS>EPSG:22192</SRS>' +
+' <SRS>EPSG:22193</SRS>' +
+' <SRS>EPSG:22194</SRS>' +
+' <SRS>EPSG:22195</SRS>' +
+' <SRS>EPSG:22196</SRS>' +
+' <SRS>EPSG:22197</SRS>' +
+' <SRS>EPSG:22234</SRS>' +
+' <SRS>EPSG:22235</SRS>' +
+' <SRS>EPSG:22236</SRS>' +
+' <SRS>EPSG:22275</SRS>' +
+' <SRS>EPSG:22277</SRS>' +
+' <SRS>EPSG:22279</SRS>' +
+' <SRS>EPSG:22281</SRS>' +
+' <SRS>EPSG:22283</SRS>' +
+' <SRS>EPSG:22285</SRS>' +
+' <SRS>EPSG:22287</SRS>' +
+' <SRS>EPSG:22289</SRS>' +
+' <SRS>EPSG:22291</SRS>' +
+' <SRS>EPSG:22293</SRS>' +
+' <SRS>EPSG:22300</SRS>' +
+' <SRS>EPSG:22332</SRS>' +
+' <SRS>EPSG:22391</SRS>' +
+' <SRS>EPSG:22392</SRS>' +
+' <SRS>EPSG:22521</SRS>' +
+' <SRS>EPSG:22522</SRS>' +
+' <SRS>EPSG:22523</SRS>' +
+' <SRS>EPSG:22524</SRS>' +
+' <SRS>EPSG:22525</SRS>' +
+' <SRS>EPSG:22700</SRS>' +
+' <SRS>EPSG:22770</SRS>' +
+' <SRS>EPSG:22780</SRS>' +
+' <SRS>EPSG:22832</SRS>' +
+' <SRS>EPSG:22991</SRS>' +
+' <SRS>EPSG:22992</SRS>' +
+' <SRS>EPSG:22993</SRS>' +
+' <SRS>EPSG:22994</SRS>' +
+' <SRS>EPSG:23028</SRS>' +
+' <SRS>EPSG:23029</SRS>' +
+' <SRS>EPSG:23030</SRS>' +
+' <SRS>EPSG:23031</SRS>' +
+' <SRS>EPSG:23032</SRS>' +
+' <SRS>EPSG:23033</SRS>' +
+' <SRS>EPSG:23034</SRS>' +
+' <SRS>EPSG:23035</SRS>' +
+' <SRS>EPSG:23036</SRS>' +
+' <SRS>EPSG:23037</SRS>' +
+' <SRS>EPSG:23038</SRS>' +
+' <SRS>EPSG:23090</SRS>' +
+' <SRS>EPSG:23095</SRS>' +
+' <SRS>EPSG:23239</SRS>' +
+' <SRS>EPSG:23240</SRS>' +
+' <SRS>EPSG:23433</SRS>' +
+' <SRS>EPSG:23700</SRS>' +
+' <SRS>EPSG:23846</SRS>' +
+' <SRS>EPSG:23847</SRS>' +
+' <SRS>EPSG:23848</SRS>' +
+' <SRS>EPSG:23849</SRS>' +
+' <SRS>EPSG:23850</SRS>' +
+' <SRS>EPSG:23851</SRS>' +
+' <SRS>EPSG:23852</SRS>' +
+' <SRS>EPSG:23853</SRS>' +
+' <SRS>EPSG:23866</SRS>' +
+' <SRS>EPSG:23867</SRS>' +
+' <SRS>EPSG:23868</SRS>' +
+' <SRS>EPSG:23869</SRS>' +
+' <SRS>EPSG:23870</SRS>' +
+' <SRS>EPSG:23871</SRS>' +
+' <SRS>EPSG:23872</SRS>' +
+' <SRS>EPSG:23877</SRS>' +
+' <SRS>EPSG:23878</SRS>' +
+' <SRS>EPSG:23879</SRS>' +
+' <SRS>EPSG:23880</SRS>' +
+' <SRS>EPSG:23881</SRS>' +
+' <SRS>EPSG:23882</SRS>' +
+' <SRS>EPSG:23883</SRS>' +
+' <SRS>EPSG:23884</SRS>' +
+' <SRS>EPSG:23886</SRS>' +
+' <SRS>EPSG:23887</SRS>' +
+' <SRS>EPSG:23888</SRS>' +
+' <SRS>EPSG:23889</SRS>' +
+' <SRS>EPSG:23890</SRS>' +
+' <SRS>EPSG:23891</SRS>' +
+' <SRS>EPSG:23892</SRS>' +
+' <SRS>EPSG:23893</SRS>' +
+' <SRS>EPSG:23894</SRS>' +
+' <SRS>EPSG:23946</SRS>' +
+' <SRS>EPSG:23947</SRS>' +
+' <SRS>EPSG:23948</SRS>' +
+' <SRS>EPSG:24047</SRS>' +
+' <SRS>EPSG:24048</SRS>' +
+' <SRS>EPSG:24100</SRS>' +
+' <SRS>EPSG:24200</SRS>' +
+' <SRS>EPSG:24305</SRS>' +
+' <SRS>EPSG:24306</SRS>' +
+' <SRS>EPSG:24311</SRS>' +
+' <SRS>EPSG:24312</SRS>' +
+' <SRS>EPSG:24313</SRS>' +
+' <SRS>EPSG:24342</SRS>' +
+' <SRS>EPSG:24343</SRS>' +
+' <SRS>EPSG:24344</SRS>' +
+' <SRS>EPSG:24345</SRS>' +
+' <SRS>EPSG:24346</SRS>' +
+' <SRS>EPSG:24347</SRS>' +
+' <SRS>EPSG:24370</SRS>' +
+' <SRS>EPSG:24371</SRS>' +
+' <SRS>EPSG:24372</SRS>' +
+' <SRS>EPSG:24373</SRS>' +
+' <SRS>EPSG:24374</SRS>' +
+' <SRS>EPSG:24375</SRS>' +
+' <SRS>EPSG:24376</SRS>' +
+' <SRS>EPSG:24377</SRS>' +
+' <SRS>EPSG:24378</SRS>' +
+' <SRS>EPSG:24379</SRS>' +
+' <SRS>EPSG:24380</SRS>' +
+' <SRS>EPSG:24381</SRS>' +
+' <SRS>EPSG:24382</SRS>' +
+' <SRS>EPSG:24383</SRS>' +
+' <SRS>EPSG:24500</SRS>' +
+' <SRS>EPSG:24547</SRS>' +
+' <SRS>EPSG:24548</SRS>' +
+' <SRS>EPSG:24571</SRS>' +
+' <SRS>EPSG:24600</SRS>' +
+' <SRS>EPSG:24718</SRS>' +
+' <SRS>EPSG:24719</SRS>' +
+' <SRS>EPSG:24720</SRS>' +
+' <SRS>EPSG:24817</SRS>' +
+' <SRS>EPSG:24818</SRS>' +
+' <SRS>EPSG:24819</SRS>' +
+' <SRS>EPSG:24820</SRS>' +
+' <SRS>EPSG:24821</SRS>' +
+' <SRS>EPSG:24877</SRS>' +
+' <SRS>EPSG:24878</SRS>' +
+' <SRS>EPSG:24879</SRS>' +
+' <SRS>EPSG:24880</SRS>' +
+' <SRS>EPSG:24881</SRS>' +
+' <SRS>EPSG:24882</SRS>' +
+' <SRS>EPSG:24891</SRS>' +
+' <SRS>EPSG:24892</SRS>' +
+' <SRS>EPSG:24893</SRS>' +
+' <SRS>EPSG:25000</SRS>' +
+' <SRS>EPSG:25231</SRS>' +
+' <SRS>EPSG:25391</SRS>' +
+' <SRS>EPSG:25392</SRS>' +
+' <SRS>EPSG:25393</SRS>' +
+' <SRS>EPSG:25394</SRS>' +
+' <SRS>EPSG:25395</SRS>' +
+' <SRS>EPSG:25700</SRS>' +
+' <SRS>EPSG:25828</SRS>' +
+' <SRS>EPSG:25829</SRS>' +
+' <SRS>EPSG:25830</SRS>' +
+' <SRS>EPSG:25831</SRS>' +
+' <SRS>EPSG:25832</SRS>' +
+' <SRS>EPSG:25833</SRS>' +
+' <SRS>EPSG:25834</SRS>' +
+' <SRS>EPSG:25835</SRS>' +
+' <SRS>EPSG:25836</SRS>' +
+' <SRS>EPSG:25837</SRS>' +
+' <SRS>EPSG:25838</SRS>' +
+' <SRS>EPSG:25884</SRS>' +
+' <SRS>EPSG:25932</SRS>' +
+' <SRS>EPSG:26191</SRS>' +
+' <SRS>EPSG:26192</SRS>' +
+' <SRS>EPSG:26193</SRS>' +
+' <SRS>EPSG:26194</SRS>' +
+' <SRS>EPSG:26195</SRS>' +
+' <SRS>EPSG:26237</SRS>' +
+' <SRS>EPSG:26331</SRS>' +
+' <SRS>EPSG:26332</SRS>' +
+' <SRS>EPSG:26391</SRS>' +
+' <SRS>EPSG:26392</SRS>' +
+' <SRS>EPSG:26393</SRS>' +
+' <SRS>EPSG:26432</SRS>' +
+' <SRS>EPSG:26591</SRS>' +
+' <SRS>EPSG:26592</SRS>' +
+' <SRS>EPSG:26632</SRS>' +
+' <SRS>EPSG:26692</SRS>' +
+' <SRS>EPSG:26701</SRS>' +
+' <SRS>EPSG:26702</SRS>' +
+' <SRS>EPSG:26703</SRS>' +
+' <SRS>EPSG:26704</SRS>' +
+' <SRS>EPSG:26705</SRS>' +
+' <SRS>EPSG:26706</SRS>' +
+' <SRS>EPSG:26707</SRS>' +
+' <SRS>EPSG:26708</SRS>' +
+' <SRS>EPSG:26709</SRS>' +
+' <SRS>EPSG:26710</SRS>' +
+' <SRS>EPSG:26711</SRS>' +
+' <SRS>EPSG:26712</SRS>' +
+' <SRS>EPSG:26713</SRS>' +
+' <SRS>EPSG:26714</SRS>' +
+' <SRS>EPSG:26715</SRS>' +
+' <SRS>EPSG:26716</SRS>' +
+' <SRS>EPSG:26717</SRS>' +
+' <SRS>EPSG:26718</SRS>' +
+' <SRS>EPSG:26719</SRS>' +
+' <SRS>EPSG:26720</SRS>' +
+' <SRS>EPSG:26721</SRS>' +
+' <SRS>EPSG:26722</SRS>' +
+' <SRS>EPSG:26729</SRS>' +
+' <SRS>EPSG:26730</SRS>' +
+' <SRS>EPSG:26731</SRS>' +
+' <SRS>EPSG:26732</SRS>' +
+' <SRS>EPSG:26733</SRS>' +
+' <SRS>EPSG:26734</SRS>' +
+' <SRS>EPSG:26735</SRS>' +
+' <SRS>EPSG:26736</SRS>' +
+' <SRS>EPSG:26737</SRS>' +
+' <SRS>EPSG:26738</SRS>' +
+' <SRS>EPSG:26739</SRS>' +
+' <SRS>EPSG:26740</SRS>' +
+' <SRS>EPSG:26741</SRS>' +
+' <SRS>EPSG:26742</SRS>' +
+' <SRS>EPSG:26743</SRS>' +
+' <SRS>EPSG:26744</SRS>' +
+' <SRS>EPSG:26745</SRS>' +
+' <SRS>EPSG:26746</SRS>' +
+' <SRS>EPSG:26747</SRS>' +
+' <SRS>EPSG:26748</SRS>' +
+' <SRS>EPSG:26749</SRS>' +
+' <SRS>EPSG:26750</SRS>' +
+' <SRS>EPSG:26751</SRS>' +
+' <SRS>EPSG:26752</SRS>' +
+' <SRS>EPSG:26753</SRS>' +
+' <SRS>EPSG:26754</SRS>' +
+' <SRS>EPSG:26755</SRS>' +
+' <SRS>EPSG:26756</SRS>' +
+' <SRS>EPSG:26757</SRS>' +
+' <SRS>EPSG:26758</SRS>' +
+' <SRS>EPSG:26759</SRS>' +
+' <SRS>EPSG:26760</SRS>' +
+' <SRS>EPSG:26766</SRS>' +
+' <SRS>EPSG:26767</SRS>' +
+' <SRS>EPSG:26768</SRS>' +
+' <SRS>EPSG:26769</SRS>' +
+' <SRS>EPSG:26770</SRS>' +
+' <SRS>EPSG:26771</SRS>' +
+' <SRS>EPSG:26772</SRS>' +
+' <SRS>EPSG:26773</SRS>' +
+' <SRS>EPSG:26774</SRS>' +
+' <SRS>EPSG:26775</SRS>' +
+' <SRS>EPSG:26776</SRS>' +
+' <SRS>EPSG:26777</SRS>' +
+' <SRS>EPSG:26778</SRS>' +
+' <SRS>EPSG:26779</SRS>' +
+' <SRS>EPSG:26780</SRS>' +
+' <SRS>EPSG:26781</SRS>' +
+' <SRS>EPSG:26782</SRS>' +
+' <SRS>EPSG:26783</SRS>' +
+' <SRS>EPSG:26784</SRS>' +
+' <SRS>EPSG:26785</SRS>' +
+' <SRS>EPSG:26786</SRS>' +
+' <SRS>EPSG:26787</SRS>' +
+' <SRS>EPSG:26791</SRS>' +
+' <SRS>EPSG:26792</SRS>' +
+' <SRS>EPSG:26793</SRS>' +
+' <SRS>EPSG:26794</SRS>' +
+' <SRS>EPSG:26795</SRS>' +
+' <SRS>EPSG:26796</SRS>' +
+' <SRS>EPSG:26797</SRS>' +
+' <SRS>EPSG:26798</SRS>' +
+' <SRS>EPSG:26799</SRS>' +
+' <SRS>EPSG:26801</SRS>' +
+' <SRS>EPSG:26802</SRS>' +
+' <SRS>EPSG:26803</SRS>' +
+' <SRS>EPSG:26811</SRS>' +
+' <SRS>EPSG:26812</SRS>' +
+' <SRS>EPSG:26813</SRS>' +
+' <SRS>EPSG:26901</SRS>' +
+' <SRS>EPSG:26902</SRS>' +
+' <SRS>EPSG:26903</SRS>' +
+' <SRS>EPSG:26904</SRS>' +
+' <SRS>EPSG:26905</SRS>' +
+' <SRS>EPSG:26906</SRS>' +
+' <SRS>EPSG:26907</SRS>' +
+' <SRS>EPSG:26908</SRS>' +
+' <SRS>EPSG:26909</SRS>' +
+' <SRS>EPSG:26910</SRS>' +
+' <SRS>EPSG:26911</SRS>' +
+' <SRS>EPSG:26912</SRS>' +
+' <SRS>EPSG:26913</SRS>' +
+' <SRS>EPSG:26914</SRS>' +
+' <SRS>EPSG:26915</SRS>' +
+' <SRS>EPSG:26916</SRS>' +
+' <SRS>EPSG:26917</SRS>' +
+' <SRS>EPSG:26918</SRS>' +
+' <SRS>EPSG:26919</SRS>' +
+' <SRS>EPSG:26920</SRS>' +
+' <SRS>EPSG:26921</SRS>' +
+' <SRS>EPSG:26922</SRS>' +
+' <SRS>EPSG:26923</SRS>' +
+' <SRS>EPSG:26929</SRS>' +
+' <SRS>EPSG:26930</SRS>' +
+' <SRS>EPSG:26931</SRS>' +
+' <SRS>EPSG:26932</SRS>' +
+' <SRS>EPSG:26933</SRS>' +
+' <SRS>EPSG:26934</SRS>' +
+' <SRS>EPSG:26935</SRS>' +
+' <SRS>EPSG:26936</SRS>' +
+' <SRS>EPSG:26937</SRS>' +
+' <SRS>EPSG:26938</SRS>' +
+' <SRS>EPSG:26939</SRS>' +
+' <SRS>EPSG:26940</SRS>' +
+' <SRS>EPSG:26941</SRS>' +
+' <SRS>EPSG:26942</SRS>' +
+' <SRS>EPSG:26943</SRS>' +
+' <SRS>EPSG:26944</SRS>' +
+' <SRS>EPSG:26945</SRS>' +
+' <SRS>EPSG:26946</SRS>' +
+' <SRS>EPSG:26948</SRS>' +
+' <SRS>EPSG:26949</SRS>' +
+' <SRS>EPSG:26950</SRS>' +
+' <SRS>EPSG:26951</SRS>' +
+' <SRS>EPSG:26952</SRS>' +
+' <SRS>EPSG:26953</SRS>' +
+' <SRS>EPSG:26954</SRS>' +
+' <SRS>EPSG:26955</SRS>' +
+' <SRS>EPSG:26956</SRS>' +
+' <SRS>EPSG:26957</SRS>' +
+' <SRS>EPSG:26958</SRS>' +
+' <SRS>EPSG:26959</SRS>' +
+' <SRS>EPSG:26960</SRS>' +
+' <SRS>EPSG:26961</SRS>' +
+' <SRS>EPSG:26962</SRS>' +
+' <SRS>EPSG:26963</SRS>' +
+' <SRS>EPSG:26964</SRS>' +
+' <SRS>EPSG:26965</SRS>' +
+' <SRS>EPSG:26966</SRS>' +
+' <SRS>EPSG:26967</SRS>' +
+' <SRS>EPSG:26968</SRS>' +
+' <SRS>EPSG:26969</SRS>' +
+' <SRS>EPSG:26970</SRS>' +
+' <SRS>EPSG:26971</SRS>' +
+' <SRS>EPSG:26972</SRS>' +
+' <SRS>EPSG:26973</SRS>' +
+' <SRS>EPSG:26974</SRS>' +
+' <SRS>EPSG:26975</SRS>' +
+' <SRS>EPSG:26976</SRS>' +
+' <SRS>EPSG:26977</SRS>' +
+' <SRS>EPSG:26978</SRS>' +
+' <SRS>EPSG:26979</SRS>' +
+' <SRS>EPSG:26980</SRS>' +
+' <SRS>EPSG:26981</SRS>' +
+' <SRS>EPSG:26982</SRS>' +
+' <SRS>EPSG:26983</SRS>' +
+' <SRS>EPSG:26984</SRS>' +
+' <SRS>EPSG:26985</SRS>' +
+' <SRS>EPSG:26986</SRS>' +
+' <SRS>EPSG:26987</SRS>' +
+' <SRS>EPSG:26988</SRS>' +
+' <SRS>EPSG:26989</SRS>' +
+' <SRS>EPSG:26990</SRS>' +
+' <SRS>EPSG:26991</SRS>' +
+' <SRS>EPSG:26992</SRS>' +
+' <SRS>EPSG:26993</SRS>' +
+' <SRS>EPSG:26994</SRS>' +
+' <SRS>EPSG:26995</SRS>' +
+' <SRS>EPSG:26996</SRS>' +
+' <SRS>EPSG:26997</SRS>' +
+' <SRS>EPSG:26998</SRS>' +
+' <SRS>EPSG:27037</SRS>' +
+' <SRS>EPSG:27038</SRS>' +
+' <SRS>EPSG:27039</SRS>' +
+' <SRS>EPSG:27040</SRS>' +
+' <SRS>EPSG:27120</SRS>' +
+' <SRS>EPSG:27200</SRS>' +
+' <SRS>EPSG:27205</SRS>' +
+' <SRS>EPSG:27206</SRS>' +
+' <SRS>EPSG:27207</SRS>' +
+' <SRS>EPSG:27208</SRS>' +
+' <SRS>EPSG:27209</SRS>' +
+' <SRS>EPSG:27210</SRS>' +
+' <SRS>EPSG:27211</SRS>' +
+' <SRS>EPSG:27212</SRS>' +
+' <SRS>EPSG:27213</SRS>' +
+' <SRS>EPSG:27214</SRS>' +
+' <SRS>EPSG:27215</SRS>' +
+' <SRS>EPSG:27216</SRS>' +
+' <SRS>EPSG:27217</SRS>' +
+' <SRS>EPSG:27218</SRS>' +
+' <SRS>EPSG:27219</SRS>' +
+' <SRS>EPSG:27220</SRS>' +
+' <SRS>EPSG:27221</SRS>' +
+' <SRS>EPSG:27222</SRS>' +
+' <SRS>EPSG:27223</SRS>' +
+' <SRS>EPSG:27224</SRS>' +
+' <SRS>EPSG:27225</SRS>' +
+' <SRS>EPSG:27226</SRS>' +
+' <SRS>EPSG:27227</SRS>' +
+' <SRS>EPSG:27228</SRS>' +
+' <SRS>EPSG:27229</SRS>' +
+' <SRS>EPSG:27230</SRS>' +
+' <SRS>EPSG:27231</SRS>' +
+' <SRS>EPSG:27232</SRS>' +
+' <SRS>EPSG:27258</SRS>' +
+' <SRS>EPSG:27259</SRS>' +
+' <SRS>EPSG:27260</SRS>' +
+' <SRS>EPSG:27291</SRS>' +
+' <SRS>EPSG:27292</SRS>' +
+' <SRS>EPSG:27391</SRS>' +
+' <SRS>EPSG:27392</SRS>' +
+' <SRS>EPSG:27393</SRS>' +
+' <SRS>EPSG:27394</SRS>' +
+' <SRS>EPSG:27395</SRS>' +
+' <SRS>EPSG:27396</SRS>' +
+' <SRS>EPSG:27397</SRS>' +
+' <SRS>EPSG:27398</SRS>' +
+' <SRS>EPSG:27429</SRS>' +
+' <SRS>EPSG:27492</SRS>' +
+' <SRS>EPSG:27500</SRS>' +
+' <SRS>EPSG:27561</SRS>' +
+' <SRS>EPSG:27562</SRS>' +
+' <SRS>EPSG:27563</SRS>' +
+' <SRS>EPSG:27564</SRS>' +
+' <SRS>EPSG:27571</SRS>' +
+' <SRS>EPSG:27572</SRS>' +
+' <SRS>EPSG:27573</SRS>' +
+' <SRS>EPSG:27574</SRS>' +
+' <SRS>EPSG:27581</SRS>' +
+' <SRS>EPSG:27582</SRS>' +
+' <SRS>EPSG:27583</SRS>' +
+' <SRS>EPSG:27584</SRS>' +
+' <SRS>EPSG:27591</SRS>' +
+' <SRS>EPSG:27592</SRS>' +
+' <SRS>EPSG:27593</SRS>' +
+' <SRS>EPSG:27594</SRS>' +
+' <SRS>EPSG:27700</SRS>' +
+' <SRS>EPSG:28191</SRS>' +
+' <SRS>EPSG:28192</SRS>' +
+' <SRS>EPSG:28193</SRS>' +
+' <SRS>EPSG:28232</SRS>' +
+' <SRS>EPSG:28348</SRS>' +
+' <SRS>EPSG:28349</SRS>' +
+' <SRS>EPSG:28350</SRS>' +
+' <SRS>EPSG:28351</SRS>' +
+' <SRS>EPSG:28352</SRS>' +
+' <SRS>EPSG:28353</SRS>' +
+' <SRS>EPSG:28354</SRS>' +
+' <SRS>EPSG:28355</SRS>' +
+' <SRS>EPSG:28356</SRS>' +
+' <SRS>EPSG:28357</SRS>' +
+' <SRS>EPSG:28358</SRS>' +
+' <SRS>EPSG:28402</SRS>' +
+' <SRS>EPSG:28403</SRS>' +
+' <SRS>EPSG:28404</SRS>' +
+' <SRS>EPSG:28405</SRS>' +
+' <SRS>EPSG:28406</SRS>' +
+' <SRS>EPSG:28407</SRS>' +
+' <SRS>EPSG:28408</SRS>' +
+' <SRS>EPSG:28409</SRS>' +
+' <SRS>EPSG:28410</SRS>' +
+' <SRS>EPSG:28411</SRS>' +
+' <SRS>EPSG:28412</SRS>' +
+' <SRS>EPSG:28413</SRS>' +
+' <SRS>EPSG:28414</SRS>' +
+' <SRS>EPSG:28415</SRS>' +
+' <SRS>EPSG:28416</SRS>' +
+' <SRS>EPSG:28417</SRS>' +
+' <SRS>EPSG:28418</SRS>' +
+' <SRS>EPSG:28419</SRS>' +
+' <SRS>EPSG:28420</SRS>' +
+' <SRS>EPSG:28421</SRS>' +
+' <SRS>EPSG:28422</SRS>' +
+' <SRS>EPSG:28423</SRS>' +
+' <SRS>EPSG:28424</SRS>' +
+' <SRS>EPSG:28425</SRS>' +
+' <SRS>EPSG:28426</SRS>' +
+' <SRS>EPSG:28427</SRS>' +
+' <SRS>EPSG:28428</SRS>' +
+' <SRS>EPSG:28429</SRS>' +
+' <SRS>EPSG:28430</SRS>' +
+' <SRS>EPSG:28431</SRS>' +
+' <SRS>EPSG:28432</SRS>' +
+' <SRS>EPSG:28462</SRS>' +
+' <SRS>EPSG:28463</SRS>' +
+' <SRS>EPSG:28464</SRS>' +
+' <SRS>EPSG:28465</SRS>' +
+' <SRS>EPSG:28466</SRS>' +
+' <SRS>EPSG:28467</SRS>' +
+' <SRS>EPSG:28468</SRS>' +
+' <SRS>EPSG:28469</SRS>' +
+' <SRS>EPSG:28470</SRS>' +
+' <SRS>EPSG:28471</SRS>' +
+' <SRS>EPSG:28472</SRS>' +
+' <SRS>EPSG:28473</SRS>' +
+' <SRS>EPSG:28474</SRS>' +
+' <SRS>EPSG:28475</SRS>' +
+' <SRS>EPSG:28476</SRS>' +
+' <SRS>EPSG:28477</SRS>' +
+' <SRS>EPSG:28478</SRS>' +
+' <SRS>EPSG:28479</SRS>' +
+' <SRS>EPSG:28480</SRS>' +
+' <SRS>EPSG:28481</SRS>' +
+' <SRS>EPSG:28482</SRS>' +
+' <SRS>EPSG:28483</SRS>' +
+' <SRS>EPSG:28484</SRS>' +
+' <SRS>EPSG:28485</SRS>' +
+' <SRS>EPSG:28486</SRS>' +
+' <SRS>EPSG:28487</SRS>' +
+' <SRS>EPSG:28488</SRS>' +
+' <SRS>EPSG:28489</SRS>' +
+' <SRS>EPSG:28490</SRS>' +
+' <SRS>EPSG:28491</SRS>' +
+' <SRS>EPSG:28492</SRS>' +
+' <SRS>EPSG:28600</SRS>' +
+' <SRS>EPSG:28991</SRS>' +
+' <SRS>EPSG:28992</SRS>' +
+' <SRS>EPSG:29100</SRS>' +
+' <SRS>EPSG:29101</SRS>' +
+' <SRS>EPSG:29118</SRS>' +
+' <SRS>EPSG:29119</SRS>' +
+' <SRS>EPSG:29120</SRS>' +
+' <SRS>EPSG:29121</SRS>' +
+' <SRS>EPSG:29122</SRS>' +
+' <SRS>EPSG:29168</SRS>' +
+' <SRS>EPSG:29169</SRS>' +
+' <SRS>EPSG:29170</SRS>' +
+' <SRS>EPSG:29171</SRS>' +
+' <SRS>EPSG:29172</SRS>' +
+' <SRS>EPSG:29177</SRS>' +
+' <SRS>EPSG:29178</SRS>' +
+' <SRS>EPSG:29179</SRS>' +
+' <SRS>EPSG:29180</SRS>' +
+' <SRS>EPSG:29181</SRS>' +
+' <SRS>EPSG:29182</SRS>' +
+' <SRS>EPSG:29183</SRS>' +
+' <SRS>EPSG:29184</SRS>' +
+' <SRS>EPSG:29185</SRS>' +
+' <SRS>EPSG:29187</SRS>' +
+' <SRS>EPSG:29188</SRS>' +
+' <SRS>EPSG:29189</SRS>' +
+' <SRS>EPSG:29190</SRS>' +
+' <SRS>EPSG:29191</SRS>' +
+' <SRS>EPSG:29192</SRS>' +
+' <SRS>EPSG:29193</SRS>' +
+' <SRS>EPSG:29194</SRS>' +
+' <SRS>EPSG:29195</SRS>' +
+' <SRS>EPSG:29220</SRS>' +
+' <SRS>EPSG:29221</SRS>' +
+' <SRS>EPSG:29333</SRS>' +
+' <SRS>EPSG:29371</SRS>' +
+' <SRS>EPSG:29373</SRS>' +
+' <SRS>EPSG:29375</SRS>' +
+' <SRS>EPSG:29377</SRS>' +
+' <SRS>EPSG:29379</SRS>' +
+' <SRS>EPSG:29381</SRS>' +
+' <SRS>EPSG:29383</SRS>' +
+' <SRS>EPSG:29385</SRS>' +
+' <SRS>EPSG:29635</SRS>' +
+' <SRS>EPSG:29636</SRS>' +
+' <SRS>EPSG:29700</SRS>' +
+' <SRS>EPSG:29701</SRS>' +
+' <SRS>EPSG:29702</SRS>' +
+' <SRS>EPSG:29738</SRS>' +
+' <SRS>EPSG:29739</SRS>' +
+' <SRS>EPSG:29849</SRS>' +
+' <SRS>EPSG:29850</SRS>' +
+' <SRS>EPSG:29871</SRS>' +
+' <SRS>EPSG:29872</SRS>' +
+' <SRS>EPSG:29873</SRS>' +
+' <SRS>EPSG:29900</SRS>' +
+' <SRS>EPSG:29901</SRS>' +
+' <SRS>EPSG:29902</SRS>' +
+' <SRS>EPSG:29903</SRS>' +
+' <SRS>EPSG:30161</SRS>' +
+' <SRS>EPSG:30162</SRS>' +
+' <SRS>EPSG:30163</SRS>' +
+' <SRS>EPSG:30164</SRS>' +
+' <SRS>EPSG:30165</SRS>' +
+' <SRS>EPSG:30166</SRS>' +
+' <SRS>EPSG:30167</SRS>' +
+' <SRS>EPSG:30168</SRS>' +
+' <SRS>EPSG:30169</SRS>' +
+' <SRS>EPSG:30170</SRS>' +
+' <SRS>EPSG:30171</SRS>' +
+' <SRS>EPSG:30172</SRS>' +
+' <SRS>EPSG:30173</SRS>' +
+' <SRS>EPSG:30174</SRS>' +
+' <SRS>EPSG:30175</SRS>' +
+' <SRS>EPSG:30176</SRS>' +
+' <SRS>EPSG:30177</SRS>' +
+' <SRS>EPSG:30178</SRS>' +
+' <SRS>EPSG:30179</SRS>' +
+' <SRS>EPSG:30200</SRS>' +
+' <SRS>EPSG:30339</SRS>' +
+' <SRS>EPSG:30340</SRS>' +
+' <SRS>EPSG:30491</SRS>' +
+' <SRS>EPSG:30492</SRS>' +
+' <SRS>EPSG:30493</SRS>' +
+' <SRS>EPSG:30494</SRS>' +
+' <SRS>EPSG:30729</SRS>' +
+' <SRS>EPSG:30730</SRS>' +
+' <SRS>EPSG:30731</SRS>' +
+' <SRS>EPSG:30732</SRS>' +
+' <SRS>EPSG:30791</SRS>' +
+' <SRS>EPSG:30792</SRS>' +
+' <SRS>EPSG:30800</SRS>' +
+' <SRS>EPSG:31028</SRS>' +
+' <SRS>EPSG:31121</SRS>' +
+' <SRS>EPSG:31154</SRS>' +
+' <SRS>EPSG:31170</SRS>' +
+' <SRS>EPSG:31171</SRS>' +
+' <SRS>EPSG:31251</SRS>' +
+' <SRS>EPSG:31252</SRS>' +
+' <SRS>EPSG:31253</SRS>' +
+' <SRS>EPSG:31254</SRS>' +
+' <SRS>EPSG:31255</SRS>' +
+' <SRS>EPSG:31256</SRS>' +
+' <SRS>EPSG:31257</SRS>' +
+' <SRS>EPSG:31258</SRS>' +
+' <SRS>EPSG:31259</SRS>' +
+' <SRS>EPSG:31265</SRS>' +
+' <SRS>EPSG:31266</SRS>' +
+' <SRS>EPSG:31267</SRS>' +
+' <SRS>EPSG:31268</SRS>' +
+' <SRS>EPSG:31275</SRS>' +
+' <SRS>EPSG:31276</SRS>' +
+' <SRS>EPSG:31277</SRS>' +
+' <SRS>EPSG:31278</SRS>' +
+' <SRS>EPSG:31279</SRS>' +
+' <SRS>EPSG:31281</SRS>' +
+' <SRS>EPSG:31282</SRS>' +
+' <SRS>EPSG:31283</SRS>' +
+' <SRS>EPSG:31284</SRS>' +
+' <SRS>EPSG:31285</SRS>' +
+' <SRS>EPSG:31286</SRS>' +
+' <SRS>EPSG:31287</SRS>' +
+' <SRS>EPSG:31288</SRS>' +
+' <SRS>EPSG:31289</SRS>' +
+' <SRS>EPSG:31290</SRS>' +
+' <SRS>EPSG:31291</SRS>' +
+' <SRS>EPSG:31292</SRS>' +
+' <SRS>EPSG:31293</SRS>' +
+' <SRS>EPSG:31294</SRS>' +
+' <SRS>EPSG:31295</SRS>' +
+' <SRS>EPSG:31296</SRS>' +
+' <SRS>EPSG:31297</SRS>' +
+' <SRS>EPSG:31300</SRS>' +
+' <SRS>EPSG:31370</SRS>' +
+' <SRS>EPSG:31461</SRS>' +
+' <SRS>EPSG:31462</SRS>' +
+' <SRS>EPSG:31463</SRS>' +
+' <SRS>EPSG:31464</SRS>' +
+' <SRS>EPSG:31465</SRS>' +
+' <SRS>EPSG:31466</SRS>' +
+' <SRS>EPSG:31467</SRS>' +
+' <SRS>EPSG:31468</SRS>' +
+' <SRS>EPSG:31469</SRS>' +
+' <SRS>EPSG:31528</SRS>' +
+' <SRS>EPSG:31529</SRS>' +
+' <SRS>EPSG:31600</SRS>' +
+' <SRS>EPSG:31700</SRS>' +
+' <SRS>EPSG:31838</SRS>' +
+' <SRS>EPSG:31839</SRS>' +
+' <SRS>EPSG:31900</SRS>' +
+' <SRS>EPSG:31901</SRS>' +
+' <SRS>EPSG:31965</SRS>' +
+' <SRS>EPSG:31966</SRS>' +
+' <SRS>EPSG:31967</SRS>' +
+' <SRS>EPSG:31968</SRS>' +
+' <SRS>EPSG:31969</SRS>' +
+' <SRS>EPSG:31970</SRS>' +
+' <SRS>EPSG:31971</SRS>' +
+' <SRS>EPSG:31972</SRS>' +
+' <SRS>EPSG:31973</SRS>' +
+' <SRS>EPSG:31974</SRS>' +
+' <SRS>EPSG:31975</SRS>' +
+' <SRS>EPSG:31976</SRS>' +
+' <SRS>EPSG:31977</SRS>' +
+' <SRS>EPSG:31978</SRS>' +
+' <SRS>EPSG:31979</SRS>' +
+' <SRS>EPSG:31980</SRS>' +
+' <SRS>EPSG:31981</SRS>' +
+' <SRS>EPSG:31982</SRS>' +
+' <SRS>EPSG:31983</SRS>' +
+' <SRS>EPSG:31984</SRS>' +
+' <SRS>EPSG:31985</SRS>' +
+' <SRS>EPSG:31986</SRS>' +
+' <SRS>EPSG:31987</SRS>' +
+' <SRS>EPSG:31988</SRS>' +
+' <SRS>EPSG:31989</SRS>' +
+' <SRS>EPSG:31990</SRS>' +
+' <SRS>EPSG:31991</SRS>' +
+' <SRS>EPSG:31992</SRS>' +
+' <SRS>EPSG:31993</SRS>' +
+' <SRS>EPSG:31994</SRS>' +
+' <SRS>EPSG:31995</SRS>' +
+' <SRS>EPSG:31996</SRS>' +
+' <SRS>EPSG:31997</SRS>' +
+' <SRS>EPSG:31998</SRS>' +
+' <SRS>EPSG:31999</SRS>' +
+' <SRS>EPSG:32000</SRS>' +
+' <SRS>EPSG:32001</SRS>' +
+' <SRS>EPSG:32002</SRS>' +
+' <SRS>EPSG:32003</SRS>' +
+' <SRS>EPSG:32005</SRS>' +
+' <SRS>EPSG:32006</SRS>' +
+' <SRS>EPSG:32007</SRS>' +
+' <SRS>EPSG:32008</SRS>' +
+' <SRS>EPSG:32009</SRS>' +
+' <SRS>EPSG:32010</SRS>' +
+' <SRS>EPSG:32011</SRS>' +
+' <SRS>EPSG:32012</SRS>' +
+' <SRS>EPSG:32013</SRS>' +
+' <SRS>EPSG:32014</SRS>' +
+' <SRS>EPSG:32015</SRS>' +
+' <SRS>EPSG:32016</SRS>' +
+' <SRS>EPSG:32017</SRS>' +
+' <SRS>EPSG:32018</SRS>' +
+' <SRS>EPSG:32019</SRS>' +
+' <SRS>EPSG:32020</SRS>' +
+' <SRS>EPSG:32021</SRS>' +
+' <SRS>EPSG:32022</SRS>' +
+' <SRS>EPSG:32023</SRS>' +
+' <SRS>EPSG:32024</SRS>' +
+' <SRS>EPSG:32025</SRS>' +
+' <SRS>EPSG:32026</SRS>' +
+' <SRS>EPSG:32027</SRS>' +
+' <SRS>EPSG:32028</SRS>' +
+' <SRS>EPSG:32029</SRS>' +
+' <SRS>EPSG:32030</SRS>' +
+' <SRS>EPSG:32031</SRS>' +
+' <SRS>EPSG:32033</SRS>' +
+' <SRS>EPSG:32034</SRS>' +
+' <SRS>EPSG:32035</SRS>' +
+' <SRS>EPSG:32036</SRS>' +
+' <SRS>EPSG:32037</SRS>' +
+' <SRS>EPSG:32038</SRS>' +
+' <SRS>EPSG:32039</SRS>' +
+' <SRS>EPSG:32040</SRS>' +
+' <SRS>EPSG:32041</SRS>' +
+' <SRS>EPSG:32042</SRS>' +
+' <SRS>EPSG:32043</SRS>' +
+' <SRS>EPSG:32044</SRS>' +
+' <SRS>EPSG:32045</SRS>' +
+' <SRS>EPSG:32046</SRS>' +
+' <SRS>EPSG:32047</SRS>' +
+' <SRS>EPSG:32048</SRS>' +
+' <SRS>EPSG:32049</SRS>' +
+' <SRS>EPSG:32050</SRS>' +
+' <SRS>EPSG:32051</SRS>' +
+' <SRS>EPSG:32052</SRS>' +
+' <SRS>EPSG:32053</SRS>' +
+' <SRS>EPSG:32054</SRS>' +
+' <SRS>EPSG:32055</SRS>' +
+' <SRS>EPSG:32056</SRS>' +
+' <SRS>EPSG:32057</SRS>' +
+' <SRS>EPSG:32058</SRS>' +
+' <SRS>EPSG:32061</SRS>' +
+' <SRS>EPSG:32062</SRS>' +
+' <SRS>EPSG:32064</SRS>' +
+' <SRS>EPSG:32065</SRS>' +
+' <SRS>EPSG:32066</SRS>' +
+' <SRS>EPSG:32067</SRS>' +
+' <SRS>EPSG:32074</SRS>' +
+' <SRS>EPSG:32075</SRS>' +
+' <SRS>EPSG:32076</SRS>' +
+' <SRS>EPSG:32077</SRS>' +
+' <SRS>EPSG:32081</SRS>' +
+' <SRS>EPSG:32082</SRS>' +
+' <SRS>EPSG:32083</SRS>' +
+' <SRS>EPSG:32084</SRS>' +
+' <SRS>EPSG:32085</SRS>' +
+' <SRS>EPSG:32086</SRS>' +
+' <SRS>EPSG:32098</SRS>' +
+' <SRS>EPSG:32099</SRS>' +
+' <SRS>EPSG:32100</SRS>' +
+' <SRS>EPSG:32104</SRS>' +
+' <SRS>EPSG:32107</SRS>' +
+' <SRS>EPSG:32108</SRS>' +
+' <SRS>EPSG:32109</SRS>' +
+' <SRS>EPSG:32110</SRS>' +
+' <SRS>EPSG:32111</SRS>' +
+' <SRS>EPSG:32112</SRS>' +
+' <SRS>EPSG:32113</SRS>' +
+' <SRS>EPSG:32114</SRS>' +
+' <SRS>EPSG:32115</SRS>' +
+' <SRS>EPSG:32116</SRS>' +
+' <SRS>EPSG:32117</SRS>' +
+' <SRS>EPSG:32118</SRS>' +
+' <SRS>EPSG:32119</SRS>' +
+' <SRS>EPSG:32120</SRS>' +
+' <SRS>EPSG:32121</SRS>' +
+' <SRS>EPSG:32122</SRS>' +
+' <SRS>EPSG:32123</SRS>' +
+' <SRS>EPSG:32124</SRS>' +
+' <SRS>EPSG:32125</SRS>' +
+' <SRS>EPSG:32126</SRS>' +
+' <SRS>EPSG:32127</SRS>' +
+' <SRS>EPSG:32128</SRS>' +
+' <SRS>EPSG:32129</SRS>' +
+' <SRS>EPSG:32130</SRS>' +
+' <SRS>EPSG:32133</SRS>' +
+' <SRS>EPSG:32134</SRS>' +
+' <SRS>EPSG:32135</SRS>' +
+' <SRS>EPSG:32136</SRS>' +
+' <SRS>EPSG:32137</SRS>' +
+' <SRS>EPSG:32138</SRS>' +
+' <SRS>EPSG:32139</SRS>' +
+' <SRS>EPSG:32140</SRS>' +
+' <SRS>EPSG:32141</SRS>' +
+' <SRS>EPSG:32142</SRS>' +
+' <SRS>EPSG:32143</SRS>' +
+' <SRS>EPSG:32144</SRS>' +
+' <SRS>EPSG:32145</SRS>' +
+' <SRS>EPSG:32146</SRS>' +
+' <SRS>EPSG:32147</SRS>' +
+' <SRS>EPSG:32148</SRS>' +
+' <SRS>EPSG:32149</SRS>' +
+' <SRS>EPSG:32150</SRS>' +
+' <SRS>EPSG:32151</SRS>' +
+' <SRS>EPSG:32152</SRS>' +
+' <SRS>EPSG:32153</SRS>' +
+' <SRS>EPSG:32154</SRS>' +
+' <SRS>EPSG:32155</SRS>' +
+' <SRS>EPSG:32156</SRS>' +
+' <SRS>EPSG:32157</SRS>' +
+' <SRS>EPSG:32158</SRS>' +
+' <SRS>EPSG:32161</SRS>' +
+' <SRS>EPSG:32164</SRS>' +
+' <SRS>EPSG:32165</SRS>' +
+' <SRS>EPSG:32166</SRS>' +
+' <SRS>EPSG:32167</SRS>' +
+' <SRS>EPSG:32180</SRS>' +
+' <SRS>EPSG:32181</SRS>' +
+' <SRS>EPSG:32182</SRS>' +
+' <SRS>EPSG:32183</SRS>' +
+' <SRS>EPSG:32184</SRS>' +
+' <SRS>EPSG:32185</SRS>' +
+' <SRS>EPSG:32186</SRS>' +
+' <SRS>EPSG:32187</SRS>' +
+' <SRS>EPSG:32188</SRS>' +
+' <SRS>EPSG:32189</SRS>' +
+' <SRS>EPSG:32190</SRS>' +
+' <SRS>EPSG:32191</SRS>' +
+' <SRS>EPSG:32192</SRS>' +
+' <SRS>EPSG:32193</SRS>' +
+' <SRS>EPSG:32194</SRS>' +
+' <SRS>EPSG:32195</SRS>' +
+' <SRS>EPSG:32196</SRS>' +
+' <SRS>EPSG:32197</SRS>' +
+' <SRS>EPSG:32198</SRS>' +
+' <SRS>EPSG:32199</SRS>' +
+' <SRS>EPSG:32201</SRS>' +
+' <SRS>EPSG:32202</SRS>' +
+' <SRS>EPSG:32203</SRS>' +
+' <SRS>EPSG:32204</SRS>' +
+' <SRS>EPSG:32205</SRS>' +
+' <SRS>EPSG:32206</SRS>' +
+' <SRS>EPSG:32207</SRS>' +
+' <SRS>EPSG:32208</SRS>' +
+' <SRS>EPSG:32209</SRS>' +
+' <SRS>EPSG:32210</SRS>' +
+' <SRS>EPSG:32211</SRS>' +
+' <SRS>EPSG:32212</SRS>' +
+' <SRS>EPSG:32213</SRS>' +
+' <SRS>EPSG:32214</SRS>' +
+' <SRS>EPSG:32215</SRS>' +
+' <SRS>EPSG:32216</SRS>' +
+' <SRS>EPSG:32217</SRS>' +
+' <SRS>EPSG:32218</SRS>' +
+' <SRS>EPSG:32219</SRS>' +
+' <SRS>EPSG:32220</SRS>' +
+' <SRS>EPSG:32221</SRS>' +
+' <SRS>EPSG:32222</SRS>' +
+' <SRS>EPSG:32223</SRS>' +
+' <SRS>EPSG:32224</SRS>' +
+' <SRS>EPSG:32225</SRS>' +
+' <SRS>EPSG:32226</SRS>' +
+' <SRS>EPSG:32227</SRS>' +
+' <SRS>EPSG:32228</SRS>' +
+' <SRS>EPSG:32229</SRS>' +
+' <SRS>EPSG:32230</SRS>' +
+' <SRS>EPSG:32231</SRS>' +
+' <SRS>EPSG:32232</SRS>' +
+' <SRS>EPSG:32233</SRS>' +
+' <SRS>EPSG:32234</SRS>' +
+' <SRS>EPSG:32235</SRS>' +
+' <SRS>EPSG:32236</SRS>' +
+' <SRS>EPSG:32237</SRS>' +
+' <SRS>EPSG:32238</SRS>' +
+' <SRS>EPSG:32239</SRS>' +
+' <SRS>EPSG:32240</SRS>' +
+' <SRS>EPSG:32241</SRS>' +
+' <SRS>EPSG:32242</SRS>' +
+' <SRS>EPSG:32243</SRS>' +
+' <SRS>EPSG:32244</SRS>' +
+' <SRS>EPSG:32245</SRS>' +
+' <SRS>EPSG:32246</SRS>' +
+' <SRS>EPSG:32247</SRS>' +
+' <SRS>EPSG:32248</SRS>' +
+' <SRS>EPSG:32249</SRS>' +
+' <SRS>EPSG:32250</SRS>' +
+' <SRS>EPSG:32251</SRS>' +
+' <SRS>EPSG:32252</SRS>' +
+' <SRS>EPSG:32253</SRS>' +
+' <SRS>EPSG:32254</SRS>' +
+' <SRS>EPSG:32255</SRS>' +
+' <SRS>EPSG:32256</SRS>' +
+' <SRS>EPSG:32257</SRS>' +
+' <SRS>EPSG:32258</SRS>' +
+' <SRS>EPSG:32259</SRS>' +
+' <SRS>EPSG:32260</SRS>' +
+' <SRS>EPSG:32301</SRS>' +
+' <SRS>EPSG:32302</SRS>' +
+' <SRS>EPSG:32303</SRS>' +
+' <SRS>EPSG:32304</SRS>' +
+' <SRS>EPSG:32305</SRS>' +
+' <SRS>EPSG:32306</SRS>' +
+' <SRS>EPSG:32307</SRS>' +
+' <SRS>EPSG:32308</SRS>' +
+' <SRS>EPSG:32309</SRS>' +
+' <SRS>EPSG:32310</SRS>' +
+' <SRS>EPSG:32311</SRS>' +
+' <SRS>EPSG:32312</SRS>' +
+' <SRS>EPSG:32313</SRS>' +
+' <SRS>EPSG:32314</SRS>' +
+' <SRS>EPSG:32315</SRS>' +
+' <SRS>EPSG:32316</SRS>' +
+' <SRS>EPSG:32317</SRS>' +
+' <SRS>EPSG:32318</SRS>' +
+' <SRS>EPSG:32319</SRS>' +
+' <SRS>EPSG:32320</SRS>' +
+' <SRS>EPSG:32321</SRS>' +
+' <SRS>EPSG:32322</SRS>' +
+' <SRS>EPSG:32323</SRS>' +
+' <SRS>EPSG:32324</SRS>' +
+' <SRS>EPSG:32325</SRS>' +
+' <SRS>EPSG:32326</SRS>' +
+' <SRS>EPSG:32327</SRS>' +
+' <SRS>EPSG:32328</SRS>' +
+' <SRS>EPSG:32329</SRS>' +
+' <SRS>EPSG:32330</SRS>' +
+' <SRS>EPSG:32331</SRS>' +
+' <SRS>EPSG:32332</SRS>' +
+' <SRS>EPSG:32333</SRS>' +
+' <SRS>EPSG:32334</SRS>' +
+' <SRS>EPSG:32335</SRS>' +
+' <SRS>EPSG:32336</SRS>' +
+' <SRS>EPSG:32337</SRS>' +
+' <SRS>EPSG:32338</SRS>' +
+' <SRS>EPSG:32339</SRS>' +
+' <SRS>EPSG:32340</SRS>' +
+' <SRS>EPSG:32341</SRS>' +
+' <SRS>EPSG:32342</SRS>' +
+' <SRS>EPSG:32343</SRS>' +
+' <SRS>EPSG:32344</SRS>' +
+' <SRS>EPSG:32345</SRS>' +
+' <SRS>EPSG:32346</SRS>' +
+' <SRS>EPSG:32347</SRS>' +
+' <SRS>EPSG:32348</SRS>' +
+' <SRS>EPSG:32349</SRS>' +
+' <SRS>EPSG:32350</SRS>' +
+' <SRS>EPSG:32351</SRS>' +
+' <SRS>EPSG:32352</SRS>' +
+' <SRS>EPSG:32353</SRS>' +
+' <SRS>EPSG:32354</SRS>' +
+' <SRS>EPSG:32355</SRS>' +
+' <SRS>EPSG:32356</SRS>' +
+' <SRS>EPSG:32357</SRS>' +
+' <SRS>EPSG:32358</SRS>' +
+' <SRS>EPSG:32359</SRS>' +
+' <SRS>EPSG:32360</SRS>' +
+' <SRS>EPSG:32401</SRS>' +
+' <SRS>EPSG:32402</SRS>' +
+' <SRS>EPSG:32403</SRS>' +
+' <SRS>EPSG:32404</SRS>' +
+' <SRS>EPSG:32405</SRS>' +
+' <SRS>EPSG:32406</SRS>' +
+' <SRS>EPSG:32407</SRS>' +
+' <SRS>EPSG:32408</SRS>' +
+' <SRS>EPSG:32409</SRS>' +
+' <SRS>EPSG:32410</SRS>' +
+' <SRS>EPSG:32411</SRS>' +
+' <SRS>EPSG:32412</SRS>' +
+' <SRS>EPSG:32413</SRS>' +
+' <SRS>EPSG:32414</SRS>' +
+' <SRS>EPSG:32415</SRS>' +
+' <SRS>EPSG:32416</SRS>' +
+' <SRS>EPSG:32417</SRS>' +
+' <SRS>EPSG:32418</SRS>' +
+' <SRS>EPSG:32419</SRS>' +
+' <SRS>EPSG:32420</SRS>' +
+' <SRS>EPSG:32421</SRS>' +
+' <SRS>EPSG:32422</SRS>' +
+' <SRS>EPSG:32423</SRS>' +
+' <SRS>EPSG:32424</SRS>' +
+' <SRS>EPSG:32425</SRS>' +
+' <SRS>EPSG:32426</SRS>' +
+' <SRS>EPSG:32427</SRS>' +
+' <SRS>EPSG:32428</SRS>' +
+' <SRS>EPSG:32429</SRS>' +
+' <SRS>EPSG:32430</SRS>' +
+' <SRS>EPSG:32431</SRS>' +
+' <SRS>EPSG:32432</SRS>' +
+' <SRS>EPSG:32433</SRS>' +
+' <SRS>EPSG:32434</SRS>' +
+' <SRS>EPSG:32435</SRS>' +
+' <SRS>EPSG:32436</SRS>' +
+' <SRS>EPSG:32437</SRS>' +
+' <SRS>EPSG:32438</SRS>' +
+' <SRS>EPSG:32439</SRS>' +
+' <SRS>EPSG:32440</SRS>' +
+' <SRS>EPSG:32441</SRS>' +
+' <SRS>EPSG:32442</SRS>' +
+' <SRS>EPSG:32443</SRS>' +
+' <SRS>EPSG:32444</SRS>' +
+' <SRS>EPSG:32445</SRS>' +
+' <SRS>EPSG:32446</SRS>' +
+' <SRS>EPSG:32447</SRS>' +
+' <SRS>EPSG:32448</SRS>' +
+' <SRS>EPSG:32449</SRS>' +
+' <SRS>EPSG:32450</SRS>' +
+' <SRS>EPSG:32451</SRS>' +
+' <SRS>EPSG:32452</SRS>' +
+' <SRS>EPSG:32453</SRS>' +
+' <SRS>EPSG:32454</SRS>' +
+' <SRS>EPSG:32455</SRS>' +
+' <SRS>EPSG:32456</SRS>' +
+' <SRS>EPSG:32457</SRS>' +
+' <SRS>EPSG:32458</SRS>' +
+' <SRS>EPSG:32459</SRS>' +
+' <SRS>EPSG:32460</SRS>' +
+' <SRS>EPSG:32501</SRS>' +
+' <SRS>EPSG:32502</SRS>' +
+' <SRS>EPSG:32503</SRS>' +
+' <SRS>EPSG:32504</SRS>' +
+' <SRS>EPSG:32505</SRS>' +
+' <SRS>EPSG:32506</SRS>' +
+' <SRS>EPSG:32507</SRS>' +
+' <SRS>EPSG:32508</SRS>' +
+' <SRS>EPSG:32509</SRS>' +
+' <SRS>EPSG:32510</SRS>' +
+' <SRS>EPSG:32511</SRS>' +
+' <SRS>EPSG:32512</SRS>' +
+' <SRS>EPSG:32513</SRS>' +
+' <SRS>EPSG:32514</SRS>' +
+' <SRS>EPSG:32515</SRS>' +
+' <SRS>EPSG:32516</SRS>' +
+' <SRS>EPSG:32517</SRS>' +
+' <SRS>EPSG:32518</SRS>' +
+' <SRS>EPSG:32519</SRS>' +
+' <SRS>EPSG:32520</SRS>' +
+' <SRS>EPSG:32521</SRS>' +
+' <SRS>EPSG:32522</SRS>' +
+' <SRS>EPSG:32523</SRS>' +
+' <SRS>EPSG:32524</SRS>' +
+' <SRS>EPSG:32525</SRS>' +
+' <SRS>EPSG:32526</SRS>' +
+' <SRS>EPSG:32527</SRS>' +
+' <SRS>EPSG:32528</SRS>' +
+' <SRS>EPSG:32529</SRS>' +
+' <SRS>EPSG:32530</SRS>' +
+' <SRS>EPSG:32531</SRS>' +
+' <SRS>EPSG:32532</SRS>' +
+' <SRS>EPSG:32533</SRS>' +
+' <SRS>EPSG:32534</SRS>' +
+' <SRS>EPSG:32535</SRS>' +
+' <SRS>EPSG:32536</SRS>' +
+' <SRS>EPSG:32537</SRS>' +
+' <SRS>EPSG:32538</SRS>' +
+' <SRS>EPSG:32539</SRS>' +
+' <SRS>EPSG:32540</SRS>' +
+' <SRS>EPSG:32541</SRS>' +
+' <SRS>EPSG:32542</SRS>' +
+' <SRS>EPSG:32543</SRS>' +
+' <SRS>EPSG:32544</SRS>' +
+' <SRS>EPSG:32545</SRS>' +
+' <SRS>EPSG:32546</SRS>' +
+' <SRS>EPSG:32547</SRS>' +
+' <SRS>EPSG:32548</SRS>' +
+' <SRS>EPSG:32549</SRS>' +
+' <SRS>EPSG:32550</SRS>' +
+' <SRS>EPSG:32551</SRS>' +
+' <SRS>EPSG:32552</SRS>' +
+' <SRS>EPSG:32553</SRS>' +
+' <SRS>EPSG:32554</SRS>' +
+' <SRS>EPSG:32555</SRS>' +
+' <SRS>EPSG:32556</SRS>' +
+' <SRS>EPSG:32557</SRS>' +
+' <SRS>EPSG:32558</SRS>' +
+' <SRS>EPSG:32559</SRS>' +
+' <SRS>EPSG:32560</SRS>' +
+' <SRS>EPSG:32600</SRS>' +
+' <SRS>EPSG:32601</SRS>' +
+' <SRS>EPSG:32602</SRS>' +
+' <SRS>EPSG:32603</SRS>' +
+' <SRS>EPSG:32604</SRS>' +
+' <SRS>EPSG:32605</SRS>' +
+' <SRS>EPSG:32606</SRS>' +
+' <SRS>EPSG:32607</SRS>' +
+' <SRS>EPSG:32608</SRS>' +
+' <SRS>EPSG:32609</SRS>' +
+' <SRS>EPSG:32610</SRS>' +
+' <SRS>EPSG:32611</SRS>' +
+' <SRS>EPSG:32612</SRS>' +
+' <SRS>EPSG:32613</SRS>' +
+' <SRS>EPSG:32614</SRS>' +
+' <SRS>EPSG:32615</SRS>' +
+' <SRS>EPSG:32616</SRS>' +
+' <SRS>EPSG:32617</SRS>' +
+' <SRS>EPSG:32618</SRS>' +
+' <SRS>EPSG:32619</SRS>' +
+' <SRS>EPSG:32620</SRS>' +
+' <SRS>EPSG:32621</SRS>' +
+' <SRS>EPSG:32622</SRS>' +
+' <SRS>EPSG:32623</SRS>' +
+' <SRS>EPSG:32624</SRS>' +
+' <SRS>EPSG:32625</SRS>' +
+' <SRS>EPSG:32626</SRS>' +
+' <SRS>EPSG:32627</SRS>' +
+' <SRS>EPSG:32628</SRS>' +
+' <SRS>EPSG:32629</SRS>' +
+' <SRS>EPSG:32630</SRS>' +
+' <SRS>EPSG:32631</SRS>' +
+' <SRS>EPSG:32632</SRS>' +
+' <SRS>EPSG:32633</SRS>' +
+' <SRS>EPSG:32634</SRS>' +
+' <SRS>EPSG:32635</SRS>' +
+' <SRS>EPSG:32636</SRS>' +
+' <SRS>EPSG:32637</SRS>' +
+' <SRS>EPSG:32638</SRS>' +
+' <SRS>EPSG:32639</SRS>' +
+' <SRS>EPSG:32640</SRS>' +
+' <SRS>EPSG:32641</SRS>' +
+' <SRS>EPSG:32642</SRS>' +
+' <SRS>EPSG:32643</SRS>' +
+' <SRS>EPSG:32644</SRS>' +
+' <SRS>EPSG:32645</SRS>' +
+' <SRS>EPSG:32646</SRS>' +
+' <SRS>EPSG:32647</SRS>' +
+' <SRS>EPSG:32648</SRS>' +
+' <SRS>EPSG:32649</SRS>' +
+' <SRS>EPSG:32650</SRS>' +
+' <SRS>EPSG:32651</SRS>' +
+' <SRS>EPSG:32652</SRS>' +
+' <SRS>EPSG:32653</SRS>' +
+' <SRS>EPSG:32654</SRS>' +
+' <SRS>EPSG:32655</SRS>' +
+' <SRS>EPSG:32656</SRS>' +
+' <SRS>EPSG:32657</SRS>' +
+' <SRS>EPSG:32658</SRS>' +
+' <SRS>EPSG:32659</SRS>' +
+' <SRS>EPSG:32660</SRS>' +
+' <SRS>EPSG:32661</SRS>' +
+' <SRS>EPSG:32662</SRS>' +
+' <SRS>EPSG:32664</SRS>' +
+' <SRS>EPSG:32665</SRS>' +
+' <SRS>EPSG:32666</SRS>' +
+' <SRS>EPSG:32667</SRS>' +
+' <SRS>EPSG:32700</SRS>' +
+' <SRS>EPSG:32701</SRS>' +
+' <SRS>EPSG:32702</SRS>' +
+' <SRS>EPSG:32703</SRS>' +
+' <SRS>EPSG:32704</SRS>' +
+' <SRS>EPSG:32705</SRS>' +
+' <SRS>EPSG:32706</SRS>' +
+' <SRS>EPSG:32707</SRS>' +
+' <SRS>EPSG:32708</SRS>' +
+' <SRS>EPSG:32709</SRS>' +
+' <SRS>EPSG:32710</SRS>' +
+' <SRS>EPSG:32711</SRS>' +
+' <SRS>EPSG:32712</SRS>' +
+' <SRS>EPSG:32713</SRS>' +
+' <SRS>EPSG:32714</SRS>' +
+' <SRS>EPSG:32715</SRS>' +
+' <SRS>EPSG:32716</SRS>' +
+' <SRS>EPSG:32717</SRS>' +
+' <SRS>EPSG:32718</SRS>' +
+' <SRS>EPSG:32719</SRS>' +
+' <SRS>EPSG:32720</SRS>' +
+' <SRS>EPSG:32721</SRS>' +
+' <SRS>EPSG:32722</SRS>' +
+' <SRS>EPSG:32723</SRS>' +
+' <SRS>EPSG:32724</SRS>' +
+' <SRS>EPSG:32725</SRS>' +
+' <SRS>EPSG:32726</SRS>' +
+' <SRS>EPSG:32727</SRS>' +
+' <SRS>EPSG:32728</SRS>' +
+' <SRS>EPSG:32729</SRS>' +
+' <SRS>EPSG:32730</SRS>' +
+' <SRS>EPSG:32731</SRS>' +
+' <SRS>EPSG:32732</SRS>' +
+' <SRS>EPSG:32733</SRS>' +
+' <SRS>EPSG:32734</SRS>' +
+' <SRS>EPSG:32735</SRS>' +
+' <SRS>EPSG:32736</SRS>' +
+' <SRS>EPSG:32737</SRS>' +
+' <SRS>EPSG:32738</SRS>' +
+' <SRS>EPSG:32739</SRS>' +
+' <SRS>EPSG:32740</SRS>' +
+' <SRS>EPSG:32741</SRS>' +
+' <SRS>EPSG:32742</SRS>' +
+' <SRS>EPSG:32743</SRS>' +
+' <SRS>EPSG:32744</SRS>' +
+' <SRS>EPSG:32745</SRS>' +
+' <SRS>EPSG:32746</SRS>' +
+' <SRS>EPSG:32747</SRS>' +
+' <SRS>EPSG:32748</SRS>' +
+' <SRS>EPSG:32749</SRS>' +
+' <SRS>EPSG:32750</SRS>' +
+' <SRS>EPSG:32751</SRS>' +
+' <SRS>EPSG:32752</SRS>' +
+' <SRS>EPSG:32753</SRS>' +
+' <SRS>EPSG:32754</SRS>' +
+' <SRS>EPSG:32755</SRS>' +
+' <SRS>EPSG:32756</SRS>' +
+' <SRS>EPSG:32757</SRS>' +
+' <SRS>EPSG:32758</SRS>' +
+' <SRS>EPSG:32759</SRS>' +
+' <SRS>EPSG:32760</SRS>' +
+' <SRS>EPSG:32761</SRS>' +
+' <SRS>EPSG:32766</SRS>' +
+' <SRS>EPSG:61206405</SRS>' +
+' <SRS>EPSG:61216405</SRS>' +
+' <SRS>EPSG:61226405</SRS>' +
+' <SRS>EPSG:61236405</SRS>' +
+' <SRS>EPSG:61246405</SRS>' +
+' <SRS>EPSG:61266405</SRS>' +
+' <SRS>EPSG:61266413</SRS>' +
+' <SRS>EPSG:61276405</SRS>' +
+' <SRS>EPSG:61286405</SRS>' +
+' <SRS>EPSG:61296405</SRS>' +
+' <SRS>EPSG:61306405</SRS>' +
+' <SRS>EPSG:61306413</SRS>' +
+' <SRS>EPSG:61316405</SRS>' +
+' <SRS>EPSG:61326405</SRS>' +
+' <SRS>EPSG:61336405</SRS>' +
+' <SRS>EPSG:61346405</SRS>' +
+' <SRS>EPSG:61356405</SRS>' +
+' <SRS>EPSG:61366405</SRS>' +
+' <SRS>EPSG:61376405</SRS>' +
+' <SRS>EPSG:61386405</SRS>' +
+' <SRS>EPSG:61396405</SRS>' +
+' <SRS>EPSG:61406405</SRS>' +
+' <SRS>EPSG:61406413</SRS>' +
+' <SRS>EPSG:61416405</SRS>' +
+' <SRS>EPSG:61426405</SRS>' +
+' <SRS>EPSG:61436405</SRS>' +
+' <SRS>EPSG:61446405</SRS>' +
+' <SRS>EPSG:61456405</SRS>' +
+' <SRS>EPSG:61466405</SRS>' +
+' <SRS>EPSG:61476405</SRS>' +
+' <SRS>EPSG:61486405</SRS>' +
+' <SRS>EPSG:61486413</SRS>' +
+' <SRS>EPSG:61496405</SRS>' +
+' <SRS>EPSG:61506405</SRS>' +
+' <SRS>EPSG:61516405</SRS>' +
+' <SRS>EPSG:61516413</SRS>' +
+' <SRS>EPSG:61526405</SRS>' +
+' <SRS>EPSG:61526413</SRS>' +
+' <SRS>EPSG:61536405</SRS>' +
+' <SRS>EPSG:61546405</SRS>' +
+' <SRS>EPSG:61556405</SRS>' +
+' <SRS>EPSG:61566405</SRS>' +
+' <SRS>EPSG:61576405</SRS>' +
+' <SRS>EPSG:61586405</SRS>' +
+' <SRS>EPSG:61596405</SRS>' +
+' <SRS>EPSG:61606405</SRS>' +
+' <SRS>EPSG:61616405</SRS>' +
+' <SRS>EPSG:61626405</SRS>' +
+' <SRS>EPSG:61636405</SRS>' +
+' <SRS>EPSG:61636413</SRS>' +
+' <SRS>EPSG:61646405</SRS>' +
+' <SRS>EPSG:61656405</SRS>' +
+' <SRS>EPSG:61666405</SRS>' +
+' <SRS>EPSG:61676405</SRS>' +
+' <SRS>EPSG:61676413</SRS>' +
+' <SRS>EPSG:61686405</SRS>' +
+' <SRS>EPSG:61696405</SRS>' +
+' <SRS>EPSG:61706405</SRS>' +
+' <SRS>EPSG:61706413</SRS>' +
+' <SRS>EPSG:61716405</SRS>' +
+' <SRS>EPSG:61716413</SRS>' +
+' <SRS>EPSG:61736405</SRS>' +
+' <SRS>EPSG:61736413</SRS>' +
+' <SRS>EPSG:61746405</SRS>' +
+' <SRS>EPSG:61756405</SRS>' +
+' <SRS>EPSG:61766405</SRS>' +
+' <SRS>EPSG:61766413</SRS>' +
+' <SRS>EPSG:61786405</SRS>' +
+' <SRS>EPSG:61796405</SRS>' +
+' <SRS>EPSG:61806405</SRS>' +
+' <SRS>EPSG:61806413</SRS>' +
+' <SRS>EPSG:61816405</SRS>' +
+' <SRS>EPSG:61826405</SRS>' +
+' <SRS>EPSG:61836405</SRS>' +
+' <SRS>EPSG:61846405</SRS>' +
+' <SRS>EPSG:61886405</SRS>' +
+' <SRS>EPSG:61896405</SRS>' +
+' <SRS>EPSG:61896413</SRS>' +
+' <SRS>EPSG:61906405</SRS>' +
+' <SRS>EPSG:61906413</SRS>' +
+' <SRS>EPSG:61916405</SRS>' +
+' <SRS>EPSG:61926405</SRS>' +
+' <SRS>EPSG:61936405</SRS>' +
+' <SRS>EPSG:61946405</SRS>' +
+' <SRS>EPSG:61956405</SRS>' +
+' <SRS>EPSG:61966405</SRS>' +
+' <SRS>EPSG:61976405</SRS>' +
+' <SRS>EPSG:61986405</SRS>' +
+' <SRS>EPSG:61996405</SRS>' +
+' <SRS>EPSG:62006405</SRS>' +
+' <SRS>EPSG:62016405</SRS>' +
+' <SRS>EPSG:62026405</SRS>' +
+' <SRS>EPSG:62036405</SRS>' +
+' <SRS>EPSG:62046405</SRS>' +
+' <SRS>EPSG:62056405</SRS>' +
+' <SRS>EPSG:62066405</SRS>' +
+' <SRS>EPSG:62076405</SRS>' +
+' <SRS>EPSG:62086405</SRS>' +
+' <SRS>EPSG:62096405</SRS>' +
+' <SRS>EPSG:62106405</SRS>' +
+' <SRS>EPSG:62116405</SRS>' +
+' <SRS>EPSG:62126405</SRS>' +
+' <SRS>EPSG:62136405</SRS>' +
+' <SRS>EPSG:62146405</SRS>' +
+' <SRS>EPSG:62156405</SRS>' +
+' <SRS>EPSG:62166405</SRS>' +
+' <SRS>EPSG:62186405</SRS>' +
+' <SRS>EPSG:62196405</SRS>' +
+' <SRS>EPSG:62206405</SRS>' +
+' <SRS>EPSG:62216405</SRS>' +
+' <SRS>EPSG:62226405</SRS>' +
+' <SRS>EPSG:62236405</SRS>' +
+' <SRS>EPSG:62246405</SRS>' +
+' <SRS>EPSG:62256405</SRS>' +
+' <SRS>EPSG:62276405</SRS>' +
+' <SRS>EPSG:62296405</SRS>' +
+' <SRS>EPSG:62306405</SRS>' +
+' <SRS>EPSG:62316405</SRS>' +
+' <SRS>EPSG:62326405</SRS>' +
+' <SRS>EPSG:62336405</SRS>' +
+' <SRS>EPSG:62366405</SRS>' +
+' <SRS>EPSG:62376405</SRS>' +
+' <SRS>EPSG:62386405</SRS>' +
+' <SRS>EPSG:62396405</SRS>' +
+' <SRS>EPSG:62406405</SRS>' +
+' <SRS>EPSG:62416405</SRS>' +
+' <SRS>EPSG:62426405</SRS>' +
+' <SRS>EPSG:62436405</SRS>' +
+' <SRS>EPSG:62446405</SRS>' +
+' <SRS>EPSG:62456405</SRS>' +
+' <SRS>EPSG:62466405</SRS>' +
+' <SRS>EPSG:62476405</SRS>' +
+' <SRS>EPSG:62486405</SRS>' +
+' <SRS>EPSG:62496405</SRS>' +
+' <SRS>EPSG:62506405</SRS>' +
+' <SRS>EPSG:62516405</SRS>' +
+' <SRS>EPSG:62526405</SRS>' +
+' <SRS>EPSG:62536405</SRS>' +
+' <SRS>EPSG:62546405</SRS>' +
+' <SRS>EPSG:62556405</SRS>' +
+' <SRS>EPSG:62566405</SRS>' +
+' <SRS>EPSG:62576405</SRS>' +
+' <SRS>EPSG:62586405</SRS>' +
+' <SRS>EPSG:62586413</SRS>' +
+' <SRS>EPSG:62596405</SRS>' +
+' <SRS>EPSG:62616405</SRS>' +
+' <SRS>EPSG:62626405</SRS>' +
+' <SRS>EPSG:62636405</SRS>' +
+' <SRS>EPSG:62646405</SRS>' +
+' <SRS>EPSG:62656405</SRS>' +
+' <SRS>EPSG:62666405</SRS>' +
+' <SRS>EPSG:62676405</SRS>' +
+' <SRS>EPSG:62686405</SRS>' +
+' <SRS>EPSG:62696405</SRS>' +
+' <SRS>EPSG:62706405</SRS>' +
+' <SRS>EPSG:62716405</SRS>' +
+' <SRS>EPSG:62726405</SRS>' +
+' <SRS>EPSG:62736405</SRS>' +
+' <SRS>EPSG:62746405</SRS>' +
+' <SRS>EPSG:62756405</SRS>' +
+' <SRS>EPSG:62766405</SRS>' +
+' <SRS>EPSG:62776405</SRS>' +
+' <SRS>EPSG:62786405</SRS>' +
+' <SRS>EPSG:62796405</SRS>' +
+' <SRS>EPSG:62806405</SRS>' +
+' <SRS>EPSG:62816405</SRS>' +
+' <SRS>EPSG:62826405</SRS>' +
+' <SRS>EPSG:62836405</SRS>' +
+' <SRS>EPSG:62836413</SRS>' +
+' <SRS>EPSG:62846405</SRS>' +
+' <SRS>EPSG:62856405</SRS>' +
+' <SRS>EPSG:62866405</SRS>' +
+' <SRS>EPSG:62886405</SRS>' +
+' <SRS>EPSG:62896405</SRS>' +
+' <SRS>EPSG:62926405</SRS>' +
+' <SRS>EPSG:62936405</SRS>' +
+' <SRS>EPSG:62956405</SRS>' +
+' <SRS>EPSG:62976405</SRS>' +
+' <SRS>EPSG:62986405</SRS>' +
+' <SRS>EPSG:62996405</SRS>' +
+' <SRS>EPSG:63006405</SRS>' +
+' <SRS>EPSG:63016405</SRS>' +
+' <SRS>EPSG:63026405</SRS>' +
+' <SRS>EPSG:63036405</SRS>' +
+' <SRS>EPSG:63046405</SRS>' +
+' <SRS>EPSG:63066405</SRS>' +
+' <SRS>EPSG:63076405</SRS>' +
+' <SRS>EPSG:63086405</SRS>' +
+' <SRS>EPSG:63096405</SRS>' +
+' <SRS>EPSG:63106405</SRS>' +
+' <SRS>EPSG:63116405</SRS>' +
+' <SRS>EPSG:63126405</SRS>' +
+' <SRS>EPSG:63136405</SRS>' +
+' <SRS>EPSG:63146405</SRS>' +
+' <SRS>EPSG:63156405</SRS>' +
+' <SRS>EPSG:63166405</SRS>' +
+' <SRS>EPSG:63176405</SRS>' +
+' <SRS>EPSG:63186405</SRS>' +
+' <SRS>EPSG:63196405</SRS>' +
+' <SRS>EPSG:63226405</SRS>' +
+' <SRS>EPSG:63246405</SRS>' +
+' <SRS>EPSG:63266405</SRS>' +
+' <SRS>EPSG:63266406</SRS>' +
+' <SRS>EPSG:63266407</SRS>' +
+' <SRS>EPSG:63266408</SRS>' +
+' <SRS>EPSG:63266409</SRS>' +
+' <SRS>EPSG:63266410</SRS>' +
+' <SRS>EPSG:63266411</SRS>' +
+' <SRS>EPSG:63266412</SRS>' +
+' <SRS>EPSG:63266413</SRS>' +
+' <SRS>EPSG:63266414</SRS>' +
+' <SRS>EPSG:63266415</SRS>' +
+' <SRS>EPSG:63266416</SRS>' +
+' <SRS>EPSG:63266417</SRS>' +
+' <SRS>EPSG:63266418</SRS>' +
+' <SRS>EPSG:63266419</SRS>' +
+' <SRS>EPSG:63266420</SRS>' +
+' <SRS>EPSG:66006405</SRS>' +
+' <SRS>EPSG:66016405</SRS>' +
+' <SRS>EPSG:66026405</SRS>' +
+' <SRS>EPSG:66036405</SRS>' +
+' <SRS>EPSG:66046405</SRS>' +
+' <SRS>EPSG:66056405</SRS>' +
+' <SRS>EPSG:66066405</SRS>' +
+' <SRS>EPSG:66076405</SRS>' +
+' <SRS>EPSG:66086405</SRS>' +
+' <SRS>EPSG:66096405</SRS>' +
+' <SRS>EPSG:66106405</SRS>' +
+' <SRS>EPSG:66116405</SRS>' +
+' <SRS>EPSG:66126405</SRS>' +
+' <SRS>EPSG:66126413</SRS>' +
+' <SRS>EPSG:66136405</SRS>' +
+' <SRS>EPSG:66146405</SRS>' +
+' <SRS>EPSG:66156405</SRS>' +
+' <SRS>EPSG:66166405</SRS>' +
+' <SRS>EPSG:66186405</SRS>' +
+' <SRS>EPSG:66196405</SRS>' +
+' <SRS>EPSG:66196413</SRS>' +
+' <SRS>EPSG:66206405</SRS>' +
+' <SRS>EPSG:66216405</SRS>' +
+' <SRS>EPSG:66226405</SRS>' +
+' <SRS>EPSG:66236405</SRS>' +
+' <SRS>EPSG:66246405</SRS>' +
+' <SRS>EPSG:66246413</SRS>' +
+' <SRS>EPSG:66256405</SRS>' +
+' <SRS>EPSG:66266405</SRS>' +
+' <SRS>EPSG:66276405</SRS>' +
+' <SRS>EPSG:66276413</SRS>' +
+' <SRS>EPSG:66286405</SRS>' +
+' <SRS>EPSG:66296405</SRS>' +
+' <SRS>EPSG:66306405</SRS>' +
+' <SRS>EPSG:66316405</SRS>' +
+' <SRS>EPSG:66326405</SRS>' +
+' <SRS>EPSG:66336405</SRS>' +
+' <SRS>EPSG:66346405</SRS>' +
+' <SRS>EPSG:66356405</SRS>' +
+' <SRS>EPSG:66366405</SRS>' +
+' <SRS>EPSG:66376405</SRS>' +
+' <SRS>EPSG:66386405</SRS>' +
+' <SRS>EPSG:66396405</SRS>' +
+' <SRS>EPSG:66406405</SRS>' +
+' <SRS>EPSG:66406413</SRS>' +
+' <SRS>EPSG:66416405</SRS>' +
+' <SRS>EPSG:66426405</SRS>' +
+' <SRS>EPSG:66436405</SRS>' +
+' <SRS>EPSG:66446405</SRS>' +
+' <SRS>EPSG:66456405</SRS>' +
+' <SRS>EPSG:66456413</SRS>' +
+' <SRS>EPSG:66466405</SRS>' +
+' <SRS>EPSG:66576405</SRS>' +
+' <SRS>EPSG:66586405</SRS>' +
+' <SRS>EPSG:66596405</SRS>' +
+' <SRS>EPSG:66596413</SRS>' +
+' <SRS>EPSG:66606405</SRS>' +
+' <SRS>EPSG:66616405</SRS>' +
+' <SRS>EPSG:66616413</SRS>' +
+' <SRS>EPSG:66636405</SRS>' +
+' <SRS>EPSG:66646405</SRS>' +
+' <SRS>EPSG:66656405</SRS>' +
+' <SRS>EPSG:66666405</SRS>' +
+' <SRS>EPSG:66676405</SRS>' +
+' <SRS>EPSG:68016405</SRS>' +
+' <SRS>EPSG:68026405</SRS>' +
+' <SRS>EPSG:68036405</SRS>' +
+' <SRS>EPSG:68046405</SRS>' +
+' <SRS>EPSG:68056405</SRS>' +
+' <SRS>EPSG:68066405</SRS>' +
+' <SRS>EPSG:68086405</SRS>' +
+' <SRS>EPSG:68096405</SRS>' +
+' <SRS>EPSG:68136405</SRS>' +
+' <SRS>EPSG:68146405</SRS>' +
+' <SRS>EPSG:68156405</SRS>' +
+' <SRS>EPSG:68186405</SRS>' +
+' <SRS>EPSG:68206405</SRS>' +
+' <SRS>EPSG:69036405</SRS>' +
+' <SRS>EPSG:42302</SRS>' +
+' <SRS>EPSG:42301</SRS>' +
+' <SRS>EPSG:900913</SRS>' +
+' <SRS>EPSG:45556</SRS>' +
+' <SRS>EPSG:45555</SRS>' +
+' <SRS>EPSG:54004</SRS>' +
+' <SRS>EPSG:41001</SRS>' +
+' <SRS>EPSG:42311</SRS>' +
+' <SRS>EPSG:42310</SRS>' +
+' <SRS>EPSG:18001</SRS>' +
+' <SRS>EPSG:100003</SRS>' +
+' <SRS>EPSG:42106</SRS>' +
+' <SRS>EPSG:100002</SRS>' +
+' <SRS>EPSG:42105</SRS>' +
+' <SRS>EPSG:100001</SRS>' +
+' <SRS>EPSG:42309</SRS>' +
+' <SRS>EPSG:42104</SRS>' +
+' <SRS>EPSG:42308</SRS>' +
+' <SRS>EPSG:42103</SRS>' +
+' <SRS>EPSG:42307</SRS>' +
+' <SRS>EPSG:42102</SRS>' +
+' <SRS>EPSG:42306</SRS>' +
+' <SRS>EPSG:42101</SRS>' +
+' <SRS>EPSG:42305</SRS>' +
+' <SRS>EPSG:42304</SRS>' +
+' <SRS>EPSG:42303</SRS>' +
+' <LatLonBoundingBox minx="-297176.16529836657" miny="-1.2694600326676274E7" maxx="3.0016785704606913E7" maxy="1.7619361543229006E7"/>' +
+' <Layer queryable="1">' +
+' <Name>og:bugsites</Name>' +
+' <Title/>' +
+' <Abstract>Sample data from GRASS, bug sites location, Spearfish, South Dakota, USA</Abstract>' +
+' <KeywordList>' +
+' <Keyword>spearfish</Keyword>' +
+' <Keyword>sfBugsites</Keyword>' +
+' <Keyword>insects</Keyword>' +
+' <Keyword>bugsites</Keyword>' +
+' <Keyword>tiger_beetles</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:26713</SRS>' +
+' <!--WKT definition of this CRS:' +
+'PROJCS["NAD27 / UTM zone 13N", ' +
+' GEOGCS["NAD27", ' +
+' DATUM["North American Datum 1927", ' +
+' SPHEROID["Clarke 1866", 6378206.4, 294.9786982138982, AUTHORITY["EPSG","7008"]], ' +
+' TOWGS84[-4.2, 135.4, 181.9, 0.0, 0.0, 0.0, 0.0], ' +
+' AUTHORITY["EPSG","6267"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4267"]], ' +
+' PROJECTION["Transverse Mercator", AUTHORITY["EPSG","9807"]], ' +
+' PARAMETER["central_meridian", -105.0], ' +
+' PARAMETER["latitude_of_origin", 0.0], ' +
+' PARAMETER["scale_factor", 0.9996], ' +
+' PARAMETER["false_easting", 500000.0], ' +
+' PARAMETER["false_northing", 0.0], ' +
+' UNIT["m", 1.0], ' +
+' AXIS["Easting", EAST], ' +
+' AXIS["Northing", NORTH], ' +
+' AUTHORITY["EPSG","26713"]]-->' +
+' <LatLonBoundingBox minx="-103.8701581843142" miny="44.286540361238224" maxx="-103.63532819794625" maxy="44.52137034760618"/>' +
+' <BoundingBox SRS="EPSG:26713" minx="590232.0" miny="4914096.0" maxx="608471.0" maxy="4920512.0"/>' +
+' <Style>' +
+' <Name>capitals</Name>' +
+' <Title>Capital cities</Title>' +
+' <Abstract/>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=og:bugsites"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>og:restricted</Name>' +
+' <Title/>' +
+' <Abstract>Sample data from GRASS, restricted areas, Spearfish, South Dakota, USA</Abstract>' +
+' <KeywordList>' +
+' <Keyword>spearfish</Keyword>' +
+' <Keyword>restricted</Keyword>' +
+' <Keyword>sfRestricted</Keyword>' +
+' <Keyword>areas</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:26713</SRS>' +
+' <!--WKT definition of this CRS:' +
+'PROJCS["NAD27 / UTM zone 13N", ' +
+' GEOGCS["NAD27", ' +
+' DATUM["North American Datum 1927", ' +
+' SPHEROID["Clarke 1866", 6378206.4, 294.9786982138982, AUTHORITY["EPSG","7008"]], ' +
+' TOWGS84[-4.2, 135.4, 181.9, 0.0, 0.0, 0.0, 0.0], ' +
+' AUTHORITY["EPSG","6267"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4267"]], ' +
+' PROJECTION["Transverse Mercator", AUTHORITY["EPSG","9807"]], ' +
+' PARAMETER["central_meridian", -105.0], ' +
+' PARAMETER["latitude_of_origin", 0.0], ' +
+' PARAMETER["scale_factor", 0.9996], ' +
+' PARAMETER["false_easting", 500000.0], ' +
+' PARAMETER["false_northing", 0.0], ' +
+' UNIT["m", 1.0], ' +
+' AXIS["Easting", EAST], ' +
+' AXIS["Northing", NORTH], ' +
+' AUTHORITY["EPSG","26713"]]-->' +
+' <LatLonBoundingBox minx="-104.36424600670885" miny="43.78798270975212" maxx="-103.06226503558304" maxy="45.089963680877936"/>' +
+' <BoundingBox SRS="EPSG:26713" minx="551796.8125" miny="4901896.0" maxx="652788.5625" maxy="4940954.0"/>' +
+' <Style>' +
+' <Name>restricted</Name>' +
+' <Title>Red, translucent style</Title>' +
+' <Abstract>A sample style that just prints out a transparent red interior with a red outline</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=og:restricted"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>og:archsites</Name>' +
+' <Title/>' +
+' <Abstract>Sample data from GRASS, archeological sites location, Spearfish, South Dakota, USA</Abstract>' +
+' <KeywordList>' +
+' <Keyword>archsites</Keyword>' +
+' <Keyword>spearfish</Keyword>' +
+' <Keyword>sfArchsites</Keyword>' +
+' <Keyword>archeology</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:26713</SRS>' +
+' <!--WKT definition of this CRS:' +
+'PROJCS["NAD27 / UTM zone 13N", ' +
+' GEOGCS["NAD27", ' +
+' DATUM["North American Datum 1927", ' +
+' SPHEROID["Clarke 1866", 6378206.4, 294.9786982138982, AUTHORITY["EPSG","7008"]], ' +
+' TOWGS84[-4.2, 135.4, 181.9, 0.0, 0.0, 0.0, 0.0], ' +
+' AUTHORITY["EPSG","6267"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4267"]], ' +
+' PROJECTION["Transverse Mercator", AUTHORITY["EPSG","9807"]], ' +
+' PARAMETER["central_meridian", -105.0], ' +
+' PARAMETER["latitude_of_origin", 0.0], ' +
+' PARAMETER["scale_factor", 0.9996], ' +
+' PARAMETER["false_easting", 500000.0], ' +
+' PARAMETER["false_northing", 0.0], ' +
+' UNIT["m", 1.0], ' +
+' AXIS["Easting", EAST], ' +
+' AXIS["Northing", NORTH], ' +
+' AUTHORITY["EPSG","26713"]]-->' +
+' <LatLonBoundingBox minx="-103.87480459767542" miny="44.31295793136913" maxx="-103.63549073047534" maxy="44.55227179856921"/>' +
+' <BoundingBox SRS="EPSG:26713" minx="589860.0" miny="4914479.0" maxx="608355.0" maxy="4926490.0"/>' +
+' <Style>' +
+' <Name>point</Name>' +
+' <Title>Default point</Title>' +
+' <Abstract>A sample style that just prints out a 6px wide red square</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=og:archsites"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>og:streams</Name>' +
+' <Title/>' +
+' <Abstract>Sample data from GRASS, streams, Spearfish, South Dakota, USA</Abstract>' +
+' <KeywordList>' +
+' <Keyword>spearfish</Keyword>' +
+' <Keyword>sfStreams</Keyword>' +
+' <Keyword>streams</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:26713</SRS>' +
+' <!--WKT definition of this CRS:' +
+'PROJCS["NAD27 / UTM zone 13N", ' +
+' GEOGCS["NAD27", ' +
+' DATUM["North American Datum 1927", ' +
+' SPHEROID["Clarke 1866", 6378206.4, 294.9786982138982, AUTHORITY["EPSG","7008"]], ' +
+' TOWGS84[-4.2, 135.4, 181.9, 0.0, 0.0, 0.0, 0.0], ' +
+' AUTHORITY["EPSG","6267"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4267"]], ' +
+' PROJECTION["Transverse Mercator", AUTHORITY["EPSG","9807"]], ' +
+' PARAMETER["central_meridian", -105.0], ' +
+' PARAMETER["latitude_of_origin", 0.0], ' +
+' PARAMETER["scale_factor", 0.9996], ' +
+' PARAMETER["false_easting", 500000.0], ' +
+' PARAMETER["false_northing", 0.0], ' +
+' UNIT["m", 1.0], ' +
+' AXIS["Easting", EAST], ' +
+' AXIS["Northing", NORTH], ' +
+' AUTHORITY["EPSG","26713"]]-->' +
+' <LatLonBoundingBox minx="-103.88033574142051" miny="44.30711172484593" maxx="-103.62022283326024" maxy="44.5672246330062"/>' +
+' <BoundingBox SRS="EPSG:26713" minx="589443.0" miny="4913935.0" maxx="609526.75" maxy="4928059.5"/>' +
+' <Style>' +
+' <Name>simple_streams</Name>' +
+' <Title>Default Styler for streams segments</Title>' +
+' <Abstract>Blue lines, 2px wide</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=og:streams"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>tiger:poly_landmarks</Name>' +
+' <Title>Manhattan (NY) landmarks</Title>' +
+' <Abstract>Manhattan landmarks, identifies water, lakes, parks, interesting buildilngs</Abstract>' +
+' <KeywordList>' +
+' <Keyword>DS_poly_landmarks</Keyword>' +
+' <Keyword>landmarks</Keyword>' +
+' <Keyword>manhattan</Keyword>' +
+' <Keyword>poly_landmarks</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:4326</SRS>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <LatLonBoundingBox minx="-74.0828672737" miny="40.67246384130001" maxx="-73.8660689563" maxy="40.8892621587"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="-74.047185" miny="40.679648" maxx="-73.90782" maxy="40.882078"/>' +
+' <Style>' +
+' <Name>poly_landmarks</Name>' +
+' <Title>Default Styler</Title>' +
+' <Abstract/>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tiger:poly_landmarks"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>tiger:poi</Name>' +
+' <Title>Manhattan (NY) points of interest</Title>' +
+' <Abstract>Points of interest in New York, New York (on Manhattan). One of the attributes contains the name of a file with a picture of the point of interest.</Abstract>' +
+' <KeywordList>' +
+' <Keyword>poi</Keyword>' +
+' <Keyword>Manhattan</Keyword>' +
+' <Keyword>DS_poi</Keyword>' +
+' <Keyword>points_of_interest</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:4326</SRS>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <LatLonBoundingBox minx="-74.01244590356289" miny="40.70750285086222" maxx="-74.00795911725866" maxy="40.711989637166425"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="-74.0118315772888" miny="40.70754683896324" maxx="-74.00153046439813" maxy="40.719885123828675"/>' +
+' <Style>' +
+' <Name>poi</Name>' +
+' <Title>Points of interest</Title>' +
+' <Abstract>Manhattan points of interest</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tiger:poi"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>tiger:tiger_roads</Name>' +
+' <Title>Manhattan (NY) roads</Title>' +
+' <Abstract>Highly simplified road layout of Manhattan in New York..</Abstract>' +
+' <KeywordList>' +
+' <Keyword>DS_tiger_roads</Keyword>' +
+' <Keyword>tiger_roads</Keyword>' +
+' <Keyword>roads</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:4326</SRS>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <LatLonBoundingBox minx="-74.06603057" miny="40.68228143" maxx="-73.86819443" maxy="40.880117569999996"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="-74.02722" miny="40.684221" maxx="-73.907005" maxy="40.878178"/>' +
+' <Style>' +
+' <Name>tiger_roads</Name>' +
+' <Title>Default Styler</Title>' +
+' <Abstract/>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tiger:tiger_roads"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>za:za_natural</Name>' +
+' <Title>Natural Landmarks in South Africa</Title>' +
+' <Abstract>This layer describes natural features of South Africa such as forests and lakes.</Abstract>' +
+' <KeywordList>' +
+' <Keyword>water</Keyword>' +
+' <Keyword>forests</Keyword>' +
+' <Keyword>landmarks</Keyword>' +
+' <Keyword>Africa</Keyword>' +
+' <Keyword>South</Keyword>' +
+' <Keyword>natural</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:4326</SRS>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <LatLonBoundingBox minx="16.779241142272962" miny="-36.53577846527099" maxx="32.70336002349853" maxy="-20.611659584045416"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="16.935359954834" miny="-34.3737831115723" maxx="32.5472412109375" maxy="-22.7736549377441"/>' +
+' <Style>' +
+' <Name>za_natural</Name>' +
+' <Title>Default Styler</Title>' +
+' <Abstract/>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=za:za_natural"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>za:za_points</Name>' +
+' <Title>Points of Interest in South Africa</Title>' +
+' <Abstract>Noteworthy locations such as hotels and tourist attractions in South Africa.</Abstract>' +
+' <KeywordList>' +
+' <Keyword>of</Keyword>' +
+' <Keyword>tourist</Keyword>' +
+' <Keyword>landmarks</Keyword>' +
+' <Keyword>zoo</Keyword>' +
+' <Keyword>cities</Keyword>' +
+' <Keyword>interest</Keyword>' +
+' <Keyword>attractions</Keyword>' +
+' <Keyword>points</Keyword>' +
+' <Keyword>hotel</Keyword>' +
+' <Keyword>museum</Keyword>' +
+' <Keyword>picnic</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:4326</SRS>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <LatLonBoundingBox minx="14.629095230102537" miny="-47.151258316040014" maxx="39.792314376831065" maxy="-21.988039169311488"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="16.4827766418457" miny="-46.9045600891113" maxx="37.9386329650879" maxy="-22.2347373962402"/>' +
+' <Style>' +
+' <Name>za_points</Name>' +
+' <Title>Default Styler</Title>' +
+' <Abstract/>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=za:za_points"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>za:za_roads</Name>' +
+' <Title>South African Roads</Title>' +
+' <Abstract>This layer describes roads in South Africa.</Abstract>' +
+' <KeywordList>' +
+' <Keyword>south</Keyword>' +
+' <Keyword>africa</Keyword>' +
+' <Keyword>roads</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:4326</SRS>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <LatLonBoundingBox minx="16.29388177871706" miny="-36.85438787460323" maxx="33.04232465744013" maxy="-20.10594499588016"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="16.4580821990967" miny="-34.8331336975098" maxx="32.8781242370605" maxy="-22.1271991729736"/>' +
+' <Style>' +
+' <Name>za_roads</Name>' +
+' <Title>Default Styler</Title>' +
+' <Abstract/>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=za:za_roads"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>za:za_vegetation</Name>' +
+' <Title>South African Vegetation</Title>' +
+' <Abstract>This layer describes vegetated areas in South Africa, categorized by biome.</Abstract>' +
+' <KeywordList>' +
+' <Keyword>south</Keyword>' +
+' <Keyword>vegetation</Keyword>' +
+' <Keyword>africa</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:4326</SRS>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <LatLonBoundingBox minx="16.30492322921758" miny="-36.855452365875216" maxx="33.05824930191042" maxy="-20.102126293182376"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="16.4691715240479" miny="-34.8336486816406" maxx="32.8940010070801" maxy="-22.123929977417"/>' +
+' <Style>' +
+' <Name>za_vegetation</Name>' +
+' <Title>Default Styler</Title>' +
+' <Abstract/>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=za:za_vegetation"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>topp:tasmania_cities</Name>' +
+' <Title>Tasmania cities</Title>' +
+' <Abstract>Cities in Tasmania (actually, just the capital)</Abstract>' +
+' <KeywordList>' +
+' <Keyword>cities</Keyword>' +
+' <Keyword>Tasmania</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:4326</SRS>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <LatLonBoundingBox minx="145.1667856" miny="-43.706631400000006" maxx="148.30373440000002" maxy="-40.56968259999999"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="147.2910004483" miny="-42.851001816890005" maxx="147.2910004483" maxy="-42.851001816890005"/>' +
+' <Style>' +
+' <Name>capitals</Name>' +
+' <Title>Capital cities</Title>' +
+' <Abstract/>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:tasmania_cities"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>topp:tasmania_roads</Name>' +
+' <Title>Tasmania roads</Title>' +
+' <Abstract>Main Tasmania roads</Abstract>' +
+' <KeywordList>' +
+' <Keyword>Roads</Keyword>' +
+' <Keyword>Tasmania</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:4326</SRS>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <LatLonBoundingBox minx="145.1667856" miny="-43.706631400000006" maxx="148.30373440000002" maxy="-40.56968259999999"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="145.19754" miny="-43.423512" maxx="148.27298000000002" maxy="-40.852802"/>' +
+' <Style>' +
+' <Name>simple_roads</Name>' +
+' <Title>Default Styler for simple road segments</Title>' +
+' <Abstract>Light red line, 2px wide</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:tasmania_roads"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>topp:tasmania_state_boundaries</Name>' +
+' <Title>Tasmania state boundaries</Title>' +
+' <Abstract>Tasmania state boundaries</Abstract>' +
+' <KeywordList>' +
+' <Keyword>boundaries</Keyword>' +
+' <Keyword>tasmania_state_boundaries</Keyword>' +
+' <Keyword>Tasmania</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:4326</SRS>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <LatLonBoundingBox minx="143.74100879660003" miny="-44.026947203400006" maxx="148.57295620340003" maxy="-39.194999796599994"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="143.83482400000003" miny="-43.648056" maxx="148.47914100000003" maxy="-39.573891"/>' +
+' <Style>' +
+' <Name>green</Name>' +
+' <Title>Green polygon</Title>' +
+' <Abstract>Green fill with black outline</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:tasmania_state_boundaries"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>topp:tasmania_water_bodies</Name>' +
+' <Title>Tasmania water bodies</Title>' +
+' <Abstract>Tasmania water bodies</Abstract>' +
+' <KeywordList>' +
+' <Keyword>Lakes</Keyword>' +
+' <Keyword>Bodies</Keyword>' +
+' <Keyword>Australia</Keyword>' +
+' <Keyword>Water</Keyword>' +
+' <Keyword>Tasmania</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:4326</SRS>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <LatLonBoundingBox minx="145.95490063999998" miny="-43.04450786" maxx="147.23641436" maxy="-41.762994139999996"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="145.97161899999998" miny="-43.031944" maxx="147.219696" maxy="-41.775558"/>' +
+' <Style>' +
+' <Name>cite_lakes</Name>' +
+' <Title>Blue lake</Title>' +
+' <Abstract>A blue fill, solid black outline style</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:tasmania_water_bodies"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>topp:states</Name>' +
+' <Title>USA Population</Title>' +
+' <Abstract>This is some census data on the states.</Abstract>' +
+' <KeywordList>' +
+' <Keyword>census</Keyword>' +
+' <Keyword>united</Keyword>' +
+' <Keyword>boundaries</Keyword>' +
+' <Keyword>state</Keyword>' +
+' <Keyword>states</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:4326</SRS>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <LatLonBoundingBox minx="-125.30903773" miny="7.705448770000002" maxx="-66.39223326999999" maxy="66.62225323"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="-124.73142200000001" miny="24.955967" maxx="-66.969849" maxy="49.371735"/>' +
+' <Style>' +
+' <Name>population</Name>' +
+' <Title>Population in the United States</Title>' +
+' <Abstract>A sample filter that filters the United States into three' +
+' categories of population, drawn in different colors</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:states"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>tike:waterways</Name>' +
+' <Title>Waterways</Title>' +
+' <Abstract>Waterways in Finland.</Abstract>' +
+' <KeywordList>' +
+' <Keyword>Finland</Keyword>' +
+' <Keyword>waterways</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:4326</SRS>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <LatLonBoundingBox minx="19.530168895721403" miny="58.860618000030506" maxx="31.6566005897522" maxy="70.9870496940613"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="19.649055480957" miny="59.9357719421387" maxx="31.5377140045166" maxy="69.9118957519531"/>' +
+' <Style>' +
+' <Name>line</Name>' +
+' <Title>1 px blue line</Title>' +
+' <Abstract>Default line style, 1 pixel wide blue</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tike:waterways"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>tike:railways</Name>' +
+' <Title>roads_Type</Title>' +
+' <Abstract>Generated from tike</Abstract>' +
+' <KeywordList>' +
+' <Keyword>tike</Keyword>' +
+' <Keyword>roads</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:4326</SRS>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <LatLonBoundingBox minx="-297176.16529836657" miny="-1.2694600326676274E7" maxx="3.0016785704606913E7" maxy="1.7619361543229006E7"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="19.5393085479736" miny="-2277.78344726562" maxx="2.971959E7" maxy="4927039.0"/>' +
+' <Style>' +
+' <Name>line</Name>' +
+' <Title>1 px blue line</Title>' +
+' <Abstract>Default line style, 1 pixel wide blue</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tike:railways"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>tike:roads</Name>' +
+' <Title>roads_Type</Title>' +
+' <Abstract>Generated from tike</Abstract>' +
+' <KeywordList>' +
+' <Keyword>tike</Keyword>' +
+' <Keyword>roads</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:4326</SRS>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <LatLonBoundingBox minx="-297176.16529836657" miny="-1.2694600326676274E7" maxx="3.0016785704606913E7" maxy="1.7619361543229006E7"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="19.5393085479736" miny="-2277.78344726562" maxx="2.971959E7" maxy="4927039.0"/>' +
+' <Style>' +
+' <Name>line</Name>' +
+' <Title>1 px blue line</Title>' +
+' <Abstract>Default line style, 1 pixel wide blue</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tike:roads"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>og:roads</Name>' +
+' <Title>roads_Type</Title>' +
+' <Abstract>Generated from sf_reset</Abstract>' +
+' <KeywordList>' +
+' <Keyword>roads</Keyword>' +
+' <Keyword>sf_reset</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:26713</SRS>' +
+' <!--WKT definition of this CRS:' +
+'PROJCS["NAD27 / UTM zone 13N", ' +
+' GEOGCS["NAD27", ' +
+' DATUM["North American Datum 1927", ' +
+' SPHEROID["Clarke 1866", 6378206.4, 294.9786982138982, AUTHORITY["EPSG","7008"]], ' +
+' TOWGS84[-4.2, 135.4, 181.9, 0.0, 0.0, 0.0, 0.0], ' +
+' AUTHORITY["EPSG","6267"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4267"]], ' +
+' PROJECTION["Transverse Mercator", AUTHORITY["EPSG","9807"]], ' +
+' PARAMETER["central_meridian", -105.0], ' +
+' PARAMETER["latitude_of_origin", 0.0], ' +
+' PARAMETER["scale_factor", 0.9996], ' +
+' PARAMETER["false_easting", 500000.0], ' +
+' PARAMETER["false_northing", 0.0], ' +
+' UNIT["m", 1.0], ' +
+' AXIS["Easting", EAST], ' +
+' AXIS["Northing", NORTH], ' +
+' AUTHORITY["EPSG","26713"]]-->' +
+' <LatLonBoundingBox minx="-103.88042792817339" miny="44.308776913708805" maxx="-103.62014761945467" maxy="44.56905722242751"/>' +
+' <BoundingBox SRS="EPSG:26713" minx="589434.8125" miny="4914006.0" maxx="609527.25" maxy="4928377.0"/>' +
+' <Style>' +
+' <Name>simple_roads</Name>' +
+' <Title>Default Styler for simple road segments</Title>' +
+' <Abstract>Light red line, 2px wide</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=og:roads"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>tike:points</Name>' +
+' <Title>roads_Type</Title>' +
+' <Abstract>Generated from tike</Abstract>' +
+' <KeywordList>' +
+' <Keyword>tike</Keyword>' +
+' <Keyword>roads</Keyword>' +
+' </KeywordList>' +
+' <SRS>EPSG:4326</SRS>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <LatLonBoundingBox minx="19.73377216339108" miny="59.107116584777835" maxx="31.40053188323972" maxy="70.77387630462647"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="19.8481521606445" miny="59.8213005065918" maxx="31.2861518859863" maxy="70.0596923828125"/>' +
+' <Style>' +
+' <Name>line</Name>' +
+' <Title>1 px blue line</Title>' +
+' <Abstract>Default line style, 1 pixel wide blue</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tike:points"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>topp:bluemarble</Name>' +
+' <Title>Blue Marble Imagery</Title>' +
+' <Abstract>Blue Marble NG global bathymetry and topography data from NASA. More information about the Blue Marble NG project is available from http://earthobservatory.nasa.gov/Features/BlueMarble .</Abstract>' +
+' <KeywordList>' +
+' <Keyword>WCS</Keyword>' +
+' <Keyword>bluemarble</Keyword>' +
+' <Keyword>bluemarble</Keyword>' +
+' </KeywordList>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <SRS>EPSG:4326</SRS>' +
+' <LatLonBoundingBox minx="-180.00000003333" miny="-89.99999996486703" maxx="179.99999993067" maxy="90.000000033333"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="-180.00000003333" miny="-89.99999996486703" maxx="179.99999993067" maxy="90.000000033333"/>' +
+' <Style>' +
+' <Name>raster</Name>' +
+' <Title>Raster</Title>' +
+' <Abstract>A sample style for rasters, good for displaying imagery</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:bluemarble"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>nurc:Arc_Sample</Name>' +
+' <Title>Global annual rainfall</Title>' +
+' <Abstract>Global annual rainfall in ArcGrid format</Abstract>' +
+' <KeywordList>' +
+' <Keyword>WCS</Keyword>' +
+' <Keyword>arcGridSample</Keyword>' +
+' <Keyword>arcGridSample_Coverage</Keyword>' +
+' </KeywordList>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <SRS>EPSG:4326</SRS>' +
+' <LatLonBoundingBox minx="-180.0" miny="-90.0" maxx="180.0" maxy="90.0"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="-180.0" miny="-90.0" maxx="180.0" maxy="90.0"/>' +
+' <Style>' +
+' <Name>raster</Name>' +
+' <Title>Raster</Title>' +
+' <Abstract>A sample style for rasters, good for displaying imagery</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=nurc:Arc_Sample"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>nurc:Img_Sample</Name>' +
+' <Title>North America sample imagery</Title>' +
+' <Abstract>A very rough imagery of North America</Abstract>' +
+' <KeywordList>' +
+' <Keyword>WCS</Keyword>' +
+' <Keyword>worldImageSample</Keyword>' +
+' <Keyword>worldImageSample_Coverage</Keyword>' +
+' </KeywordList>' +
+' <!--WKT definition of this CRS:' +
+'GEOGCS["WGS 84", ' +
+' DATUM["World Geodetic System 1984", ' +
+' SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], ' +
+' AUTHORITY["EPSG","6326"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4326"]]-->' +
+' <SRS>EPSG:4326</SRS>' +
+' <LatLonBoundingBox minx="-130.85168" miny="20.7052" maxx="-62.0054" maxy="54.1141"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="-130.85168" miny="20.7052" maxx="-62.0054" maxy="54.1141"/>' +
+' <Style>' +
+' <Name>raster</Name>' +
+' <Title>Raster</Title>' +
+' <Abstract>A sample style for rasters, good for displaying imagery</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=nurc:Img_Sample"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="1">' +
+' <Name>sf:sfdem</Name>' +
+' <Title>Spearfish DEM</Title>' +
+' <Abstract>Digital Elevation Model data for Spearfish, South Dakota</Abstract>' +
+' <KeywordList>' +
+' <Keyword>WCS</Keyword>' +
+' <Keyword>sf</Keyword>' +
+' <Keyword>dem</Keyword>' +
+' <Keyword>digital</Keyword>' +
+' <Keyword>elevation</Keyword>' +
+' <Keyword>model</Keyword>' +
+' </KeywordList>' +
+' <!--WKT definition of this CRS:' +
+'PROJCS["NAD27 / UTM zone 13N", ' +
+' GEOGCS["NAD27", ' +
+' DATUM["North American Datum 1927", ' +
+' SPHEROID["Clarke 1866", 6378206.4, 294.9786982138982, AUTHORITY["EPSG","7008"]], ' +
+' TOWGS84[-4.2, 135.4, 181.9, 0.0, 0.0, 0.0, 0.0], ' +
+' AUTHORITY["EPSG","6267"]], ' +
+' PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], ' +
+' UNIT["degree", 0.017453292519943295], ' +
+' AXIS["Geodetic longitude", EAST], ' +
+' AXIS["Geodetic latitude", NORTH], ' +
+' AUTHORITY["EPSG","4267"]], ' +
+' PROJECTION["Transverse Mercator", AUTHORITY["EPSG","9807"]], ' +
+' PARAMETER["central_meridian", -105.0], ' +
+' PARAMETER["latitude_of_origin", 0.0], ' +
+' PARAMETER["scale_factor", 0.9996], ' +
+' PARAMETER["false_easting", 500000.0], ' +
+' PARAMETER["false_northing", 0.0], ' +
+' UNIT["m", 1.0], ' +
+' AXIS["Easting", EAST], ' +
+' AXIS["Northing", NORTH], ' +
+' AUTHORITY["EPSG","26713"]]-->' +
+' <SRS>EPSG:26713</SRS>' +
+' <LatLonBoundingBox minx="-103.87108701853181" miny="44.370187074132616" maxx="-103.62940739432703" maxy="44.5016011535299"/>' +
+' <BoundingBox SRS="EPSG:26713" minx="589980.0" miny="4913700.0" maxx="609000.0" maxy="4928010.0"/>' +
+' <Style>' +
+' <Name>dem</Name>' +
+' <Title>Simple DEM style</Title>' +
+' <Abstract>Classic elevation color progression</Abstract>' +
+' <LegendURL width="20" height="20">' +
+' <Format>image/png</Format>' +
+' <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=sf:sfdem"/>' +
+' </LegendURL>' +
+' </Style>' +
+' </Layer>' +
+' <Layer queryable="0">' +
+' <Name>tasmania</Name>' +
+' <Title>tasmania</Title>' +
+' <Abstract>Layer-Group type layer: tasmania</Abstract>' +
+' <SRS>EPSG:4326</SRS>' +
+' <LatLonBoundingBox minx="143.83482400000003" miny="-43.648056" maxx="148.47914100000003" maxy="-39.573891"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="143.83482400000003" miny="-43.648056" maxx="148.47914100000003" maxy="-39.573891"/>' +
+' </Layer>' +
+' <Layer queryable="0">' +
+' <Name>tiger-ny</Name>' +
+' <Title>tiger-ny</Title>' +
+' <Abstract>Layer-Group type layer: tiger-ny</Abstract>' +
+' <SRS>EPSG:4326</SRS>' +
+' <LatLonBoundingBox minx="-74.047185" miny="40.679648" maxx="-73.907005" maxy="40.882078"/>' +
+' <BoundingBox SRS="EPSG:4326" minx="-74.047185" miny="40.679648" maxx="-73.907005" maxy="40.882078"/>' +
+' </Layer>' +
+' </Layer>' +
+' </Capability>' +
+'</WMT_MS_Capabilities>';
+
diff --git a/misc/openlayers/tests/speed/wmscaps.xml b/misc/openlayers/tests/speed/wmscaps.xml
new file mode 100644
index 0000000..885f712
--- /dev/null
+++ b/misc/openlayers/tests/speed/wmscaps.xml
@@ -0,0 +1,4954 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE WMT_MS_Capabilities SYSTEM "http://demo.opengeo.org/geoserver/schemas/wms/1.1.1/WMS_MS_Capabilities.dtd">
+<WMT_MS_Capabilities version="1.1.1" updateSequence="145">
+ <Service>
+ <Name>OGC:WMS</Name>
+ <Title>GeoServer Web Map Service</Title>
+ <Abstract>A compliant implementation of WMS 1.1.1 plus most of the SLD 1.0 extension (dynamic styling). Can also generate PDF, SVG, KML, GeoRSS</Abstract>
+ <KeywordList>
+ <Keyword>WFS</Keyword>
+ <Keyword>WMS</Keyword>
+ <Keyword>GEOSERVER</Keyword>
+ </KeywordList>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms"/>
+ <ContactInformation>
+ <ContactPersonPrimary>
+ <ContactPerson>Claudius Ptolomaeus</ContactPerson>
+ <ContactOrganization>The ancient geographes INC</ContactOrganization>
+ </ContactPersonPrimary>
+ <ContactPosition>Chief geographer</ContactPosition>
+ <ContactAddress>
+ <AddressType>Work</AddressType>
+ <Address/>
+ <City>Alexandria</City>
+ <StateOrProvince/>
+ <PostCode/>
+ <Country>Egypt</Country>
+ </ContactAddress>
+ <ContactVoiceTelephone/>
+ <ContactFacsimileTelephone/>
+ <ContactElectronicMailAddress>claudius.ptolomaeus@gmail.com</ContactElectronicMailAddress>
+ </ContactInformation>
+ <Fees>NONE</Fees>
+ <AccessConstraints>NONE</AccessConstraints>
+ </Service>
+ <Capability>
+ <Request>
+ <GetCapabilities>
+ <Format>application/vnd.ogc.wms_xml</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms?SERVICE=WMS&amp;"/>
+ </Get>
+ <Post>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms?SERVICE=WMS&amp;"/>
+ </Post>
+ </HTTP>
+ </DCPType>
+ </GetCapabilities>
+ <GetMap>
+ <Format>image/png</Format>
+ <Format>application/atom xml</Format>
+ <Format>application/atom+xml</Format>
+ <Format>application/openlayers</Format>
+ <Format>application/pdf</Format>
+ <Format>application/rss xml</Format>
+ <Format>application/rss+xml</Format>
+ <Format>application/vnd.google-earth.kml</Format>
+ <Format>application/vnd.google-earth.kml xml</Format>
+ <Format>application/vnd.google-earth.kml+xml</Format>
+ <Format>application/vnd.google-earth.kmz</Format>
+ <Format>application/vnd.google-earth.kmz xml</Format>
+ <Format>application/vnd.google-earth.kmz+xml</Format>
+ <Format>atom</Format>
+ <Format>image/geotiff</Format>
+ <Format>image/geotiff8</Format>
+ <Format>image/gif</Format>
+ <Format>image/jpeg</Format>
+ <Format>image/png8</Format>
+ <Format>image/svg</Format>
+ <Format>image/svg xml</Format>
+ <Format>image/svg+xml</Format>
+ <Format>image/tiff</Format>
+ <Format>image/tiff8</Format>
+ <Format>kml</Format>
+ <Format>kmz</Format>
+ <Format>openlayers</Format>
+ <Format>rss</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms?SERVICE=WMS&amp;"/>
+ </Get>
+ </HTTP>
+ </DCPType>
+ </GetMap>
+ <GetFeatureInfo>
+ <Format>text/plain</Format>
+ <Format>text/html</Format>
+ <Format>application/vnd.ogc.gml</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms?SERVICE=WMS&amp;"/>
+ </Get>
+ <Post>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms?SERVICE=WMS&amp;"/>
+ </Post>
+ </HTTP>
+ </DCPType>
+ </GetFeatureInfo>
+ <DescribeLayer>
+ <Format>application/vnd.ogc.wms_xml</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms?SERVICE=WMS&amp;"/>
+ </Get>
+ </HTTP>
+ </DCPType>
+ </DescribeLayer>
+ <GetLegendGraphic>
+ <Format>image/png</Format>
+ <Format>image/jpeg</Format>
+ <Format>image/gif</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms?SERVICE=WMS&amp;"/>
+ </Get>
+ </HTTP>
+ </DCPType>
+ </GetLegendGraphic>
+ </Request>
+ <Exception>
+ <Format>application/vnd.ogc.se_xml</Format>
+ </Exception>
+ <UserDefinedSymbolization SupportSLD="1" UserLayer="1" UserStyle="1" RemoteWFS="1"/>
+ <Layer>
+ <Title>GeoServer Web Map Service</Title>
+ <Abstract>A compliant implementation of WMS 1.1.1 plus most of the SLD 1.0 extension (dynamic styling). Can also generate PDF, SVG, KML, GeoRSS</Abstract>
+ <!--All supported EPSG projections:-->
+ <SRS>EPSG:WGS84(DD)</SRS>
+ <SRS>EPSG:2000</SRS>
+ <SRS>EPSG:2001</SRS>
+ <SRS>EPSG:2002</SRS>
+ <SRS>EPSG:2003</SRS>
+ <SRS>EPSG:2004</SRS>
+ <SRS>EPSG:2005</SRS>
+ <SRS>EPSG:2006</SRS>
+ <SRS>EPSG:2007</SRS>
+ <SRS>EPSG:2008</SRS>
+ <SRS>EPSG:2009</SRS>
+ <SRS>EPSG:2010</SRS>
+ <SRS>EPSG:2011</SRS>
+ <SRS>EPSG:2012</SRS>
+ <SRS>EPSG:2013</SRS>
+ <SRS>EPSG:2014</SRS>
+ <SRS>EPSG:2015</SRS>
+ <SRS>EPSG:2016</SRS>
+ <SRS>EPSG:2017</SRS>
+ <SRS>EPSG:2018</SRS>
+ <SRS>EPSG:2019</SRS>
+ <SRS>EPSG:2020</SRS>
+ <SRS>EPSG:2021</SRS>
+ <SRS>EPSG:2022</SRS>
+ <SRS>EPSG:2023</SRS>
+ <SRS>EPSG:2024</SRS>
+ <SRS>EPSG:2025</SRS>
+ <SRS>EPSG:2026</SRS>
+ <SRS>EPSG:2027</SRS>
+ <SRS>EPSG:2028</SRS>
+ <SRS>EPSG:2029</SRS>
+ <SRS>EPSG:2030</SRS>
+ <SRS>EPSG:2031</SRS>
+ <SRS>EPSG:2032</SRS>
+ <SRS>EPSG:2033</SRS>
+ <SRS>EPSG:2034</SRS>
+ <SRS>EPSG:2035</SRS>
+ <SRS>EPSG:2036</SRS>
+ <SRS>EPSG:2037</SRS>
+ <SRS>EPSG:2038</SRS>
+ <SRS>EPSG:2039</SRS>
+ <SRS>EPSG:2040</SRS>
+ <SRS>EPSG:2041</SRS>
+ <SRS>EPSG:2042</SRS>
+ <SRS>EPSG:2043</SRS>
+ <SRS>EPSG:2044</SRS>
+ <SRS>EPSG:2045</SRS>
+ <SRS>EPSG:2046</SRS>
+ <SRS>EPSG:2047</SRS>
+ <SRS>EPSG:2048</SRS>
+ <SRS>EPSG:2049</SRS>
+ <SRS>EPSG:2050</SRS>
+ <SRS>EPSG:2051</SRS>
+ <SRS>EPSG:2052</SRS>
+ <SRS>EPSG:2053</SRS>
+ <SRS>EPSG:2054</SRS>
+ <SRS>EPSG:2055</SRS>
+ <SRS>EPSG:2056</SRS>
+ <SRS>EPSG:2057</SRS>
+ <SRS>EPSG:2058</SRS>
+ <SRS>EPSG:2059</SRS>
+ <SRS>EPSG:2060</SRS>
+ <SRS>EPSG:2061</SRS>
+ <SRS>EPSG:2062</SRS>
+ <SRS>EPSG:2063</SRS>
+ <SRS>EPSG:2064</SRS>
+ <SRS>EPSG:2065</SRS>
+ <SRS>EPSG:2066</SRS>
+ <SRS>EPSG:2067</SRS>
+ <SRS>EPSG:2068</SRS>
+ <SRS>EPSG:2069</SRS>
+ <SRS>EPSG:2070</SRS>
+ <SRS>EPSG:2071</SRS>
+ <SRS>EPSG:2072</SRS>
+ <SRS>EPSG:2073</SRS>
+ <SRS>EPSG:2074</SRS>
+ <SRS>EPSG:2075</SRS>
+ <SRS>EPSG:2076</SRS>
+ <SRS>EPSG:2077</SRS>
+ <SRS>EPSG:2078</SRS>
+ <SRS>EPSG:2079</SRS>
+ <SRS>EPSG:2080</SRS>
+ <SRS>EPSG:2081</SRS>
+ <SRS>EPSG:2082</SRS>
+ <SRS>EPSG:2083</SRS>
+ <SRS>EPSG:2084</SRS>
+ <SRS>EPSG:2085</SRS>
+ <SRS>EPSG:2086</SRS>
+ <SRS>EPSG:2087</SRS>
+ <SRS>EPSG:2088</SRS>
+ <SRS>EPSG:2089</SRS>
+ <SRS>EPSG:2090</SRS>
+ <SRS>EPSG:2091</SRS>
+ <SRS>EPSG:2092</SRS>
+ <SRS>EPSG:2093</SRS>
+ <SRS>EPSG:2094</SRS>
+ <SRS>EPSG:2095</SRS>
+ <SRS>EPSG:2096</SRS>
+ <SRS>EPSG:2097</SRS>
+ <SRS>EPSG:2098</SRS>
+ <SRS>EPSG:2099</SRS>
+ <SRS>EPSG:2100</SRS>
+ <SRS>EPSG:2101</SRS>
+ <SRS>EPSG:2102</SRS>
+ <SRS>EPSG:2103</SRS>
+ <SRS>EPSG:2104</SRS>
+ <SRS>EPSG:2105</SRS>
+ <SRS>EPSG:2106</SRS>
+ <SRS>EPSG:2107</SRS>
+ <SRS>EPSG:2108</SRS>
+ <SRS>EPSG:2109</SRS>
+ <SRS>EPSG:2110</SRS>
+ <SRS>EPSG:2111</SRS>
+ <SRS>EPSG:2112</SRS>
+ <SRS>EPSG:2113</SRS>
+ <SRS>EPSG:2114</SRS>
+ <SRS>EPSG:2115</SRS>
+ <SRS>EPSG:2116</SRS>
+ <SRS>EPSG:2117</SRS>
+ <SRS>EPSG:2118</SRS>
+ <SRS>EPSG:2119</SRS>
+ <SRS>EPSG:2120</SRS>
+ <SRS>EPSG:2121</SRS>
+ <SRS>EPSG:2122</SRS>
+ <SRS>EPSG:2123</SRS>
+ <SRS>EPSG:2124</SRS>
+ <SRS>EPSG:2125</SRS>
+ <SRS>EPSG:2126</SRS>
+ <SRS>EPSG:2127</SRS>
+ <SRS>EPSG:2128</SRS>
+ <SRS>EPSG:2129</SRS>
+ <SRS>EPSG:2130</SRS>
+ <SRS>EPSG:2131</SRS>
+ <SRS>EPSG:2132</SRS>
+ <SRS>EPSG:2133</SRS>
+ <SRS>EPSG:2134</SRS>
+ <SRS>EPSG:2135</SRS>
+ <SRS>EPSG:2136</SRS>
+ <SRS>EPSG:2137</SRS>
+ <SRS>EPSG:2138</SRS>
+ <SRS>EPSG:2139</SRS>
+ <SRS>EPSG:2140</SRS>
+ <SRS>EPSG:2141</SRS>
+ <SRS>EPSG:2142</SRS>
+ <SRS>EPSG:2143</SRS>
+ <SRS>EPSG:2144</SRS>
+ <SRS>EPSG:2145</SRS>
+ <SRS>EPSG:2146</SRS>
+ <SRS>EPSG:2147</SRS>
+ <SRS>EPSG:2148</SRS>
+ <SRS>EPSG:2149</SRS>
+ <SRS>EPSG:2150</SRS>
+ <SRS>EPSG:2151</SRS>
+ <SRS>EPSG:2152</SRS>
+ <SRS>EPSG:2153</SRS>
+ <SRS>EPSG:2154</SRS>
+ <SRS>EPSG:2155</SRS>
+ <SRS>EPSG:2156</SRS>
+ <SRS>EPSG:2157</SRS>
+ <SRS>EPSG:2158</SRS>
+ <SRS>EPSG:2159</SRS>
+ <SRS>EPSG:2160</SRS>
+ <SRS>EPSG:2161</SRS>
+ <SRS>EPSG:2162</SRS>
+ <SRS>EPSG:2163</SRS>
+ <SRS>EPSG:2164</SRS>
+ <SRS>EPSG:2165</SRS>
+ <SRS>EPSG:2166</SRS>
+ <SRS>EPSG:2167</SRS>
+ <SRS>EPSG:2168</SRS>
+ <SRS>EPSG:2169</SRS>
+ <SRS>EPSG:2170</SRS>
+ <SRS>EPSG:2171</SRS>
+ <SRS>EPSG:2172</SRS>
+ <SRS>EPSG:2173</SRS>
+ <SRS>EPSG:2174</SRS>
+ <SRS>EPSG:2175</SRS>
+ <SRS>EPSG:2176</SRS>
+ <SRS>EPSG:2177</SRS>
+ <SRS>EPSG:2178</SRS>
+ <SRS>EPSG:2179</SRS>
+ <SRS>EPSG:2180</SRS>
+ <SRS>EPSG:2188</SRS>
+ <SRS>EPSG:2189</SRS>
+ <SRS>EPSG:2190</SRS>
+ <SRS>EPSG:2191</SRS>
+ <SRS>EPSG:2192</SRS>
+ <SRS>EPSG:2193</SRS>
+ <SRS>EPSG:2194</SRS>
+ <SRS>EPSG:2195</SRS>
+ <SRS>EPSG:2196</SRS>
+ <SRS>EPSG:2197</SRS>
+ <SRS>EPSG:2198</SRS>
+ <SRS>EPSG:2199</SRS>
+ <SRS>EPSG:2200</SRS>
+ <SRS>EPSG:2201</SRS>
+ <SRS>EPSG:2202</SRS>
+ <SRS>EPSG:2203</SRS>
+ <SRS>EPSG:2204</SRS>
+ <SRS>EPSG:2205</SRS>
+ <SRS>EPSG:2206</SRS>
+ <SRS>EPSG:2207</SRS>
+ <SRS>EPSG:2208</SRS>
+ <SRS>EPSG:2209</SRS>
+ <SRS>EPSG:2210</SRS>
+ <SRS>EPSG:2211</SRS>
+ <SRS>EPSG:2212</SRS>
+ <SRS>EPSG:2213</SRS>
+ <SRS>EPSG:2214</SRS>
+ <SRS>EPSG:2215</SRS>
+ <SRS>EPSG:2216</SRS>
+ <SRS>EPSG:2217</SRS>
+ <SRS>EPSG:2218</SRS>
+ <SRS>EPSG:2219</SRS>
+ <SRS>EPSG:2220</SRS>
+ <SRS>EPSG:2221</SRS>
+ <SRS>EPSG:2222</SRS>
+ <SRS>EPSG:2223</SRS>
+ <SRS>EPSG:2224</SRS>
+ <SRS>EPSG:2225</SRS>
+ <SRS>EPSG:2226</SRS>
+ <SRS>EPSG:2227</SRS>
+ <SRS>EPSG:2228</SRS>
+ <SRS>EPSG:2229</SRS>
+ <SRS>EPSG:2230</SRS>
+ <SRS>EPSG:2231</SRS>
+ <SRS>EPSG:2232</SRS>
+ <SRS>EPSG:2233</SRS>
+ <SRS>EPSG:2234</SRS>
+ <SRS>EPSG:2235</SRS>
+ <SRS>EPSG:2236</SRS>
+ <SRS>EPSG:2237</SRS>
+ <SRS>EPSG:2238</SRS>
+ <SRS>EPSG:2239</SRS>
+ <SRS>EPSG:2240</SRS>
+ <SRS>EPSG:2241</SRS>
+ <SRS>EPSG:2242</SRS>
+ <SRS>EPSG:2243</SRS>
+ <SRS>EPSG:2244</SRS>
+ <SRS>EPSG:2245</SRS>
+ <SRS>EPSG:2246</SRS>
+ <SRS>EPSG:2247</SRS>
+ <SRS>EPSG:2248</SRS>
+ <SRS>EPSG:2249</SRS>
+ <SRS>EPSG:2250</SRS>
+ <SRS>EPSG:2251</SRS>
+ <SRS>EPSG:2252</SRS>
+ <SRS>EPSG:2253</SRS>
+ <SRS>EPSG:2254</SRS>
+ <SRS>EPSG:2255</SRS>
+ <SRS>EPSG:2256</SRS>
+ <SRS>EPSG:2257</SRS>
+ <SRS>EPSG:2258</SRS>
+ <SRS>EPSG:2259</SRS>
+ <SRS>EPSG:2260</SRS>
+ <SRS>EPSG:2261</SRS>
+ <SRS>EPSG:2262</SRS>
+ <SRS>EPSG:2263</SRS>
+ <SRS>EPSG:2264</SRS>
+ <SRS>EPSG:2265</SRS>
+ <SRS>EPSG:2266</SRS>
+ <SRS>EPSG:2267</SRS>
+ <SRS>EPSG:2268</SRS>
+ <SRS>EPSG:2269</SRS>
+ <SRS>EPSG:2270</SRS>
+ <SRS>EPSG:2271</SRS>
+ <SRS>EPSG:2272</SRS>
+ <SRS>EPSG:2273</SRS>
+ <SRS>EPSG:2274</SRS>
+ <SRS>EPSG:2275</SRS>
+ <SRS>EPSG:2276</SRS>
+ <SRS>EPSG:2277</SRS>
+ <SRS>EPSG:2278</SRS>
+ <SRS>EPSG:2279</SRS>
+ <SRS>EPSG:2280</SRS>
+ <SRS>EPSG:2281</SRS>
+ <SRS>EPSG:2282</SRS>
+ <SRS>EPSG:2283</SRS>
+ <SRS>EPSG:2284</SRS>
+ <SRS>EPSG:2285</SRS>
+ <SRS>EPSG:2286</SRS>
+ <SRS>EPSG:2287</SRS>
+ <SRS>EPSG:2288</SRS>
+ <SRS>EPSG:2289</SRS>
+ <SRS>EPSG:2290</SRS>
+ <SRS>EPSG:2291</SRS>
+ <SRS>EPSG:2292</SRS>
+ <SRS>EPSG:2294</SRS>
+ <SRS>EPSG:2295</SRS>
+ <SRS>EPSG:2296</SRS>
+ <SRS>EPSG:2297</SRS>
+ <SRS>EPSG:2298</SRS>
+ <SRS>EPSG:2299</SRS>
+ <SRS>EPSG:2300</SRS>
+ <SRS>EPSG:2301</SRS>
+ <SRS>EPSG:2302</SRS>
+ <SRS>EPSG:2303</SRS>
+ <SRS>EPSG:2304</SRS>
+ <SRS>EPSG:2305</SRS>
+ <SRS>EPSG:2306</SRS>
+ <SRS>EPSG:2307</SRS>
+ <SRS>EPSG:2308</SRS>
+ <SRS>EPSG:2309</SRS>
+ <SRS>EPSG:2310</SRS>
+ <SRS>EPSG:2311</SRS>
+ <SRS>EPSG:2312</SRS>
+ <SRS>EPSG:2313</SRS>
+ <SRS>EPSG:2314</SRS>
+ <SRS>EPSG:2315</SRS>
+ <SRS>EPSG:2316</SRS>
+ <SRS>EPSG:2317</SRS>
+ <SRS>EPSG:2318</SRS>
+ <SRS>EPSG:2319</SRS>
+ <SRS>EPSG:2320</SRS>
+ <SRS>EPSG:2321</SRS>
+ <SRS>EPSG:2322</SRS>
+ <SRS>EPSG:2323</SRS>
+ <SRS>EPSG:2324</SRS>
+ <SRS>EPSG:2325</SRS>
+ <SRS>EPSG:2326</SRS>
+ <SRS>EPSG:2327</SRS>
+ <SRS>EPSG:2328</SRS>
+ <SRS>EPSG:2329</SRS>
+ <SRS>EPSG:2330</SRS>
+ <SRS>EPSG:2331</SRS>
+ <SRS>EPSG:2332</SRS>
+ <SRS>EPSG:2333</SRS>
+ <SRS>EPSG:2334</SRS>
+ <SRS>EPSG:2335</SRS>
+ <SRS>EPSG:2336</SRS>
+ <SRS>EPSG:2337</SRS>
+ <SRS>EPSG:2338</SRS>
+ <SRS>EPSG:2339</SRS>
+ <SRS>EPSG:2340</SRS>
+ <SRS>EPSG:2341</SRS>
+ <SRS>EPSG:2342</SRS>
+ <SRS>EPSG:2343</SRS>
+ <SRS>EPSG:2344</SRS>
+ <SRS>EPSG:2345</SRS>
+ <SRS>EPSG:2346</SRS>
+ <SRS>EPSG:2347</SRS>
+ <SRS>EPSG:2348</SRS>
+ <SRS>EPSG:2349</SRS>
+ <SRS>EPSG:2350</SRS>
+ <SRS>EPSG:2351</SRS>
+ <SRS>EPSG:2352</SRS>
+ <SRS>EPSG:2353</SRS>
+ <SRS>EPSG:2354</SRS>
+ <SRS>EPSG:2355</SRS>
+ <SRS>EPSG:2356</SRS>
+ <SRS>EPSG:2357</SRS>
+ <SRS>EPSG:2358</SRS>
+ <SRS>EPSG:2359</SRS>
+ <SRS>EPSG:2360</SRS>
+ <SRS>EPSG:2361</SRS>
+ <SRS>EPSG:2362</SRS>
+ <SRS>EPSG:2363</SRS>
+ <SRS>EPSG:2364</SRS>
+ <SRS>EPSG:2365</SRS>
+ <SRS>EPSG:2366</SRS>
+ <SRS>EPSG:2367</SRS>
+ <SRS>EPSG:2368</SRS>
+ <SRS>EPSG:2369</SRS>
+ <SRS>EPSG:2370</SRS>
+ <SRS>EPSG:2371</SRS>
+ <SRS>EPSG:2372</SRS>
+ <SRS>EPSG:2373</SRS>
+ <SRS>EPSG:2374</SRS>
+ <SRS>EPSG:2375</SRS>
+ <SRS>EPSG:2376</SRS>
+ <SRS>EPSG:2377</SRS>
+ <SRS>EPSG:2378</SRS>
+ <SRS>EPSG:2379</SRS>
+ <SRS>EPSG:2380</SRS>
+ <SRS>EPSG:2381</SRS>
+ <SRS>EPSG:2382</SRS>
+ <SRS>EPSG:2383</SRS>
+ <SRS>EPSG:2384</SRS>
+ <SRS>EPSG:2385</SRS>
+ <SRS>EPSG:2386</SRS>
+ <SRS>EPSG:2387</SRS>
+ <SRS>EPSG:2388</SRS>
+ <SRS>EPSG:2389</SRS>
+ <SRS>EPSG:2390</SRS>
+ <SRS>EPSG:2391</SRS>
+ <SRS>EPSG:2392</SRS>
+ <SRS>EPSG:2393</SRS>
+ <SRS>EPSG:2394</SRS>
+ <SRS>EPSG:2395</SRS>
+ <SRS>EPSG:2396</SRS>
+ <SRS>EPSG:2397</SRS>
+ <SRS>EPSG:2398</SRS>
+ <SRS>EPSG:2399</SRS>
+ <SRS>EPSG:2400</SRS>
+ <SRS>EPSG:2401</SRS>
+ <SRS>EPSG:2402</SRS>
+ <SRS>EPSG:2403</SRS>
+ <SRS>EPSG:2404</SRS>
+ <SRS>EPSG:2405</SRS>
+ <SRS>EPSG:2406</SRS>
+ <SRS>EPSG:2407</SRS>
+ <SRS>EPSG:2408</SRS>
+ <SRS>EPSG:2409</SRS>
+ <SRS>EPSG:2410</SRS>
+ <SRS>EPSG:2411</SRS>
+ <SRS>EPSG:2412</SRS>
+ <SRS>EPSG:2413</SRS>
+ <SRS>EPSG:2414</SRS>
+ <SRS>EPSG:2415</SRS>
+ <SRS>EPSG:2416</SRS>
+ <SRS>EPSG:2417</SRS>
+ <SRS>EPSG:2418</SRS>
+ <SRS>EPSG:2419</SRS>
+ <SRS>EPSG:2420</SRS>
+ <SRS>EPSG:2421</SRS>
+ <SRS>EPSG:2422</SRS>
+ <SRS>EPSG:2423</SRS>
+ <SRS>EPSG:2424</SRS>
+ <SRS>EPSG:2425</SRS>
+ <SRS>EPSG:2426</SRS>
+ <SRS>EPSG:2427</SRS>
+ <SRS>EPSG:2428</SRS>
+ <SRS>EPSG:2429</SRS>
+ <SRS>EPSG:2430</SRS>
+ <SRS>EPSG:2431</SRS>
+ <SRS>EPSG:2432</SRS>
+ <SRS>EPSG:2433</SRS>
+ <SRS>EPSG:2434</SRS>
+ <SRS>EPSG:2435</SRS>
+ <SRS>EPSG:2436</SRS>
+ <SRS>EPSG:2437</SRS>
+ <SRS>EPSG:2438</SRS>
+ <SRS>EPSG:2439</SRS>
+ <SRS>EPSG:2440</SRS>
+ <SRS>EPSG:2441</SRS>
+ <SRS>EPSG:2442</SRS>
+ <SRS>EPSG:2443</SRS>
+ <SRS>EPSG:2444</SRS>
+ <SRS>EPSG:2445</SRS>
+ <SRS>EPSG:2446</SRS>
+ <SRS>EPSG:2447</SRS>
+ <SRS>EPSG:2448</SRS>
+ <SRS>EPSG:2449</SRS>
+ <SRS>EPSG:2450</SRS>
+ <SRS>EPSG:2451</SRS>
+ <SRS>EPSG:2452</SRS>
+ <SRS>EPSG:2453</SRS>
+ <SRS>EPSG:2454</SRS>
+ <SRS>EPSG:2455</SRS>
+ <SRS>EPSG:2456</SRS>
+ <SRS>EPSG:2457</SRS>
+ <SRS>EPSG:2458</SRS>
+ <SRS>EPSG:2459</SRS>
+ <SRS>EPSG:2460</SRS>
+ <SRS>EPSG:2461</SRS>
+ <SRS>EPSG:2462</SRS>
+ <SRS>EPSG:2463</SRS>
+ <SRS>EPSG:2464</SRS>
+ <SRS>EPSG:2465</SRS>
+ <SRS>EPSG:2466</SRS>
+ <SRS>EPSG:2467</SRS>
+ <SRS>EPSG:2468</SRS>
+ <SRS>EPSG:2469</SRS>
+ <SRS>EPSG:2470</SRS>
+ <SRS>EPSG:2471</SRS>
+ <SRS>EPSG:2472</SRS>
+ <SRS>EPSG:2473</SRS>
+ <SRS>EPSG:2474</SRS>
+ <SRS>EPSG:2475</SRS>
+ <SRS>EPSG:2476</SRS>
+ <SRS>EPSG:2477</SRS>
+ <SRS>EPSG:2478</SRS>
+ <SRS>EPSG:2479</SRS>
+ <SRS>EPSG:2480</SRS>
+ <SRS>EPSG:2481</SRS>
+ <SRS>EPSG:2482</SRS>
+ <SRS>EPSG:2483</SRS>
+ <SRS>EPSG:2484</SRS>
+ <SRS>EPSG:2485</SRS>
+ <SRS>EPSG:2486</SRS>
+ <SRS>EPSG:2487</SRS>
+ <SRS>EPSG:2488</SRS>
+ <SRS>EPSG:2489</SRS>
+ <SRS>EPSG:2490</SRS>
+ <SRS>EPSG:2491</SRS>
+ <SRS>EPSG:2492</SRS>
+ <SRS>EPSG:2493</SRS>
+ <SRS>EPSG:2494</SRS>
+ <SRS>EPSG:2495</SRS>
+ <SRS>EPSG:2496</SRS>
+ <SRS>EPSG:2497</SRS>
+ <SRS>EPSG:2498</SRS>
+ <SRS>EPSG:2499</SRS>
+ <SRS>EPSG:2500</SRS>
+ <SRS>EPSG:2501</SRS>
+ <SRS>EPSG:2502</SRS>
+ <SRS>EPSG:2503</SRS>
+ <SRS>EPSG:2504</SRS>
+ <SRS>EPSG:2505</SRS>
+ <SRS>EPSG:2506</SRS>
+ <SRS>EPSG:2507</SRS>
+ <SRS>EPSG:2508</SRS>
+ <SRS>EPSG:2509</SRS>
+ <SRS>EPSG:2510</SRS>
+ <SRS>EPSG:2511</SRS>
+ <SRS>EPSG:2512</SRS>
+ <SRS>EPSG:2513</SRS>
+ <SRS>EPSG:2514</SRS>
+ <SRS>EPSG:2515</SRS>
+ <SRS>EPSG:2516</SRS>
+ <SRS>EPSG:2517</SRS>
+ <SRS>EPSG:2518</SRS>
+ <SRS>EPSG:2519</SRS>
+ <SRS>EPSG:2520</SRS>
+ <SRS>EPSG:2521</SRS>
+ <SRS>EPSG:2522</SRS>
+ <SRS>EPSG:2523</SRS>
+ <SRS>EPSG:2524</SRS>
+ <SRS>EPSG:2525</SRS>
+ <SRS>EPSG:2526</SRS>
+ <SRS>EPSG:2527</SRS>
+ <SRS>EPSG:2528</SRS>
+ <SRS>EPSG:2529</SRS>
+ <SRS>EPSG:2530</SRS>
+ <SRS>EPSG:2531</SRS>
+ <SRS>EPSG:2532</SRS>
+ <SRS>EPSG:2533</SRS>
+ <SRS>EPSG:2534</SRS>
+ <SRS>EPSG:2535</SRS>
+ <SRS>EPSG:2536</SRS>
+ <SRS>EPSG:2537</SRS>
+ <SRS>EPSG:2538</SRS>
+ <SRS>EPSG:2539</SRS>
+ <SRS>EPSG:2540</SRS>
+ <SRS>EPSG:2541</SRS>
+ <SRS>EPSG:2542</SRS>
+ <SRS>EPSG:2543</SRS>
+ <SRS>EPSG:2544</SRS>
+ <SRS>EPSG:2545</SRS>
+ <SRS>EPSG:2546</SRS>
+ <SRS>EPSG:2547</SRS>
+ <SRS>EPSG:2548</SRS>
+ <SRS>EPSG:2549</SRS>
+ <SRS>EPSG:2550</SRS>
+ <SRS>EPSG:2551</SRS>
+ <SRS>EPSG:2552</SRS>
+ <SRS>EPSG:2553</SRS>
+ <SRS>EPSG:2554</SRS>
+ <SRS>EPSG:2555</SRS>
+ <SRS>EPSG:2556</SRS>
+ <SRS>EPSG:2557</SRS>
+ <SRS>EPSG:2558</SRS>
+ <SRS>EPSG:2559</SRS>
+ <SRS>EPSG:2560</SRS>
+ <SRS>EPSG:2561</SRS>
+ <SRS>EPSG:2562</SRS>
+ <SRS>EPSG:2563</SRS>
+ <SRS>EPSG:2564</SRS>
+ <SRS>EPSG:2565</SRS>
+ <SRS>EPSG:2566</SRS>
+ <SRS>EPSG:2567</SRS>
+ <SRS>EPSG:2568</SRS>
+ <SRS>EPSG:2569</SRS>
+ <SRS>EPSG:2570</SRS>
+ <SRS>EPSG:2571</SRS>
+ <SRS>EPSG:2572</SRS>
+ <SRS>EPSG:2573</SRS>
+ <SRS>EPSG:2574</SRS>
+ <SRS>EPSG:2575</SRS>
+ <SRS>EPSG:2576</SRS>
+ <SRS>EPSG:2577</SRS>
+ <SRS>EPSG:2578</SRS>
+ <SRS>EPSG:2579</SRS>
+ <SRS>EPSG:2580</SRS>
+ <SRS>EPSG:2581</SRS>
+ <SRS>EPSG:2582</SRS>
+ <SRS>EPSG:2583</SRS>
+ <SRS>EPSG:2584</SRS>
+ <SRS>EPSG:2585</SRS>
+ <SRS>EPSG:2586</SRS>
+ <SRS>EPSG:2587</SRS>
+ <SRS>EPSG:2588</SRS>
+ <SRS>EPSG:2589</SRS>
+ <SRS>EPSG:2590</SRS>
+ <SRS>EPSG:2591</SRS>
+ <SRS>EPSG:2592</SRS>
+ <SRS>EPSG:2593</SRS>
+ <SRS>EPSG:2594</SRS>
+ <SRS>EPSG:2595</SRS>
+ <SRS>EPSG:2596</SRS>
+ <SRS>EPSG:2597</SRS>
+ <SRS>EPSG:2598</SRS>
+ <SRS>EPSG:2599</SRS>
+ <SRS>EPSG:2600</SRS>
+ <SRS>EPSG:2601</SRS>
+ <SRS>EPSG:2602</SRS>
+ <SRS>EPSG:2603</SRS>
+ <SRS>EPSG:2604</SRS>
+ <SRS>EPSG:2605</SRS>
+ <SRS>EPSG:2606</SRS>
+ <SRS>EPSG:2607</SRS>
+ <SRS>EPSG:2608</SRS>
+ <SRS>EPSG:2609</SRS>
+ <SRS>EPSG:2610</SRS>
+ <SRS>EPSG:2611</SRS>
+ <SRS>EPSG:2612</SRS>
+ <SRS>EPSG:2613</SRS>
+ <SRS>EPSG:2614</SRS>
+ <SRS>EPSG:2615</SRS>
+ <SRS>EPSG:2616</SRS>
+ <SRS>EPSG:2617</SRS>
+ <SRS>EPSG:2618</SRS>
+ <SRS>EPSG:2619</SRS>
+ <SRS>EPSG:2620</SRS>
+ <SRS>EPSG:2621</SRS>
+ <SRS>EPSG:2622</SRS>
+ <SRS>EPSG:2623</SRS>
+ <SRS>EPSG:2624</SRS>
+ <SRS>EPSG:2625</SRS>
+ <SRS>EPSG:2626</SRS>
+ <SRS>EPSG:2627</SRS>
+ <SRS>EPSG:2628</SRS>
+ <SRS>EPSG:2629</SRS>
+ <SRS>EPSG:2630</SRS>
+ <SRS>EPSG:2631</SRS>
+ <SRS>EPSG:2632</SRS>
+ <SRS>EPSG:2633</SRS>
+ <SRS>EPSG:2634</SRS>
+ <SRS>EPSG:2635</SRS>
+ <SRS>EPSG:2636</SRS>
+ <SRS>EPSG:2637</SRS>
+ <SRS>EPSG:2638</SRS>
+ <SRS>EPSG:2639</SRS>
+ <SRS>EPSG:2640</SRS>
+ <SRS>EPSG:2641</SRS>
+ <SRS>EPSG:2642</SRS>
+ <SRS>EPSG:2643</SRS>
+ <SRS>EPSG:2644</SRS>
+ <SRS>EPSG:2645</SRS>
+ <SRS>EPSG:2646</SRS>
+ <SRS>EPSG:2647</SRS>
+ <SRS>EPSG:2648</SRS>
+ <SRS>EPSG:2649</SRS>
+ <SRS>EPSG:2650</SRS>
+ <SRS>EPSG:2651</SRS>
+ <SRS>EPSG:2652</SRS>
+ <SRS>EPSG:2653</SRS>
+ <SRS>EPSG:2654</SRS>
+ <SRS>EPSG:2655</SRS>
+ <SRS>EPSG:2656</SRS>
+ <SRS>EPSG:2657</SRS>
+ <SRS>EPSG:2658</SRS>
+ <SRS>EPSG:2659</SRS>
+ <SRS>EPSG:2660</SRS>
+ <SRS>EPSG:2661</SRS>
+ <SRS>EPSG:2662</SRS>
+ <SRS>EPSG:2663</SRS>
+ <SRS>EPSG:2664</SRS>
+ <SRS>EPSG:2665</SRS>
+ <SRS>EPSG:2666</SRS>
+ <SRS>EPSG:2667</SRS>
+ <SRS>EPSG:2668</SRS>
+ <SRS>EPSG:2669</SRS>
+ <SRS>EPSG:2670</SRS>
+ <SRS>EPSG:2671</SRS>
+ <SRS>EPSG:2672</SRS>
+ <SRS>EPSG:2673</SRS>
+ <SRS>EPSG:2674</SRS>
+ <SRS>EPSG:2675</SRS>
+ <SRS>EPSG:2676</SRS>
+ <SRS>EPSG:2677</SRS>
+ <SRS>EPSG:2678</SRS>
+ <SRS>EPSG:2679</SRS>
+ <SRS>EPSG:2680</SRS>
+ <SRS>EPSG:2681</SRS>
+ <SRS>EPSG:2682</SRS>
+ <SRS>EPSG:2683</SRS>
+ <SRS>EPSG:2684</SRS>
+ <SRS>EPSG:2685</SRS>
+ <SRS>EPSG:2686</SRS>
+ <SRS>EPSG:2687</SRS>
+ <SRS>EPSG:2688</SRS>
+ <SRS>EPSG:2689</SRS>
+ <SRS>EPSG:2690</SRS>
+ <SRS>EPSG:2691</SRS>
+ <SRS>EPSG:2692</SRS>
+ <SRS>EPSG:2693</SRS>
+ <SRS>EPSG:2694</SRS>
+ <SRS>EPSG:2695</SRS>
+ <SRS>EPSG:2696</SRS>
+ <SRS>EPSG:2697</SRS>
+ <SRS>EPSG:2698</SRS>
+ <SRS>EPSG:2699</SRS>
+ <SRS>EPSG:2700</SRS>
+ <SRS>EPSG:2701</SRS>
+ <SRS>EPSG:2702</SRS>
+ <SRS>EPSG:2703</SRS>
+ <SRS>EPSG:2704</SRS>
+ <SRS>EPSG:2705</SRS>
+ <SRS>EPSG:2706</SRS>
+ <SRS>EPSG:2707</SRS>
+ <SRS>EPSG:2708</SRS>
+ <SRS>EPSG:2709</SRS>
+ <SRS>EPSG:2710</SRS>
+ <SRS>EPSG:2711</SRS>
+ <SRS>EPSG:2712</SRS>
+ <SRS>EPSG:2713</SRS>
+ <SRS>EPSG:2714</SRS>
+ <SRS>EPSG:2715</SRS>
+ <SRS>EPSG:2716</SRS>
+ <SRS>EPSG:2717</SRS>
+ <SRS>EPSG:2718</SRS>
+ <SRS>EPSG:2719</SRS>
+ <SRS>EPSG:2720</SRS>
+ <SRS>EPSG:2721</SRS>
+ <SRS>EPSG:2722</SRS>
+ <SRS>EPSG:2723</SRS>
+ <SRS>EPSG:2724</SRS>
+ <SRS>EPSG:2725</SRS>
+ <SRS>EPSG:2726</SRS>
+ <SRS>EPSG:2727</SRS>
+ <SRS>EPSG:2728</SRS>
+ <SRS>EPSG:2729</SRS>
+ <SRS>EPSG:2730</SRS>
+ <SRS>EPSG:2731</SRS>
+ <SRS>EPSG:2732</SRS>
+ <SRS>EPSG:2733</SRS>
+ <SRS>EPSG:2734</SRS>
+ <SRS>EPSG:2735</SRS>
+ <SRS>EPSG:2736</SRS>
+ <SRS>EPSG:2737</SRS>
+ <SRS>EPSG:2738</SRS>
+ <SRS>EPSG:2739</SRS>
+ <SRS>EPSG:2740</SRS>
+ <SRS>EPSG:2741</SRS>
+ <SRS>EPSG:2742</SRS>
+ <SRS>EPSG:2743</SRS>
+ <SRS>EPSG:2744</SRS>
+ <SRS>EPSG:2745</SRS>
+ <SRS>EPSG:2746</SRS>
+ <SRS>EPSG:2747</SRS>
+ <SRS>EPSG:2748</SRS>
+ <SRS>EPSG:2749</SRS>
+ <SRS>EPSG:2750</SRS>
+ <SRS>EPSG:2751</SRS>
+ <SRS>EPSG:2752</SRS>
+ <SRS>EPSG:2753</SRS>
+ <SRS>EPSG:2754</SRS>
+ <SRS>EPSG:2755</SRS>
+ <SRS>EPSG:2756</SRS>
+ <SRS>EPSG:2757</SRS>
+ <SRS>EPSG:2758</SRS>
+ <SRS>EPSG:2759</SRS>
+ <SRS>EPSG:2760</SRS>
+ <SRS>EPSG:2761</SRS>
+ <SRS>EPSG:2762</SRS>
+ <SRS>EPSG:2763</SRS>
+ <SRS>EPSG:2764</SRS>
+ <SRS>EPSG:2765</SRS>
+ <SRS>EPSG:2766</SRS>
+ <SRS>EPSG:2767</SRS>
+ <SRS>EPSG:2768</SRS>
+ <SRS>EPSG:2769</SRS>
+ <SRS>EPSG:2770</SRS>
+ <SRS>EPSG:2771</SRS>
+ <SRS>EPSG:2772</SRS>
+ <SRS>EPSG:2773</SRS>
+ <SRS>EPSG:2774</SRS>
+ <SRS>EPSG:2775</SRS>
+ <SRS>EPSG:2776</SRS>
+ <SRS>EPSG:2777</SRS>
+ <SRS>EPSG:2778</SRS>
+ <SRS>EPSG:2779</SRS>
+ <SRS>EPSG:2780</SRS>
+ <SRS>EPSG:2781</SRS>
+ <SRS>EPSG:2782</SRS>
+ <SRS>EPSG:2783</SRS>
+ <SRS>EPSG:2784</SRS>
+ <SRS>EPSG:2785</SRS>
+ <SRS>EPSG:2786</SRS>
+ <SRS>EPSG:2787</SRS>
+ <SRS>EPSG:2788</SRS>
+ <SRS>EPSG:2789</SRS>
+ <SRS>EPSG:2790</SRS>
+ <SRS>EPSG:2791</SRS>
+ <SRS>EPSG:2792</SRS>
+ <SRS>EPSG:2793</SRS>
+ <SRS>EPSG:2794</SRS>
+ <SRS>EPSG:2795</SRS>
+ <SRS>EPSG:2796</SRS>
+ <SRS>EPSG:2797</SRS>
+ <SRS>EPSG:2798</SRS>
+ <SRS>EPSG:2799</SRS>
+ <SRS>EPSG:2800</SRS>
+ <SRS>EPSG:2801</SRS>
+ <SRS>EPSG:2802</SRS>
+ <SRS>EPSG:2803</SRS>
+ <SRS>EPSG:2804</SRS>
+ <SRS>EPSG:2805</SRS>
+ <SRS>EPSG:2806</SRS>
+ <SRS>EPSG:2807</SRS>
+ <SRS>EPSG:2808</SRS>
+ <SRS>EPSG:2809</SRS>
+ <SRS>EPSG:2810</SRS>
+ <SRS>EPSG:2811</SRS>
+ <SRS>EPSG:2812</SRS>
+ <SRS>EPSG:2813</SRS>
+ <SRS>EPSG:2814</SRS>
+ <SRS>EPSG:2815</SRS>
+ <SRS>EPSG:2816</SRS>
+ <SRS>EPSG:2817</SRS>
+ <SRS>EPSG:2818</SRS>
+ <SRS>EPSG:2819</SRS>
+ <SRS>EPSG:2820</SRS>
+ <SRS>EPSG:2821</SRS>
+ <SRS>EPSG:2822</SRS>
+ <SRS>EPSG:2823</SRS>
+ <SRS>EPSG:2824</SRS>
+ <SRS>EPSG:2825</SRS>
+ <SRS>EPSG:2826</SRS>
+ <SRS>EPSG:2827</SRS>
+ <SRS>EPSG:2828</SRS>
+ <SRS>EPSG:2829</SRS>
+ <SRS>EPSG:2830</SRS>
+ <SRS>EPSG:2831</SRS>
+ <SRS>EPSG:2832</SRS>
+ <SRS>EPSG:2833</SRS>
+ <SRS>EPSG:2834</SRS>
+ <SRS>EPSG:2835</SRS>
+ <SRS>EPSG:2836</SRS>
+ <SRS>EPSG:2837</SRS>
+ <SRS>EPSG:2838</SRS>
+ <SRS>EPSG:2839</SRS>
+ <SRS>EPSG:2840</SRS>
+ <SRS>EPSG:2841</SRS>
+ <SRS>EPSG:2842</SRS>
+ <SRS>EPSG:2843</SRS>
+ <SRS>EPSG:2844</SRS>
+ <SRS>EPSG:2845</SRS>
+ <SRS>EPSG:2846</SRS>
+ <SRS>EPSG:2847</SRS>
+ <SRS>EPSG:2848</SRS>
+ <SRS>EPSG:2849</SRS>
+ <SRS>EPSG:2850</SRS>
+ <SRS>EPSG:2851</SRS>
+ <SRS>EPSG:2852</SRS>
+ <SRS>EPSG:2853</SRS>
+ <SRS>EPSG:2854</SRS>
+ <SRS>EPSG:2855</SRS>
+ <SRS>EPSG:2856</SRS>
+ <SRS>EPSG:2857</SRS>
+ <SRS>EPSG:2858</SRS>
+ <SRS>EPSG:2859</SRS>
+ <SRS>EPSG:2860</SRS>
+ <SRS>EPSG:2861</SRS>
+ <SRS>EPSG:2862</SRS>
+ <SRS>EPSG:2863</SRS>
+ <SRS>EPSG:2864</SRS>
+ <SRS>EPSG:2865</SRS>
+ <SRS>EPSG:2866</SRS>
+ <SRS>EPSG:2867</SRS>
+ <SRS>EPSG:2868</SRS>
+ <SRS>EPSG:2869</SRS>
+ <SRS>EPSG:2870</SRS>
+ <SRS>EPSG:2871</SRS>
+ <SRS>EPSG:2872</SRS>
+ <SRS>EPSG:2873</SRS>
+ <SRS>EPSG:2874</SRS>
+ <SRS>EPSG:2875</SRS>
+ <SRS>EPSG:2876</SRS>
+ <SRS>EPSG:2877</SRS>
+ <SRS>EPSG:2878</SRS>
+ <SRS>EPSG:2879</SRS>
+ <SRS>EPSG:2880</SRS>
+ <SRS>EPSG:2881</SRS>
+ <SRS>EPSG:2882</SRS>
+ <SRS>EPSG:2883</SRS>
+ <SRS>EPSG:2884</SRS>
+ <SRS>EPSG:2885</SRS>
+ <SRS>EPSG:2886</SRS>
+ <SRS>EPSG:2887</SRS>
+ <SRS>EPSG:2888</SRS>
+ <SRS>EPSG:2889</SRS>
+ <SRS>EPSG:2890</SRS>
+ <SRS>EPSG:2891</SRS>
+ <SRS>EPSG:2892</SRS>
+ <SRS>EPSG:2893</SRS>
+ <SRS>EPSG:2894</SRS>
+ <SRS>EPSG:2895</SRS>
+ <SRS>EPSG:2896</SRS>
+ <SRS>EPSG:2897</SRS>
+ <SRS>EPSG:2898</SRS>
+ <SRS>EPSG:2899</SRS>
+ <SRS>EPSG:2900</SRS>
+ <SRS>EPSG:2901</SRS>
+ <SRS>EPSG:2902</SRS>
+ <SRS>EPSG:2903</SRS>
+ <SRS>EPSG:2904</SRS>
+ <SRS>EPSG:2905</SRS>
+ <SRS>EPSG:2906</SRS>
+ <SRS>EPSG:2907</SRS>
+ <SRS>EPSG:2908</SRS>
+ <SRS>EPSG:2909</SRS>
+ <SRS>EPSG:2910</SRS>
+ <SRS>EPSG:2911</SRS>
+ <SRS>EPSG:2912</SRS>
+ <SRS>EPSG:2913</SRS>
+ <SRS>EPSG:2914</SRS>
+ <SRS>EPSG:2915</SRS>
+ <SRS>EPSG:2916</SRS>
+ <SRS>EPSG:2917</SRS>
+ <SRS>EPSG:2918</SRS>
+ <SRS>EPSG:2919</SRS>
+ <SRS>EPSG:2920</SRS>
+ <SRS>EPSG:2921</SRS>
+ <SRS>EPSG:2922</SRS>
+ <SRS>EPSG:2923</SRS>
+ <SRS>EPSG:2924</SRS>
+ <SRS>EPSG:2925</SRS>
+ <SRS>EPSG:2926</SRS>
+ <SRS>EPSG:2927</SRS>
+ <SRS>EPSG:2928</SRS>
+ <SRS>EPSG:2929</SRS>
+ <SRS>EPSG:2930</SRS>
+ <SRS>EPSG:2931</SRS>
+ <SRS>EPSG:2932</SRS>
+ <SRS>EPSG:2933</SRS>
+ <SRS>EPSG:2934</SRS>
+ <SRS>EPSG:2935</SRS>
+ <SRS>EPSG:2936</SRS>
+ <SRS>EPSG:2937</SRS>
+ <SRS>EPSG:2938</SRS>
+ <SRS>EPSG:2939</SRS>
+ <SRS>EPSG:2940</SRS>
+ <SRS>EPSG:2941</SRS>
+ <SRS>EPSG:2942</SRS>
+ <SRS>EPSG:2943</SRS>
+ <SRS>EPSG:2944</SRS>
+ <SRS>EPSG:2945</SRS>
+ <SRS>EPSG:2946</SRS>
+ <SRS>EPSG:2947</SRS>
+ <SRS>EPSG:2948</SRS>
+ <SRS>EPSG:2949</SRS>
+ <SRS>EPSG:2950</SRS>
+ <SRS>EPSG:2951</SRS>
+ <SRS>EPSG:2952</SRS>
+ <SRS>EPSG:2953</SRS>
+ <SRS>EPSG:2954</SRS>
+ <SRS>EPSG:2955</SRS>
+ <SRS>EPSG:2956</SRS>
+ <SRS>EPSG:2957</SRS>
+ <SRS>EPSG:2958</SRS>
+ <SRS>EPSG:2959</SRS>
+ <SRS>EPSG:2960</SRS>
+ <SRS>EPSG:2961</SRS>
+ <SRS>EPSG:2962</SRS>
+ <SRS>EPSG:2963</SRS>
+ <SRS>EPSG:2964</SRS>
+ <SRS>EPSG:2965</SRS>
+ <SRS>EPSG:2966</SRS>
+ <SRS>EPSG:2967</SRS>
+ <SRS>EPSG:2968</SRS>
+ <SRS>EPSG:2969</SRS>
+ <SRS>EPSG:2970</SRS>
+ <SRS>EPSG:2971</SRS>
+ <SRS>EPSG:2972</SRS>
+ <SRS>EPSG:2973</SRS>
+ <SRS>EPSG:2975</SRS>
+ <SRS>EPSG:2976</SRS>
+ <SRS>EPSG:2977</SRS>
+ <SRS>EPSG:2978</SRS>
+ <SRS>EPSG:2979</SRS>
+ <SRS>EPSG:2980</SRS>
+ <SRS>EPSG:2981</SRS>
+ <SRS>EPSG:2982</SRS>
+ <SRS>EPSG:2983</SRS>
+ <SRS>EPSG:2984</SRS>
+ <SRS>EPSG:2985</SRS>
+ <SRS>EPSG:2986</SRS>
+ <SRS>EPSG:2987</SRS>
+ <SRS>EPSG:2988</SRS>
+ <SRS>EPSG:2989</SRS>
+ <SRS>EPSG:2990</SRS>
+ <SRS>EPSG:2991</SRS>
+ <SRS>EPSG:2992</SRS>
+ <SRS>EPSG:2993</SRS>
+ <SRS>EPSG:2994</SRS>
+ <SRS>EPSG:2995</SRS>
+ <SRS>EPSG:2996</SRS>
+ <SRS>EPSG:2997</SRS>
+ <SRS>EPSG:2998</SRS>
+ <SRS>EPSG:2999</SRS>
+ <SRS>EPSG:3000</SRS>
+ <SRS>EPSG:3001</SRS>
+ <SRS>EPSG:3002</SRS>
+ <SRS>EPSG:3003</SRS>
+ <SRS>EPSG:3004</SRS>
+ <SRS>EPSG:3005</SRS>
+ <SRS>EPSG:3006</SRS>
+ <SRS>EPSG:3007</SRS>
+ <SRS>EPSG:3008</SRS>
+ <SRS>EPSG:3009</SRS>
+ <SRS>EPSG:3010</SRS>
+ <SRS>EPSG:3011</SRS>
+ <SRS>EPSG:3012</SRS>
+ <SRS>EPSG:3013</SRS>
+ <SRS>EPSG:3014</SRS>
+ <SRS>EPSG:3015</SRS>
+ <SRS>EPSG:3016</SRS>
+ <SRS>EPSG:3017</SRS>
+ <SRS>EPSG:3018</SRS>
+ <SRS>EPSG:3019</SRS>
+ <SRS>EPSG:3020</SRS>
+ <SRS>EPSG:3021</SRS>
+ <SRS>EPSG:3022</SRS>
+ <SRS>EPSG:3023</SRS>
+ <SRS>EPSG:3024</SRS>
+ <SRS>EPSG:3025</SRS>
+ <SRS>EPSG:3026</SRS>
+ <SRS>EPSG:3027</SRS>
+ <SRS>EPSG:3028</SRS>
+ <SRS>EPSG:3029</SRS>
+ <SRS>EPSG:3030</SRS>
+ <SRS>EPSG:3031</SRS>
+ <SRS>EPSG:3032</SRS>
+ <SRS>EPSG:3033</SRS>
+ <SRS>EPSG:3034</SRS>
+ <SRS>EPSG:3035</SRS>
+ <SRS>EPSG:3036</SRS>
+ <SRS>EPSG:3037</SRS>
+ <SRS>EPSG:3038</SRS>
+ <SRS>EPSG:3039</SRS>
+ <SRS>EPSG:3040</SRS>
+ <SRS>EPSG:3041</SRS>
+ <SRS>EPSG:3042</SRS>
+ <SRS>EPSG:3043</SRS>
+ <SRS>EPSG:3044</SRS>
+ <SRS>EPSG:3045</SRS>
+ <SRS>EPSG:3046</SRS>
+ <SRS>EPSG:3047</SRS>
+ <SRS>EPSG:3048</SRS>
+ <SRS>EPSG:3049</SRS>
+ <SRS>EPSG:3050</SRS>
+ <SRS>EPSG:3051</SRS>
+ <SRS>EPSG:3052</SRS>
+ <SRS>EPSG:3053</SRS>
+ <SRS>EPSG:3054</SRS>
+ <SRS>EPSG:3055</SRS>
+ <SRS>EPSG:3056</SRS>
+ <SRS>EPSG:3057</SRS>
+ <SRS>EPSG:3058</SRS>
+ <SRS>EPSG:3059</SRS>
+ <SRS>EPSG:3060</SRS>
+ <SRS>EPSG:3061</SRS>
+ <SRS>EPSG:3062</SRS>
+ <SRS>EPSG:3063</SRS>
+ <SRS>EPSG:3064</SRS>
+ <SRS>EPSG:3065</SRS>
+ <SRS>EPSG:3066</SRS>
+ <SRS>EPSG:3067</SRS>
+ <SRS>EPSG:3068</SRS>
+ <SRS>EPSG:3069</SRS>
+ <SRS>EPSG:3070</SRS>
+ <SRS>EPSG:3071</SRS>
+ <SRS>EPSG:3072</SRS>
+ <SRS>EPSG:3073</SRS>
+ <SRS>EPSG:3074</SRS>
+ <SRS>EPSG:3075</SRS>
+ <SRS>EPSG:3076</SRS>
+ <SRS>EPSG:3077</SRS>
+ <SRS>EPSG:3078</SRS>
+ <SRS>EPSG:3079</SRS>
+ <SRS>EPSG:3080</SRS>
+ <SRS>EPSG:3081</SRS>
+ <SRS>EPSG:3082</SRS>
+ <SRS>EPSG:3083</SRS>
+ <SRS>EPSG:3084</SRS>
+ <SRS>EPSG:3085</SRS>
+ <SRS>EPSG:3086</SRS>
+ <SRS>EPSG:3087</SRS>
+ <SRS>EPSG:3088</SRS>
+ <SRS>EPSG:3089</SRS>
+ <SRS>EPSG:3090</SRS>
+ <SRS>EPSG:3091</SRS>
+ <SRS>EPSG:3092</SRS>
+ <SRS>EPSG:3093</SRS>
+ <SRS>EPSG:3094</SRS>
+ <SRS>EPSG:3095</SRS>
+ <SRS>EPSG:3096</SRS>
+ <SRS>EPSG:3097</SRS>
+ <SRS>EPSG:3098</SRS>
+ <SRS>EPSG:3099</SRS>
+ <SRS>EPSG:3100</SRS>
+ <SRS>EPSG:3101</SRS>
+ <SRS>EPSG:3102</SRS>
+ <SRS>EPSG:3103</SRS>
+ <SRS>EPSG:3104</SRS>
+ <SRS>EPSG:3105</SRS>
+ <SRS>EPSG:3106</SRS>
+ <SRS>EPSG:3107</SRS>
+ <SRS>EPSG:3108</SRS>
+ <SRS>EPSG:3109</SRS>
+ <SRS>EPSG:3110</SRS>
+ <SRS>EPSG:3111</SRS>
+ <SRS>EPSG:3112</SRS>
+ <SRS>EPSG:3113</SRS>
+ <SRS>EPSG:3114</SRS>
+ <SRS>EPSG:3115</SRS>
+ <SRS>EPSG:3116</SRS>
+ <SRS>EPSG:3117</SRS>
+ <SRS>EPSG:3118</SRS>
+ <SRS>EPSG:3119</SRS>
+ <SRS>EPSG:3120</SRS>
+ <SRS>EPSG:3121</SRS>
+ <SRS>EPSG:3122</SRS>
+ <SRS>EPSG:3123</SRS>
+ <SRS>EPSG:3124</SRS>
+ <SRS>EPSG:3125</SRS>
+ <SRS>EPSG:3126</SRS>
+ <SRS>EPSG:3127</SRS>
+ <SRS>EPSG:3128</SRS>
+ <SRS>EPSG:3129</SRS>
+ <SRS>EPSG:3130</SRS>
+ <SRS>EPSG:3131</SRS>
+ <SRS>EPSG:3132</SRS>
+ <SRS>EPSG:3133</SRS>
+ <SRS>EPSG:3134</SRS>
+ <SRS>EPSG:3135</SRS>
+ <SRS>EPSG:3136</SRS>
+ <SRS>EPSG:3137</SRS>
+ <SRS>EPSG:3138</SRS>
+ <SRS>EPSG:3139</SRS>
+ <SRS>EPSG:3140</SRS>
+ <SRS>EPSG:3141</SRS>
+ <SRS>EPSG:3142</SRS>
+ <SRS>EPSG:3143</SRS>
+ <SRS>EPSG:3144</SRS>
+ <SRS>EPSG:3145</SRS>
+ <SRS>EPSG:3146</SRS>
+ <SRS>EPSG:3147</SRS>
+ <SRS>EPSG:3148</SRS>
+ <SRS>EPSG:3149</SRS>
+ <SRS>EPSG:3150</SRS>
+ <SRS>EPSG:3151</SRS>
+ <SRS>EPSG:3152</SRS>
+ <SRS>EPSG:3153</SRS>
+ <SRS>EPSG:3154</SRS>
+ <SRS>EPSG:3155</SRS>
+ <SRS>EPSG:3156</SRS>
+ <SRS>EPSG:3157</SRS>
+ <SRS>EPSG:3158</SRS>
+ <SRS>EPSG:3159</SRS>
+ <SRS>EPSG:3160</SRS>
+ <SRS>EPSG:3161</SRS>
+ <SRS>EPSG:3162</SRS>
+ <SRS>EPSG:3163</SRS>
+ <SRS>EPSG:3164</SRS>
+ <SRS>EPSG:3165</SRS>
+ <SRS>EPSG:3166</SRS>
+ <SRS>EPSG:3167</SRS>
+ <SRS>EPSG:3168</SRS>
+ <SRS>EPSG:3169</SRS>
+ <SRS>EPSG:3170</SRS>
+ <SRS>EPSG:3171</SRS>
+ <SRS>EPSG:3172</SRS>
+ <SRS>EPSG:3173</SRS>
+ <SRS>EPSG:3174</SRS>
+ <SRS>EPSG:3175</SRS>
+ <SRS>EPSG:3176</SRS>
+ <SRS>EPSG:3177</SRS>
+ <SRS>EPSG:3178</SRS>
+ <SRS>EPSG:3179</SRS>
+ <SRS>EPSG:3180</SRS>
+ <SRS>EPSG:3181</SRS>
+ <SRS>EPSG:3182</SRS>
+ <SRS>EPSG:3183</SRS>
+ <SRS>EPSG:3184</SRS>
+ <SRS>EPSG:3185</SRS>
+ <SRS>EPSG:3186</SRS>
+ <SRS>EPSG:3187</SRS>
+ <SRS>EPSG:3188</SRS>
+ <SRS>EPSG:3189</SRS>
+ <SRS>EPSG:3190</SRS>
+ <SRS>EPSG:3191</SRS>
+ <SRS>EPSG:3192</SRS>
+ <SRS>EPSG:3193</SRS>
+ <SRS>EPSG:3194</SRS>
+ <SRS>EPSG:3195</SRS>
+ <SRS>EPSG:3196</SRS>
+ <SRS>EPSG:3197</SRS>
+ <SRS>EPSG:3198</SRS>
+ <SRS>EPSG:3199</SRS>
+ <SRS>EPSG:3200</SRS>
+ <SRS>EPSG:3201</SRS>
+ <SRS>EPSG:3202</SRS>
+ <SRS>EPSG:3203</SRS>
+ <SRS>EPSG:3204</SRS>
+ <SRS>EPSG:3205</SRS>
+ <SRS>EPSG:3206</SRS>
+ <SRS>EPSG:3207</SRS>
+ <SRS>EPSG:3208</SRS>
+ <SRS>EPSG:3209</SRS>
+ <SRS>EPSG:3210</SRS>
+ <SRS>EPSG:3211</SRS>
+ <SRS>EPSG:3212</SRS>
+ <SRS>EPSG:3213</SRS>
+ <SRS>EPSG:3214</SRS>
+ <SRS>EPSG:3215</SRS>
+ <SRS>EPSG:3216</SRS>
+ <SRS>EPSG:3217</SRS>
+ <SRS>EPSG:3218</SRS>
+ <SRS>EPSG:3219</SRS>
+ <SRS>EPSG:3220</SRS>
+ <SRS>EPSG:3221</SRS>
+ <SRS>EPSG:3222</SRS>
+ <SRS>EPSG:3223</SRS>
+ <SRS>EPSG:3224</SRS>
+ <SRS>EPSG:3225</SRS>
+ <SRS>EPSG:3226</SRS>
+ <SRS>EPSG:3227</SRS>
+ <SRS>EPSG:3228</SRS>
+ <SRS>EPSG:3229</SRS>
+ <SRS>EPSG:3230</SRS>
+ <SRS>EPSG:3231</SRS>
+ <SRS>EPSG:3232</SRS>
+ <SRS>EPSG:3233</SRS>
+ <SRS>EPSG:3234</SRS>
+ <SRS>EPSG:3235</SRS>
+ <SRS>EPSG:3236</SRS>
+ <SRS>EPSG:3237</SRS>
+ <SRS>EPSG:3238</SRS>
+ <SRS>EPSG:3239</SRS>
+ <SRS>EPSG:3240</SRS>
+ <SRS>EPSG:3241</SRS>
+ <SRS>EPSG:3242</SRS>
+ <SRS>EPSG:3243</SRS>
+ <SRS>EPSG:3244</SRS>
+ <SRS>EPSG:3245</SRS>
+ <SRS>EPSG:3246</SRS>
+ <SRS>EPSG:3247</SRS>
+ <SRS>EPSG:3248</SRS>
+ <SRS>EPSG:3249</SRS>
+ <SRS>EPSG:3250</SRS>
+ <SRS>EPSG:3251</SRS>
+ <SRS>EPSG:3252</SRS>
+ <SRS>EPSG:3253</SRS>
+ <SRS>EPSG:3254</SRS>
+ <SRS>EPSG:3255</SRS>
+ <SRS>EPSG:3256</SRS>
+ <SRS>EPSG:3257</SRS>
+ <SRS>EPSG:3258</SRS>
+ <SRS>EPSG:3259</SRS>
+ <SRS>EPSG:3260</SRS>
+ <SRS>EPSG:3261</SRS>
+ <SRS>EPSG:3262</SRS>
+ <SRS>EPSG:3263</SRS>
+ <SRS>EPSG:3264</SRS>
+ <SRS>EPSG:3265</SRS>
+ <SRS>EPSG:3266</SRS>
+ <SRS>EPSG:3267</SRS>
+ <SRS>EPSG:3268</SRS>
+ <SRS>EPSG:3269</SRS>
+ <SRS>EPSG:3270</SRS>
+ <SRS>EPSG:3271</SRS>
+ <SRS>EPSG:3272</SRS>
+ <SRS>EPSG:3273</SRS>
+ <SRS>EPSG:3274</SRS>
+ <SRS>EPSG:3275</SRS>
+ <SRS>EPSG:3276</SRS>
+ <SRS>EPSG:3277</SRS>
+ <SRS>EPSG:3278</SRS>
+ <SRS>EPSG:3279</SRS>
+ <SRS>EPSG:3280</SRS>
+ <SRS>EPSG:3281</SRS>
+ <SRS>EPSG:3282</SRS>
+ <SRS>EPSG:3283</SRS>
+ <SRS>EPSG:3284</SRS>
+ <SRS>EPSG:3285</SRS>
+ <SRS>EPSG:3286</SRS>
+ <SRS>EPSG:3287</SRS>
+ <SRS>EPSG:3288</SRS>
+ <SRS>EPSG:3289</SRS>
+ <SRS>EPSG:3290</SRS>
+ <SRS>EPSG:3291</SRS>
+ <SRS>EPSG:3292</SRS>
+ <SRS>EPSG:3293</SRS>
+ <SRS>EPSG:3294</SRS>
+ <SRS>EPSG:3295</SRS>
+ <SRS>EPSG:3296</SRS>
+ <SRS>EPSG:3297</SRS>
+ <SRS>EPSG:3298</SRS>
+ <SRS>EPSG:3299</SRS>
+ <SRS>EPSG:3300</SRS>
+ <SRS>EPSG:3301</SRS>
+ <SRS>EPSG:3302</SRS>
+ <SRS>EPSG:3303</SRS>
+ <SRS>EPSG:3304</SRS>
+ <SRS>EPSG:3305</SRS>
+ <SRS>EPSG:3306</SRS>
+ <SRS>EPSG:3307</SRS>
+ <SRS>EPSG:3308</SRS>
+ <SRS>EPSG:3309</SRS>
+ <SRS>EPSG:3310</SRS>
+ <SRS>EPSG:3311</SRS>
+ <SRS>EPSG:3312</SRS>
+ <SRS>EPSG:3313</SRS>
+ <SRS>EPSG:3314</SRS>
+ <SRS>EPSG:3315</SRS>
+ <SRS>EPSG:3316</SRS>
+ <SRS>EPSG:3317</SRS>
+ <SRS>EPSG:3318</SRS>
+ <SRS>EPSG:3319</SRS>
+ <SRS>EPSG:3320</SRS>
+ <SRS>EPSG:3321</SRS>
+ <SRS>EPSG:3322</SRS>
+ <SRS>EPSG:3323</SRS>
+ <SRS>EPSG:3324</SRS>
+ <SRS>EPSG:3325</SRS>
+ <SRS>EPSG:3326</SRS>
+ <SRS>EPSG:3327</SRS>
+ <SRS>EPSG:3328</SRS>
+ <SRS>EPSG:3329</SRS>
+ <SRS>EPSG:3330</SRS>
+ <SRS>EPSG:3331</SRS>
+ <SRS>EPSG:3332</SRS>
+ <SRS>EPSG:3333</SRS>
+ <SRS>EPSG:3334</SRS>
+ <SRS>EPSG:3335</SRS>
+ <SRS>EPSG:3336</SRS>
+ <SRS>EPSG:3337</SRS>
+ <SRS>EPSG:3338</SRS>
+ <SRS>EPSG:3339</SRS>
+ <SRS>EPSG:3340</SRS>
+ <SRS>EPSG:3341</SRS>
+ <SRS>EPSG:3342</SRS>
+ <SRS>EPSG:3343</SRS>
+ <SRS>EPSG:3344</SRS>
+ <SRS>EPSG:3345</SRS>
+ <SRS>EPSG:3346</SRS>
+ <SRS>EPSG:3347</SRS>
+ <SRS>EPSG:3348</SRS>
+ <SRS>EPSG:3349</SRS>
+ <SRS>EPSG:3350</SRS>
+ <SRS>EPSG:3351</SRS>
+ <SRS>EPSG:3352</SRS>
+ <SRS>EPSG:3353</SRS>
+ <SRS>EPSG:3354</SRS>
+ <SRS>EPSG:3355</SRS>
+ <SRS>EPSG:3356</SRS>
+ <SRS>EPSG:3357</SRS>
+ <SRS>EPSG:3358</SRS>
+ <SRS>EPSG:3359</SRS>
+ <SRS>EPSG:3360</SRS>
+ <SRS>EPSG:3361</SRS>
+ <SRS>EPSG:3362</SRS>
+ <SRS>EPSG:3363</SRS>
+ <SRS>EPSG:3364</SRS>
+ <SRS>EPSG:3365</SRS>
+ <SRS>EPSG:3366</SRS>
+ <SRS>EPSG:3367</SRS>
+ <SRS>EPSG:3368</SRS>
+ <SRS>EPSG:3369</SRS>
+ <SRS>EPSG:3370</SRS>
+ <SRS>EPSG:3371</SRS>
+ <SRS>EPSG:3372</SRS>
+ <SRS>EPSG:3373</SRS>
+ <SRS>EPSG:3374</SRS>
+ <SRS>EPSG:3375</SRS>
+ <SRS>EPSG:3376</SRS>
+ <SRS>EPSG:3377</SRS>
+ <SRS>EPSG:3378</SRS>
+ <SRS>EPSG:3379</SRS>
+ <SRS>EPSG:3380</SRS>
+ <SRS>EPSG:3381</SRS>
+ <SRS>EPSG:3382</SRS>
+ <SRS>EPSG:3383</SRS>
+ <SRS>EPSG:3384</SRS>
+ <SRS>EPSG:3385</SRS>
+ <SRS>EPSG:3386</SRS>
+ <SRS>EPSG:3387</SRS>
+ <SRS>EPSG:3388</SRS>
+ <SRS>EPSG:3389</SRS>
+ <SRS>EPSG:3390</SRS>
+ <SRS>EPSG:3391</SRS>
+ <SRS>EPSG:3392</SRS>
+ <SRS>EPSG:3393</SRS>
+ <SRS>EPSG:3394</SRS>
+ <SRS>EPSG:3395</SRS>
+ <SRS>EPSG:3396</SRS>
+ <SRS>EPSG:3397</SRS>
+ <SRS>EPSG:3398</SRS>
+ <SRS>EPSG:3399</SRS>
+ <SRS>EPSG:3400</SRS>
+ <SRS>EPSG:3401</SRS>
+ <SRS>EPSG:3402</SRS>
+ <SRS>EPSG:3403</SRS>
+ <SRS>EPSG:3404</SRS>
+ <SRS>EPSG:3405</SRS>
+ <SRS>EPSG:3406</SRS>
+ <SRS>EPSG:3407</SRS>
+ <SRS>EPSG:3408</SRS>
+ <SRS>EPSG:3409</SRS>
+ <SRS>EPSG:3410</SRS>
+ <SRS>EPSG:3411</SRS>
+ <SRS>EPSG:3412</SRS>
+ <SRS>EPSG:3413</SRS>
+ <SRS>EPSG:3414</SRS>
+ <SRS>EPSG:3415</SRS>
+ <SRS>EPSG:3416</SRS>
+ <SRS>EPSG:3417</SRS>
+ <SRS>EPSG:3418</SRS>
+ <SRS>EPSG:3419</SRS>
+ <SRS>EPSG:3420</SRS>
+ <SRS>EPSG:3421</SRS>
+ <SRS>EPSG:3422</SRS>
+ <SRS>EPSG:3423</SRS>
+ <SRS>EPSG:3424</SRS>
+ <SRS>EPSG:3425</SRS>
+ <SRS>EPSG:3426</SRS>
+ <SRS>EPSG:3427</SRS>
+ <SRS>EPSG:3428</SRS>
+ <SRS>EPSG:3429</SRS>
+ <SRS>EPSG:3430</SRS>
+ <SRS>EPSG:3431</SRS>
+ <SRS>EPSG:3432</SRS>
+ <SRS>EPSG:3433</SRS>
+ <SRS>EPSG:3434</SRS>
+ <SRS>EPSG:3435</SRS>
+ <SRS>EPSG:3436</SRS>
+ <SRS>EPSG:3437</SRS>
+ <SRS>EPSG:3438</SRS>
+ <SRS>EPSG:3439</SRS>
+ <SRS>EPSG:3440</SRS>
+ <SRS>EPSG:3441</SRS>
+ <SRS>EPSG:3442</SRS>
+ <SRS>EPSG:3443</SRS>
+ <SRS>EPSG:3444</SRS>
+ <SRS>EPSG:3445</SRS>
+ <SRS>EPSG:3446</SRS>
+ <SRS>EPSG:3447</SRS>
+ <SRS>EPSG:3448</SRS>
+ <SRS>EPSG:3449</SRS>
+ <SRS>EPSG:3450</SRS>
+ <SRS>EPSG:3451</SRS>
+ <SRS>EPSG:3452</SRS>
+ <SRS>EPSG:3453</SRS>
+ <SRS>EPSG:3454</SRS>
+ <SRS>EPSG:3455</SRS>
+ <SRS>EPSG:3456</SRS>
+ <SRS>EPSG:3457</SRS>
+ <SRS>EPSG:3458</SRS>
+ <SRS>EPSG:3459</SRS>
+ <SRS>EPSG:3460</SRS>
+ <SRS>EPSG:3461</SRS>
+ <SRS>EPSG:3462</SRS>
+ <SRS>EPSG:3463</SRS>
+ <SRS>EPSG:3464</SRS>
+ <SRS>EPSG:3560</SRS>
+ <SRS>EPSG:3561</SRS>
+ <SRS>EPSG:3562</SRS>
+ <SRS>EPSG:3563</SRS>
+ <SRS>EPSG:3564</SRS>
+ <SRS>EPSG:3565</SRS>
+ <SRS>EPSG:3566</SRS>
+ <SRS>EPSG:3567</SRS>
+ <SRS>EPSG:3568</SRS>
+ <SRS>EPSG:3569</SRS>
+ <SRS>EPSG:3570</SRS>
+ <SRS>EPSG:3571</SRS>
+ <SRS>EPSG:3572</SRS>
+ <SRS>EPSG:3573</SRS>
+ <SRS>EPSG:3574</SRS>
+ <SRS>EPSG:3575</SRS>
+ <SRS>EPSG:3576</SRS>
+ <SRS>EPSG:3577</SRS>
+ <SRS>EPSG:3920</SRS>
+ <SRS>EPSG:3991</SRS>
+ <SRS>EPSG:3992</SRS>
+ <SRS>EPSG:3993</SRS>
+ <SRS>EPSG:4001</SRS>
+ <SRS>EPSG:4002</SRS>
+ <SRS>EPSG:4003</SRS>
+ <SRS>EPSG:4004</SRS>
+ <SRS>EPSG:4005</SRS>
+ <SRS>EPSG:4006</SRS>
+ <SRS>EPSG:4007</SRS>
+ <SRS>EPSG:4008</SRS>
+ <SRS>EPSG:4009</SRS>
+ <SRS>EPSG:4010</SRS>
+ <SRS>EPSG:4011</SRS>
+ <SRS>EPSG:4012</SRS>
+ <SRS>EPSG:4013</SRS>
+ <SRS>EPSG:4014</SRS>
+ <SRS>EPSG:4015</SRS>
+ <SRS>EPSG:4016</SRS>
+ <SRS>EPSG:4018</SRS>
+ <SRS>EPSG:4019</SRS>
+ <SRS>EPSG:4020</SRS>
+ <SRS>EPSG:4021</SRS>
+ <SRS>EPSG:4022</SRS>
+ <SRS>EPSG:4024</SRS>
+ <SRS>EPSG:4025</SRS>
+ <SRS>EPSG:4027</SRS>
+ <SRS>EPSG:4028</SRS>
+ <SRS>EPSG:4029</SRS>
+ <SRS>EPSG:4030</SRS>
+ <SRS>EPSG:4031</SRS>
+ <SRS>EPSG:4032</SRS>
+ <SRS>EPSG:4033</SRS>
+ <SRS>EPSG:4034</SRS>
+ <SRS>EPSG:4035</SRS>
+ <SRS>EPSG:4036</SRS>
+ <SRS>EPSG:4041</SRS>
+ <SRS>EPSG:4042</SRS>
+ <SRS>EPSG:4043</SRS>
+ <SRS>EPSG:4044</SRS>
+ <SRS>EPSG:4045</SRS>
+ <SRS>EPSG:4047</SRS>
+ <SRS>EPSG:4052</SRS>
+ <SRS>EPSG:4053</SRS>
+ <SRS>EPSG:4054</SRS>
+ <SRS>EPSG:4120</SRS>
+ <SRS>EPSG:4121</SRS>
+ <SRS>EPSG:4122</SRS>
+ <SRS>EPSG:4123</SRS>
+ <SRS>EPSG:4124</SRS>
+ <SRS>EPSG:4125</SRS>
+ <SRS>EPSG:4126</SRS>
+ <SRS>EPSG:4127</SRS>
+ <SRS>EPSG:4128</SRS>
+ <SRS>EPSG:4129</SRS>
+ <SRS>EPSG:4130</SRS>
+ <SRS>EPSG:4131</SRS>
+ <SRS>EPSG:4132</SRS>
+ <SRS>EPSG:4133</SRS>
+ <SRS>EPSG:4134</SRS>
+ <SRS>EPSG:4135</SRS>
+ <SRS>EPSG:4136</SRS>
+ <SRS>EPSG:4137</SRS>
+ <SRS>EPSG:4138</SRS>
+ <SRS>EPSG:4139</SRS>
+ <SRS>EPSG:4140</SRS>
+ <SRS>EPSG:4141</SRS>
+ <SRS>EPSG:4142</SRS>
+ <SRS>EPSG:4143</SRS>
+ <SRS>EPSG:4144</SRS>
+ <SRS>EPSG:4145</SRS>
+ <SRS>EPSG:4146</SRS>
+ <SRS>EPSG:4147</SRS>
+ <SRS>EPSG:4148</SRS>
+ <SRS>EPSG:4149</SRS>
+ <SRS>EPSG:4150</SRS>
+ <SRS>EPSG:4151</SRS>
+ <SRS>EPSG:4152</SRS>
+ <SRS>EPSG:4153</SRS>
+ <SRS>EPSG:4154</SRS>
+ <SRS>EPSG:4155</SRS>
+ <SRS>EPSG:4156</SRS>
+ <SRS>EPSG:4157</SRS>
+ <SRS>EPSG:4158</SRS>
+ <SRS>EPSG:4159</SRS>
+ <SRS>EPSG:4160</SRS>
+ <SRS>EPSG:4161</SRS>
+ <SRS>EPSG:4162</SRS>
+ <SRS>EPSG:4163</SRS>
+ <SRS>EPSG:4164</SRS>
+ <SRS>EPSG:4165</SRS>
+ <SRS>EPSG:4166</SRS>
+ <SRS>EPSG:4167</SRS>
+ <SRS>EPSG:4168</SRS>
+ <SRS>EPSG:4169</SRS>
+ <SRS>EPSG:4170</SRS>
+ <SRS>EPSG:4171</SRS>
+ <SRS>EPSG:4172</SRS>
+ <SRS>EPSG:4173</SRS>
+ <SRS>EPSG:4174</SRS>
+ <SRS>EPSG:4175</SRS>
+ <SRS>EPSG:4176</SRS>
+ <SRS>EPSG:4178</SRS>
+ <SRS>EPSG:4179</SRS>
+ <SRS>EPSG:4180</SRS>
+ <SRS>EPSG:4181</SRS>
+ <SRS>EPSG:4182</SRS>
+ <SRS>EPSG:4183</SRS>
+ <SRS>EPSG:4184</SRS>
+ <SRS>EPSG:4185</SRS>
+ <SRS>EPSG:4188</SRS>
+ <SRS>EPSG:4189</SRS>
+ <SRS>EPSG:4190</SRS>
+ <SRS>EPSG:4191</SRS>
+ <SRS>EPSG:4192</SRS>
+ <SRS>EPSG:4193</SRS>
+ <SRS>EPSG:4194</SRS>
+ <SRS>EPSG:4195</SRS>
+ <SRS>EPSG:4196</SRS>
+ <SRS>EPSG:4197</SRS>
+ <SRS>EPSG:4198</SRS>
+ <SRS>EPSG:4199</SRS>
+ <SRS>EPSG:4200</SRS>
+ <SRS>EPSG:4201</SRS>
+ <SRS>EPSG:4202</SRS>
+ <SRS>EPSG:4203</SRS>
+ <SRS>EPSG:4204</SRS>
+ <SRS>EPSG:4205</SRS>
+ <SRS>EPSG:4206</SRS>
+ <SRS>EPSG:4207</SRS>
+ <SRS>EPSG:4208</SRS>
+ <SRS>EPSG:4209</SRS>
+ <SRS>EPSG:4210</SRS>
+ <SRS>EPSG:4211</SRS>
+ <SRS>EPSG:4212</SRS>
+ <SRS>EPSG:4213</SRS>
+ <SRS>EPSG:4214</SRS>
+ <SRS>EPSG:4215</SRS>
+ <SRS>EPSG:4216</SRS>
+ <SRS>EPSG:4218</SRS>
+ <SRS>EPSG:4219</SRS>
+ <SRS>EPSG:4220</SRS>
+ <SRS>EPSG:4221</SRS>
+ <SRS>EPSG:4222</SRS>
+ <SRS>EPSG:4223</SRS>
+ <SRS>EPSG:4224</SRS>
+ <SRS>EPSG:4225</SRS>
+ <SRS>EPSG:4226</SRS>
+ <SRS>EPSG:4227</SRS>
+ <SRS>EPSG:4228</SRS>
+ <SRS>EPSG:4229</SRS>
+ <SRS>EPSG:4230</SRS>
+ <SRS>EPSG:4231</SRS>
+ <SRS>EPSG:4232</SRS>
+ <SRS>EPSG:4233</SRS>
+ <SRS>EPSG:4234</SRS>
+ <SRS>EPSG:4235</SRS>
+ <SRS>EPSG:4236</SRS>
+ <SRS>EPSG:4237</SRS>
+ <SRS>EPSG:4238</SRS>
+ <SRS>EPSG:4239</SRS>
+ <SRS>EPSG:4240</SRS>
+ <SRS>EPSG:4241</SRS>
+ <SRS>EPSG:4242</SRS>
+ <SRS>EPSG:4243</SRS>
+ <SRS>EPSG:4244</SRS>
+ <SRS>EPSG:4245</SRS>
+ <SRS>EPSG:4246</SRS>
+ <SRS>EPSG:4247</SRS>
+ <SRS>EPSG:4248</SRS>
+ <SRS>EPSG:4249</SRS>
+ <SRS>EPSG:4250</SRS>
+ <SRS>EPSG:4251</SRS>
+ <SRS>EPSG:4252</SRS>
+ <SRS>EPSG:4253</SRS>
+ <SRS>EPSG:4254</SRS>
+ <SRS>EPSG:4255</SRS>
+ <SRS>EPSG:4256</SRS>
+ <SRS>EPSG:4257</SRS>
+ <SRS>EPSG:4258</SRS>
+ <SRS>EPSG:4259</SRS>
+ <SRS>EPSG:4260</SRS>
+ <SRS>EPSG:4261</SRS>
+ <SRS>EPSG:4262</SRS>
+ <SRS>EPSG:4263</SRS>
+ <SRS>EPSG:4264</SRS>
+ <SRS>EPSG:4265</SRS>
+ <SRS>EPSG:4266</SRS>
+ <SRS>EPSG:4267</SRS>
+ <SRS>EPSG:4268</SRS>
+ <SRS>EPSG:4269</SRS>
+ <SRS>EPSG:4270</SRS>
+ <SRS>EPSG:4271</SRS>
+ <SRS>EPSG:4272</SRS>
+ <SRS>EPSG:4273</SRS>
+ <SRS>EPSG:4274</SRS>
+ <SRS>EPSG:4275</SRS>
+ <SRS>EPSG:4276</SRS>
+ <SRS>EPSG:4277</SRS>
+ <SRS>EPSG:4278</SRS>
+ <SRS>EPSG:4279</SRS>
+ <SRS>EPSG:4280</SRS>
+ <SRS>EPSG:4281</SRS>
+ <SRS>EPSG:4282</SRS>
+ <SRS>EPSG:4283</SRS>
+ <SRS>EPSG:4284</SRS>
+ <SRS>EPSG:4285</SRS>
+ <SRS>EPSG:4286</SRS>
+ <SRS>EPSG:4287</SRS>
+ <SRS>EPSG:4288</SRS>
+ <SRS>EPSG:4289</SRS>
+ <SRS>EPSG:4291</SRS>
+ <SRS>EPSG:4292</SRS>
+ <SRS>EPSG:4293</SRS>
+ <SRS>EPSG:4294</SRS>
+ <SRS>EPSG:4295</SRS>
+ <SRS>EPSG:4296</SRS>
+ <SRS>EPSG:4297</SRS>
+ <SRS>EPSG:4298</SRS>
+ <SRS>EPSG:4299</SRS>
+ <SRS>EPSG:4300</SRS>
+ <SRS>EPSG:4301</SRS>
+ <SRS>EPSG:4302</SRS>
+ <SRS>EPSG:4303</SRS>
+ <SRS>EPSG:4304</SRS>
+ <SRS>EPSG:4306</SRS>
+ <SRS>EPSG:4307</SRS>
+ <SRS>EPSG:4308</SRS>
+ <SRS>EPSG:4309</SRS>
+ <SRS>EPSG:4310</SRS>
+ <SRS>EPSG:4311</SRS>
+ <SRS>EPSG:4312</SRS>
+ <SRS>EPSG:4313</SRS>
+ <SRS>EPSG:4314</SRS>
+ <SRS>EPSG:4315</SRS>
+ <SRS>EPSG:4316</SRS>
+ <SRS>EPSG:4317</SRS>
+ <SRS>EPSG:4318</SRS>
+ <SRS>EPSG:4319</SRS>
+ <SRS>EPSG:4322</SRS>
+ <SRS>EPSG:4324</SRS>
+ <SRS>EPSG:4326</SRS>
+ <SRS>EPSG:4327</SRS>
+ <SRS>EPSG:4328</SRS>
+ <SRS>EPSG:4329</SRS>
+ <SRS>EPSG:4330</SRS>
+ <SRS>EPSG:4331</SRS>
+ <SRS>EPSG:4332</SRS>
+ <SRS>EPSG:4333</SRS>
+ <SRS>EPSG:4334</SRS>
+ <SRS>EPSG:4335</SRS>
+ <SRS>EPSG:4336</SRS>
+ <SRS>EPSG:4337</SRS>
+ <SRS>EPSG:4338</SRS>
+ <SRS>EPSG:4339</SRS>
+ <SRS>EPSG:4340</SRS>
+ <SRS>EPSG:4341</SRS>
+ <SRS>EPSG:4342</SRS>
+ <SRS>EPSG:4343</SRS>
+ <SRS>EPSG:4344</SRS>
+ <SRS>EPSG:4345</SRS>
+ <SRS>EPSG:4346</SRS>
+ <SRS>EPSG:4347</SRS>
+ <SRS>EPSG:4348</SRS>
+ <SRS>EPSG:4349</SRS>
+ <SRS>EPSG:4350</SRS>
+ <SRS>EPSG:4351</SRS>
+ <SRS>EPSG:4352</SRS>
+ <SRS>EPSG:4353</SRS>
+ <SRS>EPSG:4354</SRS>
+ <SRS>EPSG:4355</SRS>
+ <SRS>EPSG:4356</SRS>
+ <SRS>EPSG:4357</SRS>
+ <SRS>EPSG:4358</SRS>
+ <SRS>EPSG:4359</SRS>
+ <SRS>EPSG:4360</SRS>
+ <SRS>EPSG:4361</SRS>
+ <SRS>EPSG:4362</SRS>
+ <SRS>EPSG:4363</SRS>
+ <SRS>EPSG:4364</SRS>
+ <SRS>EPSG:4365</SRS>
+ <SRS>EPSG:4366</SRS>
+ <SRS>EPSG:4367</SRS>
+ <SRS>EPSG:4368</SRS>
+ <SRS>EPSG:4369</SRS>
+ <SRS>EPSG:4370</SRS>
+ <SRS>EPSG:4371</SRS>
+ <SRS>EPSG:4372</SRS>
+ <SRS>EPSG:4373</SRS>
+ <SRS>EPSG:4374</SRS>
+ <SRS>EPSG:4375</SRS>
+ <SRS>EPSG:4376</SRS>
+ <SRS>EPSG:4377</SRS>
+ <SRS>EPSG:4378</SRS>
+ <SRS>EPSG:4379</SRS>
+ <SRS>EPSG:4380</SRS>
+ <SRS>EPSG:4381</SRS>
+ <SRS>EPSG:4382</SRS>
+ <SRS>EPSG:4383</SRS>
+ <SRS>EPSG:4384</SRS>
+ <SRS>EPSG:4385</SRS>
+ <SRS>EPSG:4386</SRS>
+ <SRS>EPSG:4387</SRS>
+ <SRS>EPSG:4388</SRS>
+ <SRS>EPSG:4389</SRS>
+ <SRS>EPSG:4600</SRS>
+ <SRS>EPSG:4601</SRS>
+ <SRS>EPSG:4602</SRS>
+ <SRS>EPSG:4603</SRS>
+ <SRS>EPSG:4604</SRS>
+ <SRS>EPSG:4605</SRS>
+ <SRS>EPSG:4606</SRS>
+ <SRS>EPSG:4607</SRS>
+ <SRS>EPSG:4608</SRS>
+ <SRS>EPSG:4609</SRS>
+ <SRS>EPSG:4610</SRS>
+ <SRS>EPSG:4611</SRS>
+ <SRS>EPSG:4612</SRS>
+ <SRS>EPSG:4613</SRS>
+ <SRS>EPSG:4614</SRS>
+ <SRS>EPSG:4615</SRS>
+ <SRS>EPSG:4616</SRS>
+ <SRS>EPSG:4617</SRS>
+ <SRS>EPSG:4618</SRS>
+ <SRS>EPSG:4619</SRS>
+ <SRS>EPSG:4620</SRS>
+ <SRS>EPSG:4621</SRS>
+ <SRS>EPSG:4622</SRS>
+ <SRS>EPSG:4623</SRS>
+ <SRS>EPSG:4624</SRS>
+ <SRS>EPSG:4625</SRS>
+ <SRS>EPSG:4626</SRS>
+ <SRS>EPSG:4627</SRS>
+ <SRS>EPSG:4628</SRS>
+ <SRS>EPSG:4629</SRS>
+ <SRS>EPSG:4630</SRS>
+ <SRS>EPSG:4631</SRS>
+ <SRS>EPSG:4632</SRS>
+ <SRS>EPSG:4633</SRS>
+ <SRS>EPSG:4634</SRS>
+ <SRS>EPSG:4635</SRS>
+ <SRS>EPSG:4636</SRS>
+ <SRS>EPSG:4637</SRS>
+ <SRS>EPSG:4638</SRS>
+ <SRS>EPSG:4639</SRS>
+ <SRS>EPSG:4640</SRS>
+ <SRS>EPSG:4641</SRS>
+ <SRS>EPSG:4642</SRS>
+ <SRS>EPSG:4643</SRS>
+ <SRS>EPSG:4644</SRS>
+ <SRS>EPSG:4645</SRS>
+ <SRS>EPSG:4646</SRS>
+ <SRS>EPSG:4657</SRS>
+ <SRS>EPSG:4658</SRS>
+ <SRS>EPSG:4659</SRS>
+ <SRS>EPSG:4660</SRS>
+ <SRS>EPSG:4661</SRS>
+ <SRS>EPSG:4662</SRS>
+ <SRS>EPSG:4663</SRS>
+ <SRS>EPSG:4664</SRS>
+ <SRS>EPSG:4665</SRS>
+ <SRS>EPSG:4666</SRS>
+ <SRS>EPSG:4667</SRS>
+ <SRS>EPSG:4668</SRS>
+ <SRS>EPSG:4669</SRS>
+ <SRS>EPSG:4670</SRS>
+ <SRS>EPSG:4671</SRS>
+ <SRS>EPSG:4672</SRS>
+ <SRS>EPSG:4673</SRS>
+ <SRS>EPSG:4674</SRS>
+ <SRS>EPSG:4675</SRS>
+ <SRS>EPSG:4676</SRS>
+ <SRS>EPSG:4677</SRS>
+ <SRS>EPSG:4678</SRS>
+ <SRS>EPSG:4679</SRS>
+ <SRS>EPSG:4680</SRS>
+ <SRS>EPSG:4681</SRS>
+ <SRS>EPSG:4682</SRS>
+ <SRS>EPSG:4683</SRS>
+ <SRS>EPSG:4684</SRS>
+ <SRS>EPSG:4685</SRS>
+ <SRS>EPSG:4686</SRS>
+ <SRS>EPSG:4687</SRS>
+ <SRS>EPSG:4688</SRS>
+ <SRS>EPSG:4689</SRS>
+ <SRS>EPSG:4690</SRS>
+ <SRS>EPSG:4691</SRS>
+ <SRS>EPSG:4692</SRS>
+ <SRS>EPSG:4693</SRS>
+ <SRS>EPSG:4694</SRS>
+ <SRS>EPSG:4695</SRS>
+ <SRS>EPSG:4696</SRS>
+ <SRS>EPSG:4697</SRS>
+ <SRS>EPSG:4698</SRS>
+ <SRS>EPSG:4699</SRS>
+ <SRS>EPSG:4700</SRS>
+ <SRS>EPSG:4701</SRS>
+ <SRS>EPSG:4702</SRS>
+ <SRS>EPSG:4703</SRS>
+ <SRS>EPSG:4704</SRS>
+ <SRS>EPSG:4705</SRS>
+ <SRS>EPSG:4706</SRS>
+ <SRS>EPSG:4707</SRS>
+ <SRS>EPSG:4708</SRS>
+ <SRS>EPSG:4709</SRS>
+ <SRS>EPSG:4710</SRS>
+ <SRS>EPSG:4711</SRS>
+ <SRS>EPSG:4712</SRS>
+ <SRS>EPSG:4713</SRS>
+ <SRS>EPSG:4714</SRS>
+ <SRS>EPSG:4715</SRS>
+ <SRS>EPSG:4716</SRS>
+ <SRS>EPSG:4717</SRS>
+ <SRS>EPSG:4718</SRS>
+ <SRS>EPSG:4719</SRS>
+ <SRS>EPSG:4720</SRS>
+ <SRS>EPSG:4721</SRS>
+ <SRS>EPSG:4722</SRS>
+ <SRS>EPSG:4723</SRS>
+ <SRS>EPSG:4724</SRS>
+ <SRS>EPSG:4725</SRS>
+ <SRS>EPSG:4726</SRS>
+ <SRS>EPSG:4727</SRS>
+ <SRS>EPSG:4728</SRS>
+ <SRS>EPSG:4729</SRS>
+ <SRS>EPSG:4730</SRS>
+ <SRS>EPSG:4731</SRS>
+ <SRS>EPSG:4732</SRS>
+ <SRS>EPSG:4733</SRS>
+ <SRS>EPSG:4734</SRS>
+ <SRS>EPSG:4735</SRS>
+ <SRS>EPSG:4736</SRS>
+ <SRS>EPSG:4737</SRS>
+ <SRS>EPSG:4738</SRS>
+ <SRS>EPSG:4739</SRS>
+ <SRS>EPSG:4740</SRS>
+ <SRS>EPSG:4741</SRS>
+ <SRS>EPSG:4742</SRS>
+ <SRS>EPSG:4743</SRS>
+ <SRS>EPSG:4744</SRS>
+ <SRS>EPSG:4745</SRS>
+ <SRS>EPSG:4746</SRS>
+ <SRS>EPSG:4747</SRS>
+ <SRS>EPSG:4748</SRS>
+ <SRS>EPSG:4749</SRS>
+ <SRS>EPSG:4750</SRS>
+ <SRS>EPSG:4751</SRS>
+ <SRS>EPSG:4752</SRS>
+ <SRS>EPSG:4753</SRS>
+ <SRS>EPSG:4754</SRS>
+ <SRS>EPSG:4755</SRS>
+ <SRS>EPSG:4756</SRS>
+ <SRS>EPSG:4757</SRS>
+ <SRS>EPSG:4758</SRS>
+ <SRS>EPSG:4801</SRS>
+ <SRS>EPSG:4802</SRS>
+ <SRS>EPSG:4803</SRS>
+ <SRS>EPSG:4804</SRS>
+ <SRS>EPSG:4805</SRS>
+ <SRS>EPSG:4806</SRS>
+ <SRS>EPSG:4807</SRS>
+ <SRS>EPSG:4808</SRS>
+ <SRS>EPSG:4809</SRS>
+ <SRS>EPSG:4810</SRS>
+ <SRS>EPSG:4811</SRS>
+ <SRS>EPSG:4813</SRS>
+ <SRS>EPSG:4814</SRS>
+ <SRS>EPSG:4815</SRS>
+ <SRS>EPSG:4816</SRS>
+ <SRS>EPSG:4817</SRS>
+ <SRS>EPSG:4818</SRS>
+ <SRS>EPSG:4819</SRS>
+ <SRS>EPSG:4820</SRS>
+ <SRS>EPSG:4821</SRS>
+ <SRS>EPSG:4894</SRS>
+ <SRS>EPSG:4895</SRS>
+ <SRS>EPSG:4896</SRS>
+ <SRS>EPSG:4897</SRS>
+ <SRS>EPSG:4898</SRS>
+ <SRS>EPSG:4899</SRS>
+ <SRS>EPSG:4900</SRS>
+ <SRS>EPSG:4901</SRS>
+ <SRS>EPSG:4902</SRS>
+ <SRS>EPSG:4903</SRS>
+ <SRS>EPSG:4904</SRS>
+ <SRS>EPSG:4906</SRS>
+ <SRS>EPSG:4907</SRS>
+ <SRS>EPSG:4908</SRS>
+ <SRS>EPSG:4909</SRS>
+ <SRS>EPSG:4910</SRS>
+ <SRS>EPSG:4911</SRS>
+ <SRS>EPSG:4912</SRS>
+ <SRS>EPSG:4913</SRS>
+ <SRS>EPSG:4914</SRS>
+ <SRS>EPSG:4915</SRS>
+ <SRS>EPSG:4916</SRS>
+ <SRS>EPSG:4917</SRS>
+ <SRS>EPSG:4918</SRS>
+ <SRS>EPSG:4919</SRS>
+ <SRS>EPSG:4920</SRS>
+ <SRS>EPSG:4921</SRS>
+ <SRS>EPSG:4922</SRS>
+ <SRS>EPSG:4923</SRS>
+ <SRS>EPSG:4924</SRS>
+ <SRS>EPSG:4925</SRS>
+ <SRS>EPSG:4926</SRS>
+ <SRS>EPSG:4927</SRS>
+ <SRS>EPSG:4928</SRS>
+ <SRS>EPSG:4929</SRS>
+ <SRS>EPSG:4930</SRS>
+ <SRS>EPSG:4931</SRS>
+ <SRS>EPSG:4932</SRS>
+ <SRS>EPSG:4933</SRS>
+ <SRS>EPSG:4934</SRS>
+ <SRS>EPSG:4935</SRS>
+ <SRS>EPSG:4936</SRS>
+ <SRS>EPSG:4937</SRS>
+ <SRS>EPSG:4938</SRS>
+ <SRS>EPSG:4939</SRS>
+ <SRS>EPSG:4940</SRS>
+ <SRS>EPSG:4941</SRS>
+ <SRS>EPSG:4942</SRS>
+ <SRS>EPSG:4943</SRS>
+ <SRS>EPSG:4944</SRS>
+ <SRS>EPSG:4945</SRS>
+ <SRS>EPSG:4946</SRS>
+ <SRS>EPSG:4947</SRS>
+ <SRS>EPSG:4948</SRS>
+ <SRS>EPSG:4949</SRS>
+ <SRS>EPSG:4950</SRS>
+ <SRS>EPSG:4951</SRS>
+ <SRS>EPSG:4952</SRS>
+ <SRS>EPSG:4953</SRS>
+ <SRS>EPSG:4954</SRS>
+ <SRS>EPSG:4955</SRS>
+ <SRS>EPSG:4956</SRS>
+ <SRS>EPSG:4957</SRS>
+ <SRS>EPSG:4958</SRS>
+ <SRS>EPSG:4959</SRS>
+ <SRS>EPSG:4960</SRS>
+ <SRS>EPSG:4961</SRS>
+ <SRS>EPSG:4962</SRS>
+ <SRS>EPSG:4963</SRS>
+ <SRS>EPSG:4964</SRS>
+ <SRS>EPSG:4965</SRS>
+ <SRS>EPSG:4966</SRS>
+ <SRS>EPSG:4967</SRS>
+ <SRS>EPSG:4968</SRS>
+ <SRS>EPSG:4969</SRS>
+ <SRS>EPSG:4970</SRS>
+ <SRS>EPSG:4971</SRS>
+ <SRS>EPSG:4972</SRS>
+ <SRS>EPSG:4973</SRS>
+ <SRS>EPSG:4974</SRS>
+ <SRS>EPSG:4975</SRS>
+ <SRS>EPSG:4976</SRS>
+ <SRS>EPSG:4977</SRS>
+ <SRS>EPSG:4978</SRS>
+ <SRS>EPSG:4979</SRS>
+ <SRS>EPSG:4980</SRS>
+ <SRS>EPSG:4981</SRS>
+ <SRS>EPSG:4982</SRS>
+ <SRS>EPSG:4983</SRS>
+ <SRS>EPSG:4984</SRS>
+ <SRS>EPSG:4985</SRS>
+ <SRS>EPSG:4986</SRS>
+ <SRS>EPSG:4987</SRS>
+ <SRS>EPSG:4988</SRS>
+ <SRS>EPSG:4989</SRS>
+ <SRS>EPSG:4990</SRS>
+ <SRS>EPSG:4991</SRS>
+ <SRS>EPSG:4992</SRS>
+ <SRS>EPSG:4993</SRS>
+ <SRS>EPSG:4994</SRS>
+ <SRS>EPSG:4995</SRS>
+ <SRS>EPSG:4996</SRS>
+ <SRS>EPSG:4997</SRS>
+ <SRS>EPSG:4998</SRS>
+ <SRS>EPSG:4999</SRS>
+ <SRS>EPSG:5600</SRS>
+ <SRS>EPSG:5601</SRS>
+ <SRS>EPSG:5602</SRS>
+ <SRS>EPSG:5603</SRS>
+ <SRS>EPSG:5604</SRS>
+ <SRS>EPSG:5605</SRS>
+ <SRS>EPSG:5606</SRS>
+ <SRS>EPSG:5607</SRS>
+ <SRS>EPSG:5608</SRS>
+ <SRS>EPSG:5609</SRS>
+ <SRS>EPSG:5701</SRS>
+ <SRS>EPSG:5702</SRS>
+ <SRS>EPSG:5703</SRS>
+ <SRS>EPSG:5704</SRS>
+ <SRS>EPSG:5705</SRS>
+ <SRS>EPSG:5706</SRS>
+ <SRS>EPSG:5709</SRS>
+ <SRS>EPSG:5710</SRS>
+ <SRS>EPSG:5711</SRS>
+ <SRS>EPSG:5712</SRS>
+ <SRS>EPSG:5713</SRS>
+ <SRS>EPSG:5714</SRS>
+ <SRS>EPSG:5715</SRS>
+ <SRS>EPSG:5716</SRS>
+ <SRS>EPSG:5717</SRS>
+ <SRS>EPSG:5718</SRS>
+ <SRS>EPSG:5719</SRS>
+ <SRS>EPSG:5720</SRS>
+ <SRS>EPSG:5721</SRS>
+ <SRS>EPSG:5722</SRS>
+ <SRS>EPSG:5723</SRS>
+ <SRS>EPSG:5724</SRS>
+ <SRS>EPSG:5725</SRS>
+ <SRS>EPSG:5726</SRS>
+ <SRS>EPSG:5727</SRS>
+ <SRS>EPSG:5728</SRS>
+ <SRS>EPSG:5729</SRS>
+ <SRS>EPSG:5730</SRS>
+ <SRS>EPSG:5731</SRS>
+ <SRS>EPSG:5732</SRS>
+ <SRS>EPSG:5733</SRS>
+ <SRS>EPSG:5734</SRS>
+ <SRS>EPSG:5735</SRS>
+ <SRS>EPSG:5736</SRS>
+ <SRS>EPSG:5737</SRS>
+ <SRS>EPSG:5738</SRS>
+ <SRS>EPSG:5739</SRS>
+ <SRS>EPSG:5740</SRS>
+ <SRS>EPSG:5741</SRS>
+ <SRS>EPSG:5742</SRS>
+ <SRS>EPSG:5743</SRS>
+ <SRS>EPSG:5744</SRS>
+ <SRS>EPSG:5745</SRS>
+ <SRS>EPSG:5746</SRS>
+ <SRS>EPSG:5747</SRS>
+ <SRS>EPSG:5748</SRS>
+ <SRS>EPSG:5749</SRS>
+ <SRS>EPSG:5750</SRS>
+ <SRS>EPSG:5751</SRS>
+ <SRS>EPSG:5752</SRS>
+ <SRS>EPSG:5753</SRS>
+ <SRS>EPSG:5754</SRS>
+ <SRS>EPSG:5755</SRS>
+ <SRS>EPSG:5756</SRS>
+ <SRS>EPSG:5757</SRS>
+ <SRS>EPSG:5758</SRS>
+ <SRS>EPSG:5759</SRS>
+ <SRS>EPSG:5760</SRS>
+ <SRS>EPSG:5761</SRS>
+ <SRS>EPSG:5762</SRS>
+ <SRS>EPSG:5763</SRS>
+ <SRS>EPSG:5764</SRS>
+ <SRS>EPSG:5765</SRS>
+ <SRS>EPSG:5766</SRS>
+ <SRS>EPSG:5767</SRS>
+ <SRS>EPSG:5768</SRS>
+ <SRS>EPSG:5769</SRS>
+ <SRS>EPSG:5770</SRS>
+ <SRS>EPSG:5771</SRS>
+ <SRS>EPSG:5772</SRS>
+ <SRS>EPSG:5773</SRS>
+ <SRS>EPSG:5774</SRS>
+ <SRS>EPSG:5775</SRS>
+ <SRS>EPSG:5776</SRS>
+ <SRS>EPSG:5777</SRS>
+ <SRS>EPSG:5778</SRS>
+ <SRS>EPSG:5779</SRS>
+ <SRS>EPSG:5780</SRS>
+ <SRS>EPSG:5781</SRS>
+ <SRS>EPSG:5782</SRS>
+ <SRS>EPSG:5783</SRS>
+ <SRS>EPSG:5784</SRS>
+ <SRS>EPSG:5785</SRS>
+ <SRS>EPSG:5786</SRS>
+ <SRS>EPSG:5787</SRS>
+ <SRS>EPSG:5788</SRS>
+ <SRS>EPSG:5789</SRS>
+ <SRS>EPSG:5790</SRS>
+ <SRS>EPSG:5791</SRS>
+ <SRS>EPSG:5792</SRS>
+ <SRS>EPSG:5793</SRS>
+ <SRS>EPSG:5794</SRS>
+ <SRS>EPSG:5795</SRS>
+ <SRS>EPSG:5796</SRS>
+ <SRS>EPSG:5797</SRS>
+ <SRS>EPSG:5798</SRS>
+ <SRS>EPSG:5799</SRS>
+ <SRS>EPSG:5800</SRS>
+ <SRS>EPSG:5801</SRS>
+ <SRS>EPSG:5802</SRS>
+ <SRS>EPSG:5803</SRS>
+ <SRS>EPSG:5804</SRS>
+ <SRS>EPSG:5805</SRS>
+ <SRS>EPSG:5806</SRS>
+ <SRS>EPSG:5807</SRS>
+ <SRS>EPSG:5808</SRS>
+ <SRS>EPSG:5809</SRS>
+ <SRS>EPSG:5810</SRS>
+ <SRS>EPSG:5811</SRS>
+ <SRS>EPSG:5812</SRS>
+ <SRS>EPSG:5813</SRS>
+ <SRS>EPSG:5814</SRS>
+ <SRS>EPSG:5815</SRS>
+ <SRS>EPSG:5816</SRS>
+ <SRS>EPSG:5817</SRS>
+ <SRS>EPSG:5818</SRS>
+ <SRS>EPSG:7400</SRS>
+ <SRS>EPSG:7401</SRS>
+ <SRS>EPSG:7402</SRS>
+ <SRS>EPSG:7403</SRS>
+ <SRS>EPSG:7404</SRS>
+ <SRS>EPSG:7405</SRS>
+ <SRS>EPSG:7406</SRS>
+ <SRS>EPSG:7407</SRS>
+ <SRS>EPSG:7408</SRS>
+ <SRS>EPSG:7409</SRS>
+ <SRS>EPSG:7410</SRS>
+ <SRS>EPSG:7411</SRS>
+ <SRS>EPSG:7412</SRS>
+ <SRS>EPSG:7413</SRS>
+ <SRS>EPSG:7414</SRS>
+ <SRS>EPSG:7415</SRS>
+ <SRS>EPSG:7416</SRS>
+ <SRS>EPSG:7417</SRS>
+ <SRS>EPSG:7418</SRS>
+ <SRS>EPSG:7419</SRS>
+ <SRS>EPSG:7420</SRS>
+ <SRS>EPSG:20004</SRS>
+ <SRS>EPSG:20005</SRS>
+ <SRS>EPSG:20006</SRS>
+ <SRS>EPSG:20007</SRS>
+ <SRS>EPSG:20008</SRS>
+ <SRS>EPSG:20009</SRS>
+ <SRS>EPSG:20010</SRS>
+ <SRS>EPSG:20011</SRS>
+ <SRS>EPSG:20012</SRS>
+ <SRS>EPSG:20013</SRS>
+ <SRS>EPSG:20014</SRS>
+ <SRS>EPSG:20015</SRS>
+ <SRS>EPSG:20016</SRS>
+ <SRS>EPSG:20017</SRS>
+ <SRS>EPSG:20018</SRS>
+ <SRS>EPSG:20019</SRS>
+ <SRS>EPSG:20020</SRS>
+ <SRS>EPSG:20021</SRS>
+ <SRS>EPSG:20022</SRS>
+ <SRS>EPSG:20023</SRS>
+ <SRS>EPSG:20024</SRS>
+ <SRS>EPSG:20025</SRS>
+ <SRS>EPSG:20026</SRS>
+ <SRS>EPSG:20027</SRS>
+ <SRS>EPSG:20028</SRS>
+ <SRS>EPSG:20029</SRS>
+ <SRS>EPSG:20030</SRS>
+ <SRS>EPSG:20031</SRS>
+ <SRS>EPSG:20032</SRS>
+ <SRS>EPSG:20064</SRS>
+ <SRS>EPSG:20065</SRS>
+ <SRS>EPSG:20066</SRS>
+ <SRS>EPSG:20067</SRS>
+ <SRS>EPSG:20068</SRS>
+ <SRS>EPSG:20069</SRS>
+ <SRS>EPSG:20070</SRS>
+ <SRS>EPSG:20071</SRS>
+ <SRS>EPSG:20072</SRS>
+ <SRS>EPSG:20073</SRS>
+ <SRS>EPSG:20074</SRS>
+ <SRS>EPSG:20075</SRS>
+ <SRS>EPSG:20076</SRS>
+ <SRS>EPSG:20077</SRS>
+ <SRS>EPSG:20078</SRS>
+ <SRS>EPSG:20079</SRS>
+ <SRS>EPSG:20080</SRS>
+ <SRS>EPSG:20081</SRS>
+ <SRS>EPSG:20082</SRS>
+ <SRS>EPSG:20083</SRS>
+ <SRS>EPSG:20084</SRS>
+ <SRS>EPSG:20085</SRS>
+ <SRS>EPSG:20086</SRS>
+ <SRS>EPSG:20087</SRS>
+ <SRS>EPSG:20088</SRS>
+ <SRS>EPSG:20089</SRS>
+ <SRS>EPSG:20090</SRS>
+ <SRS>EPSG:20091</SRS>
+ <SRS>EPSG:20092</SRS>
+ <SRS>EPSG:20135</SRS>
+ <SRS>EPSG:20136</SRS>
+ <SRS>EPSG:20137</SRS>
+ <SRS>EPSG:20138</SRS>
+ <SRS>EPSG:20248</SRS>
+ <SRS>EPSG:20249</SRS>
+ <SRS>EPSG:20250</SRS>
+ <SRS>EPSG:20251</SRS>
+ <SRS>EPSG:20252</SRS>
+ <SRS>EPSG:20253</SRS>
+ <SRS>EPSG:20254</SRS>
+ <SRS>EPSG:20255</SRS>
+ <SRS>EPSG:20256</SRS>
+ <SRS>EPSG:20257</SRS>
+ <SRS>EPSG:20258</SRS>
+ <SRS>EPSG:20348</SRS>
+ <SRS>EPSG:20349</SRS>
+ <SRS>EPSG:20350</SRS>
+ <SRS>EPSG:20351</SRS>
+ <SRS>EPSG:20352</SRS>
+ <SRS>EPSG:20353</SRS>
+ <SRS>EPSG:20354</SRS>
+ <SRS>EPSG:20355</SRS>
+ <SRS>EPSG:20356</SRS>
+ <SRS>EPSG:20357</SRS>
+ <SRS>EPSG:20358</SRS>
+ <SRS>EPSG:20436</SRS>
+ <SRS>EPSG:20437</SRS>
+ <SRS>EPSG:20438</SRS>
+ <SRS>EPSG:20439</SRS>
+ <SRS>EPSG:20440</SRS>
+ <SRS>EPSG:20499</SRS>
+ <SRS>EPSG:20538</SRS>
+ <SRS>EPSG:20539</SRS>
+ <SRS>EPSG:20790</SRS>
+ <SRS>EPSG:20791</SRS>
+ <SRS>EPSG:20822</SRS>
+ <SRS>EPSG:20823</SRS>
+ <SRS>EPSG:20824</SRS>
+ <SRS>EPSG:20934</SRS>
+ <SRS>EPSG:20935</SRS>
+ <SRS>EPSG:20936</SRS>
+ <SRS>EPSG:21035</SRS>
+ <SRS>EPSG:21036</SRS>
+ <SRS>EPSG:21037</SRS>
+ <SRS>EPSG:21095</SRS>
+ <SRS>EPSG:21096</SRS>
+ <SRS>EPSG:21097</SRS>
+ <SRS>EPSG:21100</SRS>
+ <SRS>EPSG:21148</SRS>
+ <SRS>EPSG:21149</SRS>
+ <SRS>EPSG:21150</SRS>
+ <SRS>EPSG:21291</SRS>
+ <SRS>EPSG:21292</SRS>
+ <SRS>EPSG:21413</SRS>
+ <SRS>EPSG:21414</SRS>
+ <SRS>EPSG:21415</SRS>
+ <SRS>EPSG:21416</SRS>
+ <SRS>EPSG:21417</SRS>
+ <SRS>EPSG:21418</SRS>
+ <SRS>EPSG:21419</SRS>
+ <SRS>EPSG:21420</SRS>
+ <SRS>EPSG:21421</SRS>
+ <SRS>EPSG:21422</SRS>
+ <SRS>EPSG:21423</SRS>
+ <SRS>EPSG:21453</SRS>
+ <SRS>EPSG:21454</SRS>
+ <SRS>EPSG:21455</SRS>
+ <SRS>EPSG:21456</SRS>
+ <SRS>EPSG:21457</SRS>
+ <SRS>EPSG:21458</SRS>
+ <SRS>EPSG:21459</SRS>
+ <SRS>EPSG:21460</SRS>
+ <SRS>EPSG:21461</SRS>
+ <SRS>EPSG:21462</SRS>
+ <SRS>EPSG:21463</SRS>
+ <SRS>EPSG:21473</SRS>
+ <SRS>EPSG:21474</SRS>
+ <SRS>EPSG:21475</SRS>
+ <SRS>EPSG:21476</SRS>
+ <SRS>EPSG:21477</SRS>
+ <SRS>EPSG:21478</SRS>
+ <SRS>EPSG:21479</SRS>
+ <SRS>EPSG:21480</SRS>
+ <SRS>EPSG:21481</SRS>
+ <SRS>EPSG:21482</SRS>
+ <SRS>EPSG:21483</SRS>
+ <SRS>EPSG:21500</SRS>
+ <SRS>EPSG:21780</SRS>
+ <SRS>EPSG:21781</SRS>
+ <SRS>EPSG:21817</SRS>
+ <SRS>EPSG:21818</SRS>
+ <SRS>EPSG:21891</SRS>
+ <SRS>EPSG:21892</SRS>
+ <SRS>EPSG:21893</SRS>
+ <SRS>EPSG:21894</SRS>
+ <SRS>EPSG:21896</SRS>
+ <SRS>EPSG:21897</SRS>
+ <SRS>EPSG:21898</SRS>
+ <SRS>EPSG:21899</SRS>
+ <SRS>EPSG:22032</SRS>
+ <SRS>EPSG:22033</SRS>
+ <SRS>EPSG:22091</SRS>
+ <SRS>EPSG:22092</SRS>
+ <SRS>EPSG:22171</SRS>
+ <SRS>EPSG:22172</SRS>
+ <SRS>EPSG:22173</SRS>
+ <SRS>EPSG:22174</SRS>
+ <SRS>EPSG:22175</SRS>
+ <SRS>EPSG:22176</SRS>
+ <SRS>EPSG:22177</SRS>
+ <SRS>EPSG:22181</SRS>
+ <SRS>EPSG:22182</SRS>
+ <SRS>EPSG:22183</SRS>
+ <SRS>EPSG:22184</SRS>
+ <SRS>EPSG:22185</SRS>
+ <SRS>EPSG:22186</SRS>
+ <SRS>EPSG:22187</SRS>
+ <SRS>EPSG:22191</SRS>
+ <SRS>EPSG:22192</SRS>
+ <SRS>EPSG:22193</SRS>
+ <SRS>EPSG:22194</SRS>
+ <SRS>EPSG:22195</SRS>
+ <SRS>EPSG:22196</SRS>
+ <SRS>EPSG:22197</SRS>
+ <SRS>EPSG:22234</SRS>
+ <SRS>EPSG:22235</SRS>
+ <SRS>EPSG:22236</SRS>
+ <SRS>EPSG:22275</SRS>
+ <SRS>EPSG:22277</SRS>
+ <SRS>EPSG:22279</SRS>
+ <SRS>EPSG:22281</SRS>
+ <SRS>EPSG:22283</SRS>
+ <SRS>EPSG:22285</SRS>
+ <SRS>EPSG:22287</SRS>
+ <SRS>EPSG:22289</SRS>
+ <SRS>EPSG:22291</SRS>
+ <SRS>EPSG:22293</SRS>
+ <SRS>EPSG:22300</SRS>
+ <SRS>EPSG:22332</SRS>
+ <SRS>EPSG:22391</SRS>
+ <SRS>EPSG:22392</SRS>
+ <SRS>EPSG:22521</SRS>
+ <SRS>EPSG:22522</SRS>
+ <SRS>EPSG:22523</SRS>
+ <SRS>EPSG:22524</SRS>
+ <SRS>EPSG:22525</SRS>
+ <SRS>EPSG:22700</SRS>
+ <SRS>EPSG:22770</SRS>
+ <SRS>EPSG:22780</SRS>
+ <SRS>EPSG:22832</SRS>
+ <SRS>EPSG:22991</SRS>
+ <SRS>EPSG:22992</SRS>
+ <SRS>EPSG:22993</SRS>
+ <SRS>EPSG:22994</SRS>
+ <SRS>EPSG:23028</SRS>
+ <SRS>EPSG:23029</SRS>
+ <SRS>EPSG:23030</SRS>
+ <SRS>EPSG:23031</SRS>
+ <SRS>EPSG:23032</SRS>
+ <SRS>EPSG:23033</SRS>
+ <SRS>EPSG:23034</SRS>
+ <SRS>EPSG:23035</SRS>
+ <SRS>EPSG:23036</SRS>
+ <SRS>EPSG:23037</SRS>
+ <SRS>EPSG:23038</SRS>
+ <SRS>EPSG:23090</SRS>
+ <SRS>EPSG:23095</SRS>
+ <SRS>EPSG:23239</SRS>
+ <SRS>EPSG:23240</SRS>
+ <SRS>EPSG:23433</SRS>
+ <SRS>EPSG:23700</SRS>
+ <SRS>EPSG:23846</SRS>
+ <SRS>EPSG:23847</SRS>
+ <SRS>EPSG:23848</SRS>
+ <SRS>EPSG:23849</SRS>
+ <SRS>EPSG:23850</SRS>
+ <SRS>EPSG:23851</SRS>
+ <SRS>EPSG:23852</SRS>
+ <SRS>EPSG:23853</SRS>
+ <SRS>EPSG:23866</SRS>
+ <SRS>EPSG:23867</SRS>
+ <SRS>EPSG:23868</SRS>
+ <SRS>EPSG:23869</SRS>
+ <SRS>EPSG:23870</SRS>
+ <SRS>EPSG:23871</SRS>
+ <SRS>EPSG:23872</SRS>
+ <SRS>EPSG:23877</SRS>
+ <SRS>EPSG:23878</SRS>
+ <SRS>EPSG:23879</SRS>
+ <SRS>EPSG:23880</SRS>
+ <SRS>EPSG:23881</SRS>
+ <SRS>EPSG:23882</SRS>
+ <SRS>EPSG:23883</SRS>
+ <SRS>EPSG:23884</SRS>
+ <SRS>EPSG:23886</SRS>
+ <SRS>EPSG:23887</SRS>
+ <SRS>EPSG:23888</SRS>
+ <SRS>EPSG:23889</SRS>
+ <SRS>EPSG:23890</SRS>
+ <SRS>EPSG:23891</SRS>
+ <SRS>EPSG:23892</SRS>
+ <SRS>EPSG:23893</SRS>
+ <SRS>EPSG:23894</SRS>
+ <SRS>EPSG:23946</SRS>
+ <SRS>EPSG:23947</SRS>
+ <SRS>EPSG:23948</SRS>
+ <SRS>EPSG:24047</SRS>
+ <SRS>EPSG:24048</SRS>
+ <SRS>EPSG:24100</SRS>
+ <SRS>EPSG:24200</SRS>
+ <SRS>EPSG:24305</SRS>
+ <SRS>EPSG:24306</SRS>
+ <SRS>EPSG:24311</SRS>
+ <SRS>EPSG:24312</SRS>
+ <SRS>EPSG:24313</SRS>
+ <SRS>EPSG:24342</SRS>
+ <SRS>EPSG:24343</SRS>
+ <SRS>EPSG:24344</SRS>
+ <SRS>EPSG:24345</SRS>
+ <SRS>EPSG:24346</SRS>
+ <SRS>EPSG:24347</SRS>
+ <SRS>EPSG:24370</SRS>
+ <SRS>EPSG:24371</SRS>
+ <SRS>EPSG:24372</SRS>
+ <SRS>EPSG:24373</SRS>
+ <SRS>EPSG:24374</SRS>
+ <SRS>EPSG:24375</SRS>
+ <SRS>EPSG:24376</SRS>
+ <SRS>EPSG:24377</SRS>
+ <SRS>EPSG:24378</SRS>
+ <SRS>EPSG:24379</SRS>
+ <SRS>EPSG:24380</SRS>
+ <SRS>EPSG:24381</SRS>
+ <SRS>EPSG:24382</SRS>
+ <SRS>EPSG:24383</SRS>
+ <SRS>EPSG:24500</SRS>
+ <SRS>EPSG:24547</SRS>
+ <SRS>EPSG:24548</SRS>
+ <SRS>EPSG:24571</SRS>
+ <SRS>EPSG:24600</SRS>
+ <SRS>EPSG:24718</SRS>
+ <SRS>EPSG:24719</SRS>
+ <SRS>EPSG:24720</SRS>
+ <SRS>EPSG:24817</SRS>
+ <SRS>EPSG:24818</SRS>
+ <SRS>EPSG:24819</SRS>
+ <SRS>EPSG:24820</SRS>
+ <SRS>EPSG:24821</SRS>
+ <SRS>EPSG:24877</SRS>
+ <SRS>EPSG:24878</SRS>
+ <SRS>EPSG:24879</SRS>
+ <SRS>EPSG:24880</SRS>
+ <SRS>EPSG:24881</SRS>
+ <SRS>EPSG:24882</SRS>
+ <SRS>EPSG:24891</SRS>
+ <SRS>EPSG:24892</SRS>
+ <SRS>EPSG:24893</SRS>
+ <SRS>EPSG:25000</SRS>
+ <SRS>EPSG:25231</SRS>
+ <SRS>EPSG:25391</SRS>
+ <SRS>EPSG:25392</SRS>
+ <SRS>EPSG:25393</SRS>
+ <SRS>EPSG:25394</SRS>
+ <SRS>EPSG:25395</SRS>
+ <SRS>EPSG:25700</SRS>
+ <SRS>EPSG:25828</SRS>
+ <SRS>EPSG:25829</SRS>
+ <SRS>EPSG:25830</SRS>
+ <SRS>EPSG:25831</SRS>
+ <SRS>EPSG:25832</SRS>
+ <SRS>EPSG:25833</SRS>
+ <SRS>EPSG:25834</SRS>
+ <SRS>EPSG:25835</SRS>
+ <SRS>EPSG:25836</SRS>
+ <SRS>EPSG:25837</SRS>
+ <SRS>EPSG:25838</SRS>
+ <SRS>EPSG:25884</SRS>
+ <SRS>EPSG:25932</SRS>
+ <SRS>EPSG:26191</SRS>
+ <SRS>EPSG:26192</SRS>
+ <SRS>EPSG:26193</SRS>
+ <SRS>EPSG:26194</SRS>
+ <SRS>EPSG:26195</SRS>
+ <SRS>EPSG:26237</SRS>
+ <SRS>EPSG:26331</SRS>
+ <SRS>EPSG:26332</SRS>
+ <SRS>EPSG:26391</SRS>
+ <SRS>EPSG:26392</SRS>
+ <SRS>EPSG:26393</SRS>
+ <SRS>EPSG:26432</SRS>
+ <SRS>EPSG:26591</SRS>
+ <SRS>EPSG:26592</SRS>
+ <SRS>EPSG:26632</SRS>
+ <SRS>EPSG:26692</SRS>
+ <SRS>EPSG:26701</SRS>
+ <SRS>EPSG:26702</SRS>
+ <SRS>EPSG:26703</SRS>
+ <SRS>EPSG:26704</SRS>
+ <SRS>EPSG:26705</SRS>
+ <SRS>EPSG:26706</SRS>
+ <SRS>EPSG:26707</SRS>
+ <SRS>EPSG:26708</SRS>
+ <SRS>EPSG:26709</SRS>
+ <SRS>EPSG:26710</SRS>
+ <SRS>EPSG:26711</SRS>
+ <SRS>EPSG:26712</SRS>
+ <SRS>EPSG:26713</SRS>
+ <SRS>EPSG:26714</SRS>
+ <SRS>EPSG:26715</SRS>
+ <SRS>EPSG:26716</SRS>
+ <SRS>EPSG:26717</SRS>
+ <SRS>EPSG:26718</SRS>
+ <SRS>EPSG:26719</SRS>
+ <SRS>EPSG:26720</SRS>
+ <SRS>EPSG:26721</SRS>
+ <SRS>EPSG:26722</SRS>
+ <SRS>EPSG:26729</SRS>
+ <SRS>EPSG:26730</SRS>
+ <SRS>EPSG:26731</SRS>
+ <SRS>EPSG:26732</SRS>
+ <SRS>EPSG:26733</SRS>
+ <SRS>EPSG:26734</SRS>
+ <SRS>EPSG:26735</SRS>
+ <SRS>EPSG:26736</SRS>
+ <SRS>EPSG:26737</SRS>
+ <SRS>EPSG:26738</SRS>
+ <SRS>EPSG:26739</SRS>
+ <SRS>EPSG:26740</SRS>
+ <SRS>EPSG:26741</SRS>
+ <SRS>EPSG:26742</SRS>
+ <SRS>EPSG:26743</SRS>
+ <SRS>EPSG:26744</SRS>
+ <SRS>EPSG:26745</SRS>
+ <SRS>EPSG:26746</SRS>
+ <SRS>EPSG:26747</SRS>
+ <SRS>EPSG:26748</SRS>
+ <SRS>EPSG:26749</SRS>
+ <SRS>EPSG:26750</SRS>
+ <SRS>EPSG:26751</SRS>
+ <SRS>EPSG:26752</SRS>
+ <SRS>EPSG:26753</SRS>
+ <SRS>EPSG:26754</SRS>
+ <SRS>EPSG:26755</SRS>
+ <SRS>EPSG:26756</SRS>
+ <SRS>EPSG:26757</SRS>
+ <SRS>EPSG:26758</SRS>
+ <SRS>EPSG:26759</SRS>
+ <SRS>EPSG:26760</SRS>
+ <SRS>EPSG:26766</SRS>
+ <SRS>EPSG:26767</SRS>
+ <SRS>EPSG:26768</SRS>
+ <SRS>EPSG:26769</SRS>
+ <SRS>EPSG:26770</SRS>
+ <SRS>EPSG:26771</SRS>
+ <SRS>EPSG:26772</SRS>
+ <SRS>EPSG:26773</SRS>
+ <SRS>EPSG:26774</SRS>
+ <SRS>EPSG:26775</SRS>
+ <SRS>EPSG:26776</SRS>
+ <SRS>EPSG:26777</SRS>
+ <SRS>EPSG:26778</SRS>
+ <SRS>EPSG:26779</SRS>
+ <SRS>EPSG:26780</SRS>
+ <SRS>EPSG:26781</SRS>
+ <SRS>EPSG:26782</SRS>
+ <SRS>EPSG:26783</SRS>
+ <SRS>EPSG:26784</SRS>
+ <SRS>EPSG:26785</SRS>
+ <SRS>EPSG:26786</SRS>
+ <SRS>EPSG:26787</SRS>
+ <SRS>EPSG:26791</SRS>
+ <SRS>EPSG:26792</SRS>
+ <SRS>EPSG:26793</SRS>
+ <SRS>EPSG:26794</SRS>
+ <SRS>EPSG:26795</SRS>
+ <SRS>EPSG:26796</SRS>
+ <SRS>EPSG:26797</SRS>
+ <SRS>EPSG:26798</SRS>
+ <SRS>EPSG:26799</SRS>
+ <SRS>EPSG:26801</SRS>
+ <SRS>EPSG:26802</SRS>
+ <SRS>EPSG:26803</SRS>
+ <SRS>EPSG:26811</SRS>
+ <SRS>EPSG:26812</SRS>
+ <SRS>EPSG:26813</SRS>
+ <SRS>EPSG:26901</SRS>
+ <SRS>EPSG:26902</SRS>
+ <SRS>EPSG:26903</SRS>
+ <SRS>EPSG:26904</SRS>
+ <SRS>EPSG:26905</SRS>
+ <SRS>EPSG:26906</SRS>
+ <SRS>EPSG:26907</SRS>
+ <SRS>EPSG:26908</SRS>
+ <SRS>EPSG:26909</SRS>
+ <SRS>EPSG:26910</SRS>
+ <SRS>EPSG:26911</SRS>
+ <SRS>EPSG:26912</SRS>
+ <SRS>EPSG:26913</SRS>
+ <SRS>EPSG:26914</SRS>
+ <SRS>EPSG:26915</SRS>
+ <SRS>EPSG:26916</SRS>
+ <SRS>EPSG:26917</SRS>
+ <SRS>EPSG:26918</SRS>
+ <SRS>EPSG:26919</SRS>
+ <SRS>EPSG:26920</SRS>
+ <SRS>EPSG:26921</SRS>
+ <SRS>EPSG:26922</SRS>
+ <SRS>EPSG:26923</SRS>
+ <SRS>EPSG:26929</SRS>
+ <SRS>EPSG:26930</SRS>
+ <SRS>EPSG:26931</SRS>
+ <SRS>EPSG:26932</SRS>
+ <SRS>EPSG:26933</SRS>
+ <SRS>EPSG:26934</SRS>
+ <SRS>EPSG:26935</SRS>
+ <SRS>EPSG:26936</SRS>
+ <SRS>EPSG:26937</SRS>
+ <SRS>EPSG:26938</SRS>
+ <SRS>EPSG:26939</SRS>
+ <SRS>EPSG:26940</SRS>
+ <SRS>EPSG:26941</SRS>
+ <SRS>EPSG:26942</SRS>
+ <SRS>EPSG:26943</SRS>
+ <SRS>EPSG:26944</SRS>
+ <SRS>EPSG:26945</SRS>
+ <SRS>EPSG:26946</SRS>
+ <SRS>EPSG:26948</SRS>
+ <SRS>EPSG:26949</SRS>
+ <SRS>EPSG:26950</SRS>
+ <SRS>EPSG:26951</SRS>
+ <SRS>EPSG:26952</SRS>
+ <SRS>EPSG:26953</SRS>
+ <SRS>EPSG:26954</SRS>
+ <SRS>EPSG:26955</SRS>
+ <SRS>EPSG:26956</SRS>
+ <SRS>EPSG:26957</SRS>
+ <SRS>EPSG:26958</SRS>
+ <SRS>EPSG:26959</SRS>
+ <SRS>EPSG:26960</SRS>
+ <SRS>EPSG:26961</SRS>
+ <SRS>EPSG:26962</SRS>
+ <SRS>EPSG:26963</SRS>
+ <SRS>EPSG:26964</SRS>
+ <SRS>EPSG:26965</SRS>
+ <SRS>EPSG:26966</SRS>
+ <SRS>EPSG:26967</SRS>
+ <SRS>EPSG:26968</SRS>
+ <SRS>EPSG:26969</SRS>
+ <SRS>EPSG:26970</SRS>
+ <SRS>EPSG:26971</SRS>
+ <SRS>EPSG:26972</SRS>
+ <SRS>EPSG:26973</SRS>
+ <SRS>EPSG:26974</SRS>
+ <SRS>EPSG:26975</SRS>
+ <SRS>EPSG:26976</SRS>
+ <SRS>EPSG:26977</SRS>
+ <SRS>EPSG:26978</SRS>
+ <SRS>EPSG:26979</SRS>
+ <SRS>EPSG:26980</SRS>
+ <SRS>EPSG:26981</SRS>
+ <SRS>EPSG:26982</SRS>
+ <SRS>EPSG:26983</SRS>
+ <SRS>EPSG:26984</SRS>
+ <SRS>EPSG:26985</SRS>
+ <SRS>EPSG:26986</SRS>
+ <SRS>EPSG:26987</SRS>
+ <SRS>EPSG:26988</SRS>
+ <SRS>EPSG:26989</SRS>
+ <SRS>EPSG:26990</SRS>
+ <SRS>EPSG:26991</SRS>
+ <SRS>EPSG:26992</SRS>
+ <SRS>EPSG:26993</SRS>
+ <SRS>EPSG:26994</SRS>
+ <SRS>EPSG:26995</SRS>
+ <SRS>EPSG:26996</SRS>
+ <SRS>EPSG:26997</SRS>
+ <SRS>EPSG:26998</SRS>
+ <SRS>EPSG:27037</SRS>
+ <SRS>EPSG:27038</SRS>
+ <SRS>EPSG:27039</SRS>
+ <SRS>EPSG:27040</SRS>
+ <SRS>EPSG:27120</SRS>
+ <SRS>EPSG:27200</SRS>
+ <SRS>EPSG:27205</SRS>
+ <SRS>EPSG:27206</SRS>
+ <SRS>EPSG:27207</SRS>
+ <SRS>EPSG:27208</SRS>
+ <SRS>EPSG:27209</SRS>
+ <SRS>EPSG:27210</SRS>
+ <SRS>EPSG:27211</SRS>
+ <SRS>EPSG:27212</SRS>
+ <SRS>EPSG:27213</SRS>
+ <SRS>EPSG:27214</SRS>
+ <SRS>EPSG:27215</SRS>
+ <SRS>EPSG:27216</SRS>
+ <SRS>EPSG:27217</SRS>
+ <SRS>EPSG:27218</SRS>
+ <SRS>EPSG:27219</SRS>
+ <SRS>EPSG:27220</SRS>
+ <SRS>EPSG:27221</SRS>
+ <SRS>EPSG:27222</SRS>
+ <SRS>EPSG:27223</SRS>
+ <SRS>EPSG:27224</SRS>
+ <SRS>EPSG:27225</SRS>
+ <SRS>EPSG:27226</SRS>
+ <SRS>EPSG:27227</SRS>
+ <SRS>EPSG:27228</SRS>
+ <SRS>EPSG:27229</SRS>
+ <SRS>EPSG:27230</SRS>
+ <SRS>EPSG:27231</SRS>
+ <SRS>EPSG:27232</SRS>
+ <SRS>EPSG:27258</SRS>
+ <SRS>EPSG:27259</SRS>
+ <SRS>EPSG:27260</SRS>
+ <SRS>EPSG:27291</SRS>
+ <SRS>EPSG:27292</SRS>
+ <SRS>EPSG:27391</SRS>
+ <SRS>EPSG:27392</SRS>
+ <SRS>EPSG:27393</SRS>
+ <SRS>EPSG:27394</SRS>
+ <SRS>EPSG:27395</SRS>
+ <SRS>EPSG:27396</SRS>
+ <SRS>EPSG:27397</SRS>
+ <SRS>EPSG:27398</SRS>
+ <SRS>EPSG:27429</SRS>
+ <SRS>EPSG:27492</SRS>
+ <SRS>EPSG:27500</SRS>
+ <SRS>EPSG:27561</SRS>
+ <SRS>EPSG:27562</SRS>
+ <SRS>EPSG:27563</SRS>
+ <SRS>EPSG:27564</SRS>
+ <SRS>EPSG:27571</SRS>
+ <SRS>EPSG:27572</SRS>
+ <SRS>EPSG:27573</SRS>
+ <SRS>EPSG:27574</SRS>
+ <SRS>EPSG:27581</SRS>
+ <SRS>EPSG:27582</SRS>
+ <SRS>EPSG:27583</SRS>
+ <SRS>EPSG:27584</SRS>
+ <SRS>EPSG:27591</SRS>
+ <SRS>EPSG:27592</SRS>
+ <SRS>EPSG:27593</SRS>
+ <SRS>EPSG:27594</SRS>
+ <SRS>EPSG:27700</SRS>
+ <SRS>EPSG:28191</SRS>
+ <SRS>EPSG:28192</SRS>
+ <SRS>EPSG:28193</SRS>
+ <SRS>EPSG:28232</SRS>
+ <SRS>EPSG:28348</SRS>
+ <SRS>EPSG:28349</SRS>
+ <SRS>EPSG:28350</SRS>
+ <SRS>EPSG:28351</SRS>
+ <SRS>EPSG:28352</SRS>
+ <SRS>EPSG:28353</SRS>
+ <SRS>EPSG:28354</SRS>
+ <SRS>EPSG:28355</SRS>
+ <SRS>EPSG:28356</SRS>
+ <SRS>EPSG:28357</SRS>
+ <SRS>EPSG:28358</SRS>
+ <SRS>EPSG:28402</SRS>
+ <SRS>EPSG:28403</SRS>
+ <SRS>EPSG:28404</SRS>
+ <SRS>EPSG:28405</SRS>
+ <SRS>EPSG:28406</SRS>
+ <SRS>EPSG:28407</SRS>
+ <SRS>EPSG:28408</SRS>
+ <SRS>EPSG:28409</SRS>
+ <SRS>EPSG:28410</SRS>
+ <SRS>EPSG:28411</SRS>
+ <SRS>EPSG:28412</SRS>
+ <SRS>EPSG:28413</SRS>
+ <SRS>EPSG:28414</SRS>
+ <SRS>EPSG:28415</SRS>
+ <SRS>EPSG:28416</SRS>
+ <SRS>EPSG:28417</SRS>
+ <SRS>EPSG:28418</SRS>
+ <SRS>EPSG:28419</SRS>
+ <SRS>EPSG:28420</SRS>
+ <SRS>EPSG:28421</SRS>
+ <SRS>EPSG:28422</SRS>
+ <SRS>EPSG:28423</SRS>
+ <SRS>EPSG:28424</SRS>
+ <SRS>EPSG:28425</SRS>
+ <SRS>EPSG:28426</SRS>
+ <SRS>EPSG:28427</SRS>
+ <SRS>EPSG:28428</SRS>
+ <SRS>EPSG:28429</SRS>
+ <SRS>EPSG:28430</SRS>
+ <SRS>EPSG:28431</SRS>
+ <SRS>EPSG:28432</SRS>
+ <SRS>EPSG:28462</SRS>
+ <SRS>EPSG:28463</SRS>
+ <SRS>EPSG:28464</SRS>
+ <SRS>EPSG:28465</SRS>
+ <SRS>EPSG:28466</SRS>
+ <SRS>EPSG:28467</SRS>
+ <SRS>EPSG:28468</SRS>
+ <SRS>EPSG:28469</SRS>
+ <SRS>EPSG:28470</SRS>
+ <SRS>EPSG:28471</SRS>
+ <SRS>EPSG:28472</SRS>
+ <SRS>EPSG:28473</SRS>
+ <SRS>EPSG:28474</SRS>
+ <SRS>EPSG:28475</SRS>
+ <SRS>EPSG:28476</SRS>
+ <SRS>EPSG:28477</SRS>
+ <SRS>EPSG:28478</SRS>
+ <SRS>EPSG:28479</SRS>
+ <SRS>EPSG:28480</SRS>
+ <SRS>EPSG:28481</SRS>
+ <SRS>EPSG:28482</SRS>
+ <SRS>EPSG:28483</SRS>
+ <SRS>EPSG:28484</SRS>
+ <SRS>EPSG:28485</SRS>
+ <SRS>EPSG:28486</SRS>
+ <SRS>EPSG:28487</SRS>
+ <SRS>EPSG:28488</SRS>
+ <SRS>EPSG:28489</SRS>
+ <SRS>EPSG:28490</SRS>
+ <SRS>EPSG:28491</SRS>
+ <SRS>EPSG:28492</SRS>
+ <SRS>EPSG:28600</SRS>
+ <SRS>EPSG:28991</SRS>
+ <SRS>EPSG:28992</SRS>
+ <SRS>EPSG:29100</SRS>
+ <SRS>EPSG:29101</SRS>
+ <SRS>EPSG:29118</SRS>
+ <SRS>EPSG:29119</SRS>
+ <SRS>EPSG:29120</SRS>
+ <SRS>EPSG:29121</SRS>
+ <SRS>EPSG:29122</SRS>
+ <SRS>EPSG:29168</SRS>
+ <SRS>EPSG:29169</SRS>
+ <SRS>EPSG:29170</SRS>
+ <SRS>EPSG:29171</SRS>
+ <SRS>EPSG:29172</SRS>
+ <SRS>EPSG:29177</SRS>
+ <SRS>EPSG:29178</SRS>
+ <SRS>EPSG:29179</SRS>
+ <SRS>EPSG:29180</SRS>
+ <SRS>EPSG:29181</SRS>
+ <SRS>EPSG:29182</SRS>
+ <SRS>EPSG:29183</SRS>
+ <SRS>EPSG:29184</SRS>
+ <SRS>EPSG:29185</SRS>
+ <SRS>EPSG:29187</SRS>
+ <SRS>EPSG:29188</SRS>
+ <SRS>EPSG:29189</SRS>
+ <SRS>EPSG:29190</SRS>
+ <SRS>EPSG:29191</SRS>
+ <SRS>EPSG:29192</SRS>
+ <SRS>EPSG:29193</SRS>
+ <SRS>EPSG:29194</SRS>
+ <SRS>EPSG:29195</SRS>
+ <SRS>EPSG:29220</SRS>
+ <SRS>EPSG:29221</SRS>
+ <SRS>EPSG:29333</SRS>
+ <SRS>EPSG:29371</SRS>
+ <SRS>EPSG:29373</SRS>
+ <SRS>EPSG:29375</SRS>
+ <SRS>EPSG:29377</SRS>
+ <SRS>EPSG:29379</SRS>
+ <SRS>EPSG:29381</SRS>
+ <SRS>EPSG:29383</SRS>
+ <SRS>EPSG:29385</SRS>
+ <SRS>EPSG:29635</SRS>
+ <SRS>EPSG:29636</SRS>
+ <SRS>EPSG:29700</SRS>
+ <SRS>EPSG:29701</SRS>
+ <SRS>EPSG:29702</SRS>
+ <SRS>EPSG:29738</SRS>
+ <SRS>EPSG:29739</SRS>
+ <SRS>EPSG:29849</SRS>
+ <SRS>EPSG:29850</SRS>
+ <SRS>EPSG:29871</SRS>
+ <SRS>EPSG:29872</SRS>
+ <SRS>EPSG:29873</SRS>
+ <SRS>EPSG:29900</SRS>
+ <SRS>EPSG:29901</SRS>
+ <SRS>EPSG:29902</SRS>
+ <SRS>EPSG:29903</SRS>
+ <SRS>EPSG:30161</SRS>
+ <SRS>EPSG:30162</SRS>
+ <SRS>EPSG:30163</SRS>
+ <SRS>EPSG:30164</SRS>
+ <SRS>EPSG:30165</SRS>
+ <SRS>EPSG:30166</SRS>
+ <SRS>EPSG:30167</SRS>
+ <SRS>EPSG:30168</SRS>
+ <SRS>EPSG:30169</SRS>
+ <SRS>EPSG:30170</SRS>
+ <SRS>EPSG:30171</SRS>
+ <SRS>EPSG:30172</SRS>
+ <SRS>EPSG:30173</SRS>
+ <SRS>EPSG:30174</SRS>
+ <SRS>EPSG:30175</SRS>
+ <SRS>EPSG:30176</SRS>
+ <SRS>EPSG:30177</SRS>
+ <SRS>EPSG:30178</SRS>
+ <SRS>EPSG:30179</SRS>
+ <SRS>EPSG:30200</SRS>
+ <SRS>EPSG:30339</SRS>
+ <SRS>EPSG:30340</SRS>
+ <SRS>EPSG:30491</SRS>
+ <SRS>EPSG:30492</SRS>
+ <SRS>EPSG:30493</SRS>
+ <SRS>EPSG:30494</SRS>
+ <SRS>EPSG:30729</SRS>
+ <SRS>EPSG:30730</SRS>
+ <SRS>EPSG:30731</SRS>
+ <SRS>EPSG:30732</SRS>
+ <SRS>EPSG:30791</SRS>
+ <SRS>EPSG:30792</SRS>
+ <SRS>EPSG:30800</SRS>
+ <SRS>EPSG:31028</SRS>
+ <SRS>EPSG:31121</SRS>
+ <SRS>EPSG:31154</SRS>
+ <SRS>EPSG:31170</SRS>
+ <SRS>EPSG:31171</SRS>
+ <SRS>EPSG:31251</SRS>
+ <SRS>EPSG:31252</SRS>
+ <SRS>EPSG:31253</SRS>
+ <SRS>EPSG:31254</SRS>
+ <SRS>EPSG:31255</SRS>
+ <SRS>EPSG:31256</SRS>
+ <SRS>EPSG:31257</SRS>
+ <SRS>EPSG:31258</SRS>
+ <SRS>EPSG:31259</SRS>
+ <SRS>EPSG:31265</SRS>
+ <SRS>EPSG:31266</SRS>
+ <SRS>EPSG:31267</SRS>
+ <SRS>EPSG:31268</SRS>
+ <SRS>EPSG:31275</SRS>
+ <SRS>EPSG:31276</SRS>
+ <SRS>EPSG:31277</SRS>
+ <SRS>EPSG:31278</SRS>
+ <SRS>EPSG:31279</SRS>
+ <SRS>EPSG:31281</SRS>
+ <SRS>EPSG:31282</SRS>
+ <SRS>EPSG:31283</SRS>
+ <SRS>EPSG:31284</SRS>
+ <SRS>EPSG:31285</SRS>
+ <SRS>EPSG:31286</SRS>
+ <SRS>EPSG:31287</SRS>
+ <SRS>EPSG:31288</SRS>
+ <SRS>EPSG:31289</SRS>
+ <SRS>EPSG:31290</SRS>
+ <SRS>EPSG:31291</SRS>
+ <SRS>EPSG:31292</SRS>
+ <SRS>EPSG:31293</SRS>
+ <SRS>EPSG:31294</SRS>
+ <SRS>EPSG:31295</SRS>
+ <SRS>EPSG:31296</SRS>
+ <SRS>EPSG:31297</SRS>
+ <SRS>EPSG:31300</SRS>
+ <SRS>EPSG:31370</SRS>
+ <SRS>EPSG:31461</SRS>
+ <SRS>EPSG:31462</SRS>
+ <SRS>EPSG:31463</SRS>
+ <SRS>EPSG:31464</SRS>
+ <SRS>EPSG:31465</SRS>
+ <SRS>EPSG:31466</SRS>
+ <SRS>EPSG:31467</SRS>
+ <SRS>EPSG:31468</SRS>
+ <SRS>EPSG:31469</SRS>
+ <SRS>EPSG:31528</SRS>
+ <SRS>EPSG:31529</SRS>
+ <SRS>EPSG:31600</SRS>
+ <SRS>EPSG:31700</SRS>
+ <SRS>EPSG:31838</SRS>
+ <SRS>EPSG:31839</SRS>
+ <SRS>EPSG:31900</SRS>
+ <SRS>EPSG:31901</SRS>
+ <SRS>EPSG:31965</SRS>
+ <SRS>EPSG:31966</SRS>
+ <SRS>EPSG:31967</SRS>
+ <SRS>EPSG:31968</SRS>
+ <SRS>EPSG:31969</SRS>
+ <SRS>EPSG:31970</SRS>
+ <SRS>EPSG:31971</SRS>
+ <SRS>EPSG:31972</SRS>
+ <SRS>EPSG:31973</SRS>
+ <SRS>EPSG:31974</SRS>
+ <SRS>EPSG:31975</SRS>
+ <SRS>EPSG:31976</SRS>
+ <SRS>EPSG:31977</SRS>
+ <SRS>EPSG:31978</SRS>
+ <SRS>EPSG:31979</SRS>
+ <SRS>EPSG:31980</SRS>
+ <SRS>EPSG:31981</SRS>
+ <SRS>EPSG:31982</SRS>
+ <SRS>EPSG:31983</SRS>
+ <SRS>EPSG:31984</SRS>
+ <SRS>EPSG:31985</SRS>
+ <SRS>EPSG:31986</SRS>
+ <SRS>EPSG:31987</SRS>
+ <SRS>EPSG:31988</SRS>
+ <SRS>EPSG:31989</SRS>
+ <SRS>EPSG:31990</SRS>
+ <SRS>EPSG:31991</SRS>
+ <SRS>EPSG:31992</SRS>
+ <SRS>EPSG:31993</SRS>
+ <SRS>EPSG:31994</SRS>
+ <SRS>EPSG:31995</SRS>
+ <SRS>EPSG:31996</SRS>
+ <SRS>EPSG:31997</SRS>
+ <SRS>EPSG:31998</SRS>
+ <SRS>EPSG:31999</SRS>
+ <SRS>EPSG:32000</SRS>
+ <SRS>EPSG:32001</SRS>
+ <SRS>EPSG:32002</SRS>
+ <SRS>EPSG:32003</SRS>
+ <SRS>EPSG:32005</SRS>
+ <SRS>EPSG:32006</SRS>
+ <SRS>EPSG:32007</SRS>
+ <SRS>EPSG:32008</SRS>
+ <SRS>EPSG:32009</SRS>
+ <SRS>EPSG:32010</SRS>
+ <SRS>EPSG:32011</SRS>
+ <SRS>EPSG:32012</SRS>
+ <SRS>EPSG:32013</SRS>
+ <SRS>EPSG:32014</SRS>
+ <SRS>EPSG:32015</SRS>
+ <SRS>EPSG:32016</SRS>
+ <SRS>EPSG:32017</SRS>
+ <SRS>EPSG:32018</SRS>
+ <SRS>EPSG:32019</SRS>
+ <SRS>EPSG:32020</SRS>
+ <SRS>EPSG:32021</SRS>
+ <SRS>EPSG:32022</SRS>
+ <SRS>EPSG:32023</SRS>
+ <SRS>EPSG:32024</SRS>
+ <SRS>EPSG:32025</SRS>
+ <SRS>EPSG:32026</SRS>
+ <SRS>EPSG:32027</SRS>
+ <SRS>EPSG:32028</SRS>
+ <SRS>EPSG:32029</SRS>
+ <SRS>EPSG:32030</SRS>
+ <SRS>EPSG:32031</SRS>
+ <SRS>EPSG:32033</SRS>
+ <SRS>EPSG:32034</SRS>
+ <SRS>EPSG:32035</SRS>
+ <SRS>EPSG:32036</SRS>
+ <SRS>EPSG:32037</SRS>
+ <SRS>EPSG:32038</SRS>
+ <SRS>EPSG:32039</SRS>
+ <SRS>EPSG:32040</SRS>
+ <SRS>EPSG:32041</SRS>
+ <SRS>EPSG:32042</SRS>
+ <SRS>EPSG:32043</SRS>
+ <SRS>EPSG:32044</SRS>
+ <SRS>EPSG:32045</SRS>
+ <SRS>EPSG:32046</SRS>
+ <SRS>EPSG:32047</SRS>
+ <SRS>EPSG:32048</SRS>
+ <SRS>EPSG:32049</SRS>
+ <SRS>EPSG:32050</SRS>
+ <SRS>EPSG:32051</SRS>
+ <SRS>EPSG:32052</SRS>
+ <SRS>EPSG:32053</SRS>
+ <SRS>EPSG:32054</SRS>
+ <SRS>EPSG:32055</SRS>
+ <SRS>EPSG:32056</SRS>
+ <SRS>EPSG:32057</SRS>
+ <SRS>EPSG:32058</SRS>
+ <SRS>EPSG:32061</SRS>
+ <SRS>EPSG:32062</SRS>
+ <SRS>EPSG:32064</SRS>
+ <SRS>EPSG:32065</SRS>
+ <SRS>EPSG:32066</SRS>
+ <SRS>EPSG:32067</SRS>
+ <SRS>EPSG:32074</SRS>
+ <SRS>EPSG:32075</SRS>
+ <SRS>EPSG:32076</SRS>
+ <SRS>EPSG:32077</SRS>
+ <SRS>EPSG:32081</SRS>
+ <SRS>EPSG:32082</SRS>
+ <SRS>EPSG:32083</SRS>
+ <SRS>EPSG:32084</SRS>
+ <SRS>EPSG:32085</SRS>
+ <SRS>EPSG:32086</SRS>
+ <SRS>EPSG:32098</SRS>
+ <SRS>EPSG:32099</SRS>
+ <SRS>EPSG:32100</SRS>
+ <SRS>EPSG:32104</SRS>
+ <SRS>EPSG:32107</SRS>
+ <SRS>EPSG:32108</SRS>
+ <SRS>EPSG:32109</SRS>
+ <SRS>EPSG:32110</SRS>
+ <SRS>EPSG:32111</SRS>
+ <SRS>EPSG:32112</SRS>
+ <SRS>EPSG:32113</SRS>
+ <SRS>EPSG:32114</SRS>
+ <SRS>EPSG:32115</SRS>
+ <SRS>EPSG:32116</SRS>
+ <SRS>EPSG:32117</SRS>
+ <SRS>EPSG:32118</SRS>
+ <SRS>EPSG:32119</SRS>
+ <SRS>EPSG:32120</SRS>
+ <SRS>EPSG:32121</SRS>
+ <SRS>EPSG:32122</SRS>
+ <SRS>EPSG:32123</SRS>
+ <SRS>EPSG:32124</SRS>
+ <SRS>EPSG:32125</SRS>
+ <SRS>EPSG:32126</SRS>
+ <SRS>EPSG:32127</SRS>
+ <SRS>EPSG:32128</SRS>
+ <SRS>EPSG:32129</SRS>
+ <SRS>EPSG:32130</SRS>
+ <SRS>EPSG:32133</SRS>
+ <SRS>EPSG:32134</SRS>
+ <SRS>EPSG:32135</SRS>
+ <SRS>EPSG:32136</SRS>
+ <SRS>EPSG:32137</SRS>
+ <SRS>EPSG:32138</SRS>
+ <SRS>EPSG:32139</SRS>
+ <SRS>EPSG:32140</SRS>
+ <SRS>EPSG:32141</SRS>
+ <SRS>EPSG:32142</SRS>
+ <SRS>EPSG:32143</SRS>
+ <SRS>EPSG:32144</SRS>
+ <SRS>EPSG:32145</SRS>
+ <SRS>EPSG:32146</SRS>
+ <SRS>EPSG:32147</SRS>
+ <SRS>EPSG:32148</SRS>
+ <SRS>EPSG:32149</SRS>
+ <SRS>EPSG:32150</SRS>
+ <SRS>EPSG:32151</SRS>
+ <SRS>EPSG:32152</SRS>
+ <SRS>EPSG:32153</SRS>
+ <SRS>EPSG:32154</SRS>
+ <SRS>EPSG:32155</SRS>
+ <SRS>EPSG:32156</SRS>
+ <SRS>EPSG:32157</SRS>
+ <SRS>EPSG:32158</SRS>
+ <SRS>EPSG:32161</SRS>
+ <SRS>EPSG:32164</SRS>
+ <SRS>EPSG:32165</SRS>
+ <SRS>EPSG:32166</SRS>
+ <SRS>EPSG:32167</SRS>
+ <SRS>EPSG:32180</SRS>
+ <SRS>EPSG:32181</SRS>
+ <SRS>EPSG:32182</SRS>
+ <SRS>EPSG:32183</SRS>
+ <SRS>EPSG:32184</SRS>
+ <SRS>EPSG:32185</SRS>
+ <SRS>EPSG:32186</SRS>
+ <SRS>EPSG:32187</SRS>
+ <SRS>EPSG:32188</SRS>
+ <SRS>EPSG:32189</SRS>
+ <SRS>EPSG:32190</SRS>
+ <SRS>EPSG:32191</SRS>
+ <SRS>EPSG:32192</SRS>
+ <SRS>EPSG:32193</SRS>
+ <SRS>EPSG:32194</SRS>
+ <SRS>EPSG:32195</SRS>
+ <SRS>EPSG:32196</SRS>
+ <SRS>EPSG:32197</SRS>
+ <SRS>EPSG:32198</SRS>
+ <SRS>EPSG:32199</SRS>
+ <SRS>EPSG:32201</SRS>
+ <SRS>EPSG:32202</SRS>
+ <SRS>EPSG:32203</SRS>
+ <SRS>EPSG:32204</SRS>
+ <SRS>EPSG:32205</SRS>
+ <SRS>EPSG:32206</SRS>
+ <SRS>EPSG:32207</SRS>
+ <SRS>EPSG:32208</SRS>
+ <SRS>EPSG:32209</SRS>
+ <SRS>EPSG:32210</SRS>
+ <SRS>EPSG:32211</SRS>
+ <SRS>EPSG:32212</SRS>
+ <SRS>EPSG:32213</SRS>
+ <SRS>EPSG:32214</SRS>
+ <SRS>EPSG:32215</SRS>
+ <SRS>EPSG:32216</SRS>
+ <SRS>EPSG:32217</SRS>
+ <SRS>EPSG:32218</SRS>
+ <SRS>EPSG:32219</SRS>
+ <SRS>EPSG:32220</SRS>
+ <SRS>EPSG:32221</SRS>
+ <SRS>EPSG:32222</SRS>
+ <SRS>EPSG:32223</SRS>
+ <SRS>EPSG:32224</SRS>
+ <SRS>EPSG:32225</SRS>
+ <SRS>EPSG:32226</SRS>
+ <SRS>EPSG:32227</SRS>
+ <SRS>EPSG:32228</SRS>
+ <SRS>EPSG:32229</SRS>
+ <SRS>EPSG:32230</SRS>
+ <SRS>EPSG:32231</SRS>
+ <SRS>EPSG:32232</SRS>
+ <SRS>EPSG:32233</SRS>
+ <SRS>EPSG:32234</SRS>
+ <SRS>EPSG:32235</SRS>
+ <SRS>EPSG:32236</SRS>
+ <SRS>EPSG:32237</SRS>
+ <SRS>EPSG:32238</SRS>
+ <SRS>EPSG:32239</SRS>
+ <SRS>EPSG:32240</SRS>
+ <SRS>EPSG:32241</SRS>
+ <SRS>EPSG:32242</SRS>
+ <SRS>EPSG:32243</SRS>
+ <SRS>EPSG:32244</SRS>
+ <SRS>EPSG:32245</SRS>
+ <SRS>EPSG:32246</SRS>
+ <SRS>EPSG:32247</SRS>
+ <SRS>EPSG:32248</SRS>
+ <SRS>EPSG:32249</SRS>
+ <SRS>EPSG:32250</SRS>
+ <SRS>EPSG:32251</SRS>
+ <SRS>EPSG:32252</SRS>
+ <SRS>EPSG:32253</SRS>
+ <SRS>EPSG:32254</SRS>
+ <SRS>EPSG:32255</SRS>
+ <SRS>EPSG:32256</SRS>
+ <SRS>EPSG:32257</SRS>
+ <SRS>EPSG:32258</SRS>
+ <SRS>EPSG:32259</SRS>
+ <SRS>EPSG:32260</SRS>
+ <SRS>EPSG:32301</SRS>
+ <SRS>EPSG:32302</SRS>
+ <SRS>EPSG:32303</SRS>
+ <SRS>EPSG:32304</SRS>
+ <SRS>EPSG:32305</SRS>
+ <SRS>EPSG:32306</SRS>
+ <SRS>EPSG:32307</SRS>
+ <SRS>EPSG:32308</SRS>
+ <SRS>EPSG:32309</SRS>
+ <SRS>EPSG:32310</SRS>
+ <SRS>EPSG:32311</SRS>
+ <SRS>EPSG:32312</SRS>
+ <SRS>EPSG:32313</SRS>
+ <SRS>EPSG:32314</SRS>
+ <SRS>EPSG:32315</SRS>
+ <SRS>EPSG:32316</SRS>
+ <SRS>EPSG:32317</SRS>
+ <SRS>EPSG:32318</SRS>
+ <SRS>EPSG:32319</SRS>
+ <SRS>EPSG:32320</SRS>
+ <SRS>EPSG:32321</SRS>
+ <SRS>EPSG:32322</SRS>
+ <SRS>EPSG:32323</SRS>
+ <SRS>EPSG:32324</SRS>
+ <SRS>EPSG:32325</SRS>
+ <SRS>EPSG:32326</SRS>
+ <SRS>EPSG:32327</SRS>
+ <SRS>EPSG:32328</SRS>
+ <SRS>EPSG:32329</SRS>
+ <SRS>EPSG:32330</SRS>
+ <SRS>EPSG:32331</SRS>
+ <SRS>EPSG:32332</SRS>
+ <SRS>EPSG:32333</SRS>
+ <SRS>EPSG:32334</SRS>
+ <SRS>EPSG:32335</SRS>
+ <SRS>EPSG:32336</SRS>
+ <SRS>EPSG:32337</SRS>
+ <SRS>EPSG:32338</SRS>
+ <SRS>EPSG:32339</SRS>
+ <SRS>EPSG:32340</SRS>
+ <SRS>EPSG:32341</SRS>
+ <SRS>EPSG:32342</SRS>
+ <SRS>EPSG:32343</SRS>
+ <SRS>EPSG:32344</SRS>
+ <SRS>EPSG:32345</SRS>
+ <SRS>EPSG:32346</SRS>
+ <SRS>EPSG:32347</SRS>
+ <SRS>EPSG:32348</SRS>
+ <SRS>EPSG:32349</SRS>
+ <SRS>EPSG:32350</SRS>
+ <SRS>EPSG:32351</SRS>
+ <SRS>EPSG:32352</SRS>
+ <SRS>EPSG:32353</SRS>
+ <SRS>EPSG:32354</SRS>
+ <SRS>EPSG:32355</SRS>
+ <SRS>EPSG:32356</SRS>
+ <SRS>EPSG:32357</SRS>
+ <SRS>EPSG:32358</SRS>
+ <SRS>EPSG:32359</SRS>
+ <SRS>EPSG:32360</SRS>
+ <SRS>EPSG:32401</SRS>
+ <SRS>EPSG:32402</SRS>
+ <SRS>EPSG:32403</SRS>
+ <SRS>EPSG:32404</SRS>
+ <SRS>EPSG:32405</SRS>
+ <SRS>EPSG:32406</SRS>
+ <SRS>EPSG:32407</SRS>
+ <SRS>EPSG:32408</SRS>
+ <SRS>EPSG:32409</SRS>
+ <SRS>EPSG:32410</SRS>
+ <SRS>EPSG:32411</SRS>
+ <SRS>EPSG:32412</SRS>
+ <SRS>EPSG:32413</SRS>
+ <SRS>EPSG:32414</SRS>
+ <SRS>EPSG:32415</SRS>
+ <SRS>EPSG:32416</SRS>
+ <SRS>EPSG:32417</SRS>
+ <SRS>EPSG:32418</SRS>
+ <SRS>EPSG:32419</SRS>
+ <SRS>EPSG:32420</SRS>
+ <SRS>EPSG:32421</SRS>
+ <SRS>EPSG:32422</SRS>
+ <SRS>EPSG:32423</SRS>
+ <SRS>EPSG:32424</SRS>
+ <SRS>EPSG:32425</SRS>
+ <SRS>EPSG:32426</SRS>
+ <SRS>EPSG:32427</SRS>
+ <SRS>EPSG:32428</SRS>
+ <SRS>EPSG:32429</SRS>
+ <SRS>EPSG:32430</SRS>
+ <SRS>EPSG:32431</SRS>
+ <SRS>EPSG:32432</SRS>
+ <SRS>EPSG:32433</SRS>
+ <SRS>EPSG:32434</SRS>
+ <SRS>EPSG:32435</SRS>
+ <SRS>EPSG:32436</SRS>
+ <SRS>EPSG:32437</SRS>
+ <SRS>EPSG:32438</SRS>
+ <SRS>EPSG:32439</SRS>
+ <SRS>EPSG:32440</SRS>
+ <SRS>EPSG:32441</SRS>
+ <SRS>EPSG:32442</SRS>
+ <SRS>EPSG:32443</SRS>
+ <SRS>EPSG:32444</SRS>
+ <SRS>EPSG:32445</SRS>
+ <SRS>EPSG:32446</SRS>
+ <SRS>EPSG:32447</SRS>
+ <SRS>EPSG:32448</SRS>
+ <SRS>EPSG:32449</SRS>
+ <SRS>EPSG:32450</SRS>
+ <SRS>EPSG:32451</SRS>
+ <SRS>EPSG:32452</SRS>
+ <SRS>EPSG:32453</SRS>
+ <SRS>EPSG:32454</SRS>
+ <SRS>EPSG:32455</SRS>
+ <SRS>EPSG:32456</SRS>
+ <SRS>EPSG:32457</SRS>
+ <SRS>EPSG:32458</SRS>
+ <SRS>EPSG:32459</SRS>
+ <SRS>EPSG:32460</SRS>
+ <SRS>EPSG:32501</SRS>
+ <SRS>EPSG:32502</SRS>
+ <SRS>EPSG:32503</SRS>
+ <SRS>EPSG:32504</SRS>
+ <SRS>EPSG:32505</SRS>
+ <SRS>EPSG:32506</SRS>
+ <SRS>EPSG:32507</SRS>
+ <SRS>EPSG:32508</SRS>
+ <SRS>EPSG:32509</SRS>
+ <SRS>EPSG:32510</SRS>
+ <SRS>EPSG:32511</SRS>
+ <SRS>EPSG:32512</SRS>
+ <SRS>EPSG:32513</SRS>
+ <SRS>EPSG:32514</SRS>
+ <SRS>EPSG:32515</SRS>
+ <SRS>EPSG:32516</SRS>
+ <SRS>EPSG:32517</SRS>
+ <SRS>EPSG:32518</SRS>
+ <SRS>EPSG:32519</SRS>
+ <SRS>EPSG:32520</SRS>
+ <SRS>EPSG:32521</SRS>
+ <SRS>EPSG:32522</SRS>
+ <SRS>EPSG:32523</SRS>
+ <SRS>EPSG:32524</SRS>
+ <SRS>EPSG:32525</SRS>
+ <SRS>EPSG:32526</SRS>
+ <SRS>EPSG:32527</SRS>
+ <SRS>EPSG:32528</SRS>
+ <SRS>EPSG:32529</SRS>
+ <SRS>EPSG:32530</SRS>
+ <SRS>EPSG:32531</SRS>
+ <SRS>EPSG:32532</SRS>
+ <SRS>EPSG:32533</SRS>
+ <SRS>EPSG:32534</SRS>
+ <SRS>EPSG:32535</SRS>
+ <SRS>EPSG:32536</SRS>
+ <SRS>EPSG:32537</SRS>
+ <SRS>EPSG:32538</SRS>
+ <SRS>EPSG:32539</SRS>
+ <SRS>EPSG:32540</SRS>
+ <SRS>EPSG:32541</SRS>
+ <SRS>EPSG:32542</SRS>
+ <SRS>EPSG:32543</SRS>
+ <SRS>EPSG:32544</SRS>
+ <SRS>EPSG:32545</SRS>
+ <SRS>EPSG:32546</SRS>
+ <SRS>EPSG:32547</SRS>
+ <SRS>EPSG:32548</SRS>
+ <SRS>EPSG:32549</SRS>
+ <SRS>EPSG:32550</SRS>
+ <SRS>EPSG:32551</SRS>
+ <SRS>EPSG:32552</SRS>
+ <SRS>EPSG:32553</SRS>
+ <SRS>EPSG:32554</SRS>
+ <SRS>EPSG:32555</SRS>
+ <SRS>EPSG:32556</SRS>
+ <SRS>EPSG:32557</SRS>
+ <SRS>EPSG:32558</SRS>
+ <SRS>EPSG:32559</SRS>
+ <SRS>EPSG:32560</SRS>
+ <SRS>EPSG:32600</SRS>
+ <SRS>EPSG:32601</SRS>
+ <SRS>EPSG:32602</SRS>
+ <SRS>EPSG:32603</SRS>
+ <SRS>EPSG:32604</SRS>
+ <SRS>EPSG:32605</SRS>
+ <SRS>EPSG:32606</SRS>
+ <SRS>EPSG:32607</SRS>
+ <SRS>EPSG:32608</SRS>
+ <SRS>EPSG:32609</SRS>
+ <SRS>EPSG:32610</SRS>
+ <SRS>EPSG:32611</SRS>
+ <SRS>EPSG:32612</SRS>
+ <SRS>EPSG:32613</SRS>
+ <SRS>EPSG:32614</SRS>
+ <SRS>EPSG:32615</SRS>
+ <SRS>EPSG:32616</SRS>
+ <SRS>EPSG:32617</SRS>
+ <SRS>EPSG:32618</SRS>
+ <SRS>EPSG:32619</SRS>
+ <SRS>EPSG:32620</SRS>
+ <SRS>EPSG:32621</SRS>
+ <SRS>EPSG:32622</SRS>
+ <SRS>EPSG:32623</SRS>
+ <SRS>EPSG:32624</SRS>
+ <SRS>EPSG:32625</SRS>
+ <SRS>EPSG:32626</SRS>
+ <SRS>EPSG:32627</SRS>
+ <SRS>EPSG:32628</SRS>
+ <SRS>EPSG:32629</SRS>
+ <SRS>EPSG:32630</SRS>
+ <SRS>EPSG:32631</SRS>
+ <SRS>EPSG:32632</SRS>
+ <SRS>EPSG:32633</SRS>
+ <SRS>EPSG:32634</SRS>
+ <SRS>EPSG:32635</SRS>
+ <SRS>EPSG:32636</SRS>
+ <SRS>EPSG:32637</SRS>
+ <SRS>EPSG:32638</SRS>
+ <SRS>EPSG:32639</SRS>
+ <SRS>EPSG:32640</SRS>
+ <SRS>EPSG:32641</SRS>
+ <SRS>EPSG:32642</SRS>
+ <SRS>EPSG:32643</SRS>
+ <SRS>EPSG:32644</SRS>
+ <SRS>EPSG:32645</SRS>
+ <SRS>EPSG:32646</SRS>
+ <SRS>EPSG:32647</SRS>
+ <SRS>EPSG:32648</SRS>
+ <SRS>EPSG:32649</SRS>
+ <SRS>EPSG:32650</SRS>
+ <SRS>EPSG:32651</SRS>
+ <SRS>EPSG:32652</SRS>
+ <SRS>EPSG:32653</SRS>
+ <SRS>EPSG:32654</SRS>
+ <SRS>EPSG:32655</SRS>
+ <SRS>EPSG:32656</SRS>
+ <SRS>EPSG:32657</SRS>
+ <SRS>EPSG:32658</SRS>
+ <SRS>EPSG:32659</SRS>
+ <SRS>EPSG:32660</SRS>
+ <SRS>EPSG:32661</SRS>
+ <SRS>EPSG:32662</SRS>
+ <SRS>EPSG:32664</SRS>
+ <SRS>EPSG:32665</SRS>
+ <SRS>EPSG:32666</SRS>
+ <SRS>EPSG:32667</SRS>
+ <SRS>EPSG:32700</SRS>
+ <SRS>EPSG:32701</SRS>
+ <SRS>EPSG:32702</SRS>
+ <SRS>EPSG:32703</SRS>
+ <SRS>EPSG:32704</SRS>
+ <SRS>EPSG:32705</SRS>
+ <SRS>EPSG:32706</SRS>
+ <SRS>EPSG:32707</SRS>
+ <SRS>EPSG:32708</SRS>
+ <SRS>EPSG:32709</SRS>
+ <SRS>EPSG:32710</SRS>
+ <SRS>EPSG:32711</SRS>
+ <SRS>EPSG:32712</SRS>
+ <SRS>EPSG:32713</SRS>
+ <SRS>EPSG:32714</SRS>
+ <SRS>EPSG:32715</SRS>
+ <SRS>EPSG:32716</SRS>
+ <SRS>EPSG:32717</SRS>
+ <SRS>EPSG:32718</SRS>
+ <SRS>EPSG:32719</SRS>
+ <SRS>EPSG:32720</SRS>
+ <SRS>EPSG:32721</SRS>
+ <SRS>EPSG:32722</SRS>
+ <SRS>EPSG:32723</SRS>
+ <SRS>EPSG:32724</SRS>
+ <SRS>EPSG:32725</SRS>
+ <SRS>EPSG:32726</SRS>
+ <SRS>EPSG:32727</SRS>
+ <SRS>EPSG:32728</SRS>
+ <SRS>EPSG:32729</SRS>
+ <SRS>EPSG:32730</SRS>
+ <SRS>EPSG:32731</SRS>
+ <SRS>EPSG:32732</SRS>
+ <SRS>EPSG:32733</SRS>
+ <SRS>EPSG:32734</SRS>
+ <SRS>EPSG:32735</SRS>
+ <SRS>EPSG:32736</SRS>
+ <SRS>EPSG:32737</SRS>
+ <SRS>EPSG:32738</SRS>
+ <SRS>EPSG:32739</SRS>
+ <SRS>EPSG:32740</SRS>
+ <SRS>EPSG:32741</SRS>
+ <SRS>EPSG:32742</SRS>
+ <SRS>EPSG:32743</SRS>
+ <SRS>EPSG:32744</SRS>
+ <SRS>EPSG:32745</SRS>
+ <SRS>EPSG:32746</SRS>
+ <SRS>EPSG:32747</SRS>
+ <SRS>EPSG:32748</SRS>
+ <SRS>EPSG:32749</SRS>
+ <SRS>EPSG:32750</SRS>
+ <SRS>EPSG:32751</SRS>
+ <SRS>EPSG:32752</SRS>
+ <SRS>EPSG:32753</SRS>
+ <SRS>EPSG:32754</SRS>
+ <SRS>EPSG:32755</SRS>
+ <SRS>EPSG:32756</SRS>
+ <SRS>EPSG:32757</SRS>
+ <SRS>EPSG:32758</SRS>
+ <SRS>EPSG:32759</SRS>
+ <SRS>EPSG:32760</SRS>
+ <SRS>EPSG:32761</SRS>
+ <SRS>EPSG:32766</SRS>
+ <SRS>EPSG:61206405</SRS>
+ <SRS>EPSG:61216405</SRS>
+ <SRS>EPSG:61226405</SRS>
+ <SRS>EPSG:61236405</SRS>
+ <SRS>EPSG:61246405</SRS>
+ <SRS>EPSG:61266405</SRS>
+ <SRS>EPSG:61266413</SRS>
+ <SRS>EPSG:61276405</SRS>
+ <SRS>EPSG:61286405</SRS>
+ <SRS>EPSG:61296405</SRS>
+ <SRS>EPSG:61306405</SRS>
+ <SRS>EPSG:61306413</SRS>
+ <SRS>EPSG:61316405</SRS>
+ <SRS>EPSG:61326405</SRS>
+ <SRS>EPSG:61336405</SRS>
+ <SRS>EPSG:61346405</SRS>
+ <SRS>EPSG:61356405</SRS>
+ <SRS>EPSG:61366405</SRS>
+ <SRS>EPSG:61376405</SRS>
+ <SRS>EPSG:61386405</SRS>
+ <SRS>EPSG:61396405</SRS>
+ <SRS>EPSG:61406405</SRS>
+ <SRS>EPSG:61406413</SRS>
+ <SRS>EPSG:61416405</SRS>
+ <SRS>EPSG:61426405</SRS>
+ <SRS>EPSG:61436405</SRS>
+ <SRS>EPSG:61446405</SRS>
+ <SRS>EPSG:61456405</SRS>
+ <SRS>EPSG:61466405</SRS>
+ <SRS>EPSG:61476405</SRS>
+ <SRS>EPSG:61486405</SRS>
+ <SRS>EPSG:61486413</SRS>
+ <SRS>EPSG:61496405</SRS>
+ <SRS>EPSG:61506405</SRS>
+ <SRS>EPSG:61516405</SRS>
+ <SRS>EPSG:61516413</SRS>
+ <SRS>EPSG:61526405</SRS>
+ <SRS>EPSG:61526413</SRS>
+ <SRS>EPSG:61536405</SRS>
+ <SRS>EPSG:61546405</SRS>
+ <SRS>EPSG:61556405</SRS>
+ <SRS>EPSG:61566405</SRS>
+ <SRS>EPSG:61576405</SRS>
+ <SRS>EPSG:61586405</SRS>
+ <SRS>EPSG:61596405</SRS>
+ <SRS>EPSG:61606405</SRS>
+ <SRS>EPSG:61616405</SRS>
+ <SRS>EPSG:61626405</SRS>
+ <SRS>EPSG:61636405</SRS>
+ <SRS>EPSG:61636413</SRS>
+ <SRS>EPSG:61646405</SRS>
+ <SRS>EPSG:61656405</SRS>
+ <SRS>EPSG:61666405</SRS>
+ <SRS>EPSG:61676405</SRS>
+ <SRS>EPSG:61676413</SRS>
+ <SRS>EPSG:61686405</SRS>
+ <SRS>EPSG:61696405</SRS>
+ <SRS>EPSG:61706405</SRS>
+ <SRS>EPSG:61706413</SRS>
+ <SRS>EPSG:61716405</SRS>
+ <SRS>EPSG:61716413</SRS>
+ <SRS>EPSG:61736405</SRS>
+ <SRS>EPSG:61736413</SRS>
+ <SRS>EPSG:61746405</SRS>
+ <SRS>EPSG:61756405</SRS>
+ <SRS>EPSG:61766405</SRS>
+ <SRS>EPSG:61766413</SRS>
+ <SRS>EPSG:61786405</SRS>
+ <SRS>EPSG:61796405</SRS>
+ <SRS>EPSG:61806405</SRS>
+ <SRS>EPSG:61806413</SRS>
+ <SRS>EPSG:61816405</SRS>
+ <SRS>EPSG:61826405</SRS>
+ <SRS>EPSG:61836405</SRS>
+ <SRS>EPSG:61846405</SRS>
+ <SRS>EPSG:61886405</SRS>
+ <SRS>EPSG:61896405</SRS>
+ <SRS>EPSG:61896413</SRS>
+ <SRS>EPSG:61906405</SRS>
+ <SRS>EPSG:61906413</SRS>
+ <SRS>EPSG:61916405</SRS>
+ <SRS>EPSG:61926405</SRS>
+ <SRS>EPSG:61936405</SRS>
+ <SRS>EPSG:61946405</SRS>
+ <SRS>EPSG:61956405</SRS>
+ <SRS>EPSG:61966405</SRS>
+ <SRS>EPSG:61976405</SRS>
+ <SRS>EPSG:61986405</SRS>
+ <SRS>EPSG:61996405</SRS>
+ <SRS>EPSG:62006405</SRS>
+ <SRS>EPSG:62016405</SRS>
+ <SRS>EPSG:62026405</SRS>
+ <SRS>EPSG:62036405</SRS>
+ <SRS>EPSG:62046405</SRS>
+ <SRS>EPSG:62056405</SRS>
+ <SRS>EPSG:62066405</SRS>
+ <SRS>EPSG:62076405</SRS>
+ <SRS>EPSG:62086405</SRS>
+ <SRS>EPSG:62096405</SRS>
+ <SRS>EPSG:62106405</SRS>
+ <SRS>EPSG:62116405</SRS>
+ <SRS>EPSG:62126405</SRS>
+ <SRS>EPSG:62136405</SRS>
+ <SRS>EPSG:62146405</SRS>
+ <SRS>EPSG:62156405</SRS>
+ <SRS>EPSG:62166405</SRS>
+ <SRS>EPSG:62186405</SRS>
+ <SRS>EPSG:62196405</SRS>
+ <SRS>EPSG:62206405</SRS>
+ <SRS>EPSG:62216405</SRS>
+ <SRS>EPSG:62226405</SRS>
+ <SRS>EPSG:62236405</SRS>
+ <SRS>EPSG:62246405</SRS>
+ <SRS>EPSG:62256405</SRS>
+ <SRS>EPSG:62276405</SRS>
+ <SRS>EPSG:62296405</SRS>
+ <SRS>EPSG:62306405</SRS>
+ <SRS>EPSG:62316405</SRS>
+ <SRS>EPSG:62326405</SRS>
+ <SRS>EPSG:62336405</SRS>
+ <SRS>EPSG:62366405</SRS>
+ <SRS>EPSG:62376405</SRS>
+ <SRS>EPSG:62386405</SRS>
+ <SRS>EPSG:62396405</SRS>
+ <SRS>EPSG:62406405</SRS>
+ <SRS>EPSG:62416405</SRS>
+ <SRS>EPSG:62426405</SRS>
+ <SRS>EPSG:62436405</SRS>
+ <SRS>EPSG:62446405</SRS>
+ <SRS>EPSG:62456405</SRS>
+ <SRS>EPSG:62466405</SRS>
+ <SRS>EPSG:62476405</SRS>
+ <SRS>EPSG:62486405</SRS>
+ <SRS>EPSG:62496405</SRS>
+ <SRS>EPSG:62506405</SRS>
+ <SRS>EPSG:62516405</SRS>
+ <SRS>EPSG:62526405</SRS>
+ <SRS>EPSG:62536405</SRS>
+ <SRS>EPSG:62546405</SRS>
+ <SRS>EPSG:62556405</SRS>
+ <SRS>EPSG:62566405</SRS>
+ <SRS>EPSG:62576405</SRS>
+ <SRS>EPSG:62586405</SRS>
+ <SRS>EPSG:62586413</SRS>
+ <SRS>EPSG:62596405</SRS>
+ <SRS>EPSG:62616405</SRS>
+ <SRS>EPSG:62626405</SRS>
+ <SRS>EPSG:62636405</SRS>
+ <SRS>EPSG:62646405</SRS>
+ <SRS>EPSG:62656405</SRS>
+ <SRS>EPSG:62666405</SRS>
+ <SRS>EPSG:62676405</SRS>
+ <SRS>EPSG:62686405</SRS>
+ <SRS>EPSG:62696405</SRS>
+ <SRS>EPSG:62706405</SRS>
+ <SRS>EPSG:62716405</SRS>
+ <SRS>EPSG:62726405</SRS>
+ <SRS>EPSG:62736405</SRS>
+ <SRS>EPSG:62746405</SRS>
+ <SRS>EPSG:62756405</SRS>
+ <SRS>EPSG:62766405</SRS>
+ <SRS>EPSG:62776405</SRS>
+ <SRS>EPSG:62786405</SRS>
+ <SRS>EPSG:62796405</SRS>
+ <SRS>EPSG:62806405</SRS>
+ <SRS>EPSG:62816405</SRS>
+ <SRS>EPSG:62826405</SRS>
+ <SRS>EPSG:62836405</SRS>
+ <SRS>EPSG:62836413</SRS>
+ <SRS>EPSG:62846405</SRS>
+ <SRS>EPSG:62856405</SRS>
+ <SRS>EPSG:62866405</SRS>
+ <SRS>EPSG:62886405</SRS>
+ <SRS>EPSG:62896405</SRS>
+ <SRS>EPSG:62926405</SRS>
+ <SRS>EPSG:62936405</SRS>
+ <SRS>EPSG:62956405</SRS>
+ <SRS>EPSG:62976405</SRS>
+ <SRS>EPSG:62986405</SRS>
+ <SRS>EPSG:62996405</SRS>
+ <SRS>EPSG:63006405</SRS>
+ <SRS>EPSG:63016405</SRS>
+ <SRS>EPSG:63026405</SRS>
+ <SRS>EPSG:63036405</SRS>
+ <SRS>EPSG:63046405</SRS>
+ <SRS>EPSG:63066405</SRS>
+ <SRS>EPSG:63076405</SRS>
+ <SRS>EPSG:63086405</SRS>
+ <SRS>EPSG:63096405</SRS>
+ <SRS>EPSG:63106405</SRS>
+ <SRS>EPSG:63116405</SRS>
+ <SRS>EPSG:63126405</SRS>
+ <SRS>EPSG:63136405</SRS>
+ <SRS>EPSG:63146405</SRS>
+ <SRS>EPSG:63156405</SRS>
+ <SRS>EPSG:63166405</SRS>
+ <SRS>EPSG:63176405</SRS>
+ <SRS>EPSG:63186405</SRS>
+ <SRS>EPSG:63196405</SRS>
+ <SRS>EPSG:63226405</SRS>
+ <SRS>EPSG:63246405</SRS>
+ <SRS>EPSG:63266405</SRS>
+ <SRS>EPSG:63266406</SRS>
+ <SRS>EPSG:63266407</SRS>
+ <SRS>EPSG:63266408</SRS>
+ <SRS>EPSG:63266409</SRS>
+ <SRS>EPSG:63266410</SRS>
+ <SRS>EPSG:63266411</SRS>
+ <SRS>EPSG:63266412</SRS>
+ <SRS>EPSG:63266413</SRS>
+ <SRS>EPSG:63266414</SRS>
+ <SRS>EPSG:63266415</SRS>
+ <SRS>EPSG:63266416</SRS>
+ <SRS>EPSG:63266417</SRS>
+ <SRS>EPSG:63266418</SRS>
+ <SRS>EPSG:63266419</SRS>
+ <SRS>EPSG:63266420</SRS>
+ <SRS>EPSG:66006405</SRS>
+ <SRS>EPSG:66016405</SRS>
+ <SRS>EPSG:66026405</SRS>
+ <SRS>EPSG:66036405</SRS>
+ <SRS>EPSG:66046405</SRS>
+ <SRS>EPSG:66056405</SRS>
+ <SRS>EPSG:66066405</SRS>
+ <SRS>EPSG:66076405</SRS>
+ <SRS>EPSG:66086405</SRS>
+ <SRS>EPSG:66096405</SRS>
+ <SRS>EPSG:66106405</SRS>
+ <SRS>EPSG:66116405</SRS>
+ <SRS>EPSG:66126405</SRS>
+ <SRS>EPSG:66126413</SRS>
+ <SRS>EPSG:66136405</SRS>
+ <SRS>EPSG:66146405</SRS>
+ <SRS>EPSG:66156405</SRS>
+ <SRS>EPSG:66166405</SRS>
+ <SRS>EPSG:66186405</SRS>
+ <SRS>EPSG:66196405</SRS>
+ <SRS>EPSG:66196413</SRS>
+ <SRS>EPSG:66206405</SRS>
+ <SRS>EPSG:66216405</SRS>
+ <SRS>EPSG:66226405</SRS>
+ <SRS>EPSG:66236405</SRS>
+ <SRS>EPSG:66246405</SRS>
+ <SRS>EPSG:66246413</SRS>
+ <SRS>EPSG:66256405</SRS>
+ <SRS>EPSG:66266405</SRS>
+ <SRS>EPSG:66276405</SRS>
+ <SRS>EPSG:66276413</SRS>
+ <SRS>EPSG:66286405</SRS>
+ <SRS>EPSG:66296405</SRS>
+ <SRS>EPSG:66306405</SRS>
+ <SRS>EPSG:66316405</SRS>
+ <SRS>EPSG:66326405</SRS>
+ <SRS>EPSG:66336405</SRS>
+ <SRS>EPSG:66346405</SRS>
+ <SRS>EPSG:66356405</SRS>
+ <SRS>EPSG:66366405</SRS>
+ <SRS>EPSG:66376405</SRS>
+ <SRS>EPSG:66386405</SRS>
+ <SRS>EPSG:66396405</SRS>
+ <SRS>EPSG:66406405</SRS>
+ <SRS>EPSG:66406413</SRS>
+ <SRS>EPSG:66416405</SRS>
+ <SRS>EPSG:66426405</SRS>
+ <SRS>EPSG:66436405</SRS>
+ <SRS>EPSG:66446405</SRS>
+ <SRS>EPSG:66456405</SRS>
+ <SRS>EPSG:66456413</SRS>
+ <SRS>EPSG:66466405</SRS>
+ <SRS>EPSG:66576405</SRS>
+ <SRS>EPSG:66586405</SRS>
+ <SRS>EPSG:66596405</SRS>
+ <SRS>EPSG:66596413</SRS>
+ <SRS>EPSG:66606405</SRS>
+ <SRS>EPSG:66616405</SRS>
+ <SRS>EPSG:66616413</SRS>
+ <SRS>EPSG:66636405</SRS>
+ <SRS>EPSG:66646405</SRS>
+ <SRS>EPSG:66656405</SRS>
+ <SRS>EPSG:66666405</SRS>
+ <SRS>EPSG:66676405</SRS>
+ <SRS>EPSG:68016405</SRS>
+ <SRS>EPSG:68026405</SRS>
+ <SRS>EPSG:68036405</SRS>
+ <SRS>EPSG:68046405</SRS>
+ <SRS>EPSG:68056405</SRS>
+ <SRS>EPSG:68066405</SRS>
+ <SRS>EPSG:68086405</SRS>
+ <SRS>EPSG:68096405</SRS>
+ <SRS>EPSG:68136405</SRS>
+ <SRS>EPSG:68146405</SRS>
+ <SRS>EPSG:68156405</SRS>
+ <SRS>EPSG:68186405</SRS>
+ <SRS>EPSG:68206405</SRS>
+ <SRS>EPSG:69036405</SRS>
+ <SRS>EPSG:42302</SRS>
+ <SRS>EPSG:42301</SRS>
+ <SRS>EPSG:900913</SRS>
+ <SRS>EPSG:45556</SRS>
+ <SRS>EPSG:45555</SRS>
+ <SRS>EPSG:54004</SRS>
+ <SRS>EPSG:41001</SRS>
+ <SRS>EPSG:42311</SRS>
+ <SRS>EPSG:42310</SRS>
+ <SRS>EPSG:18001</SRS>
+ <SRS>EPSG:100003</SRS>
+ <SRS>EPSG:42106</SRS>
+ <SRS>EPSG:100002</SRS>
+ <SRS>EPSG:42105</SRS>
+ <SRS>EPSG:100001</SRS>
+ <SRS>EPSG:42309</SRS>
+ <SRS>EPSG:42104</SRS>
+ <SRS>EPSG:42308</SRS>
+ <SRS>EPSG:42103</SRS>
+ <SRS>EPSG:42307</SRS>
+ <SRS>EPSG:42102</SRS>
+ <SRS>EPSG:42306</SRS>
+ <SRS>EPSG:42101</SRS>
+ <SRS>EPSG:42305</SRS>
+ <SRS>EPSG:42304</SRS>
+ <SRS>EPSG:42303</SRS>
+ <LatLonBoundingBox minx="-297176.16529836657" miny="-1.2694600326676274E7" maxx="3.0016785704606913E7" maxy="1.7619361543229006E7"/>
+ <Layer queryable="1">
+ <Name>og:bugsites</Name>
+ <Title/>
+ <Abstract>Sample data from GRASS, bug sites location, Spearfish, South Dakota, USA</Abstract>
+ <KeywordList>
+ <Keyword>spearfish</Keyword>
+ <Keyword>sfBugsites</Keyword>
+ <Keyword>insects</Keyword>
+ <Keyword>bugsites</Keyword>
+ <Keyword>tiger_beetles</Keyword>
+ </KeywordList>
+ <SRS>EPSG:26713</SRS>
+ <!--WKT definition of this CRS:
+PROJCS["NAD27 / UTM zone 13N",
+ GEOGCS["NAD27",
+ DATUM["North American Datum 1927",
+ SPHEROID["Clarke 1866", 6378206.4, 294.9786982138982, AUTHORITY["EPSG","7008"]],
+ TOWGS84[-4.2, 135.4, 181.9, 0.0, 0.0, 0.0, 0.0],
+ AUTHORITY["EPSG","6267"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4267"]],
+ PROJECTION["Transverse Mercator", AUTHORITY["EPSG","9807"]],
+ PARAMETER["central_meridian", -105.0],
+ PARAMETER["latitude_of_origin", 0.0],
+ PARAMETER["scale_factor", 0.9996],
+ PARAMETER["false_easting", 500000.0],
+ PARAMETER["false_northing", 0.0],
+ UNIT["m", 1.0],
+ AXIS["Easting", EAST],
+ AXIS["Northing", NORTH],
+ AUTHORITY["EPSG","26713"]]-->
+ <LatLonBoundingBox minx="-103.8701581843142" miny="44.286540361238224" maxx="-103.63532819794625" maxy="44.52137034760618"/>
+ <BoundingBox SRS="EPSG:26713" minx="590232.0" miny="4914096.0" maxx="608471.0" maxy="4920512.0"/>
+ <Style>
+ <Name>capitals</Name>
+ <Title>Capital cities</Title>
+ <Abstract/>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=og:bugsites"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>og:restricted</Name>
+ <Title/>
+ <Abstract>Sample data from GRASS, restricted areas, Spearfish, South Dakota, USA</Abstract>
+ <KeywordList>
+ <Keyword>spearfish</Keyword>
+ <Keyword>restricted</Keyword>
+ <Keyword>sfRestricted</Keyword>
+ <Keyword>areas</Keyword>
+ </KeywordList>
+ <SRS>EPSG:26713</SRS>
+ <!--WKT definition of this CRS:
+PROJCS["NAD27 / UTM zone 13N",
+ GEOGCS["NAD27",
+ DATUM["North American Datum 1927",
+ SPHEROID["Clarke 1866", 6378206.4, 294.9786982138982, AUTHORITY["EPSG","7008"]],
+ TOWGS84[-4.2, 135.4, 181.9, 0.0, 0.0, 0.0, 0.0],
+ AUTHORITY["EPSG","6267"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4267"]],
+ PROJECTION["Transverse Mercator", AUTHORITY["EPSG","9807"]],
+ PARAMETER["central_meridian", -105.0],
+ PARAMETER["latitude_of_origin", 0.0],
+ PARAMETER["scale_factor", 0.9996],
+ PARAMETER["false_easting", 500000.0],
+ PARAMETER["false_northing", 0.0],
+ UNIT["m", 1.0],
+ AXIS["Easting", EAST],
+ AXIS["Northing", NORTH],
+ AUTHORITY["EPSG","26713"]]-->
+ <LatLonBoundingBox minx="-104.36424600670885" miny="43.78798270975212" maxx="-103.06226503558304" maxy="45.089963680877936"/>
+ <BoundingBox SRS="EPSG:26713" minx="551796.8125" miny="4901896.0" maxx="652788.5625" maxy="4940954.0"/>
+ <Style>
+ <Name>restricted</Name>
+ <Title>Red, translucent style</Title>
+ <Abstract>A sample style that just prints out a transparent red interior with a red outline</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=og:restricted"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>og:archsites</Name>
+ <Title/>
+ <Abstract>Sample data from GRASS, archeological sites location, Spearfish, South Dakota, USA</Abstract>
+ <KeywordList>
+ <Keyword>archsites</Keyword>
+ <Keyword>spearfish</Keyword>
+ <Keyword>sfArchsites</Keyword>
+ <Keyword>archeology</Keyword>
+ </KeywordList>
+ <SRS>EPSG:26713</SRS>
+ <!--WKT definition of this CRS:
+PROJCS["NAD27 / UTM zone 13N",
+ GEOGCS["NAD27",
+ DATUM["North American Datum 1927",
+ SPHEROID["Clarke 1866", 6378206.4, 294.9786982138982, AUTHORITY["EPSG","7008"]],
+ TOWGS84[-4.2, 135.4, 181.9, 0.0, 0.0, 0.0, 0.0],
+ AUTHORITY["EPSG","6267"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4267"]],
+ PROJECTION["Transverse Mercator", AUTHORITY["EPSG","9807"]],
+ PARAMETER["central_meridian", -105.0],
+ PARAMETER["latitude_of_origin", 0.0],
+ PARAMETER["scale_factor", 0.9996],
+ PARAMETER["false_easting", 500000.0],
+ PARAMETER["false_northing", 0.0],
+ UNIT["m", 1.0],
+ AXIS["Easting", EAST],
+ AXIS["Northing", NORTH],
+ AUTHORITY["EPSG","26713"]]-->
+ <LatLonBoundingBox minx="-103.87480459767542" miny="44.31295793136913" maxx="-103.63549073047534" maxy="44.55227179856921"/>
+ <BoundingBox SRS="EPSG:26713" minx="589860.0" miny="4914479.0" maxx="608355.0" maxy="4926490.0"/>
+ <Style>
+ <Name>point</Name>
+ <Title>Default point</Title>
+ <Abstract>A sample style that just prints out a 6px wide red square</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=og:archsites"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>og:streams</Name>
+ <Title/>
+ <Abstract>Sample data from GRASS, streams, Spearfish, South Dakota, USA</Abstract>
+ <KeywordList>
+ <Keyword>spearfish</Keyword>
+ <Keyword>sfStreams</Keyword>
+ <Keyword>streams</Keyword>
+ </KeywordList>
+ <SRS>EPSG:26713</SRS>
+ <!--WKT definition of this CRS:
+PROJCS["NAD27 / UTM zone 13N",
+ GEOGCS["NAD27",
+ DATUM["North American Datum 1927",
+ SPHEROID["Clarke 1866", 6378206.4, 294.9786982138982, AUTHORITY["EPSG","7008"]],
+ TOWGS84[-4.2, 135.4, 181.9, 0.0, 0.0, 0.0, 0.0],
+ AUTHORITY["EPSG","6267"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4267"]],
+ PROJECTION["Transverse Mercator", AUTHORITY["EPSG","9807"]],
+ PARAMETER["central_meridian", -105.0],
+ PARAMETER["latitude_of_origin", 0.0],
+ PARAMETER["scale_factor", 0.9996],
+ PARAMETER["false_easting", 500000.0],
+ PARAMETER["false_northing", 0.0],
+ UNIT["m", 1.0],
+ AXIS["Easting", EAST],
+ AXIS["Northing", NORTH],
+ AUTHORITY["EPSG","26713"]]-->
+ <LatLonBoundingBox minx="-103.88033574142051" miny="44.30711172484593" maxx="-103.62022283326024" maxy="44.5672246330062"/>
+ <BoundingBox SRS="EPSG:26713" minx="589443.0" miny="4913935.0" maxx="609526.75" maxy="4928059.5"/>
+ <Style>
+ <Name>simple_streams</Name>
+ <Title>Default Styler for streams segments</Title>
+ <Abstract>Blue lines, 2px wide</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=og:streams"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>tiger:poly_landmarks</Name>
+ <Title>Manhattan (NY) landmarks</Title>
+ <Abstract>Manhattan landmarks, identifies water, lakes, parks, interesting buildilngs</Abstract>
+ <KeywordList>
+ <Keyword>DS_poly_landmarks</Keyword>
+ <Keyword>landmarks</Keyword>
+ <Keyword>manhattan</Keyword>
+ <Keyword>poly_landmarks</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <LatLonBoundingBox minx="-74.0828672737" miny="40.67246384130001" maxx="-73.8660689563" maxy="40.8892621587"/>
+ <BoundingBox SRS="EPSG:4326" minx="-74.047185" miny="40.679648" maxx="-73.90782" maxy="40.882078"/>
+ <Style>
+ <Name>poly_landmarks</Name>
+ <Title>Default Styler</Title>
+ <Abstract/>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tiger:poly_landmarks"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>tiger:poi</Name>
+ <Title>Manhattan (NY) points of interest</Title>
+ <Abstract>Points of interest in New York, New York (on Manhattan). One of the attributes contains the name of a file with a picture of the point of interest.</Abstract>
+ <KeywordList>
+ <Keyword>poi</Keyword>
+ <Keyword>Manhattan</Keyword>
+ <Keyword>DS_poi</Keyword>
+ <Keyword>points_of_interest</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <LatLonBoundingBox minx="-74.01244590356289" miny="40.70750285086222" maxx="-74.00795911725866" maxy="40.711989637166425"/>
+ <BoundingBox SRS="EPSG:4326" minx="-74.0118315772888" miny="40.70754683896324" maxx="-74.00153046439813" maxy="40.719885123828675"/>
+ <Style>
+ <Name>poi</Name>
+ <Title>Points of interest</Title>
+ <Abstract>Manhattan points of interest</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tiger:poi"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>tiger:tiger_roads</Name>
+ <Title>Manhattan (NY) roads</Title>
+ <Abstract>Highly simplified road layout of Manhattan in New York..</Abstract>
+ <KeywordList>
+ <Keyword>DS_tiger_roads</Keyword>
+ <Keyword>tiger_roads</Keyword>
+ <Keyword>roads</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <LatLonBoundingBox minx="-74.06603057" miny="40.68228143" maxx="-73.86819443" maxy="40.880117569999996"/>
+ <BoundingBox SRS="EPSG:4326" minx="-74.02722" miny="40.684221" maxx="-73.907005" maxy="40.878178"/>
+ <Style>
+ <Name>tiger_roads</Name>
+ <Title>Default Styler</Title>
+ <Abstract/>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tiger:tiger_roads"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>za:za_natural</Name>
+ <Title>Natural Landmarks in South Africa</Title>
+ <Abstract>This layer describes natural features of South Africa such as forests and lakes.</Abstract>
+ <KeywordList>
+ <Keyword>water</Keyword>
+ <Keyword>forests</Keyword>
+ <Keyword>landmarks</Keyword>
+ <Keyword>Africa</Keyword>
+ <Keyword>South</Keyword>
+ <Keyword>natural</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <LatLonBoundingBox minx="16.779241142272962" miny="-36.53577846527099" maxx="32.70336002349853" maxy="-20.611659584045416"/>
+ <BoundingBox SRS="EPSG:4326" minx="16.935359954834" miny="-34.3737831115723" maxx="32.5472412109375" maxy="-22.7736549377441"/>
+ <Style>
+ <Name>za_natural</Name>
+ <Title>Default Styler</Title>
+ <Abstract/>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=za:za_natural"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>za:za_points</Name>
+ <Title>Points of Interest in South Africa</Title>
+ <Abstract>Noteworthy locations such as hotels and tourist attractions in South Africa.</Abstract>
+ <KeywordList>
+ <Keyword>of</Keyword>
+ <Keyword>tourist</Keyword>
+ <Keyword>landmarks</Keyword>
+ <Keyword>zoo</Keyword>
+ <Keyword>cities</Keyword>
+ <Keyword>interest</Keyword>
+ <Keyword>attractions</Keyword>
+ <Keyword>points</Keyword>
+ <Keyword>hotel</Keyword>
+ <Keyword>museum</Keyword>
+ <Keyword>picnic</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <LatLonBoundingBox minx="14.629095230102537" miny="-47.151258316040014" maxx="39.792314376831065" maxy="-21.988039169311488"/>
+ <BoundingBox SRS="EPSG:4326" minx="16.4827766418457" miny="-46.9045600891113" maxx="37.9386329650879" maxy="-22.2347373962402"/>
+ <Style>
+ <Name>za_points</Name>
+ <Title>Default Styler</Title>
+ <Abstract/>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=za:za_points"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>za:za_roads</Name>
+ <Title>South African Roads</Title>
+ <Abstract>This layer describes roads in South Africa.</Abstract>
+ <KeywordList>
+ <Keyword>south</Keyword>
+ <Keyword>africa</Keyword>
+ <Keyword>roads</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <LatLonBoundingBox minx="16.29388177871706" miny="-36.85438787460323" maxx="33.04232465744013" maxy="-20.10594499588016"/>
+ <BoundingBox SRS="EPSG:4326" minx="16.4580821990967" miny="-34.8331336975098" maxx="32.8781242370605" maxy="-22.1271991729736"/>
+ <Style>
+ <Name>za_roads</Name>
+ <Title>Default Styler</Title>
+ <Abstract/>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=za:za_roads"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>za:za_vegetation</Name>
+ <Title>South African Vegetation</Title>
+ <Abstract>This layer describes vegetated areas in South Africa, categorized by biome.</Abstract>
+ <KeywordList>
+ <Keyword>south</Keyword>
+ <Keyword>vegetation</Keyword>
+ <Keyword>africa</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <LatLonBoundingBox minx="16.30492322921758" miny="-36.855452365875216" maxx="33.05824930191042" maxy="-20.102126293182376"/>
+ <BoundingBox SRS="EPSG:4326" minx="16.4691715240479" miny="-34.8336486816406" maxx="32.8940010070801" maxy="-22.123929977417"/>
+ <Style>
+ <Name>za_vegetation</Name>
+ <Title>Default Styler</Title>
+ <Abstract/>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=za:za_vegetation"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>topp:tasmania_cities</Name>
+ <Title>Tasmania cities</Title>
+ <Abstract>Cities in Tasmania (actually, just the capital)</Abstract>
+ <KeywordList>
+ <Keyword>cities</Keyword>
+ <Keyword>Tasmania</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <LatLonBoundingBox minx="145.1667856" miny="-43.706631400000006" maxx="148.30373440000002" maxy="-40.56968259999999"/>
+ <BoundingBox SRS="EPSG:4326" minx="147.2910004483" miny="-42.851001816890005" maxx="147.2910004483" maxy="-42.851001816890005"/>
+ <Style>
+ <Name>capitals</Name>
+ <Title>Capital cities</Title>
+ <Abstract/>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:tasmania_cities"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>topp:tasmania_roads</Name>
+ <Title>Tasmania roads</Title>
+ <Abstract>Main Tasmania roads</Abstract>
+ <KeywordList>
+ <Keyword>Roads</Keyword>
+ <Keyword>Tasmania</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <LatLonBoundingBox minx="145.1667856" miny="-43.706631400000006" maxx="148.30373440000002" maxy="-40.56968259999999"/>
+ <BoundingBox SRS="EPSG:4326" minx="145.19754" miny="-43.423512" maxx="148.27298000000002" maxy="-40.852802"/>
+ <Style>
+ <Name>simple_roads</Name>
+ <Title>Default Styler for simple road segments</Title>
+ <Abstract>Light red line, 2px wide</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:tasmania_roads"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>topp:tasmania_state_boundaries</Name>
+ <Title>Tasmania state boundaries</Title>
+ <Abstract>Tasmania state boundaries</Abstract>
+ <KeywordList>
+ <Keyword>boundaries</Keyword>
+ <Keyword>tasmania_state_boundaries</Keyword>
+ <Keyword>Tasmania</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <LatLonBoundingBox minx="143.74100879660003" miny="-44.026947203400006" maxx="148.57295620340003" maxy="-39.194999796599994"/>
+ <BoundingBox SRS="EPSG:4326" minx="143.83482400000003" miny="-43.648056" maxx="148.47914100000003" maxy="-39.573891"/>
+ <Style>
+ <Name>green</Name>
+ <Title>Green polygon</Title>
+ <Abstract>Green fill with black outline</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:tasmania_state_boundaries"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>topp:tasmania_water_bodies</Name>
+ <Title>Tasmania water bodies</Title>
+ <Abstract>Tasmania water bodies</Abstract>
+ <KeywordList>
+ <Keyword>Lakes</Keyword>
+ <Keyword>Bodies</Keyword>
+ <Keyword>Australia</Keyword>
+ <Keyword>Water</Keyword>
+ <Keyword>Tasmania</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <LatLonBoundingBox minx="145.95490063999998" miny="-43.04450786" maxx="147.23641436" maxy="-41.762994139999996"/>
+ <BoundingBox SRS="EPSG:4326" minx="145.97161899999998" miny="-43.031944" maxx="147.219696" maxy="-41.775558"/>
+ <Style>
+ <Name>cite_lakes</Name>
+ <Title>Blue lake</Title>
+ <Abstract>A blue fill, solid black outline style</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:tasmania_water_bodies"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>topp:states</Name>
+ <Title>USA Population</Title>
+ <Abstract>This is some census data on the states.</Abstract>
+ <KeywordList>
+ <Keyword>census</Keyword>
+ <Keyword>united</Keyword>
+ <Keyword>boundaries</Keyword>
+ <Keyword>state</Keyword>
+ <Keyword>states</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <LatLonBoundingBox minx="-125.30903773" miny="7.705448770000002" maxx="-66.39223326999999" maxy="66.62225323"/>
+ <BoundingBox SRS="EPSG:4326" minx="-124.73142200000001" miny="24.955967" maxx="-66.969849" maxy="49.371735"/>
+ <Style>
+ <Name>population</Name>
+ <Title>Population in the United States</Title>
+ <Abstract>A sample filter that filters the United States into three
+ categories of population, drawn in different colors</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:states"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>tike:waterways</Name>
+ <Title>Waterways</Title>
+ <Abstract>Waterways in Finland.</Abstract>
+ <KeywordList>
+ <Keyword>Finland</Keyword>
+ <Keyword>waterways</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <LatLonBoundingBox minx="19.530168895721403" miny="58.860618000030506" maxx="31.6566005897522" maxy="70.9870496940613"/>
+ <BoundingBox SRS="EPSG:4326" minx="19.649055480957" miny="59.9357719421387" maxx="31.5377140045166" maxy="69.9118957519531"/>
+ <Style>
+ <Name>line</Name>
+ <Title>1 px blue line</Title>
+ <Abstract>Default line style, 1 pixel wide blue</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tike:waterways"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>tike:railways</Name>
+ <Title>roads_Type</Title>
+ <Abstract>Generated from tike</Abstract>
+ <KeywordList>
+ <Keyword>tike</Keyword>
+ <Keyword>roads</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <LatLonBoundingBox minx="-297176.16529836657" miny="-1.2694600326676274E7" maxx="3.0016785704606913E7" maxy="1.7619361543229006E7"/>
+ <BoundingBox SRS="EPSG:4326" minx="19.5393085479736" miny="-2277.78344726562" maxx="2.971959E7" maxy="4927039.0"/>
+ <Style>
+ <Name>line</Name>
+ <Title>1 px blue line</Title>
+ <Abstract>Default line style, 1 pixel wide blue</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tike:railways"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>tike:roads</Name>
+ <Title>roads_Type</Title>
+ <Abstract>Generated from tike</Abstract>
+ <KeywordList>
+ <Keyword>tike</Keyword>
+ <Keyword>roads</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <LatLonBoundingBox minx="-297176.16529836657" miny="-1.2694600326676274E7" maxx="3.0016785704606913E7" maxy="1.7619361543229006E7"/>
+ <BoundingBox SRS="EPSG:4326" minx="19.5393085479736" miny="-2277.78344726562" maxx="2.971959E7" maxy="4927039.0"/>
+ <Style>
+ <Name>line</Name>
+ <Title>1 px blue line</Title>
+ <Abstract>Default line style, 1 pixel wide blue</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tike:roads"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>og:roads</Name>
+ <Title>roads_Type</Title>
+ <Abstract>Generated from sf_reset</Abstract>
+ <KeywordList>
+ <Keyword>roads</Keyword>
+ <Keyword>sf_reset</Keyword>
+ </KeywordList>
+ <SRS>EPSG:26713</SRS>
+ <!--WKT definition of this CRS:
+PROJCS["NAD27 / UTM zone 13N",
+ GEOGCS["NAD27",
+ DATUM["North American Datum 1927",
+ SPHEROID["Clarke 1866", 6378206.4, 294.9786982138982, AUTHORITY["EPSG","7008"]],
+ TOWGS84[-4.2, 135.4, 181.9, 0.0, 0.0, 0.0, 0.0],
+ AUTHORITY["EPSG","6267"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4267"]],
+ PROJECTION["Transverse Mercator", AUTHORITY["EPSG","9807"]],
+ PARAMETER["central_meridian", -105.0],
+ PARAMETER["latitude_of_origin", 0.0],
+ PARAMETER["scale_factor", 0.9996],
+ PARAMETER["false_easting", 500000.0],
+ PARAMETER["false_northing", 0.0],
+ UNIT["m", 1.0],
+ AXIS["Easting", EAST],
+ AXIS["Northing", NORTH],
+ AUTHORITY["EPSG","26713"]]-->
+ <LatLonBoundingBox minx="-103.88042792817339" miny="44.308776913708805" maxx="-103.62014761945467" maxy="44.56905722242751"/>
+ <BoundingBox SRS="EPSG:26713" minx="589434.8125" miny="4914006.0" maxx="609527.25" maxy="4928377.0"/>
+ <Style>
+ <Name>simple_roads</Name>
+ <Title>Default Styler for simple road segments</Title>
+ <Abstract>Light red line, 2px wide</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=og:roads"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>tike:points</Name>
+ <Title>roads_Type</Title>
+ <Abstract>Generated from tike</Abstract>
+ <KeywordList>
+ <Keyword>tike</Keyword>
+ <Keyword>roads</Keyword>
+ </KeywordList>
+ <SRS>EPSG:4326</SRS>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <LatLonBoundingBox minx="19.73377216339108" miny="59.107116584777835" maxx="31.40053188323972" maxy="70.77387630462647"/>
+ <BoundingBox SRS="EPSG:4326" minx="19.8481521606445" miny="59.8213005065918" maxx="31.2861518859863" maxy="70.0596923828125"/>
+ <Style>
+ <Name>line</Name>
+ <Title>1 px blue line</Title>
+ <Abstract>Default line style, 1 pixel wide blue</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=tike:points"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>topp:bluemarble</Name>
+ <Title>Blue Marble Imagery</Title>
+ <Abstract>Blue Marble NG global bathymetry and topography data from NASA. More information about the Blue Marble NG project is available from http://earthobservatory.nasa.gov/Features/BlueMarble .</Abstract>
+ <KeywordList>
+ <Keyword>WCS</Keyword>
+ <Keyword>bluemarble</Keyword>
+ <Keyword>bluemarble</Keyword>
+ </KeywordList>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="-180.00000003333" miny="-89.99999996486703" maxx="179.99999993067" maxy="90.000000033333"/>
+ <BoundingBox SRS="EPSG:4326" minx="-180.00000003333" miny="-89.99999996486703" maxx="179.99999993067" maxy="90.000000033333"/>
+ <Style>
+ <Name>raster</Name>
+ <Title>Raster</Title>
+ <Abstract>A sample style for rasters, good for displaying imagery</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=topp:bluemarble"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>nurc:Arc_Sample</Name>
+ <Title>Global annual rainfall</Title>
+ <Abstract>Global annual rainfall in ArcGrid format</Abstract>
+ <KeywordList>
+ <Keyword>WCS</Keyword>
+ <Keyword>arcGridSample</Keyword>
+ <Keyword>arcGridSample_Coverage</Keyword>
+ </KeywordList>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="-180.0" miny="-90.0" maxx="180.0" maxy="90.0"/>
+ <BoundingBox SRS="EPSG:4326" minx="-180.0" miny="-90.0" maxx="180.0" maxy="90.0"/>
+ <Style>
+ <Name>raster</Name>
+ <Title>Raster</Title>
+ <Abstract>A sample style for rasters, good for displaying imagery</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=nurc:Arc_Sample"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>nurc:Img_Sample</Name>
+ <Title>North America sample imagery</Title>
+ <Abstract>A very rough imagery of North America</Abstract>
+ <KeywordList>
+ <Keyword>WCS</Keyword>
+ <Keyword>worldImageSample</Keyword>
+ <Keyword>worldImageSample_Coverage</Keyword>
+ </KeywordList>
+ <!--WKT definition of this CRS:
+GEOGCS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]],
+ AUTHORITY["EPSG","6326"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4326"]]-->
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="-130.85168" miny="20.7052" maxx="-62.0054" maxy="54.1141"/>
+ <BoundingBox SRS="EPSG:4326" minx="-130.85168" miny="20.7052" maxx="-62.0054" maxy="54.1141"/>
+ <Style>
+ <Name>raster</Name>
+ <Title>Raster</Title>
+ <Abstract>A sample style for rasters, good for displaying imagery</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=nurc:Img_Sample"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1">
+ <Name>sf:sfdem</Name>
+ <Title>Spearfish DEM</Title>
+ <Abstract>Digital Elevation Model data for Spearfish, South Dakota</Abstract>
+ <KeywordList>
+ <Keyword>WCS</Keyword>
+ <Keyword>sf</Keyword>
+ <Keyword>dem</Keyword>
+ <Keyword>digital</Keyword>
+ <Keyword>elevation</Keyword>
+ <Keyword>model</Keyword>
+ </KeywordList>
+ <!--WKT definition of this CRS:
+PROJCS["NAD27 / UTM zone 13N",
+ GEOGCS["NAD27",
+ DATUM["North American Datum 1927",
+ SPHEROID["Clarke 1866", 6378206.4, 294.9786982138982, AUTHORITY["EPSG","7008"]],
+ TOWGS84[-4.2, 135.4, 181.9, 0.0, 0.0, 0.0, 0.0],
+ AUTHORITY["EPSG","6267"]],
+ PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
+ UNIT["degree", 0.017453292519943295],
+ AXIS["Geodetic longitude", EAST],
+ AXIS["Geodetic latitude", NORTH],
+ AUTHORITY["EPSG","4267"]],
+ PROJECTION["Transverse Mercator", AUTHORITY["EPSG","9807"]],
+ PARAMETER["central_meridian", -105.0],
+ PARAMETER["latitude_of_origin", 0.0],
+ PARAMETER["scale_factor", 0.9996],
+ PARAMETER["false_easting", 500000.0],
+ PARAMETER["false_northing", 0.0],
+ UNIT["m", 1.0],
+ AXIS["Easting", EAST],
+ AXIS["Northing", NORTH],
+ AUTHORITY["EPSG","26713"]]-->
+ <SRS>EPSG:26713</SRS>
+ <LatLonBoundingBox minx="-103.87108701853181" miny="44.370187074132616" maxx="-103.62940739432703" maxy="44.5016011535299"/>
+ <BoundingBox SRS="EPSG:26713" minx="589980.0" miny="4913700.0" maxx="609000.0" maxy="4928010.0"/>
+ <Style>
+ <Name>dem</Name>
+ <Title>Simple DEM style</Title>
+ <Abstract>Classic elevation color progression</Abstract>
+ <LegendURL width="20" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://demo.opengeo.org/geoserver/wms/GetLegendGraphic?VERSION=1.0.0&amp;FORMAT=image/png&amp;WIDTH=20&amp;HEIGHT=20&amp;LAYER=sf:sfdem"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="0">
+ <Name>tasmania</Name>
+ <Title>tasmania</Title>
+ <Abstract>Layer-Group type layer: tasmania</Abstract>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="143.83482400000003" miny="-43.648056" maxx="148.47914100000003" maxy="-39.573891"/>
+ <BoundingBox SRS="EPSG:4326" minx="143.83482400000003" miny="-43.648056" maxx="148.47914100000003" maxy="-39.573891"/>
+ </Layer>
+ <Layer queryable="0">
+ <Name>tiger-ny</Name>
+ <Title>tiger-ny</Title>
+ <Abstract>Layer-Group type layer: tiger-ny</Abstract>
+ <SRS>EPSG:4326</SRS>
+ <LatLonBoundingBox minx="-74.047185" miny="40.679648" maxx="-73.907005" maxy="40.882078"/>
+ <BoundingBox SRS="EPSG:4326" minx="-74.047185" miny="40.679648" maxx="-73.907005" maxy="40.882078"/>
+ </Layer>
+ </Layer>
+ </Capability>
+</WMT_MS_Capabilities>
diff --git a/misc/openlayers/tests/throws.js b/misc/openlayers/tests/throws.js
new file mode 100644
index 0000000..0e2ac9a
--- /dev/null
+++ b/misc/openlayers/tests/throws.js
@@ -0,0 +1,82 @@
+/*
+
+ throws.js -- Adds a `throws_` method to AnotherWay test objects.
+
+ Copyright 2005 OpenLayers Contributors. released under the BSD License.
+
+
+ A reference to this file needs to be added to `run-tests.html` in the
+ head element after the AnotherWay classes are created:
+
+ <script type="text/javascript" src="throws.js"></script>
+
+ Then, it can be used just like the `ok`, `fail` and other such methods
+ in your unit tests.
+
+ e.g.
+
+ t.throws_(function () {new OpenLayers.View.Map.Dynamic();},
+ ReferenceError("No container supplied."),
+ "OpenLayers.View.Map.Dynamic instantiation with no container "
+ + "must throw.");
+
+ This was inspired by the `assertRaises` method of Python's unittest
+ library.
+
+ Possible future enhancements:
+
+ * Contribute to official AnotherWay distribution.
+ * Use `apply` rather than require a inner function (or as an option).
+ * Preserve the stack fields.
+
+ */
+
+Test.AnotherWay._test_object_t.prototype.throws_ =
+function (fn, expectedException, doc) {
+ /*
+
+ Executes the supplied function object catching any exception(s)
+ thrown, then verifies the supplied expected exception occurred.
+
+ If no exception is thrown the test fails.
+
+ If an exception is thrown and it does not match the supplied
+ expected exception the test fails.
+
+ If the exception thrown matches the supplied expected exception
+ the test passes.
+
+ Two exceptions "match" if Test.AnotherWay's `eq` method considers
+ the two equal when their respective stacks are ignored.
+
+ fn - The function object to be executed
+ expectedException - The exception object expected to result
+ doc - Description of the test
+
+ Note: The name of this method is `throws_` (with a trailing
+ underscore) as `throws` is a reserved identifier and can
+ not be used as a method name.
+
+ Note: This function does not preserve the stack field associated
+ with either exception.
+
+ */
+ var theCaughtException = null;
+
+ try {
+ fn();
+ } catch (innerCaughtException) {
+ // As `innerCaughtException` is not visible outside the scope
+ // of this `catch` block we need to make it visible explicitly.
+ theCaughtException = innerCaughtException;
+ }
+
+ if (theCaughtException) {
+ // We delete the stacks before comparison as they will never match.
+ delete theCaughtException.stack;
+ delete expectedException.stack;
+ this.eq(theCaughtException, expectedException, doc);
+ } else {
+ this.fail(doc);
+ }
+};