From ea346a785dc1b3f7c156f6fc33da634e1f1a627b Mon Sep 17 00:00:00 2001 From: Chris Schlaeger Date: Tue, 12 Aug 2014 21:56:44 +0200 Subject: Adding jquery, flot and openlayers to be included with the GEM. --- misc/openlayers/lib/Firebug/errorIcon.png | Bin 0 -> 457 bytes misc/openlayers/lib/Firebug/firebug.css | 209 + misc/openlayers/lib/Firebug/firebug.html | 23 + misc/openlayers/lib/Firebug/firebug.js | 674 +++ misc/openlayers/lib/Firebug/firebugx.js | 10 + misc/openlayers/lib/Firebug/infoIcon.png | Bin 0 -> 524 bytes misc/openlayers/lib/Firebug/license.txt | 30 + misc/openlayers/lib/Firebug/readme.txt | 13 + misc/openlayers/lib/Firebug/warningIcon.png | Bin 0 -> 516 bytes misc/openlayers/lib/OpenLayers.js | 429 ++ misc/openlayers/lib/OpenLayers/Animation.js | 102 + misc/openlayers/lib/OpenLayers/BaseTypes.js | 463 ++ misc/openlayers/lib/OpenLayers/BaseTypes/Bounds.js | 837 +++ misc/openlayers/lib/OpenLayers/BaseTypes/Class.js | 121 + misc/openlayers/lib/OpenLayers/BaseTypes/Date.js | 123 + .../openlayers/lib/OpenLayers/BaseTypes/Element.js | 189 + misc/openlayers/lib/OpenLayers/BaseTypes/LonLat.js | 215 + misc/openlayers/lib/OpenLayers/BaseTypes/Pixel.js | 143 + misc/openlayers/lib/OpenLayers/BaseTypes/Size.js | 89 + misc/openlayers/lib/OpenLayers/Console.js | 250 + misc/openlayers/lib/OpenLayers/Control.js | 371 ++ .../openlayers/lib/OpenLayers/Control/ArgParser.js | 182 + .../lib/OpenLayers/Control/Attribution.js | 104 + misc/openlayers/lib/OpenLayers/Control/Button.js | 44 + .../openlayers/lib/OpenLayers/Control/CacheRead.js | 156 + .../lib/OpenLayers/Control/CacheWrite.js | 257 + .../lib/OpenLayers/Control/DragFeature.js | 366 ++ misc/openlayers/lib/OpenLayers/Control/DragPan.js | 156 + .../lib/OpenLayers/Control/DrawFeature.js | 229 + .../lib/OpenLayers/Control/EditingToolbar.js | 81 + .../openlayers/lib/OpenLayers/Control/Geolocate.js | 192 + .../lib/OpenLayers/Control/GetFeature.js | 597 ++ .../openlayers/lib/OpenLayers/Control/Graticule.js | 377 ++ .../lib/OpenLayers/Control/KeyboardDefaults.js | 142 + .../lib/OpenLayers/Control/LayerSwitcher.js | 521 ++ misc/openlayers/lib/OpenLayers/Control/Measure.js | 379 ++ .../lib/OpenLayers/Control/ModifyFeature.js | 835 +++ .../lib/OpenLayers/Control/MousePosition.js | 227 + .../lib/OpenLayers/Control/NavToolbar.js | 57 + .../lib/OpenLayers/Control/Navigation.js | 345 ++ .../lib/OpenLayers/Control/NavigationHistory.js | 423 ++ .../lib/OpenLayers/Control/OverviewMap.js | 750 +++ misc/openlayers/lib/OpenLayers/Control/Pan.js | 95 + misc/openlayers/lib/OpenLayers/Control/PanPanel.js | 73 + misc/openlayers/lib/OpenLayers/Control/PanZoom.js | 233 + .../lib/OpenLayers/Control/PanZoomBar.js | 408 ++ misc/openlayers/lib/OpenLayers/Control/Panel.js | 431 ++ .../openlayers/lib/OpenLayers/Control/Permalink.js | 257 + .../openlayers/lib/OpenLayers/Control/PinchZoom.js | 157 + .../openlayers/lib/OpenLayers/Control/SLDSelect.js | 567 ++ misc/openlayers/lib/OpenLayers/Control/Scale.js | 100 + .../openlayers/lib/OpenLayers/Control/ScaleLine.js | 220 + .../lib/OpenLayers/Control/SelectFeature.js | 643 +++ misc/openlayers/lib/OpenLayers/Control/Snapping.js | 560 ++ misc/openlayers/lib/OpenLayers/Control/Split.js | 494 ++ .../lib/OpenLayers/Control/TouchNavigation.js | 182 + .../lib/OpenLayers/Control/TransformFeature.js | 624 +++ misc/openlayers/lib/OpenLayers/Control/UTFGrid.js | 240 + .../lib/OpenLayers/Control/WMSGetFeatureInfo.js | 532 ++ .../lib/OpenLayers/Control/WMTSGetFeatureInfo.js | 400 ++ misc/openlayers/lib/OpenLayers/Control/Zoom.js | 138 + misc/openlayers/lib/OpenLayers/Control/ZoomBox.js | 129 + misc/openlayers/lib/OpenLayers/Control/ZoomIn.js | 29 + misc/openlayers/lib/OpenLayers/Control/ZoomOut.js | 29 + .../openlayers/lib/OpenLayers/Control/ZoomPanel.js | 54 + .../lib/OpenLayers/Control/ZoomToMaxExtent.js | 35 + misc/openlayers/lib/OpenLayers/Events.js | 1170 ++++ .../lib/OpenLayers/Events/buttonclick.js | 206 + .../lib/OpenLayers/Events/featureclick.js | 321 ++ misc/openlayers/lib/OpenLayers/Feature.js | 225 + misc/openlayers/lib/OpenLayers/Feature/Vector.js | 510 ++ misc/openlayers/lib/OpenLayers/Filter.js | 87 + .../openlayers/lib/OpenLayers/Filter/Comparison.js | 267 + misc/openlayers/lib/OpenLayers/Filter/FeatureId.js | 87 + misc/openlayers/lib/OpenLayers/Filter/Function.js | 49 + misc/openlayers/lib/OpenLayers/Filter/Logical.js | 121 + misc/openlayers/lib/OpenLayers/Filter/Spatial.js | 122 + misc/openlayers/lib/OpenLayers/Format.js | 123 + misc/openlayers/lib/OpenLayers/Format/ArcXML.js | 1028 ++++ .../lib/OpenLayers/Format/ArcXML/Features.js | 46 + misc/openlayers/lib/OpenLayers/Format/Atom.js | 712 +++ misc/openlayers/lib/OpenLayers/Format/CQL.js | 452 ++ .../lib/OpenLayers/Format/CSWGetDomain.js | 34 + .../lib/OpenLayers/Format/CSWGetDomain/v2_0_2.js | 240 + .../lib/OpenLayers/Format/CSWGetRecords.js | 34 + .../lib/OpenLayers/Format/CSWGetRecords/v2_0_2.js | 457 ++ misc/openlayers/lib/OpenLayers/Format/Context.js | 334 ++ .../lib/OpenLayers/Format/EncodedPolyline.js | 557 ++ misc/openlayers/lib/OpenLayers/Format/Filter.js | 53 + misc/openlayers/lib/OpenLayers/Format/Filter/v1.js | 504 ++ .../lib/OpenLayers/Format/Filter/v1_0_0.js | 184 + .../lib/OpenLayers/Format/Filter/v1_1_0.js | 222 + misc/openlayers/lib/OpenLayers/Format/GML.js | 923 ++++ misc/openlayers/lib/OpenLayers/Format/GML/Base.js | 645 +++ misc/openlayers/lib/OpenLayers/Format/GML/v2.js | 193 + misc/openlayers/lib/OpenLayers/Format/GML/v3.js | 477 ++ misc/openlayers/lib/OpenLayers/Format/GPX.js | 385 ++ misc/openlayers/lib/OpenLayers/Format/GeoJSON.js | 716 +++ misc/openlayers/lib/OpenLayers/Format/GeoRSS.js | 409 ++ misc/openlayers/lib/OpenLayers/Format/JSON.js | 398 ++ misc/openlayers/lib/OpenLayers/Format/KML.js | 1517 +++++ .../lib/OpenLayers/Format/OGCExceptionReport.js | 108 + misc/openlayers/lib/OpenLayers/Format/OSM.js | 465 ++ misc/openlayers/lib/OpenLayers/Format/OWSCommon.js | 78 + .../lib/OpenLayers/Format/OWSCommon/v1.js | 318 ++ .../lib/OpenLayers/Format/OWSCommon/v1_0_0.js | 62 + .../lib/OpenLayers/Format/OWSCommon/v1_1_0.js | 116 + .../openlayers/lib/OpenLayers/Format/OWSContext.js | 86 + .../lib/OpenLayers/Format/OWSContext/v0_3_1.js | 595 ++ .../lib/OpenLayers/Format/QueryStringFilter.js | 183 + misc/openlayers/lib/OpenLayers/Format/SLD.js | 81 + misc/openlayers/lib/OpenLayers/Format/SLD/v1.js | 1309 +++++ .../openlayers/lib/OpenLayers/Format/SLD/v1_0_0.js | 46 + .../lib/OpenLayers/Format/SLD/v1_0_0_GeoServer.js | 149 + .../lib/OpenLayers/Format/SOSCapabilities.js | 48 + .../OpenLayers/Format/SOSCapabilities/v1_0_0.js | 158 + .../OpenLayers/Format/SOSGetFeatureOfInterest.js | 190 + .../lib/OpenLayers/Format/SOSGetObservation.js | 302 + misc/openlayers/lib/OpenLayers/Format/Text.js | 151 + .../lib/OpenLayers/Format/WCSCapabilities.js | 47 + .../lib/OpenLayers/Format/WCSCapabilities/v1.js | 55 + .../OpenLayers/Format/WCSCapabilities/v1_0_0.js | 170 + .../OpenLayers/Format/WCSCapabilities/v1_1_0.js | 109 + .../lib/OpenLayers/Format/WCSGetCoverage.js | 199 + misc/openlayers/lib/OpenLayers/Format/WFS.js | 223 + .../lib/OpenLayers/Format/WFSCapabilities.js | 47 + .../lib/OpenLayers/Format/WFSCapabilities/v1.js | 129 + .../OpenLayers/Format/WFSCapabilities/v1_0_0.js | 115 + .../OpenLayers/Format/WFSCapabilities/v1_1_0.js | 63 + .../OpenLayers/Format/WFSDescribeFeatureType.js | 234 + misc/openlayers/lib/OpenLayers/Format/WFST.js | 34 + misc/openlayers/lib/OpenLayers/Format/WFST/v1.js | 446 ++ .../lib/OpenLayers/Format/WFST/v1_0_0.js | 174 + .../lib/OpenLayers/Format/WFST/v1_1_0.js | 189 + misc/openlayers/lib/OpenLayers/Format/WKT.js | 392 ++ misc/openlayers/lib/OpenLayers/Format/WMC.js | 182 + misc/openlayers/lib/OpenLayers/Format/WMC/v1.js | 1267 +++++ .../openlayers/lib/OpenLayers/Format/WMC/v1_0_0.js | 104 + .../openlayers/lib/OpenLayers/Format/WMC/v1_1_0.js | 149 + .../lib/OpenLayers/Format/WMSCapabilities.js | 56 + .../lib/OpenLayers/Format/WMSCapabilities/v1.js | 368 ++ .../lib/OpenLayers/Format/WMSCapabilities/v1_1.js | 122 + .../OpenLayers/Format/WMSCapabilities/v1_1_0.js | 57 + .../OpenLayers/Format/WMSCapabilities/v1_1_1.js | 60 + .../Format/WMSCapabilities/v1_1_1_WMSC.js | 85 + .../lib/OpenLayers/Format/WMSCapabilities/v1_3.js | 128 + .../OpenLayers/Format/WMSCapabilities/v1_3_0.js | 30 + .../lib/OpenLayers/Format/WMSDescribeLayer.js | 53 + .../lib/OpenLayers/Format/WMSDescribeLayer/v1_1.js | 122 + .../lib/OpenLayers/Format/WMSGetFeatureInfo.js | 296 + .../lib/OpenLayers/Format/WMTSCapabilities.js | 230 + .../OpenLayers/Format/WMTSCapabilities/v1_0_0.js | 251 + .../lib/OpenLayers/Format/WPSCapabilities.js | 48 + .../OpenLayers/Format/WPSCapabilities/v1_0_0.js | 119 + .../lib/OpenLayers/Format/WPSDescribeProcess.js | 185 + .../openlayers/lib/OpenLayers/Format/WPSExecute.js | 395 ++ misc/openlayers/lib/OpenLayers/Format/XLS.js | 68 + misc/openlayers/lib/OpenLayers/Format/XLS/v1.js | 304 + .../openlayers/lib/OpenLayers/Format/XLS/v1_1_0.js | 48 + misc/openlayers/lib/OpenLayers/Format/XML.js | 897 +++ .../lib/OpenLayers/Format/XML/VersionedOGC.js | 212 + misc/openlayers/lib/OpenLayers/Geometry.js | 500 ++ .../lib/OpenLayers/Geometry/Collection.js | 563 ++ misc/openlayers/lib/OpenLayers/Geometry/Curve.js | 89 + .../lib/OpenLayers/Geometry/LineString.js | 646 +++ .../lib/OpenLayers/Geometry/LinearRing.js | 433 ++ .../lib/OpenLayers/Geometry/MultiLineString.js | 258 + .../lib/OpenLayers/Geometry/MultiPoint.js | 66 + .../lib/OpenLayers/Geometry/MultiPolygon.js | 42 + misc/openlayers/lib/OpenLayers/Geometry/Point.js | 283 + misc/openlayers/lib/OpenLayers/Geometry/Polygon.js | 255 + misc/openlayers/lib/OpenLayers/Handler.js | 325 ++ misc/openlayers/lib/OpenLayers/Handler/Box.js | 244 + misc/openlayers/lib/OpenLayers/Handler/Click.js | 505 ++ misc/openlayers/lib/OpenLayers/Handler/Drag.js | 547 ++ misc/openlayers/lib/OpenLayers/Handler/Feature.js | 434 ++ misc/openlayers/lib/OpenLayers/Handler/Hover.js | 180 + misc/openlayers/lib/OpenLayers/Handler/Keyboard.js | 117 + .../lib/OpenLayers/Handler/MouseWheel.js | 264 + misc/openlayers/lib/OpenLayers/Handler/Path.js | 543 ++ misc/openlayers/lib/OpenLayers/Handler/Pinch.js | 239 + misc/openlayers/lib/OpenLayers/Handler/Point.js | 556 ++ misc/openlayers/lib/OpenLayers/Handler/Polygon.js | 305 + .../lib/OpenLayers/Handler/RegularPolygon.js | 429 ++ misc/openlayers/lib/OpenLayers/Icon.js | 243 + misc/openlayers/lib/OpenLayers/Kinetic.js | 178 + misc/openlayers/lib/OpenLayers/Lang.js | 134 + misc/openlayers/lib/OpenLayers/Lang/ar.js | 32 + misc/openlayers/lib/OpenLayers/Lang/be-tarask.js | 54 + misc/openlayers/lib/OpenLayers/Lang/bg.js | 25 + misc/openlayers/lib/OpenLayers/Lang/br.js | 53 + misc/openlayers/lib/OpenLayers/Lang/ca.js | 89 + misc/openlayers/lib/OpenLayers/Lang/cs-CZ.js | 45 + misc/openlayers/lib/OpenLayers/Lang/da-DK.js | 80 + misc/openlayers/lib/OpenLayers/Lang/de.js | 55 + misc/openlayers/lib/OpenLayers/Lang/el.js | 19 + misc/openlayers/lib/OpenLayers/Lang/en-CA.js | 21 + misc/openlayers/lib/OpenLayers/Lang/en.js | 89 + misc/openlayers/lib/OpenLayers/Lang/es.js | 90 + misc/openlayers/lib/OpenLayers/Lang/fi.js | 32 + misc/openlayers/lib/OpenLayers/Lang/fr.js | 53 + misc/openlayers/lib/OpenLayers/Lang/fur.js | 35 + misc/openlayers/lib/OpenLayers/Lang/gl.js | 53 + misc/openlayers/lib/OpenLayers/Lang/gsw.js | 53 + misc/openlayers/lib/OpenLayers/Lang/hr.js | 37 + misc/openlayers/lib/OpenLayers/Lang/hsb.js | 53 + misc/openlayers/lib/OpenLayers/Lang/hu.js | 54 + misc/openlayers/lib/OpenLayers/Lang/ia.js | 53 + misc/openlayers/lib/OpenLayers/Lang/id.js | 54 + misc/openlayers/lib/OpenLayers/Lang/io.js | 19 + misc/openlayers/lib/OpenLayers/Lang/is.js | 27 + misc/openlayers/lib/OpenLayers/Lang/it.js | 80 + misc/openlayers/lib/OpenLayers/Lang/ja.js | 54 + misc/openlayers/lib/OpenLayers/Lang/km.js | 23 + misc/openlayers/lib/OpenLayers/Lang/ksh.js | 53 + misc/openlayers/lib/OpenLayers/Lang/lt.js | 47 + misc/openlayers/lib/OpenLayers/Lang/nb.js | 82 + misc/openlayers/lib/OpenLayers/Lang/nds.js | 37 + misc/openlayers/lib/OpenLayers/Lang/nl.js | 53 + misc/openlayers/lib/OpenLayers/Lang/nn.js | 19 + misc/openlayers/lib/OpenLayers/Lang/oc.js | 53 + misc/openlayers/lib/OpenLayers/Lang/pl.js | 89 + misc/openlayers/lib/OpenLayers/Lang/pt-BR.js | 54 + misc/openlayers/lib/OpenLayers/Lang/pt.js | 55 + misc/openlayers/lib/OpenLayers/Lang/ro.js | 69 + misc/openlayers/lib/OpenLayers/Lang/ru.js | 56 + misc/openlayers/lib/OpenLayers/Lang/sk.js | 44 + misc/openlayers/lib/OpenLayers/Lang/sv-SE.js | 45 + misc/openlayers/lib/OpenLayers/Lang/te.js | 27 + misc/openlayers/lib/OpenLayers/Lang/vi.js | 53 + misc/openlayers/lib/OpenLayers/Lang/zh-CN.js | 80 + misc/openlayers/lib/OpenLayers/Lang/zh-TW.js | 81 + misc/openlayers/lib/OpenLayers/Layer.js | 1377 +++++ .../lib/OpenLayers/Layer/ArcGIS93Rest.js | 225 + .../openlayers/lib/OpenLayers/Layer/ArcGISCache.js | 480 ++ misc/openlayers/lib/OpenLayers/Layer/ArcIMS.js | 425 ++ misc/openlayers/lib/OpenLayers/Layer/Bing.js | 333 ++ misc/openlayers/lib/OpenLayers/Layer/Boxes.js | 76 + misc/openlayers/lib/OpenLayers/Layer/EventPane.js | 441 ++ .../lib/OpenLayers/Layer/FixedZoomLevels.js | 319 ++ misc/openlayers/lib/OpenLayers/Layer/GeoRSS.js | 265 + misc/openlayers/lib/OpenLayers/Layer/Google.js | 809 +++ misc/openlayers/lib/OpenLayers/Layer/Google/v3.js | 351 ++ misc/openlayers/lib/OpenLayers/Layer/Grid.js | 1343 +++++ .../openlayers/lib/OpenLayers/Layer/HTTPRequest.js | 230 + misc/openlayers/lib/OpenLayers/Layer/Image.js | 259 + misc/openlayers/lib/OpenLayers/Layer/KaMap.js | 192 + misc/openlayers/lib/OpenLayers/Layer/KaMapCache.js | 143 + misc/openlayers/lib/OpenLayers/Layer/MapGuide.js | 443 ++ misc/openlayers/lib/OpenLayers/Layer/MapServer.js | 181 + misc/openlayers/lib/OpenLayers/Layer/Markers.js | 187 + misc/openlayers/lib/OpenLayers/Layer/OSM.js | 123 + misc/openlayers/lib/OpenLayers/Layer/PointGrid.js | 299 + misc/openlayers/lib/OpenLayers/Layer/PointTrack.js | 125 + .../lib/OpenLayers/Layer/SphericalMercator.js | 146 + misc/openlayers/lib/OpenLayers/Layer/TMS.js | 202 + misc/openlayers/lib/OpenLayers/Layer/Text.js | 267 + misc/openlayers/lib/OpenLayers/Layer/TileCache.js | 140 + misc/openlayers/lib/OpenLayers/Layer/UTFGrid.js | 184 + misc/openlayers/lib/OpenLayers/Layer/Vector.js | 1007 ++++ .../lib/OpenLayers/Layer/Vector/RootContainer.js | 154 + misc/openlayers/lib/OpenLayers/Layer/WMS.js | 267 + misc/openlayers/lib/OpenLayers/Layer/WMTS.js | 510 ++ misc/openlayers/lib/OpenLayers/Layer/WorldWind.js | 105 + misc/openlayers/lib/OpenLayers/Layer/XYZ.js | 172 + misc/openlayers/lib/OpenLayers/Layer/Zoomify.js | 260 + misc/openlayers/lib/OpenLayers/Map.js | 2867 ++++++++++ misc/openlayers/lib/OpenLayers/Marker.js | 241 + misc/openlayers/lib/OpenLayers/Marker/Box.js | 120 + misc/openlayers/lib/OpenLayers/Popup.js | 1065 ++++ misc/openlayers/lib/OpenLayers/Popup/Anchored.js | 195 + misc/openlayers/lib/OpenLayers/Popup/Framed.js | 343 ++ .../openlayers/lib/OpenLayers/Popup/FramedCloud.js | 227 + misc/openlayers/lib/OpenLayers/Projection.js | 322 ++ misc/openlayers/lib/OpenLayers/Protocol.js | 291 + misc/openlayers/lib/OpenLayers/Protocol/CSW.js | 30 + .../lib/OpenLayers/Protocol/CSW/v2_0_2.js | 127 + misc/openlayers/lib/OpenLayers/Protocol/HTTP.js | 580 ++ misc/openlayers/lib/OpenLayers/Protocol/SOS.js | 33 + .../lib/OpenLayers/Protocol/SOS/v1_0_0.js | 133 + misc/openlayers/lib/OpenLayers/Protocol/Script.js | 377 ++ misc/openlayers/lib/OpenLayers/Protocol/WFS.js | 86 + misc/openlayers/lib/OpenLayers/Protocol/WFS/v1.js | 453 ++ .../lib/OpenLayers/Protocol/WFS/v1_0_0.js | 44 + .../lib/OpenLayers/Protocol/WFS/v1_1_0.js | 68 + misc/openlayers/lib/OpenLayers/Renderer.js | 432 ++ misc/openlayers/lib/OpenLayers/Renderer/Canvas.js | 906 +++ .../openlayers/lib/OpenLayers/Renderer/Elements.js | 1053 ++++ misc/openlayers/lib/OpenLayers/Renderer/SVG.js | 1012 ++++ misc/openlayers/lib/OpenLayers/Renderer/VML.js | 985 ++++ misc/openlayers/lib/OpenLayers/Request.js | 429 ++ .../lib/OpenLayers/Request/XMLHttpRequest.js | 458 ++ misc/openlayers/lib/OpenLayers/Rule.js | 236 + misc/openlayers/lib/OpenLayers/SingleFile.js | 78 + misc/openlayers/lib/OpenLayers/Spherical.js | 67 + misc/openlayers/lib/OpenLayers/Strategy.js | 121 + misc/openlayers/lib/OpenLayers/Strategy/BBOX.js | 290 + misc/openlayers/lib/OpenLayers/Strategy/Cluster.js | 283 + misc/openlayers/lib/OpenLayers/Strategy/Filter.js | 159 + misc/openlayers/lib/OpenLayers/Strategy/Fixed.js | 135 + misc/openlayers/lib/OpenLayers/Strategy/Paging.js | 233 + misc/openlayers/lib/OpenLayers/Strategy/Refresh.js | 141 + misc/openlayers/lib/OpenLayers/Strategy/Save.js | 231 + misc/openlayers/lib/OpenLayers/Style.js | 448 ++ misc/openlayers/lib/OpenLayers/Style2.js | 112 + misc/openlayers/lib/OpenLayers/StyleMap.js | 161 + misc/openlayers/lib/OpenLayers/Symbolizer.js | 55 + misc/openlayers/lib/OpenLayers/Symbolizer/Line.js | 74 + misc/openlayers/lib/OpenLayers/Symbolizer/Point.js | 157 + .../lib/OpenLayers/Symbolizer/Polygon.js | 88 + .../openlayers/lib/OpenLayers/Symbolizer/Raster.js | 34 + misc/openlayers/lib/OpenLayers/Symbolizer/Text.js | 70 + misc/openlayers/lib/OpenLayers/Tile.js | 292 + misc/openlayers/lib/OpenLayers/Tile/Image.js | 510 ++ .../openlayers/lib/OpenLayers/Tile/Image/IFrame.js | 233 + misc/openlayers/lib/OpenLayers/Tile/UTFGrid.js | 252 + misc/openlayers/lib/OpenLayers/TileManager.js | 462 ++ misc/openlayers/lib/OpenLayers/Tween.js | 361 ++ misc/openlayers/lib/OpenLayers/Util.js | 1773 ++++++ .../openlayers/lib/OpenLayers/Util/vendorPrefix.js | 131 + misc/openlayers/lib/OpenLayers/WPSClient.js | 223 + misc/openlayers/lib/OpenLayers/WPSProcess.js | 501 ++ misc/openlayers/lib/Rico/Color.js | 253 + misc/openlayers/lib/Rico/Corner.js | 339 ++ misc/openlayers/lib/Rico/license.js | 19 + misc/openlayers/lib/deprecated.js | 5842 ++++++++++++++++++++ 326 files changed, 94044 insertions(+) create mode 100644 misc/openlayers/lib/Firebug/errorIcon.png create mode 100644 misc/openlayers/lib/Firebug/firebug.css create mode 100644 misc/openlayers/lib/Firebug/firebug.html create mode 100644 misc/openlayers/lib/Firebug/firebug.js create mode 100644 misc/openlayers/lib/Firebug/firebugx.js create mode 100644 misc/openlayers/lib/Firebug/infoIcon.png create mode 100644 misc/openlayers/lib/Firebug/license.txt create mode 100644 misc/openlayers/lib/Firebug/readme.txt create mode 100644 misc/openlayers/lib/Firebug/warningIcon.png create mode 100644 misc/openlayers/lib/OpenLayers.js create mode 100644 misc/openlayers/lib/OpenLayers/Animation.js create mode 100644 misc/openlayers/lib/OpenLayers/BaseTypes.js create mode 100644 misc/openlayers/lib/OpenLayers/BaseTypes/Bounds.js create mode 100644 misc/openlayers/lib/OpenLayers/BaseTypes/Class.js create mode 100644 misc/openlayers/lib/OpenLayers/BaseTypes/Date.js create mode 100644 misc/openlayers/lib/OpenLayers/BaseTypes/Element.js create mode 100644 misc/openlayers/lib/OpenLayers/BaseTypes/LonLat.js create mode 100644 misc/openlayers/lib/OpenLayers/BaseTypes/Pixel.js create mode 100644 misc/openlayers/lib/OpenLayers/BaseTypes/Size.js create mode 100644 misc/openlayers/lib/OpenLayers/Console.js create mode 100644 misc/openlayers/lib/OpenLayers/Control.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/ArgParser.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/Attribution.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/Button.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/CacheRead.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/CacheWrite.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/DragFeature.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/DragPan.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/DrawFeature.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/EditingToolbar.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/Geolocate.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/GetFeature.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/Graticule.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/KeyboardDefaults.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/LayerSwitcher.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/Measure.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/ModifyFeature.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/MousePosition.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/NavToolbar.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/Navigation.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/NavigationHistory.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/OverviewMap.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/Pan.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/PanPanel.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/PanZoom.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/PanZoomBar.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/Panel.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/Permalink.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/PinchZoom.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/SLDSelect.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/Scale.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/ScaleLine.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/SelectFeature.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/Snapping.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/Split.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/TouchNavigation.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/TransformFeature.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/UTFGrid.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/WMSGetFeatureInfo.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/WMTSGetFeatureInfo.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/Zoom.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/ZoomBox.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/ZoomIn.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/ZoomOut.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/ZoomPanel.js create mode 100644 misc/openlayers/lib/OpenLayers/Control/ZoomToMaxExtent.js create mode 100644 misc/openlayers/lib/OpenLayers/Events.js create mode 100644 misc/openlayers/lib/OpenLayers/Events/buttonclick.js create mode 100644 misc/openlayers/lib/OpenLayers/Events/featureclick.js create mode 100644 misc/openlayers/lib/OpenLayers/Feature.js create mode 100644 misc/openlayers/lib/OpenLayers/Feature/Vector.js create mode 100644 misc/openlayers/lib/OpenLayers/Filter.js create mode 100644 misc/openlayers/lib/OpenLayers/Filter/Comparison.js create mode 100644 misc/openlayers/lib/OpenLayers/Filter/FeatureId.js create mode 100644 misc/openlayers/lib/OpenLayers/Filter/Function.js create mode 100644 misc/openlayers/lib/OpenLayers/Filter/Logical.js create mode 100644 misc/openlayers/lib/OpenLayers/Filter/Spatial.js create mode 100644 misc/openlayers/lib/OpenLayers/Format.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/ArcXML.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/ArcXML/Features.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/Atom.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/CQL.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/CSWGetDomain.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/CSWGetDomain/v2_0_2.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/CSWGetRecords.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/CSWGetRecords/v2_0_2.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/Context.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/EncodedPolyline.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/Filter.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/Filter/v1.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/Filter/v1_0_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/Filter/v1_1_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/GML.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/GML/Base.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/GML/v2.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/GML/v3.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/GPX.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/GeoJSON.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/GeoRSS.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/JSON.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/KML.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/OGCExceptionReport.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/OSM.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/OWSCommon.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/OWSCommon/v1.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/OWSCommon/v1_0_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/OWSCommon/v1_1_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/OWSContext.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/OWSContext/v0_3_1.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/QueryStringFilter.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/SLD.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/SLD/v1.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/SLD/v1_0_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/SLD/v1_0_0_GeoServer.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/SOSCapabilities.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/SOSCapabilities/v1_0_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/SOSGetFeatureOfInterest.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/SOSGetObservation.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/Text.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WCSCapabilities.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WCSCapabilities/v1.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WCSCapabilities/v1_0_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WCSCapabilities/v1_1_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WCSGetCoverage.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WFS.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WFSCapabilities.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WFSCapabilities/v1.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WFSCapabilities/v1_0_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WFSCapabilities/v1_1_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WFSDescribeFeatureType.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WFST.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WFST/v1.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WFST/v1_0_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WFST/v1_1_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WKT.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMC.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMC/v1.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMC/v1_0_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMC/v1_1_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMSCapabilities.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMSCapabilities/v1.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMSCapabilities/v1_1.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMSCapabilities/v1_1_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMSCapabilities/v1_1_1.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMSCapabilities/v1_1_1_WMSC.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMSCapabilities/v1_3.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMSCapabilities/v1_3_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMSDescribeLayer.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMSDescribeLayer/v1_1.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMSGetFeatureInfo.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMTSCapabilities.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WMTSCapabilities/v1_0_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WPSCapabilities.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WPSCapabilities/v1_0_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WPSDescribeProcess.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/WPSExecute.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/XLS.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/XLS/v1.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/XLS/v1_1_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/XML.js create mode 100644 misc/openlayers/lib/OpenLayers/Format/XML/VersionedOGC.js create mode 100644 misc/openlayers/lib/OpenLayers/Geometry.js create mode 100644 misc/openlayers/lib/OpenLayers/Geometry/Collection.js create mode 100644 misc/openlayers/lib/OpenLayers/Geometry/Curve.js create mode 100644 misc/openlayers/lib/OpenLayers/Geometry/LineString.js create mode 100644 misc/openlayers/lib/OpenLayers/Geometry/LinearRing.js create mode 100644 misc/openlayers/lib/OpenLayers/Geometry/MultiLineString.js create mode 100644 misc/openlayers/lib/OpenLayers/Geometry/MultiPoint.js create mode 100644 misc/openlayers/lib/OpenLayers/Geometry/MultiPolygon.js create mode 100644 misc/openlayers/lib/OpenLayers/Geometry/Point.js create mode 100644 misc/openlayers/lib/OpenLayers/Geometry/Polygon.js create mode 100644 misc/openlayers/lib/OpenLayers/Handler.js create mode 100644 misc/openlayers/lib/OpenLayers/Handler/Box.js create mode 100644 misc/openlayers/lib/OpenLayers/Handler/Click.js create mode 100644 misc/openlayers/lib/OpenLayers/Handler/Drag.js create mode 100644 misc/openlayers/lib/OpenLayers/Handler/Feature.js create mode 100644 misc/openlayers/lib/OpenLayers/Handler/Hover.js create mode 100644 misc/openlayers/lib/OpenLayers/Handler/Keyboard.js create mode 100644 misc/openlayers/lib/OpenLayers/Handler/MouseWheel.js create mode 100644 misc/openlayers/lib/OpenLayers/Handler/Path.js create mode 100644 misc/openlayers/lib/OpenLayers/Handler/Pinch.js create mode 100644 misc/openlayers/lib/OpenLayers/Handler/Point.js create mode 100644 misc/openlayers/lib/OpenLayers/Handler/Polygon.js create mode 100644 misc/openlayers/lib/OpenLayers/Handler/RegularPolygon.js create mode 100644 misc/openlayers/lib/OpenLayers/Icon.js create mode 100644 misc/openlayers/lib/OpenLayers/Kinetic.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/ar.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/be-tarask.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/bg.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/br.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/ca.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/cs-CZ.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/da-DK.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/de.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/el.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/en-CA.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/en.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/es.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/fi.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/fr.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/fur.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/gl.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/gsw.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/hr.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/hsb.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/hu.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/ia.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/id.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/io.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/is.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/it.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/ja.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/km.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/ksh.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/lt.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/nb.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/nds.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/nl.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/nn.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/oc.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/pl.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/pt-BR.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/pt.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/ro.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/ru.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/sk.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/sv-SE.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/te.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/vi.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/zh-CN.js create mode 100644 misc/openlayers/lib/OpenLayers/Lang/zh-TW.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/ArcGIS93Rest.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/ArcGISCache.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/ArcIMS.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/Bing.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/Boxes.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/EventPane.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/FixedZoomLevels.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/GeoRSS.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/Google.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/Google/v3.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/Grid.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/HTTPRequest.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/Image.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/KaMap.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/KaMapCache.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/MapGuide.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/MapServer.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/Markers.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/OSM.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/PointGrid.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/PointTrack.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/SphericalMercator.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/TMS.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/Text.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/TileCache.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/UTFGrid.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/Vector.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/Vector/RootContainer.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/WMS.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/WMTS.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/WorldWind.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/XYZ.js create mode 100644 misc/openlayers/lib/OpenLayers/Layer/Zoomify.js create mode 100644 misc/openlayers/lib/OpenLayers/Map.js create mode 100644 misc/openlayers/lib/OpenLayers/Marker.js create mode 100644 misc/openlayers/lib/OpenLayers/Marker/Box.js create mode 100644 misc/openlayers/lib/OpenLayers/Popup.js create mode 100644 misc/openlayers/lib/OpenLayers/Popup/Anchored.js create mode 100644 misc/openlayers/lib/OpenLayers/Popup/Framed.js create mode 100644 misc/openlayers/lib/OpenLayers/Popup/FramedCloud.js create mode 100644 misc/openlayers/lib/OpenLayers/Projection.js create mode 100644 misc/openlayers/lib/OpenLayers/Protocol.js create mode 100644 misc/openlayers/lib/OpenLayers/Protocol/CSW.js create mode 100644 misc/openlayers/lib/OpenLayers/Protocol/CSW/v2_0_2.js create mode 100644 misc/openlayers/lib/OpenLayers/Protocol/HTTP.js create mode 100644 misc/openlayers/lib/OpenLayers/Protocol/SOS.js create mode 100644 misc/openlayers/lib/OpenLayers/Protocol/SOS/v1_0_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Protocol/Script.js create mode 100644 misc/openlayers/lib/OpenLayers/Protocol/WFS.js create mode 100644 misc/openlayers/lib/OpenLayers/Protocol/WFS/v1.js create mode 100644 misc/openlayers/lib/OpenLayers/Protocol/WFS/v1_0_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Protocol/WFS/v1_1_0.js create mode 100644 misc/openlayers/lib/OpenLayers/Renderer.js create mode 100644 misc/openlayers/lib/OpenLayers/Renderer/Canvas.js create mode 100644 misc/openlayers/lib/OpenLayers/Renderer/Elements.js create mode 100644 misc/openlayers/lib/OpenLayers/Renderer/SVG.js create mode 100644 misc/openlayers/lib/OpenLayers/Renderer/VML.js create mode 100644 misc/openlayers/lib/OpenLayers/Request.js create mode 100644 misc/openlayers/lib/OpenLayers/Request/XMLHttpRequest.js create mode 100644 misc/openlayers/lib/OpenLayers/Rule.js create mode 100644 misc/openlayers/lib/OpenLayers/SingleFile.js create mode 100644 misc/openlayers/lib/OpenLayers/Spherical.js create mode 100644 misc/openlayers/lib/OpenLayers/Strategy.js create mode 100644 misc/openlayers/lib/OpenLayers/Strategy/BBOX.js create mode 100644 misc/openlayers/lib/OpenLayers/Strategy/Cluster.js create mode 100644 misc/openlayers/lib/OpenLayers/Strategy/Filter.js create mode 100644 misc/openlayers/lib/OpenLayers/Strategy/Fixed.js create mode 100644 misc/openlayers/lib/OpenLayers/Strategy/Paging.js create mode 100644 misc/openlayers/lib/OpenLayers/Strategy/Refresh.js create mode 100644 misc/openlayers/lib/OpenLayers/Strategy/Save.js create mode 100644 misc/openlayers/lib/OpenLayers/Style.js create mode 100644 misc/openlayers/lib/OpenLayers/Style2.js create mode 100644 misc/openlayers/lib/OpenLayers/StyleMap.js create mode 100644 misc/openlayers/lib/OpenLayers/Symbolizer.js create mode 100644 misc/openlayers/lib/OpenLayers/Symbolizer/Line.js create mode 100644 misc/openlayers/lib/OpenLayers/Symbolizer/Point.js create mode 100644 misc/openlayers/lib/OpenLayers/Symbolizer/Polygon.js create mode 100644 misc/openlayers/lib/OpenLayers/Symbolizer/Raster.js create mode 100644 misc/openlayers/lib/OpenLayers/Symbolizer/Text.js create mode 100644 misc/openlayers/lib/OpenLayers/Tile.js create mode 100644 misc/openlayers/lib/OpenLayers/Tile/Image.js create mode 100644 misc/openlayers/lib/OpenLayers/Tile/Image/IFrame.js create mode 100644 misc/openlayers/lib/OpenLayers/Tile/UTFGrid.js create mode 100644 misc/openlayers/lib/OpenLayers/TileManager.js create mode 100644 misc/openlayers/lib/OpenLayers/Tween.js create mode 100644 misc/openlayers/lib/OpenLayers/Util.js create mode 100644 misc/openlayers/lib/OpenLayers/Util/vendorPrefix.js create mode 100644 misc/openlayers/lib/OpenLayers/WPSClient.js create mode 100644 misc/openlayers/lib/OpenLayers/WPSProcess.js create mode 100644 misc/openlayers/lib/Rico/Color.js create mode 100644 misc/openlayers/lib/Rico/Corner.js create mode 100644 misc/openlayers/lib/Rico/license.js create mode 100644 misc/openlayers/lib/deprecated.js (limited to 'misc/openlayers/lib') diff --git a/misc/openlayers/lib/Firebug/errorIcon.png b/misc/openlayers/lib/Firebug/errorIcon.png new file mode 100644 index 0000000..2d75261 Binary files /dev/null and b/misc/openlayers/lib/Firebug/errorIcon.png differ diff --git a/misc/openlayers/lib/Firebug/firebug.css b/misc/openlayers/lib/Firebug/firebug.css new file mode 100644 index 0000000..1f041c4 --- /dev/null +++ b/misc/openlayers/lib/Firebug/firebug.css @@ -0,0 +1,209 @@ + +html, body { + margin: 0; + background: #FFFFFF; + font-family: Lucida Grande, Tahoma, sans-serif; + font-size: 11px; + overflow: hidden; +} + +a { + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +.toolbar { + height: 14px; + border-top: 1px solid ThreeDHighlight; + border-bottom: 1px solid ThreeDShadow; + padding: 2px 6px; + background: ThreeDFace; +} + +.toolbarRight { + position: absolute; + top: 4px; + right: 6px; +} + +#log { + overflow: auto; + position: absolute; + left: 0; + width: 100%; +} + +#commandLine { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 18px; + border: none; + border-top: 1px solid ThreeDShadow; +} + +/************************************************************************************************/ + +.logRow { + position: relative; + border-bottom: 1px solid #D7D7D7; + padding: 2px 4px 1px 6px; + background-color: #FFFFFF; +} + +.logRow-command { + font-family: Monaco, monospace; + color: blue; +} + +.objectBox-null { + padding: 0 2px; + border: 1px solid #666666; + background-color: #888888; + color: #FFFFFF; +} + +.objectBox-string { + font-family: Monaco, monospace; + color: red; + white-space: pre; +} + +.objectBox-number { + color: #000088; +} + +.objectBox-function { + font-family: Monaco, monospace; + color: DarkGreen; +} + +.objectBox-object { + color: DarkGreen; + font-weight: bold; +} + +/************************************************************************************************/ + +.logRow-info, +.logRow-error, +.logRow-warning { + background: #FFFFFF no-repeat 2px 2px; + padding-left: 20px; + padding-bottom: 3px; +} + +.logRow-info { + background-image: url(infoIcon.png); +} + +.logRow-warning { + background-color: cyan; + background-image: url(warningIcon.png); +} + +.logRow-error { + background-color: LightYellow; + background-image: url(errorIcon.png); +} + +.errorMessage { + vertical-align: top; + color: #FF0000; +} + +.objectBox-sourceLink { + position: absolute; + right: 4px; + top: 2px; + padding-left: 8px; + font-family: Lucida Grande, sans-serif; + font-weight: bold; + color: #0000FF; +} + +/************************************************************************************************/ + +.logRow-group { + background: #EEEEEE; + border-bottom: none; +} + +.logGroup { + background: #EEEEEE; +} + +.logGroupBox { + margin-left: 24px; + border-top: 1px solid #D7D7D7; + border-left: 1px solid #D7D7D7; +} + +/************************************************************************************************/ + +.selectorTag, +.selectorId, +.selectorClass { + font-family: Monaco, monospace; + font-weight: normal; +} + +.selectorTag { + color: #0000FF; +} + +.selectorId { + color: DarkBlue; +} + +.selectorClass { + color: red; +} + +/************************************************************************************************/ + +.objectBox-element { + font-family: Monaco, monospace; + color: #000088; +} + +.nodeChildren { + margin-left: 16px; +} + +.nodeTag { + color: blue; +} + +.nodeValue { + color: #FF0000; + font-weight: normal; +} + +.nodeText, +.nodeComment { + margin: 0 2px; + vertical-align: top; +} + +.nodeText { + color: #333333; +} + +.nodeComment { + color: DarkGreen; +} + +/************************************************************************************************/ + +.propertyNameCell { + vertical-align: top; +} + +.propertyName { + font-weight: bold; +} diff --git a/misc/openlayers/lib/Firebug/firebug.html b/misc/openlayers/lib/Firebug/firebug.html new file mode 100644 index 0000000..861e639 --- /dev/null +++ b/misc/openlayers/lib/Firebug/firebug.html @@ -0,0 +1,23 @@ + + + + + + Firebug + + + + +
+ Clear + + Close + +
+
+ + + + + diff --git a/misc/openlayers/lib/Firebug/firebug.js b/misc/openlayers/lib/Firebug/firebug.js new file mode 100644 index 0000000..f07825e --- /dev/null +++ b/misc/openlayers/lib/Firebug/firebug.js @@ -0,0 +1,674 @@ + +if (!window.console || !console.firebug) { (function() +{ + window.console = + { + log: function() + { + logFormatted(arguments, ""); + }, + + debug: function() + { + logFormatted(arguments, "debug"); + }, + + info: function() + { + logFormatted(arguments, "info"); + }, + + warn: function() + { + logFormatted(arguments, "warning"); + }, + + error: function() + { + logFormatted(arguments, "error"); + }, + + assert: function(truth, message) + { + if (!truth) + { + var args = []; + for (var i = 1; i < arguments.length; ++i) + args.push(arguments[i]); + + logFormatted(args.length ? args : ["Assertion Failure"], "error"); + throw message ? message : "Assertion Failure"; + } + }, + + dir: function(object) + { + var html = []; + + var pairs = []; + for (var name in object) + { + try + { + pairs.push([name, object[name]]); + } + catch (exc) + { + } + } + + pairs.sort(function(a, b) { return a[0] < b[0] ? -1 : 1; }); + + html.push(''); + for (var i = 0; i < pairs.length; ++i) + { + var name = pairs[i][0], value = pairs[i][1]; + + html.push('', + '', ''); + } + html.push('
', + escapeHTML(name), ''); + appendObject(value, html); + html.push('
'); + + logRow(html, "dir"); + }, + + dirxml: function(node) + { + var html = []; + + appendNode(node, html); + logRow(html, "dirxml"); + }, + + group: function() + { + logRow(arguments, "group", pushGroup); + }, + + groupEnd: function() + { + logRow(arguments, "", popGroup); + }, + + time: function(name) + { + timeMap[name] = (new Date()).getTime(); + }, + + timeEnd: function(name) + { + if (name in timeMap) + { + var delta = (new Date()).getTime() - timeMap[name]; + logFormatted([name+ ":", delta+"ms"]); + delete timeMap[name]; + } + }, + + count: function() + { + this.warn(["count() not supported."]); + }, + + trace: function() + { + this.warn(["trace() not supported."]); + }, + + profile: function() + { + this.warn(["profile() not supported."]); + }, + + profileEnd: function() + { + }, + + clear: function() + { + consoleBody.innerHTML = ""; + }, + + open: function() + { + toggleConsole(true); + }, + + close: function() + { + if (frameVisible) + toggleConsole(); + } + }; + + // ******************************************************************************************** + + var consoleFrame = null; + var consoleBody = null; + var commandLine = null; + + var frameVisible = false; + var messageQueue = []; + var groupStack = []; + var timeMap = {}; + + var clPrefix = ">>> "; + + var isFirefox = navigator.userAgent.indexOf("Firefox") != -1; + var isIE = navigator.userAgent.indexOf("MSIE") != -1; + var isOpera = navigator.userAgent.indexOf("Opera") != -1; + var isSafari = navigator.userAgent.indexOf("AppleWebKit") != -1; + + // ******************************************************************************************** + + function toggleConsole(forceOpen) + { + frameVisible = forceOpen || !frameVisible; + if (consoleFrame) + consoleFrame.style.visibility = frameVisible ? "visible" : "hidden"; + else + waitForBody(); + } + + function focusCommandLine() + { + toggleConsole(true); + if (commandLine) + commandLine.focus(); + } + + function waitForBody() + { + if (document.body) + createFrame(); + else + setTimeout(waitForBody, 200); + } + + function createFrame() + { + if (consoleFrame) + return; + + window.onFirebugReady = function(doc) + { + window.onFirebugReady = null; + + var toolbar = doc.getElementById("toolbar"); + toolbar.onmousedown = onSplitterMouseDown; + + commandLine = doc.getElementById("commandLine"); + addEvent(commandLine, "keydown", onCommandLineKeyDown); + + addEvent(doc, isIE || isSafari ? "keydown" : "keypress", onKeyDown); + + consoleBody = doc.getElementById("log"); + layout(); + flush(); + }; + + var baseURL = getFirebugURL(); + + consoleFrame = document.createElement("iframe"); + consoleFrame.setAttribute("src", baseURL+"/firebug.html"); + consoleFrame.setAttribute("frameBorder", "0"); + consoleFrame.style.visibility = (frameVisible ? "visible" : "hidden"); + consoleFrame.style.zIndex = "2147483583"; + consoleFrame.style.position = document.all ? "absolute" : "fixed"; + consoleFrame.style.width = "100%"; + consoleFrame.style.left = "0"; + consoleFrame.style.bottom = "0"; + consoleFrame.style.height = "200px"; + document.body.appendChild(consoleFrame); + } + + function getFirebugURL() + { + var scripts = document.getElementsByTagName("script"); + for (var i = 0; i < scripts.length; ++i) + { + if (scripts[i].src.indexOf("firebug.js") != -1) + { + var lastSlash = scripts[i].src.lastIndexOf("/"); + return scripts[i].src.substr(0, lastSlash); + } + } + } + + function evalCommandLine() + { + var text = commandLine.value; + commandLine.value = ""; + + logRow([clPrefix, text], "command"); + + var value; + try + { + value = eval(text); + } + catch (exc) + { + } + + console.log(value); + } + + function layout() + { + var toolbar = consoleBody.ownerDocument.getElementById("toolbar"); + var height = consoleFrame.offsetHeight - (toolbar.offsetHeight + commandLine.offsetHeight); + height = Math.max(height, 0); + consoleBody.style.top = toolbar.offsetHeight + "px"; + consoleBody.style.height = height + "px"; + + commandLine.style.top = (consoleFrame.offsetHeight - commandLine.offsetHeight) + "px"; + } + + function logRow(message, className, handler) + { + if (consoleBody) + writeMessage(message, className, handler); + else + { + messageQueue.push([message, className, handler]); + waitForBody(); + } + } + + function flush() + { + var queue = messageQueue; + messageQueue = []; + + for (var i = 0; i < queue.length; ++i) + writeMessage(queue[i][0], queue[i][1], queue[i][2]); + } + + function writeMessage(message, className, handler) + { + var isScrolledToBottom = + consoleBody.scrollTop + consoleBody.offsetHeight >= consoleBody.scrollHeight; + + if (!handler) + handler = writeRow; + + handler(message, className); + + if (isScrolledToBottom) + consoleBody.scrollTop = consoleBody.scrollHeight - consoleBody.offsetHeight; + } + + function appendRow(row) + { + var container = groupStack.length ? groupStack[groupStack.length-1] : consoleBody; + container.appendChild(row); + } + + function writeRow(message, className) + { + var row = consoleBody.ownerDocument.createElement("div"); + row.className = "logRow" + (className ? " logRow-"+className : ""); + row.innerHTML = message.join(""); + appendRow(row); + } + + function pushGroup(message, className) + { + logFormatted(message, className); + + var groupRow = consoleBody.ownerDocument.createElement("div"); + groupRow.className = "logGroup"; + var groupRowBox = consoleBody.ownerDocument.createElement("div"); + groupRowBox.className = "logGroupBox"; + groupRow.appendChild(groupRowBox); + appendRow(groupRowBox); + groupStack.push(groupRowBox); + } + + function popGroup() + { + groupStack.pop(); + } + + // ******************************************************************************************** + + function logFormatted(objects, className) + { + var html = []; + + var format = objects[0]; + var objIndex = 0; + + if (typeof(format) != "string") + { + format = ""; + objIndex = -1; + } + + var parts = parseFormat(format); + for (var i = 0; i < parts.length; ++i) + { + var part = parts[i]; + if (part && typeof(part) == "object") + { + var object = objects[++objIndex]; + part.appender(object, html); + } + else + appendText(part, html); + } + + for (var i = objIndex+1; i < objects.length; ++i) + { + appendText(" ", html); + + var object = objects[i]; + if (typeof(object) == "string") + appendText(object, html); + else + appendObject(object, html); + } + + logRow(html, className); + } + + function parseFormat(format) + { + var parts = []; + + var reg = /((^%|[^\\]%)(\d+)?(\.)([a-zA-Z]))|((^%|[^\\]%)([a-zA-Z]))/; + var appenderMap = {s: appendText, d: appendInteger, i: appendInteger, f: appendFloat}; + + for (var m = reg.exec(format); m; m = reg.exec(format)) + { + var type = m[8] ? m[8] : m[5]; + var appender = type in appenderMap ? appenderMap[type] : appendObject; + var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0); + + parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1)); + parts.push({appender: appender, precision: precision}); + + format = format.substr(m.index+m[0].length); + } + + parts.push(format); + + return parts; + } + + function escapeHTML(value) + { + function replaceChars(ch) + { + switch (ch) + { + case "<": + return "<"; + case ">": + return ">"; + case "&": + return "&"; + case "'": + return "'"; + case '"': + return """; + } + return "?"; + }; + return String(value).replace(/[<>&"']/g, replaceChars); + } + + function objectToString(object) + { + try + { + return object+""; + } + catch (exc) + { + return null; + } + } + + // ******************************************************************************************** + + function appendText(object, html) + { + html.push(escapeHTML(objectToString(object))); + } + + function appendNull(object, html) + { + html.push('', escapeHTML(objectToString(object)), ''); + } + + function appendString(object, html) + { + html.push('"', escapeHTML(objectToString(object)), + '"'); + } + + function appendInteger(object, html) + { + html.push('', escapeHTML(objectToString(object)), ''); + } + + function appendFloat(object, html) + { + html.push('', escapeHTML(objectToString(object)), ''); + } + + function appendFunction(object, html) + { + var reName = /function ?(.*?)\(/; + var m = reName.exec(objectToString(object)); + var name = m ? m[1] : "function"; + html.push('', escapeHTML(name), '()'); + } + + function appendObject(object, html) + { + try + { + if (object == undefined) + appendNull("undefined", html); + else if (object == null) + appendNull("null", html); + else if (typeof object == "string") + appendString(object, html); + else if (typeof object == "number") + appendInteger(object, html); + else if (typeof object == "function") + appendFunction(object, html); + else if (object.nodeType == 1) + appendSelector(object, html); + else if (typeof object == "object") + appendObjectFormatted(object, html); + else + appendText(object, html); + } + catch (exc) + { + } + } + + function appendObjectFormatted(object, html) + { + var text = objectToString(object); + var reObject = /\[object (.*?)\]/; + + var m = reObject.exec(text); + html.push('', m ? m[1] : text, '') + } + + function appendSelector(object, html) + { + html.push(''); + + html.push('', escapeHTML(object.nodeName.toLowerCase()), ''); + if (object.id) + html.push('#', escapeHTML(object.id), ''); + if (object.className) + html.push('.', escapeHTML(object.className), ''); + + html.push(''); + } + + function appendNode(node, html) + { + if (node.nodeType == 1) + { + html.push( + '
', + '<', node.nodeName.toLowerCase(), ''); + + for (var i = 0; i < node.attributes.length; ++i) + { + var attr = node.attributes[i]; + if (!attr.specified) + continue; + + html.push(' ', attr.nodeName.toLowerCase(), + '="', escapeHTML(attr.nodeValue), + '"') + } + + if (node.firstChild) + { + html.push('>
'); + + for (var child = node.firstChild; child; child = child.nextSibling) + appendNode(child, html); + + html.push('
</', + node.nodeName.toLowerCase(), '>
'); + } + else + html.push('/>'); + } + else if (node.nodeType == 3) + { + html.push('
', escapeHTML(node.nodeValue), + '
'); + } + } + + // ******************************************************************************************** + + function addEvent(object, name, handler) + { + if (document.all) + object.attachEvent("on"+name, handler); + else + object.addEventListener(name, handler, false); + } + + function removeEvent(object, name, handler) + { + if (document.all) + object.detachEvent("on"+name, handler); + else + object.removeEventListener(name, handler, false); + } + + function cancelEvent(event) + { + if (document.all) + event.cancelBubble = true; + else + event.stopPropagation(); + } + + function onError(msg, href, lineNo) + { + var html = []; + + var lastSlash = href.lastIndexOf("/"); + var fileName = lastSlash == -1 ? href : href.substr(lastSlash+1); + + html.push( + '', msg, '', + '' + ); + + logRow(html, "error"); + }; + + function onKeyDown(event) + { + if (event.keyCode == 123) + toggleConsole(); + else if ((event.keyCode == 108 || event.keyCode == 76) && event.shiftKey + && (event.metaKey || event.ctrlKey)) + focusCommandLine(); + else + return; + + cancelEvent(event); + } + + function onSplitterMouseDown(event) + { + if (isSafari || isOpera) + return; + + addEvent(document, "mousemove", onSplitterMouseMove); + addEvent(document, "mouseup", onSplitterMouseUp); + + for (var i = 0; i < frames.length; ++i) + { + addEvent(frames[i].document, "mousemove", onSplitterMouseMove); + addEvent(frames[i].document, "mouseup", onSplitterMouseUp); + } + } + + function onSplitterMouseMove(event) + { + var win = document.all + ? event.srcElement.ownerDocument.parentWindow + : event.target.ownerDocument.defaultView; + + var clientY = event.clientY; + if (win != win.parent) + clientY += win.frameElement ? win.frameElement.offsetTop : 0; + + var height = consoleFrame.offsetTop + consoleFrame.clientHeight; + var toolbar = consoleBody.ownerDocument.getElementById("toolbar"); + var y = Math.max(height - clientY, + toolbar.offsetHeight + commandLine.offsetHeight); + + consoleFrame.style.height = y + "px"; + layout(); + } + + function onSplitterMouseUp(event) + { + removeEvent(document, "mousemove", onSplitterMouseMove); + removeEvent(document, "mouseup", onSplitterMouseUp); + + for (var i = 0; i < frames.length; ++i) + { + removeEvent(frames[i].document, "mousemove", onSplitterMouseMove); + removeEvent(frames[i].document, "mouseup", onSplitterMouseUp); + } + } + + function onCommandLineKeyDown(event) + { + if (event.keyCode == 13) + evalCommandLine(); + else if (event.keyCode == 27) + commandLine.value = ""; + } + + window.onerror = onError; + addEvent(document, isIE || isSafari ? "keydown" : "keypress", onKeyDown); + + if (document.documentElement.getAttribute("debug") == "true") + toggleConsole(true); +})(); +} diff --git a/misc/openlayers/lib/Firebug/firebugx.js b/misc/openlayers/lib/Firebug/firebugx.js new file mode 100644 index 0000000..f0a34df --- /dev/null +++ b/misc/openlayers/lib/Firebug/firebugx.js @@ -0,0 +1,10 @@ +(function() { + if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", + "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; + + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {} + } +})(); diff --git a/misc/openlayers/lib/Firebug/infoIcon.png b/misc/openlayers/lib/Firebug/infoIcon.png new file mode 100644 index 0000000..da1e533 Binary files /dev/null and b/misc/openlayers/lib/Firebug/infoIcon.png differ diff --git a/misc/openlayers/lib/Firebug/license.txt b/misc/openlayers/lib/Firebug/license.txt new file mode 100644 index 0000000..ba43b75 --- /dev/null +++ b/misc/openlayers/lib/Firebug/license.txt @@ -0,0 +1,30 @@ +Software License Agreement (BSD License) + +Copyright (c) 2007, Parakey Inc. +All rights reserved. + +Redistribution and use of this software in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of Parakey Inc. nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of Parakey Inc. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/misc/openlayers/lib/Firebug/readme.txt b/misc/openlayers/lib/Firebug/readme.txt new file mode 100644 index 0000000..1edebf5 --- /dev/null +++ b/misc/openlayers/lib/Firebug/readme.txt @@ -0,0 +1,13 @@ +This directory contains the source for Firebug Lite +(http://www.getfirebug.com/lite.html). This code is distributed with a +BSD License, Copyright (c) 2007, Parakey Inc. See the included license.txt +for the full text of the license. + +This is a patched version of the trunk from +http://fbug.googlecode.com/svn/trunk. + +Revision 36 was patched to resolve the issue described here +http://code.google.com/p/fbug/issues/detail?id=85 + +When this issue is resolved, Firebug Lite can be used directly - no further +modifications are needed for OpenLayers. \ No newline at end of file diff --git a/misc/openlayers/lib/Firebug/warningIcon.png b/misc/openlayers/lib/Firebug/warningIcon.png new file mode 100644 index 0000000..de51084 Binary files /dev/null and b/misc/openlayers/lib/Firebug/warningIcon.png differ diff --git a/misc/openlayers/lib/OpenLayers.js b/misc/openlayers/lib/OpenLayers.js new file mode 100644 index 0000000..03530c2 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers.js @@ -0,0 +1,429 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/* + * @requires OpenLayers/BaseTypes.js + * @requires OpenLayers/Lang/en.js + * @requires OpenLayers/Console.js + */ + +/* + * TODO: In 3.0, we will stop supporting build profiles that include + * OpenLayers.js. This means we will not need the singleFile and scriptFile + * variables, because we don't have to handle the singleFile case any more. + */ + +(function() { + /** + * Before creating the OpenLayers namespace, check to see if + * OpenLayers.singleFile is true. This occurs if the + * OpenLayers/SingleFile.js script is included before this one - as is the + * case with old single file build profiles that included both + * OpenLayers.js and OpenLayers/SingleFile.js. + */ + var singleFile = (typeof OpenLayers == "object" && OpenLayers.singleFile); + + /** + * Relative path of this script. + */ + var scriptName = (!singleFile) ? "lib/OpenLayers.js" : "OpenLayers.js"; + + /* + * If window.OpenLayers isn't set when this script (OpenLayers.js) is + * evaluated (and if singleFile is false) then this script will load + * *all* OpenLayers scripts. If window.OpenLayers is set to an array + * then this script will attempt to load scripts for each string of + * the array, using the string as the src of the script. + * + * Example: + * (code) + * + * + * (end) + * In this example OpenLayers.js will load Util.js and BaseTypes.js only. + */ + var jsFiles = window.OpenLayers; + + /** + * Namespace: OpenLayers + * The OpenLayers object provides a namespace for all things OpenLayers + */ + window.OpenLayers = { + /** + * Method: _getScriptLocation + * Return the path to this script. This is also implemented in + * OpenLayers/SingleFile.js + * + * Returns: + * {String} Path to this script + */ + _getScriptLocation: (function() { + var r = new RegExp("(^|(.*?\\/))(" + scriptName + ")(\\?|$)"), + s = document.getElementsByTagName('script'), + src, m, l = ""; + for(var i=0, len=s.length; i + * + * (end code) + * + * Please remember that when your OpenLayers script is not named + * "OpenLayers.js" you will have to make sure that the default theme is + * loaded into the page by including an appropriate -tag, + * e.g.: + * + * (code) + * + * (end code) + */ + ImgPath : '' + }; + + /** + * OpenLayers.singleFile is a flag indicating this file is being included + * in a Single File Library build of the OpenLayers Library. + * + * When we are *not* part of a SFL build we dynamically include the + * OpenLayers library code. + * + * When we *are* part of a SFL build we do not dynamically include the + * OpenLayers library code as it will be appended at the end of this file. + */ + if(!singleFile) { + if (!jsFiles) { + jsFiles = [ + "OpenLayers/BaseTypes/Class.js", + "OpenLayers/Util.js", + "OpenLayers/Util/vendorPrefix.js", + "OpenLayers/Animation.js", + "OpenLayers/BaseTypes.js", + "OpenLayers/BaseTypes/Bounds.js", + "OpenLayers/BaseTypes/Date.js", + "OpenLayers/BaseTypes/Element.js", + "OpenLayers/BaseTypes/LonLat.js", + "OpenLayers/BaseTypes/Pixel.js", + "OpenLayers/BaseTypes/Size.js", + "OpenLayers/Console.js", + "OpenLayers/Tween.js", + "OpenLayers/Kinetic.js", + "OpenLayers/Events.js", + "OpenLayers/Events/buttonclick.js", + "OpenLayers/Events/featureclick.js", + "OpenLayers/Request.js", + "OpenLayers/Request/XMLHttpRequest.js", + "OpenLayers/Projection.js", + "OpenLayers/Map.js", + "OpenLayers/Layer.js", + "OpenLayers/Icon.js", + "OpenLayers/Marker.js", + "OpenLayers/Marker/Box.js", + "OpenLayers/Popup.js", + "OpenLayers/Tile.js", + "OpenLayers/Tile/Image.js", + "OpenLayers/Tile/Image/IFrame.js", + "OpenLayers/Tile/UTFGrid.js", + "OpenLayers/Layer/Image.js", + "OpenLayers/Layer/SphericalMercator.js", + "OpenLayers/Layer/EventPane.js", + "OpenLayers/Layer/FixedZoomLevels.js", + "OpenLayers/Layer/Google.js", + "OpenLayers/Layer/Google/v3.js", + "OpenLayers/Layer/HTTPRequest.js", + "OpenLayers/Layer/Grid.js", + "OpenLayers/Layer/MapGuide.js", + "OpenLayers/Layer/MapServer.js", + "OpenLayers/Layer/KaMap.js", + "OpenLayers/Layer/KaMapCache.js", + "OpenLayers/Layer/Markers.js", + "OpenLayers/Layer/Text.js", + "OpenLayers/Layer/WorldWind.js", + "OpenLayers/Layer/ArcGIS93Rest.js", + "OpenLayers/Layer/WMS.js", + "OpenLayers/Layer/WMTS.js", + "OpenLayers/Layer/ArcIMS.js", + "OpenLayers/Layer/GeoRSS.js", + "OpenLayers/Layer/Boxes.js", + "OpenLayers/Layer/XYZ.js", + "OpenLayers/Layer/UTFGrid.js", + "OpenLayers/Layer/OSM.js", + "OpenLayers/Layer/Bing.js", + "OpenLayers/Layer/TMS.js", + "OpenLayers/Layer/TileCache.js", + "OpenLayers/Layer/Zoomify.js", + "OpenLayers/Layer/ArcGISCache.js", + "OpenLayers/Popup/Anchored.js", + "OpenLayers/Popup/Framed.js", + "OpenLayers/Popup/FramedCloud.js", + "OpenLayers/Feature.js", + "OpenLayers/Feature/Vector.js", + "OpenLayers/Handler.js", + "OpenLayers/Handler/Click.js", + "OpenLayers/Handler/Hover.js", + "OpenLayers/Handler/Point.js", + "OpenLayers/Handler/Path.js", + "OpenLayers/Handler/Polygon.js", + "OpenLayers/Handler/Feature.js", + "OpenLayers/Handler/Drag.js", + "OpenLayers/Handler/Pinch.js", + "OpenLayers/Handler/RegularPolygon.js", + "OpenLayers/Handler/Box.js", + "OpenLayers/Handler/MouseWheel.js", + "OpenLayers/Handler/Keyboard.js", + "OpenLayers/Control.js", + "OpenLayers/Control/Attribution.js", + "OpenLayers/Control/Button.js", + "OpenLayers/Control/CacheRead.js", + "OpenLayers/Control/CacheWrite.js", + "OpenLayers/Control/ZoomBox.js", + "OpenLayers/Control/ZoomToMaxExtent.js", + "OpenLayers/Control/DragPan.js", + "OpenLayers/Control/Navigation.js", + "OpenLayers/Control/PinchZoom.js", + "OpenLayers/Control/TouchNavigation.js", + "OpenLayers/Control/MousePosition.js", + "OpenLayers/Control/OverviewMap.js", + "OpenLayers/Control/KeyboardDefaults.js", + "OpenLayers/Control/PanZoom.js", + "OpenLayers/Control/PanZoomBar.js", + "OpenLayers/Control/ArgParser.js", + "OpenLayers/Control/Permalink.js", + "OpenLayers/Control/Scale.js", + "OpenLayers/Control/ScaleLine.js", + "OpenLayers/Control/Snapping.js", + "OpenLayers/Control/Split.js", + "OpenLayers/Control/LayerSwitcher.js", + "OpenLayers/Control/DrawFeature.js", + "OpenLayers/Control/DragFeature.js", + "OpenLayers/Control/ModifyFeature.js", + "OpenLayers/Control/Panel.js", + "OpenLayers/Control/SelectFeature.js", + "OpenLayers/Control/NavigationHistory.js", + "OpenLayers/Control/Measure.js", + "OpenLayers/Control/WMSGetFeatureInfo.js", + "OpenLayers/Control/WMTSGetFeatureInfo.js", + "OpenLayers/Control/Graticule.js", + "OpenLayers/Control/TransformFeature.js", + "OpenLayers/Control/UTFGrid.js", + "OpenLayers/Control/SLDSelect.js", + "OpenLayers/Control/Zoom.js", + "OpenLayers/Geometry.js", + "OpenLayers/Geometry/Collection.js", + "OpenLayers/Geometry/Point.js", + "OpenLayers/Geometry/MultiPoint.js", + "OpenLayers/Geometry/Curve.js", + "OpenLayers/Geometry/LineString.js", + "OpenLayers/Geometry/LinearRing.js", + "OpenLayers/Geometry/Polygon.js", + "OpenLayers/Geometry/MultiLineString.js", + "OpenLayers/Geometry/MultiPolygon.js", + "OpenLayers/Renderer.js", + "OpenLayers/Renderer/Elements.js", + "OpenLayers/Renderer/SVG.js", + "OpenLayers/Renderer/Canvas.js", + "OpenLayers/Renderer/VML.js", + "OpenLayers/Layer/Vector.js", + "OpenLayers/Layer/PointGrid.js", + "OpenLayers/Layer/Vector/RootContainer.js", + "OpenLayers/Strategy.js", + "OpenLayers/Strategy/Filter.js", + "OpenLayers/Strategy/Fixed.js", + "OpenLayers/Strategy/Cluster.js", + "OpenLayers/Strategy/Paging.js", + "OpenLayers/Strategy/BBOX.js", + "OpenLayers/Strategy/Save.js", + "OpenLayers/Strategy/Refresh.js", + "OpenLayers/Filter.js", + "OpenLayers/Filter/FeatureId.js", + "OpenLayers/Filter/Logical.js", + "OpenLayers/Filter/Comparison.js", + "OpenLayers/Filter/Spatial.js", + "OpenLayers/Filter/Function.js", + "OpenLayers/Protocol.js", + "OpenLayers/Protocol/HTTP.js", + "OpenLayers/Protocol/WFS.js", + "OpenLayers/Protocol/WFS/v1.js", + "OpenLayers/Protocol/WFS/v1_0_0.js", + "OpenLayers/Protocol/WFS/v1_1_0.js", + "OpenLayers/Protocol/CSW.js", + "OpenLayers/Protocol/CSW/v2_0_2.js", + "OpenLayers/Protocol/Script.js", + "OpenLayers/Protocol/SOS.js", + "OpenLayers/Protocol/SOS/v1_0_0.js", + "OpenLayers/Layer/PointTrack.js", + "OpenLayers/Style.js", + "OpenLayers/Style2.js", + "OpenLayers/StyleMap.js", + "OpenLayers/Rule.js", + "OpenLayers/Format.js", + "OpenLayers/Format/QueryStringFilter.js", + "OpenLayers/Format/XML.js", + "OpenLayers/Format/XML/VersionedOGC.js", + "OpenLayers/Format/Context.js", + "OpenLayers/Format/ArcXML.js", + "OpenLayers/Format/ArcXML/Features.js", + "OpenLayers/Format/GML.js", + "OpenLayers/Format/GML/Base.js", + "OpenLayers/Format/GML/v2.js", + "OpenLayers/Format/GML/v3.js", + "OpenLayers/Format/Atom.js", + "OpenLayers/Format/EncodedPolyline.js", + "OpenLayers/Format/KML.js", + "OpenLayers/Format/GeoRSS.js", + "OpenLayers/Format/WFS.js", + "OpenLayers/Format/OWSCommon.js", + "OpenLayers/Format/OWSCommon/v1.js", + "OpenLayers/Format/OWSCommon/v1_0_0.js", + "OpenLayers/Format/OWSCommon/v1_1_0.js", + "OpenLayers/Format/WCSCapabilities.js", + "OpenLayers/Format/WCSCapabilities/v1.js", + "OpenLayers/Format/WCSCapabilities/v1_0_0.js", + "OpenLayers/Format/WCSCapabilities/v1_1_0.js", + "OpenLayers/Format/WFSCapabilities.js", + "OpenLayers/Format/WFSCapabilities/v1.js", + "OpenLayers/Format/WFSCapabilities/v1_0_0.js", + "OpenLayers/Format/WFSCapabilities/v1_1_0.js", + "OpenLayers/Format/WFSDescribeFeatureType.js", + "OpenLayers/Format/WMSDescribeLayer.js", + "OpenLayers/Format/WMSDescribeLayer/v1_1.js", + "OpenLayers/Format/WKT.js", + "OpenLayers/Format/CQL.js", + "OpenLayers/Format/OSM.js", + "OpenLayers/Format/GPX.js", + "OpenLayers/Format/Filter.js", + "OpenLayers/Format/Filter/v1.js", + "OpenLayers/Format/Filter/v1_0_0.js", + "OpenLayers/Format/Filter/v1_1_0.js", + "OpenLayers/Format/SLD.js", + "OpenLayers/Format/SLD/v1.js", + "OpenLayers/Format/SLD/v1_0_0.js", + "OpenLayers/Format/SLD/v1_0_0_GeoServer.js", + "OpenLayers/Format/OWSCommon.js", + "OpenLayers/Format/OWSCommon/v1.js", + "OpenLayers/Format/OWSCommon/v1_0_0.js", + "OpenLayers/Format/OWSCommon/v1_1_0.js", + "OpenLayers/Format/CSWGetDomain.js", + "OpenLayers/Format/CSWGetDomain/v2_0_2.js", + "OpenLayers/Format/CSWGetRecords.js", + "OpenLayers/Format/CSWGetRecords/v2_0_2.js", + "OpenLayers/Format/WFST.js", + "OpenLayers/Format/WFST/v1.js", + "OpenLayers/Format/WFST/v1_0_0.js", + "OpenLayers/Format/WFST/v1_1_0.js", + "OpenLayers/Format/Text.js", + "OpenLayers/Format/JSON.js", + "OpenLayers/Format/GeoJSON.js", + "OpenLayers/Format/WMC.js", + "OpenLayers/Format/WMC/v1.js", + "OpenLayers/Format/WMC/v1_0_0.js", + "OpenLayers/Format/WMC/v1_1_0.js", + "OpenLayers/Format/WCSGetCoverage.js", + "OpenLayers/Format/WMSCapabilities.js", + "OpenLayers/Format/WMSCapabilities/v1.js", + "OpenLayers/Format/WMSCapabilities/v1_1.js", + "OpenLayers/Format/WMSCapabilities/v1_1_0.js", + "OpenLayers/Format/WMSCapabilities/v1_1_1.js", + "OpenLayers/Format/WMSCapabilities/v1_3.js", + "OpenLayers/Format/WMSCapabilities/v1_3_0.js", + "OpenLayers/Format/WMSCapabilities/v1_1_1_WMSC.js", + "OpenLayers/Format/WMSGetFeatureInfo.js", + "OpenLayers/Format/SOSCapabilities.js", + "OpenLayers/Format/SOSCapabilities/v1_0_0.js", + "OpenLayers/Format/SOSGetFeatureOfInterest.js", + "OpenLayers/Format/SOSGetObservation.js", + "OpenLayers/Format/OWSContext.js", + "OpenLayers/Format/OWSContext/v0_3_1.js", + "OpenLayers/Format/WMTSCapabilities.js", + "OpenLayers/Format/WMTSCapabilities/v1_0_0.js", + "OpenLayers/Format/WPSCapabilities.js", + "OpenLayers/Format/WPSCapabilities/v1_0_0.js", + "OpenLayers/Format/WPSDescribeProcess.js", + "OpenLayers/Format/WPSExecute.js", + "OpenLayers/Format/XLS.js", + "OpenLayers/Format/XLS/v1.js", + "OpenLayers/Format/XLS/v1_1_0.js", + "OpenLayers/Format/OGCExceptionReport.js", + "OpenLayers/Control/GetFeature.js", + "OpenLayers/Control/NavToolbar.js", + "OpenLayers/Control/PanPanel.js", + "OpenLayers/Control/Pan.js", + "OpenLayers/Control/ZoomIn.js", + "OpenLayers/Control/ZoomOut.js", + "OpenLayers/Control/ZoomPanel.js", + "OpenLayers/Control/EditingToolbar.js", + "OpenLayers/Control/Geolocate.js", + "OpenLayers/Symbolizer.js", + "OpenLayers/Symbolizer/Point.js", + "OpenLayers/Symbolizer/Line.js", + "OpenLayers/Symbolizer/Polygon.js", + "OpenLayers/Symbolizer/Text.js", + "OpenLayers/Symbolizer/Raster.js", + "OpenLayers/Lang.js", + "OpenLayers/Lang/en.js", + "OpenLayers/Spherical.js", + "OpenLayers/TileManager.js", + "OpenLayers/WPSClient.js", + "OpenLayers/WPSProcess.js" + ]; // etc. + } + + // use "parser-inserted scripts" for guaranteed execution order + // http://hsivonen.iki.fi/script-execution/ + var scriptTags = new Array(jsFiles.length); + var host = OpenLayers._getScriptLocation() + "lib/"; + for (var i=0, len=jsFiles.length; i"; + } + if (scriptTags.length > 0) { + document.write(scriptTags.join("")); + } + } +})(); + +/** + * Constant: VERSION_NUMBER + * + * This constant identifies the version of OpenLayers. + * + * When asking questions or reporting issues, make sure to include the output of + * OpenLayers.VERSION_NUMBER in the question or issue-description. + */ +OpenLayers.VERSION_NUMBER="Release 2.13.1"; diff --git a/misc/openlayers/lib/OpenLayers/Animation.js b/misc/openlayers/lib/OpenLayers/Animation.js new file mode 100644 index 0000000..7b47a08 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Animation.js @@ -0,0 +1,102 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/SingleFile.js + * @requires OpenLayers/Util/vendorPrefix.js + */ + +/** + * Namespace: OpenLayers.Animation + * A collection of utility functions for executing methods that repaint a + * portion of the browser window. These methods take advantage of the + * browser's scheduled repaints where requestAnimationFrame is available. + */ +OpenLayers.Animation = (function(window) { + + /** + * Property: isNative + * {Boolean} true if a native requestAnimationFrame function is available + */ + var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, "requestAnimationFrame"); + var isNative = !!(requestAnimationFrame); + + /** + * Function: requestFrame + * Schedule a function to be called at the next available animation frame. + * Uses the native method where available. Where requestAnimationFrame is + * not available, setTimeout will be called with a 16ms delay. + * + * Parameters: + * callback - {Function} The function to be called at the next animation frame. + * element - {DOMElement} Optional element that visually bounds the animation. + */ + var requestFrame = (function() { + var request = window[requestAnimationFrame] || + function(callback, element) { + window.setTimeout(callback, 16); + }; + // bind to window to avoid illegal invocation of native function + return function(callback, element) { + request.apply(window, [callback, element]); + }; + })(); + + // private variables for animation loops + var counter = 0; + var loops = {}; + + /** + * Function: start + * Executes a method with in series for some + * duration. + * + * Parameters: + * callback - {Function} The function to be called at the next animation frame. + * duration - {Number} Optional duration for the loop. If not provided, the + * animation loop will execute indefinitely. + * element - {DOMElement} Optional element that visually bounds the animation. + * + * Returns: + * {Number} Identifier for the animation loop. Used to stop animations with + * . + */ + function start(callback, duration, element) { + duration = duration > 0 ? duration : Number.POSITIVE_INFINITY; + var id = ++counter; + var start = +new Date; + loops[id] = function() { + if (loops[id] && +new Date - start <= duration) { + callback(); + if (loops[id]) { + requestFrame(loops[id], element); + } + } else { + delete loops[id]; + } + }; + requestFrame(loops[id], element); + return id; + } + + /** + * Function: stop + * Terminates an animation loop started with . + * + * Parameters: + * id - {Number} Identifier returned from . + */ + function stop(id) { + delete loops[id]; + } + + return { + isNative: isNative, + requestFrame: requestFrame, + start: start, + stop: stop + }; + +})(window); diff --git a/misc/openlayers/lib/OpenLayers/BaseTypes.js b/misc/openlayers/lib/OpenLayers/BaseTypes.js new file mode 100644 index 0000000..d416b8f --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/BaseTypes.js @@ -0,0 +1,463 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/SingleFile.js + */ + +/** + * Header: OpenLayers Base Types + * OpenLayers custom string, number and function functions are described here. + */ + +/** + * Namespace: OpenLayers.String + * Contains convenience functions for string manipulation. + */ +OpenLayers.String = { + + /** + * APIFunction: startsWith + * Test whether a string starts with another string. + * + * Parameters: + * str - {String} The string to test. + * sub - {String} The substring to look for. + * + * Returns: + * {Boolean} The first string starts with the second. + */ + startsWith: function(str, sub) { + return (str.indexOf(sub) == 0); + }, + + /** + * APIFunction: contains + * Test whether a string contains another string. + * + * Parameters: + * str - {String} The string to test. + * sub - {String} The substring to look for. + * + * Returns: + * {Boolean} The first string contains the second. + */ + contains: function(str, sub) { + return (str.indexOf(sub) != -1); + }, + + /** + * APIFunction: trim + * Removes leading and trailing whitespace characters from a string. + * + * Parameters: + * str - {String} The (potentially) space padded string. This string is not + * modified. + * + * Returns: + * {String} A trimmed version of the string with all leading and + * trailing spaces removed. + */ + trim: function(str) { + return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); + }, + + /** + * APIFunction: camelize + * Camel-case a hyphenated string. + * Ex. "chicken-head" becomes "chickenHead", and + * "-chicken-head" becomes "ChickenHead". + * + * Parameters: + * str - {String} The string to be camelized. The original is not modified. + * + * Returns: + * {String} The string, camelized + */ + camelize: function(str) { + var oStringList = str.split('-'); + var camelizedString = oStringList[0]; + for (var i=1, len=oStringList.length; i replacement = context[a]; + // 1 -> replacement = context[a][b]; + // 2 -> replacement = context[a][b][c]; + var subs = match.split(/\.+/); + for (var i=0; i< subs.length; i++) { + if (i == 0) { + replacement = context; + } + if (replacement === undefined) { + break; + } + replacement = replacement[subs[i]]; + } + + if(typeof replacement == "function") { + replacement = args ? + replacement.apply(null, args) : + replacement(); + } + + // If replacement is undefined, return the string 'undefined'. + // This is a workaround for a bugs in browsers not properly + // dealing with non-participating groups in regular expressions: + // http://blog.stevenlevithan.com/archives/npcg-javascript + if (typeof replacement == 'undefined') { + return 'undefined'; + } else { + return replacement; + } + }; + + return template.replace(OpenLayers.String.tokenRegEx, replacer); + }, + + /** + * Property: tokenRegEx + * Used to find tokens in a string. + * Examples: ${a}, ${a.b.c}, ${a-b}, ${5} + */ + tokenRegEx: /\$\{([\w.]+?)\}/g, + + /** + * Property: numberRegEx + * Used to test strings as numbers. + */ + numberRegEx: /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/, + + /** + * APIFunction: isNumeric + * Determine whether a string contains only a numeric value. + * + * Examples: + * (code) + * OpenLayers.String.isNumeric("6.02e23") // true + * OpenLayers.String.isNumeric("12 dozen") // false + * OpenLayers.String.isNumeric("4") // true + * OpenLayers.String.isNumeric(" 4 ") // false + * (end) + * + * Returns: + * {Boolean} String contains only a number. + */ + isNumeric: function(value) { + return OpenLayers.String.numberRegEx.test(value); + }, + + /** + * APIFunction: numericIf + * Converts a string that appears to be a numeric value into a number. + * + * Parameters: + * value - {String} + * trimWhitespace - {Boolean} + * + * Returns: + * {Number|String} a Number if the passed value is a number, a String + * otherwise. + */ + numericIf: function(value, trimWhitespace) { + var originalValue = value; + if (trimWhitespace === true && value != null && value.replace) { + value = value.replace(/^\s*|\s*$/g, ""); + } + return OpenLayers.String.isNumeric(value) ? parseFloat(value) : originalValue; + } + +}; + +/** + * Namespace: OpenLayers.Number + * Contains convenience functions for manipulating numbers. + */ +OpenLayers.Number = { + + /** + * Property: decimalSeparator + * Decimal separator to use when formatting numbers. + */ + decimalSeparator: ".", + + /** + * Property: thousandsSeparator + * Thousands separator to use when formatting numbers. + */ + thousandsSeparator: ",", + + /** + * APIFunction: limitSigDigs + * Limit the number of significant digits on a float. + * + * Parameters: + * num - {Float} + * sig - {Integer} + * + * Returns: + * {Float} The number, rounded to the specified number of significant + * digits. + */ + limitSigDigs: function(num, sig) { + var fig = 0; + if (sig > 0) { + fig = parseFloat(num.toPrecision(sig)); + } + return fig; + }, + + /** + * APIFunction: format + * Formats a number for output. + * + * Parameters: + * num - {Float} + * dec - {Integer} Number of decimal places to round to. + * Defaults to 0. Set to null to leave decimal places unchanged. + * tsep - {String} Thousands separator. + * Default is ",". + * dsep - {String} Decimal separator. + * Default is ".". + * + * Returns: + * {String} A string representing the formatted number. + */ + format: function(num, dec, tsep, dsep) { + dec = (typeof dec != "undefined") ? dec : 0; + tsep = (typeof tsep != "undefined") ? tsep : + OpenLayers.Number.thousandsSeparator; + dsep = (typeof dsep != "undefined") ? dsep : + OpenLayers.Number.decimalSeparator; + + if (dec != null) { + num = parseFloat(num.toFixed(dec)); + } + + var parts = num.toString().split("."); + if (parts.length == 1 && dec == null) { + // integer where we do not want to touch the decimals + dec = 0; + } + + var integer = parts[0]; + if (tsep) { + var thousands = /(-?[0-9]+)([0-9]{3})/; + while(thousands.test(integer)) { + integer = integer.replace(thousands, "$1" + tsep + "$2"); + } + } + + var str; + if (dec == 0) { + str = integer; + } else { + var rem = parts.length > 1 ? parts[1] : "0"; + if (dec != null) { + rem = rem + new Array(dec - rem.length + 1).join("0"); + } + str = integer + dsep + rem; + } + return str; + }, + + /** + * Method: zeroPad + * Create a zero padded string optionally with a radix for casting numbers. + * + * Parameters: + * num - {Number} The number to be zero padded. + * len - {Number} The length of the string to be returned. + * radix - {Number} An integer between 2 and 36 specifying the base to use + * for representing numeric values. + */ + zeroPad: function(num, len, radix) { + var str = num.toString(radix || 10); + while (str.length < len) { + str = "0" + str; + } + return str; + } +}; + +/** + * Namespace: OpenLayers.Function + * Contains convenience functions for function manipulation. + */ +OpenLayers.Function = { + /** + * APIFunction: bind + * Bind a function to an object. Method to easily create closures with + * 'this' altered. + * + * Parameters: + * func - {Function} Input function. + * object - {Object} The object to bind to the input function (as this). + * + * Returns: + * {Function} A closure with 'this' set to the passed in object. + */ + bind: function(func, object) { + // create a reference to all arguments past the second one + var args = Array.prototype.slice.apply(arguments, [2]); + return function() { + // Push on any additional arguments from the actual function call. + // These will come after those sent to the bind call. + var newArgs = args.concat( + Array.prototype.slice.apply(arguments, [0]) + ); + return func.apply(object, newArgs); + }; + }, + + /** + * APIFunction: bindAsEventListener + * Bind a function to an object, and configure it to receive the event + * object as first parameter when called. + * + * Parameters: + * func - {Function} Input function to serve as an event listener. + * object - {Object} A reference to this. + * + * Returns: + * {Function} + */ + bindAsEventListener: function(func, object) { + return function(event) { + return func.call(object, event || window.event); + }; + }, + + /** + * APIFunction: False + * A simple function to that just does "return false". We use this to + * avoid attaching anonymous functions to DOM event handlers, which + * causes "issues" on IE<8. + * + * Usage: + * document.onclick = OpenLayers.Function.False; + * + * Returns: + * {Boolean} + */ + False : function() { + return false; + }, + + /** + * APIFunction: True + * A simple function to that just does "return true". We use this to + * avoid attaching anonymous functions to DOM event handlers, which + * causes "issues" on IE<8. + * + * Usage: + * document.onclick = OpenLayers.Function.True; + * + * Returns: + * {Boolean} + */ + True : function() { + return true; + }, + + /** + * APIFunction: Void + * A reusable function that returns ``undefined``. + * + * Returns: + * {undefined} + */ + Void: function() {} + +}; + +/** + * Namespace: OpenLayers.Array + * Contains convenience functions for array manipulation. + */ +OpenLayers.Array = { + + /** + * APIMethod: filter + * Filter an array. Provides the functionality of the + * Array.prototype.filter extension to the ECMA-262 standard. Where + * available, Array.prototype.filter will be used. + * + * Based on well known example from http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/filter + * + * Parameters: + * array - {Array} The array to be filtered. This array is not mutated. + * Elements added to this array by the callback will not be visited. + * callback - {Function} A function that is called for each element in + * the array. If this function returns true, the element will be + * included in the return. The function will be called with three + * arguments: the element in the array, the index of that element, and + * the array itself. If the optional caller parameter is specified + * the callback will be called with this set to caller. + * caller - {Object} Optional object to be set as this when the callback + * is called. + * + * Returns: + * {Array} An array of elements from the passed in array for which the + * callback returns true. + */ + filter: function(array, callback, caller) { + var selected = []; + if (Array.prototype.filter) { + selected = array.filter(callback, caller); + } else { + var len = array.length; + if (typeof callback != "function") { + throw new TypeError(); + } + for(var i=0; i} A cached center location. This should not be + * accessed directly. Use instead. + */ + centerLonLat: null, + + /** + * Constructor: OpenLayers.Bounds + * Construct a new bounds object. Coordinates can either be passed as four + * arguments, or as a single argument. + * + * Parameters (four arguments): + * left - {Number} The left bounds of the box. Note that for width + * calculations, this is assumed to be less than the right value. + * bottom - {Number} The bottom bounds of the box. Note that for height + * calculations, this is assumed to be less than the top value. + * right - {Number} The right bounds. + * top - {Number} The top bounds. + * + * Parameters (single argument): + * bounds - {Array(Number)} [left, bottom, right, top] + */ + initialize: function(left, bottom, right, top) { + if (OpenLayers.Util.isArray(left)) { + top = left[3]; + right = left[2]; + bottom = left[1]; + left = left[0]; + } + if (left != null) { + this.left = OpenLayers.Util.toFloat(left); + } + if (bottom != null) { + this.bottom = OpenLayers.Util.toFloat(bottom); + } + if (right != null) { + this.right = OpenLayers.Util.toFloat(right); + } + if (top != null) { + this.top = OpenLayers.Util.toFloat(top); + } + }, + + /** + * Method: clone + * Create a cloned instance of this bounds. + * + * Returns: + * {} A fresh copy of the bounds + */ + clone:function() { + return new OpenLayers.Bounds(this.left, this.bottom, + this.right, this.top); + }, + + /** + * Method: equals + * Test a two bounds for equivalence. + * + * Parameters: + * bounds - {} + * + * Returns: + * {Boolean} The passed-in bounds object has the same left, + * right, top, bottom components as this. Note that if bounds + * passed in is null, returns false. + */ + equals:function(bounds) { + var equals = false; + if (bounds != null) { + equals = ((this.left == bounds.left) && + (this.right == bounds.right) && + (this.top == bounds.top) && + (this.bottom == bounds.bottom)); + } + return equals; + }, + + /** + * APIMethod: toString + * Returns a string representation of the bounds object. + * + * Returns: + * {String} String representation of bounds object. + */ + toString:function() { + return [this.left, this.bottom, this.right, this.top].join(","); + }, + + /** + * APIMethod: toArray + * Returns an array representation of the bounds object. + * + * Returns an array of left, bottom, right, top properties, or -- when the + * optional parameter is true -- an array of the bottom, left, top, + * right properties. + * + * Parameters: + * reverseAxisOrder - {Boolean} Should we reverse the axis order? + * + * Returns: + * {Array} array of left, bottom, right, top + */ + toArray: function(reverseAxisOrder) { + if (reverseAxisOrder === true) { + return [this.bottom, this.left, this.top, this.right]; + } else { + return [this.left, this.bottom, this.right, this.top]; + } + }, + + /** + * APIMethod: toBBOX + * Returns a boundingbox-string representation of the bounds object. + * + * Parameters: + * decimal - {Integer} How many significant digits in the bbox coords? + * Default is 6 + * reverseAxisOrder - {Boolean} Should we reverse the axis order? + * + * Returns: + * {String} Simple String representation of bounds object. + * (e.g. "5,42,10,45") + */ + toBBOX:function(decimal, reverseAxisOrder) { + if (decimal== null) { + decimal = 6; + } + var mult = Math.pow(10, decimal); + var xmin = Math.round(this.left * mult) / mult; + var ymin = Math.round(this.bottom * mult) / mult; + var xmax = Math.round(this.right * mult) / mult; + var ymax = Math.round(this.top * mult) / mult; + if (reverseAxisOrder === true) { + return ymin + "," + xmin + "," + ymax + "," + xmax; + } else { + return xmin + "," + ymin + "," + xmax + "," + ymax; + } + }, + + /** + * APIMethod: toGeometry + * Create a new polygon geometry based on this bounds. + * + * Returns: + * {} A new polygon with the coordinates + * of this bounds. + */ + toGeometry: function() { + return new OpenLayers.Geometry.Polygon([ + new OpenLayers.Geometry.LinearRing([ + new OpenLayers.Geometry.Point(this.left, this.bottom), + new OpenLayers.Geometry.Point(this.right, this.bottom), + new OpenLayers.Geometry.Point(this.right, this.top), + new OpenLayers.Geometry.Point(this.left, this.top) + ]) + ]); + }, + + /** + * APIMethod: getWidth + * Returns the width of the bounds. + * + * Returns: + * {Float} The width of the bounds (right minus left). + */ + getWidth:function() { + return (this.right - this.left); + }, + + /** + * APIMethod: getHeight + * Returns the height of the bounds. + * + * Returns: + * {Float} The height of the bounds (top minus bottom). + */ + getHeight:function() { + return (this.top - this.bottom); + }, + + /** + * APIMethod: getSize + * Returns an object of the bounds. + * + * Returns: + * {} The size of the bounds. + */ + getSize:function() { + return new OpenLayers.Size(this.getWidth(), this.getHeight()); + }, + + /** + * APIMethod: getCenterPixel + * Returns the object which represents the center of the + * bounds. + * + * Returns: + * {} The center of the bounds in pixel space. + */ + getCenterPixel:function() { + return new OpenLayers.Pixel( (this.left + this.right) / 2, + (this.bottom + this.top) / 2); + }, + + /** + * APIMethod: getCenterLonLat + * Returns the object which represents the center of the + * bounds. + * + * Returns: + * {} The center of the bounds in map space. + */ + getCenterLonLat:function() { + if(!this.centerLonLat) { + this.centerLonLat = new OpenLayers.LonLat( + (this.left + this.right) / 2, (this.bottom + this.top) / 2 + ); + } + return this.centerLonLat; + }, + + /** + * APIMethod: scale + * Scales the bounds around a pixel or lonlat. Note that the new + * bounds may return non-integer properties, even if a pixel + * is passed. + * + * Parameters: + * ratio - {Float} + * origin - { or } + * Default is center. + * + * Returns: + * {} A new bounds that is scaled by ratio + * from origin. + */ + scale: function(ratio, origin){ + if(origin == null){ + origin = this.getCenterLonLat(); + } + + var origx,origy; + + // get origin coordinates + if(origin.CLASS_NAME == "OpenLayers.LonLat"){ + origx = origin.lon; + origy = origin.lat; + } else { + origx = origin.x; + origy = origin.y; + } + + var left = (this.left - origx) * ratio + origx; + var bottom = (this.bottom - origy) * ratio + origy; + var right = (this.right - origx) * ratio + origx; + var top = (this.top - origy) * ratio + origy; + + return new OpenLayers.Bounds(left, bottom, right, top); + }, + + /** + * APIMethod: add + * Shifts the coordinates of the bound by the given horizontal and vertical + * deltas. + * + * (start code) + * var bounds = new OpenLayers.Bounds(0, 0, 10, 10); + * bounds.toString(); + * // => "0,0,10,10" + * + * bounds.add(-1.5, 4).toString(); + * // => "-1.5,4,8.5,14" + * (end) + * + * This method will throw a TypeError if it is passed null as an argument. + * + * Parameters: + * x - {Float} horizontal delta + * y - {Float} vertical delta + * + * Returns: + * {} A new bounds whose coordinates are the same as + * this, but shifted by the passed-in x and y values. + */ + add:function(x, y) { + if ( (x == null) || (y == null) ) { + throw new TypeError('Bounds.add cannot receive null values'); + } + return new OpenLayers.Bounds(this.left + x, this.bottom + y, + this.right + x, this.top + y); + }, + + /** + * APIMethod: extend + * Extend the bounds to include the , + * or specified. + * + * Please note that this function assumes that left < right and + * bottom < top. + * + * Parameters: + * object - {, or + * } The object to be included in the new bounds + * object. + */ + extend:function(object) { + if (object) { + switch(object.CLASS_NAME) { + case "OpenLayers.LonLat": + this.extendXY(object.lon, object.lat); + break; + case "OpenLayers.Geometry.Point": + this.extendXY(object.x, object.y); + break; + + case "OpenLayers.Bounds": + // clear cached center location + this.centerLonLat = null; + + if ( (this.left == null) || (object.left < this.left)) { + this.left = object.left; + } + if ( (this.bottom == null) || (object.bottom < this.bottom) ) { + this.bottom = object.bottom; + } + if ( (this.right == null) || (object.right > this.right) ) { + this.right = object.right; + } + if ( (this.top == null) || (object.top > this.top) ) { + this.top = object.top; + } + break; + } + } + }, + + /** + * APIMethod: extendXY + * Extend the bounds to include the XY coordinate specified. + * + * Parameters: + * x - {number} The X part of the the coordinate. + * y - {number} The Y part of the the coordinate. + */ + extendXY:function(x, y) { + // clear cached center location + this.centerLonLat = null; + + if ((this.left == null) || (x < this.left)) { + this.left = x; + } + if ((this.bottom == null) || (y < this.bottom)) { + this.bottom = y; + } + if ((this.right == null) || (x > this.right)) { + this.right = x; + } + if ((this.top == null) || (y > this.top)) { + this.top = y; + } + }, + + /** + * APIMethod: containsLonLat + * Returns whether the bounds object contains the given . + * + * Parameters: + * ll - {|Object} OpenLayers.LonLat or an + * object with a 'lon' and 'lat' properties. + * options - {Object} Optional parameters + * + * Acceptable options: + * inclusive - {Boolean} Whether or not to include the border. + * Default is true. + * worldBounds - {} If a worldBounds is provided, the + * ll will be considered as contained if it exceeds the world bounds, + * but can be wrapped around the dateline so it is contained by this + * bounds. + * + * Returns: + * {Boolean} The passed-in lonlat is within this bounds. + */ + containsLonLat: function(ll, options) { + if (typeof options === "boolean") { + options = {inclusive: options}; + } + options = options || {}; + var contains = this.contains(ll.lon, ll.lat, options.inclusive), + worldBounds = options.worldBounds; + if (worldBounds && !contains) { + var worldWidth = worldBounds.getWidth(); + var worldCenterX = (worldBounds.left + worldBounds.right) / 2; + var worldsAway = Math.round((ll.lon - worldCenterX) / worldWidth); + contains = this.containsLonLat({ + lon: ll.lon - worldsAway * worldWidth, + lat: ll.lat + }, {inclusive: options.inclusive}); + } + return contains; + }, + + /** + * APIMethod: containsPixel + * Returns whether the bounds object contains the given . + * + * Parameters: + * px - {} + * inclusive - {Boolean} Whether or not to include the border. Default is + * true. + * + * Returns: + * {Boolean} The passed-in pixel is within this bounds. + */ + containsPixel:function(px, inclusive) { + return this.contains(px.x, px.y, inclusive); + }, + + /** + * APIMethod: contains + * Returns whether the bounds object contains the given x and y. + * + * Parameters: + * x - {Float} + * y - {Float} + * inclusive - {Boolean} Whether or not to include the border. Default is + * true. + * + * Returns: + * {Boolean} Whether or not the passed-in coordinates are within this + * bounds. + */ + contains:function(x, y, inclusive) { + //set default + if (inclusive == null) { + inclusive = true; + } + + if (x == null || y == null) { + return false; + } + + x = OpenLayers.Util.toFloat(x); + y = OpenLayers.Util.toFloat(y); + + var contains = false; + if (inclusive) { + contains = ((x >= this.left) && (x <= this.right) && + (y >= this.bottom) && (y <= this.top)); + } else { + contains = ((x > this.left) && (x < this.right) && + (y > this.bottom) && (y < this.top)); + } + return contains; + }, + + /** + * APIMethod: intersectsBounds + * Determine whether the target bounds intersects this bounds. Bounds are + * considered intersecting if any of their edges intersect or if one + * bounds contains the other. + * + * Parameters: + * bounds - {} The target bounds. + * options - {Object} Optional parameters. + * + * Acceptable options: + * inclusive - {Boolean} Treat coincident borders as intersecting. Default + * is true. If false, bounds that do not overlap but only touch at the + * border will not be considered as intersecting. + * worldBounds - {} If a worldBounds is provided, two + * bounds will be considered as intersecting if they intersect when + * shifted to within the world bounds. This applies only to bounds that + * cross or are completely outside the world bounds. + * + * Returns: + * {Boolean} The passed-in bounds object intersects this bounds. + */ + intersectsBounds:function(bounds, options) { + if (typeof options === "boolean") { + options = {inclusive: options}; + } + options = options || {}; + if (options.worldBounds) { + var self = this.wrapDateLine(options.worldBounds); + bounds = bounds.wrapDateLine(options.worldBounds); + } else { + self = this; + } + if (options.inclusive == null) { + options.inclusive = true; + } + var intersects = false; + var mightTouch = ( + self.left == bounds.right || + self.right == bounds.left || + self.top == bounds.bottom || + self.bottom == bounds.top + ); + + // if the two bounds only touch at an edge, and inclusive is false, + // then the bounds don't *really* intersect. + if (options.inclusive || !mightTouch) { + // otherwise, if one of the boundaries even partially contains another, + // inclusive of the edges, then they do intersect. + var inBottom = ( + ((bounds.bottom >= self.bottom) && (bounds.bottom <= self.top)) || + ((self.bottom >= bounds.bottom) && (self.bottom <= bounds.top)) + ); + var inTop = ( + ((bounds.top >= self.bottom) && (bounds.top <= self.top)) || + ((self.top > bounds.bottom) && (self.top < bounds.top)) + ); + var inLeft = ( + ((bounds.left >= self.left) && (bounds.left <= self.right)) || + ((self.left >= bounds.left) && (self.left <= bounds.right)) + ); + var inRight = ( + ((bounds.right >= self.left) && (bounds.right <= self.right)) || + ((self.right >= bounds.left) && (self.right <= bounds.right)) + ); + intersects = ((inBottom || inTop) && (inLeft || inRight)); + } + // document me + if (options.worldBounds && !intersects) { + var world = options.worldBounds; + var width = world.getWidth(); + var selfCrosses = !world.containsBounds(self); + var boundsCrosses = !world.containsBounds(bounds); + if (selfCrosses && !boundsCrosses) { + bounds = bounds.add(-width, 0); + intersects = self.intersectsBounds(bounds, {inclusive: options.inclusive}); + } else if (boundsCrosses && !selfCrosses) { + self = self.add(-width, 0); + intersects = bounds.intersectsBounds(self, {inclusive: options.inclusive}); + } + } + return intersects; + }, + + /** + * APIMethod: containsBounds + * Returns whether the bounds object contains the given . + * + * bounds - {} The target bounds. + * partial - {Boolean} If any of the target corners is within this bounds + * consider the bounds contained. Default is false. If false, the + * entire target bounds must be contained within this bounds. + * inclusive - {Boolean} Treat shared edges as contained. Default is + * true. + * + * Returns: + * {Boolean} The passed-in bounds object is contained within this bounds. + */ + containsBounds:function(bounds, partial, inclusive) { + if (partial == null) { + partial = false; + } + if (inclusive == null) { + inclusive = true; + } + var bottomLeft = this.contains(bounds.left, bounds.bottom, inclusive); + var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive); + var topLeft = this.contains(bounds.left, bounds.top, inclusive); + var topRight = this.contains(bounds.right, bounds.top, inclusive); + + return (partial) ? (bottomLeft || bottomRight || topLeft || topRight) + : (bottomLeft && bottomRight && topLeft && topRight); + }, + + /** + * APIMethod: determineQuadrant + * Returns the the quadrant ("br", "tr", "tl", "bl") in which the given + * lies. + * + * Parameters: + * lonlat - {} + * + * Returns: + * {String} The quadrant ("br" "tr" "tl" "bl") of the bounds in which the + * coordinate lies. + */ + determineQuadrant: function(lonlat) { + + var quadrant = ""; + var center = this.getCenterLonLat(); + + quadrant += (lonlat.lat < center.lat) ? "b" : "t"; + quadrant += (lonlat.lon < center.lon) ? "l" : "r"; + + return quadrant; + }, + + /** + * APIMethod: transform + * Transform the Bounds object from source to dest. + * + * Parameters: + * source - {} Source projection. + * dest - {} Destination projection. + * + * Returns: + * {} Itself, for use in chaining operations. + */ + transform: function(source, dest) { + // clear cached center location + this.centerLonLat = null; + var ll = OpenLayers.Projection.transform( + {'x': this.left, 'y': this.bottom}, source, dest); + var lr = OpenLayers.Projection.transform( + {'x': this.right, 'y': this.bottom}, source, dest); + var ul = OpenLayers.Projection.transform( + {'x': this.left, 'y': this.top}, source, dest); + var ur = OpenLayers.Projection.transform( + {'x': this.right, 'y': this.top}, source, dest); + this.left = Math.min(ll.x, ul.x); + this.bottom = Math.min(ll.y, lr.y); + this.right = Math.max(lr.x, ur.x); + this.top = Math.max(ul.y, ur.y); + return this; + }, + + /** + * APIMethod: wrapDateLine + * Wraps the bounds object around the dateline. + * + * Parameters: + * maxExtent - {} + * options - {Object} Some possible options are: + * + * Allowed Options: + * leftTolerance - {float} Allow for a margin of error + * with the 'left' value of this + * bound. + * Default is 0. + * rightTolerance - {float} Allow for a margin of error + * with the 'right' value of + * this bound. + * Default is 0. + * + * Returns: + * {} A copy of this bounds, but wrapped around the + * "dateline" (as specified by the borders of + * maxExtent). Note that this function only returns + * a different bounds value if this bounds is + * *entirely* outside of the maxExtent. If this + * bounds straddles the dateline (is part in/part + * out of maxExtent), the returned bounds will always + * cross the left edge of the given maxExtent. + *. + */ + wrapDateLine: function(maxExtent, options) { + options = options || {}; + + var leftTolerance = options.leftTolerance || 0; + var rightTolerance = options.rightTolerance || 0; + + var newBounds = this.clone(); + + if (maxExtent) { + var width = maxExtent.getWidth(); + + //shift right? + while (newBounds.left < maxExtent.left && + newBounds.right - rightTolerance <= maxExtent.left ) { + newBounds = newBounds.add(width, 0); + } + + //shift left? + while (newBounds.left + leftTolerance >= maxExtent.right && + newBounds.right > maxExtent.right ) { + newBounds = newBounds.add(-width, 0); + } + + // crosses right only? force left + var newLeft = newBounds.left + leftTolerance; + if (newLeft < maxExtent.right && newLeft > maxExtent.left && + newBounds.right - rightTolerance > maxExtent.right) { + newBounds = newBounds.add(-width, 0); + } + } + + return newBounds; + }, + + CLASS_NAME: "OpenLayers.Bounds" +}); + +/** + * APIFunction: fromString + * Alternative constructor that builds a new OpenLayers.Bounds from a + * parameter string. + * + * (begin code) + * OpenLayers.Bounds.fromString("5,42,10,45"); + * // => equivalent to ... + * new OpenLayers.Bounds(5, 42, 10, 45); + * (end) + * + * Parameters: + * str - {String} Comma-separated bounds string. (e.g. "5,42,10,45") + * reverseAxisOrder - {Boolean} Does the string use reverse axis order? + * + * Returns: + * {} New bounds object built from the + * passed-in String. + */ +OpenLayers.Bounds.fromString = function(str, reverseAxisOrder) { + var bounds = str.split(","); + return OpenLayers.Bounds.fromArray(bounds, reverseAxisOrder); +}; + +/** + * APIFunction: fromArray + * Alternative constructor that builds a new OpenLayers.Bounds from an array. + * + * (begin code) + * OpenLayers.Bounds.fromArray( [5, 42, 10, 45] ); + * // => equivalent to ... + * new OpenLayers.Bounds(5, 42, 10, 45); + * (end) + * + * Parameters: + * bbox - {Array(Float)} Array of bounds values (e.g. [5,42,10,45]) + * reverseAxisOrder - {Boolean} Does the array use reverse axis order? + * + * Returns: + * {} New bounds object built from the passed-in Array. + */ +OpenLayers.Bounds.fromArray = function(bbox, reverseAxisOrder) { + return reverseAxisOrder === true ? + new OpenLayers.Bounds(bbox[1], bbox[0], bbox[3], bbox[2]) : + new OpenLayers.Bounds(bbox[0], bbox[1], bbox[2], bbox[3]); +}; + +/** + * APIFunction: fromSize + * Alternative constructor that builds a new OpenLayers.Bounds from a size. + * + * (begin code) + * OpenLayers.Bounds.fromSize( new OpenLayers.Size(10, 20) ); + * // => equivalent to ... + * new OpenLayers.Bounds(0, 20, 10, 0); + * (end) + * + * Parameters: + * size - { or Object} or an object with + * both 'w' and 'h' properties. + * + * Returns: + * {} New bounds object built from the passed-in size. + */ +OpenLayers.Bounds.fromSize = function(size) { + return new OpenLayers.Bounds(0, + size.h, + size.w, + 0); +}; + +/** + * Function: oppositeQuadrant + * Get the opposite quadrant for a given quadrant string. + * + * (begin code) + * OpenLayers.Bounds.oppositeQuadrant( "tl" ); + * // => "br" + * + * OpenLayers.Bounds.oppositeQuadrant( "tr" ); + * // => "bl" + * (end) + * + * Parameters: + * quadrant - {String} two character quadrant shortstring + * + * Returns: + * {String} The opposing quadrant ("br" "tr" "tl" "bl"). For Example, if + * you pass in "bl" it returns "tr", if you pass in "br" it + * returns "tl", etc. + */ +OpenLayers.Bounds.oppositeQuadrant = function(quadrant) { + var opp = ""; + + opp += (quadrant.charAt(0) == 't') ? 'b' : 't'; + opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l'; + + return opp; +}; diff --git a/misc/openlayers/lib/OpenLayers/BaseTypes/Class.js b/misc/openlayers/lib/OpenLayers/BaseTypes/Class.js new file mode 100644 index 0000000..2be7212 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/BaseTypes/Class.js @@ -0,0 +1,121 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/SingleFile.js + */ + +/** + * Constructor: OpenLayers.Class + * Base class used to construct all other classes. Includes support for + * multiple inheritance. + * + * This constructor is new in OpenLayers 2.5. At OpenLayers 3.0, the old + * syntax for creating classes and dealing with inheritance + * will be removed. + * + * To create a new OpenLayers-style class, use the following syntax: + * (code) + * var MyClass = OpenLayers.Class(prototype); + * (end) + * + * To create a new OpenLayers-style class with multiple inheritance, use the + * following syntax: + * (code) + * var MyClass = OpenLayers.Class(Class1, Class2, prototype); + * (end) + * + * Note that instanceof reflection will only reveal Class1 as superclass. + * + */ +OpenLayers.Class = function() { + var len = arguments.length; + var P = arguments[0]; + var F = arguments[len-1]; + + var C = typeof F.initialize == "function" ? + F.initialize : + function(){ P.prototype.initialize.apply(this, arguments); }; + + if (len > 1) { + var newArgs = [C, P].concat( + Array.prototype.slice.call(arguments).slice(1, len-1), F); + OpenLayers.inherit.apply(null, newArgs); + } else { + C.prototype = F; + } + return C; +}; + +/** + * Function: OpenLayers.inherit + * + * Parameters: + * C - {Object} the class that inherits + * P - {Object} the superclass to inherit from + * + * In addition to the mandatory C and P parameters, an arbitrary number of + * objects can be passed, which will extend C. + */ +OpenLayers.inherit = function(C, P) { + var F = function() {}; + F.prototype = P.prototype; + C.prototype = new F; + var i, l, o; + for(i=2, l=arguments.length; i"lon=5,lat=42") + */ + toString:function() { + return ("lon=" + this.lon + ",lat=" + this.lat); + }, + + /** + * APIMethod: toShortString + * + * Returns: + * {String} Shortened String representation of OpenLayers.LonLat object. + * (e.g. "5, 42") + */ + toShortString:function() { + return (this.lon + ", " + this.lat); + }, + + /** + * APIMethod: clone + * + * Returns: + * {} New OpenLayers.LonLat object with the same lon + * and lat values + */ + clone:function() { + return new OpenLayers.LonLat(this.lon, this.lat); + }, + + /** + * APIMethod: add + * + * Parameters: + * lon - {Float} + * lat - {Float} + * + * Returns: + * {} A new OpenLayers.LonLat object with the lon and + * lat passed-in added to this's. + */ + add:function(lon, lat) { + if ( (lon == null) || (lat == null) ) { + throw new TypeError('LonLat.add cannot receive null values'); + } + return new OpenLayers.LonLat(this.lon + OpenLayers.Util.toFloat(lon), + this.lat + OpenLayers.Util.toFloat(lat)); + }, + + /** + * APIMethod: equals + * + * Parameters: + * ll - {} + * + * Returns: + * {Boolean} Boolean value indicating whether the passed-in + * object has the same lon and lat + * components as this. + * Note: if ll passed in is null, returns false + */ + equals:function(ll) { + var equals = false; + if (ll != null) { + equals = ((this.lon == ll.lon && this.lat == ll.lat) || + (isNaN(this.lon) && isNaN(this.lat) && isNaN(ll.lon) && isNaN(ll.lat))); + } + return equals; + }, + + /** + * APIMethod: transform + * Transform the LonLat object from source to dest. This transformation is + * *in place*: if you want a *new* lonlat, use .clone() first. + * + * Parameters: + * source - {} Source projection. + * dest - {} Destination projection. + * + * Returns: + * {} Itself, for use in chaining operations. + */ + transform: function(source, dest) { + var point = OpenLayers.Projection.transform( + {'x': this.lon, 'y': this.lat}, source, dest); + this.lon = point.x; + this.lat = point.y; + return this; + }, + + /** + * APIMethod: wrapDateLine + * + * Parameters: + * maxExtent - {} + * + * Returns: + * {} A copy of this lonlat, but wrapped around the + * "dateline" (as specified by the borders of + * maxExtent) + */ + wrapDateLine: function(maxExtent) { + + var newLonLat = this.clone(); + + if (maxExtent) { + //shift right? + while (newLonLat.lon < maxExtent.left) { + newLonLat.lon += maxExtent.getWidth(); + } + + //shift left? + while (newLonLat.lon > maxExtent.right) { + newLonLat.lon -= maxExtent.getWidth(); + } + } + + return newLonLat; + }, + + CLASS_NAME: "OpenLayers.LonLat" +}); + +/** + * Function: fromString + * Alternative constructor that builds a new from a + * parameter string + * + * Parameters: + * str - {String} Comma-separated Lon,Lat coordinate string. + * (e.g. "5,40") + * + * Returns: + * {} New object built from the + * passed-in String. + */ +OpenLayers.LonLat.fromString = function(str) { + var pair = str.split(","); + return new OpenLayers.LonLat(pair[0], pair[1]); +}; + +/** + * Function: fromArray + * Alternative constructor that builds a new from an + * array of two numbers that represent lon- and lat-values. + * + * Parameters: + * arr - {Array(Float)} Array of lon/lat values (e.g. [5,-42]) + * + * Returns: + * {} New object built from the + * passed-in array. + */ +OpenLayers.LonLat.fromArray = function(arr) { + var gotArr = OpenLayers.Util.isArray(arr), + lon = gotArr && arr[0], + lat = gotArr && arr[1]; + return new OpenLayers.LonLat(lon, lat); +}; diff --git a/misc/openlayers/lib/OpenLayers/BaseTypes/Pixel.js b/misc/openlayers/lib/OpenLayers/BaseTypes/Pixel.js new file mode 100644 index 0000000..d6ac60a --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/BaseTypes/Pixel.js @@ -0,0 +1,143 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/BaseTypes/Class.js + */ + +/** + * Class: OpenLayers.Pixel + * This class represents a screen coordinate, in x and y coordinates + */ +OpenLayers.Pixel = OpenLayers.Class({ + + /** + * APIProperty: x + * {Number} The x coordinate + */ + x: 0.0, + + /** + * APIProperty: y + * {Number} The y coordinate + */ + y: 0.0, + + /** + * Constructor: OpenLayers.Pixel + * Create a new OpenLayers.Pixel instance + * + * Parameters: + * x - {Number} The x coordinate + * y - {Number} The y coordinate + * + * Returns: + * An instance of OpenLayers.Pixel + */ + initialize: function(x, y) { + this.x = parseFloat(x); + this.y = parseFloat(y); + }, + + /** + * Method: toString + * Cast this object into a string + * + * Returns: + * {String} The string representation of Pixel. ex: "x=200.4,y=242.2" + */ + toString:function() { + return ("x=" + this.x + ",y=" + this.y); + }, + + /** + * APIMethod: clone + * Return a clone of this pixel object + * + * Returns: + * {} A clone pixel + */ + clone:function() { + return new OpenLayers.Pixel(this.x, this.y); + }, + + /** + * APIMethod: equals + * Determine whether one pixel is equivalent to another + * + * Parameters: + * px - {|Object} An OpenLayers.Pixel or an object with + * a 'x' and 'y' properties. + * + * Returns: + * {Boolean} The point passed in as parameter is equal to this. Note that + * if px passed in is null, returns false. + */ + equals:function(px) { + var equals = false; + if (px != null) { + equals = ((this.x == px.x && this.y == px.y) || + (isNaN(this.x) && isNaN(this.y) && isNaN(px.x) && isNaN(px.y))); + } + return equals; + }, + + /** + * APIMethod: distanceTo + * Returns the distance to the pixel point passed in as a parameter. + * + * Parameters: + * px - {} + * + * Returns: + * {Float} The pixel point passed in as parameter to calculate the + * distance to. + */ + distanceTo:function(px) { + return Math.sqrt( + Math.pow(this.x - px.x, 2) + + Math.pow(this.y - px.y, 2) + ); + }, + + /** + * APIMethod: add + * + * Parameters: + * x - {Integer} + * y - {Integer} + * + * Returns: + * {} A new Pixel with this pixel's x&y augmented by the + * values passed in. + */ + add:function(x, y) { + if ( (x == null) || (y == null) ) { + throw new TypeError('Pixel.add cannot receive null values'); + } + return new OpenLayers.Pixel(this.x + x, this.y + y); + }, + + /** + * APIMethod: offset + * + * Parameters + * px - {|Object} An OpenLayers.Pixel or an object with + * a 'x' and 'y' properties. + * + * Returns: + * {} A new Pixel with this pixel's x&y augmented by the + * x&y values of the pixel passed in. + */ + offset:function(px) { + var newPx = this.clone(); + if (px) { + newPx = this.add(px.x, px.y); + } + return newPx; + }, + + CLASS_NAME: "OpenLayers.Pixel" +}); diff --git a/misc/openlayers/lib/OpenLayers/BaseTypes/Size.js b/misc/openlayers/lib/OpenLayers/BaseTypes/Size.js new file mode 100644 index 0000000..34c7a6c --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/BaseTypes/Size.js @@ -0,0 +1,89 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/BaseTypes/Class.js + */ + +/** + * Class: OpenLayers.Size + * Instances of this class represent a width/height pair + */ +OpenLayers.Size = OpenLayers.Class({ + + /** + * APIProperty: w + * {Number} width + */ + w: 0.0, + + /** + * APIProperty: h + * {Number} height + */ + h: 0.0, + + + /** + * Constructor: OpenLayers.Size + * Create an instance of OpenLayers.Size + * + * Parameters: + * w - {Number} width + * h - {Number} height + */ + initialize: function(w, h) { + this.w = parseFloat(w); + this.h = parseFloat(h); + }, + + /** + * Method: toString + * Return the string representation of a size object + * + * Returns: + * {String} The string representation of OpenLayers.Size object. + * (e.g. "w=55,h=66") + */ + toString:function() { + return ("w=" + this.w + ",h=" + this.h); + }, + + /** + * APIMethod: clone + * Create a clone of this size object + * + * Returns: + * {} A new OpenLayers.Size object with the same w and h + * values + */ + clone:function() { + return new OpenLayers.Size(this.w, this.h); + }, + + /** + * + * APIMethod: equals + * Determine where this size is equal to another + * + * Parameters: + * sz - {|Object} An OpenLayers.Size or an object with + * a 'w' and 'h' properties. + * + * Returns: + * {Boolean} The passed in size has the same h and w properties as this one. + * Note that if sz passed in is null, returns false. + */ + equals:function(sz) { + var equals = false; + if (sz != null) { + equals = ((this.w == sz.w && this.h == sz.h) || + (isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h))); + } + return equals; + }, + + CLASS_NAME: "OpenLayers.Size" +}); diff --git a/misc/openlayers/lib/OpenLayers/Console.js b/misc/openlayers/lib/OpenLayers/Console.js new file mode 100644 index 0000000..ef5029a --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Console.js @@ -0,0 +1,250 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/BaseTypes/Class.js + */ + +/** + * Namespace: OpenLayers.Console + * The OpenLayers.Console namespace is used for debugging and error logging. + * If the Firebug Lite (../Firebug/firebug.js) is included before this script, + * calls to OpenLayers.Console methods will get redirected to window.console. + * This makes use of the Firebug extension where available and allows for + * cross-browser debugging Firebug style. + * + * Note: + * Note that behavior will differ with the Firebug extention and Firebug Lite. + * Most notably, the Firebug Lite console does not currently allow for + * hyperlinks to code or for clicking on object to explore their properties. + * + */ +OpenLayers.Console = { + /** + * Create empty functions for all console methods. The real value of these + * properties will be set if Firebug Lite (../Firebug/firebug.js script) is + * included. We explicitly require the Firebug Lite script to trigger + * functionality of the OpenLayers.Console methods. + */ + + /** + * APIFunction: log + * Log an object in the console. The Firebug Lite console logs string + * representation of objects. Given multiple arguments, they will + * be cast to strings and logged with a space delimiter. If the first + * argument is a string with printf-like formatting, subsequent arguments + * will be used in string substitution. Any additional arguments (beyond + * the number substituted in a format string) will be appended in a space- + * delimited line. + * + * Parameters: + * object - {Object} + */ + log: function() {}, + + /** + * APIFunction: debug + * Writes a message to the console, including a hyperlink to the line + * where it was called. + * + * May be called with multiple arguments as with OpenLayers.Console.log(). + * + * Parameters: + * object - {Object} + */ + debug: function() {}, + + /** + * APIFunction: info + * Writes a message to the console with the visual "info" icon and color + * coding and a hyperlink to the line where it was called. + * + * May be called with multiple arguments as with OpenLayers.Console.log(). + * + * Parameters: + * object - {Object} + */ + info: function() {}, + + /** + * APIFunction: warn + * Writes a message to the console with the visual "warning" icon and + * color coding and a hyperlink to the line where it was called. + * + * May be called with multiple arguments as with OpenLayers.Console.log(). + * + * Parameters: + * object - {Object} + */ + warn: function() {}, + + /** + * APIFunction: error + * Writes a message to the console with the visual "error" icon and color + * coding and a hyperlink to the line where it was called. + * + * May be called with multiple arguments as with OpenLayers.Console.log(). + * + * Parameters: + * object - {Object} + */ + error: function() {}, + + /** + * APIFunction: userError + * A single interface for showing error messages to the user. The default + * behavior is a Javascript alert, though this can be overridden by + * reassigning OpenLayers.Console.userError to a different function. + * + * Expects a single error message + * + * Parameters: + * error - {Object} + */ + userError: function(error) { + alert(error); + }, + + /** + * APIFunction: assert + * Tests that an expression is true. If not, it will write a message to + * the console and throw an exception. + * + * May be called with multiple arguments as with OpenLayers.Console.log(). + * + * Parameters: + * object - {Object} + */ + assert: function() {}, + + /** + * APIFunction: dir + * Prints an interactive listing of all properties of the object. This + * looks identical to the view that you would see in the DOM tab. + * + * Parameters: + * object - {Object} + */ + dir: function() {}, + + /** + * APIFunction: dirxml + * Prints the XML source tree of an HTML or XML element. This looks + * identical to the view that you would see in the HTML tab. You can click + * on any node to inspect it in the HTML tab. + * + * Parameters: + * object - {Object} + */ + dirxml: function() {}, + + /** + * APIFunction: trace + * Prints an interactive stack trace of JavaScript execution at the point + * where it is called. The stack trace details the functions on the stack, + * as well as the values that were passed as arguments to each function. + * You can click each function to take you to its source in the Script tab, + * and click each argument value to inspect it in the DOM or HTML tabs. + * + */ + trace: function() {}, + + /** + * APIFunction: group + * Writes a message to the console and opens a nested block to indent all + * future messages sent to the console. Call OpenLayers.Console.groupEnd() + * to close the block. + * + * May be called with multiple arguments as with OpenLayers.Console.log(). + * + * Parameters: + * object - {Object} + */ + group: function() {}, + + /** + * APIFunction: groupEnd + * Closes the most recently opened block created by a call to + * OpenLayers.Console.group + */ + groupEnd: function() {}, + + /** + * APIFunction: time + * Creates a new timer under the given name. Call + * OpenLayers.Console.timeEnd(name) + * with the same name to stop the timer and print the time elapsed. + * + * Parameters: + * name - {String} + */ + time: function() {}, + + /** + * APIFunction: timeEnd + * Stops a timer created by a call to OpenLayers.Console.time(name) and + * writes the time elapsed. + * + * Parameters: + * name - {String} + */ + timeEnd: function() {}, + + /** + * APIFunction: profile + * Turns on the JavaScript profiler. The optional argument title would + * contain the text to be printed in the header of the profile report. + * + * This function is not currently implemented in Firebug Lite. + * + * Parameters: + * title - {String} Optional title for the profiler + */ + profile: function() {}, + + /** + * APIFunction: profileEnd + * Turns off the JavaScript profiler and prints its report. + * + * This function is not currently implemented in Firebug Lite. + */ + profileEnd: function() {}, + + /** + * APIFunction: count + * Writes the number of times that the line of code where count was called + * was executed. The optional argument title will print a message in + * addition to the number of the count. + * + * This function is not currently implemented in Firebug Lite. + * + * Parameters: + * title - {String} Optional title to be printed with count + */ + count: function() {}, + + CLASS_NAME: "OpenLayers.Console" +}; + +/** + * Execute an anonymous function to extend the OpenLayers.Console namespace + * if the firebug.js script is included. This closure is used so that the + * "scripts" and "i" variables don't pollute the global namespace. + */ +(function() { + /** + * If Firebug Lite is included (before this script), re-route all + * OpenLayers.Console calls to the console object. + */ + var scripts = document.getElementsByTagName("script"); + for(var i=0, len=scripts.length; i var map = new OpenLayers.Map('map', { controls: [] }); + * > + * > map.addControl(new OpenLayers.Control.PanZoomBar()); + * > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false})); + * > map.addControl(new OpenLayers.Control.Permalink()); + * > map.addControl(new OpenLayers.Control.Permalink('permalink')); + * > map.addControl(new OpenLayers.Control.MousePosition()); + * > map.addControl(new OpenLayers.Control.OverviewMap()); + * > map.addControl(new OpenLayers.Control.KeyboardDefaults()); + * + * The next code fragment is a quick example of how to intercept + * shift-mouse click to display the extent of the bounding box + * dragged out by the user. Usually controls are not created + * in exactly this manner. See the source for a more complete + * example: + * + * > var control = new OpenLayers.Control(); + * > OpenLayers.Util.extend(control, { + * > draw: function () { + * > // this Handler.Box will intercept the shift-mousedown + * > // before Control.MouseDefault gets to see it + * > this.box = new OpenLayers.Handler.Box( control, + * > {"done": this.notice}, + * > {keyMask: OpenLayers.Handler.MOD_SHIFT}); + * > this.box.activate(); + * > }, + * > + * > notice: function (bounds) { + * > OpenLayers.Console.userError(bounds); + * > } + * > }); + * > map.addControl(control); + * + */ +OpenLayers.Control = OpenLayers.Class({ + + /** + * Property: id + * {String} + */ + id: null, + + /** + * Property: map + * {} this gets set in the addControl() function in + * OpenLayers.Map + */ + map: null, + + /** + * APIProperty: div + * {DOMElement} The element that contains the control, if not present the + * control is placed inside the map. + */ + div: null, + + /** + * APIProperty: type + * {Number} Controls can have a 'type'. The type determines the type of + * interactions which are possible with them when they are placed in an + * . + */ + type: null, + + /** + * Property: allowSelection + * {Boolean} By default, controls do not allow selection, because + * it may interfere with map dragging. If this is true, OpenLayers + * will not prevent selection of the control. + * Default is false. + */ + allowSelection: false, + + /** + * Property: displayClass + * {string} This property is used for CSS related to the drawing of the + * Control. + */ + displayClass: "", + + /** + * APIProperty: title + * {string} This property is used for showing a tooltip over the + * Control. + */ + title: "", + + /** + * APIProperty: autoActivate + * {Boolean} Activate the control when it is added to a map. Default is + * false. + */ + autoActivate: false, + + /** + * APIProperty: active + * {Boolean} The control is active (read-only). Use and + * to change control state. + */ + active: null, + + /** + * Property: handlerOptions + * {Object} Used to set non-default properties on the control's handler + */ + handlerOptions: null, + + /** + * Property: handler + * {} null + */ + handler: null, + + /** + * APIProperty: eventListeners + * {Object} If set as an option at construction, the eventListeners + * object will be registered with . Object + * structure must be a listeners object as shown in the example for + * the events.on method. + */ + eventListeners: null, + + /** + * APIProperty: events + * {} Events instance for listeners and triggering + * control specific events. + * + * Register a listener for a particular event with the following syntax: + * (code) + * control.events.register(type, obj, listener); + * (end) + * + * Listeners will be called with a reference to an event object. The + * properties of this event depends on exactly what happened. + * + * All event objects have at least the following properties: + * object - {Object} A reference to control.events.object (a reference + * to the control). + * element - {DOMElement} A reference to control.events.element (which + * will be null unless documented otherwise). + * + * Supported map event types: + * activate - Triggered when activated. + * deactivate - Triggered when deactivated. + */ + events: null, + + /** + * Constructor: OpenLayers.Control + * Create an OpenLayers Control. The options passed as a parameter + * directly extend the control. For example passing the following: + * + * > var control = new OpenLayers.Control({div: myDiv}); + * + * Overrides the default div attribute value of null. + * + * Parameters: + * options - {Object} + */ + initialize: function (options) { + // We do this before the extend so that instances can override + // className in options. + this.displayClass = + this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, ""); + + OpenLayers.Util.extend(this, options); + + this.events = new OpenLayers.Events(this); + if(this.eventListeners instanceof Object) { + this.events.on(this.eventListeners); + } + if (this.id == null) { + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); + } + }, + + /** + * Method: destroy + * The destroy method is used to perform any clean up before the control + * is dereferenced. Typically this is where event listeners are removed + * to prevent memory leaks. + */ + destroy: function () { + if(this.events) { + if(this.eventListeners) { + this.events.un(this.eventListeners); + } + this.events.destroy(); + this.events = null; + } + this.eventListeners = null; + + // eliminate circular references + if (this.handler) { + this.handler.destroy(); + this.handler = null; + } + if(this.handlers) { + for(var key in this.handlers) { + if(this.handlers.hasOwnProperty(key) && + typeof this.handlers[key].destroy == "function") { + this.handlers[key].destroy(); + } + } + this.handlers = null; + } + if (this.map) { + this.map.removeControl(this); + this.map = null; + } + this.div = null; + }, + + /** + * Method: setMap + * Set the map property for the control. This is done through an accessor + * so that subclasses can override this and take special action once + * they have their map variable set. + * + * Parameters: + * map - {} + */ + setMap: function(map) { + this.map = map; + if (this.handler) { + this.handler.setMap(map); + } + }, + + /** + * Method: draw + * The draw method is called when the control is ready to be displayed + * on the page. If a div has not been created one is created. Controls + * with a visual component will almost always want to override this method + * to customize the look of control. + * + * Parameters: + * px - {} The top-left pixel position of the control + * or null. + * + * Returns: + * {DOMElement} A reference to the DIV DOMElement containing the control + */ + draw: function (px) { + if (this.div == null) { + this.div = OpenLayers.Util.createDiv(this.id); + this.div.className = this.displayClass; + if (!this.allowSelection) { + this.div.className += " olControlNoSelect"; + this.div.setAttribute("unselectable", "on", 0); + this.div.onselectstart = OpenLayers.Function.False; + } + if (this.title != "") { + this.div.title = this.title; + } + } + if (px != null) { + this.position = px.clone(); + } + this.moveTo(this.position); + return this.div; + }, + + /** + * Method: moveTo + * Sets the left and top style attributes to the passed in pixel + * coordinates. + * + * Parameters: + * px - {} + */ + moveTo: function (px) { + if ((px != null) && (this.div != null)) { + this.div.style.left = px.x + "px"; + this.div.style.top = px.y + "px"; + } + }, + + /** + * APIMethod: activate + * Explicitly activates a control and it's associated + * handler if one has been set. Controls can be + * deactivated by calling the deactivate() method. + * + * Returns: + * {Boolean} True if the control was successfully activated or + * false if the control was already active. + */ + activate: function () { + if (this.active) { + return false; + } + if (this.handler) { + this.handler.activate(); + } + this.active = true; + if(this.map) { + OpenLayers.Element.addClass( + this.map.viewPortDiv, + this.displayClass.replace(/ /g, "") + "Active" + ); + } + this.events.triggerEvent("activate"); + return true; + }, + + /** + * APIMethod: deactivate + * Deactivates a control and it's associated handler if any. The exact + * effect of this depends on the control itself. + * + * Returns: + * {Boolean} True if the control was effectively deactivated or false + * if the control was already inactive. + */ + deactivate: function () { + if (this.active) { + if (this.handler) { + this.handler.deactivate(); + } + this.active = false; + if(this.map) { + OpenLayers.Element.removeClass( + this.map.viewPortDiv, + this.displayClass.replace(/ /g, "") + "Active" + ); + } + this.events.triggerEvent("deactivate"); + return true; + } + return false; + }, + + CLASS_NAME: "OpenLayers.Control" +}); + +/** + * Constant: OpenLayers.Control.TYPE_BUTTON + */ +OpenLayers.Control.TYPE_BUTTON = 1; + +/** + * Constant: OpenLayers.Control.TYPE_TOGGLE + */ +OpenLayers.Control.TYPE_TOGGLE = 2; + +/** + * Constant: OpenLayers.Control.TYPE_TOOL + */ +OpenLayers.Control.TYPE_TOOL = 3; diff --git a/misc/openlayers/lib/OpenLayers/Control/ArgParser.js b/misc/openlayers/lib/OpenLayers/Control/ArgParser.js new file mode 100644 index 0000000..6b076f5 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/ArgParser.js @@ -0,0 +1,182 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/Control.js + */ + +/** + * Class: OpenLayers.Control.ArgParser + * The ArgParser control adds location bar query string parsing functionality + * to an OpenLayers Map. + * When added to a Map control, on a page load/refresh, the Map will + * automatically take the href string and parse it for lon, lat, zoom, and + * layers information. + * + * Inherits from: + * - + */ +OpenLayers.Control.ArgParser = OpenLayers.Class(OpenLayers.Control, { + + /** + * Property: center + * {} + */ + center: null, + + /** + * Property: zoom + * {int} + */ + zoom: null, + + /** + * Property: layers + * {String} Each character represents the state of the corresponding layer + * on the map. + */ + layers: null, + + /** + * APIProperty: displayProjection + * {} Requires proj4js support. + * Projection used when reading the coordinates from the URL. This will + * reproject the map coordinates from the URL into the map's + * projection. + * + * If you are using this functionality, be aware that any permalink + * which is added to the map will determine the coordinate type which + * is read from the URL, which means you should not add permalinks with + * different displayProjections to the same map. + */ + displayProjection: null, + + /** + * Constructor: OpenLayers.Control.ArgParser + * + * Parameters: + * options - {Object} + */ + + /** + * Method: getParameters + */ + getParameters: function(url) { + url = url || window.location.href; + var parameters = OpenLayers.Util.getParameters(url); + + // If we have an anchor in the url use it to split the url + var index = url.indexOf('#'); + if (index > 0) { + // create an url to parse on the getParameters + url = '?' + url.substring(index + 1, url.length); + + OpenLayers.Util.extend(parameters, + OpenLayers.Util.getParameters(url)); + } + return parameters; + }, + + /** + * Method: setMap + * Set the map property for the control. + * + * Parameters: + * map - {} + */ + setMap: function(map) { + OpenLayers.Control.prototype.setMap.apply(this, arguments); + + //make sure we dont already have an arg parser attached + for(var i=0, len=this.map.controls.length; i + */ +OpenLayers.Control.Attribution = + OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: separator + * {String} String used to separate layers. + */ + separator: ", ", + + /** + * APIProperty: template + * {String} Template for the attribution. This has to include the substring + * "${layers}", which will be replaced by the layer specific + * attributions, separated by . The default is "${layers}". + */ + template: "${layers}", + + /** + * Constructor: OpenLayers.Control.Attribution + * + * Parameters: + * options - {Object} Options for control. + */ + + /** + * Method: destroy + * Destroy control. + */ + destroy: function() { + this.map.events.un({ + "removelayer": this.updateAttribution, + "addlayer": this.updateAttribution, + "changelayer": this.updateAttribution, + "changebaselayer": this.updateAttribution, + scope: this + }); + + OpenLayers.Control.prototype.destroy.apply(this, arguments); + }, + + /** + * Method: draw + * Initialize control. + * + * Returns: + * {DOMElement} A reference to the DIV DOMElement containing the control + */ + draw: function() { + OpenLayers.Control.prototype.draw.apply(this, arguments); + + this.map.events.on({ + 'changebaselayer': this.updateAttribution, + 'changelayer': this.updateAttribution, + 'addlayer': this.updateAttribution, + 'removelayer': this.updateAttribution, + scope: this + }); + this.updateAttribution(); + + return this.div; + }, + + /** + * Method: updateAttribution + * Update attribution string. + */ + updateAttribution: function() { + var attributions = []; + if (this.map && this.map.layers) { + for(var i=0, len=this.map.layers.length; i. + * When clicked, the function trigger() is executed. + * + * Inherits from: + * - + * + * Use: + * (code) + * var button = new OpenLayers.Control.Button({ + * displayClass: "MyButton", trigger: myFunction + * }); + * panel.addControls([button]); + * (end) + * + * Will create a button with CSS class MyButtonItemInactive, that + * will call the function MyFunction() when clicked. + */ +OpenLayers.Control.Button = OpenLayers.Class(OpenLayers.Control, { + /** + * Property: type + * {Integer} OpenLayers.Control.TYPE_BUTTON. + */ + type: OpenLayers.Control.TYPE_BUTTON, + + /** + * Method: trigger + * Called by a control panel when the button is clicked. + */ + trigger: function() {}, + + CLASS_NAME: "OpenLayers.Control.Button" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/CacheRead.js b/misc/openlayers/lib/OpenLayers/Control/CacheRead.js new file mode 100644 index 0000000..7768bce --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/CacheRead.js @@ -0,0 +1,156 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + */ + +/** + * Class: OpenLayers.Control.CacheRead + * A control for using image tiles cached with + * from the browser's local storage. + * + * Inherits from: + * - + */ +OpenLayers.Control.CacheRead = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: fetchEvent + * {String} The layer event to listen to for replacing remote resource tile + * URLs with cached data URIs. Supported values are "tileerror" (try + * remote first, fall back to cached) and "tileloadstart" (try cache + * first, fall back to remote). Default is "tileloadstart". + * + * Note that "tileerror" will not work for CORS enabled images (see + * https://developer.mozilla.org/en/CORS_Enabled_Image), i.e. layers + * configured with a in + * . + */ + fetchEvent: "tileloadstart", + + /** + * APIProperty: layers + * {Array()}. Optional. If provided, only these + * layers will receive tiles from the cache. + */ + layers: null, + + /** + * APIProperty: autoActivate + * {Boolean} Activate the control when it is added to a map. Default is + * true. + */ + autoActivate: true, + + /** + * Constructor: OpenLayers.Control.CacheRead + * + * Parameters: + * options - {Object} Object with API properties for this control + */ + + /** + * Method: setMap + * Set the map property for the control. + * + * Parameters: + * map - {} + */ + setMap: function(map) { + OpenLayers.Control.prototype.setMap.apply(this, arguments); + var i, layers = this.layers || map.layers; + for (i=layers.length-1; i>=0; --i) { + this.addLayer({layer: layers[i]}); + } + if (!this.layers) { + map.events.on({ + addlayer: this.addLayer, + removeLayer: this.removeLayer, + scope: this + }); + } + }, + + /** + * Method: addLayer + * Adds a layer to the control. Once added, tiles requested for this layer + * will be cached. + * + * Parameters: + * evt - {Object} Object with a layer property referencing an + * instance + */ + addLayer: function(evt) { + evt.layer.events.register(this.fetchEvent, this, this.fetch); + }, + + /** + * Method: removeLayer + * Removes a layer from the control. Once removed, tiles requested for this + * layer will no longer be cached. + * + * Parameters: + * evt - {Object} Object with a layer property referencing an + * instance + */ + removeLayer: function(evt) { + evt.layer.events.unregister(this.fetchEvent, this, this.fetch); + }, + + /** + * Method: fetch + * Listener to the event. Replaces a tile's url with a data + * URI from the cache. + * + * Parameters: + * evt - {Object} Event object with a tile property. + */ + fetch: function(evt) { + if (this.active && window.localStorage && + evt.tile instanceof OpenLayers.Tile.Image) { + var tile = evt.tile, + url = tile.url; + // deal with modified tile urls when both CacheWrite and CacheRead + // are active + if (!tile.layer.crossOriginKeyword && OpenLayers.ProxyHost && + url.indexOf(OpenLayers.ProxyHost) === 0) { + url = OpenLayers.Control.CacheWrite.urlMap[url]; + } + var dataURI = window.localStorage.getItem("olCache_" + url); + if (dataURI) { + tile.url = dataURI; + if (evt.type === "tileerror") { + tile.setImgSrc(dataURI); + } + } + } + }, + + /** + * Method: destroy + * The destroy method is used to perform any clean up before the control + * is dereferenced. Typically this is where event listeners are removed + * to prevent memory leaks. + */ + destroy: function() { + if (this.layers || this.map) { + var i, layers = this.layers || this.map.layers; + for (i=layers.length-1; i>=0; --i) { + this.removeLayer({layer: layers[i]}); + } + } + if (this.map) { + this.map.events.un({ + addlayer: this.addLayer, + removeLayer: this.removeLayer, + scope: this + }); + } + OpenLayers.Control.prototype.destroy.apply(this, arguments); + }, + + CLASS_NAME: "OpenLayers.Control.CacheRead" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/CacheWrite.js b/misc/openlayers/lib/OpenLayers/Control/CacheWrite.js new file mode 100644 index 0000000..3d4ecf5 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/CacheWrite.js @@ -0,0 +1,257 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Request.js + * @requires OpenLayers/Console.js + */ + +/** + * Class: OpenLayers.Control.CacheWrite + * A control for caching image tiles in the browser's local storage. The + * control is used to fetch and use the cached + * tile images. + * + * Note: Before using this control on any layer that is not your own, make sure + * that the terms of service of the tile provider allow local storage of tiles. + * + * Inherits from: + * - + */ +OpenLayers.Control.CacheWrite = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: events + * {} Events instance for listeners and triggering + * control specific events. + * + * To register events in the constructor, configure . + * + * Register a listener for a particular event with the following syntax: + * (code) + * control.events.register(type, obj, listener); + * (end) + * + * Supported event types (in addition to those from ): + * cachefull - Triggered when the cache is full. Listeners receive an + * object with a tile property as first argument. The tile references + * the tile that couldn't be cached. + */ + + /** + * APIProperty: eventListeners + * {Object} Object with event listeners, keyed by event name. An optional + * scope property defines the scope that listeners will be executed in. + */ + + /** + * APIProperty: layers + * {Array()}. Optional. If provided, caching + * will be enabled for these layers only, otherwise for all cacheable + * layers. + */ + layers: null, + + /** + * APIProperty: imageFormat + * {String} The image format used for caching. The default is "image/png". + * Supported formats depend on the user agent. If an unsupported + * is provided, "image/png" will be used. For aerial + * imagery, "image/jpeg" is recommended. + */ + imageFormat: "image/png", + + /** + * Property: quotaRegEx + * {RegExp} + */ + quotaRegEx: (/quota/i), + + /** + * Constructor: OpenLayers.Control.CacheWrite + * + * Parameters: + * options - {Object} Object with API properties for this control. + */ + + /** + * Method: setMap + * Set the map property for the control. + * + * Parameters: + * map - {} + */ + setMap: function(map) { + OpenLayers.Control.prototype.setMap.apply(this, arguments); + var i, layers = this.layers || map.layers; + for (i=layers.length-1; i>=0; --i) { + this.addLayer({layer: layers[i]}); + } + if (!this.layers) { + map.events.on({ + addlayer: this.addLayer, + removeLayer: this.removeLayer, + scope: this + }); + } + }, + + /** + * Method: addLayer + * Adds a layer to the control. Once added, tiles requested for this layer + * will be cached. + * + * Parameters: + * evt - {Object} Object with a layer property referencing an + * instance + */ + addLayer: function(evt) { + evt.layer.events.on({ + tileloadstart: this.makeSameOrigin, + tileloaded: this.onTileLoaded, + scope: this + }); + }, + + /** + * Method: removeLayer + * Removes a layer from the control. Once removed, tiles requested for this + * layer will no longer be cached. + * + * Parameters: + * evt - {Object} Object with a layer property referencing an + * instance + */ + removeLayer: function(evt) { + evt.layer.events.un({ + tileloadstart: this.makeSameOrigin, + tileloaded: this.onTileLoaded, + scope: this + }); + }, + + /** + * Method: makeSameOrigin + * If the tile does not have CORS image loading enabled and is from a + * different origin, use OpenLayers.ProxyHost to make it a same origin url. + * + * Parameters: + * evt - {} + */ + makeSameOrigin: function(evt) { + if (this.active) { + var tile = evt.tile; + if (tile instanceof OpenLayers.Tile.Image && + !tile.crossOriginKeyword && + tile.url.substr(0, 5) !== "data:") { + var sameOriginUrl = OpenLayers.Request.makeSameOrigin( + tile.url, OpenLayers.ProxyHost + ); + OpenLayers.Control.CacheWrite.urlMap[sameOriginUrl] = tile.url; + tile.url = sameOriginUrl; + } + } + }, + + /** + * Method: onTileLoaded + * Decides whether a tile can be cached and calls the cache method. + * + * Parameters: + * evt - {Event} + */ + onTileLoaded: function(evt) { + if (this.active && !evt.aborted && + evt.tile instanceof OpenLayers.Tile.Image && + evt.tile.url.substr(0, 5) !== 'data:') { + this.cache({tile: evt.tile}); + delete OpenLayers.Control.CacheWrite.urlMap[evt.tile.url]; + } + }, + + /** + * Method: cache + * Adds a tile to the cache. When the cache is full, the "cachefull" event + * is triggered. + * + * Parameters: + * obj - {Object} Object with a tile property, tile being the + * with the data to add to the cache + */ + cache: function(obj) { + if (window.localStorage) { + var tile = obj.tile; + try { + var canvasContext = tile.getCanvasContext(); + if (canvasContext) { + var urlMap = OpenLayers.Control.CacheWrite.urlMap; + var url = urlMap[tile.url] || tile.url; + window.localStorage.setItem( + "olCache_" + url, + canvasContext.canvas.toDataURL(this.imageFormat) + ); + } + } catch(e) { + // local storage full or CORS violation + var reason = e.name || e.message; + if (reason && this.quotaRegEx.test(reason)) { + this.events.triggerEvent("cachefull", {tile: tile}); + } else { + OpenLayers.Console.error(e.toString()); + } + } + } + }, + + /** + * Method: destroy + * The destroy method is used to perform any clean up before the control + * is dereferenced. Typically this is where event listeners are removed + * to prevent memory leaks. + */ + destroy: function() { + if (this.layers || this.map) { + var i, layers = this.layers || this.map.layers; + for (i=layers.length-1; i>=0; --i) { + this.removeLayer({layer: layers[i]}); + } + } + if (this.map) { + this.map.events.un({ + addlayer: this.addLayer, + removeLayer: this.removeLayer, + scope: this + }); + } + OpenLayers.Control.prototype.destroy.apply(this, arguments); + }, + + CLASS_NAME: "OpenLayers.Control.CacheWrite" +}); + +/** + * APIFunction: OpenLayers.Control.CacheWrite.clearCache + * Clears all tiles cached with from the cache. + */ +OpenLayers.Control.CacheWrite.clearCache = function() { + if (!window.localStorage) { return; } + var i, key; + for (i=window.localStorage.length-1; i>=0; --i) { + key = window.localStorage.key(i); + if (key.substr(0, 8) === "olCache_") { + window.localStorage.removeItem(key); + } + } +}; + +/** + * Property: OpenLayers.Control.CacheWrite.urlMap + * {Object} Mapping of same origin urls to cache url keys. Entries will be + * deleted as soon as a tile was cached. + */ +OpenLayers.Control.CacheWrite.urlMap = {}; + + diff --git a/misc/openlayers/lib/OpenLayers/Control/DragFeature.js b/misc/openlayers/lib/OpenLayers/Control/DragFeature.js new file mode 100644 index 0000000..d8fb15f --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/DragFeature.js @@ -0,0 +1,366 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Handler/Drag.js + * @requires OpenLayers/Handler/Feature.js + */ + +/** + * Class: OpenLayers.Control.DragFeature + * The DragFeature control moves a feature with a drag of the mouse. Create a + * new control with the constructor. + * + * Inherits From: + * - + */ +OpenLayers.Control.DragFeature = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: geometryTypes + * {Array(String)} To restrict dragging to a limited set of geometry types, + * send a list of strings corresponding to the geometry class names. + */ + geometryTypes: null, + + /** + * APIProperty: onStart + * {Function} Define this function if you want to know when a drag starts. + * The function should expect to receive two arguments: the feature + * that is about to be dragged and the pixel location of the mouse. + * + * Parameters: + * feature - {} The feature that is about to be + * dragged. + * pixel - {} The pixel location of the mouse. + */ + onStart: function(feature, pixel) {}, + + /** + * APIProperty: onDrag + * {Function} Define this function if you want to know about each move of a + * feature. The function should expect to receive two arguments: the + * feature that is being dragged and the pixel location of the mouse. + * + * Parameters: + * feature - {} The feature that was dragged. + * pixel - {} The pixel location of the mouse. + */ + onDrag: function(feature, pixel) {}, + + /** + * APIProperty: onComplete + * {Function} Define this function if you want to know when a feature is + * done dragging. The function should expect to receive two arguments: + * the feature that is being dragged and the pixel location of the + * mouse. + * + * Parameters: + * feature - {} The feature that was dragged. + * pixel - {} The pixel location of the mouse. + */ + onComplete: function(feature, pixel) {}, + + /** + * APIProperty: onEnter + * {Function} Define this function if you want to know when the mouse + * goes over a feature and thereby makes this feature a candidate + * for dragging. + * + * Parameters: + * feature - {} The feature that is ready + * to be dragged. + */ + onEnter: function(feature) {}, + + /** + * APIProperty: onLeave + * {Function} Define this function if you want to know when the mouse + * goes out of the feature that was dragged. + * + * Parameters: + * feature - {} The feature that was dragged. + */ + onLeave: function(feature) {}, + + /** + * APIProperty: documentDrag + * {Boolean} If set to true, mouse dragging will continue even if the + * mouse cursor leaves the map viewport. Default is false. + */ + documentDrag: false, + + /** + * Property: layer + * {} + */ + layer: null, + + /** + * Property: feature + * {} + */ + feature: null, + + /** + * Property: dragCallbacks + * {Object} The functions that are sent to the drag handler for callback. + */ + dragCallbacks: {}, + + /** + * Property: featureCallbacks + * {Object} The functions that are sent to the feature handler for callback. + */ + featureCallbacks: {}, + + /** + * Property: lastPixel + * {} + */ + lastPixel: null, + + /** + * Constructor: OpenLayers.Control.DragFeature + * Create a new control to drag features. + * + * Parameters: + * layer - {} The layer containing features to be + * dragged. + * options - {Object} Optional object whose properties will be set on the + * control. + */ + initialize: function(layer, options) { + OpenLayers.Control.prototype.initialize.apply(this, [options]); + this.layer = layer; + this.handlers = { + drag: new OpenLayers.Handler.Drag( + this, OpenLayers.Util.extend({ + down: this.downFeature, + move: this.moveFeature, + up: this.upFeature, + out: this.cancel, + done: this.doneDragging + }, this.dragCallbacks), { + documentDrag: this.documentDrag + } + ), + feature: new OpenLayers.Handler.Feature( + this, this.layer, OpenLayers.Util.extend({ + // 'click' and 'clickout' callback are for the mobile + // support: no 'over' or 'out' in touch based browsers. + click: this.clickFeature, + clickout: this.clickoutFeature, + over: this.overFeature, + out: this.outFeature + }, this.featureCallbacks), + {geometryTypes: this.geometryTypes} + ) + }; + }, + + /** + * Method: clickFeature + * Called when the feature handler detects a click-in on a feature. + * + * Parameters: + * feature - {} + */ + clickFeature: function(feature) { + if (this.handlers.feature.touch && !this.over && this.overFeature(feature)) { + this.handlers.drag.dragstart(this.handlers.feature.evt); + // to let the events propagate to the feature handler (click callback) + this.handlers.drag.stopDown = false; + } + }, + + /** + * Method: clickoutFeature + * Called when the feature handler detects a click-out on a feature. + * + * Parameters: + * feature - {} + */ + clickoutFeature: function(feature) { + if (this.handlers.feature.touch && this.over) { + this.outFeature(feature); + this.handlers.drag.stopDown = true; + } + }, + + /** + * APIMethod: destroy + * Take care of things that are not handled in superclass + */ + destroy: function() { + this.layer = null; + OpenLayers.Control.prototype.destroy.apply(this, []); + }, + + /** + * APIMethod: activate + * Activate the control and the feature handler. + * + * Returns: + * {Boolean} Successfully activated the control and feature handler. + */ + activate: function() { + return (this.handlers.feature.activate() && + OpenLayers.Control.prototype.activate.apply(this, arguments)); + }, + + /** + * APIMethod: deactivate + * Deactivate the control and all handlers. + * + * Returns: + * {Boolean} Successfully deactivated the control. + */ + deactivate: function() { + // the return from the handlers is unimportant in this case + this.handlers.drag.deactivate(); + this.handlers.feature.deactivate(); + this.feature = null; + this.dragging = false; + this.lastPixel = null; + OpenLayers.Element.removeClass( + this.map.viewPortDiv, this.displayClass + "Over" + ); + return OpenLayers.Control.prototype.deactivate.apply(this, arguments); + }, + + /** + * Method: overFeature + * Called when the feature handler detects a mouse-over on a feature. + * This activates the drag handler. + * + * Parameters: + * feature - {} The selected feature. + * + * Returns: + * {Boolean} Successfully activated the drag handler. + */ + overFeature: function(feature) { + var activated = false; + if(!this.handlers.drag.dragging) { + this.feature = feature; + this.handlers.drag.activate(); + activated = true; + this.over = true; + OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass + "Over"); + this.onEnter(feature); + } else { + if(this.feature.id == feature.id) { + this.over = true; + } else { + this.over = false; + } + } + return activated; + }, + + /** + * Method: downFeature + * Called when the drag handler detects a mouse-down. + * + * Parameters: + * pixel - {} Location of the mouse event. + */ + downFeature: function(pixel) { + this.lastPixel = pixel; + this.onStart(this.feature, pixel); + }, + + /** + * Method: moveFeature + * Called when the drag handler detects a mouse-move. Also calls the + * optional onDrag method. + * + * Parameters: + * pixel - {} Location of the mouse event. + */ + moveFeature: function(pixel) { + var res = this.map.getResolution(); + this.feature.geometry.move(res * (pixel.x - this.lastPixel.x), + res * (this.lastPixel.y - pixel.y)); + this.layer.drawFeature(this.feature); + this.lastPixel = pixel; + this.onDrag(this.feature, pixel); + }, + + /** + * Method: upFeature + * Called when the drag handler detects a mouse-up. + * + * Parameters: + * pixel - {} Location of the mouse event. + */ + upFeature: function(pixel) { + if(!this.over) { + this.handlers.drag.deactivate(); + } + }, + + /** + * Method: doneDragging + * Called when the drag handler is done dragging. + * + * Parameters: + * pixel - {} The last event pixel location. If this event + * came from a mouseout, this may not be in the map viewport. + */ + doneDragging: function(pixel) { + this.onComplete(this.feature, pixel); + }, + + /** + * Method: outFeature + * Called when the feature handler detects a mouse-out on a feature. + * + * Parameters: + * feature - {} The feature that the mouse left. + */ + outFeature: function(feature) { + if(!this.handlers.drag.dragging) { + this.over = false; + this.handlers.drag.deactivate(); + OpenLayers.Element.removeClass( + this.map.viewPortDiv, this.displayClass + "Over" + ); + this.onLeave(feature); + this.feature = null; + } else { + if(this.feature.id == feature.id) { + this.over = false; + } + } + }, + + /** + * Method: cancel + * Called when the drag handler detects a mouse-out (from the map viewport). + */ + cancel: function() { + this.handlers.drag.deactivate(); + this.over = false; + }, + + /** + * Method: setMap + * Set the map property for the control and all handlers. + * + * Parameters: + * map - {} The control's map. + */ + setMap: function(map) { + this.handlers.drag.setMap(map); + this.handlers.feature.setMap(map); + OpenLayers.Control.prototype.setMap.apply(this, arguments); + }, + + CLASS_NAME: "OpenLayers.Control.DragFeature" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/DragPan.js b/misc/openlayers/lib/OpenLayers/Control/DragPan.js new file mode 100644 index 0000000..981a649 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/DragPan.js @@ -0,0 +1,156 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Handler/Drag.js + */ + +/** + * Class: OpenLayers.Control.DragPan + * The DragPan control pans the map with a drag of the mouse. + * + * Inherits from: + * - + */ +OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, { + + /** + * Property: type + * {OpenLayers.Control.TYPES} + */ + type: OpenLayers.Control.TYPE_TOOL, + + /** + * Property: panned + * {Boolean} The map moved. + */ + panned: false, + + /** + * Property: interval + * {Integer} The number of milliseconds that should ellapse before + * panning the map again. Defaults to 0 milliseconds, which means that + * no separate cycle is used for panning. In most cases you won't want + * to change this value. For slow machines/devices larger values can be + * tried out. + */ + interval: 0, + + /** + * APIProperty: documentDrag + * {Boolean} If set to true, mouse dragging will continue even if the + * mouse cursor leaves the map viewport. Default is false. + */ + documentDrag: false, + + /** + * Property: kinetic + * {} The OpenLayers.Kinetic object. + */ + kinetic: null, + + /** + * APIProperty: enableKinetic + * {Boolean} Set this option to enable "kinetic dragging". Can be + * set to true or to an object. If set to an object this + * object will be passed to the {} + * constructor. Defaults to true. + * To get kinetic dragging, ensure that OpenLayers/Kinetic.js is + * included in your build config. + */ + enableKinetic: true, + + /** + * APIProperty: kineticInterval + * {Integer} Interval in milliseconds between 2 steps in the "kinetic + * scrolling". Applies only if enableKinetic is set. Defaults + * to 10 milliseconds. + */ + kineticInterval: 10, + + + /** + * Method: draw + * Creates a Drag handler, using and + * as callbacks. + */ + draw: function() { + if (this.enableKinetic && OpenLayers.Kinetic) { + var config = {interval: this.kineticInterval}; + if(typeof this.enableKinetic === "object") { + config = OpenLayers.Util.extend(config, this.enableKinetic); + } + this.kinetic = new OpenLayers.Kinetic(config); + } + this.handler = new OpenLayers.Handler.Drag(this, { + "move": this.panMap, + "done": this.panMapDone, + "down": this.panMapStart + }, { + interval: this.interval, + documentDrag: this.documentDrag + } + ); + }, + + /** + * Method: panMapStart + */ + panMapStart: function() { + if(this.kinetic) { + this.kinetic.begin(); + } + }, + + /** + * Method: panMap + * + * Parameters: + * xy - {} Pixel of the mouse position + */ + panMap: function(xy) { + if(this.kinetic) { + this.kinetic.update(xy); + } + this.panned = true; + this.map.pan( + this.handler.last.x - xy.x, + this.handler.last.y - xy.y, + {dragging: true, animate: false} + ); + }, + + /** + * Method: panMapDone + * Finish the panning operation. Only call setCenter (through ) + * if the map has actually been moved. + * + * Parameters: + * xy - {} Pixel of the mouse position + */ + panMapDone: function(xy) { + if(this.panned) { + var res = null; + if (this.kinetic) { + res = this.kinetic.end(xy); + } + this.map.pan( + this.handler.last.x - xy.x, + this.handler.last.y - xy.y, + {dragging: !!res, animate: false} + ); + if (res) { + var self = this; + this.kinetic.move(res, function(x, y, end) { + self.map.pan(x, y, {dragging: !end, animate: false}); + }); + } + this.panned = false; + } + }, + + CLASS_NAME: "OpenLayers.Control.DragPan" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/DrawFeature.js b/misc/openlayers/lib/OpenLayers/Control/DrawFeature.js new file mode 100644 index 0000000..b0afc71 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/DrawFeature.js @@ -0,0 +1,229 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Feature/Vector.js + */ + +/** + * Class: OpenLayers.Control.DrawFeature + * The DrawFeature control draws point, line or polygon features on a vector + * layer when active. + * + * Inherits from: + * - + */ +OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, { + + /** + * Property: layer + * {} + */ + layer: null, + + /** + * Property: callbacks + * {Object} The functions that are sent to the handler for callback + */ + callbacks: null, + + /** + * APIProperty: events + * {} Events instance for listeners and triggering + * control specific events. + * + * Register a listener for a particular event with the following syntax: + * (code) + * control.events.register(type, obj, listener); + * (end) + * + * Supported event types (in addition to those from ): + * featureadded - Triggered when a feature is added + */ + + /** + * APIProperty: multi + * {Boolean} Cast features to multi-part geometries before passing to the + * layer. Default is false. + */ + multi: false, + + /** + * APIProperty: featureAdded + * {Function} Called after each feature is added + */ + featureAdded: function() {}, + + /** + * APIProperty: handlerOptions + * {Object} Used to set non-default properties on the control's handler + */ + + /** + * Constructor: OpenLayers.Control.DrawFeature + * + * Parameters: + * layer - {} + * handler - {} + * options - {Object} + */ + initialize: function(layer, handler, options) { + OpenLayers.Control.prototype.initialize.apply(this, [options]); + this.callbacks = OpenLayers.Util.extend( + { + done: this.drawFeature, + modify: function(vertex, feature) { + this.layer.events.triggerEvent( + "sketchmodified", {vertex: vertex, feature: feature} + ); + }, + create: function(vertex, feature) { + this.layer.events.triggerEvent( + "sketchstarted", {vertex: vertex, feature: feature} + ); + } + }, + this.callbacks + ); + this.layer = layer; + this.handlerOptions = this.handlerOptions || {}; + this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults( + this.handlerOptions.layerOptions, { + renderers: layer.renderers, rendererOptions: layer.rendererOptions + } + ); + if (!("multi" in this.handlerOptions)) { + this.handlerOptions.multi = this.multi; + } + var sketchStyle = this.layer.styleMap && this.layer.styleMap.styles.temporary; + if(sketchStyle) { + this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults( + this.handlerOptions.layerOptions, + {styleMap: new OpenLayers.StyleMap({"default": sketchStyle})} + ); + } + this.handler = new handler(this, this.callbacks, this.handlerOptions); + }, + + /** + * Method: drawFeature + */ + drawFeature: function(geometry) { + var feature = new OpenLayers.Feature.Vector(geometry); + var proceed = this.layer.events.triggerEvent( + "sketchcomplete", {feature: feature} + ); + if(proceed !== false) { + feature.state = OpenLayers.State.INSERT; + this.layer.addFeatures([feature]); + this.featureAdded(feature); + this.events.triggerEvent("featureadded",{feature : feature}); + } + }, + + /** + * APIMethod: insertXY + * Insert a point in the current sketch given x & y coordinates. + * + * Parameters: + * x - {Number} The x-coordinate of the point. + * y - {Number} The y-coordinate of the point. + */ + insertXY: function(x, y) { + if (this.handler && this.handler.line) { + this.handler.insertXY(x, y); + } + }, + + /** + * APIMethod: insertDeltaXY + * Insert a point given offsets from the previously inserted point. + * + * Parameters: + * dx - {Number} The x-coordinate offset of the point. + * dy - {Number} The y-coordinate offset of the point. + */ + insertDeltaXY: function(dx, dy) { + if (this.handler && this.handler.line) { + this.handler.insertDeltaXY(dx, dy); + } + }, + + /** + * APIMethod: insertDirectionLength + * Insert a point in the current sketch given a direction and a length. + * + * Parameters: + * direction - {Number} Degrees clockwise from the positive x-axis. + * length - {Number} Distance from the previously drawn point. + */ + insertDirectionLength: function(direction, length) { + if (this.handler && this.handler.line) { + this.handler.insertDirectionLength(direction, length); + } + }, + + /** + * APIMethod: insertDeflectionLength + * Insert a point in the current sketch given a deflection and a length. + * The deflection should be degrees clockwise from the previously + * digitized segment. + * + * Parameters: + * deflection - {Number} Degrees clockwise from the previous segment. + * length - {Number} Distance from the previously drawn point. + */ + insertDeflectionLength: function(deflection, length) { + if (this.handler && this.handler.line) { + this.handler.insertDeflectionLength(deflection, length); + } + }, + + /** + * APIMethod: undo + * Remove the most recently added point in the current sketch geometry. + * + * Returns: + * {Boolean} An edit was undone. + */ + undo: function() { + return this.handler.undo && this.handler.undo(); + }, + + /** + * APIMethod: redo + * Reinsert the most recently removed point resulting from an call. + * The undo stack is deleted whenever a point is added by other means. + * + * Returns: + * {Boolean} An edit was redone. + */ + redo: function() { + return this.handler.redo && this.handler.redo(); + }, + + /** + * APIMethod: finishSketch + * Finishes the sketch without including the currently drawn point. + * This method can be called to terminate drawing programmatically + * instead of waiting for the user to end the sketch. + */ + finishSketch: function() { + this.handler.finishGeometry(); + }, + + /** + * APIMethod: cancel + * Cancel the current sketch. This removes the current sketch and keeps + * the drawing control active. + */ + cancel: function() { + this.handler.cancel(); + }, + + CLASS_NAME: "OpenLayers.Control.DrawFeature" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/EditingToolbar.js b/misc/openlayers/lib/OpenLayers/Control/EditingToolbar.js new file mode 100644 index 0000000..ba7ca40 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/EditingToolbar.js @@ -0,0 +1,81 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control/Panel.js + * @requires OpenLayers/Control/Navigation.js + * @requires OpenLayers/Control/DrawFeature.js + * @requires OpenLayers/Handler/Point.js + * @requires OpenLayers/Handler/Path.js + * @requires OpenLayers/Handler/Polygon.js + */ + +/** + * Class: OpenLayers.Control.EditingToolbar + * The EditingToolbar is a panel of 4 controls to draw polygons, lines, + * points, or to navigate the map by panning. By default it appears in the + * upper right corner of the map. + * + * Inherits from: + * - + */ +OpenLayers.Control.EditingToolbar = OpenLayers.Class( + OpenLayers.Control.Panel, { + + /** + * APIProperty: citeCompliant + * {Boolean} If set to true, coordinates of features drawn in a map extent + * crossing the date line won't exceed the world bounds. Default is false. + */ + citeCompliant: false, + + /** + * Constructor: OpenLayers.Control.EditingToolbar + * Create an editing toolbar for a given layer. + * + * Parameters: + * layer - {} + * options - {Object} + */ + initialize: function(layer, options) { + OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); + + this.addControls( + [ new OpenLayers.Control.Navigation() ] + ); + var controls = [ + new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, { + displayClass: 'olControlDrawFeaturePoint', + handlerOptions: {citeCompliant: this.citeCompliant} + }), + new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, { + displayClass: 'olControlDrawFeaturePath', + handlerOptions: {citeCompliant: this.citeCompliant} + }), + new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, { + displayClass: 'olControlDrawFeaturePolygon', + handlerOptions: {citeCompliant: this.citeCompliant} + }) + ]; + this.addControls(controls); + }, + + /** + * Method: draw + * calls the default draw, and then activates mouse defaults. + * + * Returns: + * {DOMElement} + */ + draw: function() { + var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments); + if (this.defaultControl === null) { + this.defaultControl = this.controls[0]; + } + return div; + }, + + CLASS_NAME: "OpenLayers.Control.EditingToolbar" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/Geolocate.js b/misc/openlayers/lib/OpenLayers/Control/Geolocate.js new file mode 100644 index 0000000..4b5b439 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/Geolocate.js @@ -0,0 +1,192 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Geometry/Point.js + * @requires OpenLayers/Projection.js + */ + +/** + * Class: OpenLayers.Control.Geolocate + * The Geolocate control wraps w3c geolocation API into control that can be + * bound to a map, and generate events on location update + * + * To use this control requires to load the proj4js library if the projection + * of the map is not EPSG:4326 or EPSG:900913. + * + * Inherits from: + * - + */ +OpenLayers.Control.Geolocate = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: events + * {} Events instance for listeners and triggering + * control specific events. + * + * Register a listener for a particular event with the following syntax: + * (code) + * control.events.register(type, obj, listener); + * (end) + * + * Supported event types (in addition to those from ): + * locationupdated - Triggered when browser return a new position. Listeners will + * receive an object with a 'position' property which is the browser.geolocation.position + * native object, as well as a 'point' property which is the location transformed in the + * current map projection. + * locationfailed - Triggered when geolocation has failed + * locationuncapable - Triggered when control is activated on a browser + * which doesn't support geolocation + */ + + /** + * Property: geolocation + * {Object} The geolocation engine, as a property to be possibly mocked. + * This is set lazily to avoid a memory leak in IE9. + */ + geolocation: null, + + /** + * Property: available + * {Boolean} The navigator.geolocation object is available. + */ + available: ('geolocation' in navigator), + + /** + * APIProperty: bind + * {Boolean} If true, map center will be set on location update. + */ + bind: true, + + /** + * APIProperty: watch + * {Boolean} If true, position will be update regularly. + */ + watch: false, + + /** + * APIProperty: geolocationOptions + * {Object} Options to pass to the navigator's geolocation API. See + * . No specific + * option is passed to the geolocation API by default. + */ + geolocationOptions: null, + + /** + * Constructor: OpenLayers.Control.Geolocate + * Create a new control to deal with browser geolocation API + * + */ + + /** + * Method: destroy + */ + destroy: function() { + this.deactivate(); + OpenLayers.Control.prototype.destroy.apply(this, arguments); + }, + + /** + * Method: activate + * Activates the control. + * + * Returns: + * {Boolean} The control was effectively activated. + */ + activate: function () { + if (this.available && !this.geolocation) { + // set lazily to avoid IE9 memory leak + this.geolocation = navigator.geolocation; + } + if (!this.geolocation) { + this.events.triggerEvent("locationuncapable"); + return false; + } + if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { + if (this.watch) { + this.watchId = this.geolocation.watchPosition( + OpenLayers.Function.bind(this.geolocate, this), + OpenLayers.Function.bind(this.failure, this), + this.geolocationOptions + ); + } else { + this.getCurrentLocation(); + } + return true; + } + return false; + }, + + /** + * Method: deactivate + * Deactivates the control. + * + * Returns: + * {Boolean} The control was effectively deactivated. + */ + deactivate: function () { + if (this.active && this.watchId !== null) { + this.geolocation.clearWatch(this.watchId); + } + return OpenLayers.Control.prototype.deactivate.apply( + this, arguments + ); + }, + + /** + * Method: geolocate + * Activates the control. + * + */ + geolocate: function (position) { + var center = new OpenLayers.LonLat( + position.coords.longitude, + position.coords.latitude + ).transform( + new OpenLayers.Projection("EPSG:4326"), + this.map.getProjectionObject() + ); + if (this.bind) { + this.map.setCenter(center); + } + this.events.triggerEvent("locationupdated", { + position: position, + point: new OpenLayers.Geometry.Point( + center.lon, center.lat + ) + }); + }, + + /** + * APIMethod: getCurrentLocation + * + * Returns: + * {Boolean} Returns true if a event will be fired (successfull + * registration) + */ + getCurrentLocation: function() { + if (!this.active || this.watch) { + return false; + } + this.geolocation.getCurrentPosition( + OpenLayers.Function.bind(this.geolocate, this), + OpenLayers.Function.bind(this.failure, this), + this.geolocationOptions + ); + return true; + }, + + /** + * Method: failure + * method called on browser's geolocation failure + * + */ + failure: function (error) { + this.events.triggerEvent("locationfailed", {error: error}); + }, + + CLASS_NAME: "OpenLayers.Control.Geolocate" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/GetFeature.js b/misc/openlayers/lib/OpenLayers/Control/GetFeature.js new file mode 100644 index 0000000..144e87f --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/GetFeature.js @@ -0,0 +1,597 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Handler/Click.js + * @requires OpenLayers/Handler/Box.js + * @requires OpenLayers/Handler/Hover.js + * @requires OpenLayers/Filter/Spatial.js + */ + +/** + * Class: OpenLayers.Control.GetFeature + * Gets vector features for locations underneath the mouse cursor. Can be + * configured to act on click, hover or dragged boxes. Uses an + * that supports spatial filters to retrieve + * features from a server and fires events that notify applications of the + * selected features. + * + * Inherits from: + * - + */ +OpenLayers.Control.GetFeature = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: protocol + * {} Required. The protocol used for fetching + * features. + */ + protocol: null, + + /** + * APIProperty: multipleKey + * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets + * the property to true. Default is null. + */ + multipleKey: null, + + /** + * APIProperty: toggleKey + * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets + * the property to true. Default is null. + */ + toggleKey: null, + + /** + * Property: modifiers + * {Object} The event modifiers to use, according to the current event + * being handled by this control's handlers + */ + modifiers: null, + + /** + * APIProperty: multiple + * {Boolean} Allow selection of multiple geometries. Default is false. + */ + multiple: false, + + /** + * APIProperty: click + * {Boolean} Use a click handler for selecting/unselecting features. If + * both and are set to true, the click handler takes + * precedence over the box handler if a box with zero extent was + * selected. Default is true. + */ + click: true, + + /** + * APIProperty: single + * {Boolean} Tells whether select by click should select a single + * feature. If set to false, all matching features are selected. + * If set to true, only the best matching feature is selected. + * This option has an effect only of the option is set + * to true. Default is true. + */ + single: true, + + /** + * APIProperty: clickout + * {Boolean} Unselect features when clicking outside any feature. + * Applies only if is true. Default is true. + */ + clickout: true, + + /** + * APIProperty: toggle + * {Boolean} Unselect a selected feature on click. Applies only if + * is true. Default is false. + */ + toggle: false, + + /** + * APIProperty: clickTolerance + * {Integer} Tolerance for the filter query in pixels. This has the + * same effect as the tolerance parameter on WMS GetFeatureInfo + * requests. Will be ignored for box selections. Applies only if + * or is true. Default is 5. Note that this not + * only affects requests on click, but also on hover. + */ + clickTolerance: 5, + + /** + * APIProperty: hover + * {Boolean} Send feature requests on mouse moves. Default is false. + */ + hover: false, + + /** + * APIProperty: box + * {Boolean} Allow feature selection by drawing a box. If set to + * true set to false to disable the click handler and + * rely on the box handler only, even for "zero extent" boxes. + * See the description of the option for additional + * information. Default is false. + */ + box: false, + + /** + * APIProperty: maxFeatures + * {Integer} Maximum number of features to return from a query in single mode + * if supported by the . This set of features is then used to + * determine the best match client-side. Default is 10. + */ + maxFeatures: 10, + + /** + * Property: features + * {Object} Hash of {}, keyed by fid, holding + * the currently selected features + */ + features: null, + + /** + * Proeprty: hoverFeature + * {} The feature currently selected by the + * hover handler + */ + hoverFeature: null, + + /** + * APIProperty: handlerOptions + * {Object} Additional options for the handlers used by this control. This + * is a hash with the keys "click", "box" and "hover". + */ + + /** + * Property: handlers + * {Object} Object with references to multiple + * instances. + */ + handlers: null, + + /** + * Property: hoverResponse + * {} The response object associated with + * the currently running hover request (if any). + */ + hoverResponse: null, + + /** + * Property: filterType + * {} The type of filter to use when sending off a request. + * Possible values: + * OpenLayers.Filter.Spatial. + * Defaults to: OpenLayers.Filter.Spatial.BBOX + */ + filterType: OpenLayers.Filter.Spatial.BBOX, + + /** + * APIProperty: events + * {} Events instance for listeners and triggering + * control specific events. + * + * Register a listener for a particular event with the following syntax: + * (code) + * control.events.register(type, obj, listener); + * (end) + * + * Supported event types (in addition to those from ): + * beforefeatureselected - Triggered when is true before a + * feature is selected. The event object has a feature property with + * the feature about to select + * featureselected - Triggered when is true and a feature is + * selected. The event object has a feature property with the + * selected feature + * beforefeaturesselected - Triggered when is true before a + * set of features is selected. The event object is an array of + * feature properties with the features about to be selected. + * Return false after receiving this event to discontinue processing + * of all featureselected events and the featuresselected event. + * featuresselected - Triggered when is true and a set of + * features is selected. The event object is an array of feature + * properties of the selected features + * featureunselected - Triggered when is true and a feature is + * unselected. The event object has a feature property with the + * unselected feature + * clickout - Triggered when when is true and no feature was + * selected. + * hoverfeature - Triggered when is true and the mouse has + * stopped over a feature + * outfeature - Triggered when is true and the mouse moves + * moved away from a hover-selected feature + */ + + /** + * Constructor: OpenLayers.Control.GetFeature + * Create a new control for fetching remote features. + * + * Parameters: + * options - {Object} A configuration object which at least has to contain + * a property (if not, it has to be set before a request is + * made) + */ + initialize: function(options) { + options.handlerOptions = options.handlerOptions || {}; + + OpenLayers.Control.prototype.initialize.apply(this, [options]); + + this.features = {}; + + this.handlers = {}; + + if(this.click) { + this.handlers.click = new OpenLayers.Handler.Click(this, + {click: this.selectClick}, this.handlerOptions.click || {}); + } + + if(this.box) { + this.handlers.box = new OpenLayers.Handler.Box( + this, {done: this.selectBox}, + OpenLayers.Util.extend(this.handlerOptions.box, { + boxDivClassName: "olHandlerBoxSelectFeature" + }) + ); + } + + if(this.hover) { + this.handlers.hover = new OpenLayers.Handler.Hover( + this, {'move': this.cancelHover, 'pause': this.selectHover}, + OpenLayers.Util.extend(this.handlerOptions.hover, { + 'delay': 250, + 'pixelTolerance': 2 + }) + ); + } + }, + + /** + * Method: activate + * Activates the control. + * + * Returns: + * {Boolean} The control was effectively activated. + */ + activate: function () { + if (!this.active) { + for(var i in this.handlers) { + this.handlers[i].activate(); + } + } + return OpenLayers.Control.prototype.activate.apply( + this, arguments + ); + }, + + /** + * Method: deactivate + * Deactivates the control. + * + * Returns: + * {Boolean} The control was effectively deactivated. + */ + deactivate: function () { + if (this.active) { + for(var i in this.handlers) { + this.handlers[i].deactivate(); + } + } + return OpenLayers.Control.prototype.deactivate.apply( + this, arguments + ); + }, + + /** + * Method: selectClick + * Called on click + * + * Parameters: + * evt - {} + */ + selectClick: function(evt) { + var bounds = this.pixelToBounds(evt.xy); + + this.setModifiers(evt); + this.request(bounds, {single: this.single}); + }, + + /** + * Method: selectBox + * Callback from the handlers.box set up when selection is on + * + * Parameters: + * position - {|Object} An OpenLayers.Bounds or + * an object with a 'left', 'bottom', 'right' and 'top' properties. + */ + selectBox: function(position) { + var bounds; + if (position instanceof OpenLayers.Bounds) { + var minXY = this.map.getLonLatFromPixel({ + x: position.left, + y: position.bottom + }); + var maxXY = this.map.getLonLatFromPixel({ + x: position.right, + y: position.top + }); + bounds = new OpenLayers.Bounds( + minXY.lon, minXY.lat, maxXY.lon, maxXY.lat + ); + + } else { + if(this.click) { + // box without extent - let the click handler take care of it + return; + } + bounds = this.pixelToBounds(position); + } + this.setModifiers(this.handlers.box.dragHandler.evt); + this.request(bounds); + }, + + /** + * Method: selectHover + * Callback from the handlers.hover set up when selection is on + * + * Parameters: + * evt - {Object} event object with an xy property + */ + selectHover: function(evt) { + var bounds = this.pixelToBounds(evt.xy); + this.request(bounds, {single: true, hover: true}); + }, + + /** + * Method: cancelHover + * Callback from the handlers.hover set up when selection is on + */ + cancelHover: function() { + if (this.hoverResponse) { + this.protocol.abort(this.hoverResponse); + this.hoverResponse = null; + + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); + } + }, + + /** + * Method: request + * Sends a GetFeature request to the WFS + * + * Parameters: + * bounds - {} bounds for the request's BBOX filter + * options - {Object} additional options for this method. + * + * Supported options include: + * single - {Boolean} A single feature should be returned. + * Note that this will be ignored if the protocol does not + * return the geometries of the features. + * hover - {Boolean} Do the request for the hover handler. + */ + request: function(bounds, options) { + options = options || {}; + var filter = new OpenLayers.Filter.Spatial({ + type: this.filterType, + value: bounds + }); + + // Set the cursor to "wait" to tell the user we're working. + OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait"); + + var response = this.protocol.read({ + maxFeatures: options.single == true ? this.maxFeatures : undefined, + filter: filter, + callback: function(result) { + if(result.success()) { + if(result.features.length) { + if(options.single == true) { + this.selectBestFeature(result.features, + bounds.getCenterLonLat(), options); + } else { + this.select(result.features); + } + } else if(options.hover) { + this.hoverSelect(); + } else { + this.events.triggerEvent("clickout"); + if(this.clickout) { + this.unselectAll(); + } + } + } + // Reset the cursor. + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); + }, + scope: this + }); + if(options.hover == true) { + this.hoverResponse = response; + } + }, + + /** + * Method: selectBestFeature + * Selects the feature from an array of features that is the best match + * for the click position. + * + * Parameters: + * features - {Array()} + * clickPosition - {} + * options - {Object} additional options for this method + * + * Supported options include: + * hover - {Boolean} Do the selection for the hover handler. + */ + selectBestFeature: function(features, clickPosition, options) { + options = options || {}; + if(features.length) { + var point = new OpenLayers.Geometry.Point(clickPosition.lon, + clickPosition.lat); + var feature, resultFeature, dist; + var minDist = Number.MAX_VALUE; + for(var i=0; i} + */ + setModifiers: function(evt) { + this.modifiers = { + multiple: this.multiple || (this.multipleKey && evt[this.multipleKey]), + toggle: this.toggle || (this.toggleKey && evt[this.toggleKey]) + }; + }, + + /** + * Method: select + * Add feature to the hash of selected features and trigger the + * featureselected and featuresselected events. + * + * Parameters: + * features - {} or an array of features + */ + select: function(features) { + if(!this.modifiers.multiple && !this.modifiers.toggle) { + this.unselectAll(); + } + if(!(OpenLayers.Util.isArray(features))) { + features = [features]; + } + + var cont = this.events.triggerEvent("beforefeaturesselected", { + features: features + }); + if(cont !== false) { + var selectedFeatures = []; + var feature; + for(var i=0, len=features.length; i + * + * Parameters: + * feature - {} the feature to hover-select. + * If none is provided, the current will be nulled and + * the outfeature event will be triggered. + */ + hoverSelect: function(feature) { + var fid = feature ? feature.fid || feature.id : null; + var hfid = this.hoverFeature ? + this.hoverFeature.fid || this.hoverFeature.id : null; + + if(hfid && hfid != fid) { + this.events.triggerEvent("outfeature", + {feature: this.hoverFeature}); + this.hoverFeature = null; + } + if(fid && fid != hfid) { + this.events.triggerEvent("hoverfeature", {feature: feature}); + this.hoverFeature = feature; + } + }, + + /** + * Method: unselect + * Remove feature from the hash of selected features and trigger the + * featureunselected event. + * + * Parameters: + * feature - {} + */ + unselect: function(feature) { + delete this.features[feature.fid || feature.id]; + this.events.triggerEvent("featureunselected", {feature: feature}); + }, + + /** + * Method: unselectAll + * Unselect all selected features. + */ + unselectAll: function() { + // we'll want an option to supress notification here + for(var fid in this.features) { + this.unselect(this.features[fid]); + } + }, + + /** + * Method: setMap + * Set the map property for the control. + * + * Parameters: + * map - {} + */ + setMap: function(map) { + for(var i in this.handlers) { + this.handlers[i].setMap(map); + } + OpenLayers.Control.prototype.setMap.apply(this, arguments); + }, + + /** + * Method: pixelToBounds + * Takes a pixel as argument and creates bounds after adding the + * . + * + * Parameters: + * pixel - {} + */ + pixelToBounds: function(pixel) { + var llPx = pixel.add(-this.clickTolerance/2, this.clickTolerance/2); + var urPx = pixel.add(this.clickTolerance/2, -this.clickTolerance/2); + var ll = this.map.getLonLatFromPixel(llPx); + var ur = this.map.getLonLatFromPixel(urPx); + return new OpenLayers.Bounds(ll.lon, ll.lat, ur.lon, ur.lat); + }, + + CLASS_NAME: "OpenLayers.Control.GetFeature" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/Graticule.js b/misc/openlayers/lib/OpenLayers/Control/Graticule.js new file mode 100644 index 0000000..2fce50d --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/Graticule.js @@ -0,0 +1,377 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Lang.js + * @requires OpenLayers/Rule.js + * @requires OpenLayers/StyleMap.js + * @requires OpenLayers/Layer/Vector.js + */ + +/** + * Class: OpenLayers.Control.Graticule + * The Graticule displays a grid of latitude/longitude lines reprojected on + * the map. + * + * Inherits from: + * - + * + */ +OpenLayers.Control.Graticule = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: autoActivate + * {Boolean} Activate the control when it is added to a map. Default is + * true. + */ + autoActivate: true, + + /** + * APIProperty: intervals + * {Array(Float)} A list of possible graticule widths in degrees. + */ + intervals: [ 45, 30, 20, 10, 5, 2, 1, + 0.5, 0.2, 0.1, 0.05, 0.01, + 0.005, 0.002, 0.001 ], + + /** + * APIProperty: displayInLayerSwitcher + * {Boolean} Allows the Graticule control to be switched on and off by + * LayerSwitcher control. Defaults is true. + */ + displayInLayerSwitcher: true, + + /** + * APIProperty: visible + * {Boolean} should the graticule be initially visible (default=true) + */ + visible: true, + + /** + * APIProperty: numPoints + * {Integer} The number of points to use in each graticule line. Higher + * numbers result in a smoother curve for projected maps + */ + numPoints: 50, + + /** + * APIProperty: targetSize + * {Integer} The maximum size of the grid in pixels on the map + */ + targetSize: 200, + + /** + * APIProperty: layerName + * {String} The name to be displayed in the layer switcher, default is set + * by {}. + */ + layerName: null, + + /** + * APIProperty: labelled + * {Boolean} Should the graticule lines be labelled?. default=true + */ + labelled: true, + + /** + * APIProperty: labelFormat + * {String} the format of the labels, default = 'dm'. See + * for other options. + */ + labelFormat: 'dm', + + /** + * APIProperty: lineSymbolizer + * {symbolizer} the symbolizer used to render lines + */ + lineSymbolizer: { + strokeColor: "#333", + strokeWidth: 1, + strokeOpacity: 0.5 + }, + + /** + * APIProperty: labelSymbolizer + * {symbolizer} the symbolizer used to render labels + */ + labelSymbolizer: {}, + + /** + * Property: gratLayer + * {} vector layer used to draw the graticule on + */ + gratLayer: null, + + /** + * Constructor: OpenLayers.Control.Graticule + * Create a new graticule control to display a grid of latitude longitude + * lines. + * + * Parameters: + * options - {Object} An optional object whose properties will be used + * to extend the control. + */ + initialize: function(options) { + options = options || {}; + options.layerName = options.layerName || OpenLayers.i18n("Graticule"); + OpenLayers.Control.prototype.initialize.apply(this, [options]); + + this.labelSymbolizer.stroke = false; + this.labelSymbolizer.fill = false; + this.labelSymbolizer.label = "${label}"; + this.labelSymbolizer.labelAlign = "${labelAlign}"; + this.labelSymbolizer.labelXOffset = "${xOffset}"; + this.labelSymbolizer.labelYOffset = "${yOffset}"; + }, + + /** + * APIMethod: destroy + */ + destroy: function() { + this.deactivate(); + OpenLayers.Control.prototype.destroy.apply(this, arguments); + if (this.gratLayer) { + this.gratLayer.destroy(); + this.gratLayer = null; + } + }, + + /** + * Method: draw + * + * initializes the graticule layer and does the initial update + * + * Returns: + * {DOMElement} + */ + draw: function() { + OpenLayers.Control.prototype.draw.apply(this, arguments); + if (!this.gratLayer) { + var gratStyle = new OpenLayers.Style({},{ + rules: [new OpenLayers.Rule({'symbolizer': + {"Point":this.labelSymbolizer, + "Line":this.lineSymbolizer} + })] + }); + this.gratLayer = new OpenLayers.Layer.Vector(this.layerName, { + styleMap: new OpenLayers.StyleMap({'default':gratStyle}), + visibility: this.visible, + displayInLayerSwitcher: this.displayInLayerSwitcher + }); + } + return this.div; + }, + + /** + * APIMethod: activate + */ + activate: function() { + if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { + this.map.addLayer(this.gratLayer); + this.map.events.register('moveend', this, this.update); + this.update(); + return true; + } else { + return false; + } + }, + + /** + * APIMethod: deactivate + */ + deactivate: function() { + if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { + this.map.events.unregister('moveend', this, this.update); + this.map.removeLayer(this.gratLayer); + return true; + } else { + return false; + } + }, + /** + * Method: update + * + * calculates the grid to be displayed and actually draws it + * + * Returns: + * {DOMElement} + */ + update: function() { + //wait for the map to be initialized before proceeding + var mapBounds = this.map.getExtent(); + if (!mapBounds) { + return; + } + + //clear out the old grid + this.gratLayer.destroyFeatures(); + + //get the projection objects required + var llProj = new OpenLayers.Projection("EPSG:4326"); + var mapProj = this.map.getProjectionObject(); + var mapRes = this.map.getResolution(); + + //if the map is in lon/lat, then the lines are straight and only one + //point is required + if (mapProj.proj && mapProj.proj.projName == "longlat") { + this.numPoints = 1; + } + + //get the map center in EPSG:4326 + var mapCenter = this.map.getCenter(); //lon and lat here are really map x and y + var mapCenterLL = new OpenLayers.Pixel(mapCenter.lon, mapCenter.lat); + OpenLayers.Projection.transform(mapCenterLL, mapProj, llProj); + + /* This block of code determines the lon/lat interval to use for the + * grid by calculating the diagonal size of one grid cell at the map + * center. Iterates through the intervals array until the diagonal + * length is less than the targetSize option. + */ + //find lat/lon interval that results in a grid of less than the target size + var testSq = this.targetSize*mapRes; + testSq *= testSq; //compare squares rather than doing a square root to save time + var llInterval; + for (var i=0; i= mapBounds.bottom && !labelPoint) { + labelPoint = gridPoint; + } + } + if (this.labelled) { + //keep track of when this grid line crosses the map bounds to set + //the label position + //labels along the bottom, add 10 pixel offset up into the map + //TODO add option for labels on top + var labelPos = new OpenLayers.Geometry.Point(labelPoint.x,mapBounds.bottom); + var labelAttrs = { + value: lon, + label: this.labelled?OpenLayers.Util.getFormattedLonLat(lon, "lon", this.labelFormat):"", + labelAlign: "cb", + xOffset: 0, + yOffset: 2 + }; + this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos,labelAttrs)); + } + var geom = new OpenLayers.Geometry.LineString(pointList); + lines.push(new OpenLayers.Feature.Vector(geom)); + } + + //now draw the lines of constant latitude + for (var j=0; j < centerLonPoints.length; ++j) { + lat = centerLonPoints[j].y; + if (lat<-90 || lat>90) { //latitudes only valid between -90 and 90 + continue; + } + var pointList = []; + var lonStart = centerLatPoints[0].x; + var lonEnd = centerLatPoints[centerLatPoints.length - 1].x; + var lonDelta = (lonEnd - lonStart)/this.numPoints; + var lon = lonStart; + var labelPoint = null; + for(var i=0; i <= this.numPoints ; ++i) { + var gridPoint = new OpenLayers.Geometry.Point(lon,lat); + gridPoint.transform(llProj, mapProj); + pointList.push(gridPoint); + lon += lonDelta; + if (gridPoint.x < mapBounds.right) { + labelPoint = gridPoint; + } + } + if (this.labelled) { + //keep track of when this grid line crosses the map bounds to set + //the label position + //labels along the right, 30 pixel offset left into the map + //TODO add option for labels on left + var labelPos = new OpenLayers.Geometry.Point(mapBounds.right, labelPoint.y); + var labelAttrs = { + value: lat, + label: this.labelled?OpenLayers.Util.getFormattedLonLat(lat, "lat", this.labelFormat):"", + labelAlign: "rb", + xOffset: -2, + yOffset: 2 + }; + this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos,labelAttrs)); + } + var geom = new OpenLayers.Geometry.LineString(pointList); + lines.push(new OpenLayers.Feature.Vector(geom)); + } + this.gratLayer.addFeatures(lines); + }, + + CLASS_NAME: "OpenLayers.Control.Graticule" +}); + diff --git a/misc/openlayers/lib/OpenLayers/Control/KeyboardDefaults.js b/misc/openlayers/lib/OpenLayers/Control/KeyboardDefaults.js new file mode 100644 index 0000000..3af8831 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/KeyboardDefaults.js @@ -0,0 +1,142 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Handler/Keyboard.js + * @requires OpenLayers/Events.js + */ + +/** + * Class: OpenLayers.Control.KeyboardDefaults + * The KeyboardDefaults control adds panning and zooming functions, controlled + * with the keyboard. By default arrow keys pan, +/- keys zoom & Page Up/Page + * Down/Home/End scroll by three quarters of a page. + * + * This control has no visible appearance. + * + * Inherits from: + * - + */ +OpenLayers.Control.KeyboardDefaults = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: autoActivate + * {Boolean} Activate the control when it is added to a map. Default is + * true. + */ + autoActivate: true, + + /** + * APIProperty: slideFactor + * Pixels to slide by. + */ + slideFactor: 75, + + /** + * APIProperty: observeElement + * {DOMelement|String} The DOM element to handle keys for. You + * can use the map div here, to have the navigation keys + * work when the map div has the focus. If undefined the + * document is used. + */ + observeElement: null, + + /** + * Constructor: OpenLayers.Control.KeyboardDefaults + */ + + /** + * Method: draw + * Create handler. + */ + draw: function() { + var observeElement = this.observeElement || document; + this.handler = new OpenLayers.Handler.Keyboard( this, + {"keydown": this.defaultKeyPress}, + {observeElement: observeElement} + ); + }, + + /** + * Method: defaultKeyPress + * When handling the key event, we only use evt.keyCode. This holds + * some drawbacks, though we get around them below. 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 + * + * Parameters: + * evt - {Event} + */ + defaultKeyPress: function (evt) { + var size, handled = true; + + var target = OpenLayers.Event.element(evt); + if (target && + (target.tagName == 'INPUT' || + target.tagName == 'TEXTAREA' || + target.tagName == 'SELECT')) { + return; + } + + switch (evt.keyCode) { + case OpenLayers.Event.KEY_LEFT: + this.map.pan(-this.slideFactor, 0); + break; + case OpenLayers.Event.KEY_RIGHT: + this.map.pan(this.slideFactor, 0); + break; + case OpenLayers.Event.KEY_UP: + this.map.pan(0, -this.slideFactor); + break; + case OpenLayers.Event.KEY_DOWN: + this.map.pan(0, this.slideFactor); + break; + + case 33: // Page Up. Same in all browsers. + size = this.map.getSize(); + this.map.pan(0, -0.75*size.h); + break; + case 34: // Page Down. Same in all browsers. + size = this.map.getSize(); + this.map.pan(0, 0.75*size.h); + break; + case 35: // End. Same in all browsers. + size = this.map.getSize(); + this.map.pan(0.75*size.w, 0); + break; + case 36: // Home. Same in all browsers. + size = this.map.getSize(); + this.map.pan(-0.75*size.w, 0); + break; + + case 43: // +/= (ASCII), keypad + (ASCII, Opera) + case 61: // +/= (Mozilla, Opera, some ASCII) + case 187: // +/= (IE) + case 107: // keypad + (IE, Mozilla) + this.map.zoomIn(); + break; + case 45: // -/_ (ASCII, Opera), keypad - (ASCII, Opera) + case 109: // -/_ (Mozilla), keypad - (Mozilla, IE) + case 189: // -/_ (IE) + case 95: // -/_ (some ASCII) + this.map.zoomOut(); + break; + default: + handled = false; + } + if (handled) { + // prevent browser default not to move the page + // when moving the page with the keyboard + OpenLayers.Event.stop(evt); + } + }, + + CLASS_NAME: "OpenLayers.Control.KeyboardDefaults" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/LayerSwitcher.js b/misc/openlayers/lib/OpenLayers/Control/LayerSwitcher.js new file mode 100644 index 0000000..668f5c3 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/LayerSwitcher.js @@ -0,0 +1,521 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Lang.js + * @requires OpenLayers/Util.js + * @requires OpenLayers/Events/buttonclick.js + */ + +/** + * Class: OpenLayers.Control.LayerSwitcher + * The LayerSwitcher control displays a table of contents for the map. This + * allows the user interface to switch between BaseLasyers and to show or hide + * Overlays. By default the switcher is shown minimized on the right edge of + * the map, the user may expand it by clicking on the handle. + * + * To create the LayerSwitcher outside of the map, pass the Id of a html div + * as the first argument to the constructor. + * + * Inherits from: + * - + */ +OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, { + + /** + * Property: layerStates + * {Array(Object)} Basically a copy of the "state" of the map's layers + * the last time the control was drawn. We have this in order to avoid + * unnecessarily redrawing the control. + */ + layerStates: null, + + // DOM Elements + + /** + * Property: layersDiv + * {DOMElement} + */ + layersDiv: null, + + /** + * Property: baseLayersDiv + * {DOMElement} + */ + baseLayersDiv: null, + + /** + * Property: baseLayers + * {Array(Object)} + */ + baseLayers: null, + + + /** + * Property: dataLbl + * {DOMElement} + */ + dataLbl: null, + + /** + * Property: dataLayersDiv + * {DOMElement} + */ + dataLayersDiv: null, + + /** + * Property: dataLayers + * {Array(Object)} + */ + dataLayers: null, + + + /** + * Property: minimizeDiv + * {DOMElement} + */ + minimizeDiv: null, + + /** + * Property: maximizeDiv + * {DOMElement} + */ + maximizeDiv: null, + + /** + * APIProperty: ascending + * {Boolean} + */ + ascending: true, + + /** + * Constructor: OpenLayers.Control.LayerSwitcher + * + * Parameters: + * options - {Object} + */ + initialize: function(options) { + OpenLayers.Control.prototype.initialize.apply(this, arguments); + this.layerStates = []; + }, + + /** + * APIMethod: destroy + */ + destroy: function() { + + //clear out layers info and unregister their events + this.clearLayersArray("base"); + this.clearLayersArray("data"); + + this.map.events.un({ + buttonclick: this.onButtonClick, + addlayer: this.redraw, + changelayer: this.redraw, + removelayer: this.redraw, + changebaselayer: this.redraw, + scope: this + }); + this.events.unregister("buttonclick", this, this.onButtonClick); + + OpenLayers.Control.prototype.destroy.apply(this, arguments); + }, + + /** + * Method: setMap + * + * Properties: + * map - {} + */ + setMap: function(map) { + OpenLayers.Control.prototype.setMap.apply(this, arguments); + + this.map.events.on({ + addlayer: this.redraw, + changelayer: this.redraw, + removelayer: this.redraw, + changebaselayer: this.redraw, + scope: this + }); + if (this.outsideViewport) { + this.events.attachToElement(this.div); + this.events.register("buttonclick", this, this.onButtonClick); + } else { + this.map.events.register("buttonclick", this, this.onButtonClick); + } + }, + + /** + * Method: draw + * + * Returns: + * {DOMElement} A reference to the DIV DOMElement containing the + * switcher tabs. + */ + draw: function() { + OpenLayers.Control.prototype.draw.apply(this); + + // create layout divs + this.loadContents(); + + // set mode to minimize + if(!this.outsideViewport) { + this.minimizeControl(); + } + + // populate div with current info + this.redraw(); + + return this.div; + }, + + /** + * Method: onButtonClick + * + * Parameters: + * evt - {Event} + */ + onButtonClick: function(evt) { + var button = evt.buttonElement; + if (button === this.minimizeDiv) { + this.minimizeControl(); + } else if (button === this.maximizeDiv) { + this.maximizeControl(); + } else if (button._layerSwitcher === this.id) { + if (button["for"]) { + button = document.getElementById(button["for"]); + } + if (!button.disabled) { + if (button.type == "radio") { + button.checked = true; + this.map.setBaseLayer(this.map.getLayer(button._layer)); + } else { + button.checked = !button.checked; + this.updateMap(); + } + } + } + }, + + /** + * Method: clearLayersArray + * User specifies either "base" or "data". we then clear all the + * corresponding listeners, the div, and reinitialize a new array. + * + * Parameters: + * layersType - {String} + */ + clearLayersArray: function(layersType) { + this[layersType + "LayersDiv"].innerHTML = ""; + this[layersType + "Layers"] = []; + }, + + + /** + * Method: checkRedraw + * Checks if the layer state has changed since the last redraw() call. + * + * Returns: + * {Boolean} The layer state changed since the last redraw() call. + */ + checkRedraw: function() { + if ( !this.layerStates.length || + (this.map.layers.length != this.layerStates.length) ) { + return true; + } + + for (var i = 0, len = this.layerStates.length; i < len; i++) { + var layerState = this.layerStates[i]; + var layer = this.map.layers[i]; + if ( (layerState.name != layer.name) || + (layerState.inRange != layer.inRange) || + (layerState.id != layer.id) || + (layerState.visibility != layer.visibility) ) { + return true; + } + } + + return false; + }, + + /** + * Method: redraw + * Goes through and takes the current state of the Map and rebuilds the + * control to display that state. Groups base layers into a + * radio-button group and lists each data layer with a checkbox. + * + * Returns: + * {DOMElement} A reference to the DIV DOMElement containing the control + */ + redraw: function() { + //if the state hasn't changed since last redraw, no need + // to do anything. Just return the existing div. + if (!this.checkRedraw()) { + return this.div; + } + + //clear out previous layers + this.clearLayersArray("base"); + this.clearLayersArray("data"); + + var containsOverlays = false; + var containsBaseLayers = false; + + // Save state -- for checking layer if the map state changed. + // We save this before redrawing, because in the process of redrawing + // we will trigger more visibility changes, and we want to not redraw + // and enter an infinite loop. + var len = this.map.layers.length; + this.layerStates = new Array(len); + for (var i=0; i + labelSpan["for"] = inputElem.id; + OpenLayers.Element.addClass(labelSpan, "labelSpan olButton"); + labelSpan._layer = layer.id; + labelSpan._layerSwitcher = this.id; + if (!baseLayer && !layer.inRange) { + labelSpan.style.color = "gray"; + } + labelSpan.innerHTML = layer.name; + labelSpan.style.verticalAlign = (baseLayer) ? "bottom" + : "baseline"; + // create line break + var br = document.createElement("br"); + + + var groupArray = (baseLayer) ? this.baseLayers + : this.dataLayers; + groupArray.push({ + 'layer': layer, + 'inputElem': inputElem, + 'labelSpan': labelSpan + }); + + + var groupDiv = (baseLayer) ? this.baseLayersDiv + : this.dataLayersDiv; + groupDiv.appendChild(inputElem); + groupDiv.appendChild(labelSpan); + groupDiv.appendChild(br); + } + } + + // if no overlays, dont display the overlay label + this.dataLbl.style.display = (containsOverlays) ? "" : "none"; + + // if no baselayers, dont display the baselayer label + this.baseLbl.style.display = (containsBaseLayers) ? "" : "none"; + + return this.div; + }, + + /** + * Method: updateMap + * Cycles through the loaded data and base layer input arrays and makes + * the necessary calls to the Map object such that that the map's + * visual state corresponds to what the user has selected in + * the control. + */ + updateMap: function() { + + // set the newly selected base layer + for(var i=0, len=this.baseLayers.length; i + */ +OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: events + * {} Events instance for listeners and triggering + * control specific events. + * + * Register a listener for a particular event with the following syntax: + * (code) + * control.events.register(type, obj, listener); + * (end) + * + * Supported event types (in addition to those from ): + * measure - Triggered when a measurement sketch is complete. Listeners + * will receive an event with measure, units, order, and geometry + * properties. + * measurepartial - Triggered when a new point is added to the + * measurement sketch or if the property is true and the + * measurement sketch is modified. Listeners receive an event with measure, + * units, order, and geometry. + */ + + /** + * APIProperty: handlerOptions + * {Object} Used to set non-default properties on the control's handler + */ + + /** + * Property: callbacks + * {Object} The functions that are sent to the handler for callback + */ + callbacks: null, + + /** + * APIProperty: displaySystem + * {String} Display system for output measurements. Supported values + * are 'english', 'metric', and 'geographic'. Default is 'metric'. + */ + displaySystem: 'metric', + + /** + * APIProperty: geodesic + * {Boolean} Calculate geodesic metrics instead of planar metrics. This + * requires that geometries can be transformed into Geographic/WGS84 + * (if that is not already the map projection). Default is false. + */ + geodesic: false, + + /** + * Property: displaySystemUnits + * {Object} Units for various measurement systems. Values are arrays + * of unit abbreviations (from OpenLayers.INCHES_PER_UNIT) in decreasing + * order of length. + */ + displaySystemUnits: { + geographic: ['dd'], + english: ['mi', 'ft', 'in'], + metric: ['km', 'm'] + }, + + /** + * Property: delay + * {Number} Number of milliseconds between clicks before the event is + * considered a double-click. The "measurepartial" event will not + * be triggered if the sketch is completed within this time. This + * is required for IE where creating a browser reflow (if a listener + * is modifying the DOM by displaying the measurement values) messes + * with the dblclick listener in the sketch handler. + */ + partialDelay: 300, + + /** + * Property: delayedTrigger + * {Number} Timeout id of trigger for measurepartial. + */ + delayedTrigger: null, + + /** + * APIProperty: persist + * {Boolean} Keep the temporary measurement sketch drawn after the + * measurement is complete. The geometry will persist until a new + * measurement is started, the control is deactivated, or is + * called. + */ + persist: false, + + /** + * APIProperty: immediate + * {Boolean} Activates the immediate measurement so that the "measurepartial" + * event is also fired once the measurement sketch is modified. + * Default is false. + */ + immediate : false, + + /** + * Constructor: OpenLayers.Control.Measure + * + * Parameters: + * handler - {} + * options - {Object} + */ + initialize: function(handler, options) { + OpenLayers.Control.prototype.initialize.apply(this, [options]); + var callbacks = {done: this.measureComplete, + point: this.measurePartial}; + if (this.immediate){ + callbacks.modify = this.measureImmediate; + } + this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks); + + // let the handler options override, so old code that passes 'persist' + // directly to the handler does not need an update + this.handlerOptions = OpenLayers.Util.extend( + {persist: this.persist}, this.handlerOptions + ); + this.handler = new handler(this, this.callbacks, this.handlerOptions); + }, + + /** + * APIMethod: deactivate + */ + deactivate: function() { + this.cancelDelay(); + return OpenLayers.Control.prototype.deactivate.apply(this, arguments); + }, + + /** + * APIMethod: cancel + * Stop the control from measuring. If is true, the temporary + * sketch will be erased. + */ + cancel: function() { + this.cancelDelay(); + this.handler.cancel(); + }, + + /** + * APIMethod: setImmediate + * Sets the property. Changes the activity of immediate + * measurement. + */ + setImmediate: function(immediate) { + this.immediate = immediate; + if (this.immediate){ + this.callbacks.modify = this.measureImmediate; + } else { + delete this.callbacks.modify; + } + }, + + /** + * Method: updateHandler + * + * Parameters: + * handler - {Function} One of the sketch handler constructors. + * options - {Object} Options for the handler. + */ + updateHandler: function(handler, options) { + var active = this.active; + if(active) { + this.deactivate(); + } + this.handler = new handler(this, this.callbacks, options); + if(active) { + this.activate(); + } + }, + + /** + * Method: measureComplete + * Called when the measurement sketch is done. + * + * Parameters: + * geometry - {} + */ + measureComplete: function(geometry) { + this.cancelDelay(); + this.measure(geometry, "measure"); + }, + + /** + * Method: measurePartial + * Called each time a new point is added to the measurement sketch. + * + * Parameters: + * point - {} The last point added. + * geometry - {} The sketch geometry. + */ + measurePartial: function(point, geometry) { + this.cancelDelay(); + geometry = geometry.clone(); + // when we're wating for a dblclick, we have to trigger measurepartial + // after some delay to deal with reflow issues in IE + if (this.handler.freehandMode(this.handler.evt)) { + // no dblclick in freehand mode + this.measure(geometry, "measurepartial"); + } else { + this.delayedTrigger = window.setTimeout( + OpenLayers.Function.bind(function() { + this.delayedTrigger = null; + this.measure(geometry, "measurepartial"); + }, this), + this.partialDelay + ); + } + }, + + /** + * Method: measureImmediate + * Called each time the measurement sketch is modified. + * + * Parameters: + * point - {} The point at the mouse position. + * feature - {} The sketch feature. + * drawing - {Boolean} Indicates whether we're currently drawing. + */ + measureImmediate : function(point, feature, drawing) { + if (drawing && !this.handler.freehandMode(this.handler.evt)) { + this.cancelDelay(); + this.measure(feature.geometry, "measurepartial"); + } + }, + + /** + * Method: cancelDelay + * Cancels the delay measurement that measurePartial began. + */ + cancelDelay: function() { + if (this.delayedTrigger !== null) { + window.clearTimeout(this.delayedTrigger); + this.delayedTrigger = null; + } + }, + + /** + * Method: measure + * + * Parameters: + * geometry - {} + * eventType - {String} + */ + measure: function(geometry, eventType) { + var stat, order; + if(geometry.CLASS_NAME.indexOf('LineString') > -1) { + stat = this.getBestLength(geometry); + order = 1; + } else { + stat = this.getBestArea(geometry); + order = 2; + } + this.events.triggerEvent(eventType, { + measure: stat[0], + units: stat[1], + order: order, + geometry: geometry + }); + }, + + /** + * Method: getBestArea + * Based on the returns the area of a geometry. + * + * Parameters: + * geometry - {} + * + * Returns: + * {Array([Float, String])} Returns a two item array containing the + * area and the units abbreviation. + */ + getBestArea: function(geometry) { + var units = this.displaySystemUnits[this.displaySystem]; + var unit, area; + for(var i=0, len=units.length; i 1) { + break; + } + } + return [area, unit]; + }, + + /** + * Method: getArea + * + * Parameters: + * geometry - {} + * units - {String} Unit abbreviation + * + * Returns: + * {Float} The geometry area in the given units. + */ + getArea: function(geometry, units) { + var area, geomUnits; + if(this.geodesic) { + area = geometry.getGeodesicArea(this.map.getProjectionObject()); + geomUnits = "m"; + } else { + area = geometry.getArea(); + geomUnits = this.map.getUnits(); + } + var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units]; + if(inPerDisplayUnit) { + var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits]; + area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2); + } + return area; + }, + + /** + * Method: getBestLength + * Based on the returns the length of a geometry. + * + * Parameters: + * geometry - {} + * + * Returns: + * {Array([Float, String])} Returns a two item array containing the + * length and the units abbreviation. + */ + getBestLength: function(geometry) { + var units = this.displaySystemUnits[this.displaySystem]; + var unit, length; + for(var i=0, len=units.length; i 1) { + break; + } + } + return [length, unit]; + }, + + /** + * Method: getLength + * + * Parameters: + * geometry - {} + * units - {String} Unit abbreviation + * + * Returns: + * {Float} The geometry length in the given units. + */ + getLength: function(geometry, units) { + var length, geomUnits; + if(this.geodesic) { + length = geometry.getGeodesicLength(this.map.getProjectionObject()); + geomUnits = "m"; + } else { + length = geometry.getLength(); + geomUnits = this.map.getUnits(); + } + var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units]; + if(inPerDisplayUnit) { + var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits]; + length *= (inPerMapUnit / inPerDisplayUnit); + } + return length; + }, + + CLASS_NAME: "OpenLayers.Control.Measure" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/ModifyFeature.js b/misc/openlayers/lib/OpenLayers/Control/ModifyFeature.js new file mode 100644 index 0000000..e574608 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/ModifyFeature.js @@ -0,0 +1,835 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Handler/Drag.js + * @requires OpenLayers/Handler/Keyboard.js + */ + +/** + * Class: OpenLayers.Control.ModifyFeature + * Control to modify features. When activated, a click renders the vertices + * of a feature - these vertices can then be dragged. By default, the + * delete key will delete the vertex under the mouse. New features are + * added by dragging "virtual vertices" between vertices. Create a new + * control with the constructor. + * + * Inherits From: + * - + */ +OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: documentDrag + * {Boolean} If set to true, dragging vertices will continue even if the + * mouse cursor leaves the map viewport. Default is false. + */ + documentDrag: false, + + /** + * APIProperty: geometryTypes + * {Array(String)} To restrict modification to a limited set of geometry + * types, send a list of strings corresponding to the geometry class + * names. + */ + geometryTypes: null, + + /** + * APIProperty: clickout + * {Boolean} Unselect features when clicking outside any feature. + * Default is true. + */ + clickout: true, + + /** + * APIProperty: toggle + * {Boolean} Unselect a selected feature on click. + * Default is true. + */ + toggle: true, + + /** + * APIProperty: standalone + * {Boolean} Set to true to create a control without SelectFeature + * capabilities. Default is false. If standalone is true, to modify + * a feature, call the method with the target feature. + * Note that you must call the method to finish + * feature modification in standalone mode (before starting to modify + * another feature). + */ + standalone: false, + + /** + * Property: layer + * {} + */ + layer: null, + + /** + * Property: feature + * {} Feature currently available for modification. + */ + feature: null, + + /** + * Property: vertex + * {} Vertex currently being modified. + */ + vertex: null, + + /** + * Property: vertices + * {Array()} Verticies currently available + * for dragging. + */ + vertices: null, + + /** + * Property: virtualVertices + * {Array()} Virtual vertices in the middle + * of each edge. + */ + virtualVertices: null, + + /** + * Property: handlers + * {Object} + */ + handlers: null, + + /** + * APIProperty: deleteCodes + * {Array(Integer)} Keycodes for deleting verticies. Set to null to disable + * vertex deltion by keypress. If non-null, keypresses with codes + * in this array will delete vertices under the mouse. Default + * is 46 and 68, the 'delete' and lowercase 'd' keys. + */ + deleteCodes: null, + + /** + * APIProperty: virtualStyle + * {Object} A symbolizer to be used for virtual vertices. + */ + virtualStyle: null, + + /** + * APIProperty: vertexRenderIntent + * {String} The renderIntent to use for vertices. If no is + * provided, this renderIntent will also be used for virtual vertices, with + * a fillOpacity and strokeOpacity of 0.3. Default is null, which means + * that the layer's default style will be used for vertices. + */ + vertexRenderIntent: null, + + /** + * APIProperty: mode + * {Integer} Bitfields specifying the modification mode. Defaults to + * OpenLayers.Control.ModifyFeature.RESHAPE. To set the mode to a + * combination of options, use the | operator. For example, to allow + * the control to both resize and rotate features, use the following + * syntax + * (code) + * control.mode = OpenLayers.Control.ModifyFeature.RESIZE | + * OpenLayers.Control.ModifyFeature.ROTATE; + * (end) + */ + mode: null, + + /** + * APIProperty: createVertices + * {Boolean} Create new vertices by dragging the virtual vertices + * in the middle of each edge. Default is true. + */ + createVertices: true, + + /** + * Property: modified + * {Boolean} The currently selected feature has been modified. + */ + modified: false, + + /** + * Property: radiusHandle + * {} A handle for rotating/resizing a feature. + */ + radiusHandle: null, + + /** + * Property: dragHandle + * {} A handle for dragging a feature. + */ + dragHandle: null, + + /** + * APIProperty: onModificationStart + * {Function} *Deprecated*. Register for "beforefeaturemodified" instead. + * The "beforefeaturemodified" event is triggered on the layer before + * any modification begins. + * + * Optional function to be called when a feature is selected + * to be modified. The function should expect to be called with a + * feature. This could be used for example to allow to lock the + * feature on server-side. + */ + onModificationStart: function() {}, + + /** + * APIProperty: onModification + * {Function} *Deprecated*. Register for "featuremodified" instead. + * The "featuremodified" event is triggered on the layer with each + * feature modification. + * + * Optional function to be called when a feature has been + * modified. The function should expect to be called with a feature. + */ + onModification: function() {}, + + /** + * APIProperty: onModificationEnd + * {Function} *Deprecated*. Register for "afterfeaturemodified" instead. + * The "afterfeaturemodified" event is triggered on the layer after + * a feature has been modified. + * + * Optional function to be called when a feature is finished + * being modified. The function should expect to be called with a + * feature. + */ + onModificationEnd: function() {}, + + /** + * Constructor: OpenLayers.Control.ModifyFeature + * Create a new modify feature control. + * + * Parameters: + * layer - {} Layer that contains features that + * will be modified. + * options - {Object} Optional object whose properties will be set on the + * control. + */ + initialize: function(layer, options) { + options = options || {}; + this.layer = layer; + this.vertices = []; + this.virtualVertices = []; + this.virtualStyle = OpenLayers.Util.extend({}, + this.layer.style || + this.layer.styleMap.createSymbolizer(null, options.vertexRenderIntent) + ); + this.virtualStyle.fillOpacity = 0.3; + this.virtualStyle.strokeOpacity = 0.3; + this.deleteCodes = [46, 68]; + this.mode = OpenLayers.Control.ModifyFeature.RESHAPE; + OpenLayers.Control.prototype.initialize.apply(this, [options]); + if(!(OpenLayers.Util.isArray(this.deleteCodes))) { + this.deleteCodes = [this.deleteCodes]; + } + + // configure the drag handler + var dragCallbacks = { + down: function(pixel) { + this.vertex = null; + var feature = this.layer.getFeatureFromEvent( + this.handlers.drag.evt); + if (feature) { + this.dragStart(feature); + } else if (this.clickout) { + this._unselect = this.feature; + } + }, + move: function(pixel) { + delete this._unselect; + if (this.vertex) { + this.dragVertex(this.vertex, pixel); + } + }, + up: function() { + this.handlers.drag.stopDown = false; + if (this._unselect) { + this.unselectFeature(this._unselect); + delete this._unselect; + } + }, + done: function(pixel) { + if (this.vertex) { + this.dragComplete(this.vertex); + } + } + }; + var dragOptions = { + documentDrag: this.documentDrag, + stopDown: false + }; + + // configure the keyboard handler + var keyboardOptions = { + keydown: this.handleKeypress + }; + this.handlers = { + keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions), + drag: new OpenLayers.Handler.Drag(this, dragCallbacks, dragOptions) + }; + }, + + /** + * APIMethod: destroy + * Take care of things that are not handled in superclass. + */ + destroy: function() { + if (this.map) { + this.map.events.un({ + "removelayer": this.handleMapEvents, + "changelayer": this.handleMapEvents, + scope: this + }); + } + this.layer = null; + OpenLayers.Control.prototype.destroy.apply(this, []); + }, + + /** + * APIMethod: activate + * Activate the control. + * + * Returns: + * {Boolean} Successfully activated the control. + */ + activate: function() { + this.moveLayerToTop(); + this.map.events.on({ + "removelayer": this.handleMapEvents, + "changelayer": this.handleMapEvents, + scope: this + }); + return (this.handlers.keyboard.activate() && + this.handlers.drag.activate() && + OpenLayers.Control.prototype.activate.apply(this, arguments)); + }, + + /** + * APIMethod: deactivate + * Deactivate the control. + * + * Returns: + * {Boolean} Successfully deactivated the control. + */ + deactivate: function() { + var deactivated = false; + // the return from the controls is unimportant in this case + if(OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { + this.moveLayerBack(); + this.map.events.un({ + "removelayer": this.handleMapEvents, + "changelayer": this.handleMapEvents, + scope: this + }); + this.layer.removeFeatures(this.vertices, {silent: true}); + this.layer.removeFeatures(this.virtualVertices, {silent: true}); + this.vertices = []; + this.handlers.drag.deactivate(); + this.handlers.keyboard.deactivate(); + var feature = this.feature; + if (feature && feature.geometry && feature.layer) { + this.unselectFeature(feature); + } + deactivated = true; + } + return deactivated; + }, + + /** + * Method: beforeSelectFeature + * Called before a feature is selected. + * + * Parameters: + * feature - {} The feature about to be selected. + */ + beforeSelectFeature: function(feature) { + return this.layer.events.triggerEvent( + "beforefeaturemodified", {feature: feature} + ); + }, + + /** + * APIMethod: selectFeature + * Select a feature for modification in standalone mode. In non-standalone + * mode, this method is called when a feature is selected by clicking. + * Register a listener to the beforefeaturemodified event and return false + * to prevent feature modification. + * + * Parameters: + * feature - {} the selected feature. + */ + selectFeature: function(feature) { + if (this.feature === feature || + (this.geometryTypes && OpenLayers.Util.indexOf(this.geometryTypes, + feature.geometry.CLASS_NAME) == -1)) { + return; + } + if (this.beforeSelectFeature(feature) !== false) { + if (this.feature) { + this.unselectFeature(this.feature); + } + this.feature = feature; + this.layer.selectedFeatures.push(feature); + this.layer.drawFeature(feature, 'select'); + this.modified = false; + this.resetVertices(); + this.onModificationStart(this.feature); + } + // keep track of geometry modifications + var modified = feature.modified; + if (feature.geometry && !(modified && modified.geometry)) { + this._originalGeometry = feature.geometry.clone(); + } + }, + + /** + * APIMethod: unselectFeature + * Called when the select feature control unselects a feature. + * + * Parameters: + * feature - {} The unselected feature. + */ + unselectFeature: function(feature) { + this.layer.removeFeatures(this.vertices, {silent: true}); + this.vertices = []; + this.layer.destroyFeatures(this.virtualVertices, {silent: true}); + this.virtualVertices = []; + if(this.dragHandle) { + this.layer.destroyFeatures([this.dragHandle], {silent: true}); + delete this.dragHandle; + } + if(this.radiusHandle) { + this.layer.destroyFeatures([this.radiusHandle], {silent: true}); + delete this.radiusHandle; + } + this.layer.drawFeature(this.feature, 'default'); + this.feature = null; + OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature); + this.onModificationEnd(feature); + this.layer.events.triggerEvent("afterfeaturemodified", { + feature: feature, + modified: this.modified + }); + this.modified = false; + }, + + + /** + * Method: dragStart + * Called by the drag handler before a feature is dragged. This method is + * used to differentiate between points and vertices + * of higher order geometries. + * + * Parameters: + * feature - {} The point or vertex about to be + * dragged. + */ + dragStart: function(feature) { + var isPoint = feature.geometry.CLASS_NAME == + 'OpenLayers.Geometry.Point'; + if (!this.standalone && + ((!feature._sketch && isPoint) || !feature._sketch)) { + if (this.toggle && this.feature === feature) { + // mark feature for unselection + this._unselect = feature; + } + this.selectFeature(feature); + } + if (feature._sketch || isPoint) { + // feature is a drag or virtual handle or point + this.vertex = feature; + this.handlers.drag.stopDown = true; + } + }, + + /** + * Method: dragVertex + * Called by the drag handler with each drag move of a vertex. + * + * Parameters: + * vertex - {} The vertex being dragged. + * pixel - {} Pixel location of the mouse event. + */ + dragVertex: function(vertex, pixel) { + var pos = this.map.getLonLatFromViewPortPx(pixel); + var geom = vertex.geometry; + geom.move(pos.lon - geom.x, pos.lat - geom.y); + this.modified = true; + /** + * Five cases: + * 1) dragging a simple point + * 2) dragging a virtual vertex + * 3) dragging a drag handle + * 4) dragging a real vertex + * 5) dragging a radius handle + */ + if(this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { + // dragging a simple point + this.layer.events.triggerEvent("vertexmodified", { + vertex: vertex.geometry, + feature: this.feature, + pixel: pixel + }); + } else { + if(vertex._index) { + // dragging a virtual vertex + vertex.geometry.parent.addComponent(vertex.geometry, + vertex._index); + // move from virtual to real vertex + delete vertex._index; + OpenLayers.Util.removeItem(this.virtualVertices, vertex); + this.vertices.push(vertex); + } else if(vertex == this.dragHandle) { + // dragging a drag handle + this.layer.removeFeatures(this.vertices, {silent: true}); + this.vertices = []; + if(this.radiusHandle) { + this.layer.destroyFeatures([this.radiusHandle], {silent: true}); + this.radiusHandle = null; + } + } else if(vertex !== this.radiusHandle) { + // dragging a real vertex + this.layer.events.triggerEvent("vertexmodified", { + vertex: vertex.geometry, + feature: this.feature, + pixel: pixel + }); + } + // dragging a radius handle - no special treatment + if(this.virtualVertices.length > 0) { + this.layer.destroyFeatures(this.virtualVertices, {silent: true}); + this.virtualVertices = []; + } + this.layer.drawFeature(this.feature, this.standalone ? undefined : + 'select'); + } + // keep the vertex on top so it gets the mouseout after dragging + // this should be removed in favor of an option to draw under or + // maintain node z-index + this.layer.drawFeature(vertex); + }, + + /** + * Method: dragComplete + * Called by the drag handler when the feature dragging is complete. + * + * Parameters: + * vertex - {} The vertex being dragged. + */ + dragComplete: function(vertex) { + this.resetVertices(); + this.setFeatureState(); + this.onModification(this.feature); + this.layer.events.triggerEvent("featuremodified", + {feature: this.feature}); + }, + + /** + * Method: setFeatureState + * Called when the feature is modified. If the current state is not + * INSERT or DELETE, the state is set to UPDATE. + */ + setFeatureState: function() { + if(this.feature.state != OpenLayers.State.INSERT && + this.feature.state != OpenLayers.State.DELETE) { + this.feature.state = OpenLayers.State.UPDATE; + if (this.modified && this._originalGeometry) { + var feature = this.feature; + feature.modified = OpenLayers.Util.extend(feature.modified, { + geometry: this._originalGeometry + }); + delete this._originalGeometry; + } + } + }, + + /** + * Method: resetVertices + */ + resetVertices: function() { + if(this.vertices.length > 0) { + this.layer.removeFeatures(this.vertices, {silent: true}); + this.vertices = []; + } + if(this.virtualVertices.length > 0) { + this.layer.removeFeatures(this.virtualVertices, {silent: true}); + this.virtualVertices = []; + } + if(this.dragHandle) { + this.layer.destroyFeatures([this.dragHandle], {silent: true}); + this.dragHandle = null; + } + if(this.radiusHandle) { + this.layer.destroyFeatures([this.radiusHandle], {silent: true}); + this.radiusHandle = null; + } + if(this.feature && + this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") { + if((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) { + this.collectDragHandle(); + } + if((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE | + OpenLayers.Control.ModifyFeature.RESIZE))) { + this.collectRadiusHandle(); + } + if(this.mode & OpenLayers.Control.ModifyFeature.RESHAPE){ + // Don't collect vertices when we're resizing + if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)){ + this.collectVertices(); + } + } + } + }, + + /** + * Method: handleKeypress + * Called by the feature handler on keypress. This is used to delete + * vertices. If the property is set, vertices will + * be deleted when a feature is selected for modification and + * the mouse is over a vertex. + * + * Parameters: + * evt - {Event} Keypress event. + */ + handleKeypress: function(evt) { + var code = evt.keyCode; + + // check for delete key + if(this.feature && + OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) { + var vertex = this.layer.getFeatureFromEvent(this.handlers.drag.evt); + if (vertex && + OpenLayers.Util.indexOf(this.vertices, vertex) != -1 && + !this.handlers.drag.dragging && vertex.geometry.parent) { + // remove the vertex + vertex.geometry.parent.removeComponent(vertex.geometry); + this.layer.events.triggerEvent("vertexremoved", { + vertex: vertex.geometry, + feature: this.feature, + pixel: evt.xy + }); + this.layer.drawFeature(this.feature, this.standalone ? + undefined : 'select'); + this.modified = true; + this.resetVertices(); + this.setFeatureState(); + this.onModification(this.feature); + this.layer.events.triggerEvent("featuremodified", + {feature: this.feature}); + } + } + }, + + /** + * Method: collectVertices + * Collect the vertices from the modifiable feature's geometry and push + * them on to the control's vertices array. + */ + collectVertices: function() { + this.vertices = []; + this.virtualVertices = []; + var control = this; + function collectComponentVertices(geometry) { + var i, vertex, component, len; + if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { + vertex = new OpenLayers.Feature.Vector(geometry); + vertex._sketch = true; + vertex.renderIntent = control.vertexRenderIntent; + control.vertices.push(vertex); + } else { + var numVert = geometry.components.length; + if(geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { + numVert -= 1; + } + for(i=0; i} The control's map. + */ + setMap: function(map) { + this.handlers.drag.setMap(map); + OpenLayers.Control.prototype.setMap.apply(this, arguments); + }, + + /** + * Method: handleMapEvents + * + * Parameters: + * evt - {Object} + */ + handleMapEvents: function(evt) { + if (evt.type == "removelayer" || evt.property == "order") { + this.moveLayerToTop(); + } + }, + + /** + * Method: moveLayerToTop + * Moves the layer for this handler to the top, so mouse events can reach + * it. + */ + moveLayerToTop: function() { + var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1, + this.layer.getZIndex()) + 1; + this.layer.setZIndex(index); + + }, + + /** + * Method: moveLayerBack + * Moves the layer back to the position determined by the map's layers + * array. + */ + moveLayerBack: function() { + var index = this.layer.getZIndex() - 1; + if (index >= this.map.Z_INDEX_BASE['Feature']) { + this.layer.setZIndex(index); + } else { + this.map.setLayerZIndex(this.layer, + this.map.getLayerIndex(this.layer)); + } + }, + + CLASS_NAME: "OpenLayers.Control.ModifyFeature" +}); + +/** + * Constant: RESHAPE + * {Integer} Constant used to make the control work in reshape mode + */ +OpenLayers.Control.ModifyFeature.RESHAPE = 1; +/** + * Constant: RESIZE + * {Integer} Constant used to make the control work in resize mode + */ +OpenLayers.Control.ModifyFeature.RESIZE = 2; +/** + * Constant: ROTATE + * {Integer} Constant used to make the control work in rotate mode + */ +OpenLayers.Control.ModifyFeature.ROTATE = 4; +/** + * Constant: DRAG + * {Integer} Constant used to make the control work in drag mode + */ +OpenLayers.Control.ModifyFeature.DRAG = 8; diff --git a/misc/openlayers/lib/OpenLayers/Control/MousePosition.js b/misc/openlayers/lib/OpenLayers/Control/MousePosition.js new file mode 100644 index 0000000..0c88fcf --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/MousePosition.js @@ -0,0 +1,227 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/Control.js + */ + +/** + * Class: OpenLayers.Control.MousePosition + * The MousePosition control displays geographic coordinates of the mouse + * pointer, as it is moved about the map. + * + * You can use the - or -properties to provide more information + * about the displayed coordinates to the user: + * + * (code) + * var mousePositionCtrl = new OpenLayers.Control.MousePosition({ + * prefix: '' + + * 'EPSG:4326 coordinates: ' + * } + * ); + * (end code) + * + * Inherits from: + * - + */ +OpenLayers.Control.MousePosition = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: autoActivate + * {Boolean} Activate the control when it is added to a map. Default is + * true. + */ + autoActivate: true, + + /** + * Property: element + * {DOMElement} + */ + element: null, + + /** + * APIProperty: prefix + * {String} A string to be prepended to the current pointers coordinates + * when it is rendered. Defaults to the empty string ''. + */ + prefix: '', + + /** + * APIProperty: separator + * {String} A string to be used to seperate the two coordinates from each + * other. Defaults to the string ', ', which will result in a + * rendered coordinate of e.g. '42.12, 21.22'. + */ + separator: ', ', + + /** + * APIProperty: suffix + * {String} A string to be appended to the current pointers coordinates + * when it is rendered. Defaults to the empty string ''. + */ + suffix: '', + + /** + * APIProperty: numDigits + * {Integer} The number of digits each coordinate shall have when being + * rendered, Defaults to 5. + */ + numDigits: 5, + + /** + * APIProperty: granularity + * {Integer} + */ + granularity: 10, + + /** + * APIProperty: emptyString + * {String} Set this to some value to set when the mouse is outside the + * map. + */ + emptyString: null, + + /** + * Property: lastXy + * {} + */ + lastXy: null, + + /** + * APIProperty: displayProjection + * {} The projection in which the mouse position is + * displayed. + */ + displayProjection: null, + + /** + * Constructor: OpenLayers.Control.MousePosition + * + * Parameters: + * options - {Object} Options for control. + */ + + /** + * Method: destroy + */ + destroy: function() { + this.deactivate(); + OpenLayers.Control.prototype.destroy.apply(this, arguments); + }, + + /** + * APIMethod: activate + */ + activate: function() { + if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { + this.map.events.register('mousemove', this, this.redraw); + this.map.events.register('mouseout', this, this.reset); + this.redraw(); + return true; + } else { + return false; + } + }, + + /** + * APIMethod: deactivate + */ + deactivate: function() { + if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { + this.map.events.unregister('mousemove', this, this.redraw); + this.map.events.unregister('mouseout', this, this.reset); + this.element.innerHTML = ""; + return true; + } else { + return false; + } + }, + + /** + * Method: draw + * {DOMElement} + */ + draw: function() { + OpenLayers.Control.prototype.draw.apply(this, arguments); + + if (!this.element) { + this.div.left = ""; + this.div.top = ""; + this.element = this.div; + } + + return this.div; + }, + + /** + * Method: redraw + */ + redraw: function(evt) { + + var lonLat; + + if (evt == null) { + this.reset(); + return; + } else { + if (this.lastXy == null || + Math.abs(evt.xy.x - this.lastXy.x) > this.granularity || + Math.abs(evt.xy.y - this.lastXy.y) > this.granularity) + { + this.lastXy = evt.xy; + return; + } + + lonLat = this.map.getLonLatFromPixel(evt.xy); + if (!lonLat) { + // map has not yet been properly initialized + return; + } + if (this.displayProjection) { + lonLat.transform(this.map.getProjectionObject(), + this.displayProjection ); + } + this.lastXy = evt.xy; + + } + + var newHtml = this.formatOutput(lonLat); + + if (newHtml != this.element.innerHTML) { + this.element.innerHTML = newHtml; + } + }, + + /** + * Method: reset + */ + reset: function(evt) { + if (this.emptyString != null) { + this.element.innerHTML = this.emptyString; + } + }, + + /** + * Method: formatOutput + * Override to provide custom display output + * + * Parameters: + * lonLat - {} Location to display + */ + formatOutput: function(lonLat) { + var digits = parseInt(this.numDigits); + var newHtml = + this.prefix + + lonLat.lon.toFixed(digits) + + this.separator + + lonLat.lat.toFixed(digits) + + this.suffix; + return newHtml; + }, + + CLASS_NAME: "OpenLayers.Control.MousePosition" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/NavToolbar.js b/misc/openlayers/lib/OpenLayers/Control/NavToolbar.js new file mode 100644 index 0000000..b6bc2aa --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/NavToolbar.js @@ -0,0 +1,57 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control/Panel.js + * @requires OpenLayers/Control/Navigation.js + * @requires OpenLayers/Control/ZoomBox.js + */ + +/** + * Class: OpenLayers.Control.NavToolbar + * This Toolbar is an alternative to the Navigation control that displays + * the state of the control, and provides a UI for changing state to + * use the zoomBox via a Panel control. + * + * If you wish to change the properties of the Navigation control used + * in the NavToolbar, see: + * http://trac.openlayers.org/wiki/Toolbars#SubclassingNavToolbar + * + * + * Inherits from: + * - + */ +OpenLayers.Control.NavToolbar = OpenLayers.Class(OpenLayers.Control.Panel, { + + /** + * Constructor: OpenLayers.Control.NavToolbar + * Add our two mousedefaults controls. + * + * Parameters: + * options - {Object} An optional object whose properties will be used + * to extend the control. + */ + initialize: function(options) { + OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); + this.addControls([ + new OpenLayers.Control.Navigation(), + new OpenLayers.Control.ZoomBox() + ]); + }, + + /** + * Method: draw + * calls the default draw, and then activates mouse defaults. + */ + draw: function() { + var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments); + if (this.defaultControl === null) { + this.defaultControl = this.controls[0]; + } + return div; + }, + + CLASS_NAME: "OpenLayers.Control.NavToolbar" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/Navigation.js b/misc/openlayers/lib/OpenLayers/Control/Navigation.js new file mode 100644 index 0000000..d50e131 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/Navigation.js @@ -0,0 +1,345 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control/ZoomBox.js + * @requires OpenLayers/Control/DragPan.js + * @requires OpenLayers/Handler/MouseWheel.js + * @requires OpenLayers/Handler/Click.js + */ + +/** + * Class: OpenLayers.Control.Navigation + * The navigation control handles map browsing with mouse events (dragging, + * double-clicking, and scrolling the wheel). Create a new navigation + * control with the control. + * + * Note that this control is added to the map by default (if no controls + * array is sent in the options object to the + * constructor). + * + * Inherits: + * - + */ +OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, { + + /** + * Property: dragPan + * {} + */ + dragPan: null, + + /** + * APIProperty: dragPanOptions + * {Object} Options passed to the DragPan control. + */ + dragPanOptions: null, + + /** + * Property: pinchZoom + * {} + */ + pinchZoom: null, + + /** + * APIProperty: pinchZoomOptions + * {Object} Options passed to the PinchZoom control. + */ + pinchZoomOptions: null, + + /** + * APIProperty: documentDrag + * {Boolean} Allow panning of the map by dragging outside map viewport. + * Default is false. + */ + documentDrag: false, + + /** + * Property: zoomBox + * {} + */ + zoomBox: null, + + /** + * APIProperty: zoomBoxEnabled + * {Boolean} Whether the user can draw a box to zoom + */ + zoomBoxEnabled: true, + + /** + * APIProperty: zoomWheelEnabled + * {Boolean} Whether the mousewheel should zoom the map + */ + zoomWheelEnabled: true, + + /** + * Property: mouseWheelOptions + * {Object} Options passed to the MouseWheel control (only useful if + * is set to true). Default is no options for maps + * with fractionalZoom set to true, otherwise + * {cumulative: false, interval: 50, maxDelta: 6} + */ + mouseWheelOptions: null, + + /** + * APIProperty: handleRightClicks + * {Boolean} Whether or not to handle right clicks. Default is false. + */ + handleRightClicks: false, + + /** + * APIProperty: zoomBoxKeyMask + * {Integer} key code of the key, which has to be + * pressed, while drawing the zoom box with the mouse on the screen. + * You should probably set handleRightClicks to true if you use this + * with MOD_CTRL, to disable the context menu for machines which use + * CTRL-Click as a right click. + * Default: + */ + zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT, + + /** + * APIProperty: autoActivate + * {Boolean} Activate the control when it is added to a map. Default is + * true. + */ + autoActivate: true, + + /** + * Constructor: OpenLayers.Control.Navigation + * Create a new navigation control + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * the control + */ + initialize: function(options) { + this.handlers = {}; + OpenLayers.Control.prototype.initialize.apply(this, arguments); + }, + + /** + * Method: destroy + * The destroy method is used to perform any clean up before the control + * is dereferenced. Typically this is where event listeners are removed + * to prevent memory leaks. + */ + destroy: function() { + this.deactivate(); + + if (this.dragPan) { + this.dragPan.destroy(); + } + this.dragPan = null; + + if (this.zoomBox) { + this.zoomBox.destroy(); + } + this.zoomBox = null; + + if (this.pinchZoom) { + this.pinchZoom.destroy(); + } + this.pinchZoom = null; + + OpenLayers.Control.prototype.destroy.apply(this,arguments); + }, + + /** + * Method: activate + */ + activate: function() { + this.dragPan.activate(); + if (this.zoomWheelEnabled) { + this.handlers.wheel.activate(); + } + this.handlers.click.activate(); + if (this.zoomBoxEnabled) { + this.zoomBox.activate(); + } + if (this.pinchZoom) { + this.pinchZoom.activate(); + } + return OpenLayers.Control.prototype.activate.apply(this,arguments); + }, + + /** + * Method: deactivate + */ + deactivate: function() { + if (this.pinchZoom) { + this.pinchZoom.deactivate(); + } + this.zoomBox.deactivate(); + this.dragPan.deactivate(); + this.handlers.click.deactivate(); + this.handlers.wheel.deactivate(); + return OpenLayers.Control.prototype.deactivate.apply(this,arguments); + }, + + /** + * Method: draw + */ + draw: function() { + // disable right mouse context menu for support of right click events + if (this.handleRightClicks) { + this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False; + } + + var clickCallbacks = { + 'click': this.defaultClick, + 'dblclick': this.defaultDblClick, + 'dblrightclick': this.defaultDblRightClick + }; + var clickOptions = { + 'double': true, + 'stopDouble': true + }; + this.handlers.click = new OpenLayers.Handler.Click( + this, clickCallbacks, clickOptions + ); + this.dragPan = new OpenLayers.Control.DragPan( + OpenLayers.Util.extend({ + map: this.map, + documentDrag: this.documentDrag + }, this.dragPanOptions) + ); + this.zoomBox = new OpenLayers.Control.ZoomBox( + {map: this.map, keyMask: this.zoomBoxKeyMask}); + this.dragPan.draw(); + this.zoomBox.draw(); + var wheelOptions = this.map.fractionalZoom ? {} : { + cumulative: false, + interval: 50, + maxDelta: 6 + }; + this.handlers.wheel = new OpenLayers.Handler.MouseWheel( + this, {up : this.wheelUp, down: this.wheelDown}, + OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions) + ); + if (OpenLayers.Control.PinchZoom) { + this.pinchZoom = new OpenLayers.Control.PinchZoom( + OpenLayers.Util.extend( + {map: this.map}, this.pinchZoomOptions)); + } + }, + + /** + * Method: defaultClick + * + * Parameters: + * evt - {Event} + */ + defaultClick: function (evt) { + if (evt.lastTouches && evt.lastTouches.length == 2) { + this.map.zoomOut(); + } + }, + + /** + * Method: defaultDblClick + * + * Parameters: + * evt - {Event} + */ + defaultDblClick: function (evt) { + this.map.zoomTo(this.map.zoom + 1, evt.xy); + }, + + /** + * Method: defaultDblRightClick + * + * Parameters: + * evt - {Event} + */ + defaultDblRightClick: function (evt) { + this.map.zoomTo(this.map.zoom - 1, evt.xy); + }, + + /** + * Method: wheelChange + * + * Parameters: + * evt - {Event} + * deltaZ - {Integer} + */ + wheelChange: function(evt, deltaZ) { + if (!this.map.fractionalZoom) { + deltaZ = Math.round(deltaZ); + } + var currentZoom = this.map.getZoom(), + newZoom = currentZoom + deltaZ; + newZoom = Math.max(newZoom, 0); + newZoom = Math.min(newZoom, this.map.getNumZoomLevels()); + if (newZoom === currentZoom) { + return; + } + this.map.zoomTo(newZoom, evt.xy); + }, + + /** + * Method: wheelUp + * User spun scroll wheel up + * + * Parameters: + * evt - {Event} + * delta - {Integer} + */ + wheelUp: function(evt, delta) { + this.wheelChange(evt, delta || 1); + }, + + /** + * Method: wheelDown + * User spun scroll wheel down + * + * Parameters: + * evt - {Event} + * delta - {Integer} + */ + wheelDown: function(evt, delta) { + this.wheelChange(evt, delta || -1); + }, + + /** + * Method: disableZoomBox + */ + disableZoomBox : function() { + this.zoomBoxEnabled = false; + this.zoomBox.deactivate(); + }, + + /** + * Method: enableZoomBox + */ + enableZoomBox : function() { + this.zoomBoxEnabled = true; + if (this.active) { + this.zoomBox.activate(); + } + }, + + /** + * Method: disableZoomWheel + */ + + disableZoomWheel : function() { + this.zoomWheelEnabled = false; + this.handlers.wheel.deactivate(); + }, + + /** + * Method: enableZoomWheel + */ + + enableZoomWheel : function() { + this.zoomWheelEnabled = true; + if (this.active) { + this.handlers.wheel.activate(); + } + }, + + CLASS_NAME: "OpenLayers.Control.Navigation" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/NavigationHistory.js b/misc/openlayers/lib/OpenLayers/Control/NavigationHistory.js new file mode 100644 index 0000000..bf2f95a --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/NavigationHistory.js @@ -0,0 +1,423 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Control/Button.js + */ + +/** + * Class: OpenLayers.Control.NavigationHistory + * A navigation history control. This is a meta-control, that creates two + * dependent controls: and . Call the trigger method + * on the and controls to restore previous and next + * history states. The previous and next controls will become active + * when there are available states to restore and will become deactive + * when there are no states to restore. + * + * Inherits from: + * - + */ +OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, { + + /** + * Property: type + * {String} Note that this control is not intended to be added directly + * to a control panel. Instead, add the sub-controls previous and + * next. These sub-controls are button type controls that activate + * and deactivate themselves. If this parent control is added to + * a panel, it will act as a toggle. + */ + type: OpenLayers.Control.TYPE_TOGGLE, + + /** + * APIProperty: previous + * {} A button type control whose trigger method restores + * the previous state managed by this control. + */ + previous: null, + + /** + * APIProperty: previousOptions + * {Object} Set this property on the options argument of the constructor + * to set optional properties on the control. + */ + previousOptions: null, + + /** + * APIProperty: next + * {} A button type control whose trigger method restores + * the next state managed by this control. + */ + next: null, + + /** + * APIProperty: nextOptions + * {Object} Set this property on the options argument of the constructor + * to set optional properties on the control. + */ + nextOptions: null, + + /** + * APIProperty: limit + * {Integer} Optional limit on the number of history items to retain. If + * null, there is no limit. Default is 50. + */ + limit: 50, + + /** + * APIProperty: autoActivate + * {Boolean} Activate the control when it is added to a map. Default is + * true. + */ + autoActivate: true, + + /** + * Property: clearOnDeactivate + * {Boolean} Clear the history when the control is deactivated. Default + * is false. + */ + clearOnDeactivate: false, + + /** + * Property: registry + * {Object} An object with keys corresponding to event types. Values + * are functions that return an object representing the current state. + */ + registry: null, + + /** + * Property: nextStack + * {Array} Array of items in the history. + */ + nextStack: null, + + /** + * Property: previousStack + * {Array} List of items in the history. First item represents the current + * state. + */ + previousStack: null, + + /** + * Property: listeners + * {Object} An object containing properties corresponding to event types. + * This object is used to configure the control and is modified on + * construction. + */ + listeners: null, + + /** + * Property: restoring + * {Boolean} Currently restoring a history state. This is set to true + * before calling restore and set to false after restore returns. + */ + restoring: false, + + /** + * Constructor: OpenLayers.Control.NavigationHistory + * + * Parameters: + * options - {Object} An optional object whose properties will be used + * to extend the control. + */ + initialize: function(options) { + OpenLayers.Control.prototype.initialize.apply(this, [options]); + + this.registry = OpenLayers.Util.extend({ + "moveend": this.getState + }, this.registry); + + var previousOptions = { + trigger: OpenLayers.Function.bind(this.previousTrigger, this), + displayClass: this.displayClass + " " + this.displayClass + "Previous" + }; + OpenLayers.Util.extend(previousOptions, this.previousOptions); + this.previous = new OpenLayers.Control.Button(previousOptions); + + var nextOptions = { + trigger: OpenLayers.Function.bind(this.nextTrigger, this), + displayClass: this.displayClass + " " + this.displayClass + "Next" + }; + OpenLayers.Util.extend(nextOptions, this.nextOptions); + this.next = new OpenLayers.Control.Button(nextOptions); + + this.clear(); + }, + + /** + * Method: onPreviousChange + * Called when the previous history stack changes. + * + * Parameters: + * state - {Object} An object representing the state to be restored + * if previous is triggered again or null if no previous states remain. + * length - {Integer} The number of remaining previous states that can + * be restored. + */ + onPreviousChange: function(state, length) { + if(state && !this.previous.active) { + this.previous.activate(); + } else if(!state && this.previous.active) { + this.previous.deactivate(); + } + }, + + /** + * Method: onNextChange + * Called when the next history stack changes. + * + * Parameters: + * state - {Object} An object representing the state to be restored + * if next is triggered again or null if no next states remain. + * length - {Integer} The number of remaining next states that can + * be restored. + */ + onNextChange: function(state, length) { + if(state && !this.next.active) { + this.next.activate(); + } else if(!state && this.next.active) { + this.next.deactivate(); + } + }, + + /** + * APIMethod: destroy + * Destroy the control. + */ + destroy: function() { + OpenLayers.Control.prototype.destroy.apply(this); + this.previous.destroy(); + this.next.destroy(); + this.deactivate(); + for(var prop in this) { + this[prop] = null; + } + }, + + /** + * Method: setMap + * Set the map property for the control and and child + * controls. + * + * Parameters: + * map - {} + */ + setMap: function(map) { + this.map = map; + this.next.setMap(map); + this.previous.setMap(map); + }, + + /** + * Method: draw + * Called when the control is added to the map. + */ + draw: function() { + OpenLayers.Control.prototype.draw.apply(this, arguments); + this.next.draw(); + this.previous.draw(); + }, + + /** + * Method: previousTrigger + * Restore the previous state. If no items are in the previous history + * stack, this has no effect. + * + * Returns: + * {Object} Item representing state that was restored. Undefined if no + * items are in the previous history stack. + */ + previousTrigger: function() { + var current = this.previousStack.shift(); + var state = this.previousStack.shift(); + if(state != undefined) { + this.nextStack.unshift(current); + this.previousStack.unshift(state); + this.restoring = true; + this.restore(state); + this.restoring = false; + this.onNextChange(this.nextStack[0], this.nextStack.length); + this.onPreviousChange( + this.previousStack[1], this.previousStack.length - 1 + ); + } else { + this.previousStack.unshift(current); + } + return state; + }, + + /** + * APIMethod: nextTrigger + * Restore the next state. If no items are in the next history + * stack, this has no effect. The next history stack is populated + * as states are restored from the previous history stack. + * + * Returns: + * {Object} Item representing state that was restored. Undefined if no + * items are in the next history stack. + */ + nextTrigger: function() { + var state = this.nextStack.shift(); + if(state != undefined) { + this.previousStack.unshift(state); + this.restoring = true; + this.restore(state); + this.restoring = false; + this.onNextChange(this.nextStack[0], this.nextStack.length); + this.onPreviousChange( + this.previousStack[1], this.previousStack.length - 1 + ); + } + return state; + }, + + /** + * APIMethod: clear + * Clear history. + */ + clear: function() { + this.previousStack = []; + this.previous.deactivate(); + this.nextStack = []; + this.next.deactivate(); + }, + + /** + * Method: getState + * Get the current state and return it. + * + * Returns: + * {Object} An object representing the current state. + */ + getState: function() { + return { + center: this.map.getCenter(), + resolution: this.map.getResolution(), + projection: this.map.getProjectionObject(), + units: this.map.getProjectionObject().getUnits() || + this.map.units || this.map.baseLayer.units + }; + }, + + /** + * Method: restore + * Update the state with the given object. + * + * Parameters: + * state - {Object} An object representing the state to restore. + */ + restore: function(state) { + var center, zoom; + if (this.map.getProjectionObject() == state.projection) { + zoom = this.map.getZoomForResolution(state.resolution); + center = state.center; + } else { + center = state.center.clone(); + center.transform(state.projection, this.map.getProjectionObject()); + var sourceUnits = state.units; + var targetUnits = this.map.getProjectionObject().getUnits() || + this.map.units || this.map.baseLayer.units; + var resolutionFactor = sourceUnits && targetUnits ? + OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1; + zoom = this.map.getZoomForResolution(resolutionFactor*state.resolution); + } + this.map.setCenter(center, zoom); + }, + + /** + * Method: setListeners + * Sets functions to be registered in the listeners object. + */ + setListeners: function() { + this.listeners = {}; + for(var type in this.registry) { + this.listeners[type] = OpenLayers.Function.bind(function() { + if(!this.restoring) { + var state = this.registry[type].apply(this, arguments); + this.previousStack.unshift(state); + if(this.previousStack.length > 1) { + this.onPreviousChange( + this.previousStack[1], this.previousStack.length - 1 + ); + } + if(this.previousStack.length > (this.limit + 1)) { + this.previousStack.pop(); + } + if(this.nextStack.length > 0) { + this.nextStack = []; + this.onNextChange(null, 0); + } + } + return true; + }, this); + } + }, + + /** + * APIMethod: activate + * Activate the control. This registers any listeners. + * + * Returns: + * {Boolean} Control successfully activated. + */ + activate: function() { + var activated = false; + if(this.map) { + if(OpenLayers.Control.prototype.activate.apply(this)) { + if(this.listeners == null) { + this.setListeners(); + } + for(var type in this.listeners) { + this.map.events.register(type, this, this.listeners[type]); + } + activated = true; + if(this.previousStack.length == 0) { + this.initStack(); + } + } + } + return activated; + }, + + /** + * Method: initStack + * Called after the control is activated if the previous history stack is + * empty. + */ + initStack: function() { + if(this.map.getCenter()) { + this.listeners.moveend(); + } + }, + + /** + * APIMethod: deactivate + * Deactivate the control. This unregisters any listeners. + * + * Returns: + * {Boolean} Control successfully deactivated. + */ + deactivate: function() { + var deactivated = false; + if(this.map) { + if(OpenLayers.Control.prototype.deactivate.apply(this)) { + for(var type in this.listeners) { + this.map.events.unregister( + type, this, this.listeners[type] + ); + } + if(this.clearOnDeactivate) { + this.clear(); + } + deactivated = true; + } + } + return deactivated; + }, + + CLASS_NAME: "OpenLayers.Control.NavigationHistory" +}); + diff --git a/misc/openlayers/lib/OpenLayers/Control/OverviewMap.js b/misc/openlayers/lib/OpenLayers/Control/OverviewMap.js new file mode 100644 index 0000000..50b9300 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/OverviewMap.js @@ -0,0 +1,750 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/BaseTypes.js + * @requires OpenLayers/Events/buttonclick.js + * @requires OpenLayers/Map.js + * @requires OpenLayers/Handler/Click.js + * @requires OpenLayers/Handler/Drag.js + */ + +/** + * Class: OpenLayers.Control.OverviewMap + * The OverMap control creates a small overview map, useful to display the + * extent of a zoomed map and your main map and provide additional + * navigation options to the User. By default the overview map is drawn in + * the lower right corner of the main map. Create a new overview map with the + * constructor. + * + * Inherits from: + * - + */ +OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, { + + /** + * Property: element + * {DOMElement} The DOM element that contains the overview map + */ + element: null, + + /** + * APIProperty: ovmap + * {} A reference to the overview map itself. + */ + ovmap: null, + + /** + * APIProperty: size + * {} The overvew map size in pixels. Note that this is + * the size of the map itself - the element that contains the map (default + * class name olControlOverviewMapElement) may have padding or other style + * attributes added via CSS. + */ + size: {w: 180, h: 90}, + + /** + * APIProperty: layers + * {Array()} Ordered list of layers in the overview map. + * If none are sent at construction, the base layer for the main map is used. + */ + layers: null, + + /** + * APIProperty: minRectSize + * {Integer} The minimum width or height (in pixels) of the extent + * rectangle on the overview map. When the extent rectangle reaches + * this size, it will be replaced depending on the value of the + * property. Default is 15 pixels. + */ + minRectSize: 15, + + /** + * APIProperty: minRectDisplayClass + * {String} Replacement style class name for the extent rectangle when + * is reached. This string will be suffixed on to the + * displayClass. Default is "RectReplacement". + * + * Example CSS declaration: + * (code) + * .olControlOverviewMapRectReplacement { + * overflow: hidden; + * cursor: move; + * background-image: url("img/overview_replacement.gif"); + * background-repeat: no-repeat; + * background-position: center; + * } + * (end) + */ + minRectDisplayClass: "RectReplacement", + + /** + * APIProperty: minRatio + * {Float} The ratio of the overview map resolution to the main map + * resolution at which to zoom farther out on the overview map. + */ + minRatio: 8, + + /** + * APIProperty: maxRatio + * {Float} The ratio of the overview map resolution to the main map + * resolution at which to zoom farther in on the overview map. + */ + maxRatio: 32, + + /** + * APIProperty: mapOptions + * {Object} An object containing any non-default properties to be sent to + * the overview map's map constructor. These should include any + * non-default options that the main map was constructed with. + */ + mapOptions: null, + + /** + * APIProperty: autoPan + * {Boolean} Always pan the overview map, so the extent marker remains in + * the center. Default is false. If true, when you drag the extent + * marker, the overview map will update itself so the marker returns + * to the center. + */ + autoPan: false, + + /** + * Property: handlers + * {Object} + */ + handlers: null, + + /** + * Property: resolutionFactor + * {Object} + */ + resolutionFactor: 1, + + /** + * APIProperty: maximized + * {Boolean} Start as maximized (visible). Defaults to false. + */ + maximized: false, + + /** + * APIProperty: maximizeTitle + * {String} This property is used for showing a tooltip over the + * maximize div. Defaults to "" (no title). + */ + maximizeTitle: "", + + /** + * APIProperty: minimizeTitle + * {String} This property is used for showing a tooltip over the + * minimize div. Defaults to "" (no title). + */ + minimizeTitle: "", + + /** + * Constructor: OpenLayers.Control.OverviewMap + * Create a new overview map + * + * Parameters: + * options - {Object} Properties of this object will be set on the overview + * map object. Note, to set options on the map object contained in this + * control, set as one of the options properties. + */ + initialize: function(options) { + this.layers = []; + this.handlers = {}; + OpenLayers.Control.prototype.initialize.apply(this, [options]); + }, + + /** + * APIMethod: destroy + * Deconstruct the control + */ + destroy: function() { + if (!this.mapDiv) { // we've already been destroyed + return; + } + if (this.handlers.click) { + this.handlers.click.destroy(); + } + if (this.handlers.drag) { + this.handlers.drag.destroy(); + } + + this.ovmap && this.ovmap.viewPortDiv.removeChild(this.extentRectangle); + this.extentRectangle = null; + + if (this.rectEvents) { + this.rectEvents.destroy(); + this.rectEvents = null; + } + + if (this.ovmap) { + this.ovmap.destroy(); + this.ovmap = null; + } + + this.element.removeChild(this.mapDiv); + this.mapDiv = null; + + this.div.removeChild(this.element); + this.element = null; + + if (this.maximizeDiv) { + this.div.removeChild(this.maximizeDiv); + this.maximizeDiv = null; + } + + if (this.minimizeDiv) { + this.div.removeChild(this.minimizeDiv); + this.minimizeDiv = null; + } + + this.map.events.un({ + buttonclick: this.onButtonClick, + moveend: this.update, + changebaselayer: this.baseLayerDraw, + scope: this + }); + + OpenLayers.Control.prototype.destroy.apply(this, arguments); + }, + + /** + * Method: draw + * Render the control in the browser. + */ + draw: function() { + OpenLayers.Control.prototype.draw.apply(this, arguments); + if (this.layers.length === 0) { + if (this.map.baseLayer) { + var layer = this.map.baseLayer.clone(); + this.layers = [layer]; + } else { + this.map.events.register("changebaselayer", this, this.baseLayerDraw); + return this.div; + } + } + + // create overview map DOM elements + this.element = document.createElement('div'); + this.element.className = this.displayClass + 'Element'; + this.element.style.display = 'none'; + + this.mapDiv = document.createElement('div'); + this.mapDiv.style.width = this.size.w + 'px'; + this.mapDiv.style.height = this.size.h + 'px'; + this.mapDiv.style.position = 'relative'; + this.mapDiv.style.overflow = 'hidden'; + this.mapDiv.id = OpenLayers.Util.createUniqueID('overviewMap'); + + this.extentRectangle = document.createElement('div'); + this.extentRectangle.style.position = 'absolute'; + this.extentRectangle.style.zIndex = 1000; //HACK + this.extentRectangle.className = this.displayClass+'ExtentRectangle'; + + this.element.appendChild(this.mapDiv); + + this.div.appendChild(this.element); + + // Optionally add min/max buttons if the control will go in the + // map viewport. + if(!this.outsideViewport) { + this.div.className += " " + this.displayClass + 'Container'; + // maximize button div + var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png'); + this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv( + this.displayClass + 'MaximizeButton', + null, + null, + img, + 'absolute'); + this.maximizeDiv.style.display = 'none'; + this.maximizeDiv.className = this.displayClass + 'MaximizeButton olButton'; + if (this.maximizeTitle) { + this.maximizeDiv.title = this.maximizeTitle; + } + this.div.appendChild(this.maximizeDiv); + + // minimize button div + var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png'); + this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv( + 'OpenLayers_Control_minimizeDiv', + null, + null, + img, + 'absolute'); + this.minimizeDiv.style.display = 'none'; + this.minimizeDiv.className = this.displayClass + 'MinimizeButton olButton'; + if (this.minimizeTitle) { + this.minimizeDiv.title = this.minimizeTitle; + } + this.div.appendChild(this.minimizeDiv); + this.minimizeControl(); + } else { + // show the overview map + this.element.style.display = ''; + } + if(this.map.getExtent()) { + this.update(); + } + + this.map.events.on({ + buttonclick: this.onButtonClick, + moveend: this.update, + scope: this + }); + + if (this.maximized) { + this.maximizeControl(); + } + return this.div; + }, + + /** + * Method: baseLayerDraw + * Draw the base layer - called if unable to complete in the initial draw + */ + baseLayerDraw: function() { + this.draw(); + this.map.events.unregister("changebaselayer", this, this.baseLayerDraw); + }, + + /** + * Method: rectDrag + * Handle extent rectangle drag + * + * Parameters: + * px - {} The pixel location of the drag. + */ + rectDrag: function(px) { + var deltaX = this.handlers.drag.last.x - px.x; + var deltaY = this.handlers.drag.last.y - px.y; + if(deltaX != 0 || deltaY != 0) { + var rectTop = this.rectPxBounds.top; + var rectLeft = this.rectPxBounds.left; + var rectHeight = Math.abs(this.rectPxBounds.getHeight()); + var rectWidth = this.rectPxBounds.getWidth(); + // don't allow dragging off of parent element + var newTop = Math.max(0, (rectTop - deltaY)); + newTop = Math.min(newTop, + this.ovmap.size.h - this.hComp - rectHeight); + var newLeft = Math.max(0, (rectLeft - deltaX)); + newLeft = Math.min(newLeft, + this.ovmap.size.w - this.wComp - rectWidth); + this.setRectPxBounds(new OpenLayers.Bounds(newLeft, + newTop + rectHeight, + newLeft + rectWidth, + newTop)); + } + }, + + /** + * Method: mapDivClick + * Handle browser events + * + * Parameters: + * evt - {} evt + */ + mapDivClick: function(evt) { + var pxCenter = this.rectPxBounds.getCenterPixel(); + var deltaX = evt.xy.x - pxCenter.x; + var deltaY = evt.xy.y - pxCenter.y; + var top = this.rectPxBounds.top; + var left = this.rectPxBounds.left; + var height = Math.abs(this.rectPxBounds.getHeight()); + var width = this.rectPxBounds.getWidth(); + var newTop = Math.max(0, (top + deltaY)); + newTop = Math.min(newTop, this.ovmap.size.h - height); + var newLeft = Math.max(0, (left + deltaX)); + newLeft = Math.min(newLeft, this.ovmap.size.w - width); + this.setRectPxBounds(new OpenLayers.Bounds(newLeft, + newTop + height, + newLeft + width, + newTop)); + this.updateMapToRect(); + }, + + /** + * Method: onButtonClick + * + * Parameters: + * evt - {Event} + */ + onButtonClick: function(evt) { + if (evt.buttonElement === this.minimizeDiv) { + this.minimizeControl(); + } else if (evt.buttonElement === this.maximizeDiv) { + this.maximizeControl(); + } + }, + + /** + * Method: maximizeControl + * Unhide the control. Called when the control is in the map viewport. + * + * Parameters: + * e - {} + */ + maximizeControl: function(e) { + this.element.style.display = ''; + this.showToggle(false); + if (e != null) { + OpenLayers.Event.stop(e); + } + }, + + /** + * Method: minimizeControl + * Hide all the contents of the control, shrink the size, + * add the maximize icon + * + * Parameters: + * e - {} + */ + minimizeControl: function(e) { + this.element.style.display = 'none'; + this.showToggle(true); + if (e != null) { + OpenLayers.Event.stop(e); + } + }, + + /** + * Method: showToggle + * Hide/Show the toggle depending on whether the control is minimized + * + * Parameters: + * minimize - {Boolean} + */ + showToggle: function(minimize) { + if (this.maximizeDiv) { + this.maximizeDiv.style.display = minimize ? '' : 'none'; + } + if (this.minimizeDiv) { + this.minimizeDiv.style.display = minimize ? 'none' : ''; + } + }, + + /** + * Method: update + * Update the overview map after layers move. + */ + update: function() { + if(this.ovmap == null) { + this.createMap(); + } + + if(this.autoPan || !this.isSuitableOverview()) { + this.updateOverview(); + } + + // update extent rectangle + this.updateRectToMap(); + }, + + /** + * Method: isSuitableOverview + * Determines if the overview map is suitable given the extent and + * resolution of the main map. + */ + isSuitableOverview: function() { + var mapExtent = this.map.getExtent(); + var maxExtent = this.map.getMaxExtent(); + var testExtent = new OpenLayers.Bounds( + Math.max(mapExtent.left, maxExtent.left), + Math.max(mapExtent.bottom, maxExtent.bottom), + Math.min(mapExtent.right, maxExtent.right), + Math.min(mapExtent.top, maxExtent.top)); + + if (this.ovmap.getProjection() != this.map.getProjection()) { + testExtent = testExtent.transform( + this.map.getProjectionObject(), + this.ovmap.getProjectionObject() ); + } + + var resRatio = this.ovmap.getResolution() / this.map.getResolution(); + return ((resRatio > this.minRatio) && + (resRatio <= this.maxRatio) && + (this.ovmap.getExtent().containsBounds(testExtent))); + }, + + /** + * Method updateOverview + * Called by if returns true + */ + updateOverview: function() { + var mapRes = this.map.getResolution(); + var targetRes = this.ovmap.getResolution(); + var resRatio = targetRes / mapRes; + if(resRatio > this.maxRatio) { + // zoom in overview map + targetRes = this.minRatio * mapRes; + } else if(resRatio <= this.minRatio) { + // zoom out overview map + targetRes = this.maxRatio * mapRes; + } + var center; + if (this.ovmap.getProjection() != this.map.getProjection()) { + center = this.map.center.clone(); + center.transform(this.map.getProjectionObject(), + this.ovmap.getProjectionObject() ); + } else { + center = this.map.center; + } + this.ovmap.setCenter(center, this.ovmap.getZoomForResolution( + targetRes * this.resolutionFactor)); + this.updateRectToMap(); + }, + + /** + * Method: createMap + * Construct the map that this control contains + */ + createMap: function() { + // create the overview map + var options = OpenLayers.Util.extend( + {controls: [], maxResolution: 'auto', + fallThrough: false}, this.mapOptions); + this.ovmap = new OpenLayers.Map(this.mapDiv, options); + this.ovmap.viewPortDiv.appendChild(this.extentRectangle); + + // prevent ovmap from being destroyed when the page unloads, because + // the OverviewMap control has to do this (and does it). + OpenLayers.Event.stopObserving(window, 'unload', this.ovmap.unloadDestroy); + + this.ovmap.addLayers(this.layers); + this.ovmap.zoomToMaxExtent(); + // check extent rectangle border width + this.wComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle, + 'border-left-width')) + + parseInt(OpenLayers.Element.getStyle(this.extentRectangle, + 'border-right-width')); + this.wComp = (this.wComp) ? this.wComp : 2; + this.hComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle, + 'border-top-width')) + + parseInt(OpenLayers.Element.getStyle(this.extentRectangle, + 'border-bottom-width')); + this.hComp = (this.hComp) ? this.hComp : 2; + + this.handlers.drag = new OpenLayers.Handler.Drag( + this, {move: this.rectDrag, done: this.updateMapToRect}, + {map: this.ovmap} + ); + this.handlers.click = new OpenLayers.Handler.Click( + this, { + "click": this.mapDivClick + },{ + "single": true, "double": false, + "stopSingle": true, "stopDouble": true, + "pixelTolerance": 1, + map: this.ovmap + } + ); + this.handlers.click.activate(); + + this.rectEvents = new OpenLayers.Events(this, this.extentRectangle, + null, true); + this.rectEvents.register("mouseover", this, function(e) { + if(!this.handlers.drag.active && !this.map.dragging) { + this.handlers.drag.activate(); + } + }); + this.rectEvents.register("mouseout", this, function(e) { + if(!this.handlers.drag.dragging) { + this.handlers.drag.deactivate(); + } + }); + + if (this.ovmap.getProjection() != this.map.getProjection()) { + var sourceUnits = this.map.getProjectionObject().getUnits() || + this.map.units || this.map.baseLayer.units; + var targetUnits = this.ovmap.getProjectionObject().getUnits() || + this.ovmap.units || this.ovmap.baseLayer.units; + this.resolutionFactor = sourceUnits && targetUnits ? + OpenLayers.INCHES_PER_UNIT[sourceUnits] / + OpenLayers.INCHES_PER_UNIT[targetUnits] : 1; + } + }, + + /** + * Method: updateRectToMap + * Updates the extent rectangle position and size to match the map extent + */ + updateRectToMap: function() { + // If the projections differ we need to reproject + var bounds; + if (this.ovmap.getProjection() != this.map.getProjection()) { + bounds = this.map.getExtent().transform( + this.map.getProjectionObject(), + this.ovmap.getProjectionObject() ); + } else { + bounds = this.map.getExtent(); + } + var pxBounds = this.getRectBoundsFromMapBounds(bounds); + if (pxBounds) { + this.setRectPxBounds(pxBounds); + } + }, + + /** + * Method: updateMapToRect + * Updates the map extent to match the extent rectangle position and size + */ + updateMapToRect: function() { + var lonLatBounds = this.getMapBoundsFromRectBounds(this.rectPxBounds); + if (this.ovmap.getProjection() != this.map.getProjection()) { + lonLatBounds = lonLatBounds.transform( + this.ovmap.getProjectionObject(), + this.map.getProjectionObject() ); + } + this.map.panTo(lonLatBounds.getCenterLonLat()); + }, + + /** + * Method: setRectPxBounds + * Set extent rectangle pixel bounds. + * + * Parameters: + * pxBounds - {} + */ + setRectPxBounds: function(pxBounds) { + var top = Math.max(pxBounds.top, 0); + var left = Math.max(pxBounds.left, 0); + var bottom = Math.min(pxBounds.top + Math.abs(pxBounds.getHeight()), + this.ovmap.size.h - this.hComp); + var right = Math.min(pxBounds.left + pxBounds.getWidth(), + this.ovmap.size.w - this.wComp); + var width = Math.max(right - left, 0); + var height = Math.max(bottom - top, 0); + if(width < this.minRectSize || height < this.minRectSize) { + this.extentRectangle.className = this.displayClass + + this.minRectDisplayClass; + var rLeft = left + (width / 2) - (this.minRectSize / 2); + var rTop = top + (height / 2) - (this.minRectSize / 2); + this.extentRectangle.style.top = Math.round(rTop) + 'px'; + this.extentRectangle.style.left = Math.round(rLeft) + 'px'; + this.extentRectangle.style.height = this.minRectSize + 'px'; + this.extentRectangle.style.width = this.minRectSize + 'px'; + } else { + this.extentRectangle.className = this.displayClass + + 'ExtentRectangle'; + this.extentRectangle.style.top = Math.round(top) + 'px'; + this.extentRectangle.style.left = Math.round(left) + 'px'; + this.extentRectangle.style.height = Math.round(height) + 'px'; + this.extentRectangle.style.width = Math.round(width) + 'px'; + } + this.rectPxBounds = new OpenLayers.Bounds( + Math.round(left), Math.round(bottom), + Math.round(right), Math.round(top) + ); + }, + + /** + * Method: getRectBoundsFromMapBounds + * Get the rect bounds from the map bounds. + * + * Parameters: + * lonLatBounds - {} + * + * Returns: + * {}A bounds which is the passed-in map lon/lat extent + * translated into pixel bounds for the overview map + */ + getRectBoundsFromMapBounds: function(lonLatBounds) { + var leftBottomPx = this.getOverviewPxFromLonLat({ + lon: lonLatBounds.left, + lat: lonLatBounds.bottom + }); + var rightTopPx = this.getOverviewPxFromLonLat({ + lon: lonLatBounds.right, + lat: lonLatBounds.top + }); + var bounds = null; + if (leftBottomPx && rightTopPx) { + bounds = new OpenLayers.Bounds(leftBottomPx.x, leftBottomPx.y, + rightTopPx.x, rightTopPx.y); + } + return bounds; + }, + + /** + * Method: getMapBoundsFromRectBounds + * Get the map bounds from the rect bounds. + * + * Parameters: + * pxBounds - {} + * + * Returns: + * {} Bounds which is the passed-in overview rect bounds + * translated into lon/lat bounds for the overview map + */ + getMapBoundsFromRectBounds: function(pxBounds) { + var leftBottomLonLat = this.getLonLatFromOverviewPx({ + x: pxBounds.left, + y: pxBounds.bottom + }); + var rightTopLonLat = this.getLonLatFromOverviewPx({ + x: pxBounds.right, + y: pxBounds.top + }); + return new OpenLayers.Bounds(leftBottomLonLat.lon, leftBottomLonLat.lat, + rightTopLonLat.lon, rightTopLonLat.lat); + }, + + /** + * Method: getLonLatFromOverviewPx + * Get a map location from a pixel location + * + * Parameters: + * overviewMapPx - {|Object} OpenLayers.Pixel or + * an object with a + * 'x' and 'y' properties. + * + * Returns: + * {Object} Location which is the passed-in overview map + * OpenLayers.Pixel, translated into lon/lat by the overview + * map. An object with a 'lon' and 'lat' properties. + */ + getLonLatFromOverviewPx: function(overviewMapPx) { + var size = this.ovmap.size; + var res = this.ovmap.getResolution(); + var center = this.ovmap.getExtent().getCenterLonLat(); + + var deltaX = overviewMapPx.x - (size.w / 2); + var deltaY = overviewMapPx.y - (size.h / 2); + + return { + lon: center.lon + deltaX * res, + lat: center.lat - deltaY * res + }; + }, + + /** + * Method: getOverviewPxFromLonLat + * Get a pixel location from a map location + * + * Parameters: + * lonlat - {|Object} OpenLayers.LonLat or an + * object with a 'lon' and 'lat' properties. + * + * Returns: + * {Object} Location which is the passed-in OpenLayers.LonLat, + * translated into overview map pixels + */ + getOverviewPxFromLonLat: function(lonlat) { + var res = this.ovmap.getResolution(); + var extent = this.ovmap.getExtent(); + if (extent) { + return { + x: Math.round(1/res * (lonlat.lon - extent.left)), + y: Math.round(1/res * (extent.top - lonlat.lat)) + }; + } + }, + + CLASS_NAME: 'OpenLayers.Control.OverviewMap' +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/Pan.js b/misc/openlayers/lib/OpenLayers/Control/Pan.js new file mode 100644 index 0000000..d7fcc07 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/Pan.js @@ -0,0 +1,95 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control/Button.js + */ + +/** + * Class: OpenLayers.Control.Pan + * The Pan control is a single button to pan the map in one direction. For + * a more complete control see . + * + * Inherits from: + * - + */ +OpenLayers.Control.Pan = OpenLayers.Class(OpenLayers.Control.Button, { + + /** + * APIProperty: slideFactor + * {Integer} Number of pixels by which we'll pan the map in any direction + * on clicking the arrow buttons, defaults to 50. If you want to pan + * by some ratio of the map dimensions, use instead. + */ + slideFactor: 50, + + /** + * APIProperty: slideRatio + * {Number} The fraction of map width/height by which we'll pan the map + * on clicking the arrow buttons. Default is null. If set, will + * override . E.g. if slideRatio is .5, then Pan Up will + * pan up half the map height. + */ + slideRatio: null, + + /** + * Property: direction + * {String} in {'North', 'South', 'East', 'West'} + */ + direction: null, + + /** + * Constructor: OpenLayers.Control.Pan + * Control which handles the panning (in any of the cardinal directions) + * of the map by a set px distance. + * + * Parameters: + * direction - {String} The direction this button should pan. + * options - {Object} An optional object whose properties will be used + * to extend the control. + */ + initialize: function(direction, options) { + + this.direction = direction; + this.CLASS_NAME += this.direction; + + OpenLayers.Control.prototype.initialize.apply(this, [options]); + }, + + /** + * Method: trigger + */ + trigger: function(){ + if (this.map) { + var getSlideFactor = OpenLayers.Function.bind(function (dim) { + return this.slideRatio ? + this.map.getSize()[dim] * this.slideRatio : + this.slideFactor; + }, this); + + switch (this.direction) { + case OpenLayers.Control.Pan.NORTH: + this.map.pan(0, -getSlideFactor("h")); + break; + case OpenLayers.Control.Pan.SOUTH: + this.map.pan(0, getSlideFactor("h")); + break; + case OpenLayers.Control.Pan.WEST: + this.map.pan(-getSlideFactor("w"), 0); + break; + case OpenLayers.Control.Pan.EAST: + this.map.pan(getSlideFactor("w"), 0); + break; + } + } + }, + + CLASS_NAME: "OpenLayers.Control.Pan" +}); + +OpenLayers.Control.Pan.NORTH = "North"; +OpenLayers.Control.Pan.SOUTH = "South"; +OpenLayers.Control.Pan.EAST = "East"; +OpenLayers.Control.Pan.WEST = "West"; diff --git a/misc/openlayers/lib/OpenLayers/Control/PanPanel.js b/misc/openlayers/lib/OpenLayers/Control/PanPanel.js new file mode 100644 index 0000000..eeedbd0 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/PanPanel.js @@ -0,0 +1,73 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control/Panel.js + * @requires OpenLayers/Control/Pan.js + */ + +/** + * Class: OpenLayers.Control.PanPanel + * The PanPanel is visible control for panning the map North, South, East or + * West in small steps. By default it is drawn in the top left corner of the + * map. + * + * Note: + * If you wish to use this class with the default images and you want + * it to look nice in ie6, you should add the following, conditionally + * added css stylesheet to your HTML file: + * + * (code) + * + * (end) + * + * Inherits from: + * - + */ +OpenLayers.Control.PanPanel = OpenLayers.Class(OpenLayers.Control.Panel, { + + /** + * APIProperty: slideFactor + * {Integer} Number of pixels by which we'll pan the map in any direction + * on clicking the arrow buttons, defaults to 50. If you want to pan + * by some ratio of the map dimensions, use instead. + */ + slideFactor: 50, + + /** + * APIProperty: slideRatio + * {Number} The fraction of map width/height by which we'll pan the map + * on clicking the arrow buttons. Default is null. If set, will + * override . E.g. if slideRatio is .5, then Pan Up will + * pan up half the map height. + */ + slideRatio: null, + + /** + * Constructor: OpenLayers.Control.PanPanel + * Add the four directional pan buttons. + * + * Parameters: + * options - {Object} An optional object whose properties will be used + * to extend the control. + */ + initialize: function(options) { + OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); + var options = { + slideFactor: this.slideFactor, + slideRatio: this.slideRatio + }; + this.addControls([ + new OpenLayers.Control.Pan(OpenLayers.Control.Pan.NORTH, options), + new OpenLayers.Control.Pan(OpenLayers.Control.Pan.SOUTH, options), + new OpenLayers.Control.Pan(OpenLayers.Control.Pan.EAST, options), + new OpenLayers.Control.Pan(OpenLayers.Control.Pan.WEST, options) + ]); + }, + + CLASS_NAME: "OpenLayers.Control.PanPanel" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/PanZoom.js b/misc/openlayers/lib/OpenLayers/Control/PanZoom.js new file mode 100644 index 0000000..dd007cf --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/PanZoom.js @@ -0,0 +1,233 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Events/buttonclick.js + */ + +/** + * Class: OpenLayers.Control.PanZoom + * The PanZoom is a visible control, composed of a + * and a . By + * default it is drawn in the upper left corner of the map. + * + * Inherits from: + * - + */ +OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: slideFactor + * {Integer} Number of pixels by which we'll pan the map in any direction + * on clicking the arrow buttons. If you want to pan by some ratio + * of the map dimensions, use instead. + */ + slideFactor: 50, + + /** + * APIProperty: slideRatio + * {Number} The fraction of map width/height by which we'll pan the map + * on clicking the arrow buttons. Default is null. If set, will + * override . E.g. if slideRatio is .5, then the Pan Up + * button will pan up half the map height. + */ + slideRatio: null, + + /** + * Property: buttons + * {Array(DOMElement)} Array of Button Divs + */ + buttons: null, + + /** + * Property: position + * {} + */ + position: null, + + /** + * Constructor: OpenLayers.Control.PanZoom + * + * Parameters: + * options - {Object} + */ + initialize: function(options) { + this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X, + OpenLayers.Control.PanZoom.Y); + OpenLayers.Control.prototype.initialize.apply(this, arguments); + }, + + /** + * APIMethod: destroy + */ + destroy: function() { + if (this.map) { + this.map.events.unregister("buttonclick", this, this.onButtonClick); + } + this.removeButtons(); + this.buttons = null; + this.position = null; + OpenLayers.Control.prototype.destroy.apply(this, arguments); + }, + + /** + * Method: setMap + * + * Properties: + * map - {} + */ + setMap: function(map) { + OpenLayers.Control.prototype.setMap.apply(this, arguments); + this.map.events.register("buttonclick", this, this.onButtonClick); + }, + + /** + * Method: draw + * + * Parameters: + * px - {} + * + * Returns: + * {DOMElement} A reference to the container div for the PanZoom control. + */ + draw: function(px) { + // initialize our internal div + OpenLayers.Control.prototype.draw.apply(this, arguments); + px = this.position; + + // place the controls + this.buttons = []; + + var sz = {w: 18, h: 18}; + var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y); + + this._addButton("panup", "north-mini.png", centered, sz); + px.y = centered.y+sz.h; + this._addButton("panleft", "west-mini.png", px, sz); + this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz); + this._addButton("pandown", "south-mini.png", + centered.add(0, sz.h*2), sz); + this._addButton("zoomin", "zoom-plus-mini.png", + centered.add(0, sz.h*3+5), sz); + this._addButton("zoomworld", "zoom-world-mini.png", + centered.add(0, sz.h*4+5), sz); + this._addButton("zoomout", "zoom-minus-mini.png", + centered.add(0, sz.h*5+5), sz); + return this.div; + }, + + /** + * Method: _addButton + * + * Parameters: + * id - {String} + * img - {String} + * xy - {} + * sz - {} + * + * Returns: + * {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the + * image of the button, and has all the proper event handlers set. + */ + _addButton:function(id, img, xy, sz) { + var imgLocation = OpenLayers.Util.getImageLocation(img); + var btn = OpenLayers.Util.createAlphaImageDiv( + this.id + "_" + id, + xy, sz, imgLocation, "absolute"); + btn.style.cursor = "pointer"; + //we want to add the outer div + this.div.appendChild(btn); + btn.action = id; + btn.className = "olButton"; + + //we want to remember/reference the outer div + this.buttons.push(btn); + return btn; + }, + + /** + * Method: _removeButton + * + * Parameters: + * btn - {Object} + */ + _removeButton: function(btn) { + this.div.removeChild(btn); + OpenLayers.Util.removeItem(this.buttons, btn); + }, + + /** + * Method: removeButtons + */ + removeButtons: function() { + for(var i=this.buttons.length-1; i>=0; --i) { + this._removeButton(this.buttons[i]); + } + }, + + /** + * Method: onButtonClick + * + * Parameters: + * evt - {Event} + */ + onButtonClick: function(evt) { + var btn = evt.buttonElement; + switch (btn.action) { + case "panup": + this.map.pan(0, -this.getSlideFactor("h")); + break; + case "pandown": + this.map.pan(0, this.getSlideFactor("h")); + break; + case "panleft": + this.map.pan(-this.getSlideFactor("w"), 0); + break; + case "panright": + this.map.pan(this.getSlideFactor("w"), 0); + break; + case "zoomin": + this.map.zoomIn(); + break; + case "zoomout": + this.map.zoomOut(); + break; + case "zoomworld": + this.map.zoomToMaxExtent(); + break; + } + }, + + /** + * Method: getSlideFactor + * + * Parameters: + * dim - {String} "w" or "h" (for width or height). + * + * Returns: + * {Number} The slide factor for panning in the requested direction. + */ + getSlideFactor: function(dim) { + return this.slideRatio ? + this.map.getSize()[dim] * this.slideRatio : + this.slideFactor; + }, + + CLASS_NAME: "OpenLayers.Control.PanZoom" +}); + +/** + * Constant: X + * {Integer} + */ +OpenLayers.Control.PanZoom.X = 4; + +/** + * Constant: Y + * {Integer} + */ +OpenLayers.Control.PanZoom.Y = 4; diff --git a/misc/openlayers/lib/OpenLayers/Control/PanZoomBar.js b/misc/openlayers/lib/OpenLayers/Control/PanZoomBar.js new file mode 100644 index 0000000..ebf2964 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/PanZoomBar.js @@ -0,0 +1,408 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/Control/PanZoom.js + */ + +/** + * Class: OpenLayers.Control.PanZoomBar + * The PanZoomBar is a visible control composed of a + * and a . + * By default it is displayed in the upper left corner of the map as 4 + * directional arrows above a vertical slider. + * + * Inherits from: + * - + */ +OpenLayers.Control.PanZoomBar = OpenLayers.Class(OpenLayers.Control.PanZoom, { + + /** + * APIProperty: zoomStopWidth + */ + zoomStopWidth: 18, + + /** + * APIProperty: zoomStopHeight + */ + zoomStopHeight: 11, + + /** + * Property: slider + */ + slider: null, + + /** + * Property: sliderEvents + * {} + */ + sliderEvents: null, + + /** + * Property: zoombarDiv + * {DOMElement} + */ + zoombarDiv: null, + + /** + * APIProperty: zoomWorldIcon + * {Boolean} + */ + zoomWorldIcon: false, + + /** + * APIProperty: panIcons + * {Boolean} Set this property to false not to display the pan icons. If + * false the zoom world icon is placed under the zoom bar. Defaults to + * true. + */ + panIcons: true, + + /** + * APIProperty: forceFixedZoomLevel + * {Boolean} Force a fixed zoom level even though the map has + * fractionalZoom + */ + forceFixedZoomLevel: false, + + /** + * Property: mouseDragStart + * {} + */ + mouseDragStart: null, + + /** + * Property: deltaY + * {Number} The cumulative vertical pixel offset during a zoom bar drag. + */ + deltaY: null, + + /** + * Property: zoomStart + * {} + */ + zoomStart: null, + + /** + * Constructor: OpenLayers.Control.PanZoomBar + */ + + /** + * APIMethod: destroy + */ + destroy: function() { + + this._removeZoomBar(); + + this.map.events.un({ + "changebaselayer": this.redraw, + "updatesize": this.redraw, + scope: this + }); + + OpenLayers.Control.PanZoom.prototype.destroy.apply(this, arguments); + + delete this.mouseDragStart; + delete this.zoomStart; + }, + + /** + * Method: setMap + * + * Parameters: + * map - {} + */ + setMap: function(map) { + OpenLayers.Control.PanZoom.prototype.setMap.apply(this, arguments); + this.map.events.on({ + "changebaselayer": this.redraw, + "updatesize": this.redraw, + scope: this + }); + }, + + /** + * Method: redraw + * clear the div and start over. + */ + redraw: function() { + if (this.div != null) { + this.removeButtons(); + this._removeZoomBar(); + } + this.draw(); + }, + + /** + * Method: draw + * + * Parameters: + * px - {} + */ + draw: function(px) { + // initialize our internal div + OpenLayers.Control.prototype.draw.apply(this, arguments); + px = this.position.clone(); + + // place the controls + this.buttons = []; + + var sz = {w: 18, h: 18}; + if (this.panIcons) { + var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y); + var wposition = sz.w; + + if (this.zoomWorldIcon) { + centered = new OpenLayers.Pixel(px.x+sz.w, px.y); + } + + this._addButton("panup", "north-mini.png", centered, sz); + px.y = centered.y+sz.h; + this._addButton("panleft", "west-mini.png", px, sz); + if (this.zoomWorldIcon) { + this._addButton("zoomworld", "zoom-world-mini.png", px.add(sz.w, 0), sz); + + wposition *= 2; + } + this._addButton("panright", "east-mini.png", px.add(wposition, 0), sz); + this._addButton("pandown", "south-mini.png", centered.add(0, sz.h*2), sz); + this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h*3+5), sz); + centered = this._addZoomBar(centered.add(0, sz.h*4 + 5)); + this._addButton("zoomout", "zoom-minus-mini.png", centered, sz); + } + else { + this._addButton("zoomin", "zoom-plus-mini.png", px, sz); + centered = this._addZoomBar(px.add(0, sz.h)); + this._addButton("zoomout", "zoom-minus-mini.png", centered, sz); + if (this.zoomWorldIcon) { + centered = centered.add(0, sz.h+3); + this._addButton("zoomworld", "zoom-world-mini.png", centered, sz); + } + } + return this.div; + }, + + /** + * Method: _addZoomBar + * + * Parameters: + * centered - {} where zoombar drawing is to start. + */ + _addZoomBar:function(centered) { + var imgLocation = OpenLayers.Util.getImageLocation("slider.png"); + var id = this.id + "_" + this.map.id; + var minZoom = this.map.getMinZoom(); + var zoomsToEnd = this.map.getNumZoomLevels() - 1 - this.map.getZoom(); + var slider = OpenLayers.Util.createAlphaImageDiv(id, + centered.add(-1, zoomsToEnd * this.zoomStopHeight), + {w: 20, h: 9}, + imgLocation, + "absolute"); + slider.style.cursor = "move"; + this.slider = slider; + + this.sliderEvents = new OpenLayers.Events(this, slider, null, true, + {includeXY: true}); + this.sliderEvents.on({ + "touchstart": this.zoomBarDown, + "touchmove": this.zoomBarDrag, + "touchend": this.zoomBarUp, + "mousedown": this.zoomBarDown, + "mousemove": this.zoomBarDrag, + "mouseup": this.zoomBarUp + }); + + var sz = { + w: this.zoomStopWidth, + h: this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom) + }; + var imgLocation = OpenLayers.Util.getImageLocation("zoombar.png"); + var div = null; + + if (OpenLayers.Util.alphaHack()) { + var id = this.id + "_" + this.map.id; + div = OpenLayers.Util.createAlphaImageDiv(id, centered, + {w: sz.w, h: this.zoomStopHeight}, + imgLocation, + "absolute", null, "crop"); + div.style.height = sz.h + "px"; + } else { + div = OpenLayers.Util.createDiv( + 'OpenLayers_Control_PanZoomBar_Zoombar' + this.map.id, + centered, + sz, + imgLocation); + } + div.style.cursor = "pointer"; + div.className = "olButton"; + this.zoombarDiv = div; + + this.div.appendChild(div); + + this.startTop = parseInt(div.style.top); + this.div.appendChild(slider); + + this.map.events.register("zoomend", this, this.moveZoomBar); + + centered = centered.add(0, + this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom)); + return centered; + }, + + /** + * Method: _removeZoomBar + */ + _removeZoomBar: function() { + this.sliderEvents.un({ + "touchstart": this.zoomBarDown, + "touchmove": this.zoomBarDrag, + "touchend": this.zoomBarUp, + "mousedown": this.zoomBarDown, + "mousemove": this.zoomBarDrag, + "mouseup": this.zoomBarUp + }); + this.sliderEvents.destroy(); + + this.div.removeChild(this.zoombarDiv); + this.zoombarDiv = null; + this.div.removeChild(this.slider); + this.slider = null; + + this.map.events.unregister("zoomend", this, this.moveZoomBar); + }, + + /** + * Method: onButtonClick + * + * Parameters: + * evt - {Event} + */ + onButtonClick: function(evt) { + OpenLayers.Control.PanZoom.prototype.onButtonClick.apply(this, arguments); + if (evt.buttonElement === this.zoombarDiv) { + var levels = evt.buttonXY.y / this.zoomStopHeight; + if(this.forceFixedZoomLevel || !this.map.fractionalZoom) { + levels = Math.floor(levels); + } + var zoom = (this.map.getNumZoomLevels() - 1) - levels; + zoom = Math.min(Math.max(zoom, 0), this.map.getNumZoomLevels() - 1); + this.map.zoomTo(zoom); + } + }, + + /** + * Method: passEventToSlider + * This function is used to pass events that happen on the div, or the map, + * through to the slider, which then does its moving thing. + * + * Parameters: + * evt - {} + */ + passEventToSlider:function(evt) { + this.sliderEvents.handleBrowserEvent(evt); + }, + + /* + * Method: zoomBarDown + * event listener for clicks on the slider + * + * Parameters: + * evt - {} + */ + zoomBarDown:function(evt) { + if (!OpenLayers.Event.isLeftClick(evt) && !OpenLayers.Event.isSingleTouch(evt)) { + return; + } + this.map.events.on({ + "touchmove": this.passEventToSlider, + "mousemove": this.passEventToSlider, + "mouseup": this.passEventToSlider, + scope: this + }); + this.mouseDragStart = evt.xy.clone(); + this.zoomStart = evt.xy.clone(); + this.div.style.cursor = "move"; + // reset the div offsets just in case the div moved + this.zoombarDiv.offsets = null; + OpenLayers.Event.stop(evt); + }, + + /* + * Method: zoomBarDrag + * This is what happens when a click has occurred, and the client is + * dragging. Here we must ensure that the slider doesn't go beyond the + * bottom/top of the zoombar div, as well as moving the slider to its new + * visual location + * + * Parameters: + * evt - {} + */ + zoomBarDrag:function(evt) { + if (this.mouseDragStart != null) { + var deltaY = this.mouseDragStart.y - evt.xy.y; + var offsets = OpenLayers.Util.pagePosition(this.zoombarDiv); + if ((evt.clientY - offsets[1]) > 0 && + (evt.clientY - offsets[1]) < parseInt(this.zoombarDiv.style.height) - 2) { + var newTop = parseInt(this.slider.style.top) - deltaY; + this.slider.style.top = newTop+"px"; + this.mouseDragStart = evt.xy.clone(); + } + // set cumulative displacement + this.deltaY = this.zoomStart.y - evt.xy.y; + OpenLayers.Event.stop(evt); + } + }, + + /* + * Method: zoomBarUp + * Perform cleanup when a mouseup event is received -- discover new zoom + * level and switch to it. + * + * Parameters: + * evt - {} + */ + zoomBarUp:function(evt) { + if (!OpenLayers.Event.isLeftClick(evt) && evt.type !== "touchend") { + return; + } + if (this.mouseDragStart) { + this.div.style.cursor=""; + this.map.events.un({ + "touchmove": this.passEventToSlider, + "mouseup": this.passEventToSlider, + "mousemove": this.passEventToSlider, + scope: this + }); + var zoomLevel = this.map.zoom; + if (!this.forceFixedZoomLevel && this.map.fractionalZoom) { + zoomLevel += this.deltaY/this.zoomStopHeight; + zoomLevel = Math.min(Math.max(zoomLevel, 0), + this.map.getNumZoomLevels() - 1); + } else { + zoomLevel += this.deltaY/this.zoomStopHeight; + zoomLevel = Math.max(Math.round(zoomLevel), 0); + } + this.map.zoomTo(zoomLevel); + this.mouseDragStart = null; + this.zoomStart = null; + this.deltaY = 0; + OpenLayers.Event.stop(evt); + } + }, + + /* + * Method: moveZoomBar + * Change the location of the slider to match the current zoom level. + */ + moveZoomBar:function() { + var newTop = + ((this.map.getNumZoomLevels()-1) - this.map.getZoom()) * + this.zoomStopHeight + this.startTop + 1; + this.slider.style.top = newTop + "px"; + }, + + CLASS_NAME: "OpenLayers.Control.PanZoomBar" +}); \ No newline at end of file diff --git a/misc/openlayers/lib/OpenLayers/Control/Panel.js b/misc/openlayers/lib/OpenLayers/Control/Panel.js new file mode 100644 index 0000000..150afa7 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/Panel.js @@ -0,0 +1,431 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Events/buttonclick.js + */ + +/** + * Class: OpenLayers.Control.Panel + * The Panel control is a container for other controls. With it toolbars + * may be composed. + * + * Inherits from: + * - + */ +OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, { + /** + * Property: controls + * {Array()} + */ + controls: null, + + /** + * APIProperty: autoActivate + * {Boolean} Activate the control when it is added to a map. Default is + * true. + */ + autoActivate: true, + + /** + * APIProperty: defaultControl + * {} The control which is activated when the control is + * activated (turned on), which also happens at instantiation. + * If is true, will be nullified after the + * first activation of the panel. + */ + defaultControl: null, + + /** + * APIProperty: saveState + * {Boolean} If set to true, the active state of this panel's controls will + * be stored on panel deactivation, and restored on reactivation. Default + * is false. + */ + saveState: false, + + /** + * APIProperty: allowDepress + * {Boolean} If is true the controls can + * be deactivated by clicking the icon that represents them. Default + * is false. + */ + allowDepress: false, + + /** + * Property: activeState + * {Object} stores the active state of this panel's controls. + */ + activeState: null, + + /** + * Constructor: OpenLayers.Control.Panel + * Create a new control panel. + * + * Each control in the panel is represented by an icon. When clicking + * on an icon, the method is called. + * + * Specific properties for controls on a panel: + * type - {Number} One of , + * , . + * If not provided, is assumed. + * title - {string} Text displayed when mouse is over the icon that + * represents the control. + * + * The of a control determines the behavior when + * clicking its icon: + * - The control is activated and other + * controls of this type in the same panel are deactivated. This is + * the default type. + * - The active state of the control is + * toggled. + * - The + * method of the control is called, + * but its active state is not changed. + * + * If a control is , it will be drawn with the + * olControl[Name]ItemActive class, otherwise with the + * olControl[Name]ItemInactive class. + * + * Parameters: + * options - {Object} An optional object whose properties will be used + * to extend the control. + */ + initialize: function(options) { + OpenLayers.Control.prototype.initialize.apply(this, [options]); + this.controls = []; + this.activeState = {}; + }, + + /** + * APIMethod: destroy + */ + destroy: function() { + if (this.map) { + this.map.events.unregister("buttonclick", this, this.onButtonClick); + } + OpenLayers.Control.prototype.destroy.apply(this, arguments); + for (var ctl, i = this.controls.length - 1; i >= 0; i--) { + ctl = this.controls[i]; + if (ctl.events) { + ctl.events.un({ + activate: this.iconOn, + deactivate: this.iconOff + }); + } + ctl.panel_div = null; + } + this.activeState = null; + }, + + /** + * APIMethod: activate + */ + activate: function() { + if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { + var control; + for (var i=0, len=this.controls.length; i=0; i--) { + this.div.removeChild(this.div.childNodes[i]); + } + this.div.innerHTML = ""; + if (this.active) { + for (var i=0, len=this.controls.length; i} + */ + activateControl: function (control) { + if (!this.active) { return false; } + if (control.type == OpenLayers.Control.TYPE_BUTTON) { + control.trigger(); + return; + } + if (control.type == OpenLayers.Control.TYPE_TOGGLE) { + if (control.active) { + control.deactivate(); + } else { + control.activate(); + } + return; + } + if (this.allowDepress && control.active) { + control.deactivate(); + } else { + var c; + for (var i=0, len=this.controls.length; i} Controls to add in the panel. + */ + addControls: function(controls) { + if (!(OpenLayers.Util.isArray(controls))) { + controls = [controls]; + } + this.controls = this.controls.concat(controls); + + for (var i=0, len=controls.length; i} The control to create the HTML + * markup for. + * + * Returns: + * {DOMElement} The markup. + */ + createControlMarkup: function(control) { + return document.createElement("div"); + }, + + /** + * Method: addControlsToMap + * Only for internal use in draw() and addControls() methods. + * + * Parameters: + * controls - {Array()} Controls to add into map. + */ + addControlsToMap: function (controls) { + var control; + for (var i=0, len=controls.length; i=0; --i) { + if (controls[i].panel_div === button) { + this.activateControl(controls[i]); + break; + } + } + }, + + /** + * APIMethod: getControlsBy + * Get a list of controls with properties matching the given criteria. + * + * Parameters: + * property - {String} A control property to be matched. + * match - {String | Object} A string to match. Can also be a regular + * expression literal or object. In addition, it can be any object + * with a method named test. For reqular expressions or other, if + * match.test(control[property]) evaluates to true, the control will be + * included in the array returned. If no controls are found, an empty + * array is returned. + * + * Returns: + * {Array()} A list of controls matching the given criteria. + * An empty array is returned if no matches are found. + */ + getControlsBy: function(property, match) { + var test = (typeof match.test == "function"); + var found = OpenLayers.Array.filter(this.controls, function(item) { + return item[property] == match || (test && match.test(item[property])); + }); + return found; + }, + + /** + * APIMethod: getControlsByName + * Get a list of contorls with names matching the given name. + * + * Parameters: + * match - {String | Object} A control name. The name can also be a regular + * expression literal or object. In addition, it can be any object + * with a method named test. For reqular expressions or other, if + * name.test(control.name) evaluates to true, the control will be included + * in the list of controls returned. If no controls are found, an empty + * array is returned. + * + * Returns: + * {Array()} A list of controls matching the given name. + * An empty array is returned if no matches are found. + */ + getControlsByName: function(match) { + return this.getControlsBy("name", match); + }, + + /** + * APIMethod: getControlsByClass + * Get a list of controls of a given type (CLASS_NAME). + * + * Parameters: + * match - {String | Object} A control class name. The type can also be a + * regular expression literal or object. In addition, it can be any + * object with a method named test. For reqular expressions or other, + * if type.test(control.CLASS_NAME) evaluates to true, the control will + * be included in the list of controls returned. If no controls are + * found, an empty array is returned. + * + * Returns: + * {Array()} A list of controls matching the given type. + * An empty array is returned if no matches are found. + */ + getControlsByClass: function(match) { + return this.getControlsBy("CLASS_NAME", match); + }, + + CLASS_NAME: "OpenLayers.Control.Panel" +}); + diff --git a/misc/openlayers/lib/OpenLayers/Control/Permalink.js b/misc/openlayers/lib/OpenLayers/Control/Permalink.js new file mode 100644 index 0000000..3d5d7a2 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/Permalink.js @@ -0,0 +1,257 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Control/ArgParser.js + * @requires OpenLayers/Lang.js + */ + +/** + * Class: OpenLayers.Control.Permalink + * The Permalink control is hyperlink that will return the user to the + * current map view. By default it is drawn in the lower right corner of the + * map. The href is updated as the map is zoomed, panned and whilst layers + * are switched. + * + * Inherits from: + * - + */ +OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: argParserClass + * {Class} The ArgParser control class (not instance) to use with this + * control. + */ + argParserClass: OpenLayers.Control.ArgParser, + + /** + * Property: element + * {DOMElement} + */ + element: null, + + /** + * APIProperty: anchor + * {Boolean} This option changes 3 things: + * the character '#' is used in place of the character '?', + * the window.href is updated if no element is provided. + * When this option is set to true it's not recommend to provide + * a base without provide an element. + */ + anchor: false, + + /** + * APIProperty: base + * {String} + */ + base: '', + + /** + * APIProperty: displayProjection + * {} Requires proj4js support. Projection used + * when creating the coordinates in the link. This will reproject the + * map coordinates into display coordinates. If you are using this + * functionality, the permalink which is last added to the map will + * determine the coordinate type which is read from the URL, which + * means you should not add permalinks with different + * displayProjections to the same map. + */ + displayProjection: null, + + /** + * Constructor: OpenLayers.Control.Permalink + * + * Parameters: + * element - {DOMElement} + * base - {String} + * options - {Object} options to the control. + * + * Or for anchor: + * options - {Object} options to the control. + */ + initialize: function(element, base, options) { + if (element !== null && typeof element == 'object' && !OpenLayers.Util.isElement(element)) { + options = element; + this.base = document.location.href; + OpenLayers.Control.prototype.initialize.apply(this, [options]); + if (this.element != null) { + this.element = OpenLayers.Util.getElement(this.element); + } + } + else { + OpenLayers.Control.prototype.initialize.apply(this, [options]); + this.element = OpenLayers.Util.getElement(element); + this.base = base || document.location.href; + } + }, + + /** + * APIMethod: destroy + */ + destroy: function() { + if (this.element && this.element.parentNode == this.div) { + this.div.removeChild(this.element); + this.element = null; + } + if (this.map) { + this.map.events.unregister('moveend', this, this.updateLink); + } + + OpenLayers.Control.prototype.destroy.apply(this, arguments); + }, + + /** + * Method: setMap + * Set the map property for the control. + * + * Parameters: + * map - {} + */ + setMap: function(map) { + OpenLayers.Control.prototype.setMap.apply(this, arguments); + + //make sure we have an arg parser attached + for(var i=0, len=this.map.controls.length; i} center to encode in the permalink. + * Defaults to the current map center. + * zoom - {Integer} zoom level to encode in the permalink. Defaults to the + * current map zoom level. + * layers - {Array()} layers to encode in the permalink. + * Defaults to the current map layers. + * + * Returns: + * {Object} Hash of parameters that will be url-encoded into the + * permalink. + */ + createParams: function(center, zoom, layers) { + center = center || this.map.getCenter(); + + var params = OpenLayers.Util.getParameters(this.base); + + // If there's still no center, map is not initialized yet. + // Break out of this function, and simply return the params from the + // base link. + if (center) { + + //zoom + params.zoom = zoom || this.map.getZoom(); + + //lon,lat + var lat = center.lat; + var lon = center.lon; + + if (this.displayProjection) { + var mapPosition = OpenLayers.Projection.transform( + { x: lon, y: lat }, + this.map.getProjectionObject(), + this.displayProjection ); + lon = mapPosition.x; + lat = mapPosition.y; + } + params.lat = Math.round(lat*100000)/100000; + params.lon = Math.round(lon*100000)/100000; + + //layers + layers = layers || this.map.layers; + params.layers = ''; + for (var i=0, len=layers.length; i + */ +OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, { + + /** + * Property: type + * {OpenLayers.Control.TYPES} + */ + type: OpenLayers.Control.TYPE_TOOL, + + /** + * Property: pinchOrigin + * {Object} Cached object representing the pinch start (in pixels). + */ + pinchOrigin: null, + + /** + * Property: currentCenter + * {Object} Cached object representing the latest pinch center (in pixels). + */ + currentCenter: null, + + /** + * APIProperty: autoActivate + * {Boolean} Activate the control when it is added to a map. Default is + * true. + */ + autoActivate: true, + + /** + * APIProperty: preserveCenter + * {Boolean} Set this to true if you don't want the map center to change + * while pinching. For example you may want to set preserveCenter to + * true when the user location is being watched and you want to preserve + * the user location at the center of the map even if he zooms in or + * out using pinch. This property's value can be changed any time on an + * existing instance. Default is false. + */ + preserveCenter: false, + + /** + * APIProperty: handlerOptions + * {Object} Used to set non-default properties on the pinch handler + */ + + /** + * Constructor: OpenLayers.Control.PinchZoom + * Create a control for zooming with pinch gestures. This works on devices + * with multi-touch support. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * the control + */ + initialize: function(options) { + OpenLayers.Control.prototype.initialize.apply(this, arguments); + this.handler = new OpenLayers.Handler.Pinch(this, { + start: this.pinchStart, + move: this.pinchMove, + done: this.pinchDone + }, this.handlerOptions); + }, + + /** + * Method: pinchStart + * + * Parameters: + * evt - {Event} + * pinchData - {Object} pinch data object related to the current touchmove + * of the pinch gesture. This give us the current scale of the pinch. + */ + pinchStart: function(evt, pinchData) { + var xy = (this.preserveCenter) ? + this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy; + this.pinchOrigin = xy; + this.currentCenter = xy; + }, + + /** + * Method: pinchMove + * + * Parameters: + * evt - {Event} + * pinchData - {Object} pinch data object related to the current touchmove + * of the pinch gesture. This give us the current scale of the pinch. + */ + pinchMove: function(evt, pinchData) { + var scale = pinchData.scale; + var containerOrigin = this.map.layerContainerOriginPx; + var pinchOrigin = this.pinchOrigin; + var current = (this.preserveCenter) ? + this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy; + + var dx = Math.round((containerOrigin.x + current.x - pinchOrigin.x) + (scale - 1) * (containerOrigin.x - pinchOrigin.x)); + var dy = Math.round((containerOrigin.y + current.y - pinchOrigin.y) + (scale - 1) * (containerOrigin.y - pinchOrigin.y)); + + this.map.applyTransform(dx, dy, scale); + this.currentCenter = current; + }, + + /** + * Method: pinchDone + * + * Parameters: + * evt - {Event} + * start - {Object} pinch data object related to the touchstart event that + * started the pinch gesture. + * last - {Object} pinch data object related to the last touchmove event + * of the pinch gesture. This give us the final scale of the pinch. + */ + pinchDone: function(evt, start, last) { + this.map.applyTransform(); + var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true); + if (zoom !== this.map.getZoom() || !this.currentCenter.equals(this.pinchOrigin)) { + var resolution = this.map.getResolutionForZoom(zoom); + + var location = this.map.getLonLatFromPixel(this.pinchOrigin); + var zoomPixel = this.currentCenter; + var size = this.map.getSize(); + + location.lon += resolution * ((size.w / 2) - zoomPixel.x); + location.lat -= resolution * ((size.h / 2) - zoomPixel.y); + + // Force a reflow before calling setCenter. This is to work + // around an issue occuring in iOS. + // + // See https://github.com/openlayers/openlayers/pull/351. + // + // Without a reflow setting the layer container div's top left + // style properties to "0px" - as done in Map.moveTo when zoom + // is changed - won't actually correctly reposition the layer + // container div. + // + // Also, we need to use a statement that the Google Closure + // compiler won't optimize away. + this.map.div.clientWidth = this.map.div.clientWidth; + + this.map.setCenter(location, zoom); + } + }, + + CLASS_NAME: "OpenLayers.Control.PinchZoom" + +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/SLDSelect.js b/misc/openlayers/lib/OpenLayers/Control/SLDSelect.js new file mode 100644 index 0000000..cd348a7 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/SLDSelect.js @@ -0,0 +1,567 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Layer/WMS.js + * @requires OpenLayers/Handler/RegularPolygon.js + * @requires OpenLayers/Handler/Polygon.js + * @requires OpenLayers/Handler/Path.js + * @requires OpenLayers/Handler/Click.js + * @requires OpenLayers/Filter/Spatial.js + * @requires OpenLayers/Format/SLD/v1_0_0.js + */ + +/** + * Class: OpenLayers.Control.SLDSelect + * Perform selections on WMS layers using Styled Layer Descriptor (SLD) + * + * Inherits from: + * - + */ +OpenLayers.Control.SLDSelect = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: events + * {} Events instance for listeners and triggering + * control specific events. + * + * Register a listener for a particular event with the following syntax: + * (code) + * control.events.register(type, obj, listener); + * (end) + * + * Supported event types (in addition to those from ): + * selected - Triggered when a selection occurs. Listeners receive an + * event with *filters* and *layer* properties. Filters will be an + * array of OpenLayers.Filter objects created in order to perform + * the particular selection. + */ + + /** + * APIProperty: clearOnDeactivate + * {Boolean} Should the selection be cleared when the control is + * deactivated. Default value is false. + */ + clearOnDeactivate: false, + + /** + * APIProperty: layers + * {Array()} The WMS layers this control will work + * on. + */ + layers: null, + + /** + * Property: callbacks + * {Object} The functions that are sent to the handler for callback + */ + callbacks: null, + + /** + * APIProperty: selectionSymbolizer + * {Object} Determines the styling of the selected objects. Default is + * a selection in red. + */ + selectionSymbolizer: { + 'Polygon': {fillColor: '#FF0000', stroke: false}, + 'Line': {strokeColor: '#FF0000', strokeWidth: 2}, + 'Point': {graphicName: 'square', fillColor: '#FF0000', pointRadius: 5} + }, + + /** + * APIProperty: layerOptions + * {Object} The options to apply to the selection layer, by default the + * selection layer will be kept out of the layer switcher. + */ + layerOptions: null, + + /** + * APIProperty: handlerOptions + * {Object} Used to set non-default properties on the control's handler + */ + + /** + * APIProperty: sketchStyle + * {|Object} Style or symbolizer to use for the sketch + * handler. The recommended way of styling the sketch layer, however, is + * to configure an in the layerOptions of the + * : + * + * (code) + * new OpenLayers.Control.SLDSelect(OpenLayers.Handler.Path, { + * handlerOptions: { + * layerOptions: { + * styleMap: new OpenLayers.StyleMap({ + * "default": {strokeColor: "yellow"} + * }) + * } + * } + * }); + * (end) + */ + sketchStyle: null, + + /** + * APIProperty: wfsCache + * {Object} Cache to use for storing parsed results from + * . If not provided, + * these will be cached on the prototype. + */ + wfsCache: {}, + + /** + * APIProperty: layerCache + * {Object} Cache to use for storing references to the selection layers. + * Normally each source layer will have exactly 1 selection layer of + * type OpenLayers.Layer.WMS. If not provided, layers will + * be cached on the prototype. Note that if is + * true, the layer will no longer be cached after deactivating the + * control. + */ + layerCache: {}, + + /** + * Constructor: OpenLayers.Control.SLDSelect + * Create a new control for selecting features in WMS layers using + * Styled Layer Descriptor (SLD). + * + * Parameters: + * handler - {} A sketch handler class. This determines + * the type of selection, e.g. box (), point + * (), path () or + * polygon () selection. To use circle + * type selection, use and pass + * the number of desired sides (e.g. 40) as "sides" property to the + * . + * options - {Object} An object containing all configuration properties for + * the control. + * + * Valid options: + * layers - Array({}) The layers to perform the + * selection on. + */ + initialize: function(handler, options) { + OpenLayers.Control.prototype.initialize.apply(this, [options]); + + this.callbacks = OpenLayers.Util.extend({done: this.select, + click: this.select}, this.callbacks); + this.handlerOptions = this.handlerOptions || {}; + this.layerOptions = OpenLayers.Util.applyDefaults(this.layerOptions, { + displayInLayerSwitcher: false, + tileOptions: {maxGetUrlLength: 2048} + }); + if (this.sketchStyle) { + this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults( + this.handlerOptions.layerOptions, + {styleMap: new OpenLayers.StyleMap({"default": this.sketchStyle})} + ); + } + this.handler = new handler(this, this.callbacks, this.handlerOptions); + }, + + /** + * APIMethod: destroy + * Take care of things that are not handled in superclass. + */ + destroy: function() { + for (var key in this.layerCache) { + delete this.layerCache[key]; + } + for (var key in this.wfsCache) { + delete this.wfsCache[key]; + } + OpenLayers.Control.prototype.destroy.apply(this, arguments); + }, + + /** + * Method: coupleLayerVisiblity + * Couple the selection layer and the source layer with respect to + * layer visibility. So if the source layer is turned off, the + * selection layer is also turned off. + * + * Context: + * - {} + * + * Parameters: + * evt - {Object} + */ + coupleLayerVisiblity: function(evt) { + this.setVisibility(evt.object.getVisibility()); + }, + + /** + * Method: createSelectionLayer + * Creates a "clone" from the source layer in which the selection can + * be drawn. This ensures both the source layer and the selection are + * visible and not only the selection. + * + * Parameters: + * source - {} The source layer on which the selection + * is performed. + * + * Returns: + * {} A WMS layer with maxGetUrlLength configured to 2048 + * since SLD selections can easily get quite long. + */ + createSelectionLayer: function(source) { + // check if we already have a selection layer for the source layer + var selectionLayer; + if (!this.layerCache[source.id]) { + selectionLayer = new OpenLayers.Layer.WMS(source.name, + source.url, source.params, + OpenLayers.Util.applyDefaults( + this.layerOptions, + source.getOptions()) + ); + this.layerCache[source.id] = selectionLayer; + // make sure the layers are coupled wrt visibility, but only + // if they are not displayed in the layer switcher, because in + // that case the user cannot control visibility. + if (this.layerOptions.displayInLayerSwitcher === false) { + source.events.on({ + "visibilitychanged": this.coupleLayerVisiblity, + scope: selectionLayer}); + } + this.map.addLayer(selectionLayer); + } else { + selectionLayer = this.layerCache[source.id]; + } + return selectionLayer; + }, + + /** + * Method: createSLD + * Create the SLD document for the layer using the supplied filters. + * + * Parameters: + * layer - {} + * filters - Array({}) The filters to be applied. + * geometryAttributes - Array({Object}) The geometry attributes of the + * layer. + * + * Returns: + * {String} The SLD document generated as a string. + */ + createSLD: function(layer, filters, geometryAttributes) { + var sld = {version: "1.0.0", namedLayers: {}}; + var layerNames = [layer.params.LAYERS].join(",").split(","); + for (var i=0, len=layerNames.length; i= 0) { + symbolizer = {Polygon: this.selectionSymbolizer['Polygon']}; + } else if (geometryAttribute.type.indexOf('LineString') >= 0) { + symbolizer = {Line: this.selectionSymbolizer['Line']}; + } else if (geometryAttribute.type.indexOf('Point') >= 0) { + symbolizer = {Point: this.selectionSymbolizer['Point']}; + } + var filter = filters[i]; + sld.namedLayers[name].userStyles.push({name: 'default', rules: [ + new OpenLayers.Rule({symbolizer: symbolizer, + filter: filter, + maxScaleDenominator: layer.options.minScale}) + ]}); + } + return new OpenLayers.Format.SLD({srsName: this.map.getProjection()}).write(sld); + }, + + /** + * Method: parseDescribeLayer + * Parse the SLD WMS DescribeLayer response and issue the corresponding + * WFS DescribeFeatureType request + * + * request - {XMLHttpRequest} The request object. + */ + parseDescribeLayer: function(request) { + var format = new OpenLayers.Format.WMSDescribeLayer(); + var doc = request.responseXML; + if(!doc || !doc.documentElement) { + doc = request.responseText; + } + var describeLayer = format.read(doc); + var typeNames = []; + var url = null; + for (var i=0, len=describeLayer.length; i} The layer for which to look up the + * geometry attributes. + * + * Returns: + * Array({Object}) Array of geometry attributes + */ + getGeometryAttributes: function(layer) { + var result = []; + var cache = this.wfsCache[layer.id]; + for (var i=0, len=cache.featureTypes.length; i= 0) || + (type.indexOf('GeometryAssociationType') >=0) || + (type.indexOf('GeometryPropertyType') >= 0) || + (type.indexOf('Point') >= 0) || + (type.indexOf('Polygon') >= 0) ) { + result.push(property); + } + } + } + return result; + }, + + /** + * APIMethod: activate + * Activate the control. Activating the control will perform a SLD WMS + * DescribeLayer request followed by a WFS DescribeFeatureType request + * so that the proper symbolizers can be chosen based on the geometry + * type. + */ + activate: function() { + var activated = OpenLayers.Control.prototype.activate.call(this); + if(activated) { + for (var i=0, len=this.layers.length; i)} The new set of layers on which + * the selection should be performed. + */ + setLayers: function(layers) { + if(this.active) { + this.deactivate(); + this.layers = layers; + this.activate(); + } else { + this.layers = layers; + } + }, + + /** + * Function: createFilter + * Create the filter to be used in the SLD. + * + * Parameters: + * geometryAttribute - {Object} Used to get the name of the geometry + * attribute which is needed for constructing the spatial filter. + * geometry - {} The geometry to use. + * + * Returns: + * {} The spatial filter created. + */ + createFilter: function(geometryAttribute, geometry) { + var filter = null; + if (this.handler instanceof OpenLayers.Handler.RegularPolygon) { + // box + if (this.handler.irregular === true) { + filter = new OpenLayers.Filter.Spatial({ + type: OpenLayers.Filter.Spatial.BBOX, + property: geometryAttribute.name, + value: geometry.getBounds()} + ); + } else { + filter = new OpenLayers.Filter.Spatial({ + type: OpenLayers.Filter.Spatial.INTERSECTS, + property: geometryAttribute.name, + value: geometry} + ); + } + } else if (this.handler instanceof OpenLayers.Handler.Polygon) { + filter = new OpenLayers.Filter.Spatial({ + type: OpenLayers.Filter.Spatial.INTERSECTS, + property: geometryAttribute.name, + value: geometry} + ); + } else if (this.handler instanceof OpenLayers.Handler.Path) { + // if source layer is point based, use DWITHIN instead + if (geometryAttribute.type.indexOf('Point') >= 0) { + filter = new OpenLayers.Filter.Spatial({ + type: OpenLayers.Filter.Spatial.DWITHIN, + property: geometryAttribute.name, + distance: this.map.getExtent().getWidth()*0.01 , + distanceUnits: this.map.getUnits(), + value: geometry} + ); + } else { + filter = new OpenLayers.Filter.Spatial({ + type: OpenLayers.Filter.Spatial.INTERSECTS, + property: geometryAttribute.name, + value: geometry} + ); + } + } else if (this.handler instanceof OpenLayers.Handler.Click) { + if (geometryAttribute.type.indexOf('Polygon') >= 0) { + filter = new OpenLayers.Filter.Spatial({ + type: OpenLayers.Filter.Spatial.INTERSECTS, + property: geometryAttribute.name, + value: geometry} + ); + } else { + filter = new OpenLayers.Filter.Spatial({ + type: OpenLayers.Filter.Spatial.DWITHIN, + property: geometryAttribute.name, + distance: this.map.getExtent().getWidth()*0.01 , + distanceUnits: this.map.getUnits(), + value: geometry} + ); + } + } + return filter; + }, + + /** + * Method: select + * When the handler is done, use SLD_BODY on the selection layer to + * display the selection in the map. + * + * Parameters: + * geometry - {Object} or {} + */ + select: function(geometry) { + this._queue = function() { + for (var i=0, len=this.layers.length; i + */ +OpenLayers.Control.Scale = OpenLayers.Class(OpenLayers.Control, { + + /** + * Property: element + * {DOMElement} + */ + element: null, + + /** + * APIProperty: geodesic + * {Boolean} Use geodesic measurement. Default is false. The recommended + * setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to + * true, the scale will be calculated based on the horizontal size of the + * pixel in the center of the map viewport. + */ + geodesic: false, + + /** + * Constructor: OpenLayers.Control.Scale + * + * Parameters: + * element - {DOMElement} + * options - {Object} + */ + initialize: function(element, options) { + OpenLayers.Control.prototype.initialize.apply(this, [options]); + this.element = OpenLayers.Util.getElement(element); + }, + + /** + * Method: draw + * + * Returns: + * {DOMElement} + */ + draw: function() { + OpenLayers.Control.prototype.draw.apply(this, arguments); + if (!this.element) { + this.element = document.createElement("div"); + this.div.appendChild(this.element); + } + this.map.events.register( 'moveend', this, this.updateScale); + this.updateScale(); + return this.div; + }, + + /** + * Method: updateScale + */ + updateScale: function() { + var scale; + if(this.geodesic === true) { + var units = this.map.getUnits(); + if(!units) { + return; + } + var inches = OpenLayers.INCHES_PER_UNIT; + scale = (this.map.getGeodesicPixelSize().w || 0.000001) * + inches["km"] * OpenLayers.DOTS_PER_INCH; + } else { + scale = this.map.getScale(); + } + + if (!scale) { + return; + } + + if (scale >= 9500 && scale <= 950000) { + scale = Math.round(scale / 1000) + "K"; + } else if (scale >= 950000) { + scale = Math.round(scale / 1000000) + "M"; + } else { + scale = Math.round(scale); + } + + this.element.innerHTML = OpenLayers.i18n("Scale = 1 : ${scaleDenom}", {'scaleDenom':scale}); + }, + + CLASS_NAME: "OpenLayers.Control.Scale" +}); + diff --git a/misc/openlayers/lib/OpenLayers/Control/ScaleLine.js b/misc/openlayers/lib/OpenLayers/Control/ScaleLine.js new file mode 100644 index 0000000..9262414 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/ScaleLine.js @@ -0,0 +1,220 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + */ + +/** + * Class: OpenLayers.Control.ScaleLine + * The ScaleLine displays a small line indicator representing the current + * map scale on the map. By default it is drawn in the lower left corner of + * the map. + * + * Inherits from: + * - + * + * Is a very close copy of: + * - + */ +OpenLayers.Control.ScaleLine = OpenLayers.Class(OpenLayers.Control, { + + /** + * Property: maxWidth + * {Integer} Maximum width of the scale line in pixels. Default is 100. + */ + maxWidth: 100, + + /** + * Property: topOutUnits + * {String} Units for zoomed out on top bar. Default is km. + */ + topOutUnits: "km", + + /** + * Property: topInUnits + * {String} Units for zoomed in on top bar. Default is m. + */ + topInUnits: "m", + + /** + * Property: bottomOutUnits + * {String} Units for zoomed out on bottom bar. Default is mi. + */ + bottomOutUnits: "mi", + + /** + * Property: bottomInUnits + * {String} Units for zoomed in on bottom bar. Default is ft. + */ + bottomInUnits: "ft", + + /** + * Property: eTop + * {DOMElement} + */ + eTop: null, + + /** + * Property: eBottom + * {DOMElement} + */ + eBottom:null, + + /** + * APIProperty: geodesic + * {Boolean} Use geodesic measurement. Default is false. The recommended + * setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to + * true, the scale will be calculated based on the horizontal size of the + * pixel in the center of the map viewport. + */ + geodesic: false, + + /** + * Constructor: OpenLayers.Control.ScaleLine + * Create a new scale line control. + * + * Parameters: + * options - {Object} An optional object whose properties will be used + * to extend the control. + */ + + /** + * Method: draw + * + * Returns: + * {DOMElement} + */ + draw: function() { + OpenLayers.Control.prototype.draw.apply(this, arguments); + if (!this.eTop) { + // stick in the top bar + this.eTop = document.createElement("div"); + this.eTop.className = this.displayClass + "Top"; + var theLen = this.topInUnits.length; + this.div.appendChild(this.eTop); + if((this.topOutUnits == "") || (this.topInUnits == "")) { + this.eTop.style.visibility = "hidden"; + } else { + this.eTop.style.visibility = "visible"; + } + + // and the bottom bar + this.eBottom = document.createElement("div"); + this.eBottom.className = this.displayClass + "Bottom"; + this.div.appendChild(this.eBottom); + if((this.bottomOutUnits == "") || (this.bottomInUnits == "")) { + this.eBottom.style.visibility = "hidden"; + } else { + this.eBottom.style.visibility = "visible"; + } + } + this.map.events.register('moveend', this, this.update); + this.update(); + return this.div; + }, + + /** + * Method: getBarLen + * Given a number, round it down to the nearest 1,2,5 times a power of 10. + * That seems a fairly useful set of number groups to use. + * + * Parameters: + * maxLen - {float} the number we're rounding down from + * + * Returns: + * {Float} the rounded number (less than or equal to maxLen) + */ + getBarLen: function(maxLen) { + // nearest power of 10 lower than maxLen + var digits = parseInt(Math.log(maxLen) / Math.log(10)); + var pow10 = Math.pow(10, digits); + + // ok, find first character + var firstChar = parseInt(maxLen / pow10); + + // right, put it into the correct bracket + var barLen; + if(firstChar > 5) { + barLen = 5; + } else if(firstChar > 2) { + barLen = 2; + } else { + barLen = 1; + } + + // scale it up the correct power of 10 + return barLen * pow10; + }, + + /** + * Method: update + * Update the size of the bars, and the labels they contain. + */ + update: function() { + var res = this.map.getResolution(); + if (!res) { + return; + } + + var curMapUnits = this.map.getUnits(); + var inches = OpenLayers.INCHES_PER_UNIT; + + // convert maxWidth to map units + var maxSizeData = this.maxWidth * res * inches[curMapUnits]; + var geodesicRatio = 1; + if(this.geodesic === true) { + var maxSizeGeodesic = (this.map.getGeodesicPixelSize().w || + 0.000001) * this.maxWidth; + var maxSizeKilometers = maxSizeData / inches["km"]; + geodesicRatio = maxSizeGeodesic / maxSizeKilometers; + maxSizeData *= geodesicRatio; + } + + // decide whether to use large or small scale units + var topUnits; + var bottomUnits; + if(maxSizeData > 100000) { + topUnits = this.topOutUnits; + bottomUnits = this.bottomOutUnits; + } else { + topUnits = this.topInUnits; + bottomUnits = this.bottomInUnits; + } + + // and to map units units + var topMax = maxSizeData / inches[topUnits]; + var bottomMax = maxSizeData / inches[bottomUnits]; + + // now trim this down to useful block length + var topRounded = this.getBarLen(topMax); + var bottomRounded = this.getBarLen(bottomMax); + + // and back to display units + topMax = topRounded / inches[curMapUnits] * inches[topUnits]; + bottomMax = bottomRounded / inches[curMapUnits] * inches[bottomUnits]; + + // and to pixel units + var topPx = topMax / res / geodesicRatio; + var bottomPx = bottomMax / res / geodesicRatio; + + // now set the pixel widths + // and the values inside them + + if (this.eBottom.style.visibility == "visible"){ + this.eBottom.style.width = Math.round(bottomPx) + "px"; + this.eBottom.innerHTML = bottomRounded + " " + bottomUnits ; + } + + if (this.eTop.style.visibility == "visible"){ + this.eTop.style.width = Math.round(topPx) + "px"; + this.eTop.innerHTML = topRounded + " " + topUnits; + } + + }, + + CLASS_NAME: "OpenLayers.Control.ScaleLine" +}); + diff --git a/misc/openlayers/lib/OpenLayers/Control/SelectFeature.js b/misc/openlayers/lib/OpenLayers/Control/SelectFeature.js new file mode 100644 index 0000000..5467267 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/SelectFeature.js @@ -0,0 +1,643 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Feature/Vector.js + * @requires OpenLayers/Handler/Feature.js + * @requires OpenLayers/Layer/Vector/RootContainer.js + */ + +/** + * Class: OpenLayers.Control.SelectFeature + * The SelectFeature control selects vector features from a given layer on + * click or hover. + * + * Inherits from: + * - + */ +OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: events + * {} Events instance for listeners and triggering + * control specific events. + * + * Register a listener for a particular event with the following syntax: + * (code) + * control.events.register(type, obj, listener); + * (end) + * + * Supported event types (in addition to those from ): + * beforefeaturehighlighted - Triggered before a feature is highlighted + * featurehighlighted - Triggered when a feature is highlighted + * featureunhighlighted - Triggered when a feature is unhighlighted + * boxselectionstart - Triggered before box selection starts + * boxselectionend - Triggered after box selection ends + */ + + /** + * Property: multipleKey + * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets + * the property to true. Default is null. + */ + multipleKey: null, + + /** + * Property: toggleKey + * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets + * the property to true. Default is null. + */ + toggleKey: null, + + /** + * APIProperty: multiple + * {Boolean} Allow selection of multiple geometries. Default is false. + */ + multiple: false, + + /** + * APIProperty: clickout + * {Boolean} Unselect features when clicking outside any feature. + * Default is true. + */ + clickout: true, + + /** + * APIProperty: toggle + * {Boolean} Unselect a selected feature on click. Default is false. Only + * has meaning if hover is false. + */ + toggle: false, + + /** + * APIProperty: hover + * {Boolean} Select on mouse over and deselect on mouse out. If true, this + * ignores clicks and only listens to mouse moves. + */ + hover: false, + + /** + * APIProperty: highlightOnly + * {Boolean} If true do not actually select features (that is place them in + * the layer's selected features array), just highlight them. This property + * has no effect if hover is false. Defaults to false. + */ + highlightOnly: false, + + /** + * APIProperty: box + * {Boolean} Allow feature selection by drawing a box. + */ + box: false, + + /** + * Property: onBeforeSelect + * {Function} Optional function to be called before a feature is selected. + * The function should expect to be called with a feature. + */ + onBeforeSelect: function() {}, + + /** + * APIProperty: onSelect + * {Function} Optional function to be called when a feature is selected. + * The function should expect to be called with a feature. + */ + onSelect: function() {}, + + /** + * APIProperty: onUnselect + * {Function} Optional function to be called when a feature is unselected. + * The function should expect to be called with a feature. + */ + onUnselect: function() {}, + + /** + * Property: scope + * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect + * callbacks. If null the scope will be this control. + */ + scope: null, + + /** + * APIProperty: geometryTypes + * {Array(String)} To restrict selecting to a limited set of geometry types, + * send a list of strings corresponding to the geometry class names. + */ + geometryTypes: null, + + /** + * Property: layer + * {} The vector layer with a common renderer + * root for all layers this control is configured with (if an array of + * layers was passed to the constructor), or the vector layer the control + * was configured with (if a single layer was passed to the constructor). + */ + layer: null, + + /** + * Property: layers + * {Array()} The layers this control will work on, + * or null if the control was configured with a single layer + */ + layers: null, + + /** + * APIProperty: callbacks + * {Object} The functions that are sent to the handlers.feature for callback + */ + callbacks: null, + + /** + * APIProperty: selectStyle + * {Object} Hash of styles + */ + selectStyle: null, + + /** + * Property: renderIntent + * {String} key used to retrieve the select style from the layer's + * style map. + */ + renderIntent: "select", + + /** + * Property: handlers + * {Object} Object with references to multiple + * instances. + */ + handlers: null, + + /** + * Constructor: OpenLayers.Control.SelectFeature + * Create a new control for selecting features. + * + * Parameters: + * layers - {}, or an array of vector layers. The + * layer(s) this control will select features from. + * options - {Object} + */ + initialize: function(layers, options) { + OpenLayers.Control.prototype.initialize.apply(this, [options]); + + if(this.scope === null) { + this.scope = this; + } + this.initLayer(layers); + var callbacks = { + click: this.clickFeature, + clickout: this.clickoutFeature + }; + if (this.hover) { + callbacks.over = this.overFeature; + callbacks.out = this.outFeature; + } + + this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks); + this.handlers = { + feature: new OpenLayers.Handler.Feature( + this, this.layer, this.callbacks, + {geometryTypes: this.geometryTypes} + ) + }; + + if (this.box) { + this.handlers.box = new OpenLayers.Handler.Box( + this, {done: this.selectBox}, + {boxDivClassName: "olHandlerBoxSelectFeature"} + ); + } + }, + + /** + * Method: initLayer + * Assign the layer property. If layers is an array, we need to use + * a RootContainer. + * + * Parameters: + * layers - {}, or an array of vector layers. + */ + initLayer: function(layers) { + if(OpenLayers.Util.isArray(layers)) { + this.layers = layers; + this.layer = new OpenLayers.Layer.Vector.RootContainer( + this.id + "_container", { + layers: layers + } + ); + } else { + this.layer = layers; + } + }, + + /** + * Method: destroy + */ + destroy: function() { + if(this.active && this.layers) { + this.map.removeLayer(this.layer); + } + OpenLayers.Control.prototype.destroy.apply(this, arguments); + if(this.layers) { + this.layer.destroy(); + } + }, + + /** + * Method: activate + * Activates the control. + * + * Returns: + * {Boolean} The control was effectively activated. + */ + activate: function () { + if (!this.active) { + if(this.layers) { + this.map.addLayer(this.layer); + } + this.handlers.feature.activate(); + if(this.box && this.handlers.box) { + this.handlers.box.activate(); + } + } + return OpenLayers.Control.prototype.activate.apply( + this, arguments + ); + }, + + /** + * Method: deactivate + * Deactivates the control. + * + * Returns: + * {Boolean} The control was effectively deactivated. + */ + deactivate: function () { + if (this.active) { + this.handlers.feature.deactivate(); + if(this.handlers.box) { + this.handlers.box.deactivate(); + } + if(this.layers) { + this.map.removeLayer(this.layer); + } + } + return OpenLayers.Control.prototype.deactivate.apply( + this, arguments + ); + }, + + /** + * Method: unselectAll + * Unselect all selected features. To unselect all except for a single + * feature, set the options.except property to the feature. + * + * Parameters: + * options - {Object} Optional configuration object. + */ + unselectAll: function(options) { + // we'll want an option to supress notification here + var layers = this.layers || [this.layer], + layer, feature, l, numExcept; + for(l=0; l numExcept) { + feature = layer.selectedFeatures[numExcept]; + if(!options || options.except != feature) { + this.unselect(feature); + } else { + ++numExcept; + } + } + } + } + }, + + /** + * Method: clickFeature + * Called on click in a feature + * Only responds if this.hover is false. + * + * Parameters: + * feature - {} + */ + clickFeature: function(feature) { + if(!this.hover) { + var selected = (OpenLayers.Util.indexOf( + feature.layer.selectedFeatures, feature) > -1); + if(selected) { + if(this.toggleSelect()) { + this.unselect(feature); + } else if(!this.multipleSelect()) { + this.unselectAll({except: feature}); + } + } else { + if(!this.multipleSelect()) { + this.unselectAll({except: feature}); + } + this.select(feature); + } + } + }, + + /** + * Method: multipleSelect + * Allow for multiple selected features based on property and + * event modifier. + * + * Returns: + * {Boolean} Allow for multiple selected features. + */ + multipleSelect: function() { + return this.multiple || (this.handlers.feature.evt && + this.handlers.feature.evt[this.multipleKey]); + }, + + /** + * Method: toggleSelect + * Event should toggle the selected state of a feature based on + * property and event modifier. + * + * Returns: + * {Boolean} Toggle the selected state of a feature. + */ + toggleSelect: function() { + return this.toggle || (this.handlers.feature.evt && + this.handlers.feature.evt[this.toggleKey]); + }, + + /** + * Method: clickoutFeature + * Called on click outside a previously clicked (selected) feature. + * Only responds if this.hover is false. + * + * Parameters: + * feature - {} + */ + clickoutFeature: function(feature) { + if(!this.hover && this.clickout) { + this.unselectAll(); + } + }, + + /** + * Method: overFeature + * Called on over a feature. + * Only responds if this.hover is true. + * + * Parameters: + * feature - {} + */ + overFeature: function(feature) { + var layer = feature.layer; + if(this.hover) { + if(this.highlightOnly) { + this.highlight(feature); + } else if(OpenLayers.Util.indexOf( + layer.selectedFeatures, feature) == -1) { + this.select(feature); + } + } + }, + + /** + * Method: outFeature + * Called on out of a selected feature. + * Only responds if this.hover is true. + * + * Parameters: + * feature - {} + */ + outFeature: function(feature) { + if(this.hover) { + if(this.highlightOnly) { + // we do nothing if we're not the last highlighter of the + // feature + if(feature._lastHighlighter == this.id) { + // if another select control had highlighted the feature before + // we did it ourself then we use that control to highlight the + // feature as it was before we highlighted it, else we just + // unhighlight it + if(feature._prevHighlighter && + feature._prevHighlighter != this.id) { + delete feature._lastHighlighter; + var control = this.map.getControl( + feature._prevHighlighter); + if(control) { + control.highlight(feature); + } + } else { + this.unhighlight(feature); + } + } + } else { + this.unselect(feature); + } + } + }, + + /** + * Method: highlight + * Redraw feature with the select style. + * + * Parameters: + * feature - {} + */ + highlight: function(feature) { + var layer = feature.layer; + var cont = this.events.triggerEvent("beforefeaturehighlighted", { + feature : feature + }); + if(cont !== false) { + feature._prevHighlighter = feature._lastHighlighter; + feature._lastHighlighter = this.id; + var style = this.selectStyle || this.renderIntent; + layer.drawFeature(feature, style); + this.events.triggerEvent("featurehighlighted", {feature : feature}); + } + }, + + /** + * Method: unhighlight + * Redraw feature with the "default" style + * + * Parameters: + * feature - {} + */ + unhighlight: function(feature) { + var layer = feature.layer; + // three cases: + // 1. there's no other highlighter, in that case _prev is undefined, + // and we just need to undef _last + // 2. another control highlighted the feature after we did it, in + // that case _last references this other control, and we just + // need to undef _prev + // 3. another control highlighted the feature before we did it, in + // that case _prev references this other control, and we need to + // set _last to _prev and undef _prev + if(feature._prevHighlighter == undefined) { + delete feature._lastHighlighter; + } else if(feature._prevHighlighter == this.id) { + delete feature._prevHighlighter; + } else { + feature._lastHighlighter = feature._prevHighlighter; + delete feature._prevHighlighter; + } + layer.drawFeature(feature, feature.style || feature.layer.style || + "default"); + this.events.triggerEvent("featureunhighlighted", {feature : feature}); + }, + + /** + * Method: select + * Add feature to the layer's selectedFeature array, render the feature as + * selected, and call the onSelect function. + * + * Parameters: + * feature - {} + */ + select: function(feature) { + var cont = this.onBeforeSelect.call(this.scope, feature); + var layer = feature.layer; + if(cont !== false) { + cont = layer.events.triggerEvent("beforefeatureselected", { + feature: feature + }); + if(cont !== false) { + layer.selectedFeatures.push(feature); + this.highlight(feature); + // if the feature handler isn't involved in the feature + // selection (because the box handler is used or the + // feature is selected programatically) we fake the + // feature handler to allow unselecting on click + if(!this.handlers.feature.lastFeature) { + this.handlers.feature.lastFeature = layer.selectedFeatures[0]; + } + layer.events.triggerEvent("featureselected", {feature: feature}); + this.onSelect.call(this.scope, feature); + } + } + }, + + /** + * Method: unselect + * Remove feature from the layer's selectedFeature array, render the feature as + * normal, and call the onUnselect function. + * + * Parameters: + * feature - {} + */ + unselect: function(feature) { + var layer = feature.layer; + // Store feature style for restoration later + this.unhighlight(feature); + OpenLayers.Util.removeItem(layer.selectedFeatures, feature); + layer.events.triggerEvent("featureunselected", {feature: feature}); + this.onUnselect.call(this.scope, feature); + }, + + /** + * Method: selectBox + * Callback from the handlers.box set up when selection is true + * on. + * + * Parameters: + * position - { || } + */ + selectBox: function(position) { + if (position instanceof OpenLayers.Bounds) { + var minXY = this.map.getLonLatFromPixel({ + x: position.left, + y: position.bottom + }); + var maxXY = this.map.getLonLatFromPixel({ + x: position.right, + y: position.top + }); + var bounds = new OpenLayers.Bounds( + minXY.lon, minXY.lat, maxXY.lon, maxXY.lat + ); + + // if multiple is false, first deselect currently selected features + if (!this.multipleSelect()) { + this.unselectAll(); + } + + // because we're using a box, we consider we want multiple selection + var prevMultiple = this.multiple; + this.multiple = true; + var layers = this.layers || [this.layer]; + this.events.triggerEvent("boxselectionstart", {layers: layers}); + var layer; + for(var l=0; l -1) { + if (bounds.toGeometry().intersects(feature.geometry)) { + if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) { + this.select(feature); + } + } + } + } + } + this.multiple = prevMultiple; + this.events.triggerEvent("boxselectionend", {layers: layers}); + } + }, + + /** + * Method: setMap + * Set the map property for the control. + * + * Parameters: + * map - {} + */ + setMap: function(map) { + this.handlers.feature.setMap(map); + if (this.box) { + this.handlers.box.setMap(map); + } + OpenLayers.Control.prototype.setMap.apply(this, arguments); + }, + + /** + * APIMethod: setLayer + * Attach a new layer to the control, overriding any existing layers. + * + * Parameters: + * layers - Array of {} or a single + * {} + */ + setLayer: function(layers) { + var isActive = this.active; + this.unselectAll(); + this.deactivate(); + if(this.layers) { + this.layer.destroy(); + this.layers = null; + } + this.initLayer(layers); + this.handlers.feature.layer = this.layer; + if (isActive) { + this.activate(); + } + }, + + CLASS_NAME: "OpenLayers.Control.SelectFeature" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/Snapping.js b/misc/openlayers/lib/OpenLayers/Control/Snapping.js new file mode 100644 index 0000000..2173114 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/Snapping.js @@ -0,0 +1,560 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Layer/Vector.js + */ + +/** + * Class: OpenLayers.Control.Snapping + * Acts as a snapping agent while editing vector features. + * + * Inherits from: + * - + */ +OpenLayers.Control.Snapping = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: events + * {} Events instance for listeners and triggering + * control specific events. + * + * Register a listener for a particular event with the following syntax: + * (code) + * control.events.register(type, obj, listener); + * (end) + * + * Supported event types (in addition to those from ): + * beforesnap - Triggered before a snap occurs. Listeners receive an + * event object with *point*, *x*, *y*, *distance*, *layer*, and + * *snapType* properties. The point property will be original point + * geometry considered for snapping. The x and y properties represent + * coordinates the point will receive. The distance is the distance + * of the snap. The layer is the target layer. The snapType property + * will be one of "node", "vertex", or "edge". Return false to stop + * snapping from occurring. + * snap - Triggered when a snap occurs. Listeners receive an event with + * *point*, *snapType*, *layer*, and *distance* properties. The point + * will be the location snapped to. The snapType will be one of "node", + * "vertex", or "edge". The layer will be the target layer. The + * distance will be the distance of the snap in map units. + * unsnap - Triggered when a vertex is unsnapped. Listeners receive an + * event with a *point* property. + */ + + /** + * CONSTANT: DEFAULTS + * Default target properties. + */ + DEFAULTS: { + tolerance: 10, + node: true, + edge: true, + vertex: true + }, + + /** + * Property: greedy + * {Boolean} Snap to closest feature in first layer with an eligible + * feature. Default is true. + */ + greedy: true, + + /** + * Property: precedence + * {Array} List representing precedence of different snapping types. + * Default is "node", "vertex", "edge". + */ + precedence: ["node", "vertex", "edge"], + + /** + * Property: resolution + * {Float} The map resolution for the previously considered snap. + */ + resolution: null, + + /** + * Property: geoToleranceCache + * {Object} A cache of geo-tolerances. Tolerance values (in map units) are + * calculated when the map resolution changes. + */ + geoToleranceCache: null, + + /** + * Property: layer + * {} The current editable layer. Set at + * construction or after construction with . + */ + layer: null, + + /** + * Property: feature + * {} The current editable feature. + */ + feature: null, + + /** + * Property: point + * {} The currently snapped vertex. + */ + point: null, + + /** + * Constructor: OpenLayers.Control.Snapping + * Creates a new snapping control. A control is constructed with an editable + * layer and a set of configuration objects for target layers. While the + * control is active, dragging vertices while drawing new features or + * modifying existing features on the editable layer will engage + * snapping to features on the target layers. Whether a vertex snaps to + * a feature on a target layer depends on the target layer configuration. + * + * Parameters: + * options - {Object} An object containing all configuration properties for + * the control. + * + * Valid options: + * layer - {} The editable layer. Features from this + * layer that are digitized or modified may have vertices snapped to + * features from any of the target layers. + * targets - {Array(Object | OpenLayers.Layer.Vector)} A list of objects for + * configuring target layers. See valid properties of the target + * objects below. If the items in the targets list are vector layers + * (instead of configuration objects), the defaults from the + * property will apply. The editable layer itself may be a target + * layer, allowing newly created or edited features to be snapped to + * existing features from the same layer. If no targets are provided + * the layer given in the constructor (as ) will become the + * initial target. + * defaults - {Object} An object with default properties to be applied + * to all target objects. + * greedy - {Boolean} Snap to closest feature in first target layer that + * applies. Default is true. If false, all features in all target + * layers will be checked and the closest feature in all target layers + * will be chosen. The greedy property determines if the order of the + * target layers is significant. By default, the order of the target + * layers is significant where layers earlier in the target layer list + * have precedence over layers later in the list. Within a single + * layer, the closest feature is always chosen for snapping. This + * property only determines whether the search for a closer feature + * continues after an eligible feature is found in a target layer. + * + * Valid target properties: + * layer - {} A target layer. Features from this + * layer will be eligible to act as snapping target for the editable + * layer. + * tolerance - {Float} The distance (in pixels) at which snapping may occur. + * Default is 10. + * node - {Boolean} Snap to nodes (first or last point in a geometry) in + * target layer. Default is true. + * nodeTolerance - {Float} Optional distance at which snapping may occur + * for nodes specifically. If none is provided, will be + * used. + * vertex - {Boolean} Snap to vertices in target layer. Default is true. + * vertexTolerance - {Float} Optional distance at which snapping may occur + * for vertices specifically. If none is provided, will be + * used. + * edge - {Boolean} Snap to edges in target layer. Default is true. + * edgeTolerance - {Float} Optional distance at which snapping may occur + * for edges specifically. If none is provided, will be + * used. + * filter - {} Optional filter to evaluate to determine if + * feature is eligible for snapping. If filter evaluates to true for a + * target feature a vertex may be snapped to the feature. + * minResolution - {Number} If a minResolution is provided, snapping to this + * target will only be considered if the map resolution is greater than + * or equal to this value (the minResolution is inclusive). Default is + * no minimum resolution limit. + * maxResolution - {Number} If a maxResolution is provided, snapping to this + * target will only be considered if the map resolution is strictly + * less than this value (the maxResolution is exclusive). Default is + * no maximum resolution limit. + */ + initialize: function(options) { + OpenLayers.Control.prototype.initialize.apply(this, [options]); + this.options = options || {}; // TODO: this could be done by the super + + // set the editable layer if provided + if(this.options.layer) { + this.setLayer(this.options.layer); + } + // configure target layers + var defaults = OpenLayers.Util.extend({}, this.options.defaults); + this.defaults = OpenLayers.Util.applyDefaults(defaults, this.DEFAULTS); + this.setTargets(this.options.targets); + if(this.targets.length === 0 && this.layer) { + this.addTargetLayer(this.layer); + } + + this.geoToleranceCache = {}; + }, + + /** + * APIMethod: setLayer + * Set the editable layer. Call the setLayer method if the editable layer + * changes and the same control should be used on a new editable layer. + * If the control is already active, it will be active after the new + * layer is set. + * + * Parameters: + * layer - {} The new editable layer. + */ + setLayer: function(layer) { + if(this.active) { + this.deactivate(); + this.layer = layer; + this.activate(); + } else { + this.layer = layer; + } + }, + + /** + * Method: setTargets + * Set the targets for the snapping agent. + * + * Parameters: + * targets - {Array} An array of target configs or target layers. + */ + setTargets: function(targets) { + this.targets = []; + if(targets && targets.length) { + var target; + for(var i=0, len=targets.length; i} A target layer. + */ + addTargetLayer: function(layer) { + this.addTarget({layer: layer}); + }, + + /** + * Method: addTarget + * Add a configured target layer. + * + * Parameters: + * target - {Object} A target config. + */ + addTarget: function(target) { + target = OpenLayers.Util.applyDefaults(target, this.defaults); + target.nodeTolerance = target.nodeTolerance || target.tolerance; + target.vertexTolerance = target.vertexTolerance || target.tolerance; + target.edgeTolerance = target.edgeTolerance || target.tolerance; + this.targets.push(target); + }, + + /** + * Method: removeTargetLayer + * Remove a target layer. + * + * Parameters: + * layer - {} The target layer to remove. + */ + removeTargetLayer: function(layer) { + var target; + for(var i=this.targets.length-1; i>=0; --i) { + target = this.targets[i]; + if(target.layer === layer) { + this.removeTarget(target); + } + } + }, + + /** + * Method: removeTarget + * Remove a target. + * + * Parameters: + * target - {Object} A target config. + * + * Returns: + * {Array} The targets array. + */ + removeTarget: function(target) { + return OpenLayers.Util.removeItem(this.targets, target); + }, + + /** + * APIMethod: activate + * Activate the control. Activating the control registers listeners for + * editing related events so that during feature creation and + * modification, moving vertices will trigger snapping. + */ + activate: function() { + var activated = OpenLayers.Control.prototype.activate.call(this); + if(activated) { + if(this.layer && this.layer.events) { + this.layer.events.on({ + sketchstarted: this.onSketchModified, + sketchmodified: this.onSketchModified, + vertexmodified: this.onVertexModified, + scope: this + }); + } + } + return activated; + }, + + /** + * APIMethod: deactivate + * Deactivate the control. Deactivating the control unregisters listeners + * so feature editing may proceed without engaging the snapping agent. + */ + deactivate: function() { + var deactivated = OpenLayers.Control.prototype.deactivate.call(this); + if(deactivated) { + if(this.layer && this.layer.events) { + this.layer.events.un({ + sketchstarted: this.onSketchModified, + sketchmodified: this.onSketchModified, + vertexmodified: this.onVertexModified, + scope: this + }); + } + } + this.feature = null; + this.point = null; + return deactivated; + }, + + /** + * Method: onSketchModified + * Registered as a listener for the sketchmodified event on the editable + * layer. + * + * Parameters: + * event - {Object} The sketch modified event. + */ + onSketchModified: function(event) { + this.feature = event.feature; + this.considerSnapping(event.vertex, event.vertex); + }, + + /** + * Method: onVertexModified + * Registered as a listener for the vertexmodified event on the editable + * layer. + * + * Parameters: + * event - {Object} The vertex modified event. + */ + onVertexModified: function(event) { + this.feature = event.feature; + var loc = this.layer.map.getLonLatFromViewPortPx(event.pixel); + this.considerSnapping( + event.vertex, new OpenLayers.Geometry.Point(loc.lon, loc.lat) + ); + }, + + /** + * Method: considerSnapping + * + * Parameters: + * point - {} The vertex to be snapped (or + * unsnapped). + * loc - {} The location of the mouse in map + * coords. + */ + considerSnapping: function(point, loc) { + var best = { + rank: Number.POSITIVE_INFINITY, + dist: Number.POSITIVE_INFINITY, + x: null, y: null + }; + var snapped = false; + var result, target; + for(var i=0, len=this.targets.length; i} The location of the mouse in map + * coords. + * + * Returns: + * {Object} A result object with rank, dist, x, and y properties. + * Returns null if candidate is not eligible for snapping. + */ + testTarget: function(target, loc) { + var resolution = this.layer.map.getResolution(); + if ("minResolution" in target) { + if (resolution < target.minResolution) { + return null; + } + } + if ("maxResolution" in target) { + if (resolution >= target.maxResolution) { + return null; + } + } + var tolerance = { + node: this.getGeoTolerance(target.nodeTolerance, resolution), + vertex: this.getGeoTolerance(target.vertexTolerance, resolution), + edge: this.getGeoTolerance(target.edgeTolerance, resolution) + }; + // this could be cached if we don't support setting tolerance values directly + var maxTolerance = Math.max( + tolerance.node, tolerance.vertex, tolerance.edge + ); + var result = { + rank: Number.POSITIVE_INFINITY, dist: Number.POSITIVE_INFINITY + }; + var eligible = false; + var features = target.layer.features; + var feature, type, vertices, vertex, closest, dist, found; + var numTypes = this.precedence.length; + var ll = new OpenLayers.LonLat(loc.x, loc.y); + for(var i=0, len=features.length; i when the map resolution + * has not changed. + * + * Parameters: + * tolerance - {Number} A tolerance value in pixels. + * resolution - {Number} Map resolution. + * + * Returns: + * {Number} A tolerance value in map units. + */ + getGeoTolerance: function(tolerance, resolution) { + if(resolution !== this.resolution) { + this.resolution = resolution; + this.geoToleranceCache = {}; + } + var geoTolerance = this.geoToleranceCache[tolerance]; + if(geoTolerance === undefined) { + geoTolerance = tolerance * resolution; + this.geoToleranceCache[tolerance] = geoTolerance; + } + return geoTolerance; + }, + + /** + * Method: destroy + * Clean up the control. + */ + destroy: function() { + if(this.active) { + this.deactivate(); // TODO: this should be handled by the super + } + delete this.layer; + delete this.targets; + OpenLayers.Control.prototype.destroy.call(this); + }, + + CLASS_NAME: "OpenLayers.Control.Snapping" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/Split.js b/misc/openlayers/lib/OpenLayers/Control/Split.js new file mode 100644 index 0000000..de19eb7 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/Split.js @@ -0,0 +1,494 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Handler/Path.js + * @requires OpenLayers/Layer/Vector.js + */ + +/** + * Class: OpenLayers.Control.Split + * Acts as a split feature agent while editing vector features. + * + * Inherits from: + * - + */ +OpenLayers.Control.Split = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: events + * {} Events instance for listeners and triggering + * control specific events. + * + * Register a listener for a particular event with the following syntax: + * (code) + * control.events.register(type, obj, listener); + * (end) + * + * Supported event types (in addition to those from ): + * beforesplit - Triggered before a split occurs. Listeners receive an + * event object with *source* and *target* properties. + * split - Triggered when a split occurs. Listeners receive an event with + * an *original* property and a *features* property. The original + * is a reference to the target feature that the sketch or modified + * feature intersects. The features property is a list of all features + * that result from this single split. This event is triggered before + * the resulting features are added to the layer (while the layer still + * has a reference to the original). + * aftersplit - Triggered after all splits resulting from a single sketch + * or feature modification have occurred. The original features + * have been destroyed and features that result from the split + * have already been added to the layer. Listeners receive an event + * with a *source* and *features* property. The source references the + * sketch or modified feature used as a splitter. The features + * property is a list of all resulting features. + */ + + /** + * APIProperty: layer + * {} The target layer with features to be split. + * Set at construction or after construction with . + */ + layer: null, + + /** + * Property: source + * {} Optional source layer. Any newly created + * or modified features from this layer will be used to split features + * on the target layer. If not provided, a temporary sketch layer will + * be created. + */ + source: null, + + /** + * Property: sourceOptions + * {Options} If a temporary sketch layer is created, these layer options + * will be applied. + */ + sourceOptions: null, + + /** + * APIProperty: tolerance + * {Number} Distance between the calculated intersection and a vertex on + * the source geometry below which the existing vertex will be used + * for the split. Default is null. + */ + tolerance: null, + + /** + * APIProperty: edge + * {Boolean} Allow splits given intersection of edges only. Default is + * true. If false, a vertex on the source must be within the + * distance of the calculated intersection for a split + * to occur. + */ + edge: true, + + /** + * APIProperty: deferDelete + * {Boolean} Instead of removing features from the layer, set feature + * states of split features to DELETE. This assumes a save strategy + * or other component is in charge of removing features from the + * layer. Default is false. If false, split features will be + * immediately deleted from the layer. + */ + deferDelete: false, + + /** + * APIProperty: mutual + * {Boolean} If source and target layers are the same, split source + * features and target features where they intersect. Default is + * true. If false, only target features will be split. + */ + mutual: true, + + /** + * APIProperty: targetFilter + * {} Optional filter that will be evaluated + * to determine if a feature from the target layer is eligible for + * splitting. + */ + targetFilter: null, + + /** + * APIProperty: sourceFilter + * {} Optional filter that will be evaluated + * to determine if a feature from the source layer is eligible for + * splitting. + */ + sourceFilter: null, + + /** + * Property: handler + * {} The temporary sketch handler created if + * no source layer is provided. + */ + handler: null, + + /** + * Constructor: OpenLayers.Control.Split + * Creates a new split control. A control is constructed with a target + * layer and an optional source layer. While the control is active, + * creating new features or modifying existing features on the source + * layer will result in splitting any eligible features on the target + * layer. If no source layer is provided, a temporary sketch layer will + * be created to create lines for splitting features on the target. + * + * Parameters: + * options - {Object} An object containing all configuration properties for + * the control. + * + * Valid options: + * layer - {} The target layer. Features from this + * layer will be split by new or modified features on the source layer + * or temporary sketch layer. + * source - {} Optional source layer. If provided + * newly created features or modified features will be used to split + * features on the target layer. If not provided, a temporary sketch + * layer will be created for drawing lines. + * tolerance - {Number} Optional value for the distance between a source + * vertex and the calculated intersection below which the split will + * occur at the vertex. + * edge - {Boolean} Allow splits given intersection of edges only. Default + * is true. If false, a vertex on the source must be within the + * distance of the calculated intersection for a split + * to occur. + * mutual - {Boolean} If source and target are the same, split source + * features and target features where they intersect. Default is + * true. If false, only target features will be split. + * targetFilter - {} Optional filter that will be evaluated + * to determine if a feature from the target layer is eligible for + * splitting. + * sourceFilter - {} Optional filter that will be evaluated + * to determine if a feature from the target layer is eligible for + * splitting. + */ + initialize: function(options) { + OpenLayers.Control.prototype.initialize.apply(this, [options]); + this.options = options || {}; // TODO: this could be done by the super + + // set the source layer if provided + if(this.options.source) { + this.setSource(this.options.source); + } + }, + + /** + * APIMethod: setSource + * Set the source layer for edits layer. + * + * Parameters: + * layer - {} The new source layer layer. If + * null, a temporary sketch layer will be created. + */ + setSource: function(layer) { + if(this.active) { + this.deactivate(); + if(this.handler) { + this.handler.destroy(); + delete this.handler; + } + this.source = layer; + this.activate(); + } else { + this.source = layer; + } + }, + + /** + * APIMethod: activate + * Activate the control. Activating the control registers listeners for + * editing related events so that during feature creation and + * modification, features in the target will be considered for + * splitting. + */ + activate: function() { + var activated = OpenLayers.Control.prototype.activate.call(this); + if(activated) { + if(!this.source) { + if(!this.handler) { + this.handler = new OpenLayers.Handler.Path(this, + {done: function(geometry) { + this.onSketchComplete({ + feature: new OpenLayers.Feature.Vector(geometry) + }); + }}, + {layerOptions: this.sourceOptions} + ); + } + this.handler.activate(); + } else if(this.source.events) { + this.source.events.on({ + sketchcomplete: this.onSketchComplete, + afterfeaturemodified: this.afterFeatureModified, + scope: this + }); + } + } + return activated; + }, + + /** + * APIMethod: deactivate + * Deactivate the control. Deactivating the control unregisters listeners + * so feature editing may proceed without engaging the split agent. + */ + deactivate: function() { + var deactivated = OpenLayers.Control.prototype.deactivate.call(this); + if(deactivated) { + if(this.source && this.source.events) { + this.source.events.un({ + sketchcomplete: this.onSketchComplete, + afterfeaturemodified: this.afterFeatureModified, + scope: this + }); + } + } + return deactivated; + }, + + /** + * Method: onSketchComplete + * Registered as a listener for the sketchcomplete event on the editable + * layer. + * + * Parameters: + * event - {Object} The sketch complete event. + * + * Returns: + * {Boolean} Stop the sketch from being added to the layer (it has been + * split). + */ + onSketchComplete: function(event) { + this.feature = null; + return !this.considerSplit(event.feature); + }, + + /** + * Method: afterFeatureModified + * Registered as a listener for the afterfeaturemodified event on the + * editable layer. + * + * Parameters: + * event - {Object} The after feature modified event. + */ + afterFeatureModified: function(event) { + if(event.modified) { + var feature = event.feature; + if (typeof feature.geometry.split === "function") { + this.feature = event.feature; + this.considerSplit(event.feature); + } + } + }, + + /** + * Method: removeByGeometry + * Remove a feature from a list based on the given geometry. + * + * Parameters: + * features - {Array()} A list of features. + * geometry - {} A geometry. + */ + removeByGeometry: function(features, geometry) { + for(var i=0, len=features.length; i} The target feature. + * + * Returns: + * {Boolean} The target is eligible for splitting. + */ + isEligible: function(target) { + if (!target.geometry) { + return false; + } else { + return ( + target.state !== OpenLayers.State.DELETE + ) && ( + typeof target.geometry.split === "function" + ) && ( + this.feature !== target + ) && ( + !this.targetFilter || + this.targetFilter.evaluate(target.attributes) + ); + } + }, + + /** + * Method: considerSplit + * Decide whether or not to split target features with the supplied + * feature. If is true, both the source and target features + * will be split if eligible. + * + * Parameters: + * feature - {} The newly created or modified + * feature. + * + * Returns: + * {Boolean} The supplied feature was split (and destroyed). + */ + considerSplit: function(feature) { + var sourceSplit = false; + var targetSplit = false; + if(!this.sourceFilter || + this.sourceFilter.evaluate(feature.attributes)) { + var features = this.layer && this.layer.features || []; + var target, results, proceed; + var additions = [], removals = []; + var mutual = (this.layer === this.source) && this.mutual; + var options = { + edge: this.edge, + tolerance: this.tolerance, + mutual: mutual + }; + var sourceParts = [feature.geometry]; + var targetFeature, targetParts; + var source, parts; + for(var i=0, len=features.length; i 1) { + // splice in new source parts + parts.unshift(j, 1); // add args for splice below + Array.prototype.splice.apply(sourceParts, parts); + j += parts.length - 3; + } + results = results[1]; + } + // handle parts that result from target splitting + if(results.length > 1) { + // splice in new target parts + results.unshift(k, 1); // add args for splice below + Array.prototype.splice.apply(targetParts, results); + k += results.length - 3; + } + } + } + } + } + } + if(targetParts && targetParts.length > 1) { + this.geomsToFeatures(targetFeature, targetParts); + this.events.triggerEvent("split", { + original: targetFeature, + features: targetParts + }); + Array.prototype.push.apply(additions, targetParts); + removals.push(targetFeature); + targetSplit = true; + } + } + } + if(sourceParts && sourceParts.length > 1) { + this.geomsToFeatures(feature, sourceParts); + this.events.triggerEvent("split", { + original: feature, + features: sourceParts + }); + Array.prototype.push.apply(additions, sourceParts); + removals.push(feature); + sourceSplit = true; + } + if(sourceSplit || targetSplit) { + // remove and add feature events are suppressed + // listen for split event on this control instead + if(this.deferDelete) { + // Set state instead of removing. Take care to avoid + // setting delete for features that have not yet been + // inserted - those should be destroyed immediately. + var feat, destroys = []; + for(var i=0, len=removals.length; i} The feature to be cloned. + * geoms - {Array()} List of goemetries. This will + * become a list of new features. + */ + geomsToFeatures: function(feature, geoms) { + var clone = feature.clone(); + delete clone.geometry; + var newFeature; + for(var i=0, len=geoms.length; i constructor. + * + * If you’re only targeting touch enabled devices with your mapping application, + * you can create a map with only a TouchNavigation control. The + * control is mobile ready by default, but + * you can generate a smaller build of the library by only including this + * touch navigation control if you aren't concerned about mouse interaction. + * + * Inherits: + * - + */ +OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, { + + /** + * Property: dragPan + * {} + */ + dragPan: null, + + /** + * APIProperty: dragPanOptions + * {Object} Options passed to the DragPan control. + */ + dragPanOptions: null, + + /** + * Property: pinchZoom + * {} + */ + pinchZoom: null, + + /** + * APIProperty: pinchZoomOptions + * {Object} Options passed to the PinchZoom control. + */ + pinchZoomOptions: null, + + /** + * APIProperty: clickHandlerOptions + * {Object} Options passed to the Click handler. + */ + clickHandlerOptions: null, + + /** + * APIProperty: documentDrag + * {Boolean} Allow panning of the map by dragging outside map viewport. + * Default is false. + */ + documentDrag: false, + + /** + * APIProperty: autoActivate + * {Boolean} Activate the control when it is added to a map. Default is + * true. + */ + autoActivate: true, + + /** + * Constructor: OpenLayers.Control.TouchNavigation + * Create a new navigation control + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * the control + */ + initialize: function(options) { + this.handlers = {}; + OpenLayers.Control.prototype.initialize.apply(this, arguments); + }, + + /** + * Method: destroy + * The destroy method is used to perform any clean up before the control + * is dereferenced. Typically this is where event listeners are removed + * to prevent memory leaks. + */ + destroy: function() { + this.deactivate(); + if(this.dragPan) { + this.dragPan.destroy(); + } + this.dragPan = null; + if (this.pinchZoom) { + this.pinchZoom.destroy(); + delete this.pinchZoom; + } + OpenLayers.Control.prototype.destroy.apply(this,arguments); + }, + + /** + * Method: activate + */ + activate: function() { + if(OpenLayers.Control.prototype.activate.apply(this,arguments)) { + this.dragPan.activate(); + this.handlers.click.activate(); + this.pinchZoom.activate(); + return true; + } + return false; + }, + + /** + * Method: deactivate + */ + deactivate: function() { + if(OpenLayers.Control.prototype.deactivate.apply(this,arguments)) { + this.dragPan.deactivate(); + this.handlers.click.deactivate(); + this.pinchZoom.deactivate(); + return true; + } + return false; + }, + + /** + * Method: draw + */ + draw: function() { + var clickCallbacks = { + click: this.defaultClick, + dblclick: this.defaultDblClick + }; + var clickOptions = OpenLayers.Util.extend({ + "double": true, + stopDouble: true, + pixelTolerance: 2 + }, this.clickHandlerOptions); + this.handlers.click = new OpenLayers.Handler.Click( + this, clickCallbacks, clickOptions + ); + this.dragPan = new OpenLayers.Control.DragPan( + OpenLayers.Util.extend({ + map: this.map, + documentDrag: this.documentDrag + }, this.dragPanOptions) + ); + this.dragPan.draw(); + this.pinchZoom = new OpenLayers.Control.PinchZoom( + OpenLayers.Util.extend({map: this.map}, this.pinchZoomOptions) + ); + }, + + /** + * Method: defaultClick + * + * Parameters: + * evt - {Event} + */ + defaultClick: function (evt) { + if(evt.lastTouches && evt.lastTouches.length == 2) { + this.map.zoomOut(); + } + }, + + /** + * Method: defaultDblClick + * + * Parameters: + * evt - {Event} + */ + defaultDblClick: function (evt) { + this.map.zoomTo(this.map.zoom + 1, evt.xy); + }, + + CLASS_NAME: "OpenLayers.Control.TouchNavigation" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/TransformFeature.js b/misc/openlayers/lib/OpenLayers/Control/TransformFeature.js new file mode 100644 index 0000000..8c21456 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/TransformFeature.js @@ -0,0 +1,624 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Control/DragFeature.js + * @requires OpenLayers/Feature/Vector.js + * @requires OpenLayers/Geometry/LineString.js + * @requires OpenLayers/Geometry/Point.js + */ + +/** + * Class: OpenLayers.Control.TransformFeature + * Control to transform features with a standard transformation box. + * + * Inherits From: + * - + */ +OpenLayers.Control.TransformFeature = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: events + * {} Events instance for listeners and triggering + * control specific events. + * + * Register a listener for a particular event with the following syntax: + * (code) + * control.events.register(type, obj, listener); + * (end) + * + * Supported event types (in addition to those from ): + * beforesetfeature - Triggered before a feature is set for + * tranformation. The feature will not be set if a listener returns + * false. Listeners receive a *feature* property, with the feature + * that will be set for transformation. Listeners are allowed to + * set the control's *scale*, *ratio* and *rotation* properties, + * which will set the initial scale, ratio and rotation of the + * feature, like the method's initialParams argument. + * setfeature - Triggered when a feature is set for tranformation. + * Listeners receive a *feature* property, with the feature that + * is now set for transformation. + * beforetransform - Triggered while dragging, before a feature is + * transformed. The feature will not be transformed if a listener + * returns false (but the box still will). Listeners receive one or + * more of *center*, *scale*, *ratio* and *rotation*. The *center* + * property is an object with the new + * center of the transformed feature, the others are Floats with the + * scale, ratio or rotation change since the last transformation. + * transform - Triggered while dragging, when a feature is transformed. + * Listeners receive an event object with one or more of *center*, + * scale*, *ratio* and *rotation*. The *center* property is an + * object with the new center of the + * transformed feature, the others are Floats with the scale, ratio + * or rotation change of the feature since the last transformation. + * transformcomplete - Triggered after dragging. Listeners receive + * an event object with the transformed *feature*. + */ + + /** + * APIProperty: geometryTypes + * {Array(String)} To restrict transformation to a limited set of geometry + * types, send a list of strings corresponding to the geometry class + * names. + */ + geometryTypes: null, + + /** + * Property: layer + * {} + */ + layer: null, + + /** + * APIProperty: preserveAspectRatio + * {Boolean} set to true to not change the feature's aspect ratio. + */ + preserveAspectRatio: false, + + /** + * APIProperty: rotate + * {Boolean} set to false if rotation should be disabled. Default is true. + * To be passed with the constructor or set when the control is not + * active. + */ + rotate: true, + + /** + * APIProperty: feature + * {} Feature currently available for + * transformation. Read-only, use to set it manually. + */ + feature: null, + + /** + * APIProperty: renderIntent + * {String|Object} Render intent for the transformation box and + * handles. A symbolizer object can also be provided here. + */ + renderIntent: "temporary", + + /** + * APIProperty: rotationHandleSymbolizer + * {Object|String} Optional. A custom symbolizer for the rotation handles. + * A render intent can also be provided here. Defaults to + * (code) + * { + * stroke: false, + * pointRadius: 10, + * fillOpacity: 0, + * cursor: "pointer" + * } + * (end) + */ + rotationHandleSymbolizer: null, + + /** + * APIProperty: box + * {} The transformation box rectangle. + * Read-only. + */ + box: null, + + /** + * APIProperty: center + * {} The center of the feature bounds. + * Read-only. + */ + center: null, + + /** + * APIProperty: scale + * {Float} The scale of the feature, relative to the scale the time the + * feature was set. Read-only, except for *beforesetfeature* + * listeners. + */ + scale: 1, + + /** + * APIProperty: ratio + * {Float} The ratio of the feature relative to the ratio the time the + * feature was set. Read-only, except for *beforesetfeature* + * listeners. + */ + ratio: 1, + + /** + * Property: rotation + * {Integer} the current rotation angle of the box. Read-only, except for + * *beforesetfeature* listeners. + */ + rotation: 0, + + /** + * APIProperty: handles + * {Array()} The 8 handles currently available + * for scaling/resizing. Numbered counterclockwise, starting from the + * southwest corner. Read-only. + */ + handles: null, + + /** + * APIProperty: rotationHandles + * {Array()} The 4 rotation handles currently + * available for rotating. Numbered counterclockwise, starting from + * the southwest corner. Read-only. + */ + rotationHandles: null, + + /** + * Property: dragControl + * {} + */ + dragControl: null, + + /** + * APIProperty: irregular + * {Boolean} Make scaling/resizing work irregularly. If true then + * dragging a handle causes the feature to resize in the direction + * of movement. If false then the feature resizes symetrically + * about it's center. + */ + irregular: false, + + /** + * Constructor: OpenLayers.Control.TransformFeature + * Create a new transform feature control. + * + * Parameters: + * layer - {} Layer that contains features that + * will be transformed. + * options - {Object} Optional object whose properties will be set on the + * control. + */ + initialize: function(layer, options) { + OpenLayers.Control.prototype.initialize.apply(this, [options]); + + this.layer = layer; + + if(!this.rotationHandleSymbolizer) { + this.rotationHandleSymbolizer = { + stroke: false, + pointRadius: 10, + fillOpacity: 0, + cursor: "pointer" + }; + } + + this.createBox(); + this.createControl(); + }, + + /** + * APIMethod: activate + * Activates the control. + */ + activate: function() { + var activated = false; + if(OpenLayers.Control.prototype.activate.apply(this, arguments)) { + this.dragControl.activate(); + this.layer.addFeatures([this.box]); + this.rotate && this.layer.addFeatures(this.rotationHandles); + this.layer.addFeatures(this.handles); + activated = true; + } + return activated; + }, + + /** + * APIMethod: deactivate + * Deactivates the control. + */ + deactivate: function() { + var deactivated = false; + if(OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { + this.layer.removeFeatures(this.handles); + this.rotate && this.layer.removeFeatures(this.rotationHandles); + this.layer.removeFeatures([this.box]); + this.dragControl.deactivate(); + deactivated = true; + } + return deactivated; + }, + + /** + * Method: setMap + * + * Parameters: + * map - {} + */ + setMap: function(map) { + this.dragControl.setMap(map); + OpenLayers.Control.prototype.setMap.apply(this, arguments); + }, + + /** + * APIMethod: setFeature + * Place the transformation box on a feature and start transforming it. + * If the control is not active, it will be activated. + * + * Parameters: + * feature - {} + * initialParams - {Object} Initial values for rotation, scale or ratio. + * Setting a rotation value here will cause the transformation box to + * start rotated. Setting a scale or ratio will not affect the + * transormation box, but applications may use this to keep track of + * scale and ratio of a feature across multiple transforms. + */ + setFeature: function(feature, initialParams) { + initialParams = OpenLayers.Util.applyDefaults(initialParams, { + rotation: 0, + scale: 1, + ratio: 1 + }); + + var oldRotation = this.rotation; + var oldCenter = this.center; + OpenLayers.Util.extend(this, initialParams); + + var cont = this.events.triggerEvent("beforesetfeature", + {feature: feature} + ); + if (cont === false) { + return; + } + + this.feature = feature; + this.activate(); + + this._setfeature = true; + + var featureBounds = this.feature.geometry.getBounds(); + this.box.move(featureBounds.getCenterLonLat()); + this.box.geometry.rotate(-oldRotation, oldCenter); + this._angle = 0; + + var ll; + if(this.rotation) { + var geom = feature.geometry.clone(); + geom.rotate(-this.rotation, this.center); + var box = new OpenLayers.Feature.Vector( + geom.getBounds().toGeometry()); + box.geometry.rotate(this.rotation, this.center); + this.box.geometry.rotate(this.rotation, this.center); + this.box.move(box.geometry.getBounds().getCenterLonLat()); + var llGeom = box.geometry.components[0].components[0]; + ll = llGeom.getBounds().getCenterLonLat(); + } else { + ll = new OpenLayers.LonLat(featureBounds.left, featureBounds.bottom); + } + this.handles[0].move(ll); + + delete this._setfeature; + + this.events.triggerEvent("setfeature", {feature: feature}); + }, + + /** + * APIMethod: unsetFeature + * Remove the transformation box off any feature. + * If the control is active, it will be deactivated first. + */ + unsetFeature: function() { + if (this.active) { + this.deactivate(); + } else { + this.feature = null; + this.rotation = 0; + this.scale = 1; + this.ratio = 1; + } + }, + + /** + * Method: createBox + * Creates the box with all handles and transformation handles. + */ + createBox: function() { + var control = this; + + this.center = new OpenLayers.Geometry.Point(0, 0); + this.box = new OpenLayers.Feature.Vector( + new OpenLayers.Geometry.LineString([ + new OpenLayers.Geometry.Point(-1, -1), + new OpenLayers.Geometry.Point(0, -1), + new OpenLayers.Geometry.Point(1, -1), + new OpenLayers.Geometry.Point(1, 0), + new OpenLayers.Geometry.Point(1, 1), + new OpenLayers.Geometry.Point(0, 1), + new OpenLayers.Geometry.Point(-1, 1), + new OpenLayers.Geometry.Point(-1, 0), + new OpenLayers.Geometry.Point(-1, -1) + ]), null, + typeof this.renderIntent == "string" ? null : this.renderIntent + ); + + // Override for box move - make sure that the center gets updated + this.box.geometry.move = function(x, y) { + control._moving = true; + OpenLayers.Geometry.LineString.prototype.move.apply(this, arguments); + control.center.move(x, y); + delete control._moving; + }; + + // Overrides for vertex move, resize and rotate - make sure that + // handle and rotationHandle geometries are also moved, resized and + // rotated. + var vertexMoveFn = function(x, y) { + OpenLayers.Geometry.Point.prototype.move.apply(this, arguments); + this._rotationHandle && this._rotationHandle.geometry.move(x, y); + this._handle.geometry.move(x, y); + }; + var vertexResizeFn = function(scale, center, ratio) { + OpenLayers.Geometry.Point.prototype.resize.apply(this, arguments); + this._rotationHandle && this._rotationHandle.geometry.resize( + scale, center, ratio); + this._handle.geometry.resize(scale, center, ratio); + }; + var vertexRotateFn = function(angle, center) { + OpenLayers.Geometry.Point.prototype.rotate.apply(this, arguments); + this._rotationHandle && this._rotationHandle.geometry.rotate( + angle, center); + this._handle.geometry.rotate(angle, center); + }; + + // Override for handle move - make sure that the box and other handles + // are updated, and finally transform the feature. + var handleMoveFn = function(x, y) { + var oldX = this.x, oldY = this.y; + OpenLayers.Geometry.Point.prototype.move.call(this, x, y); + if(control._moving) { + return; + } + var evt = control.dragControl.handlers.drag.evt; + var preserveAspectRatio = !control._setfeature && + control.preserveAspectRatio; + var reshape = !preserveAspectRatio && !(evt && evt.shiftKey); + var oldGeom = new OpenLayers.Geometry.Point(oldX, oldY); + var centerGeometry = control.center; + this.rotate(-control.rotation, centerGeometry); + oldGeom.rotate(-control.rotation, centerGeometry); + var dx1 = this.x - centerGeometry.x; + var dy1 = this.y - centerGeometry.y; + var dx0 = dx1 - (this.x - oldGeom.x); + var dy0 = dy1 - (this.y - oldGeom.y); + if (control.irregular && !control._setfeature) { + dx1 -= (this.x - oldGeom.x) / 2; + dy1 -= (this.y - oldGeom.y) / 2; + } + this.x = oldX; + this.y = oldY; + var scale, ratio = 1; + if (reshape) { + scale = Math.abs(dy0) < 0.00001 ? 1 : dy1 / dy0; + ratio = (Math.abs(dx0) < 0.00001 ? 1 : (dx1 / dx0)) / scale; + } else { + var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0)); + var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1)); + scale = l1 / l0; + } + + // rotate the box to 0 before resizing - saves us some + // calculations and is inexpensive because we don't drawFeature. + control._moving = true; + control.box.geometry.rotate(-control.rotation, centerGeometry); + delete control._moving; + + control.box.geometry.resize(scale, centerGeometry, ratio); + control.box.geometry.rotate(control.rotation, centerGeometry); + control.transformFeature({scale: scale, ratio: ratio}); + if (control.irregular && !control._setfeature) { + var newCenter = centerGeometry.clone(); + newCenter.x += Math.abs(oldX - centerGeometry.x) < 0.00001 ? 0 : (this.x - oldX); + newCenter.y += Math.abs(oldY - centerGeometry.y) < 0.00001 ? 0 : (this.y - oldY); + control.box.geometry.move(this.x - oldX, this.y - oldY); + control.transformFeature({center: newCenter}); + } + }; + + // Override for rotation handle move - make sure that the box and + // other handles are updated, and finally transform the feature. + var rotationHandleMoveFn = function(x, y){ + var oldX = this.x, oldY = this.y; + OpenLayers.Geometry.Point.prototype.move.call(this, x, y); + if(control._moving) { + return; + } + var evt = control.dragControl.handlers.drag.evt; + var constrain = (evt && evt.shiftKey) ? 45 : 1; + var centerGeometry = control.center; + var dx1 = this.x - centerGeometry.x; + var dy1 = this.y - centerGeometry.y; + var dx0 = dx1 - x; + var dy0 = dy1 - y; + this.x = oldX; + this.y = oldY; + var a0 = Math.atan2(dy0, dx0); + var a1 = Math.atan2(dy1, dx1); + var angle = a1 - a0; + angle *= 180 / Math.PI; + control._angle = (control._angle + angle) % 360; + var diff = control.rotation % constrain; + if(Math.abs(control._angle) >= constrain || diff !== 0) { + angle = Math.round(control._angle / constrain) * constrain - + diff; + control._angle = 0; + control.box.geometry.rotate(angle, centerGeometry); + control.transformFeature({rotation: angle}); + } + }; + + var handles = new Array(8); + var rotationHandles = new Array(4); + var geom, handle, rotationHandle; + var positions = ["sw", "s", "se", "e", "ne", "n", "nw", "w"]; + for(var i=0; i<8; ++i) { + geom = this.box.geometry.components[i]; + handle = new OpenLayers.Feature.Vector(geom.clone(), { + role: positions[i] + "-resize" + }, typeof this.renderIntent == "string" ? null : + this.renderIntent); + if(i % 2 == 0) { + rotationHandle = new OpenLayers.Feature.Vector(geom.clone(), { + role: positions[i] + "-rotate" + }, typeof this.rotationHandleSymbolizer == "string" ? + null : this.rotationHandleSymbolizer); + rotationHandle.geometry.move = rotationHandleMoveFn; + geom._rotationHandle = rotationHandle; + rotationHandles[i/2] = rotationHandle; + } + geom.move = vertexMoveFn; + geom.resize = vertexResizeFn; + geom.rotate = vertexRotateFn; + handle.geometry.move = handleMoveFn; + geom._handle = handle; + handles[i] = handle; + } + + this.rotationHandles = rotationHandles; + this.handles = handles; + }, + + /** + * Method: createControl + * Creates a DragFeature control for this control. + */ + createControl: function() { + var control = this; + this.dragControl = new OpenLayers.Control.DragFeature(this.layer, { + documentDrag: true, + // avoid moving the feature itself - move the box instead + moveFeature: function(pixel) { + if(this.feature === control.feature) { + this.feature = control.box; + } + OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this, + arguments); + }, + // transform while dragging + onDrag: function(feature, pixel) { + if(feature === control.box) { + control.transformFeature({center: control.center}); + } + }, + // set a new feature + onStart: function(feature, pixel) { + var eligible = !control.geometryTypes || + OpenLayers.Util.indexOf(control.geometryTypes, + feature.geometry.CLASS_NAME) !== -1; + var i = OpenLayers.Util.indexOf(control.handles, feature); + i += OpenLayers.Util.indexOf(control.rotationHandles, + feature); + if(feature !== control.feature && feature !== control.box && + i == -2 && eligible) { + control.setFeature(feature); + } + }, + onComplete: function(feature, pixel) { + control.events.triggerEvent("transformcomplete", + {feature: control.feature}); + } + }); + }, + + /** + * Method: drawHandles + * Draws the handles to match the box. + */ + drawHandles: function() { + var layer = this.layer; + for(var i=0; i<8; ++i) { + if(this.rotate && i % 2 === 0) { + layer.drawFeature(this.rotationHandles[i/2], + this.rotationHandleSymbolizer); + } + layer.drawFeature(this.handles[i], this.renderIntent); + } + }, + + /** + * Method: transformFeature + * Transforms the feature. + * + * Parameters: + * mods - {Object} An object with optional scale, ratio, rotation and + * center properties. + */ + transformFeature: function(mods) { + if(!this._setfeature) { + this.scale *= (mods.scale || 1); + this.ratio *= (mods.ratio || 1); + var oldRotation = this.rotation; + this.rotation = (this.rotation + (mods.rotation || 0)) % 360; + + if(this.events.triggerEvent("beforetransform", mods) !== false) { + var feature = this.feature; + var geom = feature.geometry; + var center = this.center; + geom.rotate(-oldRotation, center); + if(mods.scale || mods.ratio) { + geom.resize(mods.scale, center, mods.ratio); + } else if(mods.center) { + feature.move(mods.center.getBounds().getCenterLonLat()); + } + geom.rotate(this.rotation, center); + this.layer.drawFeature(feature); + feature.toState(OpenLayers.State.UPDATE); + this.events.triggerEvent("transform", mods); + } + } + this.layer.drawFeature(this.box, this.renderIntent); + this.drawHandles(); + }, + + /** + * APIMethod: destroy + * Take care of things that are not handled in superclass. + */ + destroy: function() { + var geom; + for(var i=0; i<8; ++i) { + geom = this.box.geometry.components[i]; + geom._handle.destroy(); + geom._handle = null; + geom._rotationHandle && geom._rotationHandle.destroy(); + geom._rotationHandle = null; + } + this.center = null; + this.feature = null; + this.handles = null; + this.rotationHandleSymbolizer = null; + this.rotationHandles = null; + this.box.destroy(); + this.box = null; + this.layer = null; + this.dragControl.destroy(); + this.dragControl = null; + OpenLayers.Control.prototype.destroy.apply(this, arguments); + }, + + CLASS_NAME: "OpenLayers.Control.TransformFeature" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/UTFGrid.js b/misc/openlayers/lib/OpenLayers/Control/UTFGrid.js new file mode 100644 index 0000000..7993201 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/UTFGrid.js @@ -0,0 +1,240 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Handler/Hover.js + * @requires OpenLayers/Handler/Click.js + */ + +/** + * Class: OpenLayers.Control.UTFGrid + * + * This Control provides behavior associated with UTFGrid Layers. + * These 'hit grids' provide underlying feature attributes without + * calling the server (again). This control allows Mousemove, Hovering + * and Click events to trigger callbacks that use the attributes in + * whatever way you need. + * + * The most common example may be a UTFGrid layer containing feature + * attributes that are displayed in a div as you mouseover. + * + * Example Code: + * + * (start code) + * var world_utfgrid = new OpenLayers.Layer.UTFGrid( + * 'UTFGrid Layer', + * "http://tiles/world_utfgrid/${z}/${x}/${y}.json" + * ); + * map.addLayer(world_utfgrid); + * + * var control = new OpenLayers.Control.UTFGrid({ + * layers: [world_utfgrid], + * handlerMode: 'move', + * callback: function(infoLookup) { + * // do something with returned data + * + * } + * }) + * (end code) + * + * + * Inherits from: + * - + */ +OpenLayers.Control.UTFGrid = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: autoActivate + * {Boolean} Activate the control when it is added to a map. Default is + * true. + */ + autoActivate: true, + + /** + * APIProperty: Layers + * List of layers to consider. Must be Layer.UTFGrids + * `null` is the default indicating all UTFGrid Layers are queried. + * {Array} + */ + layers: null, + + /* Property: defaultHandlerOptions + * The default opts passed to the handler constructors + */ + defaultHandlerOptions: { + 'delay': 300, + 'pixelTolerance': 4, + 'stopMove': false, + 'single': true, + 'double': false, + 'stopSingle': false, + 'stopDouble': false + }, + + /* APIProperty: handlerMode + * Defaults to 'click'. Can be 'hover' or 'move'. + */ + handlerMode: 'click', + + /** + * APIMethod: setHandler + * sets this.handlerMode and calls resetHandler() + * + * Parameters: + * hm - {String} Handler Mode string; 'click', 'hover' or 'move'. + */ + setHandler: function(hm) { + this.handlerMode = hm; + this.resetHandler(); + }, + + /** + * Method: resetHandler + * Deactivates the old hanlder and creates a new + * based on the mode specified in + * this.handlerMode + * + */ + resetHandler: function() { + if (this.handler) { + this.handler.deactivate(); + this.handler.destroy(); + this.handler = null; + } + + if (this.handlerMode == 'hover') { + // Handle this event on hover + this.handler = new OpenLayers.Handler.Hover( + this, + {'pause': this.handleEvent, 'move': this.reset}, + this.handlerOptions + ); + } else if (this.handlerMode == 'click') { + // Handle this event on click + this.handler = new OpenLayers.Handler.Click( + this, { + 'click': this.handleEvent + }, this.handlerOptions + ); + } else if (this.handlerMode == 'move') { + this.handler = new OpenLayers.Handler.Hover( + this, + // Handle this event while hovering OR moving + {'pause': this.handleEvent, 'move': this.handleEvent}, + this.handlerOptions + ); + } + if (this.handler) { + return true; + } else { + return false; + } + }, + + /** + * Constructor: + * + * Parameters: + * options - {Object} + */ + initialize: function(options) { + options = options || {}; + options.handlerOptions = options.handlerOptions || this.defaultHandlerOptions; + OpenLayers.Control.prototype.initialize.apply(this, [options]); + this.resetHandler(); + }, + + /** + * Method: handleEvent + * Internal method called when specified event is triggered. + * + * This method does several things: + * + * Gets the lonLat of the event. + * + * Loops through the appropriate hit grid layers and gathers the attributes. + * + * Passes the attributes to the callback + * + * Parameters: + * evt - {} + */ + handleEvent: function(evt) { + if (evt == null) { + this.reset(); + return; + } + + var lonLat = this.map.getLonLatFromPixel(evt.xy); + if (!lonLat) { + return; + } + + var layers = this.findLayers(); + if (layers.length > 0) { + var infoLookup = {}; + var layer, idx; + for (var i=0, len=layers.length; i=0; --i) { + layer = candidates[i]; + if (layer instanceof OpenLayers.Layer.UTFGrid ) { + layers.push(layer); + } + } + return layers; + }, + + CLASS_NAME: "OpenLayers.Control.UTFGrid" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/WMSGetFeatureInfo.js b/misc/openlayers/lib/OpenLayers/Control/WMSGetFeatureInfo.js new file mode 100644 index 0000000..c9242f6 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/WMSGetFeatureInfo.js @@ -0,0 +1,532 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Handler/Click.js + * @requires OpenLayers/Handler/Hover.js + * @requires OpenLayers/Request.js + * @requires OpenLayers/Format/WMSGetFeatureInfo.js + */ + +/** + * Class: OpenLayers.Control.WMSGetFeatureInfo + * The WMSGetFeatureInfo control uses a WMS query to get information about a point on the map. The + * information may be in a display-friendly format such as HTML, or a machine-friendly format such + * as GML, depending on the server's capabilities and the client's configuration. This control + * handles click or hover events, attempts to parse the results using an OpenLayers.Format, and + * fires a 'getfeatureinfo' event with the click position, the raw body of the response, and an + * array of features if it successfully read the response. + * + * Inherits from: + * - + */ +OpenLayers.Control.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: hover + * {Boolean} Send GetFeatureInfo requests when mouse stops moving. + * Default is false. + */ + hover: false, + + /** + * APIProperty: drillDown + * {Boolean} Drill down over all WMS layers in the map. When + * using drillDown mode, hover is not possible, and an infoFormat that + * returns parseable features is required. Default is false. + */ + drillDown: false, + + /** + * APIProperty: maxFeatures + * {Integer} Maximum number of features to return from a WMS query. This + * sets the feature_count parameter on WMS GetFeatureInfo + * requests. + */ + maxFeatures: 10, + + /** + * APIProperty: clickCallback + * {String} The click callback to register in the + * {} object created when the hover + * option is set to false. Default is "click". + */ + clickCallback: "click", + + /** + * APIProperty: output + * {String} Either "features" or "object". When triggering a getfeatureinfo + * request should we pass on an array of features or an object with with + * a "features" property and other properties (such as the url of the + * WMS). Default is "features". + */ + output: "features", + + /** + * APIProperty: layers + * {Array()} The layers to query for feature info. + * If omitted, all map WMS layers with a url that matches this or + * will be considered. + */ + layers: null, + + /** + * APIProperty: queryVisible + * {Boolean} If true, filter out hidden layers when searching the map for + * layers to query. Default is false. + */ + queryVisible: false, + + /** + * APIProperty: url + * {String} The URL of the WMS service to use. If not provided, the url + * of the first eligible layer will be used. + */ + url: null, + + /** + * APIProperty: layerUrls + * {Array(String)} Optional list of urls for layers that should be queried. + * This can be used when the layer url differs from the url used for + * making GetFeatureInfo requests (in the case of a layer using cached + * tiles). + */ + layerUrls: null, + + /** + * APIProperty: infoFormat + * {String} The mimetype to request from the server. If you are using + * drillDown mode and have multiple servers that do not share a common + * infoFormat, you can override the control's infoFormat by providing an + * INFO_FORMAT parameter in your instance(s). + */ + infoFormat: 'text/html', + + /** + * APIProperty: vendorParams + * {Object} Additional parameters that will be added to the request, for + * WMS implementations that support them. This could e.g. look like + * (start code) + * { + * radius: 5 + * } + * (end) + */ + vendorParams: {}, + + /** + * APIProperty: format + * {} A format for parsing GetFeatureInfo responses. + * Default is . + */ + format: null, + + /** + * APIProperty: formatOptions + * {Object} Optional properties to set on the format (if one is not provided + * in the property. + */ + formatOptions: null, + + /** + * APIProperty: handlerOptions + * {Object} Additional options for the handlers used by this control, e.g. + * (start code) + * { + * "click": {delay: 100}, + * "hover": {delay: 300} + * } + * (end) + */ + + /** + * Property: handler + * {Object} Reference to the for this control + */ + handler: null, + + /** + * Property: hoverRequest + * {} contains the currently running hover request + * (if any). + */ + hoverRequest: null, + + /** + * APIProperty: events + * {} Events instance for listeners and triggering + * control specific events. + * + * Register a listener for a particular event with the following syntax: + * (code) + * control.events.register(type, obj, listener); + * (end) + * + * Supported event types (in addition to those from ): + * beforegetfeatureinfo - Triggered before the request is sent. + * The event object has an *xy* property with the position of the + * mouse click or hover event that triggers the request. + * nogetfeatureinfo - no queryable layers were found. + * getfeatureinfo - Triggered when a GetFeatureInfo response is received. + * The event object has a *text* property with the body of the + * response (String), a *features* property with an array of the + * parsed features, an *xy* property with the position of the mouse + * click or hover event that triggered the request, and a *request* + * property with the request itself. If drillDown is set to true and + * multiple requests were issued to collect feature info from all + * layers, *text* and *request* will only contain the response body + * and request object of the last request. + */ + + /** + * Constructor: + * + * Parameters: + * options - {Object} + */ + initialize: function(options) { + options = options || {}; + options.handlerOptions = options.handlerOptions || {}; + + OpenLayers.Control.prototype.initialize.apply(this, [options]); + + if(!this.format) { + this.format = new OpenLayers.Format.WMSGetFeatureInfo( + options.formatOptions + ); + } + + if(this.drillDown === true) { + this.hover = false; + } + + if(this.hover) { + this.handler = new OpenLayers.Handler.Hover( + this, { + 'move': this.cancelHover, + 'pause': this.getInfoForHover + }, + OpenLayers.Util.extend(this.handlerOptions.hover || {}, { + 'delay': 250 + })); + } else { + var callbacks = {}; + callbacks[this.clickCallback] = this.getInfoForClick; + this.handler = new OpenLayers.Handler.Click( + this, callbacks, this.handlerOptions.click || {}); + } + }, + + /** + * Method: getInfoForClick + * Called on click + * + * Parameters: + * evt - {} + */ + getInfoForClick: function(evt) { + this.events.triggerEvent("beforegetfeatureinfo", {xy: evt.xy}); + // Set the cursor to "wait" to tell the user we're working on their + // click. + OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait"); + this.request(evt.xy, {}); + }, + + /** + * Method: getInfoForHover + * Pause callback for the hover handler + * + * Parameters: + * evt - {Object} + */ + getInfoForHover: function(evt) { + this.events.triggerEvent("beforegetfeatureinfo", {xy: evt.xy}); + this.request(evt.xy, {hover: true}); + }, + + /** + * Method: cancelHover + * Cancel callback for the hover handler + */ + cancelHover: function() { + if (this.hoverRequest) { + this.hoverRequest.abort(); + this.hoverRequest = null; + } + }, + + /** + * Method: findLayers + * Internal method to get the layers, independent of whether we are + * inspecting the map or using a client-provided array + */ + findLayers: function() { + + var candidates = this.layers || this.map.layers; + var layers = []; + var layer, url; + for(var i = candidates.length - 1; i >= 0; --i) { + layer = candidates[i]; + if(layer instanceof OpenLayers.Layer.WMS && + (!this.queryVisible || layer.getVisibility())) { + url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url; + // if the control was not configured with a url, set it + // to the first layer url + if(this.drillDown === false && !this.url) { + this.url = url; + } + if(this.drillDown === true || this.urlMatches(url)) { + layers.push(layer); + } + } + } + return layers; + }, + + /** + * Method: urlMatches + * Test to see if the provided url matches either the control or one + * of the . + * + * Parameters: + * url - {String} The url to test. + * + * Returns: + * {Boolean} The provided url matches the control or one of the + * . + */ + urlMatches: function(url) { + var matches = OpenLayers.Util.isEquivalentUrl(this.url, url); + if(!matches && this.layerUrls) { + for(var i=0, len=this.layerUrls.length; i} The position on the map where the mouse + * event occurred. + * format - {String} The format from the corresponding GetMap request + */ + buildWMSOptions: function(url, layers, clickPosition, format) { + var layerNames = [], styleNames = []; + for (var i = 0, len = layers.length; i < len; i++) { + if (layers[i].params.LAYERS != null) { + layerNames = layerNames.concat(layers[i].params.LAYERS); + styleNames = styleNames.concat(this.getStyleNames(layers[i])); + } + } + var firstLayer = layers[0]; + // use the firstLayer's projection if it matches the map projection - + // this assumes that all layers will be available in this projection + var projection = this.map.getProjection(); + var layerProj = firstLayer.projection; + if (layerProj && layerProj.equals(this.map.getProjectionObject())) { + projection = layerProj.getCode(); + } + var params = OpenLayers.Util.extend({ + service: "WMS", + version: firstLayer.params.VERSION, + request: "GetFeatureInfo", + exceptions: firstLayer.params.EXCEPTIONS, + bbox: this.map.getExtent().toBBOX(null, + firstLayer.reverseAxisOrder()), + feature_count: this.maxFeatures, + height: this.map.getSize().h, + width: this.map.getSize().w, + format: format, + info_format: firstLayer.params.INFO_FORMAT || this.infoFormat + }, (parseFloat(firstLayer.params.VERSION) >= 1.3) ? + { + crs: projection, + i: parseInt(clickPosition.x), + j: parseInt(clickPosition.y) + } : + { + srs: projection, + x: parseInt(clickPosition.x), + y: parseInt(clickPosition.y) + } + ); + if (layerNames.length != 0) { + params = OpenLayers.Util.extend({ + layers: layerNames, + query_layers: layerNames, + styles: styleNames + }, params); + } + OpenLayers.Util.applyDefaults(params, this.vendorParams); + return { + url: url, + params: OpenLayers.Util.upperCaseObject(params), + callback: function(request) { + this.handleResponse(clickPosition, request, url); + }, + scope: this + }; + }, + + /** + * Method: getStyleNames + * Gets the STYLES parameter for the layer. Make sure the STYLES parameter + * matches the LAYERS parameter + * + * Parameters: + * layer - {} + * + * Returns: + * {Array(String)} The STYLES parameter + */ + getStyleNames: function(layer) { + // in the event of a WMS layer bundling multiple layers but not + // specifying styles,we need the same number of commas to specify + // the default style for each of the layers. We can't just leave it + // blank as we may be including other layers that do specify styles. + var styleNames; + if (layer.params.STYLES) { + styleNames = layer.params.STYLES; + } else { + if (OpenLayers.Util.isArray(layer.params.LAYERS)) { + styleNames = new Array(layer.params.LAYERS.length); + } else { // Assume it's a String + styleNames = layer.params.LAYERS.replace(/[^,]/g, ""); + } + } + return styleNames; + }, + + /** + * Method: request + * Sends a GetFeatureInfo request to the WMS + * + * Parameters: + * clickPosition - {} The position on the map where the + * mouse event occurred. + * options - {Object} additional options for this method. + * + * Valid options: + * - *hover* {Boolean} true if we do the request for the hover handler + */ + request: function(clickPosition, options) { + var layers = this.findLayers(); + if(layers.length == 0) { + this.events.triggerEvent("nogetfeatureinfo"); + // Reset the cursor. + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); + return; + } + + options = options || {}; + if(this.drillDown === false) { + var wmsOptions = this.buildWMSOptions(this.url, layers, + clickPosition, layers[0].params.FORMAT); + var request = OpenLayers.Request.GET(wmsOptions); + + if (options.hover === true) { + this.hoverRequest = request; + } + } else { + this._requestCount = 0; + this._numRequests = 0; + this.features = []; + // group according to service url to combine requests + var services = {}, url; + for(var i=0, len=layers.length; i} The position on the map where the + * mouse event occurred. + * features - {Array()} or + * {Array({Object}) when output is "object". The object has a url and a + * features property which contains an array of features. + */ + triggerGetFeatureInfo: function(request, xy, features) { + this.events.triggerEvent("getfeatureinfo", { + text: request.responseText, + features: features, + request: request, + xy: xy + }); + + // Reset the cursor. + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); + }, + + /** + * Method: handleResponse + * Handler for the GetFeatureInfo response. + * + * Parameters: + * xy - {} The position on the map where the + * mouse event occurred. + * request - {XMLHttpRequest} The request object. + * url - {String} The url which was used for this request. + */ + handleResponse: function(xy, request, url) { + + var doc = request.responseXML; + if(!doc || !doc.documentElement) { + doc = request.responseText; + } + var features = this.format.read(doc); + if (this.drillDown === false) { + this.triggerGetFeatureInfo(request, xy, features); + } else { + this._requestCount++; + if (this.output === "object") { + this._features = (this._features || []).concat( + {url: url, features: features} + ); + } else { + this._features = (this._features || []).concat(features); + } + if (this._requestCount === this._numRequests) { + this.triggerGetFeatureInfo(request, xy, this._features.concat()); + delete this._features; + delete this._requestCount; + delete this._numRequests; + } + } + }, + + CLASS_NAME: "OpenLayers.Control.WMSGetFeatureInfo" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/WMTSGetFeatureInfo.js b/misc/openlayers/lib/OpenLayers/Control/WMTSGetFeatureInfo.js new file mode 100644 index 0000000..c26f8f3 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/WMTSGetFeatureInfo.js @@ -0,0 +1,400 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Handler/Click.js + * @requires OpenLayers/Handler/Hover.js + * @requires OpenLayers/Request.js + * @requires OpenLayers/Format/WMSGetFeatureInfo.js + */ + +/** + * Class: OpenLayers.Control.WMTSGetFeatureInfo + * The WMTSGetFeatureInfo control uses a WMTS query to get information about a + * point on the map. The information may be in a display-friendly format + * such as HTML, or a machine-friendly format such as GML, depending on the + * server's capabilities and the client's configuration. This control + * handles click or hover events, attempts to parse the results using an + * OpenLayers.Format, and fires a 'getfeatureinfo' event for each layer + * queried. + * + * Inherits from: + * - + */ +OpenLayers.Control.WMTSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: hover + * {Boolean} Send GetFeatureInfo requests when mouse stops moving. + * Default is false. + */ + hover: false, + + /** + * Property: requestEncoding + * {String} One of "KVP" or "REST". Only KVP encoding is supported at this + * time. + */ + requestEncoding: "KVP", + + /** + * APIProperty: drillDown + * {Boolean} Drill down over all WMTS layers in the map. When + * using drillDown mode, hover is not possible. A getfeatureinfo event + * will be fired for each layer queried. + */ + drillDown: false, + + /** + * APIProperty: maxFeatures + * {Integer} Maximum number of features to return from a WMTS query. This + * sets the feature_count parameter on WMTS GetFeatureInfo + * requests. + */ + maxFeatures: 10, + + /** APIProperty: clickCallback + * {String} The click callback to register in the + * {} object created when the hover + * option is set to false. Default is "click". + */ + clickCallback: "click", + + /** + * Property: layers + * {Array()} The layers to query for feature info. + * If omitted, all map WMTS layers will be considered. + */ + layers: null, + + /** + * APIProperty: queryVisible + * {Boolean} Filter out hidden layers when searching the map for layers to + * query. Default is true. + */ + queryVisible: true, + + /** + * Property: infoFormat + * {String} The mimetype to request from the server + */ + infoFormat: 'text/html', + + /** + * Property: vendorParams + * {Object} Additional parameters that will be added to the request, for + * WMTS implementations that support them. This could e.g. look like + * (start code) + * { + * radius: 5 + * } + * (end) + */ + vendorParams: {}, + + /** + * Property: format + * {} A format for parsing GetFeatureInfo responses. + * Default is . + */ + format: null, + + /** + * Property: formatOptions + * {Object} Optional properties to set on the format (if one is not provided + * in the property. + */ + formatOptions: null, + + /** + * APIProperty: handlerOptions + * {Object} Additional options for the handlers used by this control, e.g. + * (start code) + * { + * "click": {delay: 100}, + * "hover": {delay: 300} + * } + * (end) + */ + + /** + * Property: handler + * {Object} Reference to the for this control + */ + handler: null, + + /** + * Property: hoverRequest + * {} contains the currently running hover request + * (if any). + */ + hoverRequest: null, + + /** + * APIProperty: events + * {} Events instance for listeners and triggering + * control specific events. + * + * Register a listener for a particular event with the following syntax: + * (code) + * control.events.register(type, obj, listener); + * (end) + * + * Supported event types (in addition to those from ): + * beforegetfeatureinfo - Triggered before each request is sent. + * The event object has an *xy* property with the position of the + * mouse click or hover event that triggers the request and a *layer* + * property referencing the layer about to be queried. If a listener + * returns false, the request will not be issued. + * getfeatureinfo - Triggered when a GetFeatureInfo response is received. + * The event object has a *text* property with the body of the + * response (String), a *features* property with an array of the + * parsed features, an *xy* property with the position of the mouse + * click or hover event that triggered the request, a *layer* property + * referencing the layer queried and a *request* property with the + * request itself. If drillDown is set to true, one event will be fired + * for each layer queried. + * exception - Triggered when a GetFeatureInfo request fails (with a + * status other than 200) or whenparsing fails. Listeners will receive + * an event with *request*, *xy*, and *layer* properties. In the case + * of a parsing error, the event will also contain an *error* property. + */ + + /** + * Property: pending + * {Number} The number of pending requests. + */ + pending: 0, + + /** + * Constructor: + * + * Parameters: + * options - {Object} + */ + initialize: function(options) { + options = options || {}; + options.handlerOptions = options.handlerOptions || {}; + + OpenLayers.Control.prototype.initialize.apply(this, [options]); + + if (!this.format) { + this.format = new OpenLayers.Format.WMSGetFeatureInfo( + options.formatOptions + ); + } + + if (this.drillDown === true) { + this.hover = false; + } + + if (this.hover) { + this.handler = new OpenLayers.Handler.Hover( + this, { + move: this.cancelHover, + pause: this.getInfoForHover + }, + OpenLayers.Util.extend( + this.handlerOptions.hover || {}, {delay: 250} + ) + ); + } else { + var callbacks = {}; + callbacks[this.clickCallback] = this.getInfoForClick; + this.handler = new OpenLayers.Handler.Click( + this, callbacks, this.handlerOptions.click || {} + ); + } + }, + + /** + * Method: getInfoForClick + * Called on click + * + * Parameters: + * evt - {} + */ + getInfoForClick: function(evt) { + this.request(evt.xy, {}); + }, + + /** + * Method: getInfoForHover + * Pause callback for the hover handler + * + * Parameters: + * evt - {Object} + */ + getInfoForHover: function(evt) { + this.request(evt.xy, {hover: true}); + }, + + /** + * Method: cancelHover + * Cancel callback for the hover handler + */ + cancelHover: function() { + if (this.hoverRequest) { + --this.pending; + if (this.pending <= 0) { + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); + this.pending = 0; + } + this.hoverRequest.abort(); + this.hoverRequest = null; + } + }, + + /** + * Method: findLayers + * Internal method to get the layers, independent of whether we are + * inspecting the map or using a client-provided array + */ + findLayers: function() { + var candidates = this.layers || this.map.layers; + var layers = []; + var layer; + for (var i=candidates.length-1; i>=0; --i) { + layer = candidates[i]; + if (layer instanceof OpenLayers.Layer.WMTS && + layer.requestEncoding === this.requestEncoding && + (!this.queryVisible || layer.getVisibility())) { + layers.push(layer); + if (!this.drillDown || this.hover) { + break; + } + } + } + return layers; + }, + + /** + * Method: buildRequestOptions + * Build an object with the relevant options for the GetFeatureInfo request. + * + * Parameters: + * layer - {} A WMTS layer. + * xy - {} The position on the map where the + * mouse event occurred. + */ + buildRequestOptions: function(layer, xy) { + var loc = this.map.getLonLatFromPixel(xy); + var getTileUrl = layer.getURL( + new OpenLayers.Bounds(loc.lon, loc.lat, loc.lon, loc.lat) + ); + var params = OpenLayers.Util.getParameters(getTileUrl); + var tileInfo = layer.getTileInfo(loc); + OpenLayers.Util.extend(params, { + service: "WMTS", + version: layer.version, + request: "GetFeatureInfo", + infoFormat: this.infoFormat, + i: tileInfo.i, + j: tileInfo.j + }); + OpenLayers.Util.applyDefaults(params, this.vendorParams); + return { + url: OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url, + params: OpenLayers.Util.upperCaseObject(params), + callback: function(request) { + this.handleResponse(xy, request, layer); + }, + scope: this + }; + }, + + /** + * Method: request + * Sends a GetFeatureInfo request to the WMTS + * + * Parameters: + * xy - {} The position on the map where the mouse event + * occurred. + * options - {Object} additional options for this method. + * + * Valid options: + * - *hover* {Boolean} true if we do the request for the hover handler + */ + request: function(xy, options) { + options = options || {}; + var layers = this.findLayers(); + if (layers.length > 0) { + var issue, layer; + for (var i=0, len=layers.length; i 0) { + OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait"); + } + } + }, + + /** + * Method: handleResponse + * Handler for the GetFeatureInfo response. + * + * Parameters: + * xy - {} The position on the map where the mouse event + * occurred. + * request - {XMLHttpRequest} The request object. + * layer - {} The queried layer. + */ + handleResponse: function(xy, request, layer) { + --this.pending; + if (this.pending <= 0) { + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); + this.pending = 0; + } + if (request.status && (request.status < 200 || request.status >= 300)) { + this.events.triggerEvent("exception", { + xy: xy, + request: request, + layer: layer + }); + } else { + var doc = request.responseXML; + if (!doc || !doc.documentElement) { + doc = request.responseText; + } + var features, except; + try { + features = this.format.read(doc); + } catch (error) { + except = true; + this.events.triggerEvent("exception", { + xy: xy, + request: request, + error: error, + layer: layer + }); + } + if (!except) { + this.events.triggerEvent("getfeatureinfo", { + text: request.responseText, + features: features, + request: request, + xy: xy, + layer: layer + }); + } + } + }, + + CLASS_NAME: "OpenLayers.Control.WMTSGetFeatureInfo" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/Zoom.js b/misc/openlayers/lib/OpenLayers/Control/Zoom.js new file mode 100644 index 0000000..70140f4 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/Zoom.js @@ -0,0 +1,138 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Events/buttonclick.js + */ + +/** + * Class: OpenLayers.Control.Zoom + * The Zoom control is a pair of +/- links for zooming in and out. + * + * Inherits from: + * - + */ +OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: zoomInText + * {String} + * Text for zoom-in link. Default is "+". + */ + zoomInText: "+", + + /** + * APIProperty: zoomInId + * {String} + * Instead of having the control create a zoom in link, you can provide + * the identifier for an anchor element already added to the document. + * By default, an element with id "olZoomInLink" will be searched for + * and used if it exists. + */ + zoomInId: "olZoomInLink", + + /** + * APIProperty: zoomOutText + * {String} + * Text for zoom-out link. Default is "\u2212". + */ + zoomOutText: "\u2212", + + /** + * APIProperty: zoomOutId + * {String} + * Instead of having the control create a zoom out link, you can provide + * the identifier for an anchor element already added to the document. + * By default, an element with id "olZoomOutLink" will be searched for + * and used if it exists. + */ + zoomOutId: "olZoomOutLink", + + /** + * Method: draw + * + * Returns: + * {DOMElement} A reference to the DOMElement containing the zoom links. + */ + draw: function() { + var div = OpenLayers.Control.prototype.draw.apply(this), + links = this.getOrCreateLinks(div), + zoomIn = links.zoomIn, + zoomOut = links.zoomOut, + eventsInstance = this.map.events; + + if (zoomOut.parentNode !== div) { + eventsInstance = this.events; + eventsInstance.attachToElement(zoomOut.parentNode); + } + eventsInstance.register("buttonclick", this, this.onZoomClick); + + this.zoomInLink = zoomIn; + this.zoomOutLink = zoomOut; + return div; + }, + + /** + * Method: getOrCreateLinks + * + * Parameters: + * el - {DOMElement} + * + * Return: + * {Object} Object with zoomIn and zoomOut properties referencing links. + */ + getOrCreateLinks: function(el) { + var zoomIn = document.getElementById(this.zoomInId), + zoomOut = document.getElementById(this.zoomOutId); + if (!zoomIn) { + zoomIn = document.createElement("a"); + zoomIn.href = "#zoomIn"; + zoomIn.appendChild(document.createTextNode(this.zoomInText)); + zoomIn.className = "olControlZoomIn"; + el.appendChild(zoomIn); + } + OpenLayers.Element.addClass(zoomIn, "olButton"); + if (!zoomOut) { + zoomOut = document.createElement("a"); + zoomOut.href = "#zoomOut"; + zoomOut.appendChild(document.createTextNode(this.zoomOutText)); + zoomOut.className = "olControlZoomOut"; + el.appendChild(zoomOut); + } + OpenLayers.Element.addClass(zoomOut, "olButton"); + return { + zoomIn: zoomIn, zoomOut: zoomOut + }; + }, + + /** + * Method: onZoomClick + * Called when zoomin/out link is clicked. + */ + onZoomClick: function(evt) { + var button = evt.buttonElement; + if (button === this.zoomInLink) { + this.map.zoomIn(); + } else if (button === this.zoomOutLink) { + this.map.zoomOut(); + } + }, + + /** + * Method: destroy + * Clean up. + */ + destroy: function() { + if (this.map) { + this.map.events.unregister("buttonclick", this, this.onZoomClick); + } + delete this.zoomInLink; + delete this.zoomOutLink; + OpenLayers.Control.prototype.destroy.apply(this); + }, + + CLASS_NAME: "OpenLayers.Control.Zoom" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/ZoomBox.js b/misc/openlayers/lib/OpenLayers/Control/ZoomBox.js new file mode 100644 index 0000000..9d4b2da --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/ZoomBox.js @@ -0,0 +1,129 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + * @requires OpenLayers/Handler/Box.js + */ + +/** + * Class: OpenLayers.Control.ZoomBox + * The ZoomBox control enables zooming directly to a given extent, by drawing + * a box on the map. The box is drawn by holding down shift, whilst dragging + * the mouse. + * + * Inherits from: + * - + */ +OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, { + /** + * Property: type + * {OpenLayers.Control.TYPE} + */ + type: OpenLayers.Control.TYPE_TOOL, + + /** + * Property: out + * {Boolean} Should the control be used for zooming out? + */ + out: false, + + /** + * APIProperty: keyMask + * {Integer} Zoom only occurs if the keyMask matches the combination of + * keys down. Use bitwise operators and one or more of the + * constants to construct a keyMask. Leave null if + * not used mask. Default is null. + */ + keyMask: null, + + /** + * APIProperty: alwaysZoom + * {Boolean} Always zoom in/out when box drawn, even if the zoom level does + * not change. + */ + alwaysZoom: false, + + /** + * APIProperty: zoomOnClick + * {Boolean} Should we zoom when no box was dragged, i.e. the user only + * clicked? Default is true. + */ + zoomOnClick: true, + + /** + * Method: draw + */ + draw: function() { + this.handler = new OpenLayers.Handler.Box( this, + {done: this.zoomBox}, {keyMask: this.keyMask} ); + }, + + /** + * Method: zoomBox + * + * Parameters: + * position - {} or {} + */ + zoomBox: function (position) { + if (position instanceof OpenLayers.Bounds) { + var bounds, + targetCenterPx = position.getCenterPixel(); + if (!this.out) { + var minXY = this.map.getLonLatFromPixel({ + x: position.left, + y: position.bottom + }); + var maxXY = this.map.getLonLatFromPixel({ + x: position.right, + y: position.top + }); + bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, + maxXY.lon, maxXY.lat); + } else { + var pixWidth = position.right - position.left; + var pixHeight = position.bottom - position.top; + var zoomFactor = Math.min((this.map.size.h / pixHeight), + (this.map.size.w / pixWidth)); + var extent = this.map.getExtent(); + var center = this.map.getLonLatFromPixel(targetCenterPx); + var xmin = center.lon - (extent.getWidth()/2)*zoomFactor; + var xmax = center.lon + (extent.getWidth()/2)*zoomFactor; + var ymin = center.lat - (extent.getHeight()/2)*zoomFactor; + var ymax = center.lat + (extent.getHeight()/2)*zoomFactor; + bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax); + } + // always zoom in/out + var lastZoom = this.map.getZoom(), + size = this.map.getSize(), + centerPx = {x: size.w / 2, y: size.h / 2}, + zoom = this.map.getZoomForExtent(bounds), + oldRes = this.map.getResolution(), + newRes = this.map.getResolutionForZoom(zoom); + if (oldRes == newRes) { + this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx)); + } else { + var zoomOriginPx = { + x: (oldRes * targetCenterPx.x - newRes * centerPx.x) / + (oldRes - newRes), + y: (oldRes * targetCenterPx.y - newRes * centerPx.y) / + (oldRes - newRes) + }; + this.map.zoomTo(zoom, zoomOriginPx); + } + if (lastZoom == this.map.getZoom() && this.alwaysZoom == true){ + this.map.zoomTo(lastZoom + (this.out ? -1 : 1)); + } + } else if (this.zoomOnClick) { // it's a pixel + if (!this.out) { + this.map.zoomTo(this.map.getZoom() + 1, position); + } else { + this.map.zoomTo(this.map.getZoom() - 1, position); + } + } + }, + + CLASS_NAME: "OpenLayers.Control.ZoomBox" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/ZoomIn.js b/misc/openlayers/lib/OpenLayers/Control/ZoomIn.js new file mode 100644 index 0000000..8da1e1c --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/ZoomIn.js @@ -0,0 +1,29 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control/Button.js + */ + +/** + * Class: OpenLayers.Control.ZoomIn + * The ZoomIn control is a button to increase the zoom level of a map. + * + * Inherits from: + * - + */ +OpenLayers.Control.ZoomIn = OpenLayers.Class(OpenLayers.Control.Button, { + + /** + * Method: trigger + */ + trigger: function(){ + if (this.map) { + this.map.zoomIn(); + } + }, + + CLASS_NAME: "OpenLayers.Control.ZoomIn" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/ZoomOut.js b/misc/openlayers/lib/OpenLayers/Control/ZoomOut.js new file mode 100644 index 0000000..72a657b --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/ZoomOut.js @@ -0,0 +1,29 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control/Button.js + */ + +/** + * Class: OpenLayers.Control.ZoomOut + * The ZoomOut control is a button to decrease the zoom level of a map. + * + * Inherits from: + * - + */ +OpenLayers.Control.ZoomOut = OpenLayers.Class(OpenLayers.Control.Button, { + + /** + * Method: trigger + */ + trigger: function(){ + if (this.map) { + this.map.zoomOut(); + } + }, + + CLASS_NAME: "OpenLayers.Control.ZoomOut" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/ZoomPanel.js b/misc/openlayers/lib/OpenLayers/Control/ZoomPanel.js new file mode 100644 index 0000000..147f6cb --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/ZoomPanel.js @@ -0,0 +1,54 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control/Panel.js + * @requires OpenLayers/Control/ZoomIn.js + * @requires OpenLayers/Control/ZoomOut.js + * @requires OpenLayers/Control/ZoomToMaxExtent.js + */ + +/** + * Class: OpenLayers.Control.ZoomPanel + * The ZoomPanel control is a compact collecton of 3 zoom controls: a + * , a , and a + * . By default it is drawn in the upper left + * corner of the map. + * + * Note: + * If you wish to use this class with the default images and you want + * it to look nice in ie6, you should add the following, conditionally + * added css stylesheet to your HTML file: + * + * (code) + * + * (end) + * + * Inherits from: + * - + */ +OpenLayers.Control.ZoomPanel = OpenLayers.Class(OpenLayers.Control.Panel, { + + /** + * Constructor: OpenLayers.Control.ZoomPanel + * Add the three zooming controls. + * + * Parameters: + * options - {Object} An optional object whose properties will be used + * to extend the control. + */ + initialize: function(options) { + OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); + this.addControls([ + new OpenLayers.Control.ZoomIn(), + new OpenLayers.Control.ZoomToMaxExtent(), + new OpenLayers.Control.ZoomOut() + ]); + }, + + CLASS_NAME: "OpenLayers.Control.ZoomPanel" +}); diff --git a/misc/openlayers/lib/OpenLayers/Control/ZoomToMaxExtent.js b/misc/openlayers/lib/OpenLayers/Control/ZoomToMaxExtent.js new file mode 100644 index 0000000..bc2e754 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Control/ZoomToMaxExtent.js @@ -0,0 +1,35 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control/Button.js + */ + +/** + * Class: OpenLayers.Control.ZoomToMaxExtent + * The ZoomToMaxExtent control is a button that zooms out to the maximum + * extent of the map. It is designed to be used with a + * . + * + * Inherits from: + * - + */ +OpenLayers.Control.ZoomToMaxExtent = OpenLayers.Class(OpenLayers.Control.Button, { + + /** + * Method: trigger + * + * Called whenever this control is being rendered inside of a panel and a + * click occurs on this controls element. Actually zooms to the maximum + * extent of this controls map. + */ + trigger: function() { + if (this.map) { + this.map.zoomToMaxExtent(); + } + }, + + CLASS_NAME: "OpenLayers.Control.ZoomToMaxExtent" +}); diff --git a/misc/openlayers/lib/OpenLayers/Events.js b/misc/openlayers/lib/OpenLayers/Events.js new file mode 100644 index 0000000..6a4a129 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Events.js @@ -0,0 +1,1170 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/Util.js + */ + +/** + * Namespace: OpenLayers.Event + * Utility functions for event handling. + */ +OpenLayers.Event = { + + /** + * Property: observers + * {Object} A hashtable cache of the event observers. Keyed by + * element._eventCacheID + */ + observers: false, + + /** + * Constant: KEY_SPACE + * {int} + */ + KEY_SPACE: 32, + + /** + * Constant: KEY_BACKSPACE + * {int} + */ + KEY_BACKSPACE: 8, + + /** + * Constant: KEY_TAB + * {int} + */ + KEY_TAB: 9, + + /** + * Constant: KEY_RETURN + * {int} + */ + KEY_RETURN: 13, + + /** + * Constant: KEY_ESC + * {int} + */ + KEY_ESC: 27, + + /** + * Constant: KEY_LEFT + * {int} + */ + KEY_LEFT: 37, + + /** + * Constant: KEY_UP + * {int} + */ + KEY_UP: 38, + + /** + * Constant: KEY_RIGHT + * {int} + */ + KEY_RIGHT: 39, + + /** + * Constant: KEY_DOWN + * {int} + */ + KEY_DOWN: 40, + + /** + * Constant: KEY_DELETE + * {int} + */ + KEY_DELETE: 46, + + + /** + * Method: element + * Cross browser event element detection. + * + * Parameters: + * event - {Event} + * + * Returns: + * {DOMElement} The element that caused the event + */ + element: function(event) { + return event.target || event.srcElement; + }, + + /** + * Method: isSingleTouch + * Determine whether event was caused by a single touch + * + * Parameters: + * event - {Event} + * + * Returns: + * {Boolean} + */ + isSingleTouch: function(event) { + return event.touches && event.touches.length == 1; + }, + + /** + * Method: isMultiTouch + * Determine whether event was caused by a multi touch + * + * Parameters: + * event - {Event} + * + * Returns: + * {Boolean} + */ + isMultiTouch: function(event) { + return event.touches && event.touches.length > 1; + }, + + /** + * Method: isLeftClick + * Determine whether event was caused by a left click. + * + * Parameters: + * event - {Event} + * + * Returns: + * {Boolean} + */ + isLeftClick: function(event) { + return (((event.which) && (event.which == 1)) || + ((event.button) && (event.button == 1))); + }, + + /** + * Method: isRightClick + * Determine whether event was caused by a right mouse click. + * + * Parameters: + * event - {Event} + * + * Returns: + * {Boolean} + */ + isRightClick: function(event) { + return (((event.which) && (event.which == 3)) || + ((event.button) && (event.button == 2))); + }, + + /** + * Method: stop + * Stops an event from propagating. + * + * Parameters: + * event - {Event} + * allowDefault - {Boolean} If true, we stop the event chain but + * still allow the default browser behaviour (text selection, + * radio-button clicking, etc). Default is false. + */ + stop: function(event, allowDefault) { + + if (!allowDefault) { + OpenLayers.Event.preventDefault(event); + } + + if (event.stopPropagation) { + event.stopPropagation(); + } else { + event.cancelBubble = true; + } + }, + + /** + * Method: preventDefault + * Cancels the event if it is cancelable, without stopping further + * propagation of the event. + * + * Parameters: + * event - {Event} + */ + preventDefault: function(event) { + if (event.preventDefault) { + event.preventDefault(); + } else { + event.returnValue = false; + } + }, + + /** + * Method: findElement + * + * Parameters: + * event - {Event} + * tagName - {String} + * + * Returns: + * {DOMElement} The first node with the given tagName, starting from the + * node the event was triggered on and traversing the DOM upwards + */ + findElement: function(event, tagName) { + var element = OpenLayers.Event.element(event); + while (element.parentNode && (!element.tagName || + (element.tagName.toUpperCase() != tagName.toUpperCase()))){ + element = element.parentNode; + } + return element; + }, + + /** + * Method: observe + * + * Parameters: + * elementParam - {DOMElement || String} + * name - {String} + * observer - {function} + * useCapture - {Boolean} + */ + observe: function(elementParam, name, observer, useCapture) { + var element = OpenLayers.Util.getElement(elementParam); + useCapture = useCapture || false; + + if (name == 'keypress' && + (navigator.appVersion.match(/Konqueror|Safari|KHTML/) + || element.attachEvent)) { + name = 'keydown'; + } + + //if observers cache has not yet been created, create it + if (!this.observers) { + this.observers = {}; + } + + //if not already assigned, make a new unique cache ID + if (!element._eventCacheID) { + var idPrefix = "eventCacheID_"; + if (element.id) { + idPrefix = element.id + "_" + idPrefix; + } + element._eventCacheID = OpenLayers.Util.createUniqueID(idPrefix); + } + + var cacheID = element._eventCacheID; + + //if there is not yet a hash entry for this element, add one + if (!this.observers[cacheID]) { + this.observers[cacheID] = []; + } + + //add a new observer to this element's list + this.observers[cacheID].push({ + 'element': element, + 'name': name, + 'observer': observer, + 'useCapture': useCapture + }); + + //add the actual browser event listener + if (element.addEventListener) { + element.addEventListener(name, observer, useCapture); + } else if (element.attachEvent) { + element.attachEvent('on' + name, observer); + } + }, + + /** + * Method: stopObservingElement + * Given the id of an element to stop observing, cycle through the + * element's cached observers, calling stopObserving on each one, + * skipping those entries which can no longer be removed. + * + * parameters: + * elementParam - {DOMElement || String} + */ + stopObservingElement: function(elementParam) { + var element = OpenLayers.Util.getElement(elementParam); + var cacheID = element._eventCacheID; + + this._removeElementObservers(OpenLayers.Event.observers[cacheID]); + }, + + /** + * Method: _removeElementObservers + * + * Parameters: + * elementObservers - {Array(Object)} Array of (element, name, + * observer, usecapture) objects, + * taken directly from hashtable + */ + _removeElementObservers: function(elementObservers) { + if (elementObservers) { + for(var i = elementObservers.length-1; i >= 0; i--) { + var entry = elementObservers[i]; + OpenLayers.Event.stopObserving.apply(this, [ + entry.element, entry.name, entry.observer, entry.useCapture + ]); + } + } + }, + + /** + * Method: stopObserving + * + * Parameters: + * elementParam - {DOMElement || String} + * name - {String} + * observer - {function} + * useCapture - {Boolean} + * + * Returns: + * {Boolean} Whether or not the event observer was removed + */ + stopObserving: function(elementParam, name, observer, useCapture) { + useCapture = useCapture || false; + + var element = OpenLayers.Util.getElement(elementParam); + var cacheID = element._eventCacheID; + + if (name == 'keypress') { + if ( navigator.appVersion.match(/Konqueror|Safari|KHTML/) || + element.detachEvent) { + name = 'keydown'; + } + } + + // find element's entry in this.observers cache and remove it + var foundEntry = false; + var elementObservers = OpenLayers.Event.observers[cacheID]; + if (elementObservers) { + + // find the specific event type in the element's list + var i=0; + while(!foundEntry && i < elementObservers.length) { + var cacheEntry = elementObservers[i]; + + if ((cacheEntry.name == name) && + (cacheEntry.observer == observer) && + (cacheEntry.useCapture == useCapture)) { + + elementObservers.splice(i, 1); + if (elementObservers.length == 0) { + delete OpenLayers.Event.observers[cacheID]; + } + foundEntry = true; + break; + } + i++; + } + } + + //actually remove the event listener from browser + if (foundEntry) { + if (element.removeEventListener) { + element.removeEventListener(name, observer, useCapture); + } else if (element && element.detachEvent) { + element.detachEvent('on' + name, observer); + } + } + return foundEntry; + }, + + /** + * Method: unloadCache + * Cycle through all the element entries in the events cache and call + * stopObservingElement on each. + */ + unloadCache: function() { + // check for OpenLayers.Event before checking for observers, because + // OpenLayers.Event may be undefined in IE if no map instance was + // created + if (OpenLayers.Event && OpenLayers.Event.observers) { + for (var cacheID in OpenLayers.Event.observers) { + var elementObservers = OpenLayers.Event.observers[cacheID]; + OpenLayers.Event._removeElementObservers.apply(this, + [elementObservers]); + } + OpenLayers.Event.observers = false; + } + }, + + CLASS_NAME: "OpenLayers.Event" +}; + +/* prevent memory leaks in IE */ +OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false); + +/** + * Class: OpenLayers.Events + */ +OpenLayers.Events = OpenLayers.Class({ + + /** + * Constant: BROWSER_EVENTS + * {Array(String)} supported events + */ + BROWSER_EVENTS: [ + "mouseover", "mouseout", + "mousedown", "mouseup", "mousemove", + "click", "dblclick", "rightclick", "dblrightclick", + "resize", "focus", "blur", + "touchstart", "touchmove", "touchend", + "keydown" + ], + + /** + * Property: listeners + * {Object} Hashtable of Array(Function): events listener functions + */ + listeners: null, + + /** + * Property: object + * {Object} the code object issuing application events + */ + object: null, + + /** + * Property: element + * {DOMElement} the DOM element receiving browser events + */ + element: null, + + /** + * Property: eventHandler + * {Function} bound event handler attached to elements + */ + eventHandler: null, + + /** + * APIProperty: fallThrough + * {Boolean} + */ + fallThrough: null, + + /** + * APIProperty: includeXY + * {Boolean} Should the .xy property automatically be created for browser + * mouse events? In general, this should be false. If it is true, then + * mouse events will automatically generate a '.xy' property on the + * event object that is passed. (Prior to OpenLayers 2.7, this was true + * by default.) Otherwise, you can call the getMousePosition on the + * relevant events handler on the object available via the 'evt.object' + * property of the evt object. So, for most events, you can call: + * function named(evt) { + * this.xy = this.object.events.getMousePosition(evt) + * } + * + * This option typically defaults to false for performance reasons: + * when creating an events object whose primary purpose is to manage + * relatively positioned mouse events within a div, it may make + * sense to set it to true. + * + * This option is also used to control whether the events object caches + * offsets. If this is false, it will not: the reason for this is that + * it is only expected to be called many times if the includeXY property + * is set to true. If you set this to true, you are expected to clear + * the offset cache manually (using this.clearMouseCache()) if: + * the border of the element changes + * the location of the element in the page changes + */ + includeXY: false, + + /** + * APIProperty: extensions + * {Object} Event extensions registered with this instance. Keys are + * event types, values are {OpenLayers.Events.*} extension instances or + * {Boolean} for events that an instantiated extension provides in + * addition to the one it was created for. + * + * Extensions create an event in addition to browser events, which usually + * fires when a sequence of browser events is completed. Extensions are + * automatically instantiated when a listener is registered for an event + * provided by an extension. + * + * Extensions are created in the namespace using + * , and named after the event they provide. + * The constructor receives the target instance as + * argument. Extensions that need to capture browser events before they + * propagate can register their listeners events using , with + * {extension: true} as 4th argument. + * + * If an extension creates more than one event, an alias for each event + * type should be created and reference the same class. The constructor + * should set a reference in the target's extensions registry to itself. + * + * Below is a minimal extension that provides the "foostart" and "fooend" + * event types, which replace the native "click" event type if clicked on + * an element with the css class "foo": + * + * (code) + * OpenLayers.Events.foostart = OpenLayers.Class({ + * initialize: function(target) { + * this.target = target; + * this.target.register("click", this, this.doStuff, {extension: true}); + * // only required if extension provides more than one event type + * this.target.extensions["foostart"] = true; + * this.target.extensions["fooend"] = true; + * }, + * destroy: function() { + * var target = this.target; + * target.unregister("click", this, this.doStuff); + * delete this.target; + * // only required if extension provides more than one event type + * delete target.extensions["foostart"]; + * delete target.extensions["fooend"]; + * }, + * doStuff: function(evt) { + * var propagate = true; + * if (OpenLayers.Event.element(evt).className === "foo") { + * propagate = false; + * var target = this.target; + * target.triggerEvent("foostart"); + * window.setTimeout(function() { + * target.triggerEvent("fooend"); + * }, 1000); + * } + * return propagate; + * } + * }); + * // only required if extension provides more than one event type + * OpenLayers.Events.fooend = OpenLayers.Events.foostart; + * (end) + * + */ + extensions: null, + + /** + * Property: extensionCount + * {Object} Keys are event types (like in ), values are the + * number of extension listeners for each event type. + */ + extensionCount: null, + + /** + * Method: clearMouseListener + * A version of that is bound to this instance so that + * it can be used with and + * . + */ + clearMouseListener: null, + + /** + * Constructor: OpenLayers.Events + * Construct an OpenLayers.Events object. + * + * Parameters: + * object - {Object} The js object to which this Events object is being added + * element - {DOMElement} A dom element to respond to browser events + * eventTypes - {Array(String)} Deprecated. Array of custom application + * events. A listener may be registered for any named event, regardless + * of the values provided here. + * fallThrough - {Boolean} Allow events to fall through after these have + * been handled? + * options - {Object} Options for the events object. + */ + initialize: function (object, element, eventTypes, fallThrough, options) { + OpenLayers.Util.extend(this, options); + this.object = object; + this.fallThrough = fallThrough; + this.listeners = {}; + this.extensions = {}; + this.extensionCount = {}; + this._msTouches = []; + + // if a dom element is specified, add a listeners list + // for browser events on the element and register them + if (element != null) { + this.attachToElement(element); + } + }, + + /** + * APIMethod: destroy + */ + destroy: function () { + for (var e in this.extensions) { + if (typeof this.extensions[e] !== "boolean") { + this.extensions[e].destroy(); + } + } + this.extensions = null; + if (this.element) { + OpenLayers.Event.stopObservingElement(this.element); + if(this.element.hasScrollEvent) { + OpenLayers.Event.stopObserving( + window, "scroll", this.clearMouseListener + ); + } + } + this.element = null; + + this.listeners = null; + this.object = null; + this.fallThrough = null; + this.eventHandler = null; + }, + + /** + * APIMethod: addEventType + * Deprecated. Any event can be triggered without adding it first. + * + * Parameters: + * eventName - {String} + */ + addEventType: function(eventName) { + }, + + /** + * Method: attachToElement + * + * Parameters: + * element - {HTMLDOMElement} a DOM element to attach browser events to + */ + attachToElement: function (element) { + if (this.element) { + OpenLayers.Event.stopObservingElement(this.element); + } else { + // keep a bound copy of handleBrowserEvent() so that we can + // pass the same function to both Event.observe() and .stopObserving() + this.eventHandler = OpenLayers.Function.bindAsEventListener( + this.handleBrowserEvent, this + ); + + // to be used with observe and stopObserving + this.clearMouseListener = OpenLayers.Function.bind( + this.clearMouseCache, this + ); + } + this.element = element; + var msTouch = !!window.navigator.msMaxTouchPoints; + var type; + for (var i = 0, len = this.BROWSER_EVENTS.length; i < len; i++) { + type = this.BROWSER_EVENTS[i]; + // register the event cross-browser + OpenLayers.Event.observe(element, type, this.eventHandler + ); + if (msTouch && type.indexOf('touch') === 0) { + this.addMsTouchListener(element, type, this.eventHandler); + } + } + // disable dragstart in IE so that mousedown/move/up works normally + OpenLayers.Event.observe(element, "dragstart", OpenLayers.Event.stop); + }, + + /** + * APIMethod: on + * Convenience method for registering listeners with a common scope. + * Internally, this method calls as shown in the examples + * below. + * + * Example use: + * (code) + * // register a single listener for the "loadstart" event + * events.on({"loadstart": loadStartListener}); + * + * // this is equivalent to the following + * events.register("loadstart", undefined, loadStartListener); + * + * // register multiple listeners to be called with the same `this` object + * events.on({ + * "loadstart": loadStartListener, + * "loadend": loadEndListener, + * scope: object + * }); + * + * // this is equivalent to the following + * events.register("loadstart", object, loadStartListener); + * events.register("loadend", object, loadEndListener); + * (end) + * + * Parameters: + * object - {Object} + */ + on: function(object) { + for(var type in object) { + if(type != "scope" && object.hasOwnProperty(type)) { + this.register(type, object.scope, object[type]); + } + } + }, + + /** + * APIMethod: register + * Register an event on the events object. + * + * When the event is triggered, the 'func' function will be called, in the + * context of 'obj'. Imagine we were to register an event, specifying an + * OpenLayers.Bounds Object as 'obj'. When the event is triggered, the + * context in the callback function will be our Bounds object. This means + * that within our callback function, we can access the properties and + * methods of the Bounds object through the "this" variable. So our + * callback could execute something like: + * : leftStr = "Left: " + this.left; + * + * or + * + * : centerStr = "Center: " + this.getCenterLonLat(); + * + * Parameters: + * type - {String} Name of the event to register + * obj - {Object} The object to bind the context to for the callback#. + * If no object is specified, default is the Events's 'object' property. + * func - {Function} The callback function. If no callback is + * specified, this function does nothing. + * priority - {Boolean|Object} If true, adds the new listener to the + * *front* of the events queue instead of to the end. + * + * Valid options for priority: + * extension - {Boolean} If true, then the event will be registered as + * extension event. Extension events are handled before all other + * events. + */ + register: function (type, obj, func, priority) { + if (type in OpenLayers.Events && !this.extensions[type]) { + this.extensions[type] = new OpenLayers.Events[type](this); + } + if (func != null) { + if (obj == null) { + obj = this.object; + } + var listeners = this.listeners[type]; + if (!listeners) { + listeners = []; + this.listeners[type] = listeners; + this.extensionCount[type] = 0; + } + var listener = {obj: obj, func: func}; + if (priority) { + listeners.splice(this.extensionCount[type], 0, listener); + if (typeof priority === "object" && priority.extension) { + this.extensionCount[type]++; + } + } else { + listeners.push(listener); + } + } + }, + + /** + * APIMethod: registerPriority + * Same as register() but adds the new listener to the *front* of the + * events queue instead of to the end. + * + * TODO: get rid of this in 3.0 - Decide whether listeners should be + * called in the order they were registered or in reverse order. + * + * + * Parameters: + * type - {String} Name of the event to register + * obj - {Object} The object to bind the context to for the callback#. + * If no object is specified, default is the Events's + * 'object' property. + * func - {Function} The callback function. If no callback is + * specified, this function does nothing. + */ + registerPriority: function (type, obj, func) { + this.register(type, obj, func, true); + }, + + /** + * APIMethod: un + * Convenience method for unregistering listeners with a common scope. + * Internally, this method calls as shown in the examples + * below. + * + * Example use: + * (code) + * // unregister a single listener for the "loadstart" event + * events.un({"loadstart": loadStartListener}); + * + * // this is equivalent to the following + * events.unregister("loadstart", undefined, loadStartListener); + * + * // unregister multiple listeners with the same `this` object + * events.un({ + * "loadstart": loadStartListener, + * "loadend": loadEndListener, + * scope: object + * }); + * + * // this is equivalent to the following + * events.unregister("loadstart", object, loadStartListener); + * events.unregister("loadend", object, loadEndListener); + * (end) + */ + un: function(object) { + for(var type in object) { + if(type != "scope" && object.hasOwnProperty(type)) { + this.unregister(type, object.scope, object[type]); + } + } + }, + + /** + * APIMethod: unregister + * + * Parameters: + * type - {String} + * obj - {Object} If none specified, defaults to this.object + * func - {Function} + */ + unregister: function (type, obj, func) { + if (obj == null) { + obj = this.object; + } + var listeners = this.listeners[type]; + if (listeners != null) { + for (var i=0, len=listeners.length; i Math.floor(evt.pageY) || + evt.pageX === 0 && Math.floor(x) > Math.floor(evt.pageX)) { + // iOS4 include scroll offset in clientX/Y + x = x - winPageX; + y = y - winPageY; + } else if (y < (evt.pageY - winPageY) || x < (evt.pageX - winPageX) ) { + // Some Android browsers have totally bogus values for clientX/Y + // when scrolling/zooming a page + x = evt.pageX - winPageX; + y = evt.pageY - winPageY; + } + + evt.olClientX = x; + evt.olClientY = y; + + return { + clientX: x, + clientY: y + }; + }, + + /** + * APIMethod: clearMouseCache + * Clear cached data about the mouse position. This should be called any + * time the element that events are registered on changes position + * within the page. + */ + clearMouseCache: function() { + this.element.scrolls = null; + this.element.lefttop = null; + this.element.offsets = null; + }, + + /** + * Method: getMousePosition + * + * Parameters: + * evt - {Event} + * + * Returns: + * {} The current xy coordinate of the mouse, adjusted + * for offsets + */ + getMousePosition: function (evt) { + if (!this.includeXY) { + this.clearMouseCache(); + } else if (!this.element.hasScrollEvent) { + OpenLayers.Event.observe(window, "scroll", this.clearMouseListener); + this.element.hasScrollEvent = true; + } + + if (!this.element.scrolls) { + var viewportElement = OpenLayers.Util.getViewportElement(); + this.element.scrolls = [ + window.pageXOffset || viewportElement.scrollLeft, + window.pageYOffset || viewportElement.scrollTop + ]; + } + + if (!this.element.lefttop) { + this.element.lefttop = [ + (document.documentElement.clientLeft || 0), + (document.documentElement.clientTop || 0) + ]; + } + + if (!this.element.offsets) { + this.element.offsets = OpenLayers.Util.pagePosition(this.element); + } + + return new OpenLayers.Pixel( + (evt.clientX + this.element.scrolls[0]) - this.element.offsets[0] + - this.element.lefttop[0], + (evt.clientY + this.element.scrolls[1]) - this.element.offsets[1] + - this.element.lefttop[1] + ); + }, + + /** + * Method: addMsTouchListener + * + * Parameters: + * element - {DOMElement} The DOM element to register the listener on + * type - {String} The event type + * handler - {Function} the handler + */ + addMsTouchListener: function (element, type, handler) { + var eventHandler = this.eventHandler; + var touches = this._msTouches; + + function msHandler(evt) { + handler(OpenLayers.Util.applyDefaults({ + stopPropagation: function() { + for (var i=touches.length-1; i>=0; --i) { + touches[i].stopPropagation(); + } + }, + preventDefault: function() { + for (var i=touches.length-1; i>=0; --i) { + touches[i].preventDefault(); + } + }, + type: type + }, evt)); + } + + switch (type) { + case 'touchstart': + return this.addMsTouchListenerStart(element, type, msHandler); + case 'touchend': + return this.addMsTouchListenerEnd(element, type, msHandler); + case 'touchmove': + return this.addMsTouchListenerMove(element, type, msHandler); + default: + throw 'Unknown touch event type'; + } + }, + + /** + * Method: addMsTouchListenerStart + * + * Parameters: + * element - {DOMElement} The DOM element to register the listener on + * type - {String} The event type + * handler - {Function} the handler + */ + addMsTouchListenerStart: function(element, type, handler) { + var touches = this._msTouches; + + var cb = function(e) { + + var alreadyInArray = false; + for (var i=0, ii=touches.length; i when a button was + * clicked. Buttons are detected by the "olButton" class. + * + * This event type makes sure that button clicks do not interfere with other + * events that are registered on the same . + * + * Event types provided by this extension: + * - *buttonclick* Triggered when a button is clicked. Listeners receive an + * object with a *buttonElement* property referencing the dom element of + * the clicked button, and an *buttonXY* property with the click position + * relative to the button. + */ +OpenLayers.Events.buttonclick = OpenLayers.Class({ + + /** + * Property: target + * {} The events instance that the buttonclick event will + * be triggered on. + */ + target: null, + + /** + * Property: events + * {Array} Events to observe and conditionally stop from propagating when + * an element with the olButton class (or its olAlphaImg child) is + * clicked. + */ + events: [ + 'mousedown', 'mouseup', 'click', 'dblclick', + 'touchstart', 'touchmove', 'touchend', 'keydown' + ], + + /** + * Property: startRegEx + * {RegExp} Regular expression to test Event.type for events that start + * a buttonclick sequence. + */ + startRegEx: /^mousedown|touchstart$/, + + /** + * Property: cancelRegEx + * {RegExp} Regular expression to test Event.type for events that cancel + * a buttonclick sequence. + */ + cancelRegEx: /^touchmove$/, + + /** + * Property: completeRegEx + * {RegExp} Regular expression to test Event.type for events that complete + * a buttonclick sequence. + */ + completeRegEx: /^mouseup|touchend$/, + + /** + * Property: startEvt + * {Event} The event that started the click sequence + */ + + /** + * Constructor: OpenLayers.Events.buttonclick + * Construct a buttonclick event type. Applications are not supposed to + * create instances of this class - they are created on demand by + * instances. + * + * Parameters: + * target - {} The events instance that the buttonclick + * event will be triggered on. + */ + initialize: function(target) { + this.target = target; + for (var i=this.events.length-1; i>=0; --i) { + this.target.register(this.events[i], this, this.buttonClick, { + extension: true + }); + } + }, + + /** + * Method: destroy + */ + destroy: function() { + for (var i=this.events.length-1; i>=0; --i) { + this.target.unregister(this.events[i], this, this.buttonClick); + } + delete this.target; + }, + + /** + * Method: getPressedButton + * Get the pressed button, if any. Returns undefined if no button + * was pressed. + * + * Arguments: + * element - {DOMElement} The event target. + * + * Returns: + * {DOMElement} The button element, or undefined. + */ + getPressedButton: function(element) { + var depth = 3, // limit the search depth + button; + do { + if(OpenLayers.Element.hasClass(element, "olButton")) { + // hit! + button = element; + break; + } + element = element.parentNode; + } while(--depth > 0 && element); + return button; + }, + + /** + * Method: ignore + * Check for event target elements that should be ignored by OpenLayers. + * + * Parameters: + * element - {DOMElement} The event target. + */ + ignore: function(element) { + var depth = 3, + ignore = false; + do { + if (element.nodeName.toLowerCase() === 'a') { + ignore = true; + break; + } + element = element.parentNode; + } while (--depth > 0 && element); + return ignore; + }, + + /** + * Method: buttonClick + * Check if a button was clicked, and fire the buttonclick event + * + * Parameters: + * evt - {Event} + */ + buttonClick: function(evt) { + var propagate = true, + element = OpenLayers.Event.element(evt); + if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf("mouse"))) { + // was a button pressed? + var button = this.getPressedButton(element); + if (button) { + if (evt.type === "keydown") { + switch (evt.keyCode) { + case OpenLayers.Event.KEY_RETURN: + case OpenLayers.Event.KEY_SPACE: + this.target.triggerEvent("buttonclick", { + buttonElement: button + }); + OpenLayers.Event.stop(evt); + propagate = false; + break; + } + } else if (this.startEvt) { + if (this.completeRegEx.test(evt.type)) { + var pos = OpenLayers.Util.pagePosition(button); + var viewportElement = OpenLayers.Util.getViewportElement(); + var scrollTop = window.pageYOffset || viewportElement.scrollTop; + var scrollLeft = window.pageXOffset || viewportElement.scrollLeft; + pos[0] = pos[0] - scrollLeft; + pos[1] = pos[1] - scrollTop; + + this.target.triggerEvent("buttonclick", { + buttonElement: button, + buttonXY: { + x: this.startEvt.clientX - pos[0], + y: this.startEvt.clientY - pos[1] + } + }); + } + if (this.cancelRegEx.test(evt.type)) { + delete this.startEvt; + } + OpenLayers.Event.stop(evt); + propagate = false; + } + if (this.startRegEx.test(evt.type)) { + this.startEvt = evt; + OpenLayers.Event.stop(evt); + propagate = false; + } + } else { + propagate = !this.ignore(OpenLayers.Event.element(evt)); + delete this.startEvt; + } + } + return propagate; + } + +}); diff --git a/misc/openlayers/lib/OpenLayers/Events/featureclick.js b/misc/openlayers/lib/OpenLayers/Events/featureclick.js new file mode 100644 index 0000000..9ae6ec5 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Events/featureclick.js @@ -0,0 +1,321 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Events.js + */ + +/** + * Class: OpenLayers.Events.featureclick + * + * Extension event type for handling feature click events, including overlapping + * features. + * + * Event types provided by this extension: + * - featureclick + */ +OpenLayers.Events.featureclick = OpenLayers.Class({ + + /** + * Property: cache + * {Object} A cache of features under the mouse. + */ + cache: null, + + /** + * Property: map + * {} The map to register browser events on. + */ + map: null, + + /** + * Property: provides + * {Array(String)} The event types provided by this extension. + */ + provides: ["featureclick", "nofeatureclick", "featureover", "featureout"], + + /** + * Constructor: OpenLayers.Events.featureclick + * Create a new featureclick event type. + * + * Parameters: + * target - {} The events instance to create the events + * for. + */ + initialize: function(target) { + this.target = target; + if (target.object instanceof OpenLayers.Map) { + this.setMap(target.object); + } else if (target.object instanceof OpenLayers.Layer.Vector) { + if (target.object.map) { + this.setMap(target.object.map); + } else { + target.object.events.register("added", this, function(evt) { + this.setMap(target.object.map); + }); + } + } else { + throw("Listeners for '" + this.provides.join("', '") + + "' events can only be registered for OpenLayers.Layer.Vector " + + "or OpenLayers.Map instances"); + } + for (var i=this.provides.length-1; i>=0; --i) { + target.extensions[this.provides[i]] = true; + } + }, + + /** + * Method: setMap + * + * Parameters: + * map - {} The map to register browser events on. + */ + setMap: function(map) { + this.map = map; + this.cache = {}; + map.events.register("mousedown", this, this.start, {extension: true}); + map.events.register("mouseup", this, this.onClick, {extension: true}); + map.events.register("touchstart", this, this.start, {extension: true}); + map.events.register("touchmove", this, this.cancel, {extension: true}); + map.events.register("touchend", this, this.onClick, {extension: true}); + map.events.register("mousemove", this, this.onMousemove, {extension: true}); + }, + + /** + * Method: start + * Sets startEvt = evt. + * + * Parameters: + * evt - {} + */ + start: function(evt) { + this.startEvt = evt; + }, + + /** + * Method: cancel + * Deletes the start event. + * + * Parameters: + * evt - {} + */ + cancel: function(evt) { + delete this.startEvt; + }, + + /** + * Method: onClick + * Listener for the click event. + * + * Parameters: + * evt - {} + */ + onClick: function(evt) { + if (!this.startEvt || evt.type !== "touchend" && + !OpenLayers.Event.isLeftClick(evt)) { + return; + } + var features = this.getFeatures(this.startEvt); + delete this.startEvt; + // fire featureclick events + var feature, layer, more, clicked = {}; + for (var i=0, len=features.length; i} + */ + onMousemove: function(evt) { + delete this.startEvt; + var features = this.getFeatures(evt); + var over = {}, newly = [], feature; + for (var i=0, len=features.length; i)} List of features at the given point. + */ + getFeatures: function(evt) { + var x = evt.clientX, y = evt.clientY, + features = [], targets = [], layers = [], + layer, target, feature, i, len; + // go through all layers looking for targets + for (i=this.map.layers.length-1; i>=0; --i) { + layer = this.map.layers[i]; + if (layer.div.style.display !== "none") { + if (layer.renderer instanceof OpenLayers.Renderer.Elements) { + if (layer instanceof OpenLayers.Layer.Vector) { + target = document.elementFromPoint(x, y); + while (target && target._featureId) { + feature = layer.getFeatureById(target._featureId); + if (feature) { + features.push(feature); + target.style.display = "none"; + targets.push(target); + target = document.elementFromPoint(x, y); + } else { + // sketch, all bets off + target = false; + } + } + } + layers.push(layer); + layer.div.style.display = "none"; + } else if (layer.renderer instanceof OpenLayers.Renderer.Canvas) { + feature = layer.renderer.getFeatureIdFromEvent(evt); + if (feature) { + features.push(feature); + layers.push(layer); + } + } + } + } + // restore feature visibility + for (i=0, len=targets.length; i=0; --i) { + layers[i].div.style.display = "block"; + } + return features; + }, + + /** + * APIMethod: destroy + * Clean up. + */ + destroy: function() { + for (var i=this.provides.length-1; i>=0; --i) { + delete this.target.extensions[this.provides[i]]; + } + this.map.events.un({ + mousemove: this.onMousemove, + mousedown: this.start, + mouseup: this.onClick, + touchstart: this.start, + touchmove: this.cancel, + touchend: this.onClick, + scope: this + }); + delete this.cache; + delete this.map; + delete this.target; + } + +}); + +/** + * Class: OpenLayers.Events.nofeatureclick + * + * Extension event type for handling click events that do not hit a feature. + * + * Event types provided by this extension: + * - nofeatureclick + */ +OpenLayers.Events.nofeatureclick = OpenLayers.Events.featureclick; + +/** + * Class: OpenLayers.Events.featureover + * + * Extension event type for handling hovering over a feature. + * + * Event types provided by this extension: + * - featureover + */ +OpenLayers.Events.featureover = OpenLayers.Events.featureclick; + +/** + * Class: OpenLayers.Events.featureout + * + * Extension event type for handling leaving a feature. + * + * Event types provided by this extension: + * - featureout + */ +OpenLayers.Events.featureout = OpenLayers.Events.featureclick; diff --git a/misc/openlayers/lib/OpenLayers/Feature.js b/misc/openlayers/lib/OpenLayers/Feature.js new file mode 100644 index 0000000..ed7fd16 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Feature.js @@ -0,0 +1,225 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/BaseTypes/Class.js + * @requires OpenLayers/Util.js + */ + +/** + * Class: OpenLayers.Feature + * Features are combinations of geography and attributes. The OpenLayers.Feature + * class specifically combines a marker and a lonlat. + */ +OpenLayers.Feature = OpenLayers.Class({ + + /** + * Property: layer + * {} + */ + layer: null, + + /** + * Property: id + * {String} + */ + id: null, + + /** + * Property: lonlat + * {} + */ + lonlat: null, + + /** + * Property: data + * {Object} + */ + data: null, + + /** + * Property: marker + * {} + */ + marker: null, + + /** + * APIProperty: popupClass + * {} The class which will be used to instantiate + * a new Popup. Default is . + */ + popupClass: null, + + /** + * Property: popup + * {} + */ + popup: null, + + /** + * Constructor: OpenLayers.Feature + * Constructor for features. + * + * Parameters: + * layer - {} + * lonlat - {} + * data - {Object} + * + * Returns: + * {} + */ + initialize: function(layer, lonlat, data) { + this.layer = layer; + this.lonlat = lonlat; + this.data = (data != null) ? data : {}; + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); + }, + + /** + * Method: destroy + * nullify references to prevent circular references and memory leaks + */ + destroy: function() { + + //remove the popup from the map + if ((this.layer != null) && (this.layer.map != null)) { + if (this.popup != null) { + this.layer.map.removePopup(this.popup); + } + } + // remove the marker from the layer + if (this.layer != null && this.marker != null) { + this.layer.removeMarker(this.marker); + } + + this.layer = null; + this.id = null; + this.lonlat = null; + this.data = null; + if (this.marker != null) { + this.destroyMarker(this.marker); + this.marker = null; + } + if (this.popup != null) { + this.destroyPopup(this.popup); + this.popup = null; + } + }, + + /** + * Method: onScreen + * + * Returns: + * {Boolean} Whether or not the feature is currently visible on screen + * (based on its 'lonlat' property) + */ + onScreen:function() { + + var onScreen = false; + if ((this.layer != null) && (this.layer.map != null)) { + var screenBounds = this.layer.map.getExtent(); + onScreen = screenBounds.containsLonLat(this.lonlat); + } + return onScreen; + }, + + + /** + * Method: createMarker + * Based on the data associated with the Feature, create and return a marker object. + * + * Returns: + * {} A Marker Object created from the 'lonlat' and 'icon' properties + * set in this.data. If no 'lonlat' is set, returns null. If no + * 'icon' is set, OpenLayers.Marker() will load the default image. + * + * Note - this.marker is set to return value + * + */ + createMarker: function() { + + if (this.lonlat != null) { + this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon); + } + return this.marker; + }, + + /** + * Method: destroyMarker + * Destroys marker. + * If user overrides the createMarker() function, s/he should be able + * to also specify an alternative function for destroying it + */ + destroyMarker: function() { + this.marker.destroy(); + }, + + /** + * Method: createPopup + * Creates a popup object created from the 'lonlat', 'popupSize', + * and 'popupContentHTML' properties set in this.data. It uses + * this.marker.icon as default anchor. + * + * If no 'lonlat' is set, returns null. + * If no this.marker has been created, no anchor is sent. + * + * Note - the returned popup object is 'owned' by the feature, so you + * cannot use the popup's destroy method to discard the popup. + * Instead, you must use the feature's destroyPopup + * + * Note - this.popup is set to return value + * + * Parameters: + * closeBox - {Boolean} create popup with closebox or not + * + * Returns: + * {} Returns the created popup, which is also set + * as 'popup' property of this feature. Will be of whatever type + * specified by this feature's 'popupClass' property, but must be + * of type . + * + */ + createPopup: function(closeBox) { + + if (this.lonlat != null) { + if (!this.popup) { + var anchor = (this.marker) ? this.marker.icon : null; + var popupClass = this.popupClass ? + this.popupClass : OpenLayers.Popup.Anchored; + this.popup = new popupClass(this.id + "_popup", + this.lonlat, + this.data.popupSize, + this.data.popupContentHTML, + anchor, + closeBox); + } + if (this.data.overflow != null) { + this.popup.contentDiv.style.overflow = this.data.overflow; + } + + this.popup.feature = this; + } + return this.popup; + }, + + + /** + * Method: destroyPopup + * Destroys the popup created via createPopup. + * + * As with the marker, if user overrides the createPopup() function, s/he + * should also be able to override the destruction + */ + destroyPopup: function() { + if (this.popup) { + this.popup.feature = null; + this.popup.destroy(); + this.popup = null; + } + }, + + CLASS_NAME: "OpenLayers.Feature" +}); diff --git a/misc/openlayers/lib/OpenLayers/Feature/Vector.js b/misc/openlayers/lib/OpenLayers/Feature/Vector.js new file mode 100644 index 0000000..a6c8c70 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Feature/Vector.js @@ -0,0 +1,510 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +// TRASH THIS +OpenLayers.State = { + /** states */ + UNKNOWN: 'Unknown', + INSERT: 'Insert', + UPDATE: 'Update', + DELETE: 'Delete' +}; + +/** + * @requires OpenLayers/Feature.js + * @requires OpenLayers/Util.js + */ + +/** + * Class: OpenLayers.Feature.Vector + * Vector features use the OpenLayers.Geometry classes as geometry description. + * They have an 'attributes' property, which is the data object, and a 'style' + * property, the default values of which are defined in the + * objects. + * + * Inherits from: + * - + */ +OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, { + + /** + * Property: fid + * {String} + */ + fid: null, + + /** + * APIProperty: geometry + * {} + */ + geometry: null, + + /** + * APIProperty: attributes + * {Object} This object holds arbitrary, serializable properties that + * describe the feature. + */ + attributes: null, + + /** + * Property: bounds + * {} The box bounding that feature's geometry, that + * property can be set by an object when + * deserializing the feature, so in most cases it represents an + * information set by the server. + */ + bounds: null, + + /** + * Property: state + * {String} + */ + state: null, + + /** + * APIProperty: style + * {Object} + */ + style: null, + + /** + * APIProperty: url + * {String} If this property is set it will be taken into account by + * {} when upadting or deleting the feature. + */ + url: null, + + /** + * Property: renderIntent + * {String} rendering intent currently being used + */ + renderIntent: "default", + + /** + * APIProperty: modified + * {Object} An object with the originals of the geometry and attributes of + * the feature, if they were changed. Currently this property is only read + * by , and written by + * , which sets the geometry property. + * Applications can set the originals of modified attributes in the + * attributes property. Note that applications have to check if this + * object and the attributes property is already created before using it. + * After a change made with ModifyFeature, this object could look like + * + * (code) + * { + * geometry: >Object + * } + * (end) + * + * When an application has made changes to feature attributes, it could + * have set the attributes to something like this: + * + * (code) + * { + * attributes: { + * myAttribute: "original" + * } + * } + * (end) + * + * Note that only checks for truthy values in + * *modified.geometry* and the attribute names in *modified.attributes*, + * but it is recommended to set the original values (and not just true) as + * attribute value, so applications could use this information to undo + * changes. + */ + modified: null, + + /** + * Constructor: OpenLayers.Feature.Vector + * Create a vector feature. + * + * Parameters: + * geometry - {} The geometry that this feature + * represents. + * attributes - {Object} An optional object that will be mapped to the + * property. + * style - {Object} An optional style object. + */ + initialize: function(geometry, attributes, style) { + OpenLayers.Feature.prototype.initialize.apply(this, + [null, null, attributes]); + this.lonlat = null; + this.geometry = geometry ? geometry : null; + this.state = null; + this.attributes = {}; + if (attributes) { + this.attributes = OpenLayers.Util.extend(this.attributes, + attributes); + } + this.style = style ? style : null; + }, + + /** + * Method: destroy + * nullify references to prevent circular references and memory leaks + */ + destroy: function() { + if (this.layer) { + this.layer.removeFeatures(this); + this.layer = null; + } + + this.geometry = null; + this.modified = null; + OpenLayers.Feature.prototype.destroy.apply(this, arguments); + }, + + /** + * Method: clone + * Create a clone of this vector feature. Does not set any non-standard + * properties. + * + * Returns: + * {} An exact clone of this vector feature. + */ + clone: function () { + return new OpenLayers.Feature.Vector( + this.geometry ? this.geometry.clone() : null, + this.attributes, + this.style); + }, + + /** + * Method: onScreen + * Determine whether the feature is within the map viewport. This method + * tests for an intersection between the geometry and the viewport + * bounds. If a more effecient but less precise geometry bounds + * intersection is desired, call the method with the boundsOnly + * parameter true. + * + * Parameters: + * boundsOnly - {Boolean} Only test whether a feature's bounds intersects + * the viewport bounds. Default is false. If false, the feature's + * geometry must intersect the viewport for onScreen to return true. + * + * Returns: + * {Boolean} The feature is currently visible on screen (optionally + * based on its bounds if boundsOnly is true). + */ + onScreen:function(boundsOnly) { + var onScreen = false; + if(this.layer && this.layer.map) { + var screenBounds = this.layer.map.getExtent(); + if(boundsOnly) { + var featureBounds = this.geometry.getBounds(); + onScreen = screenBounds.intersectsBounds(featureBounds); + } else { + var screenPoly = screenBounds.toGeometry(); + onScreen = screenPoly.intersects(this.geometry); + } + } + return onScreen; + }, + + /** + * Method: getVisibility + * Determine whether the feature is displayed or not. It may not displayed + * because: + * - its style display property is set to 'none', + * - it doesn't belong to any layer, + * - the styleMap creates a symbolizer with display property set to 'none' + * for it, + * - the layer which it belongs to is not visible. + * + * Returns: + * {Boolean} The feature is currently displayed. + */ + getVisibility: function() { + return !(this.style && this.style.display == 'none' || + !this.layer || + this.layer && this.layer.styleMap && + this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == 'none' || + this.layer && !this.layer.getVisibility()); + }, + + /** + * Method: createMarker + * HACK - we need to decide if all vector features should be able to + * create markers + * + * Returns: + * {} For now just returns null + */ + createMarker: function() { + return null; + }, + + /** + * Method: destroyMarker + * HACK - we need to decide if all vector features should be able to + * delete markers + * + * If user overrides the createMarker() function, s/he should be able + * to also specify an alternative function for destroying it + */ + destroyMarker: function() { + // pass + }, + + /** + * Method: createPopup + * HACK - we need to decide if all vector features should be able to + * create popups + * + * Returns: + * {} For now just returns null + */ + createPopup: function() { + return null; + }, + + /** + * Method: atPoint + * Determins whether the feature intersects with the specified location. + * + * Parameters: + * lonlat - {|Object} OpenLayers.LonLat or an + * object with a 'lon' and 'lat' properties. + * toleranceLon - {float} Optional tolerance in Geometric Coords + * toleranceLat - {float} Optional tolerance in Geographic Coords + * + * Returns: + * {Boolean} Whether or not the feature is at the specified location + */ + atPoint: function(lonlat, toleranceLon, toleranceLat) { + var atPoint = false; + if(this.geometry) { + atPoint = this.geometry.atPoint(lonlat, toleranceLon, + toleranceLat); + } + return atPoint; + }, + + /** + * Method: destroyPopup + * HACK - we need to decide if all vector features should be able to + * delete popups + */ + destroyPopup: function() { + // pass + }, + + /** + * Method: move + * Moves the feature and redraws it at its new location + * + * Parameters: + * location - { or } the + * location to which to move the feature. + */ + move: function(location) { + + if(!this.layer || !this.geometry.move){ + //do nothing if no layer or immoveable geometry + return undefined; + } + + var pixel; + if (location.CLASS_NAME == "OpenLayers.LonLat") { + pixel = this.layer.getViewPortPxFromLonLat(location); + } else { + pixel = location; + } + + var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat()); + var res = this.layer.map.getResolution(); + this.geometry.move(res * (pixel.x - lastPixel.x), + res * (lastPixel.y - pixel.y)); + this.layer.drawFeature(this); + return lastPixel; + }, + + /** + * Method: toState + * Sets the new state + * + * Parameters: + * state - {String} + */ + toState: function(state) { + if (state == OpenLayers.State.UPDATE) { + switch (this.state) { + case OpenLayers.State.UNKNOWN: + case OpenLayers.State.DELETE: + this.state = state; + break; + case OpenLayers.State.UPDATE: + case OpenLayers.State.INSERT: + break; + } + } else if (state == OpenLayers.State.INSERT) { + switch (this.state) { + case OpenLayers.State.UNKNOWN: + break; + default: + this.state = state; + break; + } + } else if (state == OpenLayers.State.DELETE) { + switch (this.state) { + case OpenLayers.State.INSERT: + // the feature should be destroyed + break; + case OpenLayers.State.DELETE: + break; + case OpenLayers.State.UNKNOWN: + case OpenLayers.State.UPDATE: + this.state = state; + break; + } + } else if (state == OpenLayers.State.UNKNOWN) { + this.state = state; + } + }, + + CLASS_NAME: "OpenLayers.Feature.Vector" +}); + + +/** + * Constant: OpenLayers.Feature.Vector.style + * OpenLayers features can have a number of style attributes. The 'default' + * style will typically be used if no other style is specified. These + * styles correspond for the most part, to the styling properties defined + * by the SVG standard. + * Information on fill properties: http://www.w3.org/TR/SVG/painting.html#FillProperties + * Information on stroke properties: http://www.w3.org/TR/SVG/painting.html#StrokeProperties + * + * Symbolizer properties: + * fill - {Boolean} Set to false if no fill is desired. + * fillColor - {String} Hex fill color. Default is "#ee9900". + * fillOpacity - {Number} Fill opacity (0-1). Default is 0.4 + * stroke - {Boolean} Set to false if no stroke is desired. + * strokeColor - {String} Hex stroke color. Default is "#ee9900". + * strokeOpacity - {Number} Stroke opacity (0-1). Default is 1. + * strokeWidth - {Number} Pixel stroke width. Default is 1. + * strokeLinecap - {String} Stroke cap type. Default is "round". [butt | round | square] + * strokeDashstyle - {String} Stroke dash style. Default is "solid". [dot | dash | dashdot | longdash | longdashdot | solid] + * graphic - {Boolean} Set to false if no graphic is desired. + * pointRadius - {Number} Pixel point radius. Default is 6. + * pointerEvents - {String} Default is "visiblePainted". + * cursor - {String} Default is "". + * externalGraphic - {String} Url to an external graphic that will be used for rendering points. + * graphicWidth - {Number} Pixel width for sizing an external graphic. + * graphicHeight - {Number} Pixel height for sizing an external graphic. + * graphicOpacity - {Number} Opacity (0-1) for an external graphic. + * graphicXOffset - {Number} Pixel offset along the positive x axis for displacing an external graphic. + * graphicYOffset - {Number} Pixel offset along the positive y axis for displacing an external graphic. + * rotation - {Number} For point symbolizers, this is the rotation of a graphic in the clockwise direction about its center point (or any point off center as specified by graphicXOffset and graphicYOffset). + * graphicZIndex - {Number} The integer z-index value to use in rendering. + * graphicName - {String} Named graphic to use when rendering points. Supported values include "circle" (default), + * "square", "star", "x", "cross", "triangle". + * graphicTitle - {String} Tooltip when hovering over a feature. *deprecated*, use title instead + * title - {String} Tooltip when hovering over a feature. Not supported by the canvas renderer. + * backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic. + * backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic. + * backgroundXOffset - {Number} The x offset (in pixels) for the background graphic. + * backgroundYOffset - {Number} The y offset (in pixels) for the background graphic. + * backgroundHeight - {Number} The height of the background graphic. If not provided, the graphicHeight will be used. + * backgroundWidth - {Number} The width of the background width. If not provided, the graphicWidth will be used. + * label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either + * fillText or mozDrawText to be available. + * labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string + * composed of two characters. The first character is for the horizontal alignment, the second for the vertical + * alignment. Valid values for horizontal alignment: "l"=left, "c"=center, "r"=right. Valid values for vertical + * alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb". Default is "cm". + * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer. + * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer. + * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls. + * Default is false. + * labelOutlineColor - {String} The color of the label outline. Default is 'white'. Only supported by the canvas & SVG renderers. + * labelOutlineWidth - {Number} The width of the label outline. Default is 3, set to 0 or null to disable. Only supported by the SVG renderers. + * labelOutlineOpacity - {Number} The opacity (0-1) of the label outline. Default is fontOpacity. Only supported by the canvas & SVG renderers. + * fontColor - {String} The font color for the label, to be provided like CSS. + * fontOpacity - {Number} Opacity (0-1) for the label + * fontFamily - {String} The font family for the label, to be provided like in CSS. + * fontSize - {String} The font size for the label, to be provided like in CSS. + * fontStyle - {String} The font style for the label, to be provided like in CSS. + * fontWeight - {String} The font weight for the label, to be provided like in CSS. + * display - {String} Symbolizers will have no effect if display is set to "none". All other values have no effect. + */ +OpenLayers.Feature.Vector.style = { + 'default': { + fillColor: "#ee9900", + fillOpacity: 0.4, + hoverFillColor: "white", + hoverFillOpacity: 0.8, + strokeColor: "#ee9900", + strokeOpacity: 1, + strokeWidth: 1, + strokeLinecap: "round", + strokeDashstyle: "solid", + hoverStrokeColor: "red", + hoverStrokeOpacity: 1, + hoverStrokeWidth: 0.2, + pointRadius: 6, + hoverPointRadius: 1, + hoverPointUnit: "%", + pointerEvents: "visiblePainted", + cursor: "inherit", + fontColor: "#000000", + labelAlign: "cm", + labelOutlineColor: "white", + labelOutlineWidth: 3 + }, + 'select': { + fillColor: "blue", + fillOpacity: 0.4, + hoverFillColor: "white", + hoverFillOpacity: 0.8, + strokeColor: "blue", + strokeOpacity: 1, + strokeWidth: 2, + strokeLinecap: "round", + strokeDashstyle: "solid", + hoverStrokeColor: "red", + hoverStrokeOpacity: 1, + hoverStrokeWidth: 0.2, + pointRadius: 6, + hoverPointRadius: 1, + hoverPointUnit: "%", + pointerEvents: "visiblePainted", + cursor: "pointer", + fontColor: "#000000", + labelAlign: "cm", + labelOutlineColor: "white", + labelOutlineWidth: 3 + + }, + 'temporary': { + fillColor: "#66cccc", + fillOpacity: 0.2, + hoverFillColor: "white", + hoverFillOpacity: 0.8, + strokeColor: "#66cccc", + strokeOpacity: 1, + strokeLinecap: "round", + strokeWidth: 2, + strokeDashstyle: "solid", + hoverStrokeColor: "red", + hoverStrokeOpacity: 1, + hoverStrokeWidth: 0.2, + pointRadius: 6, + hoverPointRadius: 1, + hoverPointUnit: "%", + pointerEvents: "visiblePainted", + cursor: "inherit", + fontColor: "#000000", + labelAlign: "cm", + labelOutlineColor: "white", + labelOutlineWidth: 3 + + }, + 'delete': { + display: "none" + } +}; diff --git a/misc/openlayers/lib/OpenLayers/Filter.js b/misc/openlayers/lib/OpenLayers/Filter.js new file mode 100644 index 0000000..99c0448 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Filter.js @@ -0,0 +1,87 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/BaseTypes/Class.js + * @requires OpenLayers/Util.js + * @requires OpenLayers/Style.js + */ + +/** + * Class: OpenLayers.Filter + * This class represents an OGC Filter. + */ +OpenLayers.Filter = OpenLayers.Class({ + + /** + * Constructor: OpenLayers.Filter + * This class represents a generic filter. + * + * Parameters: + * options - {Object} Optional object whose properties will be set on the + * instance. + * + * Returns: + * {} + */ + initialize: function(options) { + OpenLayers.Util.extend(this, options); + }, + + /** + * APIMethod: destroy + * Remove reference to anything added. + */ + destroy: function() { + }, + + /** + * APIMethod: evaluate + * Evaluates this filter in a specific context. Instances or subclasses + * are supposed to override this method. + * + * Parameters: + * context - {Object} Context to use in evaluating the filter. If a vector + * feature is provided, the feature.attributes will be used as context. + * + * Returns: + * {Boolean} The filter applies. + */ + evaluate: function(context) { + return true; + }, + + /** + * APIMethod: clone + * Clones this filter. Should be implemented by subclasses. + * + * Returns: + * {} Clone of this filter. + */ + clone: function() { + return null; + }, + + /** + * APIMethod: toString + * + * Returns: + * {String} Include in your build to get a CQL + * representation of the filter returned. Otherwise "[Object object]" + * will be returned. + */ + toString: function() { + var string; + if (OpenLayers.Format && OpenLayers.Format.CQL) { + string = OpenLayers.Format.CQL.prototype.write(this); + } else { + string = Object.prototype.toString.call(this); + } + return string; + }, + + CLASS_NAME: "OpenLayers.Filter" +}); diff --git a/misc/openlayers/lib/OpenLayers/Filter/Comparison.js b/misc/openlayers/lib/OpenLayers/Filter/Comparison.js new file mode 100644 index 0000000..6863ea8 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Filter/Comparison.js @@ -0,0 +1,267 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Filter.js + */ + +/** + * Class: OpenLayers.Filter.Comparison + * This class represents a comparison filter. + * + * Inherits from: + * - + */ +OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, { + + /** + * APIProperty: type + * {String} type: type of the comparison. This is one of + * - OpenLayers.Filter.Comparison.EQUAL_TO = "=="; + * - OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!="; + * - OpenLayers.Filter.Comparison.LESS_THAN = "<"; + * - OpenLayers.Filter.Comparison.GREATER_THAN = ">"; + * - OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<="; + * - OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">="; + * - OpenLayers.Filter.Comparison.BETWEEN = ".."; + * - OpenLayers.Filter.Comparison.LIKE = "~"; + * - OpenLayers.Filter.Comparison.IS_NULL = "NULL"; + */ + type: null, + + /** + * APIProperty: property + * {String} + * name of the context property to compare + */ + property: null, + + /** + * APIProperty: value + * {Number} or {String} + * comparison value for binary comparisons. In the case of a String, this + * can be a combination of text and propertyNames in the form + * "literal ${propertyName}" + */ + value: null, + + /** + * Property: matchCase + * {Boolean} Force case sensitive searches for EQUAL_TO and NOT_EQUAL_TO + * comparisons. The Filter Encoding 1.1 specification added a matchCase + * attribute to ogc:PropertyIsEqualTo and ogc:PropertyIsNotEqualTo + * elements. This property will be serialized with those elements only + * if using the v1.1.0 filter format. However, when evaluating filters + * here, the matchCase property will always be respected (for EQUAL_TO + * and NOT_EQUAL_TO). Default is true. + */ + matchCase: true, + + /** + * APIProperty: lowerBoundary + * {Number} or {String} + * lower boundary for between comparisons. In the case of a String, this + * can be a combination of text and propertyNames in the form + * "literal ${propertyName}" + */ + lowerBoundary: null, + + /** + * APIProperty: upperBoundary + * {Number} or {String} + * upper boundary for between comparisons. In the case of a String, this + * can be a combination of text and propertyNames in the form + * "literal ${propertyName}" + */ + upperBoundary: null, + + /** + * Constructor: OpenLayers.Filter.Comparison + * Creates a comparison rule. + * + * Parameters: + * options - {Object} An optional object with properties to set on the + * rule + * + * Returns: + * {} + */ + initialize: function(options) { + OpenLayers.Filter.prototype.initialize.apply(this, [options]); + // since matchCase on PropertyIsLike is not schema compliant, we only + // want to use this if explicitly asked for + if (this.type === OpenLayers.Filter.Comparison.LIKE + && options.matchCase === undefined) { + this.matchCase = null; + } + }, + + /** + * APIMethod: evaluate + * Evaluates this filter in a specific context. + * + * Parameters: + * context - {Object} Context to use in evaluating the filter. If a vector + * feature is provided, the feature.attributes will be used as context. + * + * Returns: + * {Boolean} The filter applies. + */ + evaluate: function(context) { + if (context instanceof OpenLayers.Feature.Vector) { + context = context.attributes; + } + var result = false; + var got = context[this.property]; + var exp; + switch(this.type) { + case OpenLayers.Filter.Comparison.EQUAL_TO: + exp = this.value; + if(!this.matchCase && + typeof got == "string" && typeof exp == "string") { + result = (got.toUpperCase() == exp.toUpperCase()); + } else { + result = (got == exp); + } + break; + case OpenLayers.Filter.Comparison.NOT_EQUAL_TO: + exp = this.value; + if(!this.matchCase && + typeof got == "string" && typeof exp == "string") { + result = (got.toUpperCase() != exp.toUpperCase()); + } else { + result = (got != exp); + } + break; + case OpenLayers.Filter.Comparison.LESS_THAN: + result = got < this.value; + break; + case OpenLayers.Filter.Comparison.GREATER_THAN: + result = got > this.value; + break; + case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO: + result = got <= this.value; + break; + case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO: + result = got >= this.value; + break; + case OpenLayers.Filter.Comparison.BETWEEN: + result = (got >= this.lowerBoundary) && + (got <= this.upperBoundary); + break; + case OpenLayers.Filter.Comparison.LIKE: + var regexp = new RegExp(this.value, "gi"); + result = regexp.test(got); + break; + case OpenLayers.Filter.Comparison.IS_NULL: + result = (got === null); + break; + } + return result; + }, + + /** + * APIMethod: value2regex + * Converts the value of this rule into a regular expression string, + * according to the wildcard characters specified. This method has to + * be called after instantiation of this class, if the value is not a + * regular expression already. + * + * Parameters: + * wildCard - {Char} wildcard character in the above value, default + * is "*" + * singleChar - {Char} single-character wildcard in the above value + * default is "." + * escapeChar - {Char} escape character in the above value, default is + * "!" + * + * Returns: + * {String} regular expression string + */ + value2regex: function(wildCard, singleChar, escapeChar) { + if (wildCard == ".") { + throw new Error("'.' is an unsupported wildCard character for " + + "OpenLayers.Filter.Comparison"); + } + + + // set UMN MapServer defaults for unspecified parameters + wildCard = wildCard ? wildCard : "*"; + singleChar = singleChar ? singleChar : "."; + escapeChar = escapeChar ? escapeChar : "!"; + + this.value = this.value.replace( + new RegExp("\\"+escapeChar+"(.|$)", "g"), "\\$1"); + this.value = this.value.replace( + new RegExp("\\"+singleChar, "g"), "."); + this.value = this.value.replace( + new RegExp("\\"+wildCard, "g"), ".*"); + this.value = this.value.replace( + new RegExp("\\\\.\\*", "g"), "\\"+wildCard); + this.value = this.value.replace( + new RegExp("\\\\\\.", "g"), "\\"+singleChar); + + return this.value; + }, + + /** + * Method: regex2value + * Convert the value of this rule from a regular expression string into an + * ogc literal string using a wildCard of *, a singleChar of ., and an + * escape of !. Leaves the property unmodified. + * + * Returns: + * {String} A string value. + */ + regex2value: function() { + + var value = this.value; + + // replace ! with !! + value = value.replace(/!/g, "!!"); + + // replace \. with !. (watching out for \\.) + value = value.replace(/(\\)?\\\./g, function($0, $1) { + return $1 ? $0 : "!."; + }); + + // replace \* with #* (watching out for \\*) + value = value.replace(/(\\)?\\\*/g, function($0, $1) { + return $1 ? $0 : "!*"; + }); + + // replace \\ with \ + value = value.replace(/\\\\/g, "\\"); + + // convert .* to * (the sequence #.* is not allowed) + value = value.replace(/\.\*/g, "*"); + + return value; + }, + + /** + * APIMethod: clone + * Clones this filter. + * + * Returns: + * {} Clone of this filter. + */ + clone: function() { + return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this); + }, + + CLASS_NAME: "OpenLayers.Filter.Comparison" +}); + + +OpenLayers.Filter.Comparison.EQUAL_TO = "=="; +OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!="; +OpenLayers.Filter.Comparison.LESS_THAN = "<"; +OpenLayers.Filter.Comparison.GREATER_THAN = ">"; +OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<="; +OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">="; +OpenLayers.Filter.Comparison.BETWEEN = ".."; +OpenLayers.Filter.Comparison.LIKE = "~"; +OpenLayers.Filter.Comparison.IS_NULL = "NULL"; diff --git a/misc/openlayers/lib/OpenLayers/Filter/FeatureId.js b/misc/openlayers/lib/OpenLayers/Filter/FeatureId.js new file mode 100644 index 0000000..2927651 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Filter/FeatureId.js @@ -0,0 +1,87 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/Filter.js + */ + +/** + * Class: OpenLayers.Filter.FeatureId + * This class represents a ogc:FeatureId Filter, as being used for rule-based SLD + * styling + * + * Inherits from: + * - + */ +OpenLayers.Filter.FeatureId = OpenLayers.Class(OpenLayers.Filter, { + + /** + * APIProperty: fids + * {Array(String)} Feature Ids to evaluate this rule against. + * To be passed inside the params object. + */ + fids: null, + + /** + * Property: type + * {String} Type to identify this filter. + */ + type: "FID", + + /** + * Constructor: OpenLayers.Filter.FeatureId + * Creates an ogc:FeatureId rule. + * + * Parameters: + * options - {Object} An optional object with properties to set on the + * rule + * + * Returns: + * {} + */ + initialize: function(options) { + this.fids = []; + OpenLayers.Filter.prototype.initialize.apply(this, [options]); + }, + + /** + * APIMethod: evaluate + * evaluates this rule for a specific feature + * + * Parameters: + * feature - {} feature to apply the rule to. + * For vector features, the check is run against the fid, + * for plain features against the id. + * + * Returns: + * {Boolean} true if the rule applies, false if it does not + */ + evaluate: function(feature) { + for (var i=0, len=this.fids.length; i} Clone of this filter. + */ + clone: function() { + var filter = new OpenLayers.Filter.FeatureId(); + OpenLayers.Util.extend(filter, this); + filter.fids = this.fids.slice(); + return filter; + }, + + CLASS_NAME: "OpenLayers.Filter.FeatureId" +}); diff --git a/misc/openlayers/lib/OpenLayers/Filter/Function.js b/misc/openlayers/lib/OpenLayers/Filter/Function.js new file mode 100644 index 0000000..0d5a7a9 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Filter/Function.js @@ -0,0 +1,49 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Filter.js + */ + +/** + * Class: OpenLayers.Filter.Function + * This class represents a filter function. + * We are using this class for creation of complex + * filters that can contain filter functions as values. + * Nesting function as other functions parameter is supported. + * + * Inherits from: + * - + */ +OpenLayers.Filter.Function = OpenLayers.Class(OpenLayers.Filter, { + + /** + * APIProperty: name + * {String} Name of the function. + */ + name: null, + + /** + * APIProperty: params + * {Array( || String || Number)} Function parameters + * For now support only other Functions, String or Number + */ + params: null, + + /** + * Constructor: OpenLayers.Filter.Function + * Creates a filter function. + * + * Parameters: + * options - {Object} An optional object with properties to set on the + * function. + * + * Returns: + * {} + */ + + CLASS_NAME: "OpenLayers.Filter.Function" +}); + diff --git a/misc/openlayers/lib/OpenLayers/Filter/Logical.js b/misc/openlayers/lib/OpenLayers/Filter/Logical.js new file mode 100644 index 0000000..4eac579 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Filter/Logical.js @@ -0,0 +1,121 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/Filter.js + */ + +/** + * Class: OpenLayers.Filter.Logical + * This class represents ogc:And, ogc:Or and ogc:Not rules. + * + * Inherits from: + * - + */ +OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, { + + /** + * APIProperty: filters + * {Array()} Child filters for this filter. + */ + filters: null, + + /** + * APIProperty: type + * {String} type of logical operator. Available types are: + * - OpenLayers.Filter.Logical.AND = "&&"; + * - OpenLayers.Filter.Logical.OR = "||"; + * - OpenLayers.Filter.Logical.NOT = "!"; + */ + type: null, + + /** + * Constructor: OpenLayers.Filter.Logical + * Creates a logical filter (And, Or, Not). + * + * Parameters: + * options - {Object} An optional object with properties to set on the + * filter. + * + * Returns: + * {} + */ + initialize: function(options) { + this.filters = []; + OpenLayers.Filter.prototype.initialize.apply(this, [options]); + }, + + /** + * APIMethod: destroy + * Remove reference to child filters. + */ + destroy: function() { + this.filters = null; + OpenLayers.Filter.prototype.destroy.apply(this); + }, + + /** + * APIMethod: evaluate + * Evaluates this filter in a specific context. + * + * Parameters: + * context - {Object} Context to use in evaluating the filter. A vector + * feature may also be provided to evaluate feature attributes in + * comparison filters or geometries in spatial filters. + * + * Returns: + * {Boolean} The filter applies. + */ + evaluate: function(context) { + var i, len; + switch(this.type) { + case OpenLayers.Filter.Logical.AND: + for (i=0, len=this.filters.length; i} Clone of this filter. + */ + clone: function() { + var filters = []; + for(var i=0, len=this.filters.length; i + */ +OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, { + + /** + * APIProperty: type + * {String} Type of spatial filter. + * + * The type should be one of: + * - OpenLayers.Filter.Spatial.BBOX + * - OpenLayers.Filter.Spatial.INTERSECTS + * - OpenLayers.Filter.Spatial.DWITHIN + * - OpenLayers.Filter.Spatial.WITHIN + * - OpenLayers.Filter.Spatial.CONTAINS + */ + type: null, + + /** + * APIProperty: property + * {String} Name of the context property to compare. + */ + property: null, + + /** + * APIProperty: value + * { || } The bounds or geometry + * to be used by the filter. Use bounds for BBOX filters and geometry + * for INTERSECTS or DWITHIN filters. + */ + value: null, + + /** + * APIProperty: distance + * {Number} The distance to use in a DWithin spatial filter. + */ + distance: null, + + /** + * APIProperty: distanceUnits + * {String} The units to use for the distance, e.g. 'm'. + */ + distanceUnits: null, + + /** + * Constructor: OpenLayers.Filter.Spatial + * Creates a spatial filter. + * + * Parameters: + * options - {Object} An optional object with properties to set on the + * filter. + * + * Returns: + * {} + */ + + /** + * Method: evaluate + * Evaluates this filter for a specific feature. + * + * Parameters: + * feature - {} feature to apply the filter to. + * + * Returns: + * {Boolean} The feature meets filter criteria. + */ + evaluate: function(feature) { + var intersect = false; + switch(this.type) { + case OpenLayers.Filter.Spatial.BBOX: + case OpenLayers.Filter.Spatial.INTERSECTS: + if(feature.geometry) { + var geom = this.value; + if(this.value.CLASS_NAME == "OpenLayers.Bounds") { + geom = this.value.toGeometry(); + } + if(feature.geometry.intersects(geom)) { + intersect = true; + } + } + break; + default: + throw new Error('evaluate is not implemented for this filter type.'); + } + return intersect; + }, + + /** + * APIMethod: clone + * Clones this filter. + * + * Returns: + * {} Clone of this filter. + */ + clone: function() { + var options = OpenLayers.Util.applyDefaults({ + value: this.value && this.value.clone && this.value.clone() + }, this); + return new OpenLayers.Filter.Spatial(options); + }, + CLASS_NAME: "OpenLayers.Filter.Spatial" +}); + +OpenLayers.Filter.Spatial.BBOX = "BBOX"; +OpenLayers.Filter.Spatial.INTERSECTS = "INTERSECTS"; +OpenLayers.Filter.Spatial.DWITHIN = "DWITHIN"; +OpenLayers.Filter.Spatial.WITHIN = "WITHIN"; +OpenLayers.Filter.Spatial.CONTAINS = "CONTAINS"; diff --git a/misc/openlayers/lib/OpenLayers/Format.js b/misc/openlayers/lib/OpenLayers/Format.js new file mode 100644 index 0000000..620ecc7 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format.js @@ -0,0 +1,123 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/BaseTypes/Class.js + * @requires OpenLayers/Util.js + */ + +/** + * Class: OpenLayers.Format + * Base class for format reading/writing a variety of formats. Subclasses + * of OpenLayers.Format are expected to have read and write methods. + */ +OpenLayers.Format = OpenLayers.Class({ + + /** + * Property: options + * {Object} A reference to options passed to the constructor. + */ + options: null, + + /** + * APIProperty: externalProjection + * {} When passed a externalProjection and + * internalProjection, the format will reproject the geometries it + * reads or writes. The externalProjection is the projection used by + * the content which is passed into read or which comes out of write. + * In order to reproject, a projection transformation function for the + * specified projections must be available. This support may be + * provided via proj4js or via a custom transformation function. See + * {} for more information on + * custom transformations. + */ + externalProjection: null, + + /** + * APIProperty: internalProjection + * {} When passed a externalProjection and + * internalProjection, the format will reproject the geometries it + * reads or writes. The internalProjection is the projection used by + * the geometries which are returned by read or which are passed into + * write. In order to reproject, a projection transformation function + * for the specified projections must be available. This support may be + * provided via proj4js or via a custom transformation function. See + * {} for more information on + * custom transformations. + */ + internalProjection: null, + + /** + * APIProperty: data + * {Object} When is true, this is the parsed string sent to + * . + */ + data: null, + + /** + * APIProperty: keepData + * {Object} Maintain a reference () to the most recently read data. + * Default is false. + */ + keepData: false, + + /** + * Constructor: OpenLayers.Format + * Instances of this class are not useful. See one of the subclasses. + * + * Parameters: + * options - {Object} An optional object with properties to set on the + * format + * + * Valid options: + * keepData - {Boolean} If true, upon , the data property will be + * set to the parsed object (e.g. the json or xml object). + * + * Returns: + * An instance of OpenLayers.Format + */ + initialize: function(options) { + OpenLayers.Util.extend(this, options); + this.options = options; + }, + + /** + * APIMethod: destroy + * Clean up. + */ + destroy: function() { + }, + + /** + * Method: read + * Read data from a string, and return an object whose type depends on the + * subclass. + * + * Parameters: + * data - {string} Data to read/parse. + * + * Returns: + * Depends on the subclass + */ + read: function(data) { + throw new Error('Read not implemented.'); + }, + + /** + * Method: write + * Accept an object, and return a string. + * + * Parameters: + * object - {Object} Object to be serialized + * + * Returns: + * {String} A string representation of the object. + */ + write: function(object) { + throw new Error('Write not implemented.'); + }, + + CLASS_NAME: "OpenLayers.Format" +}); diff --git a/misc/openlayers/lib/OpenLayers/Format/ArcXML.js b/misc/openlayers/lib/OpenLayers/Format/ArcXML.js new file mode 100644 index 0000000..9d523d1 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/ArcXML.js @@ -0,0 +1,1028 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/XML.js + * @requires OpenLayers/Geometry/Polygon.js + * @requires OpenLayers/Geometry/Point.js + * @requires OpenLayers/Geometry/MultiPolygon.js + * @requires OpenLayers/Geometry/LinearRing.js + */ + +/** + * Class: OpenLayers.Format.ArcXML + * Read/Write ArcXML. Create a new instance with the + * constructor. + * + * Inherits from: + * - + */ +OpenLayers.Format.ArcXML = OpenLayers.Class(OpenLayers.Format.XML, { + + /** + * Property: fontStyleKeys + * {Array} List of keys used in font styling. + */ + fontStyleKeys: [ + 'antialiasing', 'blockout', 'font', 'fontcolor','fontsize', 'fontstyle', + 'glowing', 'interval', 'outline', 'printmode', 'shadow', 'transparency' + ], + + /** + * Property: request + * A get_image request destined for an ArcIMS server. + */ + request: null, + + /** + * Property: response + * A parsed response from an ArcIMS server. + */ + response: null, + + /** + * Constructor: OpenLayers.Format.ArcXML + * Create a new parser/writer for ArcXML. Create an instance of this class + * to begin authoring a request to an ArcIMS service. This is used + * primarily by the ArcIMS layer, but could be used to do other wild + * stuff, like geocoding. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + */ + initialize: function(options) { + this.request = new OpenLayers.Format.ArcXML.Request(); + this.response = new OpenLayers.Format.ArcXML.Response(); + + if (options) { + if (options.requesttype == "feature") { + this.request.get_image = null; + + var qry = this.request.get_feature.query; + this.addCoordSys(qry.featurecoordsys, options.featureCoordSys); + this.addCoordSys(qry.filtercoordsys, options.filterCoordSys); + + if (options.polygon) { + qry.isspatial = true; + qry.spatialfilter.polygon = options.polygon; + } else if (options.envelope) { + qry.isspatial = true; + qry.spatialfilter.envelope = {minx:0, miny:0, maxx:0, maxy:0}; + this.parseEnvelope(qry.spatialfilter.envelope, options.envelope); + } + } else if (options.requesttype == "image") { + this.request.get_feature = null; + + var props = this.request.get_image.properties; + this.parseEnvelope(props.envelope, options.envelope); + + this.addLayers(props.layerlist, options.layers); + this.addImageSize(props.imagesize, options.tileSize); + this.addCoordSys(props.featurecoordsys, options.featureCoordSys); + this.addCoordSys(props.filtercoordsys, options.filterCoordSys); + } else { + // if an arcxml object is being created with no request type, it is + // probably going to consume a response, so do not throw an error if + // the requesttype is not defined + this.request = null; + } + } + + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); + }, + + /** + * Method: parseEnvelope + * Parse an array of coordinates into an ArcXML envelope structure. + * + * Parameters: + * env - {Object} An envelope object that will contain the parsed coordinates. + * arr - {Array(double)} An array of coordinates in the order: [ minx, miny, maxx, maxy ] + */ + parseEnvelope: function(env, arr) { + if (arr && arr.length == 4) { + env.minx = arr[0]; + env.miny = arr[1]; + env.maxx = arr[2]; + env.maxy = arr[3]; + } + }, + + /** + * Method: addLayers + * Add a collection of layers to another collection of layers. Each layer in the list is tuple of + * { id, visible }. These layer collections represent the + * /ARCXML/REQUEST/get_image/PROPERTIES/LAYERLIST/LAYERDEF items in ArcXML + * + * TODO: Add support for dynamic layer rendering. + * + * Parameters: + * ll - {Array({id,visible})} A list of layer definitions. + * lyrs - {Array({id,visible})} A list of layer definitions. + */ + addLayers: function(ll, lyrs) { + for(var lind = 0, len=lyrs.length; lind < len; lind++) { + ll.push(lyrs[lind]); + } + }, + + /** + * Method: addImageSize + * Set the size of the requested image. + * + * Parameters: + * imsize - {Object} An ArcXML imagesize object. + * olsize - {} The image size to set. + */ + addImageSize: function(imsize, olsize) { + if (olsize !== null) { + imsize.width = olsize.w; + imsize.height = olsize.h; + imsize.printwidth = olsize.w; + imsize.printheight = olsize.h; + } + }, + + /** + * Method: addCoordSys + * Add the coordinate system information to an object. The object may be + * + * Parameters: + * featOrFilt - {Object} A featurecoordsys or filtercoordsys ArcXML structure. + * fsys - {String} or {} or {filtercoordsys} or + * {featurecoordsys} A projection representation. If it's a {String}, + * the value is assumed to be the SRID. If it's a {OpenLayers.Projection} + * AND Proj4js is available, the projection number and name are extracted + * from there. If it's a filter or feature ArcXML structure, it is copied. + */ + addCoordSys: function(featOrFilt, fsys) { + if (typeof fsys == "string") { + featOrFilt.id = parseInt(fsys); + featOrFilt.string = fsys; + } + // is this a proj4js instance? + else if (typeof fsys == "object" && fsys.proj !== null){ + featOrFilt.id = fsys.proj.srsProjNumber; + featOrFilt.string = fsys.proj.srsCode; + } else { + featOrFilt = fsys; + } + }, + + /** + * APIMethod: iserror + * Check to see if the response from the server was an error. + * + * Parameters: + * data - {String} or {DOMElement} data to read/parse. If nothing is supplied, + * the current response is examined. + * + * Returns: + * {Boolean} true if the response was an error. + */ + iserror: function(data) { + var ret = null; + + if (!data) { + ret = (this.response.error !== ''); + } else { + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); + var errorNodes = data.documentElement.getElementsByTagName("ERROR"); + ret = (errorNodes !== null && errorNodes.length > 0); + } + + return ret; + }, + + /** + * APIMethod: read + * Read data from a string, and return an response. + * + * Parameters: + * data - {String} or {DOMElement} data to read/parse. + * + * Returns: + * {} An ArcXML response. Note that this response + * data may change in the future. + */ + read: function(data) { + if(typeof data == "string") { + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); + } + + var arcNode = null; + if (data && data.documentElement) { + if(data.documentElement.nodeName == "ARCXML") { + arcNode = data.documentElement; + } else { + arcNode = data.documentElement.getElementsByTagName("ARCXML")[0]; + } + } + + // in Safari, arcNode will be there but will have a child named + // parsererror + if (!arcNode || arcNode.firstChild.nodeName === 'parsererror') { + var error, source; + try { + error = data.firstChild.nodeValue; + source = data.firstChild.childNodes[1].firstChild.nodeValue; + } catch (err) { + // pass + } + throw { + message: "Error parsing the ArcXML request", + error: error, + source: source + }; + } + + var response = this.parseResponse(arcNode); + return response; + }, + + /** + * APIMethod: write + * Generate an ArcXml document string for sending to an ArcIMS server. + * + * Returns: + * {String} A string representing the ArcXML document request. + */ + write: function(request) { + if (!request) { + request = this.request; + } + var root = this.createElementNS("", "ARCXML"); + root.setAttribute("version","1.1"); + + var reqElem = this.createElementNS("", "REQUEST"); + + if (request.get_image != null) { + var getElem = this.createElementNS("", "GET_IMAGE"); + reqElem.appendChild(getElem); + + var propElem = this.createElementNS("", "PROPERTIES"); + getElem.appendChild(propElem); + + var props = request.get_image.properties; + if (props.featurecoordsys != null) { + var feat = this.createElementNS("", "FEATURECOORDSYS"); + propElem.appendChild(feat); + + if (props.featurecoordsys.id === 0) { + feat.setAttribute("string", props.featurecoordsys['string']); + } + else { + feat.setAttribute("id", props.featurecoordsys.id); + } + } + + if (props.filtercoordsys != null) { + var filt = this.createElementNS("", "FILTERCOORDSYS"); + propElem.appendChild(filt); + + if (props.filtercoordsys.id === 0) { + filt.setAttribute("string", props.filtercoordsys.string); + } + else { + filt.setAttribute("id", props.filtercoordsys.id); + } + } + + if (props.envelope != null) { + var env = this.createElementNS("", "ENVELOPE"); + propElem.appendChild(env); + + env.setAttribute("minx", props.envelope.minx); + env.setAttribute("miny", props.envelope.miny); + env.setAttribute("maxx", props.envelope.maxx); + env.setAttribute("maxy", props.envelope.maxy); + } + + var imagesz = this.createElementNS("", "IMAGESIZE"); + propElem.appendChild(imagesz); + + imagesz.setAttribute("height", props.imagesize.height); + imagesz.setAttribute("width", props.imagesize.width); + + if (props.imagesize.height != props.imagesize.printheight || + props.imagesize.width != props.imagesize.printwidth) { + imagesz.setAttribute("printheight", props.imagesize.printheight); + imagesz.setArrtibute("printwidth", props.imagesize.printwidth); + } + + if (props.background != null) { + var backgrnd = this.createElementNS("", "BACKGROUND"); + propElem.appendChild(backgrnd); + + backgrnd.setAttribute("color", + props.background.color.r + "," + + props.background.color.g + "," + + props.background.color.b); + + if (props.background.transcolor !== null) { + backgrnd.setAttribute("transcolor", + props.background.transcolor.r + "," + + props.background.transcolor.g + "," + + props.background.transcolor.b); + } + } + + if (props.layerlist != null && props.layerlist.length > 0) { + var layerlst = this.createElementNS("", "LAYERLIST"); + propElem.appendChild(layerlst); + + for (var ld = 0; ld < props.layerlist.length; ld++) { + var ldef = this.createElementNS("", "LAYERDEF"); + layerlst.appendChild(ldef); + + ldef.setAttribute("id", props.layerlist[ld].id); + ldef.setAttribute("visible", props.layerlist[ld].visible); + + if (typeof props.layerlist[ld].query == "object") { + var query = props.layerlist[ld].query; + + if (query.where.length < 0) { + continue; + } + + var queryElem = null; + if (typeof query.spatialfilter == "boolean" && query.spatialfilter) { + // handle spatial filter madness + queryElem = this.createElementNS("", "SPATIALQUERY"); + } + else { + queryElem = this.createElementNS("", "QUERY"); + } + + queryElem.setAttribute("where", query.where); + + if (typeof query.accuracy == "number" && query.accuracy > 0) { + queryElem.setAttribute("accuracy", query.accuracy); + } + if (typeof query.featurelimit == "number" && query.featurelimit < 2000) { + queryElem.setAttribute("featurelimit", query.featurelimit); + } + if (typeof query.subfields == "string" && query.subfields != "#ALL#") { + queryElem.setAttribute("subfields", query.subfields); + } + if (typeof query.joinexpression == "string" && query.joinexpression.length > 0) { + queryElem.setAttribute("joinexpression", query.joinexpression); + } + if (typeof query.jointables == "string" && query.jointables.length > 0) { + queryElem.setAttribute("jointables", query.jointables); + } + + ldef.appendChild(queryElem); + } + + if (typeof props.layerlist[ld].renderer == "object") { + this.addRenderer(ldef, props.layerlist[ld].renderer); + } + } + } + } else if (request.get_feature != null) { + var getElem = this.createElementNS("", "GET_FEATURES"); + getElem.setAttribute("outputmode", "newxml"); + getElem.setAttribute("checkesc", "true"); + + if (request.get_feature.geometry) { + getElem.setAttribute("geometry", request.get_feature.geometry); + } + else { + getElem.setAttribute("geometry", "false"); + } + + if (request.get_feature.compact) { + getElem.setAttribute("compact", request.get_feature.compact); + } + + if (request.get_feature.featurelimit == "number") { + getElem.setAttribute("featurelimit", request.get_feature.featurelimit); + } + + getElem.setAttribute("globalenvelope", "true"); + reqElem.appendChild(getElem); + + if (request.get_feature.layer != null && request.get_feature.layer.length > 0) { + var lyrElem = this.createElementNS("", "LAYER"); + lyrElem.setAttribute("id", request.get_feature.layer); + getElem.appendChild(lyrElem); + } + + var fquery = request.get_feature.query; + if (fquery != null) { + var qElem = null; + if (fquery.isspatial) { + qElem = this.createElementNS("", "SPATIALQUERY"); + } else { + qElem = this.createElementNS("", "QUERY"); + } + getElem.appendChild(qElem); + + if (typeof fquery.accuracy == "number") { + qElem.setAttribute("accuracy", fquery.accuracy); + } + //qElem.setAttribute("featurelimit", "5"); + + if (fquery.featurecoordsys != null) { + var fcsElem1 = this.createElementNS("", "FEATURECOORDSYS"); + + if (fquery.featurecoordsys.id == 0) { + fcsElem1.setAttribute("string", fquery.featurecoordsys.string); + } else { + fcsElem1.setAttribute("id", fquery.featurecoordsys.id); + } + qElem.appendChild(fcsElem1); + } + + if (fquery.filtercoordsys != null) { + var fcsElem2 = this.createElementNS("", "FILTERCOORDSYS"); + + if (fquery.filtercoordsys.id === 0) { + fcsElem2.setAttribute("string", fquery.filtercoordsys.string); + } else { + fcsElem2.setAttribute("id", fquery.filtercoordsys.id); + } + qElem.appendChild(fcsElem2); + } + + if (fquery.buffer > 0) { + var bufElem = this.createElementNS("", "BUFFER"); + bufElem.setAttribute("distance", fquery.buffer); + qElem.appendChild(bufElem); + } + + if (fquery.isspatial) { + var spfElem = this.createElementNS("", "SPATIALFILTER"); + spfElem.setAttribute("relation", fquery.spatialfilter.relation); + qElem.appendChild(spfElem); + + if (fquery.spatialfilter.envelope) { + var envElem = this.createElementNS("", "ENVELOPE"); + envElem.setAttribute("minx", fquery.spatialfilter.envelope.minx); + envElem.setAttribute("miny", fquery.spatialfilter.envelope.miny); + envElem.setAttribute("maxx", fquery.spatialfilter.envelope.maxx); + envElem.setAttribute("maxy", fquery.spatialfilter.envelope.maxy); + spfElem.appendChild(envElem); + } else if(typeof fquery.spatialfilter.polygon == "object") { + spfElem.appendChild(this.writePolygonGeometry(fquery.spatialfilter.polygon)); + } + } + + if (fquery.where != null && fquery.where.length > 0) { + qElem.setAttribute("where", fquery.where); + } + } + } + + root.appendChild(reqElem); + + return OpenLayers.Format.XML.prototype.write.apply(this, [root]); + }, + + + addGroupRenderer: function(ldef, toprenderer) { + var topRelem = this.createElementNS("", "GROUPRENDERER"); + ldef.appendChild(topRelem); + + for (var rind = 0; rind < toprenderer.length; rind++) { + var renderer = toprenderer[rind]; + this.addRenderer(topRelem, renderer); + } + }, + + + addRenderer: function(topRelem, renderer) { + if (OpenLayers.Util.isArray(renderer)) { + this.addGroupRenderer(topRelem, renderer); + } else { + var renderElem = this.createElementNS("", renderer.type.toUpperCase() + "RENDERER"); + topRelem.appendChild(renderElem); + + if (renderElem.tagName == "VALUEMAPRENDERER") { + this.addValueMapRenderer(renderElem, renderer); + } else if (renderElem.tagName == "VALUEMAPLABELRENDERER") { + this.addValueMapLabelRenderer(renderElem, renderer); + } else if (renderElem.tagName == "SIMPLELABELRENDERER") { + this.addSimpleLabelRenderer(renderElem, renderer); + } else if (renderElem.tagName == "SCALEDEPENDENTRENDERER") { + this.addScaleDependentRenderer(renderElem, renderer); + } + } + }, + + + addScaleDependentRenderer: function(renderElem, renderer) { + if (typeof renderer.lower == "string" || typeof renderer.lower == "number") { + renderElem.setAttribute("lower", renderer.lower); + } + if (typeof renderer.upper == "string" || typeof renderer.upper == "number") { + renderElem.setAttribute("upper", renderer.upper); + } + + this.addRenderer(renderElem, renderer.renderer); + }, + + + addValueMapLabelRenderer: function(renderElem, renderer) { + renderElem.setAttribute("lookupfield", renderer.lookupfield); + renderElem.setAttribute("labelfield", renderer.labelfield); + + if (typeof renderer.exacts == "object") { + for (var ext=0, extlen=renderer.exacts.length; ext 0) { + response.error = this.getChildValue(errorNode, "Unknown error."); + } else { + var responseNode = data.getElementsByTagName("RESPONSE"); + + if (responseNode == null || responseNode.length == 0) { + response.error = "No RESPONSE tag found in ArcXML response."; + return response; + } + + var rtype = responseNode[0].firstChild.nodeName; + if (rtype == "#text") { + rtype = responseNode[0].firstChild.nextSibling.nodeName; + } + + if (rtype == "IMAGE") { + var envelopeNode = data.getElementsByTagName("ENVELOPE"); + var outputNode = data.getElementsByTagName("OUTPUT"); + + if (envelopeNode == null || envelopeNode.length == 0) { + response.error = "No ENVELOPE tag found in ArcXML response."; + } else if (outputNode == null || outputNode.length == 0) { + response.error = "No OUTPUT tag found in ArcXML response."; + } else { + var envAttr = this.parseAttributes(envelopeNode[0]); + var outputAttr = this.parseAttributes(outputNode[0]); + + if (typeof outputAttr.type == "string") { + response.image = { + envelope: envAttr, + output: { + type: outputAttr.type, + data: this.getChildValue(outputNode[0]) + } + }; + } else { + response.image = { envelope: envAttr, output: outputAttr }; + } + } + } else if (rtype == "FEATURES") { + var features = responseNode[0].getElementsByTagName("FEATURES"); + + // get the feature count + var featureCount = features[0].getElementsByTagName("FEATURECOUNT"); + response.features.featurecount = featureCount[0].getAttribute("count"); + + if (response.features.featurecount > 0) { + // get the feature envelope + var envelope = features[0].getElementsByTagName("ENVELOPE"); + response.features.envelope = this.parseAttributes(envelope[0], typeof(0)); + + // get the field values per feature + var featureList = features[0].getElementsByTagName("FEATURE"); + for (var fn = 0; fn < featureList.length; fn++) { + var feature = new OpenLayers.Feature.Vector(); + var fields = featureList[fn].getElementsByTagName("FIELD"); + + for (var fdn = 0; fdn < fields.length; fdn++) { + var fieldName = fields[fdn].getAttribute("name"); + var fieldValue = fields[fdn].getAttribute("value"); + feature.attributes[ fieldName ] = fieldValue; + } + + var geom = featureList[fn].getElementsByTagName("POLYGON"); + + if (geom.length > 0) { + // if there is a polygon, create an openlayers polygon, and assign + // it to the .geometry property of the feature + var ring = geom[0].getElementsByTagName("RING"); + + var polys = []; + for (var rn = 0; rn < ring.length; rn++) { + var linearRings = []; + linearRings.push(this.parsePointGeometry(ring[rn])); + + var holes = ring[rn].getElementsByTagName("HOLE"); + for (var hn = 0; hn < holes.length; hn++) { + linearRings.push(this.parsePointGeometry(holes[hn])); + } + holes = null; + polys.push(new OpenLayers.Geometry.Polygon(linearRings)); + linearRings = null; + } + ring = null; + + if (polys.length == 1) { + feature.geometry = polys[0]; + } else + { + feature.geometry = new OpenLayers.Geometry.MultiPolygon(polys); + } + } + + response.features.feature.push(feature); + } + } + } else { + response.error = "Unidentified response type."; + } + } + return response; + }, + + + /** + * Method: parseAttributes + * + * Parameters: + * node - {} An element to parse attributes from. + * + * Returns: + * {Object} An attributes object, with properties set to attribute values. + */ + parseAttributes: function(node,type) { + var attributes = {}; + for(var attr = 0; attr < node.attributes.length; attr++) { + if (type == "number") { + attributes[node.attributes[attr].nodeName] = parseFloat(node.attributes[attr].nodeValue); + } else { + attributes[node.attributes[attr].nodeName] = node.attributes[attr].nodeValue; + } + } + return attributes; + }, + + + /** + * Method: parsePointGeometry + * + * Parameters: + * node - {} An element to parse or arcxml data from. + * + * Returns: + * {} A linear ring represented by the node's points. + */ + parsePointGeometry: function(node) { + var ringPoints = []; + var coords = node.getElementsByTagName("COORDS"); + + if (coords.length > 0) { + // if coords is present, it's the only coords item + var coordArr = this.getChildValue(coords[0]); + coordArr = coordArr.split(/;/); + for (var cn = 0; cn < coordArr.length; cn++) { + var coordItems = coordArr[cn].split(/ /); + ringPoints.push(new OpenLayers.Geometry.Point(coordItems[0], coordItems[1])); + } + coords = null; + } else { + var point = node.getElementsByTagName("POINT"); + if (point.length > 0) { + for (var pn = 0; pn < point.length; pn++) { + ringPoints.push( + new OpenLayers.Geometry.Point( + parseFloat(point[pn].getAttribute("x")), + parseFloat(point[pn].getAttribute("y")) + ) + ); + } + } + point = null; + } + + return new OpenLayers.Geometry.LinearRing(ringPoints); + }, + + CLASS_NAME: "OpenLayers.Format.ArcXML" +}); + +OpenLayers.Format.ArcXML.Request = OpenLayers.Class({ + initialize: function(params) { + var defaults = { + get_image: { + properties: { + background: null, + /*{ + color: { r:255, g:255, b:255 }, + transcolor: null + },*/ + draw: true, + envelope: { + minx: 0, + miny: 0, + maxx: 0, + maxy: 0 + }, + featurecoordsys: { + id:0, + string:"", + datumtransformid:0, + datumtransformstring:"" + }, + filtercoordsys:{ + id:0, + string:"", + datumtransformid:0, + datumtransformstring:"" + }, + imagesize:{ + height:0, + width:0, + dpi:96, + printheight:0, + printwidth:0, + scalesymbols:false + }, + layerlist:[], + /* no support for legends */ + output:{ + baseurl:"", + legendbaseurl:"", + legendname:"", + legendpath:"", + legendurl:"", + name:"", + path:"", + type:"jpg", + url:"" + } + } + }, + + get_feature: { + layer: "", + query: { + isspatial: false, + featurecoordsys: { + id:0, + string:"", + datumtransformid:0, + datumtransformstring:"" + }, + filtercoordsys: { + id:0, + string:"", + datumtransformid:0, + datumtransformstring:"" + }, + buffer:0, + where:"", + spatialfilter: { + relation: "envelope_intersection", + envelope: null + } + } + }, + + environment: { + separators: { + cs:" ", + ts:";" + } + }, + + layer: [], + workspaces: [] + }; + + return OpenLayers.Util.extend(this, defaults); + }, + + CLASS_NAME: "OpenLayers.Format.ArcXML.Request" +}); + +OpenLayers.Format.ArcXML.Response = OpenLayers.Class({ + initialize: function(params) { + var defaults = { + image: { + envelope:null, + output:'' + }, + + features: { + featurecount: 0, + envelope: null, + feature: [] + }, + + error:'' + }; + + return OpenLayers.Util.extend(this, defaults); + }, + + CLASS_NAME: "OpenLayers.Format.ArcXML.Response" +}); diff --git a/misc/openlayers/lib/OpenLayers/Format/ArcXML/Features.js b/misc/openlayers/lib/OpenLayers/Format/ArcXML/Features.js new file mode 100644 index 0000000..5b8730d --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/ArcXML/Features.js @@ -0,0 +1,46 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/ArcXML.js + */ + +/** + * Class: OpenLayers.Format.ArcXML.Features + * Read/Write ArcXML features. Create a new instance with the + * constructor. + * + * Inherits from: + * - + */ +OpenLayers.Format.ArcXML.Features = OpenLayers.Class(OpenLayers.Format.XML, { + + /** + * Constructor: OpenLayers.Format.ArcXML.Features + * Create a new parser/writer for ArcXML Features. Create an instance of this class + * to get a set of features from an ArcXML response. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + */ + + /** + * APIMethod: read + * Read data from a string of ArcXML, and return a set of OpenLayers features. + * + * Parameters: + * data - {String} or {DOMElement} data to read/parse. + * + * Returns: + * {Array()} A collection of features. + */ + read: function(data) { + var axl = new OpenLayers.Format.ArcXML(); + var parsed = axl.read(data); + + return parsed.features.feature; + } +}); diff --git a/misc/openlayers/lib/OpenLayers/Format/Atom.js b/misc/openlayers/lib/OpenLayers/Format/Atom.js new file mode 100644 index 0000000..8eb5792 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/Atom.js @@ -0,0 +1,712 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/XML.js + * @requires OpenLayers/Format/GML/v3.js + * @requires OpenLayers/Feature/Vector.js + */ + +/** + * Class: OpenLayers.Format.Atom + * Read/write Atom feeds. Create a new instance with the + * constructor. + * + * Inherits from: + * - + */ +OpenLayers.Format.Atom = OpenLayers.Class(OpenLayers.Format.XML, { + + /** + * Property: namespaces + * {Object} Mapping of namespace aliases to namespace URIs. Properties + * of this object should not be set individually. Read-only. All + * XML subclasses should have their own namespaces object. Use + * to add or set a namespace alias after construction. + */ + namespaces: { + atom: "http://www.w3.org/2005/Atom", + georss: "http://www.georss.org/georss" + }, + + /** + * APIProperty: feedTitle + * {String} Atom feed elements require a title. Default is "untitled". + */ + feedTitle: "untitled", + + /** + * APIProperty: defaultEntryTitle + * {String} Atom entry elements require a title. In cases where one is + * not provided in the feature attributes, this will be used. Default + * is "untitled". + */ + defaultEntryTitle: "untitled", + + /** + * Property: gmlParse + * {Object} GML Format object for parsing features + * Non-API and only created if necessary + */ + gmlParser: null, + + /** + * APIProperty: xy + * {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x) + * For GeoRSS the default is (y,x), therefore: false + */ + xy: false, + + /** + * Constructor: OpenLayers.Format.AtomEntry + * Create a new parser for Atom. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + */ + + /** + * APIMethod: read + * Return a list of features from an Atom feed or entry document. + + * Parameters: + * doc - {Element} or {String} + * + * Returns: + * Array({}) + */ + read: function(doc) { + if (typeof doc == "string") { + doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); + } + return this.parseFeatures(doc); + }, + + /** + * APIMethod: write + * Serialize or more feature nodes to Atom documents. + * + * Parameters: + * features - {} or Array({}) + * + * Returns: + * {String} an Atom entry document if passed one feature node, or a feed + * document if passed an array of feature nodes. + */ + write: function(features) { + var doc; + if (OpenLayers.Util.isArray(features)) { + doc = this.createElementNSPlus("atom:feed"); + doc.appendChild( + this.createElementNSPlus("atom:title", { + value: this.feedTitle + }) + ); + for (var i=0, ii=features.length; i} + * + * Returns: + * {DOMElement} an Atom entry node. + * + * These entries are geared for publication using AtomPub. + * + * TODO: support extension elements + */ + buildEntryNode: function(feature) { + var attrib = feature.attributes; + var atomAttrib = attrib.atom || {}; + var entryNode = this.createElementNSPlus("atom:entry"); + + // atom:author + if (atomAttrib.authors) { + var authors = OpenLayers.Util.isArray(atomAttrib.authors) ? + atomAttrib.authors : [atomAttrib.authors]; + for (var i=0, ii=authors.length; i} + * + * Returns: + * {DOMElement} A gml node. + */ + buildGeometryNode: function(geometry) { + if (!this.gmlParser) { + this.initGmlParser(); + } + var node = this.gmlParser.writeNode("feature:_geometry", geometry); + return node.firstChild; + }, + + /** + * Method: buildPersonConstructNode + * + * Parameters: + * name - {String} + * value - {Object} + * + * Returns: + * {DOMElement} an Atom person construct node. + * + * Example: + * >>> buildPersonConstructNode("author", {name: "John Smith"}) + * {John Smith} + * + * TODO: how to specify extension elements? Add to the oNames array? + */ + buildPersonConstructNode: function(name, value) { + var oNames = ["uri", "email"]; + var personNode = this.createElementNSPlus("atom:" + name); + personNode.appendChild( + this.createElementNSPlus("atom:name", { + value: value.name + }) + ); + for (var i=0, ii=oNames.length; i 0) { + value = this.getChildValue(nodes[0], def); + } else { + value = def; + } + return value; + }, + + /** + * Method: parseFeature + * Parse feature from an Atom entry node.. + * + * Parameters: + * node - {DOMElement} An Atom entry or feed node. + * + * Returns: + * {} + */ + parseFeature: function(node) { + var atomAttrib = {}; + var value = null; + var nodes = null; + var attval = null; + var atomns = this.namespaces.atom; + + // atomAuthor* + this.parsePersonConstructs(node, "author", atomAttrib); + + // atomCategory* + nodes = this.getElementsByTagNameNS(node, atomns, "category"); + if (nodes.length > 0) { + atomAttrib.categories = []; + } + for (var i=0, ii=nodes.length; i 0) { + value = {}; + attval = nodes[0].getAttribute("type"); + if (attval) { + value.type = attval; + } + attval = nodes[0].getAttribute("src"); + if (attval) { + value.src = attval; + } else { + if (value.type == "text" || + value.type == "html" || + value.type == null ) { + value.value = this.getFirstChildValue( + node, + atomns, + "content", + null + ); + } else if (value.type == "xhtml" || + value.type.match(/(\+|\/)xml$/)) { + value.value = this.getChildEl(nodes[0]); + } else { // MUST be base64 encoded + value.value = this.getFirstChildValue( + node, + atomns, + "content", + null + ); + } + atomAttrib.content = value; + } + } + + // atomContributor* + this.parsePersonConstructs(node, "contributor", atomAttrib); + + // atomId + atomAttrib.id = this.getFirstChildValue(node, atomns, "id", null); + + // atomLink* + nodes = this.getElementsByTagNameNS(node, atomns, "link"); + if (nodes.length > 0) { + atomAttrib.links = new Array(nodes.length); + } + var oAtts = ["rel", "type", "hreflang", "title", "length"]; + for (var i=0, ii=nodes.length; i}) + */ + parseFeatures: function(node) { + var features = []; + var entries = this.getElementsByTagNameNS( + node, this.namespaces.atom, "entry" + ); + if (entries.length == 0) { + entries = [node]; + } + for (var i=0, ii=entries.length; i}) + */ + parseLocations: function(node) { + var georssns = this.namespaces.georss; + + var locations = {components: []}; + var where = this.getElementsByTagNameNS(node, georssns, "where"); + if (where && where.length > 0) { + if (!this.gmlParser) { + this.initGmlParser(); + } + for (var i=0, ii=where.length; i 0) { + for (var i=0, ii=point.length; i 0) { + var coords; + var p; + var points; + for (var i=0, ii=line.length; i 0) { + var coords; + var p; + var points; + for (var i=0, ii=polygon.length; i 0) { + data[name + "s"] = persons; + } + }, + + CLASS_NAME: "OpenLayers.Format.Atom" +}); diff --git a/misc/openlayers/lib/OpenLayers/Format/CQL.js b/misc/openlayers/lib/OpenLayers/Format/CQL.js new file mode 100644 index 0000000..8430a8b --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/CQL.js @@ -0,0 +1,452 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/WKT.js + * @requires OpenLayers/Filter/Comparison.js + * @requires OpenLayers/Filter/Logical.js + * @requires OpenLayers/Filter/Spatial.js + */ + +/** + * Class: OpenLayers.Format.CQL + * Read CQL strings to get objects. Write + * objects to get CQL strings. Create a new parser with + * the constructor. + * + * Inherits from: + * - + */ +OpenLayers.Format.CQL = (function() { + + var tokens = [ + "PROPERTY", "COMPARISON", "VALUE", "LOGICAL" + ], + + patterns = { + PROPERTY: /^[_a-zA-Z]\w*/, + COMPARISON: /^(=|<>|<=|<|>=|>|LIKE)/i, + IS_NULL: /^IS NULL/i, + COMMA: /^,/, + LOGICAL: /^(AND|OR)/i, + VALUE: /^('([^']|'')*'|\d+(\.\d*)?|\.\d+)/, + LPAREN: /^\(/, + RPAREN: /^\)/, + SPATIAL: /^(BBOX|INTERSECTS|DWITHIN|WITHIN|CONTAINS)/i, + NOT: /^NOT/i, + BETWEEN: /^BETWEEN/i, + GEOMETRY: function(text) { + var type = /^(POINT|LINESTRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)/.exec(text); + if (type) { + var len = text.length; + var idx = text.indexOf("(", type[0].length); + if (idx > -1) { + var depth = 1; + while (idx < len && depth > 0) { + idx++; + switch(text.charAt(idx)) { + case '(': + depth++; + break; + case ')': + depth--; + break; + default: + // in default case, do nothing + } + } + } + return [text.substr(0, idx+1)]; + } + }, + END: /^$/ + }, + + follows = { + LPAREN: ['GEOMETRY', 'SPATIAL', 'PROPERTY', 'VALUE', 'LPAREN'], + RPAREN: ['NOT', 'LOGICAL', 'END', 'RPAREN'], + PROPERTY: ['COMPARISON', 'BETWEEN', 'COMMA', 'IS_NULL'], + BETWEEN: ['VALUE'], + IS_NULL: ['END'], + COMPARISON: ['VALUE'], + COMMA: ['GEOMETRY', 'VALUE', 'PROPERTY'], + VALUE: ['LOGICAL', 'COMMA', 'RPAREN', 'END'], + SPATIAL: ['LPAREN'], + LOGICAL: ['NOT', 'VALUE', 'SPATIAL', 'PROPERTY', 'LPAREN'], + NOT: ['PROPERTY', 'LPAREN'], + GEOMETRY: ['COMMA', 'RPAREN'] + }, + + operators = { + '=': OpenLayers.Filter.Comparison.EQUAL_TO, + '<>': OpenLayers.Filter.Comparison.NOT_EQUAL_TO, + '<': OpenLayers.Filter.Comparison.LESS_THAN, + '<=': OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO, + '>': OpenLayers.Filter.Comparison.GREATER_THAN, + '>=': OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO, + 'LIKE': OpenLayers.Filter.Comparison.LIKE, + 'BETWEEN': OpenLayers.Filter.Comparison.BETWEEN, + 'IS NULL': OpenLayers.Filter.Comparison.IS_NULL + }, + + operatorReverse = {}, + + logicals = { + 'AND': OpenLayers.Filter.Logical.AND, + 'OR': OpenLayers.Filter.Logical.OR + }, + + logicalReverse = {}, + + precedence = { + 'RPAREN': 3, + 'LOGICAL': 2, + 'COMPARISON': 1 + }; + + var i; + for (i in operators) { + if (operators.hasOwnProperty(i)) { + operatorReverse[operators[i]] = i; + } + } + + for (i in logicals) { + if (logicals.hasOwnProperty(i)) { + logicalReverse[logicals[i]] = i; + } + } + + function tryToken(text, pattern) { + if (pattern instanceof RegExp) { + return pattern.exec(text); + } else { + return pattern(text); + } + } + + function nextToken(text, tokens) { + var i, token, len = tokens.length; + for (i=0; i 0 && + (precedence[operatorStack[operatorStack.length - 1].type] <= p) + ) { + postfix.push(operatorStack.pop()); + } + + operatorStack.push(tok); + break; + case "SPATIAL": + case "NOT": + case "LPAREN": + operatorStack.push(tok); + break; + case "RPAREN": + while (operatorStack.length > 0 && + (operatorStack[operatorStack.length - 1].type != "LPAREN") + ) { + postfix.push(operatorStack.pop()); + } + operatorStack.pop(); // toss out the LPAREN + + if (operatorStack.length > 0 && + operatorStack[operatorStack.length-1].type == "SPATIAL") { + postfix.push(operatorStack.pop()); + } + case "COMMA": + case "END": + break; + default: + throw new Error("Unknown token type " + tok.type); + } + } + + while (operatorStack.length > 0) { + postfix.push(operatorStack.pop()); + } + + function buildTree() { + var tok = postfix.pop(); + switch (tok.type) { + case "LOGICAL": + var rhs = buildTree(), + lhs = buildTree(); + return new OpenLayers.Filter.Logical({ + filters: [lhs, rhs], + type: logicals[tok.text.toUpperCase()] + }); + case "NOT": + var operand = buildTree(); + return new OpenLayers.Filter.Logical({ + filters: [operand], + type: OpenLayers.Filter.Logical.NOT + }); + case "BETWEEN": + var min, max, property; + postfix.pop(); // unneeded AND token here + max = buildTree(); + min = buildTree(); + property = buildTree(); + return new OpenLayers.Filter.Comparison({ + property: property, + lowerBoundary: min, + upperBoundary: max, + type: OpenLayers.Filter.Comparison.BETWEEN + }); + case "COMPARISON": + var value = buildTree(), + property = buildTree(); + return new OpenLayers.Filter.Comparison({ + property: property, + value: value, + type: operators[tok.text.toUpperCase()] + }); + case "IS_NULL": + var property = buildTree(); + return new OpenLayers.Filter.Comparison({ + property: property, + type: operators[tok.text.toUpperCase()] + }); + case "VALUE": + var match = tok.text.match(/^'(.*)'$/); + if (match) { + return match[1].replace(/''/g, "'"); + } else { + return Number(tok.text); + } + case "SPATIAL": + switch(tok.text.toUpperCase()) { + case "BBOX": + var maxy = buildTree(), + maxx = buildTree(), + miny = buildTree(), + minx = buildTree(), + prop = buildTree(); + + return new OpenLayers.Filter.Spatial({ + type: OpenLayers.Filter.Spatial.BBOX, + property: prop, + value: OpenLayers.Bounds.fromArray( + [minx, miny, maxx, maxy] + ) + }); + case "INTERSECTS": + var value = buildTree(), + property = buildTree(); + return new OpenLayers.Filter.Spatial({ + type: OpenLayers.Filter.Spatial.INTERSECTS, + property: property, + value: value + }); + case "WITHIN": + var value = buildTree(), + property = buildTree(); + return new OpenLayers.Filter.Spatial({ + type: OpenLayers.Filter.Spatial.WITHIN, + property: property, + value: value + }); + case "CONTAINS": + var value = buildTree(), + property = buildTree(); + return new OpenLayers.Filter.Spatial({ + type: OpenLayers.Filter.Spatial.CONTAINS, + property: property, + value: value + }); + case "DWITHIN": + var distance = buildTree(), + value = buildTree(), + property = buildTree(); + return new OpenLayers.Filter.Spatial({ + type: OpenLayers.Filter.Spatial.DWITHIN, + value: value, + property: property, + distance: Number(distance) + }); + } + case "GEOMETRY": + return OpenLayers.Geometry.fromWKT(tok.text); + default: + return tok.text; + } + } + + var result = buildTree(); + if (postfix.length > 0) { + var msg = "Remaining tokens after building AST: \n"; + for (var i = postfix.length - 1; i >= 0; i--) { + msg += postfix[i].type + ": " + postfix[i].text + "\n"; + } + throw new Error(msg); + } + + return result; + } + + return OpenLayers.Class(OpenLayers.Format, { + /** + * APIMethod: read + * Generate a filter from a CQL string. + + * Parameters: + * text - {String} The CQL text. + * + * Returns: + * {} A filter based on the CQL text. + */ + read: function(text) { + var result = buildAst(tokenize(text)); + if (this.keepData) { + this.data = result; + } + return result; + }, + + /** + * APIMethod: write + * Convert a filter into a CQL string. + + * Parameters: + * filter - {} The filter. + * + * Returns: + * {String} A CQL string based on the filter. + */ + write: function(filter) { + if (filter instanceof OpenLayers.Geometry) { + return filter.toString(); + } + switch (filter.CLASS_NAME) { + case "OpenLayers.Filter.Spatial": + switch(filter.type) { + case OpenLayers.Filter.Spatial.BBOX: + return "BBOX(" + + filter.property + "," + + filter.value.toBBOX() + + ")"; + case OpenLayers.Filter.Spatial.DWITHIN: + return "DWITHIN(" + + filter.property + ", " + + this.write(filter.value) + ", " + + filter.distance + ")"; + case OpenLayers.Filter.Spatial.WITHIN: + return "WITHIN(" + + filter.property + ", " + + this.write(filter.value) + ")"; + case OpenLayers.Filter.Spatial.INTERSECTS: + return "INTERSECTS(" + + filter.property + ", " + + this.write(filter.value) + ")"; + case OpenLayers.Filter.Spatial.CONTAINS: + return "CONTAINS(" + + filter.property + ", " + + this.write(filter.value) + ")"; + default: + throw new Error("Unknown spatial filter type: " + filter.type); + } + case "OpenLayers.Filter.Logical": + if (filter.type == OpenLayers.Filter.Logical.NOT) { + // TODO: deal with precedence of logical operators to + // avoid extra parentheses (not urgent) + return "NOT (" + this.write(filter.filters[0]) + ")"; + } else { + var res = "("; + var first = true; + for (var i = 0; i < filter.filters.length; i++) { + if (first) { + first = false; + } else { + res += ") " + logicalReverse[filter.type] + " ("; + } + res += this.write(filter.filters[i]); + } + return res + ")"; + } + case "OpenLayers.Filter.Comparison": + if (filter.type == OpenLayers.Filter.Comparison.BETWEEN) { + return filter.property + " BETWEEN " + + this.write(filter.lowerBoundary) + " AND " + + this.write(filter.upperBoundary); + } else { + return (filter.value !== null) ? filter.property + + " " + operatorReverse[filter.type] + " " + + this.write(filter.value) : filter.property + + " " + operatorReverse[filter.type]; + } + case undefined: + if (typeof filter === "string") { + return "'" + filter.replace(/'/g, "''") + "'"; + } else if (typeof filter === "number") { + return String(filter); + } + default: + throw new Error("Can't encode: " + filter.CLASS_NAME + " " + filter); + } + }, + + CLASS_NAME: "OpenLayers.Format.CQL" + + }); +})(); + diff --git a/misc/openlayers/lib/OpenLayers/Format/CSWGetDomain.js b/misc/openlayers/lib/OpenLayers/Format/CSWGetDomain.js new file mode 100644 index 0000000..18d5328 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/CSWGetDomain.js @@ -0,0 +1,34 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format.js + */ + +/** + * Class: OpenLayers.Format.CSWGetDomain + * Default version is 2.0.2. + * + * Returns: + * {} A CSWGetDomain format of the given version. + */ +OpenLayers.Format.CSWGetDomain = function(options) { + options = OpenLayers.Util.applyDefaults( + options, OpenLayers.Format.CSWGetDomain.DEFAULTS + ); + var cls = OpenLayers.Format.CSWGetDomain["v"+options.version.replace(/\./g, "_")]; + if(!cls) { + throw "Unsupported CSWGetDomain version: " + options.version; + } + return new cls(options); +}; + +/** + * Constant: DEFAULTS + * {Object} Default properties for the CSWGetDomain format. + */ +OpenLayers.Format.CSWGetDomain.DEFAULTS = { + "version": "2.0.2" +}; diff --git a/misc/openlayers/lib/OpenLayers/Format/CSWGetDomain/v2_0_2.js b/misc/openlayers/lib/OpenLayers/Format/CSWGetDomain/v2_0_2.js new file mode 100644 index 0000000..78200ea --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/CSWGetDomain/v2_0_2.js @@ -0,0 +1,240 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/XML.js + * @requires OpenLayers/Format/CSWGetDomain.js + */ + +/** + * Class: OpenLayers.Format.CSWGetDomain.v2_0_2 + * A format for creating CSWGetDomain v2.0.2 transactions. + * Create a new instance with the + * constructor. + * + * Inherits from: + * - + */ +OpenLayers.Format.CSWGetDomain.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, { + + /** + * Property: namespaces + * {Object} Mapping of namespace aliases to namespace URIs. + */ + namespaces: { + xlink: "http://www.w3.org/1999/xlink", + xsi: "http://www.w3.org/2001/XMLSchema-instance", + csw: "http://www.opengis.net/cat/csw/2.0.2" + }, + + /** + * Property: defaultPrefix + * {String} The default prefix (used by Format.XML). + */ + defaultPrefix: "csw", + + /** + * Property: version + * {String} CSW version number. + */ + version: "2.0.2", + + /** + * Property: schemaLocation + * {String} http://www.opengis.net/cat/csw/2.0.2 + * http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd + */ + schemaLocation: "http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd", + + /** + * APIProperty: PropertyName + * {String} Value of the csw:PropertyName element, used when + * writing a GetDomain document. + */ + PropertyName: null, + + /** + * APIProperty: ParameterName + * {String} Value of the csw:ParameterName element, used when + * writing a GetDomain document. + */ + ParameterName: null, + + /** + * Constructor: OpenLayers.Format.CSWGetDomain.v2_0_2 + * A class for parsing and generating CSWGetDomain v2.0.2 transactions. + * + * Parameters: + * options - {Object} Optional object whose properties will be set on the + * instance. + * + * Valid options properties: + * - PropertyName + * - ParameterName + */ + + /** + * APIMethod: read + * Parse the response from a GetDomain request. + */ + read: function(data) { + if(typeof data == "string") { + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); + } + if(data && data.nodeType == 9) { + data = data.documentElement; + } + var obj = {}; + this.readNode(data, obj); + return obj; + }, + + /** + * Property: readers + * Contains public functions, grouped by namespace prefix, that will + * be applied when a namespaced node is found matching the function + * name. The function will be applied in the scope of this parser + * with two arguments: the node being read and a context object passed + * from the parent. + */ + readers: { + "csw": { + "GetDomainResponse": function(node, obj) { + this.readChildNodes(node, obj); + }, + "DomainValues": function(node, obj) { + if (!(OpenLayers.Util.isArray(obj.DomainValues))) { + obj.DomainValues = []; + } + var attrs = node.attributes; + var domainValue = {}; + for(var i=0, len=attrs.length; i} A CSWGetRecords format of the given version. + */ +OpenLayers.Format.CSWGetRecords = function(options) { + options = OpenLayers.Util.applyDefaults( + options, OpenLayers.Format.CSWGetRecords.DEFAULTS + ); + var cls = OpenLayers.Format.CSWGetRecords["v"+options.version.replace(/\./g, "_")]; + if(!cls) { + throw "Unsupported CSWGetRecords version: " + options.version; + } + return new cls(options); +}; + +/** + * Constant: DEFAULTS + * {Object} Default properties for the CSWGetRecords format. + */ +OpenLayers.Format.CSWGetRecords.DEFAULTS = { + "version": "2.0.2" +}; diff --git a/misc/openlayers/lib/OpenLayers/Format/CSWGetRecords/v2_0_2.js b/misc/openlayers/lib/OpenLayers/Format/CSWGetRecords/v2_0_2.js new file mode 100644 index 0000000..3c87612 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/CSWGetRecords/v2_0_2.js @@ -0,0 +1,457 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/XML.js + * @requires OpenLayers/Format/CSWGetRecords.js + * @requires OpenLayers/Format/Filter/v1_0_0.js + * @requires OpenLayers/Format/Filter/v1_1_0.js + * @requires OpenLayers/Format/OWSCommon/v1_0_0.js + */ + +/** + * Class: OpenLayers.Format.CSWGetRecords.v2_0_2 + * A format for creating CSWGetRecords v2.0.2 transactions. + * Create a new instance with the + * constructor. + * + * Inherits from: + * - + */ +OpenLayers.Format.CSWGetRecords.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, { + + /** + * Property: namespaces + * {Object} Mapping of namespace aliases to namespace URIs. + */ + namespaces: { + csw: "http://www.opengis.net/cat/csw/2.0.2", + dc: "http://purl.org/dc/elements/1.1/", + dct: "http://purl.org/dc/terms/", + gmd: "http://www.isotc211.org/2005/gmd", + geonet: "http://www.fao.org/geonetwork", + ogc: "http://www.opengis.net/ogc", + ows: "http://www.opengis.net/ows", + xlink: "http://www.w3.org/1999/xlink", + xsi: "http://www.w3.org/2001/XMLSchema-instance" + }, + + /** + * Property: defaultPrefix + * {String} The default prefix (used by Format.XML). + */ + defaultPrefix: "csw", + + /** + * Property: version + * {String} CSW version number. + */ + version: "2.0.2", + + /** + * Property: schemaLocation + * {String} http://www.opengis.net/cat/csw/2.0.2 + * http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd + */ + schemaLocation: "http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd", + + /** + * APIProperty: requestId + * {String} Value of the requestId attribute of the GetRecords element. + */ + requestId: null, + + /** + * APIProperty: resultType + * {String} Value of the resultType attribute of the GetRecords element, + * specifies the result type in the GetRecords response, "hits" is + * the default. + */ + resultType: null, + + /** + * APIProperty: outputFormat + * {String} Value of the outputFormat attribute of the GetRecords element, + * specifies the format of the GetRecords response, + * "application/xml" is the default. + */ + outputFormat: null, + + /** + * APIProperty: outputSchema + * {String} Value of the outputSchema attribute of the GetRecords element, + * specifies the schema of the GetRecords response. + */ + outputSchema: null, + + /** + * APIProperty: startPosition + * {String} Value of the startPosition attribute of the GetRecords element, + * specifies the start position (offset+1) for the GetRecords response, + * 1 is the default. + */ + startPosition: null, + + /** + * APIProperty: maxRecords + * {String} Value of the maxRecords attribute of the GetRecords element, + * specifies the maximum number of records in the GetRecords response, + * 10 is the default. + */ + maxRecords: null, + + /** + * APIProperty: DistributedSearch + * {String} Value of the csw:DistributedSearch element, used when writing + * a csw:GetRecords document. + */ + DistributedSearch: null, + + /** + * APIProperty: ResponseHandler + * {Array({String})} Values of the csw:ResponseHandler elements, used when + * writting a csw:GetRecords document. + */ + ResponseHandler: null, + + /** + * APIProperty: Query + * {String} Value of the csw:Query element, used when writing a csw:GetRecords + * document. + */ + Query: null, + + /** + * Property: regExes + * Compiled regular expressions for manipulating strings. + */ + regExes: { + trimSpace: (/^\s*|\s*$/g), + removeSpace: (/\s*/g), + splitSpace: (/\s+/), + trimComma: (/\s*,\s*/g) + }, + + /** + * Constructor: OpenLayers.Format.CSWGetRecords.v2_0_2 + * A class for parsing and generating CSWGetRecords v2.0.2 transactions. + * + * Parameters: + * options - {Object} Optional object whose properties will be set on the + * instance. + * + * Valid options properties (documented as class properties): + * - requestId + * - resultType + * - outputFormat + * - outputSchema + * - startPosition + * - maxRecords + * - DistributedSearch + * - ResponseHandler + * - Query + */ + initialize: function(options) { + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); + }, + + /** + * APIMethod: read + * Parse the response from a GetRecords request. + */ + read: function(data) { + if(typeof data == "string") { + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); + } + if(data && data.nodeType == 9) { + data = data.documentElement; + } + var obj = {}; + this.readNode(data, obj); + return obj; + }, + + /** + * Property: readers + * Contains public functions, grouped by namespace prefix, that will + * be applied when a namespaced node is found matching the function + * name. The function will be applied in the scope of this parser + * with two arguments: the node being read and a context object passed + * from the parent. + */ + readers: { + "csw": { + "GetRecordsResponse": function(node, obj) { + obj.records = []; + this.readChildNodes(node, obj); + var version = this.getAttributeNS(node, "", 'version'); + if (version != "") { + obj.version = version; + } + }, + "RequestId": function(node, obj) { + obj.RequestId = this.getChildValue(node); + }, + "SearchStatus": function(node, obj) { + obj.SearchStatus = {}; + var timestamp = this.getAttributeNS(node, "", 'timestamp'); + if (timestamp != "") { + obj.SearchStatus.timestamp = timestamp; + } + }, + "SearchResults": function(node, obj) { + this.readChildNodes(node, obj); + var attrs = node.attributes; + var SearchResults = {}; + for(var i=0, len=attrs.length; i 0) { + // ResponseHandler must be a non-empty array + for(var i=0, len=ResponseHandler.length; i 0) { + // ElementName must be a non-empty array + for(var i=0, len=ElementName.length; i + */ +OpenLayers.Format.Context = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { + + /** + * Property: layerOptions + * {Object} Default options for layers created by the parser. These + * options are overridden by the options which are read from the + * capabilities document. + */ + layerOptions: null, + + /** + * Property: layerParams + * {Object} Default parameters for layers created by the parser. This + * can be used e.g. to override DEFAULT_PARAMS for + * OpenLayers.Layer.WMS. + */ + layerParams: null, + + /** + * Constructor: OpenLayers.Format.Context + * Create a new parser for Context documents. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + */ + + /** + * APIMethod: read + * Read Context data from a string, and return an object with map + * properties and a list of layers. + * + * Parameters: + * data - {String} or {DOMElement} data to read/parse. + * options - {Object} The options object must contain a map property. If + * the map property is a string, it must be the id of a dom element + * where the new map will be placed. If the map property is an + * , the layers from the context document will be added + * to the map. + * + * Returns: + * {} A map based on the context. + */ + read: function(data, options) { + var context = OpenLayers.Format.XML.VersionedOGC.prototype.read.apply(this, + arguments); + var map; + if(options && options.map) { + this.context = context; + if(options.map instanceof OpenLayers.Map) { + map = this.mergeContextToMap(context, options.map); + } else { + var mapOptions = options.map; + if(OpenLayers.Util.isElement(mapOptions) || + typeof mapOptions == "string") { + // we assume mapOptions references a div + // element + mapOptions = {div: mapOptions}; + } + map = this.contextToMap(context, mapOptions); + } + } else { + // not documented as part of the API, provided as a non-API option + map = context; + } + return map; + }, + + /** + * Method: getLayerFromContext + * Create a WMS layer from a layerContext object. + * + * Parameters: + * layerContext - {Object} An object representing a WMS layer. + * + * Returns: + * {} A WMS layer. + */ + getLayerFromContext: function(layerContext) { + var i, len; + // fill initial options object from layerContext + var options = { + queryable: layerContext.queryable, //keep queryable for api compatibility + visibility: layerContext.visibility, + maxExtent: layerContext.maxExtent, + metadata: OpenLayers.Util.applyDefaults(layerContext.metadata, + {styles: layerContext.styles, + formats: layerContext.formats, + "abstract": layerContext["abstract"], + dataURL: layerContext.dataURL + }), + numZoomLevels: layerContext.numZoomLevels, + units: layerContext.units, + isBaseLayer: layerContext.isBaseLayer, + opacity: layerContext.opacity, + displayInLayerSwitcher: layerContext.displayInLayerSwitcher, + singleTile: layerContext.singleTile, + tileSize: (layerContext.tileSize) ? + new OpenLayers.Size( + layerContext.tileSize.width, + layerContext.tileSize.height + ) : undefined, + minScale: layerContext.minScale || layerContext.maxScaleDenominator, + maxScale: layerContext.maxScale || layerContext.minScaleDenominator, + srs: layerContext.srs, + dimensions: layerContext.dimensions, + metadataURL: layerContext.metadataURL + }; + if (this.layerOptions) { + OpenLayers.Util.applyDefaults(options, this.layerOptions); + } + + var params = { + layers: layerContext.name, + transparent: layerContext.transparent, + version: layerContext.version + }; + if (layerContext.formats && layerContext.formats.length>0) { + // set default value for params if current attribute is not positionned + params.format = layerContext.formats[0].value; + for (i=0, len=layerContext.formats.length; i0) { + for (i=0, len=layerContext.styles.length; i)} An array of layers. + */ + getLayersFromContext: function(layersContext) { + var layers = []; + for (var i=0, len=layersContext.length; i} A map based on the context object. + */ + contextToMap: function(context, options) { + options = OpenLayers.Util.applyDefaults({ + maxExtent: context.maxExtent, + projection: context.projection, + units: context.units + }, options); + + if (options.maxExtent) { + options.maxResolution = + options.maxExtent.getWidth() / OpenLayers.Map.TILE_WIDTH; + } + + var metadata = { + contactInformation: context.contactInformation, + "abstract": context["abstract"], + keywords: context.keywords, + logo: context.logo, + descriptionURL: context.descriptionURL + }; + + options.metadata = metadata; + + var map = new OpenLayers.Map(options); + map.addLayers(this.getLayersFromContext(context.layersContext)); + map.setCenter( + context.bounds.getCenterLonLat(), + map.getZoomForExtent(context.bounds, true) + ); + return map; + }, + + /** + * Method: mergeContextToMap + * Add layers from a context object to a map. + * + * Parameters: + * context - {Object} The context object. + * map - {} The map. + * + * Returns: + * {} The same map with layers added. + */ + mergeContextToMap: function(context, map) { + map.addLayers(this.getLayersFromContext(context.layersContext)); + return map; + }, + + /** + * APIMethod: write + * Write a context document given a map. + * + * Parameters: + * obj - { | Object} A map or context object. + * options - {Object} Optional configuration object. + * + * Returns: + * {String} A context document string. + */ + write: function(obj, options) { + obj = this.toContext(obj); + return OpenLayers.Format.XML.VersionedOGC.prototype.write.apply(this, + arguments); + }, + + CLASS_NAME: "OpenLayers.Format.Context" +}); + +/** + * Constant: OpenLayers.Format.Context.serviceTypes + * Enumeration for service types + */ +OpenLayers.Format.Context.serviceTypes = { + "WMS": "urn:ogc:serviceType:WMS", + "WFS": "urn:ogc:serviceType:WFS", + "WCS": "urn:ogc:serviceType:WCS", + "GML": "urn:ogc:serviceType:GML", + "SLD": "urn:ogc:serviceType:SLD", + "FES": "urn:ogc:serviceType:FES", + "KML": "urn:ogc:serviceType:KML" +}; diff --git a/misc/openlayers/lib/OpenLayers/Format/EncodedPolyline.js b/misc/openlayers/lib/OpenLayers/Format/EncodedPolyline.js new file mode 100644 index 0000000..e10e8b2 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/EncodedPolyline.js @@ -0,0 +1,557 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format.js + * @requires OpenLayers/Feature/Vector.js + */ + +/** + * Class: OpenLayers.Format.EncodedPolyline + * Class for reading and writing encoded polylines. Create a new instance + * with the constructor. + * + * Inherits from: + * - + */ +OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, { + + /** + * APIProperty: geometryType + * {String} Geometry type to output. One of: linestring (default), + * linearring, point, multipoint or polygon. If the geometryType is + * point, only the first point of the string is returned. + */ + geometryType: "linestring", + + /** + * Constructor: OpenLayers.Format.EncodedPolyline + * Create a new parser for encoded polylines + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance + * + * Returns: + * {} A new encoded polylines parser. + */ + initialize: function(options) { + OpenLayers.Format.prototype.initialize.apply(this, [options]); + }, + + /** + * APIMethod: read + * Deserialize an encoded polyline string and return a vector feature. + * + * Parameters: + * encoded - {String} An encoded polyline string + * + * Returns: + * {} A vector feature with a linestring. + */ + read: function(encoded) { + var geomType; + if (this.geometryType == "linestring") + geomType = OpenLayers.Geometry.LineString; + else if (this.geometryType == "linearring") + geomType = OpenLayers.Geometry.LinearRing; + else if (this.geometryType == "multipoint") + geomType = OpenLayers.Geometry.MultiPoint; + else if (this.geometryType != "point" && this.geometryType != "polygon") + return null; + + var flatPoints = this.decodeDeltas(encoded, 2); + var flatPointsLength = flatPoints.length; + + var pointGeometries = []; + for (var i = 0; i + 1 < flatPointsLength;) { + var y = flatPoints[i++], x = flatPoints[i++]; + pointGeometries.push(new OpenLayers.Geometry.Point(x, y)); + } + + + if (this.geometryType == "point") + return new OpenLayers.Feature.Vector( + pointGeometries[0] + ); + + if (this.geometryType == "polygon") + return new OpenLayers.Feature.Vector( + new OpenLayers.Geometry.Polygon([ + new OpenLayers.Geometry.LinearRing(pointGeometries) + ]) + ); + + return new OpenLayers.Feature.Vector( + new geomType(pointGeometries) + ); + }, + + /** + * APIMethod: decode + * Deserialize an encoded string and return an array of n-dimensional + * points. + * + * Parameters: + * encoded - {String} An encoded string + * dims - {int} The dimension of the points that are returned + * + * Returns: + * {Array(Array(int))} An array containing n-dimensional arrays of + * coordinates. + */ + decode: function(encoded, dims, opt_factor) { + var factor = opt_factor || 1e5; + var flatPoints = this.decodeDeltas(encoded, dims, factor); + var flatPointsLength = flatPoints.length; + + var points = []; + for (var i = 0; i + (dims - 1) < flatPointsLength;) { + var point = []; + + for (var dim = 0; dim < dims; ++dim) { + point.push(flatPoints[i++]) + } + + points.push(point); + } + + return points; + }, + + /** + * APIMethod: write + * Serialize a feature or array of features into a WKT string. + * + * Parameters: + * features - {|Array} A feature or array of + * features + * + * Returns: + * {String} The WKT string representation of the input geometries + */ + write: function(features) { + var feature; + if (features.constructor == Array) + feature = features[0]; + else + feature = features; + + var geometry = feature.geometry; + var type = geometry.CLASS_NAME.split('.')[2].toLowerCase(); + + var pointGeometries; + if (type == "point") + pointGeometries = new Array(geometry); + else if (type == "linestring" || + type == "linearring" || + type == "multipoint") + pointGeometries = geometry.components; + else if (type == "polygon") + pointGeometries = geometry.components[0].components; + else + return null; + + var flatPoints = []; + + var pointGeometriesLength = pointGeometries.length; + for (var i = 0; i < pointGeometriesLength; ++i) { + var pointGeometry = pointGeometries[i]; + flatPoints.push(pointGeometry.y); + flatPoints.push(pointGeometry.x); + } + + return this.encodeDeltas(flatPoints, 2); + }, + + /** + * APIMethod: encode + * Serialize an array of n-dimensional points and return an encoded string + * + * Parameters: + * points - {Array(Array(int))} An array containing n-dimensional + * arrays of coordinates + * dims - {int} The dimension of the points that should be read + * + * Returns: + * {String} An encoded string + */ + encode: function (points, dims, opt_factor) { + var factor = opt_factor || 1e5; + var flatPoints = []; + + var pointsLength = points.length; + for (var i = 0; i < pointsLength; ++i) { + var point = points[i]; + + for (var dim = 0; dim < dims; ++dim) { + flatPoints.push(point[dim]); + } + } + + return this.encodeDeltas(flatPoints, dims, factor); + }, + + /** + * APIMethod: encodeDeltas + * Encode a list of n-dimensional points and return an encoded string + * + * Attention: This function will modify the passed array! + * + * Parameters: + * numbers - {Array.} A list of n-dimensional points. + * dimension - {number} The dimension of the points in the list. + * opt_factor - {number=} The factor by which the numbers will be + * multiplied. The remaining decimal places will get rounded away. + * + * Returns: + * {string} The encoded string. + */ + encodeDeltas: function(numbers, dimension, opt_factor) { + var factor = opt_factor || 1e5; + var d; + + var lastNumbers = new Array(dimension); + for (d = 0; d < dimension; ++d) { + lastNumbers[d] = 0; + } + + var numbersLength = numbers.length; + for (var i = 0; i < numbersLength;) { + for (d = 0; d < dimension; ++d, ++i) { + var num = numbers[i]; + var delta = num - lastNumbers[d]; + lastNumbers[d] = num; + + numbers[i] = delta; + } + } + + return this.encodeFloats(numbers, factor); + }, + + + /** + * APIMethod: decodeDeltas + * Decode a list of n-dimensional points from an encoded string + * + * Parameters: + * encoded - {string} An encoded string. + * dimension - {number} The dimension of the points in the encoded string. + * opt_factor - {number=} The factor by which the resulting numbers will + * be divided. + * + * Returns: + * {Array.} A list of n-dimensional points. + */ + decodeDeltas: function(encoded, dimension, opt_factor) { + var factor = opt_factor || 1e5; + var d; + + var lastNumbers = new Array(dimension); + for (d = 0; d < dimension; ++d) { + lastNumbers[d] = 0; + } + + var numbers = this.decodeFloats(encoded, factor); + + var numbersLength = numbers.length; + for (var i = 0; i < numbersLength;) { + for (d = 0; d < dimension; ++d, ++i) { + lastNumbers[d] += numbers[i]; + + numbers[i] = lastNumbers[d]; + } + } + + return numbers; + }, + + + /** + * APIMethod: encodeFloats + * Encode a list of floating point numbers and return an encoded string + * + * Attention: This function will modify the passed array! + * + * Parameters: + * numbers - {Array.} A list of floating point numbers. + * opt_factor - {number=} The factor by which the numbers will be + * multiplied. The remaining decimal places will get rounded away. + * + * Returns: + * {string} The encoded string. + */ + encodeFloats: function(numbers, opt_factor) { + var factor = opt_factor || 1e5; + + var numbersLength = numbers.length; + for (var i = 0; i < numbersLength; ++i) { + numbers[i] = Math.round(numbers[i] * factor); + } + + return this.encodeSignedIntegers(numbers); + }, + + + /** + * APIMethod: decodeFloats + * Decode a list of floating point numbers from an encoded string + * + * Parameters: + * encoded - {string} An encoded string. + * opt_factor - {number=} The factor by which the result will be divided. + * + * Returns: + * {Array.} A list of floating point numbers. + */ + decodeFloats: function(encoded, opt_factor) { + var factor = opt_factor || 1e5; + + var numbers = this.decodeSignedIntegers(encoded); + + var numbersLength = numbers.length; + for (var i = 0; i < numbersLength; ++i) { + numbers[i] /= factor; + } + + return numbers; + }, + + + /** + * APIMethod: encodeSignedIntegers + * Encode a list of signed integers and return an encoded string + * + * Attention: This function will modify the passed array! + * + * Parameters: + * numbers - {Array.} A list of signed integers. + * + * Returns: + * {string} The encoded string. + */ + encodeSignedIntegers: function(numbers) { + var numbersLength = numbers.length; + for (var i = 0; i < numbersLength; ++i) { + var num = numbers[i]; + + var signedNum = num << 1; + if (num < 0) { + signedNum = ~(signedNum); + } + + numbers[i] = signedNum; + } + + return this.encodeUnsignedIntegers(numbers); + }, + + + /** + * APIMethod: decodeSignedIntegers + * Decode a list of signed integers from an encoded string + * + * Parameters: + * encoded - {string} An encoded string. + * + * Returns: + * {Array.} A list of signed integers. + */ + decodeSignedIntegers: function(encoded) { + var numbers = this.decodeUnsignedIntegers(encoded); + + var numbersLength = numbers.length; + for (var i = 0; i < numbersLength; ++i) { + var num = numbers[i]; + numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1); + } + + return numbers; + }, + + + /** + * APIMethod: encodeUnsignedIntegers + * Encode a list of unsigned integers and return an encoded string + * + * Parameters: + * numbers - {Array.} A list of unsigned integers. + * + * Returns: + * {string} The encoded string. + */ + encodeUnsignedIntegers: function(numbers) { + var encoded = ''; + + var numbersLength = numbers.length; + for (var i = 0; i < numbersLength; ++i) { + encoded += this.encodeUnsignedInteger(numbers[i]); + } + + return encoded; + }, + + + /** + * APIMethod: decodeUnsignedIntegers + * Decode a list of unsigned integers from an encoded string + * + * Parameters: + * encoded - {string} An encoded string. + * + * Returns: + * {Array.} A list of unsigned integers. + */ + decodeUnsignedIntegers: function(encoded) { + var numbers = []; + + var current = 0; + var shift = 0; + + var encodedLength = encoded.length; + for (var i = 0; i < encodedLength; ++i) { + var b = encoded.charCodeAt(i) - 63; + + current |= (b & 0x1f) << shift; + + if (b < 0x20) { + numbers.push(current); + current = 0; + shift = 0; + } else { + shift += 5; + } + } + + return numbers; + }, + + + /** + * Method: encodeFloat + * Encode one single floating point number and return an encoded string + * + * Parameters: + * num - {number} Floating point number that should be encoded. + * opt_factor - {number=} The factor by which num will be multiplied. + * The remaining decimal places will get rounded away. + * + * Returns: + * {string} The encoded string. + */ + encodeFloat: function(num, opt_factor) { + num = Math.round(num * (opt_factor || 1e5)); + return this.encodeSignedInteger(num); + }, + + + /** + * Method: decodeFloat + * Decode one single floating point number from an encoded string + * + * Parameters: + * encoded - {string} An encoded string. + * opt_factor - {number=} The factor by which the result will be divided. + * + * Returns: + * {number} The decoded floating point number. + */ + decodeFloat: function(encoded, opt_factor) { + var result = this.decodeSignedInteger(encoded); + return result / (opt_factor || 1e5); + }, + + + /** + * Method: encodeSignedInteger + * Encode one single signed integer and return an encoded string + * + * Parameters: + * num - {number} Signed integer that should be encoded. + * + * Returns: + * {string} The encoded string. + */ + encodeSignedInteger: function(num) { + var signedNum = num << 1; + if (num < 0) { + signedNum = ~(signedNum); + } + + return this.encodeUnsignedInteger(signedNum); + }, + + + /** + * Method: decodeSignedInteger + * Decode one single signed integer from an encoded string + * + * Parameters: + * encoded - {string} An encoded string. + * + * Returns: + * {number} The decoded signed integer. + */ + decodeSignedInteger: function(encoded) { + var result = this.decodeUnsignedInteger(encoded); + return ((result & 1) ? ~(result >> 1) : (result >> 1)); + }, + + + /** + * Method: encodeUnsignedInteger + * Encode one single unsigned integer and return an encoded string + * + * Parameters: + * num - {number} Unsigned integer that should be encoded. + * + * Returns: + * {string} The encoded string. + */ + encodeUnsignedInteger: function(num) { + var value, encoded = ''; + while (num >= 0x20) { + value = (0x20 | (num & 0x1f)) + 63; + encoded += (String.fromCharCode(value)); + num >>= 5; + } + value = num + 63; + encoded += (String.fromCharCode(value)); + return encoded; + }, + + + /** + * Method: decodeUnsignedInteger + * Decode one single unsigned integer from an encoded string + * + * Parameters: + * encoded - {string} An encoded string. + * + * Returns: + * {number} The decoded unsigned integer. + */ + decodeUnsignedInteger: function(encoded) { + var result = 0; + var shift = 0; + + var encodedLength = encoded.length; + for (var i = 0; i < encodedLength; ++i) { + var b = encoded.charCodeAt(i) - 63; + + result |= (b & 0x1f) << shift; + + if (b < 0x20) + break; + + shift += 5; + } + + return result; + }, + + CLASS_NAME: "OpenLayers.Format.EncodedPolyline" +}); diff --git a/misc/openlayers/lib/OpenLayers/Format/Filter.js b/misc/openlayers/lib/OpenLayers/Format/Filter.js new file mode 100644 index 0000000..59c06a8 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/Filter.js @@ -0,0 +1,53 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/XML/VersionedOGC.js + * @requires OpenLayers/Filter/FeatureId.js + * @requires OpenLayers/Filter/Logical.js + * @requires OpenLayers/Filter/Comparison.js + */ + +/** + * Class: OpenLayers.Format.Filter + * Read/Write ogc:Filter. Create a new instance with the + * constructor. + * + * Inherits from: + * - + */ +OpenLayers.Format.Filter = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { + + /** + * APIProperty: defaultVersion + * {String} Version number to assume if none found. Default is "1.0.0". + */ + defaultVersion: "1.0.0", + + /** + * APIMethod: write + * Write an ogc:Filter given a filter object. + * + * Parameters: + * filter - {} An filter. + * options - {Object} Optional configuration object. + * + * Returns: + * {Elment} An ogc:Filter element node. + */ + + /** + * APIMethod: read + * Read and Filter doc and return an object representing the Filter. + * + * Parameters: + * data - {String | DOMElement} Data to read. + * + * Returns: + * {} A filter object. + */ + + CLASS_NAME: "OpenLayers.Format.Filter" +}); diff --git a/misc/openlayers/lib/OpenLayers/Format/Filter/v1.js b/misc/openlayers/lib/OpenLayers/Format/Filter/v1.js new file mode 100644 index 0000000..539ec0f --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/Filter/v1.js @@ -0,0 +1,504 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ +/** + * @requires OpenLayers/Format/Filter.js + * @requires OpenLayers/Format/XML.js + * @requires OpenLayers/Filter/Function.js + * @requires OpenLayers/BaseTypes/Date.js + */ + +/** + * Class: OpenLayers.Format.Filter.v1 + * Superclass for Filter version 1 parsers. + * + * Inherits from: + * - + */ +OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, { + + /** + * Property: namespaces + * {Object} Mapping of namespace aliases to namespace URIs. + */ + namespaces: { + ogc: "http://www.opengis.net/ogc", + gml: "http://www.opengis.net/gml", + xlink: "http://www.w3.org/1999/xlink", + xsi: "http://www.w3.org/2001/XMLSchema-instance" + }, + + /** + * Property: defaultPrefix + */ + defaultPrefix: "ogc", + + /** + * Property: schemaLocation + * {String} Schema location for a particular minor version. + */ + schemaLocation: null, + + /** + * Constructor: OpenLayers.Format.Filter.v1 + * Instances of this class are not created directly. Use the + * constructor instead. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + */ + initialize: function(options) { + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); + }, + + /** + * Method: read + * + * Parameters: + * data - {DOMElement} A Filter document element. + * + * Returns: + * {} A filter object. + */ + read: function(data) { + var obj = {}; + this.readers.ogc["Filter"].apply(this, [data, obj]); + return obj.filter; + }, + + /** + * Property: readers + * Contains public functions, grouped by namespace prefix, that will + * be applied when a namespaced node is found matching the function + * name. The function will be applied in the scope of this parser + * with two arguments: the node being read and a context object passed + * from the parent. + */ + readers: { + "ogc": { + "_expression": function(node) { + // only the simplest of ogc:expression handled + // "some text and an attribute"} + var obj, value = ""; + for(var child=node.firstChild; child; child=child.nextSibling) { + switch(child.nodeType) { + case 1: + obj = this.readNode(child); + if (obj.property) { + value += "${" + obj.property + "}"; + } else if (obj.value !== undefined) { + value += obj.value; + } + break; + case 3: // text node + case 4: // cdata section + value += child.nodeValue; + } + } + return value; + }, + "Filter": function(node, parent) { + // Filters correspond to subclasses of OpenLayers.Filter. + // Since they contain information we don't persist, we + // create a temporary object and then pass on the filter + // (ogc:Filter) to the parent obj. + var obj = { + fids: [], + filters: [] + }; + this.readChildNodes(node, obj); + if(obj.fids.length > 0) { + parent.filter = new OpenLayers.Filter.FeatureId({ + fids: obj.fids + }); + } else if(obj.filters.length > 0) { + parent.filter = obj.filters[0]; + } + }, + "FeatureId": function(node, obj) { + var fid = node.getAttribute("fid"); + if(fid) { + obj.fids.push(fid); + } + }, + "And": function(node, obj) { + var filter = new OpenLayers.Filter.Logical({ + type: OpenLayers.Filter.Logical.AND + }); + this.readChildNodes(node, filter); + obj.filters.push(filter); + }, + "Or": function(node, obj) { + var filter = new OpenLayers.Filter.Logical({ + type: OpenLayers.Filter.Logical.OR + }); + this.readChildNodes(node, filter); + obj.filters.push(filter); + }, + "Not": function(node, obj) { + var filter = new OpenLayers.Filter.Logical({ + type: OpenLayers.Filter.Logical.NOT + }); + this.readChildNodes(node, filter); + obj.filters.push(filter); + }, + "PropertyIsLessThan": function(node, obj) { + var filter = new OpenLayers.Filter.Comparison({ + type: OpenLayers.Filter.Comparison.LESS_THAN + }); + this.readChildNodes(node, filter); + obj.filters.push(filter); + }, + "PropertyIsGreaterThan": function(node, obj) { + var filter = new OpenLayers.Filter.Comparison({ + type: OpenLayers.Filter.Comparison.GREATER_THAN + }); + this.readChildNodes(node, filter); + obj.filters.push(filter); + }, + "PropertyIsLessThanOrEqualTo": function(node, obj) { + var filter = new OpenLayers.Filter.Comparison({ + type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO + }); + this.readChildNodes(node, filter); + obj.filters.push(filter); + }, + "PropertyIsGreaterThanOrEqualTo": function(node, obj) { + var filter = new OpenLayers.Filter.Comparison({ + type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO + }); + this.readChildNodes(node, filter); + obj.filters.push(filter); + }, + "PropertyIsBetween": function(node, obj) { + var filter = new OpenLayers.Filter.Comparison({ + type: OpenLayers.Filter.Comparison.BETWEEN + }); + this.readChildNodes(node, filter); + obj.filters.push(filter); + }, + "Literal": function(node, obj) { + obj.value = OpenLayers.String.numericIf( + this.getChildValue(node), true); + }, + "PropertyName": function(node, filter) { + filter.property = this.getChildValue(node); + }, + "LowerBoundary": function(node, filter) { + filter.lowerBoundary = OpenLayers.String.numericIf( + this.readers.ogc._expression.call(this, node), true); + }, + "UpperBoundary": function(node, filter) { + filter.upperBoundary = OpenLayers.String.numericIf( + this.readers.ogc._expression.call(this, node), true); + }, + "Intersects": function(node, obj) { + this.readSpatial(node, obj, OpenLayers.Filter.Spatial.INTERSECTS); + }, + "Within": function(node, obj) { + this.readSpatial(node, obj, OpenLayers.Filter.Spatial.WITHIN); + }, + "Contains": function(node, obj) { + this.readSpatial(node, obj, OpenLayers.Filter.Spatial.CONTAINS); + }, + "DWithin": function(node, obj) { + this.readSpatial(node, obj, OpenLayers.Filter.Spatial.DWITHIN); + }, + "Distance": function(node, obj) { + obj.distance = parseInt(this.getChildValue(node)); + obj.distanceUnits = node.getAttribute("units"); + }, + "Function": function(node, obj) { + //TODO write decoder for it + return; + }, + "PropertyIsNull": function(node, obj) { + var filter = new OpenLayers.Filter.Comparison({ + type: OpenLayers.Filter.Comparison.IS_NULL + }); + this.readChildNodes(node, filter); + obj.filters.push(filter); + } + } + }, + + /** + * Method: readSpatial + * + * Read a {} filter. + * + * Parameters: + * node - {DOMElement} A DOM element that contains an ogc:expression. + * obj - {Object} The target object. + * type - {String} One of the OpenLayers.Filter.Spatial.* constants. + * + * Returns: + * {} The created filter. + */ + readSpatial: function(node, obj, type) { + var filter = new OpenLayers.Filter.Spatial({ + type: type + }); + this.readChildNodes(node, filter); + filter.value = filter.components[0]; + delete filter.components; + obj.filters.push(filter); + }, + + /** + * APIMethod: encodeLiteral + * Generates the string representation of a value for use in + * elements. The default encoder writes Date values as ISO 8601 + * strings. + * + * Parameters: + * value - {Object} Literal value to encode + * + * Returns: + * {String} String representation of the provided value. + */ + encodeLiteral: function(value) { + if (value instanceof Date) { + value = OpenLayers.Date.toISOString(value); + } + return value; + }, + + /** + * Method: writeOgcExpression + * Limited support for writing OGC expressions. Currently it supports + * ( || String || Number) + * + * Parameters: + * value - ( || String || Number) + * node - {DOMElement} A parent DOM element + * + * Returns: + * {DOMElement} Updated node element. + */ + writeOgcExpression: function(value, node) { + if (value instanceof OpenLayers.Filter.Function){ + this.writeNode("Function", value, node); + } else { + this.writeNode("Literal", value, node); + } + return node; + }, + + /** + * Method: write + * + * Parameters: + * filter - {} A filter object. + * + * Returns: + * {DOMElement} An ogc:Filter element. + */ + write: function(filter) { + return this.writers.ogc["Filter"].apply(this, [filter]); + }, + + /** + * Property: writers + * As a compliment to the readers property, this structure contains public + * writing functions grouped by namespace alias and named like the + * node names they produce. + */ + writers: { + "ogc": { + "Filter": function(filter) { + var node = this.createElementNSPlus("ogc:Filter"); + this.writeNode(this.getFilterType(filter), filter, node); + return node; + }, + "_featureIds": function(filter) { + var node = this.createDocumentFragment(); + for (var i=0, ii=filter.fids.length; i": "PropertyIsGreaterThan", + "<=": "PropertyIsLessThanOrEqualTo", + ">=": "PropertyIsGreaterThanOrEqualTo", + "..": "PropertyIsBetween", + "~": "PropertyIsLike", + "NULL": "PropertyIsNull", + "BBOX": "BBOX", + "DWITHIN": "DWITHIN", + "WITHIN": "WITHIN", + "CONTAINS": "CONTAINS", + "INTERSECTS": "INTERSECTS", + "FID": "_featureIds" + }, + + CLASS_NAME: "OpenLayers.Format.Filter.v1" + +}); diff --git a/misc/openlayers/lib/OpenLayers/Format/Filter/v1_0_0.js b/misc/openlayers/lib/OpenLayers/Format/Filter/v1_0_0.js new file mode 100644 index 0000000..52e650e --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/Filter/v1_0_0.js @@ -0,0 +1,184 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/GML/v2.js + * @requires OpenLayers/Format/Filter/v1.js + */ + +/** + * Class: OpenLayers.Format.Filter.v1_0_0 + * Write ogc:Filter version 1.0.0. + * + * Inherits from: + * - + * - + */ +OpenLayers.Format.Filter.v1_0_0 = OpenLayers.Class( + OpenLayers.Format.GML.v2, OpenLayers.Format.Filter.v1, { + + /** + * Constant: VERSION + * {String} 1.0.0 + */ + VERSION: "1.0.0", + + /** + * Property: schemaLocation + * {String} http://www.opengis.net/ogc/filter/1.0.0/filter.xsd + */ + schemaLocation: "http://www.opengis.net/ogc/filter/1.0.0/filter.xsd", + + /** + * Constructor: OpenLayers.Format.Filter.v1_0_0 + * Instances of this class are not created directly. Use the + * constructor instead. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + */ + initialize: function(options) { + OpenLayers.Format.GML.v2.prototype.initialize.apply( + this, [options] + ); + }, + + /** + * Property: readers + * Contains public functions, grouped by namespace prefix, that will + * be applied when a namespaced node is found matching the function + * name. The function will be applied in the scope of this parser + * with two arguments: the node being read and a context object passed + * from the parent. + */ + readers: { + "ogc": OpenLayers.Util.applyDefaults({ + "PropertyIsEqualTo": function(node, obj) { + var filter = new OpenLayers.Filter.Comparison({ + type: OpenLayers.Filter.Comparison.EQUAL_TO + }); + this.readChildNodes(node, filter); + obj.filters.push(filter); + }, + "PropertyIsNotEqualTo": function(node, obj) { + var filter = new OpenLayers.Filter.Comparison({ + type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO + }); + this.readChildNodes(node, filter); + obj.filters.push(filter); + }, + "PropertyIsLike": function(node, obj) { + var filter = new OpenLayers.Filter.Comparison({ + type: OpenLayers.Filter.Comparison.LIKE + }); + this.readChildNodes(node, filter); + var wildCard = node.getAttribute("wildCard"); + var singleChar = node.getAttribute("singleChar"); + var esc = node.getAttribute("escape"); + filter.value2regex(wildCard, singleChar, esc); + obj.filters.push(filter); + } + }, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]), + "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"], + "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"] + }, + + /** + * Property: writers + * As a compliment to the readers property, this structure contains public + * writing functions grouped by namespace alias and named like the + * node names they produce. + */ + writers: { + "ogc": OpenLayers.Util.applyDefaults({ + "PropertyIsEqualTo": function(filter) { + var node = this.createElementNSPlus("ogc:PropertyIsEqualTo"); + // no ogc:expression handling for PropertyName for now + this.writeNode("PropertyName", filter, node); + // handle Literals or Functions for now + this.writeOgcExpression(filter.value, node); + return node; + }, + "PropertyIsNotEqualTo": function(filter) { + var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo"); + // no ogc:expression handling for PropertyName for now + this.writeNode("PropertyName", filter, node); + // handle Literals or Functions for now + this.writeOgcExpression(filter.value, node); + return node; + }, + "PropertyIsLike": function(filter) { + var node = this.createElementNSPlus("ogc:PropertyIsLike", { + attributes: { + wildCard: "*", singleChar: ".", escape: "!" + } + }); + // no ogc:expression handling for now + this.writeNode("PropertyName", filter, node); + // convert regex string to ogc string + this.writeNode("Literal", filter.regex2value(), node); + return node; + }, + "BBOX": function(filter) { + var node = this.createElementNSPlus("ogc:BBOX"); + // PropertyName is mandatory in 1.0.0, but e.g. GeoServer also + // accepts filters without it. When this is used with + // OpenLayers.Protocol.WFS, OpenLayers.Format.WFST will set a + // missing filter.property to the geometryName that is + // configured with the protocol, which defaults to "the_geom". + // So the only way to omit this mandatory property is to not + // set the property on the filter and to set the geometryName + // on the WFS protocol to null. The latter also happens when + // the protocol is configured without a geometryName and a + // featureNS. + filter.property && this.writeNode("PropertyName", filter, node); + var box = this.writeNode("gml:Box", filter.value, node); + if(filter.projection) { + box.setAttribute("srsName", filter.projection); + } + return node; + } + }, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]), + "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"], + "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"] + }, + + /** + * Method: writeSpatial + * + * Read a {} filter and converts it into XML. + * + * Parameters: + * filter - {} The filter. + * name - {String} Name of the generated XML element. + * + * Returns: + * {DOMElement} The created XML element. + */ + writeSpatial: function(filter, name) { + var node = this.createElementNSPlus("ogc:"+name); + this.writeNode("PropertyName", filter, node); + if(filter.value instanceof OpenLayers.Filter.Function) { + this.writeNode("Function", filter.value, node); + } else { + var child; + if(filter.value instanceof OpenLayers.Geometry) { + child = this.writeNode("feature:_geometry", filter.value).firstChild; + } else { + child = this.writeNode("gml:Box", filter.value); + } + if(filter.projection) { + child.setAttribute("srsName", filter.projection); + } + node.appendChild(child); + } + return node; + }, + + + CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0" + +}); \ No newline at end of file diff --git a/misc/openlayers/lib/OpenLayers/Format/Filter/v1_1_0.js b/misc/openlayers/lib/OpenLayers/Format/Filter/v1_1_0.js new file mode 100644 index 0000000..628c489 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/Filter/v1_1_0.js @@ -0,0 +1,222 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/Filter/v1.js + * @requires OpenLayers/Format/GML/v3.js + */ + +/** + * Class: OpenLayers.Format.Filter.v1_1_0 + * Write ogc:Filter version 1.1.0. + * + * Differences from the v1.0.0 parser: + * - uses GML v3 instead of GML v2 + * - reads matchCase attribute on ogc:PropertyIsEqual and + * ogc:PropertyIsNotEqual elements. + * - writes matchCase attribute from comparison filters of type EQUAL_TO, + * NOT_EQUAL_TO and LIKE. + * + * Inherits from: + * - + * - + */ +OpenLayers.Format.Filter.v1_1_0 = OpenLayers.Class( + OpenLayers.Format.GML.v3, OpenLayers.Format.Filter.v1, { + + /** + * Constant: VERSION + * {String} 1.1.0 + */ + VERSION: "1.1.0", + + /** + * Property: schemaLocation + * {String} http://www.opengis.net/ogc/filter/1.1.0/filter.xsd + */ + schemaLocation: "http://www.opengis.net/ogc/filter/1.1.0/filter.xsd", + + /** + * Constructor: OpenLayers.Format.Filter.v1_1_0 + * Instances of this class are not created directly. Use the + * constructor instead. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + */ + initialize: function(options) { + OpenLayers.Format.GML.v3.prototype.initialize.apply( + this, [options] + ); + }, + + /** + * Property: readers + * Contains public functions, grouped by namespace prefix, that will + * be applied when a namespaced node is found matching the function + * name. The function will be applied in the scope of this parser + * with two arguments: the node being read and a context object passed + * from the parent. + */ + readers: { + "ogc": OpenLayers.Util.applyDefaults({ + "PropertyIsEqualTo": function(node, obj) { + var matchCase = node.getAttribute("matchCase"); + var filter = new OpenLayers.Filter.Comparison({ + type: OpenLayers.Filter.Comparison.EQUAL_TO, + matchCase: !(matchCase === "false" || matchCase === "0") + }); + this.readChildNodes(node, filter); + obj.filters.push(filter); + }, + "PropertyIsNotEqualTo": function(node, obj) { + var matchCase = node.getAttribute("matchCase"); + var filter = new OpenLayers.Filter.Comparison({ + type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO, + matchCase: !(matchCase === "false" || matchCase === "0") + }); + this.readChildNodes(node, filter); + obj.filters.push(filter); + }, + "PropertyIsLike": function(node, obj) { + var filter = new OpenLayers.Filter.Comparison({ + type: OpenLayers.Filter.Comparison.LIKE + }); + this.readChildNodes(node, filter); + var wildCard = node.getAttribute("wildCard"); + var singleChar = node.getAttribute("singleChar"); + var esc = node.getAttribute("escapeChar"); + filter.value2regex(wildCard, singleChar, esc); + obj.filters.push(filter); + } + }, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]), + "gml": OpenLayers.Format.GML.v3.prototype.readers["gml"], + "feature": OpenLayers.Format.GML.v3.prototype.readers["feature"] + }, + + /** + * Property: writers + * As a compliment to the readers property, this structure contains public + * writing functions grouped by namespace alias and named like the + * node names they produce. + */ + writers: { + "ogc": OpenLayers.Util.applyDefaults({ + "PropertyIsEqualTo": function(filter) { + var node = this.createElementNSPlus("ogc:PropertyIsEqualTo", { + attributes: {matchCase: filter.matchCase} + }); + // no ogc:expression handling for PropertyName for now + this.writeNode("PropertyName", filter, node); + // handle Literals or Functions for now + this.writeOgcExpression(filter.value, node); + return node; + }, + "PropertyIsNotEqualTo": function(filter) { + var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo", { + attributes: {matchCase: filter.matchCase} + }); + // no ogc:expression handling for PropertyName for now + this.writeNode("PropertyName", filter, node); + // handle Literals or Functions for now + this.writeOgcExpression(filter.value, node); + return node; + }, + "PropertyIsLike": function(filter) { + var node = this.createElementNSPlus("ogc:PropertyIsLike", { + attributes: { + matchCase: filter.matchCase, + wildCard: "*", singleChar: ".", escapeChar: "!" + } + }); + // no ogc:expression handling for now + this.writeNode("PropertyName", filter, node); + // convert regex string to ogc string + this.writeNode("Literal", filter.regex2value(), node); + return node; + }, + "BBOX": function(filter) { + var node = this.createElementNSPlus("ogc:BBOX"); + // PropertyName is optional in 1.1.0 + filter.property && this.writeNode("PropertyName", filter, node); + var box = this.writeNode("gml:Envelope", filter.value); + if(filter.projection) { + box.setAttribute("srsName", filter.projection); + } + node.appendChild(box); + return node; + }, + "SortBy": function(sortProperties) { + var node = this.createElementNSPlus("ogc:SortBy"); + for (var i=0,l=sortProperties.length;i} filter and converts it into XML. + * + * Parameters: + * filter - {} The filter. + * name - {String} Name of the generated XML element. + * + * Returns: + * {DOMElement} The created XML element. + */ + writeSpatial: function(filter, name) { + var node = this.createElementNSPlus("ogc:"+name); + this.writeNode("PropertyName", filter, node); + if(filter.value instanceof OpenLayers.Filter.Function) { + this.writeNode("Function", filter.value, node); + } else { + var child; + if(filter.value instanceof OpenLayers.Geometry) { + child = this.writeNode("feature:_geometry", filter.value).firstChild; + } else { + child = this.writeNode("gml:Envelope", filter.value); + } + if(filter.projection) { + child.setAttribute("srsName", filter.projection); + } + node.appendChild(child); + } + return node; + }, + + CLASS_NAME: "OpenLayers.Format.Filter.v1_1_0" + +}); diff --git a/misc/openlayers/lib/OpenLayers/Format/GML.js b/misc/openlayers/lib/OpenLayers/Format/GML.js new file mode 100644 index 0000000..467f875 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/GML.js @@ -0,0 +1,923 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/XML.js + * @requires OpenLayers/Feature/Vector.js + * @requires OpenLayers/Geometry/Point.js + * @requires OpenLayers/Geometry/MultiPoint.js + * @requires OpenLayers/Geometry/LineString.js + * @requires OpenLayers/Geometry/MultiLineString.js + * @requires OpenLayers/Geometry/Polygon.js + * @requires OpenLayers/Geometry/MultiPolygon.js + */ + +/** + * Class: OpenLayers.Format.GML + * Read/Write GML. Create a new instance with the + * constructor. Supports the GML simple features profile. + * + * Inherits from: + * - + */ +OpenLayers.Format.GML = OpenLayers.Class(OpenLayers.Format.XML, { + + /** + * APIProperty: featureNS + * {String} Namespace used for feature attributes. Default is + * "http://mapserver.gis.umn.edu/mapserver". + */ + featureNS: "http://mapserver.gis.umn.edu/mapserver", + + /** + * APIProperty: featurePrefix + * {String} Namespace alias (or prefix) for feature nodes. Default is + * "feature". + */ + featurePrefix: "feature", + + /** + * APIProperty: featureName + * {String} Element name for features. Default is "featureMember". + */ + featureName: "featureMember", + + /** + * APIProperty: layerName + * {String} Name of data layer. Default is "features". + */ + layerName: "features", + + /** + * APIProperty: geometryName + * {String} Name of geometry element. Defaults to "geometry". + */ + geometryName: "geometry", + + /** + * APIProperty: collectionName + * {String} Name of featureCollection element. + */ + collectionName: "FeatureCollection", + + /** + * APIProperty: gmlns + * {String} GML Namespace. + */ + gmlns: "http://www.opengis.net/gml", + + /** + * APIProperty: extractAttributes + * {Boolean} Extract attributes from GML. + */ + extractAttributes: true, + + /** + * APIProperty: xy + * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x) + * Changing is not recommended, a new Format should be instantiated. + */ + xy: true, + + /** + * Constructor: OpenLayers.Format.GML + * Create a new parser for GML. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + */ + initialize: function(options) { + // compile regular expressions once instead of every time they are used + this.regExes = { + trimSpace: (/^\s*|\s*$/g), + removeSpace: (/\s*/g), + splitSpace: (/\s+/), + trimComma: (/\s*,\s*/g) + }; + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); + }, + + /** + * APIMethod: read + * Read data from a string, and return a list of features. + * + * Parameters: + * data - {String} or {DOMElement} data to read/parse. + * + * Returns: + * {Array()} An array of features. + */ + read: function(data) { + if(typeof data == "string") { + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); + } + var featureNodes = this.getElementsByTagNameNS(data.documentElement, + this.gmlns, + this.featureName); + var features = []; + for(var i=0; i 0) { + // only deal with first geometry of this type + parser = this.parseGeometry[type.toLowerCase()]; + if(parser) { + geometry = parser.apply(this, [nodeList[0]]); + if (this.internalProjection && this.externalProjection) { + geometry.transform(this.externalProjection, + this.internalProjection); + } + } else { + throw new TypeError("Unsupported geometry type: " + type); + } + // stop looking for different geometry types + break; + } + } + + var bounds; + var boxNodes = this.getElementsByTagNameNS(node, this.gmlns, "Box"); + for(i=0; i} A point geometry. + */ + point: function(node) { + /** + * Three coordinate variations to consider: + * 1) x y z + * 2) x, y, z + * 3) xy + */ + var nodeList, coordString; + var coords = []; + + // look for + var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "pos"); + if(nodeList.length > 0) { + coordString = nodeList[0].firstChild.nodeValue; + coordString = coordString.replace(this.regExes.trimSpace, ""); + coords = coordString.split(this.regExes.splitSpace); + } + + // look for + if(coords.length == 0) { + nodeList = this.getElementsByTagNameNS(node, this.gmlns, + "coordinates"); + if(nodeList.length > 0) { + coordString = nodeList[0].firstChild.nodeValue; + coordString = coordString.replace(this.regExes.removeSpace, + ""); + coords = coordString.split(","); + } + } + + // look for + if(coords.length == 0) { + nodeList = this.getElementsByTagNameNS(node, this.gmlns, + "coord"); + if(nodeList.length > 0) { + var xList = this.getElementsByTagNameNS(nodeList[0], + this.gmlns, "X"); + var yList = this.getElementsByTagNameNS(nodeList[0], + this.gmlns, "Y"); + if(xList.length > 0 && yList.length > 0) { + coords = [xList[0].firstChild.nodeValue, + yList[0].firstChild.nodeValue]; + } + } + } + + // preserve third dimension + if(coords.length == 2) { + coords[2] = null; + } + + if (this.xy) { + return new OpenLayers.Geometry.Point(coords[0], coords[1], + coords[2]); + } + else{ + return new OpenLayers.Geometry.Point(coords[1], coords[0], + coords[2]); + } + }, + + /** + * Method: parseGeometry.multipoint + * Given a GML node representing a multipoint geometry, create an + * OpenLayers multipoint geometry. + * + * Parameters: + * node - {DOMElement} A GML node. + * + * Returns: + * {} A multipoint geometry. + */ + multipoint: function(node) { + var nodeList = this.getElementsByTagNameNS(node, this.gmlns, + "Point"); + var components = []; + if(nodeList.length > 0) { + var point; + for(var i=0; i} A linestring geometry. + */ + linestring: function(node, ring) { + /** + * Two coordinate variations to consider: + * 1) x0 y0 z0 x1 y1 z1 + * 2) x0, y0, z0 x1, y1, z1 + */ + var nodeList, coordString; + var coords = []; + var points = []; + + // look for + nodeList = this.getElementsByTagNameNS(node, this.gmlns, "posList"); + if(nodeList.length > 0) { + coordString = this.getChildValue(nodeList[0]); + coordString = coordString.replace(this.regExes.trimSpace, ""); + coords = coordString.split(this.regExes.splitSpace); + var dim = parseInt(nodeList[0].getAttribute("dimension")); + var j, x, y, z; + for(var i=0; i + if(coords.length == 0) { + nodeList = this.getElementsByTagNameNS(node, this.gmlns, + "coordinates"); + if(nodeList.length > 0) { + coordString = this.getChildValue(nodeList[0]); + coordString = coordString.replace(this.regExes.trimSpace, + ""); + coordString = coordString.replace(this.regExes.trimComma, + ","); + var pointList = coordString.split(this.regExes.splitSpace); + for(var i=0; i} A multilinestring geometry. + */ + multilinestring: function(node) { + var nodeList = this.getElementsByTagNameNS(node, this.gmlns, + "LineString"); + var components = []; + if(nodeList.length > 0) { + var line; + for(var i=0; i} A polygon geometry. + */ + polygon: function(node) { + var nodeList = this.getElementsByTagNameNS(node, this.gmlns, + "LinearRing"); + var components = []; + if(nodeList.length > 0) { + // this assumes exterior ring first, inner rings after + var ring; + for(var i=0; i} A multipolygon geometry. + */ + multipolygon: function(node) { + var nodeList = this.getElementsByTagNameNS(node, this.gmlns, + "Polygon"); + var components = []; + if(nodeList.length > 0) { + var polygon; + for(var i=0; i 0) { + var coords = []; + + if(lpoint.length > 0) { + coordString = lpoint[0].firstChild.nodeValue; + coordString = coordString.replace(this.regExes.trimSpace, ""); + coords = coordString.split(this.regExes.splitSpace); + } + + if(coords.length == 2) { + coords[2] = null; + } + if (this.xy) { + var lowerPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]); + } else { + var lowerPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]); + } + } + + var upoint = this.getElementsByTagNameNS(node, this.gmlns, "upperCorner"); + if (upoint.length > 0) { + var coords = []; + + if(upoint.length > 0) { + coordString = upoint[0].firstChild.nodeValue; + coordString = coordString.replace(this.regExes.trimSpace, ""); + coords = coordString.split(this.regExes.splitSpace); + } + + if(coords.length == 2) { + coords[2] = null; + } + if (this.xy) { + var upperPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]); + } else { + var upperPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]); + } + } + + if (lowerPoint && upperPoint) { + components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y)); + components.push(new OpenLayers.Geometry.Point(upperPoint.x, lowerPoint.y)); + components.push(new OpenLayers.Geometry.Point(upperPoint.x, upperPoint.y)); + components.push(new OpenLayers.Geometry.Point(lowerPoint.x, upperPoint.y)); + components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y)); + + var ring = new OpenLayers.Geometry.LinearRing(components); + envelope = new OpenLayers.Geometry.Polygon([ring]); + } + return envelope; + }, + + /** + * Method: parseGeometry.box + * Given a GML node representing a box geometry, create an + * OpenLayers.Bounds. + * + * Parameters: + * node - {DOMElement} A GML node. + * + * Returns: + * {} A bounds representing the box. + */ + box: function(node) { + var nodeList = this.getElementsByTagNameNS(node, this.gmlns, + "coordinates"); + var coordString; + var coords, beginPoint = null, endPoint = null; + if (nodeList.length > 0) { + coordString = nodeList[0].firstChild.nodeValue; + coords = coordString.split(" "); + if (coords.length == 2) { + beginPoint = coords[0].split(","); + endPoint = coords[1].split(","); + } + } + if (beginPoint !== null && endPoint !== null) { + return new OpenLayers.Bounds(parseFloat(beginPoint[0]), + parseFloat(beginPoint[1]), + parseFloat(endPoint[0]), + parseFloat(endPoint[1]) ); + } + } + + }, + + /** + * Method: parseAttributes + * + * Parameters: + * node - {DOMElement} + * + * Returns: + * {Object} An attributes object. + */ + parseAttributes: function(node) { + var attributes = {}; + // assume attributes are children of the first type 1 child + var childNode = node.firstChild; + var children, i, child, grandchildren, grandchild, name, value; + while(childNode) { + if(childNode.nodeType == 1) { + // attributes are type 1 children with one type 3 child + children = childNode.childNodes; + for(i=0; i becomes + // {fieldname: null} + attributes[child.nodeName.split(":").pop()] = null; + } + } + } + break; + } + childNode = childNode.nextSibling; + } + return attributes; + }, + + /** + * APIMethod: write + * Generate a GML document string given a list of features. + * + * Parameters: + * features - {Array()} List of features to + * serialize into a string. + * + * Returns: + * {String} A string representing the GML document. + */ + write: function(features) { + if(!(OpenLayers.Util.isArray(features))) { + features = [features]; + } + var gml = this.createElementNS("http://www.opengis.net/wfs", + "wfs:" + this.collectionName); + for(var i=0; i} The feature to be built as GML. + * + * Returns: + * {DOMElement} A node reprensting the feature in GML. + */ + createFeatureXML: function(feature) { + var geometry = feature.geometry; + var geometryNode = this.buildGeometryNode(geometry); + var geomContainer = this.createElementNS(this.featureNS, + this.featurePrefix + ":" + + this.geometryName); + geomContainer.appendChild(geometryNode); + var featureNode = this.createElementNS(this.gmlns, + "gml:" + this.featureName); + var featureContainer = this.createElementNS(this.featureNS, + this.featurePrefix + ":" + + this.layerName); + var fid = feature.fid || feature.id; + featureContainer.setAttribute("fid", fid); + featureContainer.appendChild(geomContainer); + for(var attr in feature.attributes) { + var attrText = this.createTextNode(feature.attributes[attr]); + var nodename = attr.substring(attr.lastIndexOf(":") + 1); + var attrContainer = this.createElementNS(this.featureNS, + this.featurePrefix + ":" + + nodename); + attrContainer.appendChild(attrText); + featureContainer.appendChild(attrContainer); + } + featureNode.appendChild(featureContainer); + return featureNode; + }, + + /** + * APIMethod: buildGeometryNode + */ + buildGeometryNode: function(geometry) { + if (this.externalProjection && this.internalProjection) { + geometry = geometry.clone(); + geometry.transform(this.internalProjection, + this.externalProjection); + } + var className = geometry.CLASS_NAME; + var type = className.substring(className.lastIndexOf(".") + 1); + var builder = this.buildGeometry[type.toLowerCase()]; + return builder.apply(this, [geometry]); + }, + + /** + * Property: buildGeometry + * Object containing methods to do the actual geometry node building + * based on geometry type. + */ + buildGeometry: { + // TBD retrieve the srs from layer + // srsName is non-standard, so not including it until it's right. + // gml.setAttribute("srsName", + // "http://www.opengis.net/gml/srs/epsg.xml#4326"); + + /** + * Method: buildGeometry.point + * Given an OpenLayers point geometry, create a GML point. + * + * Parameters: + * geometry - {} A point geometry. + * + * Returns: + * {DOMElement} A GML point node. + */ + point: function(geometry) { + var gml = this.createElementNS(this.gmlns, "gml:Point"); + gml.appendChild(this.buildCoordinatesNode(geometry)); + return gml; + }, + + /** + * Method: buildGeometry.multipoint + * Given an OpenLayers multipoint geometry, create a GML multipoint. + * + * Parameters: + * geometry - {} A multipoint geometry. + * + * Returns: + * {DOMElement} A GML multipoint node. + */ + multipoint: function(geometry) { + var gml = this.createElementNS(this.gmlns, "gml:MultiPoint"); + var points = geometry.components; + var pointMember, pointGeom; + for(var i=0; i} A linestring geometry. + * + * Returns: + * {DOMElement} A GML linestring node. + */ + linestring: function(geometry) { + var gml = this.createElementNS(this.gmlns, "gml:LineString"); + gml.appendChild(this.buildCoordinatesNode(geometry)); + return gml; + }, + + /** + * Method: buildGeometry.multilinestring + * Given an OpenLayers multilinestring geometry, create a GML + * multilinestring. + * + * Parameters: + * geometry - {} A multilinestring + * geometry. + * + * Returns: + * {DOMElement} A GML multilinestring node. + */ + multilinestring: function(geometry) { + var gml = this.createElementNS(this.gmlns, "gml:MultiLineString"); + var lines = geometry.components; + var lineMember, lineGeom; + for(var i=0; i} A linearring geometry. + * + * Returns: + * {DOMElement} A GML linearring node. + */ + linearring: function(geometry) { + var gml = this.createElementNS(this.gmlns, "gml:LinearRing"); + gml.appendChild(this.buildCoordinatesNode(geometry)); + return gml; + }, + + /** + * Method: buildGeometry.polygon + * Given an OpenLayers polygon geometry, create a GML polygon. + * + * Parameters: + * geometry - {} A polygon geometry. + * + * Returns: + * {DOMElement} A GML polygon node. + */ + polygon: function(geometry) { + var gml = this.createElementNS(this.gmlns, "gml:Polygon"); + var rings = geometry.components; + var ringMember, ringGeom, type; + for(var i=0; i} A multipolygon + * geometry. + * + * Returns: + * {DOMElement} A GML multipolygon node. + */ + multipolygon: function(geometry) { + var gml = this.createElementNS(this.gmlns, "gml:MultiPolygon"); + var polys = geometry.components; + var polyMember, polyGeom; + for(var i=0; i} A bounds object. + * + * Returns: + * {DOMElement} A GML box node. + */ + bounds: function(bounds) { + var gml = this.createElementNS(this.gmlns, "gml:Box"); + gml.appendChild(this.buildCoordinatesNode(bounds)); + return gml; + } + }, + + /** + * Method: buildCoordinates + * builds the coordinates XmlNode + * (code) + * ... + * (end) + * + * Parameters: + * geometry - {} + * + * Returns: + * {XmlNode} created xmlNode + */ + buildCoordinatesNode: function(geometry) { + var coordinatesNode = this.createElementNS(this.gmlns, + "gml:coordinates"); + coordinatesNode.setAttribute("decimal", "."); + coordinatesNode.setAttribute("cs", ","); + coordinatesNode.setAttribute("ts", " "); + + var parts = []; + + if(geometry instanceof OpenLayers.Bounds){ + parts.push(geometry.left + "," + geometry.bottom); + parts.push(geometry.right + "," + geometry.top); + } else { + var points = (geometry.components) ? geometry.components : [geometry]; + for(var i=0; i + */ +OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, { + + /** + * Property: namespaces + * {Object} Mapping of namespace aliases to namespace URIs. + */ + namespaces: { + gml: "http://www.opengis.net/gml", + xlink: "http://www.w3.org/1999/xlink", + xsi: "http://www.w3.org/2001/XMLSchema-instance", + wfs: "http://www.opengis.net/wfs" // this is a convenience for reading wfs:FeatureCollection + }, + + /** + * Property: defaultPrefix + */ + defaultPrefix: "gml", + + /** + * Property: schemaLocation + * {String} Schema location for a particular minor version. + */ + schemaLocation: null, + + /** + * APIProperty: featureType + * {Array(String) or String} The local (without prefix) feature typeName(s). + */ + featureType: null, + + /** + * APIProperty: featureNS + * {String} The feature namespace. Must be set in the options at + * construction. + */ + featureNS: null, + + /** + * APIProperty: geometry + * {String} Name of geometry element. Defaults to "geometry". If null, it + * will be set on when the first geometry is parsed. + */ + geometryName: "geometry", + + /** + * APIProperty: extractAttributes + * {Boolean} Extract attributes from GML. Default is true. + */ + extractAttributes: true, + + /** + * APIProperty: srsName + * {String} URI for spatial reference system. This is optional for + * single part geometries and mandatory for collections and multis. + * If set, the srsName attribute will be written for all geometries. + * Default is null. + */ + srsName: null, + + /** + * APIProperty: xy + * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x) + * Changing is not recommended, a new Format should be instantiated. + */ + xy: true, + + /** + * Property: geometryTypes + * {Object} Maps OpenLayers geometry class names to GML element names. + * Use before accessing this property. + */ + geometryTypes: null, + + /** + * Property: singleFeatureType + * {Boolean} True if there is only 1 featureType, and not an array + * of featuretypes. + */ + singleFeatureType: null, + + /** + * Property: autoConfig + * {Boolean} Indicates if the format was configured without a , + * but auto-configured and during read. + * Subclasses making use of auto-configuration should make + * the first call to the method (usually in the read method) + * with true as 3rd argument, so the auto-configured featureType can be + * reset and the format can be reused for subsequent reads with data from + * different featureTypes. Set to false after read if you want to keep the + * auto-configured values. + */ + + /** + * Property: regExes + * Compiled regular expressions for manipulating strings. + */ + regExes: { + trimSpace: (/^\s*|\s*$/g), + removeSpace: (/\s*/g), + splitSpace: (/\s+/), + trimComma: (/\s*,\s*/g), + featureMember: (/^(.*:)?featureMembers?$/) + }, + + /** + * Constructor: OpenLayers.Format.GML.Base + * Instances of this class are not created directly. Use the + * or constructor + * instead. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + * + * Valid options properties: + * featureType - {Array(String) or String} Local (without prefix) feature + * typeName(s) (required for write). + * featureNS - {String} Feature namespace (required for write). + * geometryName - {String} Geometry element name (required for write). + */ + initialize: function(options) { + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); + this.setGeometryTypes(); + if(options && options.featureNS) { + this.setNamespace("feature", options.featureNS); + } + this.singleFeatureType = !options || (typeof options.featureType === "string"); + }, + + /** + * Method: read + * + * Parameters: + * data - {DOMElement} A gml:featureMember element, a gml:featureMembers + * element, or an element containing either of the above at any level. + * + * Returns: + * {Array()} An array of features. + */ + read: function(data) { + if(typeof data == "string") { + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); + } + if(data && data.nodeType == 9) { + data = data.documentElement; + } + var features = []; + this.readNode(data, {features: features}, true); + if(features.length == 0) { + // look for gml:featureMember elements + var elements = this.getElementsByTagNameNS( + data, this.namespaces.gml, "featureMember" + ); + if(elements.length) { + for(var i=0, len=elements.length; i 0) { + obj.bounds = container.components[0]; + } + }, + "Point": function(node, container) { + var obj = {points: []}; + this.readChildNodes(node, obj); + if(!container.components) { + container.components = []; + } + container.components.push(obj.points[0]); + }, + "coordinates": function(node, obj) { + var str = this.getChildValue(node).replace( + this.regExes.trimSpace, "" + ); + str = str.replace(this.regExes.trimComma, ","); + var pointList = str.split(this.regExes.splitSpace); + var coords; + var numPoints = pointList.length; + var points = new Array(numPoints); + for(var i=0; i) | OpenLayers.Feature.Vector} + * An array of features or a single feature. + * + * Returns: + * {String} Given an array of features, a doc with a gml:featureMembers + * element will be returned. Given a single feature, a doc with a + * gml:featureMember element will be returned. + */ + write: function(features) { + var name; + if(OpenLayers.Util.isArray(features)) { + name = "featureMembers"; + } else { + name = "featureMember"; + } + var root = this.writeNode("gml:" + name, features); + this.setAttributeNS( + root, this.namespaces["xsi"], + "xsi:schemaLocation", this.schemaLocation + ); + + return OpenLayers.Format.XML.prototype.write.apply(this, [root]); + }, + + /** + * Property: writers + * As a compliment to the readers property, this structure contains public + * writing functions grouped by namespace alias and named like the + * node names they produce. + */ + writers: { + "gml": { + "featureMember": function(feature) { + var node = this.createElementNSPlus("gml:featureMember"); + this.writeNode("feature:_typeName", feature, node); + return node; + }, + "MultiPoint": function(geometry) { + var node = this.createElementNSPlus("gml:MultiPoint"); + var components = geometry.components || [geometry]; + for(var i=0, ii=components.length; i mapping. + */ + setGeometryTypes: function() { + this.geometryTypes = { + "OpenLayers.Geometry.Point": "Point", + "OpenLayers.Geometry.MultiPoint": "MultiPoint", + "OpenLayers.Geometry.LineString": "LineString", + "OpenLayers.Geometry.MultiLineString": "MultiLineString", + "OpenLayers.Geometry.Polygon": "Polygon", + "OpenLayers.Geometry.MultiPolygon": "MultiPolygon", + "OpenLayers.Geometry.Collection": "GeometryCollection" + }; + }, + + CLASS_NAME: "OpenLayers.Format.GML.Base" + +}); diff --git a/misc/openlayers/lib/OpenLayers/Format/GML/v2.js b/misc/openlayers/lib/OpenLayers/Format/GML/v2.js new file mode 100644 index 0000000..bd26b99 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/GML/v2.js @@ -0,0 +1,193 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/GML/Base.js + */ + +/** + * Class: OpenLayers.Format.GML.v2 + * Parses GML version 2. + * + * Inherits from: + * - + */ +OpenLayers.Format.GML.v2 = OpenLayers.Class(OpenLayers.Format.GML.Base, { + + /** + * Property: schemaLocation + * {String} Schema location for a particular minor version. + */ + schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd", + + /** + * Constructor: OpenLayers.Format.GML.v2 + * Create a parser for GML v2. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + * + * Valid options properties: + * featureType - {String} Local (without prefix) feature typeName (required). + * featureNS - {String} Feature namespace (required). + * geometryName - {String} Geometry element name. + */ + initialize: function(options) { + OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]); + }, + + /** + * Property: readers + * Contains public functions, grouped by namespace prefix, that will + * be applied when a namespaced node is found matching the function + * name. The function will be applied in the scope of this parser + * with two arguments: the node being read and a context object passed + * from the parent. + */ + readers: { + "gml": OpenLayers.Util.applyDefaults({ + "outerBoundaryIs": function(node, container) { + var obj = {}; + this.readChildNodes(node, obj); + container.outer = obj.components[0]; + }, + "innerBoundaryIs": function(node, container) { + var obj = {}; + this.readChildNodes(node, obj); + container.inner.push(obj.components[0]); + }, + "Box": function(node, container) { + var obj = {}; + this.readChildNodes(node, obj); + if(!container.components) { + container.components = []; + } + var min = obj.points[0]; + var max = obj.points[1]; + container.components.push( + new OpenLayers.Bounds(min.x, min.y, max.x, max.y) + ); + } + }, OpenLayers.Format.GML.Base.prototype.readers["gml"]), + "feature": OpenLayers.Format.GML.Base.prototype.readers["feature"], + "wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"] + }, + + /** + * Method: write + * + * Parameters: + * features - {Array() | OpenLayers.Feature.Vector} + * An array of features or a single feature. + * + * Returns: + * {String} Given an array of features, a doc with a gml:featureMembers + * element will be returned. Given a single feature, a doc with a + * gml:featureMember element will be returned. + */ + write: function(features) { + var name; + if(OpenLayers.Util.isArray(features)) { + // GML2 only has abstract feature collections + // wfs provides a feature collection from a well-known schema + name = "wfs:FeatureCollection"; + } else { + name = "gml:featureMember"; + } + var root = this.writeNode(name, features); + this.setAttributeNS( + root, this.namespaces["xsi"], + "xsi:schemaLocation", this.schemaLocation + ); + + return OpenLayers.Format.XML.prototype.write.apply(this, [root]); + }, + + /** + * Property: writers + * As a compliment to the readers property, this structure contains public + * writing functions grouped by namespace alias and named like the + * node names they produce. + */ + writers: { + "gml": OpenLayers.Util.applyDefaults({ + "Point": function(geometry) { + var node = this.createElementNSPlus("gml:Point"); + this.writeNode("coordinates", [geometry], node); + return node; + }, + "coordinates": function(points) { + var numPoints = points.length; + var parts = new Array(numPoints); + var point; + for(var i=0; i + */ +OpenLayers.Format.GML.v3 = OpenLayers.Class(OpenLayers.Format.GML.Base, { + + /** + * Property: schemaLocation + * {String} Schema location for a particular minor version. The writers + * conform with the Simple Features Profile for GML. + */ + schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd", + + /** + * Property: curve + * {Boolean} Write gml:Curve instead of gml:LineString elements. This also + * affects the elements in multi-part geometries. Default is false. + * To write gml:Curve elements instead of gml:LineString, set curve + * to true in the options to the contstructor (cannot be changed after + * instantiation). + */ + curve: false, + + /** + * Property: multiCurve + * {Boolean} Write gml:MultiCurve instead of gml:MultiLineString. Since + * the latter is deprecated in GML 3, the default is true. To write + * gml:MultiLineString instead of gml:MultiCurve, set multiCurve to + * false in the options to the constructor (cannot be changed after + * instantiation). + */ + multiCurve: true, + + /** + * Property: surface + * {Boolean} Write gml:Surface instead of gml:Polygon elements. This also + * affects the elements in multi-part geometries. Default is false. + * To write gml:Surface elements instead of gml:Polygon, set surface + * to true in the options to the contstructor (cannot be changed after + * instantiation). + */ + surface: false, + + /** + * Property: multiSurface + * {Boolean} Write gml:multiSurface instead of gml:MultiPolygon. Since + * the latter is deprecated in GML 3, the default is true. To write + * gml:MultiPolygon instead of gml:multiSurface, set multiSurface to + * false in the options to the constructor (cannot be changed after + * instantiation). + */ + multiSurface: true, + + /** + * Constructor: OpenLayers.Format.GML.v3 + * Create a parser for GML v3. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + * + * Valid options properties: + * featureType - {String} Local (without prefix) feature typeName (required). + * featureNS - {String} Feature namespace (required). + * geometryName - {String} Geometry element name. + */ + initialize: function(options) { + OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]); + }, + + /** + * Property: readers + * Contains public functions, grouped by namespace prefix, that will + * be applied when a namespaced node is found matching the function + * name. The function will be applied in the scope of this parser + * with two arguments: the node being read and a context object passed + * from the parent. + */ + readers: { + "gml": OpenLayers.Util.applyDefaults({ + "_inherit": function(node, obj, container) { + // SRSReferenceGroup attributes + var dim = parseInt(node.getAttribute("srsDimension"), 10) || + (container && container.srsDimension); + if (dim) { + obj.srsDimension = dim; + } + }, + "featureMembers": function(node, obj) { + this.readChildNodes(node, obj); + }, + "Curve": function(node, container) { + var obj = {points: []}; + this.readers.gml._inherit.apply(this, [node, obj, container]); + this.readChildNodes(node, obj); + if(!container.components) { + container.components = []; + } + container.components.push( + new OpenLayers.Geometry.LineString(obj.points) + ); + }, + "segments": function(node, obj) { + this.readChildNodes(node, obj); + }, + "LineStringSegment": function(node, container) { + var obj = {}; + this.readChildNodes(node, obj); + if(obj.points) { + Array.prototype.push.apply(container.points, obj.points); + } + }, + "pos": function(node, obj) { + var str = this.getChildValue(node).replace( + this.regExes.trimSpace, "" + ); + var coords = str.split(this.regExes.splitSpace); + var point; + if(this.xy) { + point = new OpenLayers.Geometry.Point( + coords[0], coords[1], coords[2] + ); + } else { + point = new OpenLayers.Geometry.Point( + coords[1], coords[0], coords[2] + ); + } + obj.points = [point]; + }, + "posList": function(node, obj) { + var str = this.getChildValue(node).replace( + this.regExes.trimSpace, "" + ); + var coords = str.split(this.regExes.splitSpace); + // The "dimension" attribute is from the GML 3.0.1 spec. + var dim = obj.srsDimension || + parseInt(node.getAttribute("srsDimension") || node.getAttribute("dimension"), 10) || 2; + var j, x, y, z; + var numPoints = coords.length / dim; + var points = new Array(numPoints); + for(var i=0, len=coords.length; i 0) { + container.components = [ + new OpenLayers.Geometry.MultiLineString(obj.components) + ]; + } + }, + "curveMember": function(node, obj) { + this.readChildNodes(node, obj); + }, + "MultiSurface": function(node, container) { + var obj = {components: []}; + this.readers.gml._inherit.apply(this, [node, obj, container]); + this.readChildNodes(node, obj); + if(obj.components.length > 0) { + container.components = [ + new OpenLayers.Geometry.MultiPolygon(obj.components) + ]; + } + }, + "surfaceMember": function(node, obj) { + this.readChildNodes(node, obj); + }, + "surfaceMembers": function(node, obj) { + this.readChildNodes(node, obj); + }, + "pointMembers": function(node, obj) { + this.readChildNodes(node, obj); + }, + "lineStringMembers": function(node, obj) { + this.readChildNodes(node, obj); + }, + "polygonMembers": function(node, obj) { + this.readChildNodes(node, obj); + }, + "geometryMembers": function(node, obj) { + this.readChildNodes(node, obj); + }, + "Envelope": function(node, container) { + var obj = {points: new Array(2)}; + this.readChildNodes(node, obj); + if(!container.components) { + container.components = []; + } + var min = obj.points[0]; + var max = obj.points[1]; + container.components.push( + new OpenLayers.Bounds(min.x, min.y, max.x, max.y) + ); + }, + "lowerCorner": function(node, container) { + var obj = {}; + this.readers.gml.pos.apply(this, [node, obj]); + container.points[0] = obj.points[0]; + }, + "upperCorner": function(node, container) { + var obj = {}; + this.readers.gml.pos.apply(this, [node, obj]); + container.points[1] = obj.points[0]; + } + }, OpenLayers.Format.GML.Base.prototype.readers["gml"]), + "feature": OpenLayers.Format.GML.Base.prototype.readers["feature"], + "wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"] + }, + + /** + * Method: write + * + * Parameters: + * features - {Array() | OpenLayers.Feature.Vector} + * An array of features or a single feature. + * + * Returns: + * {String} Given an array of features, a doc with a gml:featureMembers + * element will be returned. Given a single feature, a doc with a + * gml:featureMember element will be returned. + */ + write: function(features) { + var name; + if(OpenLayers.Util.isArray(features)) { + name = "featureMembers"; + } else { + name = "featureMember"; + } + var root = this.writeNode("gml:" + name, features); + this.setAttributeNS( + root, this.namespaces["xsi"], + "xsi:schemaLocation", this.schemaLocation + ); + + return OpenLayers.Format.XML.prototype.write.apply(this, [root]); + }, + + /** + * Property: writers + * As a compliment to the readers property, this structure contains public + * writing functions grouped by namespace alias and named like the + * node names they produce. + */ + writers: { + "gml": OpenLayers.Util.applyDefaults({ + "featureMembers": function(features) { + var node = this.createElementNSPlus("gml:featureMembers"); + for(var i=0, len=features.length; i mapping. + */ + setGeometryTypes: function() { + this.geometryTypes = { + "OpenLayers.Geometry.Point": "Point", + "OpenLayers.Geometry.MultiPoint": "MultiPoint", + "OpenLayers.Geometry.LineString": (this.curve === true) ? "Curve": "LineString", + "OpenLayers.Geometry.MultiLineString": (this.multiCurve === false) ? "MultiLineString" : "MultiCurve", + "OpenLayers.Geometry.Polygon": (this.surface === true) ? "Surface" : "Polygon", + "OpenLayers.Geometry.MultiPolygon": (this.multiSurface === false) ? "MultiPolygon" : "MultiSurface", + "OpenLayers.Geometry.Collection": "GeometryCollection" + }; + }, + + CLASS_NAME: "OpenLayers.Format.GML.v3" + +}); diff --git a/misc/openlayers/lib/OpenLayers/Format/GPX.js b/misc/openlayers/lib/OpenLayers/Format/GPX.js new file mode 100644 index 0000000..16a8056 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/GPX.js @@ -0,0 +1,385 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/XML.js + * @requires OpenLayers/Feature/Vector.js + * @requires OpenLayers/Geometry/Point.js + * @requires OpenLayers/Geometry/LineString.js + * @requires OpenLayers/Projection.js + */ + +/** + * Class: OpenLayers.Format.GPX + * Read/write GPX parser. Create a new instance with the + * constructor. + * + * Inherits from: + * - + */ +OpenLayers.Format.GPX = OpenLayers.Class(OpenLayers.Format.XML, { + + + /** + * APIProperty: defaultDesc + * {String} Default description for the waypoints/tracks in the case + * where the feature has no "description" attribute. + * Default is "No description available". + */ + defaultDesc: "No description available", + + /** + * APIProperty: extractWaypoints + * {Boolean} Extract waypoints from GPX. (default: true) + */ + extractWaypoints: true, + + /** + * APIProperty: extractTracks + * {Boolean} Extract tracks from GPX. (default: true) + */ + extractTracks: true, + + /** + * APIProperty: extractRoutes + * {Boolean} Extract routes from GPX. (default: true) + */ + extractRoutes: true, + + /** + * APIProperty: extractAttributes + * {Boolean} Extract feature attributes from GPX. (default: true) + * NOTE: Attributes as part of extensions to the GPX standard may not + * be extracted. + */ + extractAttributes: true, + + /** + * Property: namespaces + * {Object} Mapping of namespace aliases to namespace URIs. + */ + namespaces: { + gpx: "http://www.topografix.com/GPX/1/1", + xsi: "http://www.w3.org/2001/XMLSchema-instance" + }, + + /** + * Property: schemaLocation + * {String} Schema location. Defaults to + * "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" + */ + schemaLocation: "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd", + + /** + * APIProperty: creator + * {String} The creator attribute to be added to the written GPX files. + * Defaults to "OpenLayers" + */ + creator: "OpenLayers", + + /** + * Constructor: OpenLayers.Format.GPX + * Create a new parser for GPX. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + */ + initialize: function(options) { + // GPX coordinates are always in longlat WGS84 + this.externalProjection = new OpenLayers.Projection("EPSG:4326"); + + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); + }, + + /** + * APIMethod: read + * Return a list of features from a GPX doc + * + * Parameters: + * doc - {Element} + * + * Returns: + * Array({}) + */ + read: function(doc) { + if (typeof doc == "string") { + doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); + } + var features = []; + + if(this.extractTracks) { + var tracks = doc.getElementsByTagName("trk"); + for (var i=0, len=tracks.length; i} A linestring geometry + */ + extractSegment: function(segment, segmentType) { + var points = this.getElementsByTagNameNS(segment, segment.namespaceURI, segmentType); + var point_features = []; + for (var i = 0, len = points.length; i < len; i++) { + point_features.push(new OpenLayers.Geometry.Point(points[i].getAttribute("lon"), points[i].getAttribute("lat"))); + } + return new OpenLayers.Geometry.LineString(point_features); + }, + + /** + * Method: parseAttributes + * + * Parameters: + * node - {} + * + * Returns: + * {Object} An attributes object. + */ + parseAttributes: function(node) { + // node is either a wpt, trk or rte + // attributes are children of the form value + var attributes = {}; + var attrNode = node.firstChild, value, name; + while(attrNode) { + if(attrNode.nodeType == 1 && attrNode.firstChild) { + value = attrNode.firstChild; + if(value.nodeType == 3 || value.nodeType == 4) { + name = (attrNode.prefix) ? + attrNode.nodeName.split(":")[1] : + attrNode.nodeName; + if(name != "trkseg" && name != "rtept") { + attributes[name] = value.nodeValue; + } + } + } + attrNode = attrNode.nextSibling; + } + return attributes; + }, + + /** + * APIMethod: write + * Accepts Feature Collection, and returns a string. + * + * Parameters: + * features - {Array()} List of features to serialize into a string. + * metadata - {Object} A key/value pairs object to build a metadata node to + * add to the gpx. Supported keys are 'name', 'desc', 'author'. + */ + write: function(features, metadata) { + features = OpenLayers.Util.isArray(features) ? + features : [features]; + var gpx = this.createElementNS(this.namespaces.gpx, "gpx"); + gpx.setAttribute("version", "1.1"); + gpx.setAttribute("creator", this.creator); + this.setAttributes(gpx, { + "xsi:schemaLocation": this.schemaLocation + }); + + if (metadata && typeof metadata == 'object') { + gpx.appendChild(this.buildMetadataNode(metadata)); + } + for(var i=0, len=features.length; i, and builds a node for it. + * + * Parameters: + * feature - {} + * + * Returns: + * {DOMElement} - The created node, either a 'wpt' or a 'trk'. + */ + buildFeatureNode: function(feature) { + var geometry = feature.geometry; + geometry = geometry.clone(); + if (this.internalProjection && this.externalProjection) { + geometry.transform(this.internalProjection, + this.externalProjection); + } + if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { + var wpt = this.buildWptNode(geometry); + this.appendAttributesNode(wpt, feature); + return wpt; + } else { + var trkNode = this.createElementNS(this.namespaces.gpx, "trk"); + this.appendAttributesNode(trkNode, feature); + var trkSegNodes = this.buildTrkSegNode(geometry); + trkSegNodes = OpenLayers.Util.isArray(trkSegNodes) ? + trkSegNodes : [trkSegNodes]; + for (var i = 0, len = trkSegNodes.length; i < len; i++) { + trkNode.appendChild(trkSegNodes[i]); + } + return trkNode; + } + }, + + /** + * Method: buildTrkSegNode + * Builds trkseg node(s) given a geometry + * + * Parameters: + * trknode + * geometry - {} + */ + buildTrkSegNode: function(geometry) { + var node, + i, + len, + point, + nodes; + if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" || + geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { + node = this.createElementNS(this.namespaces.gpx, "trkseg"); + for (i = 0, len=geometry.components.length; i < len; i++) { + point = geometry.components[i]; + node.appendChild(this.buildTrkPtNode(point)); + } + return node; + } else { + nodes = []; + for (i = 0, len = geometry.components.length; i < len; i++) { + nodes.push(this.buildTrkSegNode(geometry.components[i])); + } + return nodes; + } + }, + + /** + * Method: buildTrkPtNode + * Builds a trkpt node given a point + * + * Parameters: + * point - {} + * + * Returns: + * {DOMElement} A trkpt node + */ + buildTrkPtNode: function(point) { + var node = this.createElementNS(this.namespaces.gpx, "trkpt"); + node.setAttribute("lon", point.x); + node.setAttribute("lat", point.y); + return node; + }, + + /** + * Method: buildWptNode + * Builds a wpt node given a point + * + * Parameters: + * geometry - {} + * + * Returns: + * {DOMElement} A wpt node + */ + buildWptNode: function(geometry) { + var node = this.createElementNS(this.namespaces.gpx, "wpt"); + node.setAttribute("lon", geometry.x); + node.setAttribute("lat", geometry.y); + return node; + }, + + /** + * Method: appendAttributesNode + * Adds some attributes node. + * + * Parameters: + * node - {DOMElement} the node to append the attribute nodes to. + * feature - {} + */ + appendAttributesNode: function(node, feature) { + var name = this.createElementNS(this.namespaces.gpx, 'name'); + name.appendChild(this.createTextNode( + feature.attributes.name || feature.id)); + node.appendChild(name); + var desc = this.createElementNS(this.namespaces.gpx, 'desc'); + desc.appendChild(this.createTextNode( + feature.attributes.description || this.defaultDesc)); + node.appendChild(desc); + // TBD - deal with remaining (non name/description) attributes. + }, + + CLASS_NAME: "OpenLayers.Format.GPX" +}); diff --git a/misc/openlayers/lib/OpenLayers/Format/GeoJSON.js b/misc/openlayers/lib/OpenLayers/Format/GeoJSON.js new file mode 100644 index 0000000..0e02377 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/GeoJSON.js @@ -0,0 +1,716 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/JSON.js + * @requires OpenLayers/Feature/Vector.js + * @requires OpenLayers/Geometry/Point.js + * @requires OpenLayers/Geometry/MultiPoint.js + * @requires OpenLayers/Geometry/LineString.js + * @requires OpenLayers/Geometry/MultiLineString.js + * @requires OpenLayers/Geometry/Polygon.js + * @requires OpenLayers/Geometry/MultiPolygon.js + * @requires OpenLayers/Console.js + */ + +/** + * Class: OpenLayers.Format.GeoJSON + * Read and write GeoJSON. Create a new parser with the + * constructor. + * + * Inherits from: + * - + */ +OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, { + + /** + * APIProperty: ignoreExtraDims + * {Boolean} Ignore dimensions higher than 2 when reading geometry + * coordinates. + */ + ignoreExtraDims: false, + + /** + * Constructor: OpenLayers.Format.GeoJSON + * Create a new parser for GeoJSON. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + */ + + /** + * APIMethod: read + * Deserialize a GeoJSON string. + * + * Parameters: + * json - {String} A GeoJSON string + * type - {String} Optional string that determines the structure of + * the output. Supported values are "Geometry", "Feature", and + * "FeatureCollection". If absent or null, a default of + * "FeatureCollection" is assumed. + * filter - {Function} A function which will be called for every key and + * value at every level of the final result. Each value will be + * replaced by the result of the filter function. This can be used to + * reform generic objects into instances of classes, or to transform + * date strings into Date objects. + * + * Returns: + * {Object} The return depends on the value of the type argument. If type + * is "FeatureCollection" (the default), the return will be an array + * of . If type is "Geometry", the input json + * must represent a single geometry, and the return will be an + * . If type is "Feature", the input json must + * represent a single feature, and the return will be an + * . + */ + read: function(json, type, filter) { + type = (type) ? type : "FeatureCollection"; + var results = null; + var obj = null; + if (typeof json == "string") { + obj = OpenLayers.Format.JSON.prototype.read.apply(this, + [json, filter]); + } else { + obj = json; + } + if(!obj) { + OpenLayers.Console.error("Bad JSON: " + json); + } else if(typeof(obj.type) != "string") { + OpenLayers.Console.error("Bad GeoJSON - no type: " + json); + } else if(this.isValidType(obj, type)) { + switch(type) { + case "Geometry": + try { + results = this.parseGeometry(obj); + } catch(err) { + OpenLayers.Console.error(err); + } + break; + case "Feature": + try { + results = this.parseFeature(obj); + results.type = "Feature"; + } catch(err) { + OpenLayers.Console.error(err); + } + break; + case "FeatureCollection": + // for type FeatureCollection, we allow input to be any type + results = []; + switch(obj.type) { + case "Feature": + try { + results.push(this.parseFeature(obj)); + } catch(err) { + results = null; + OpenLayers.Console.error(err); + } + break; + case "FeatureCollection": + for(var i=0, len=obj.features.length; i. + * + * Parameters: + * obj - {Object} An object created from a GeoJSON object + * + * Returns: + * {} A feature. + */ + parseFeature: function(obj) { + var feature, geometry, attributes, bbox; + attributes = (obj.properties) ? obj.properties : {}; + bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox; + try { + geometry = this.parseGeometry(obj.geometry); + } catch(err) { + // deal with bad geometries + throw err; + } + feature = new OpenLayers.Feature.Vector(geometry, attributes); + if(bbox) { + feature.bounds = OpenLayers.Bounds.fromArray(bbox); + } + if(obj.id) { + feature.fid = obj.id; + } + return feature; + }, + + /** + * Method: parseGeometry + * Convert a geometry object from GeoJSON into an . + * + * Parameters: + * obj - {Object} An object created from a GeoJSON object + * + * Returns: + * {} A geometry. + */ + parseGeometry: function(obj) { + if (obj == null) { + return null; + } + var geometry, collection = false; + if(obj.type == "GeometryCollection") { + if(!(OpenLayers.Util.isArray(obj.geometries))) { + throw "GeometryCollection must have geometries array: " + obj; + } + var numGeom = obj.geometries.length; + var components = new Array(numGeom); + for(var i=0; i. + * + * Parameters: + * array - {Object} The coordinates array from the GeoJSON fragment. + * + * Returns: + * {} A geometry. + */ + "point": function(array) { + if (this.ignoreExtraDims == false && + array.length != 2) { + throw "Only 2D points are supported: " + array; + } + return new OpenLayers.Geometry.Point(array[0], array[1]); + }, + + /** + * Method: parseCoords.multipoint + * Convert a coordinate array from GeoJSON into an + * . + * + * Parameters: + * array - {Object} The coordinates array from the GeoJSON fragment. + * + * Returns: + * {} A geometry. + */ + "multipoint": function(array) { + var points = []; + var p = null; + for(var i=0, len=array.length; i. + * + * Parameters: + * array - {Object} The coordinates array from the GeoJSON fragment. + * + * Returns: + * {} A geometry. + */ + "linestring": function(array) { + var points = []; + var p = null; + for(var i=0, len=array.length; i. + * + * Parameters: + * array - {Object} The coordinates array from the GeoJSON fragment. + * + * Returns: + * {} A geometry. + */ + "multilinestring": function(array) { + var lines = []; + var l = null; + for(var i=0, len=array.length; i. + * + * Returns: + * {} A geometry. + */ + "polygon": function(array) { + var rings = []; + var r, l; + for(var i=0, len=array.length; i. + * + * Parameters: + * array - {Object} The coordinates array from the GeoJSON fragment. + * + * Returns: + * {} A geometry. + */ + "multipolygon": function(array) { + var polys = []; + var p = null; + for(var i=0, len=array.length; i. + * + * Parameters: + * array - {Object} The coordinates array from the GeoJSON fragment. + * + * Returns: + * {} A geometry. + */ + "box": function(array) { + if(array.length != 2) { + throw "GeoJSON box coordinates must have 2 elements"; + } + return new OpenLayers.Geometry.Polygon([ + new OpenLayers.Geometry.LinearRing([ + new OpenLayers.Geometry.Point(array[0][0], array[0][1]), + new OpenLayers.Geometry.Point(array[1][0], array[0][1]), + new OpenLayers.Geometry.Point(array[1][0], array[1][1]), + new OpenLayers.Geometry.Point(array[0][0], array[1][1]), + new OpenLayers.Geometry.Point(array[0][0], array[0][1]) + ]) + ]); + } + + }, + + /** + * APIMethod: write + * Serialize a feature, geometry, array of features into a GeoJSON string. + * + * Parameters: + * obj - {Object} An , , + * or an array of features. + * pretty - {Boolean} Structure the output with newlines and indentation. + * Default is false. + * + * Returns: + * {String} The GeoJSON string representation of the input geometry, + * features, or array of features. + */ + write: function(obj, pretty) { + var geojson = { + "type": null + }; + if(OpenLayers.Util.isArray(obj)) { + geojson.type = "FeatureCollection"; + var numFeatures = obj.length; + geojson.features = new Array(numFeatures); + for(var i=0; i} + * + * Returns: + * {Object} An object which can be assigned to the crs property + * of a GeoJSON object. + */ + createCRSObject: function(object) { + var proj = object.layer.projection.toString(); + var crs = {}; + if (proj.match(/epsg:/i)) { + var code = parseInt(proj.substring(proj.indexOf(":") + 1)); + if (code == 4326) { + crs = { + "type": "name", + "properties": { + "name": "urn:ogc:def:crs:OGC:1.3:CRS84" + } + }; + } else { + crs = { + "type": "name", + "properties": { + "name": "EPSG:" + code + } + }; + } + } + return crs; + }, + + /** + * Property: extract + * Object with properties corresponding to the GeoJSON types. + * Property values are functions that do the actual value extraction. + */ + extract: { + /** + * Method: extract.feature + * Return a partial GeoJSON object representing a single feature. + * + * Parameters: + * feature - {} + * + * Returns: + * {Object} An object representing the point. + */ + 'feature': function(feature) { + var geom = this.extract.geometry.apply(this, [feature.geometry]); + var json = { + "type": "Feature", + "properties": feature.attributes, + "geometry": geom + }; + if (feature.fid != null) { + json.id = feature.fid; + } + return json; + }, + + /** + * Method: extract.geometry + * Return a GeoJSON object representing a single geometry. + * + * Parameters: + * geometry - {} + * + * Returns: + * {Object} An object representing the geometry. + */ + 'geometry': function(geometry) { + if (geometry == null) { + return null; + } + if (this.internalProjection && this.externalProjection) { + geometry = geometry.clone(); + geometry.transform(this.internalProjection, + this.externalProjection); + } + var geometryType = geometry.CLASS_NAME.split('.')[2]; + var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]); + var json; + if(geometryType == "Collection") { + json = { + "type": "GeometryCollection", + "geometries": data + }; + } else { + json = { + "type": geometryType, + "coordinates": data + }; + } + + return json; + }, + + /** + * Method: extract.point + * Return an array of coordinates from a point. + * + * Parameters: + * point - {} + * + * Returns: + * {Array} An array of coordinates representing the point. + */ + 'point': function(point) { + return [point.x, point.y]; + }, + + /** + * Method: extract.multipoint + * Return an array of point coordinates from a multipoint. + * + * Parameters: + * multipoint - {} + * + * Returns: + * {Array} An array of point coordinate arrays representing + * the multipoint. + */ + 'multipoint': function(multipoint) { + var array = []; + for(var i=0, len=multipoint.components.length; i} + * + * Returns: + * {Array} An array of coordinate arrays representing + * the linestring. + */ + 'linestring': function(linestring) { + var array = []; + for(var i=0, len=linestring.components.length; i} + * + * Returns: + * {Array} An array of linestring arrays representing + * the multilinestring. + */ + 'multilinestring': function(multilinestring) { + var array = []; + for(var i=0, len=multilinestring.components.length; i} + * + * Returns: + * {Array} An array of linear ring arrays representing the polygon. + */ + 'polygon': function(polygon) { + var array = []; + for(var i=0, len=polygon.components.length; i} + * + * Returns: + * {Array} An array of polygon arrays representing + * the multipolygon + */ + 'multipolygon': function(multipolygon) { + var array = []; + for(var i=0, len=multipolygon.components.length; i} + * + * Returns: + * {Array} An array of geometry objects representing the geometry + * collection. + */ + 'collection': function(collection) { + var len = collection.components.length; + var array = new Array(len); + for(var i=0; i constructor. + * + * Inherits from: + * - + */ +OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format.XML, { + + /** + * APIProperty: rssns + * {String} RSS namespace to use. Defaults to + * "http://backend.userland.com/rss2" + */ + rssns: "http://backend.userland.com/rss2", + + /** + * APIProperty: featurens + * {String} Feature Attributes namespace. Defaults to + * "http://mapserver.gis.umn.edu/mapserver" + */ + featureNS: "http://mapserver.gis.umn.edu/mapserver", + + /** + * APIProperty: georssns + * {String} GeoRSS namespace to use. Defaults to + * "http://www.georss.org/georss" + */ + georssns: "http://www.georss.org/georss", + + /** + * APIProperty: geons + * {String} W3C Geo namespace to use. Defaults to + * "http://www.w3.org/2003/01/geo/wgs84_pos#" + */ + geons: "http://www.w3.org/2003/01/geo/wgs84_pos#", + + /** + * APIProperty: featureTitle + * {String} Default title for features. Defaults to "Untitled" + */ + featureTitle: "Untitled", + + /** + * APIProperty: featureDescription + * {String} Default description for features. Defaults to "No Description" + */ + featureDescription: "No Description", + + /** + * Property: gmlParse + * {Object} GML Format object for parsing features + * Non-API and only created if necessary + */ + gmlParser: null, + + /** + * APIProperty: xy + * {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x) + * For GeoRSS the default is (y,x), therefore: false + */ + xy: false, + + /** + * Constructor: OpenLayers.Format.GeoRSS + * Create a new parser for GeoRSS. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + */ + + /** + * Method: createGeometryFromItem + * Return a geometry from a GeoRSS Item. + * + * Parameters: + * item - {DOMElement} A GeoRSS item node. + * + * Returns: + * {} A geometry representing the node. + */ + createGeometryFromItem: function(item) { + var point = this.getElementsByTagNameNS(item, this.georssns, "point"); + var lat = this.getElementsByTagNameNS(item, this.geons, 'lat'); + var lon = this.getElementsByTagNameNS(item, this.geons, 'long'); + + var line = this.getElementsByTagNameNS(item, + this.georssns, + "line"); + var polygon = this.getElementsByTagNameNS(item, + this.georssns, + "polygon"); + var where = this.getElementsByTagNameNS(item, + this.georssns, + "where"); + var box = this.getElementsByTagNameNS(item, + this.georssns, + "box"); + + if (point.length > 0 || (lat.length > 0 && lon.length > 0)) { + var location; + if (point.length > 0) { + location = OpenLayers.String.trim( + point[0].firstChild.nodeValue).split(/\s+/); + if (location.length !=2) { + location = OpenLayers.String.trim( + point[0].firstChild.nodeValue).split(/\s*,\s*/); + } + } else { + location = [parseFloat(lat[0].firstChild.nodeValue), + parseFloat(lon[0].firstChild.nodeValue)]; + } + + var geometry = new OpenLayers.Geometry.Point(location[1], location[0]); + + } else if (line.length > 0) { + var coords = OpenLayers.String.trim(this.getChildValue(line[0])).split(/\s+/); + var components = []; + var point; + for (var i=0, len=coords.length; i 0) { + var coords = OpenLayers.String.trim(this.getChildValue(polygon[0])).split(/\s+/); + var components = []; + var point; + for (var i=0, len=coords.length; i 0) { + if (!this.gmlParser) { + this.gmlParser = new OpenLayers.Format.GML({'xy': this.xy}); + } + var feature = this.gmlParser.parseFeature(where[0]); + geometry = feature.geometry; + } else if (box.length > 0) { + var coords = OpenLayers.String.trim(box[0].firstChild.nodeValue).split(/\s+/); + var components = []; + var point; + if (coords.length > 3) { + point = new OpenLayers.Geometry.Point(coords[1], coords[0]); + components.push(point); + point = new OpenLayers.Geometry.Point(coords[1], coords[2]); + components.push(point); + point = new OpenLayers.Geometry.Point(coords[3], coords[2]); + components.push(point); + point = new OpenLayers.Geometry.Point(coords[3], coords[0]); + components.push(point); + point = new OpenLayers.Geometry.Point(coords[1], coords[0]); + components.push(point); + } + geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]); + } + + if (geometry && this.internalProjection && this.externalProjection) { + geometry.transform(this.externalProjection, + this.internalProjection); + } + + return geometry; + }, + + /** + * Method: createFeatureFromItem + * Return a feature from a GeoRSS Item. + * + * Parameters: + * item - {DOMElement} A GeoRSS item node. + * + * Returns: + * {} A feature representing the item. + */ + createFeatureFromItem: function(item) { + var geometry = this.createGeometryFromItem(item); + + /* Provide defaults for title and description */ + var title = this._getChildValue(item, "*", "title", this.featureTitle); + + /* First try RSS descriptions, then Atom summaries */ + var description = this._getChildValue( + item, "*", "description", + this._getChildValue(item, "*", "content", + this._getChildValue(item, "*", "summary", this.featureDescription))); + + /* If no link URL is found in the first child node, try the + href attribute */ + var link = this._getChildValue(item, "*", "link"); + if(!link) { + try { + link = this.getElementsByTagNameNS(item, "*", "link")[0].getAttribute("href"); + } catch(e) { + link = null; + } + } + + var id = this._getChildValue(item, "*", "id", null); + + var data = { + "title": title, + "description": description, + "link": link + }; + var feature = new OpenLayers.Feature.Vector(geometry, data); + feature.fid = id; + return feature; + }, + + /** + * Method: _getChildValue + * + * Parameters: + * node - {DOMElement} + * nsuri - {String} Child node namespace uri ("*" for any). + * name - {String} Child node name. + * def - {String} Optional string default to return if no child found. + * + * Returns: + * {String} The value of the first child with the given tag name. Returns + * default value or empty string if none found. + */ + _getChildValue: function(node, nsuri, name, def) { + var value; + var eles = this.getElementsByTagNameNS(node, nsuri, name); + if(eles && eles[0] && eles[0].firstChild + && eles[0].firstChild.nodeValue) { + value = this.getChildValue(eles[0]); + } else { + value = (def == undefined) ? "" : def; + } + return value; + }, + + /** + * APIMethod: read + * Return a list of features from a GeoRSS doc + * + * Parameters: + * doc - {Element} + * + * Returns: + * {Array()} + */ + read: function(doc) { + if (typeof doc == "string") { + doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); + } + + /* Try RSS items first, then Atom entries */ + var itemlist = null; + itemlist = this.getElementsByTagNameNS(doc, '*', 'item'); + if (itemlist.length == 0) { + itemlist = this.getElementsByTagNameNS(doc, '*', 'entry'); + } + + var numItems = itemlist.length; + var features = new Array(numItems); + for(var i=0; i)} List of features to serialize into a string. + */ + write: function(features) { + var georss; + if(OpenLayers.Util.isArray(features)) { + georss = this.createElementNS(this.rssns, "rss"); + for(var i=0, len=features.length; i, and build a geometry for it. + * + * Parameters: + * feature - {} + * + * Returns: + * {DOMElement} + */ + createFeatureXML: function(feature) { + var geometryNode = this.buildGeometryNode(feature.geometry); + var featureNode = this.createElementNS(this.rssns, "item"); + var titleNode = this.createElementNS(this.rssns, "title"); + titleNode.appendChild(this.createTextNode(feature.attributes.title ? feature.attributes.title : "")); + var descNode = this.createElementNS(this.rssns, "description"); + descNode.appendChild(this.createTextNode(feature.attributes.description ? feature.attributes.description : "")); + featureNode.appendChild(titleNode); + featureNode.appendChild(descNode); + if (feature.attributes.link) { + var linkNode = this.createElementNS(this.rssns, "link"); + linkNode.appendChild(this.createTextNode(feature.attributes.link)); + featureNode.appendChild(linkNode); + } + for(var attr in feature.attributes) { + if (attr == "link" || attr == "title" || attr == "description") { continue; } + var attrText = this.createTextNode(feature.attributes[attr]); + var nodename = attr; + if (attr.search(":") != -1) { + nodename = attr.split(":")[1]; + } + var attrContainer = this.createElementNS(this.featureNS, "feature:"+nodename); + attrContainer.appendChild(attrText); + featureNode.appendChild(attrContainer); + } + featureNode.appendChild(geometryNode); + return featureNode; + }, + + /** + * Method: buildGeometryNode + * builds a GeoRSS node with a given geometry + * + * Parameters: + * geometry - {} + * + * Returns: + * {DOMElement} A gml node. + */ + buildGeometryNode: function(geometry) { + if (this.internalProjection && this.externalProjection) { + geometry = geometry.clone(); + geometry.transform(this.internalProjection, + this.externalProjection); + } + var node; + // match Polygon + if (geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") { + node = this.createElementNS(this.georssns, 'georss:polygon'); + + node.appendChild(this.buildCoordinatesNode(geometry.components[0])); + } + // match LineString + else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") { + node = this.createElementNS(this.georssns, 'georss:line'); + + node.appendChild(this.buildCoordinatesNode(geometry)); + } + // match Point + else if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { + node = this.createElementNS(this.georssns, 'georss:point'); + node.appendChild(this.buildCoordinatesNode(geometry)); + } else { + throw "Couldn't parse " + geometry.CLASS_NAME; + } + return node; + }, + + /** + * Method: buildCoordinatesNode + * + * Parameters: + * geometry - {} + */ + buildCoordinatesNode: function(geometry) { + var points = null; + + if (geometry.components) { + points = geometry.components; + } + + var path; + if (points) { + var numPoints = points.length; + var parts = new Array(numPoints); + for (var i = 0; i < numPoints; i++) { + parts[i] = points[i].y + " " + points[i].x; + } + path = parts.join(" "); + } else { + path = geometry.y + " " + geometry.x; + } + return this.createTextNode(path); + }, + + CLASS_NAME: "OpenLayers.Format.GeoRSS" +}); diff --git a/misc/openlayers/lib/OpenLayers/Format/JSON.js b/misc/openlayers/lib/OpenLayers/Format/JSON.js new file mode 100644 index 0000000..5b25e6a --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/JSON.js @@ -0,0 +1,398 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * Note: + * This work draws heavily from the public domain JSON serializer/deserializer + * at http://www.json.org/json.js. Rewritten so that it doesn't modify + * basic data prototypes. + */ + +/** + * @requires OpenLayers/Format.js + */ + +/** + * Class: OpenLayers.Format.JSON + * A parser to read/write JSON safely. Create a new instance with the + * constructor. + * + * Inherits from: + * - + */ +OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, { + + /** + * APIProperty: indent + * {String} For "pretty" printing, the indent string will be used once for + * each indentation level. + */ + indent: " ", + + /** + * APIProperty: space + * {String} For "pretty" printing, the space string will be used after + * the ":" separating a name/value pair. + */ + space: " ", + + /** + * APIProperty: newline + * {String} For "pretty" printing, the newline string will be used at the + * end of each name/value pair or array item. + */ + newline: "\n", + + /** + * Property: level + * {Integer} For "pretty" printing, this is incremented/decremented during + * serialization. + */ + level: 0, + + /** + * Property: pretty + * {Boolean} Serialize with extra whitespace for structure. This is set + * by the method. + */ + pretty: false, + + /** + * Property: nativeJSON + * {Boolean} Does the browser support native json? + */ + nativeJSON: (function() { + return !!(window.JSON && typeof JSON.parse == "function" && typeof JSON.stringify == "function"); + })(), + + /** + * Constructor: OpenLayers.Format.JSON + * Create a new parser for JSON. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + */ + + /** + * APIMethod: read + * Deserialize a json string. + * + * Parameters: + * json - {String} A JSON string + * filter - {Function} A function which will be called for every key and + * value at every level of the final result. Each value will be + * replaced by the result of the filter function. This can be used to + * reform generic objects into instances of classes, or to transform + * date strings into Date objects. + * + * Returns: + * {Object} An object, array, string, or number . + */ + read: function(json, filter) { + var object; + if (this.nativeJSON) { + object = JSON.parse(json, filter); + } else try { + /** + * Parsing happens in three stages. In the first stage, we run the + * text against a regular expression which looks for non-JSON + * characters. We are especially concerned with '()' and 'new' + * because they can cause invocation, and '=' because it can + * cause mutation. But just to be safe, we will reject all + * unexpected characters. + */ + if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@'). + replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). + replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + + /** + * In the second stage we use the eval function to compile the + * text into a JavaScript structure. The '{' operator is + * subject to a syntactic ambiguity in JavaScript - it can + * begin a block or an object literal. We wrap the text in + * parens to eliminate the ambiguity. + */ + object = eval('(' + json + ')'); + + /** + * In the optional third stage, we recursively walk the new + * structure, passing each name/value pair to a filter + * function for possible transformation. + */ + if(typeof filter === 'function') { + function walk(k, v) { + if(v && typeof v === 'object') { + for(var i in v) { + if(v.hasOwnProperty(i)) { + v[i] = walk(i, v[i]); + } + } + } + return filter(k, v); + } + object = walk('', object); + } + } + } catch(e) { + // Fall through if the regexp test fails. + } + + if(this.keepData) { + this.data = object; + } + + return object; + }, + + /** + * APIMethod: write + * Serialize an object into a JSON string. + * + * Parameters: + * value - {String} The object, array, string, number, boolean or date + * to be serialized. + * pretty - {Boolean} Structure the output with newlines and indentation. + * Default is false. + * + * Returns: + * {String} The JSON string representation of the input value. + */ + write: function(value, pretty) { + this.pretty = !!pretty; + var json = null; + var type = typeof value; + if(this.serialize[type]) { + try { + json = (!this.pretty && this.nativeJSON) ? + JSON.stringify(value) : + this.serialize[type].apply(this, [value]); + } catch(err) { + OpenLayers.Console.error("Trouble serializing: " + err); + } + } + return json; + }, + + /** + * Method: writeIndent + * Output an indentation string depending on the indentation level. + * + * Returns: + * {String} An appropriate indentation string. + */ + writeIndent: function() { + var pieces = []; + if(this.pretty) { + for(var i=0; i 0) { + pieces.push(','); + } + pieces.push(this.writeNewline(), this.writeIndent(), json); + } + } + + this.level -= 1; + pieces.push(this.writeNewline(), this.writeIndent(), ']'); + return pieces.join(''); + }, + + /** + * Method: serialize.string + * Transform a string into a JSON string. + * + * Parameters: + * string - {String} The string to be serialized + * + * Returns: + * {String} A JSON string representing the string. + */ + 'string': function(string) { + // If the string contains no control characters, no quote characters, and no + // backslash characters, then we can simply slap some quotes around it. + // Otherwise we must also replace the offending characters with safe + // sequences. + var m = { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }; + if(/["\\\x00-\x1f]/.test(string)) { + return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) { + var c = m[b]; + if(c) { + return c; + } + c = b.charCodeAt(); + return '\\u00' + + Math.floor(c / 16).toString(16) + + (c % 16).toString(16); + }) + '"'; + } + return '"' + string + '"'; + }, + + /** + * Method: serialize.number + * Transform a number into a JSON string. + * + * Parameters: + * number - {Number} The number to be serialized. + * + * Returns: + * {String} A JSON string representing the number. + */ + 'number': function(number) { + return isFinite(number) ? String(number) : "null"; + }, + + /** + * Method: serialize.boolean + * Transform a boolean into a JSON string. + * + * Parameters: + * bool - {Boolean} The boolean to be serialized. + * + * Returns: + * {String} A JSON string representing the boolean. + */ + 'boolean': function(bool) { + return String(bool); + }, + + /** + * Method: serialize.object + * Transform a date into a JSON string. + * + * Parameters: + * date - {Date} The date to be serialized. + * + * Returns: + * {String} A JSON string representing the date. + */ + 'date': function(date) { + function format(number) { + // Format integers to have at least two digits. + return (number < 10) ? '0' + number : number; + } + return '"' + date.getFullYear() + '-' + + format(date.getMonth() + 1) + '-' + + format(date.getDate()) + 'T' + + format(date.getHours()) + ':' + + format(date.getMinutes()) + ':' + + format(date.getSeconds()) + '"'; + } + }, + + CLASS_NAME: "OpenLayers.Format.JSON" + +}); diff --git a/misc/openlayers/lib/OpenLayers/Format/KML.js b/misc/openlayers/lib/OpenLayers/Format/KML.js new file mode 100644 index 0000000..e10bce7 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Format/KML.js @@ -0,0 +1,1517 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/BaseTypes/Date.js + * @requires OpenLayers/Format/XML.js + * @requires OpenLayers/Feature/Vector.js + * @requires OpenLayers/Geometry/Point.js + * @requires OpenLayers/Geometry/LineString.js + * @requires OpenLayers/Geometry/Polygon.js + * @requires OpenLayers/Geometry/Collection.js + * @requires OpenLayers/Request/XMLHttpRequest.js + * @requires OpenLayers/Projection.js + */ + +/** + * Class: OpenLayers.Format.KML + * Read/Write KML. Create a new instance with the + * constructor. + * + * Inherits from: + * - + */ +OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, { + + /** + * Property: namespaces + * {Object} Mapping of namespace aliases to namespace URIs. + */ + namespaces: { + kml: "http://www.opengis.net/kml/2.2", + gx: "http://www.google.com/kml/ext/2.2" + }, + + /** + * APIProperty: kmlns + * {String} KML Namespace to use. Defaults to 2.0 namespace. + */ + kmlns: "http://earth.google.com/kml/2.0", + + /** + * APIProperty: placemarksDesc + * {String} Name of the placemarks. Default is "No description available". + */ + placemarksDesc: "No description available", + + /** + * APIProperty: foldersName + * {String} Name of the folders. Default is "OpenLayers export". + * If set to null, no name element will be created. + */ + foldersName: "OpenLayers export", + + /** + * APIProperty: foldersDesc + * {String} Description of the folders. Default is "Exported on [date]." + * If set to null, no description element will be created. + */ + foldersDesc: "Exported on " + new Date(), + + /** + * APIProperty: extractAttributes + * {Boolean} Extract attributes from KML. Default is true. + * Extracting styleUrls requires this to be set to true + * Note that currently only Data and SimpleData + * elements are handled. + */ + extractAttributes: true, + + /** + * APIProperty: kvpAttributes + * {Boolean} Only used if extractAttributes is true. + * If set to true, attributes will be simple + * key-value pairs, compatible with other formats, + * Any displayName elements will be ignored. + * If set to false, attributes will be objects, + * retaining any displayName elements, but not + * compatible with other formats. Any CDATA in + * displayName will be read in as a string value. + * Default is false. + */ + kvpAttributes: false, + + /** + * Property: extractStyles + * {Boolean} Extract styles from KML. Default is false. + * Extracting styleUrls also requires extractAttributes to be + * set to true + */ + extractStyles: false, + + /** + * APIProperty: extractTracks + * {Boolean} Extract gx:Track elements from Placemark elements. Default + * is false. If true, features will be generated for all points in + * all gx:Track elements. Features will have a when (Date) attribute + * based on when elements in the track. If tracks include angle + * elements, features will have heading, tilt, and roll attributes. + * If track point coordinates have three values, features will have + * an altitude attribute with the third coordinate value. + */ + extractTracks: false, + + /** + * APIProperty: trackAttributes + * {Array} If is true, points within gx:Track elements will + * be parsed as features with when, heading, tilt, and roll attributes. + * Any additional attribute names can be provided in . + */ + trackAttributes: null, + + /** + * Property: internalns + * {String} KML Namespace to use -- defaults to the namespace of the + * Placemark node being parsed, but falls back to kmlns. + */ + internalns: null, + + /** + * Property: features + * {Array} Array of features + * + */ + features: null, + + /** + * Property: styles + * {Object} Storage of style objects + * + */ + styles: null, + + /** + * Property: styleBaseUrl + * {String} + */ + styleBaseUrl: "", + + /** + * Property: fetched + * {Object} Storage of KML URLs that have been fetched before + * in order to prevent reloading them. + */ + fetched: null, + + /** + * APIProperty: maxDepth + * {Integer} Maximum depth for recursive loading external KML URLs + * Defaults to 0: do no external fetching + */ + maxDepth: 0, + + /** + * Constructor: OpenLayers.Format.KML + * Create a new parser for KML. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + */ + initialize: function(options) { + // compile regular expressions once instead of every time they are used + this.regExes = { + trimSpace: (/^\s*|\s*$/g), + removeSpace: (/\s*/g), + splitSpace: (/\s+/), + trimComma: (/\s*,\s*/g), + kmlColor: (/(\w{2})(\w{2})(\w{2})(\w{2})/), + kmlIconPalette: (/root:\/\/icons\/palette-(\d+)(\.\w+)/), + straightBracket: (/\$\[(.*?)\]/g) + }; + // KML coordinates are always in longlat WGS84 + this.externalProjection = new OpenLayers.Projection("EPSG:4326"); + + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); + }, + + /** + * APIMethod: read + * Read data from a string, and return a list of features. + * + * Parameters: + * data - {String} or {DOMElement} data to read/parse. + * + * Returns: + * {Array()} List of features. + */ + read: function(data) { + this.features = []; + this.styles = {}; + this.fetched = {}; + + // Set default options + var options = { + depth: 0, + styleBaseUrl: this.styleBaseUrl + }; + + return this.parseData(data, options); + }, + + /** + * Method: parseData + * Read data from a string, and return a list of features. + * + * Parameters: + * data - {String} or {DOMElement} data to read/parse. + * options - {Object} Hash of options + * + * Returns: + * {Array()} List of features. + */ + parseData: function(data, options) { + if(typeof data == "string") { + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); + } + + // Loop throught the following node types in this order and + // process the nodes found + var types = ["Link", "NetworkLink", "Style", "StyleMap", "Placemark"]; + for(var i=0, len=types.length; i and + // Don't do anything if we have reached our maximum depth for recursion + if (options.depth >= this.maxDepth) { + return false; + } + + // increase depth + var newOptions = OpenLayers.Util.extend({}, options); + newOptions.depth++; + + for(var i=0, len=nodes.length; i nodes + * + * Parameters: + * nodes - {Array} of {DOMElement} data to read/parse. + * options - {Object} Hash of options + * + */ + parseStyles: function(nodes, options) { + for(var i=0, len=nodes.length; i node and builds the style hash + * accordingly + * + * Parameters: + * node - {DOMElement}