From 7da6a3540e924f113d16475502dd1bb0244920d7 Mon Sep 17 00:00:00 2001 From: crschmidt Date: Sat, 8 Dec 2007 14:21:53 +0000 Subject: [PATCH] Merge the excellent documentation work done during foss4g into trunk. Many thanks to all the contributors who helped put this together. I'm not exactly sure of what's going to happen with this, but for now, at http://openlayers.org/dev/doc/examples.html you can see links to all the examples *with descriptions*. Hooray! git-svn-id: http://svn.openlayers.org/trunk/openlayers@5362 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf --- doc/Jugl.js | 74 + doc/examples.html | 55 + examples/GMLParser.html | 21 +- examples/KMLParser.html | 28 +- examples/accessible.html | 79 +- examples/attribution.html | 74 +- examples/baseLayers.html | 162 ++- examples/boxes.html | 91 +- examples/buffer.html | 89 +- examples/click.html | 78 +- examples/controls.html | 97 +- examples/custom-control-point.html | 16 +- examples/custom-control.html | 100 +- examples/custom-style.html | 114 +- examples/debug.html | 131 +- examples/doubleSetCenter.html | 47 +- examples/drag-feature.html | 187 +-- examples/draw-feature.html | 188 +-- examples/editingtoolbar.html | 82 +- examples/example.html | 35 +- examples/fullScreen.html | 87 +- examples/fullScreen2.html | 51 + examples/fullscreen.jpg | Bin 0 -> 104464 bytes examples/georss-markers.html | 18 +- examples/georss-serialize.html | 29 +- examples/georss.html | 40 +- examples/georss.xml | 2 +- examples/getfeatureinfo.html | 51 +- examples/gml-layer.html | 17 +- examples/gml-serialize.html | 63 +- examples/google.html | 35 +- examples/gutter.html | 102 +- examples/image-layer.html | 35 +- examples/kamap.html | 16 +- examples/kml-layer.html | 16 +- examples/layer-opacity.html | 37 +- examples/layerLoadMonitoring.html | 87 +- examples/layerswitcher.html | 19 +- examples/lite.html | 19 +- examples/mapserver.html | 7 + examples/mapserver_untiled.html | 7 + examples/markerResize.html | 10 +- examples/markers.html | 10 +- examples/markersTextLayer.html | 7 +- examples/mm.html | 6 +- examples/modify-feature.html | 3 +- examples/mouse-position.html | 6 + examples/multimap-mercator.html | 8 +- examples/multiserver.html | 12 + examples/mvs.html | 2 +- examples/navtoolbar.html | 8 +- examples/notile.html | 6 +- examples/openmnnd.html | 102 +- examples/outOfRangeMarkers.html | 7 +- examples/overviewmap.html | 9 +- examples/panel.html | 5 + examples/popups.html | 13 +- examples/projected-map.html | 7 + examples/regular-polygons.html | 6 +- examples/resize-features.html | 5 + examples/restricted-extent.html | 7 +- examples/rotate-features.html | 12 +- examples/select-feature-openpopup.html | 6 +- examples/select-feature.html | 8 +- examples/setextent.html | 5 +- examples/spherical-mercator.html | 36 +- examples/supports-vector.html | 22 - examples/tilecache.html | 29 +- examples/tms.html | 38 +- examples/urban.html | 24 +- examples/ve.html | 39 +- examples/vector-features.html | 45 +- examples/vector-formats.html | 38 +- examples/wfs-scribble.html | 79 +- examples/wfs-states.html | 37 +- examples/wfs-t.html | 70 +- examples/wfs.html | 6 +- examples/wkt.html | 31 +- examples/wms-untiled.html | 20 +- examples/wms.html | 25 +- examples/wmst.html | 35 +- examples/worldwind.html | 32 +- examples/wrapDateLine.html | 32 +- examples/xhtml.html | 27 +- examples/xml.html | 94 +- examples/yahoo.html | 24 +- examples/yelp-georss.xml | 2 +- examples/zoomLevels.html | 48 +- tools/BeautifulSoup.py | 1767 ++++++++++++++++++++++++ tools/exampleparser.py | 124 ++ 90 files changed, 4187 insertions(+), 1293 deletions(-) create mode 100644 doc/Jugl.js create mode 100644 doc/examples.html create mode 100644 examples/fullScreen2.html create mode 100644 examples/fullscreen.jpg delete mode 100644 examples/supports-vector.html create mode 100644 tools/BeautifulSoup.py create mode 100755 tools/exampleparser.py diff --git a/doc/Jugl.js b/doc/Jugl.js new file mode 100644 index 0000000000..5dcf1a521d --- /dev/null +++ b/doc/Jugl.js @@ -0,0 +1,74 @@ +/** + * Jugl.js -- JavaScript Template Attribute Language + * This code is not yet licensed for release or distribution. + * + * Copyright 2007 Tim Schaub + */ + +/** + * Contains portions of OpenLayers.js -- OpenLayers Map Viewer Library + * + * Copyright 2005-2006 MetaCarta, Inc., released under a modified BSD license. + * Please see http://svn.openlayers.org/trunk/openlayers/repository-license.txt + * for the full text of the license. + */ + +/** + * Contains portions of Prototype.js: + * + * Prototype JavaScript framework, version 1.4.0 + * (c) 2005 Sam Stephenson + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://prototype.conio.net/ + */ + +(function(){var uri="http://jugl.tschaub.net/trunk/lib/Jugl.js";var Jugl={singleFile:true};window[uri]=Jugl;})();(function(){var uri="http://jugl.tschaub.net/trunk/lib/Jugl.js";var singleFile=(typeof window[uri]=="object"&&window[uri].singleFile);var Jugl={prefix:"jugl",namespaceURI:"http://namespace.jugl.org/",scriptName:(!singleFile)?"lib/Jugl.js":"Jugl.js",getScriptLocation:function(){var scriptLocation="";var scriptName=Jugl.scriptName;var scripts=document.getElementsByTagName('script');for(var i=0;i-1)&&(index+scriptName.length==src.length)){scriptLocation=src.slice(0,-scriptName.length);break;}}} +return scriptLocation;}};if(!singleFile){var jsfiles=new Array("Jugl/Util.js","Jugl/Class.js","Jugl/Async.js","Jugl/Node.js","Jugl/Attribute.js","Jugl/Console.js","Jugl/Template.js");var allScriptTags="";var host=Jugl.getScriptLocation()+"lib/";for(var i=0;i";allScriptTags+=currentScriptTag;}else{var s=document.createElement("script");s.src=host+jsfiles[i];var h=document.getElementsByTagName("head").length?document.getElementsByTagName("head")[0]:document.body;h.appendChild(s);}} +if(allScriptTags){document.write(allScriptTags);}} +window[uri]=Jugl;})();(function(){var uri="http://jugl.tschaub.net/trunk/lib/Jugl.js";var Jugl=window[uri];Jugl.Class=function(){var Class=function(){this.initialize.apply(this,arguments);} +var extended={};var parent;for(var i=0;i + + + OL Docs + + + + + + +
+
+
+ Example # goes here +
+
+ Title goes here +
+ +
+ + Short Description goes here + +
+
+ + Related Classes go here + +
+
+
+ + \ No newline at end of file diff --git a/examples/GMLParser.html b/examples/GMLParser.html index 44718139f2..20c6ee79a7 100644 --- a/examples/GMLParser.html +++ b/examples/GMLParser.html @@ -1,5 +1,6 @@ + OpenLayers GML Parser + OpenLayers KML Parser Example +

KML Parser Example

+ +
+ +

+ Demonstrate the operation of the KML parser. +

+ +
+ +
+ This script reads data from a KML file and parses out the coordinates, appending them to a HTML string with markup tags. + This markup is dumped to an element in the page. +
- + diff --git a/examples/accessible.html b/examples/accessible.html index d0ca058719..272bd28daf 100644 --- a/examples/accessible.html +++ b/examples/accessible.html @@ -1,9 +1,11 @@ + OpenLayers Accessible Example + -

OpenLayers Example

+

Accessible Example

+ +
+
+ +

+ Demonstrate how to use the KeyboardDefaults option parameter for layer types. +

+ @@ -95,30 +109,35 @@

OpenLayers Example

-

Navigate the map in one of three ways: -

    -
  • Click on the named links to zoom and pan
  • -
  • Use following keys to pan and zoom: -
      -
    • + (zoom in)
    • -
    • - (zoom out)
    • -
    • up-arrow (pan north)
    • -
    • down-arrow (pan south)
    • -
    • left-arrow (pan east)
    • -
    • right-arrow (pan west)
    • -
    -
  • -
  • If access keys work for links in your browser, use: -
      -
    • i (zoom in)
    • -
    • o (zoom out)
    • -
    • n (pan north)
    • -
    • s (pan south)
    • -
    • e (pan east)
    • -
    • w (pan west)
    • -
    -
  • -
-

+ +
+

Navigate the map in one of three ways: +

    +
  • Click on the named links to zoom and pan
  • +
  • Use following keys to pan and zoom: +
      +
    • + (zoom in)
    • +
    • - (zoom out)
    • +
    • up-arrow (pan north)
    • +
    • down-arrow (pan south)
    • +
    • left-arrow (pan east)
    • +
    • right-arrow (pan west)
    • +
    +
  • +
  • If access keys work for links in your browser, use: +
      +
    • i (zoom in)
    • +
    • o (zoom out)
    • +
    • n (pan north)
    • +
    • s (pan south)
    • +
    • e (pan east)
    • +
    • w (pan west)
    • +
    +
  • +
+

+ + This is an example of using alternate methods to control panning and zooming. This approach uses map.pan() and map.zoom(). You'll note that to pan, additional math is necessary along with map.size() in order to set the distance to pan. +
diff --git a/examples/attribution.html b/examples/attribution.html index 5383db471f..79bef75b0e 100644 --- a/examples/attribution.html +++ b/examples/attribution.html @@ -1,36 +1,52 @@ - - - - + - - -

OpenLayers Example

-
- + map.addLayers([ol_wms, jpl_wms]); + map.addControl(new OpenLayers.Control.LayerSwitcher()); + map.addControl(new OpenLayers.Control.Attribution()); + // map.setCenter(new OpenLayers.LonLat(0, 0), 0); + map.zoomToMaxExtent(); + } + + + +

Attribution Example

+ +
+
+ +

+ Shows the use of the attribution layer option on a number of layer types. +

+ +
+ +
+ This is an example of how to add an attribution block to the OpenLayers window. In order to use an + attribution block, an attribution parameter must be set in each layer that requires attribution. In + addition, an attribution control must be added to the map. +
+ diff --git a/examples/baseLayers.html b/examples/baseLayers.html index 2114d3f08e..714c742118 100644 --- a/examples/baseLayers.html +++ b/examples/baseLayers.html @@ -1,80 +1,100 @@ - - - - - - - - - - - - + + + + + + + - - -

OpenLayers With WMS, Google, VE Example

-
-
click to add a marker to the map
-
click to remove the marker from the map
- + map.setCenter(new OpenLayers.LonLat(lon, lat), zoom); + map.addControl( new OpenLayers.Control.LayerSwitcher() ); + map.addControl( new OpenLayers.Control.MousePosition() ); + + } + + function add() { + var url = 'http://boston.openguides.org/markers/AQUA.png'; + var sz = new OpenLayers.Size(10, 17); + var calculateOffset = function(size) { + return new OpenLayers.Pixel(-(size.w/2), -size.h); + }; + var icon = new OpenLayers.Icon(url, sz, null, calculateOffset); + marker = new OpenLayers.Marker(barcelona, icon); + markers.addMarker(marker); + + marker = new OpenLayers.Marker(madrid, icon.clone()); + markers.addMarker(marker); + + } + + function remove() { + markers.removeMarker(marker); + } + + + + +

Base Layers Example

+ +
+
+ +

+ This example shows the use base layers from multiple commercial map image providers. +

+ +
+
+ +
click to add a marker to the map
+
click to remove the marker from the map
+
+ +
+
+ diff --git a/examples/boxes.html b/examples/boxes.html index 1b18984d3d..f6ee49247b 100644 --- a/examples/boxes.html +++ b/examples/boxes.html @@ -1,47 +1,58 @@ - - - - + + + +

Boxes Example

+ +
+
+ +

+ Demonstrate marker and box type annotations on a map. +

+ +
- map.addLayers([ol_wms, boxes]); - map.addControl(new OpenLayers.Control.LayerSwitcher()); - map.zoomToMaxExtent(); - } - - - -

OpenLayers Example

-
- +
+ diff --git a/examples/buffer.html b/examples/buffer.html index 03f86b565f..87b1e7d69e 100644 --- a/examples/buffer.html +++ b/examples/buffer.html @@ -1,41 +1,54 @@ - - - - + - - -
-

Use the buffer property to control how many tiles are included - outside the visible map area. Default is 2

- + function init(){ + map = new OpenLayers.Map( 'map' ); + layer = new OpenLayers.Layer.WMS( "0 buffer: OpenLayers WMS", + "http://labs.metacarta.com/wms/vmap0", + {layers: 'basic'}, {'buffer':0} ); + map.addLayer(layer); + layer = new OpenLayers.Layer.WMS( "1 buffer: OpenLayers WMS", + "http://labs.metacarta.com/wms/vmap0", + {layers: 'basic'}, {'buffer':1} ); + map.addLayer(layer); + layer = new OpenLayers.Layer.WMS( "4 buffer: OpenLayers WMS", + "http://labs.metacarta.com/wms/vmap0", + {layers: 'basic'}, {'buffer':4} ); + map.addLayer(layer); + map.addControl(new OpenLayers.Control.LayerSwitcher()) + map.setCenter(new OpenLayers.LonLat(lon, lat), zoom); + } + + + +

Buffer Example

+ +
+
+ +

+ This example shows the use of the buffer layer option for any layer that inherits from OpenLayers.Layer.Grid. +

+ +
+ +
+ Use the buffer property to control how many tiles are included + outside the visible map area. Default is 2. +
+ diff --git a/examples/click.html b/examples/click.html index 8a5b73a1af..b7be3f90a1 100644 --- a/examples/click.html +++ b/examples/click.html @@ -1,41 +1,53 @@ - - - - + - - -

OpenLayers Example

-
- + map.addLayers([ol_wms, jpl_wms]); + map.addControl(new OpenLayers.Control.LayerSwitcher()); + // map.setCenter(new OpenLayers.LonLat(0, 0), 0); + map.zoomToMaxExtent(); + map.events.register("click", map, function(e) { + var lonlat = map.getLonLatFromViewPortPx(e.xy); + alert("You clicked near " + lonlat.lat + " N, " + + + lonlat.lon + " E"); + }); + } + + + +

Click Event Example

+ +
+
+ +

+ This example shows the use of the register and getLonLatFromViewPortPx functions to trigger events on mouse click. +

+ +
+ +
+ diff --git a/examples/controls.html b/examples/controls.html index 07d88e6265..9fd94ec774 100644 --- a/examples/controls.html +++ b/examples/controls.html @@ -1,52 +1,61 @@ - - - - + + + +

Map Controls Example

- var dm_wms = new OpenLayers.Layer.WMS( "DM Solutions Demo", - "http://www2.dmsolutions.ca/cgi-bin/mswms_gmap", - {layers: "bathymetry,land_fn,park,drain_fn,drainage," + - "prov_bound,fedlimit,rail,road,popplace", - transparent: "true", format: "image/png" }); +
+
- jpl_wms.setVisibility(false); - dm_wms.setVisibility(false); +

+ Attach zooming, panning, layer switcher, overview map, and permalink map controls to an OpenLayers window. +

- map.addLayers([ol_wms, jpl_wms, dm_wms]); - if (!map.getCenter()) map.zoomToMaxExtent(); - } - - - -

OpenLayers Example

- -
- + +
+ +
+ diff --git a/examples/custom-control-point.html b/examples/custom-control-point.html index ffd4e92037..24e08073ec 100644 --- a/examples/custom-control-point.html +++ b/examples/custom-control-point.html @@ -1,9 +1,10 @@ + OpenLayers Custom Control Point Examle @@ -45,7 +46,18 @@ +

Custom Control Point Example

+ +
+
+ +

+ Demonstrate the addition of a point reporting control to the OpenLayers window. +

+
+ +
diff --git a/examples/custom-control.html b/examples/custom-control.html index 1fce366b7f..ae684f05a6 100644 --- a/examples/custom-control.html +++ b/examples/custom-control.html @@ -1,49 +1,61 @@ - - - - - - -
- + notice: function (bounds) { + alert(bounds); + } + }); + + map.addLayer(layer); + map.addControl(control); + map.setCenter(new OpenLayers.LonLat(lon, lat), zoom); + } + // --> + + + +

Custom Control Example

+ +
+
+ +

+ Demonstrate the addition of a draggable rectangle to the OpenLayers window. +

+ +
+ +
+ diff --git a/examples/custom-style.html b/examples/custom-style.html index 79bab7ae1e..b03ad8dfb9 100644 --- a/examples/custom-style.html +++ b/examples/custom-style.html @@ -1,53 +1,67 @@ - - - - - - - -
-

If you care to modify the style of any OpenLayers element, include - the default stylesheet as a link and declare any style modifications - below that link. These style declarations can be in other linked - stylesheets or in style tags. In addition, construct your map with - options that include {theme: null}. This will disable the default - method of loading the stylesheet and allow you to declare style rules - in your own linked stylesheets or style tags.

-

This example shows how to declare the font family, size, and color - for the mouse position. Note that only the style keys that you want to - modify (change from the default) need to be specified.

- + function init(){ + var options = {theme: null}; + map = new OpenLayers.Map('map', options); + layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", + "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} ); + + map.addControl(new OpenLayers.Control.MousePosition()); + map.addLayer(layer); + map.setCenter(new OpenLayers.LonLat(lon, lat), zoom); + } + // --> + + + +

Custom Style Example

+ +
+
+ +

+ Demonstrate changing CSS styles on controls in the OpenLayers window. +

+ +
+ +
+

If you care to modify the style of any OpenLayers element, include + the default stylesheet as a link and declare any style modifications + below that link. These style declarations can be in other linked + stylesheets or in style tags. In addition, construct your map with + options that include {theme: null}. This will disable the default + method of loading the stylesheet and allow you to declare style rules + in your own linked stylesheets or style tags.

+

This example shows how to declare the font family, size, and color + for the mouse position. Note that only the style keys that you want to + modify (change from the default) need to be specified.

+
+ diff --git a/examples/debug.html b/examples/debug.html index f0d1ae5c8e..3317de090d 100644 --- a/examples/debug.html +++ b/examples/debug.html @@ -1,64 +1,75 @@ - - - - - - -

OpenLayers Debug Example

-

To run OpenLayers in debug mode, include the following script - tag before the tag that loads OpenLayers: -

-    <script src="../lib/Firebug/firebug.js"></script>
-    
- The path to firebug.js must be relative to your - html file. With this script included calls to OpenLayers.Console - will be displayed in the Firebug console. For browsers without - the Firebug extension, the script creates a Firebug Lite console. - This console can be opened by hitting F12 or Ctrl+Shift+L - (?+Shift+L on a Mac). If you want the Firebug Lite console - to be open when the page loads, add debug="true" to the opening - html tag of your page. Open the console and click on the links below - to see console calls.

- -

The Firebug website has a complete list of - console calls. - Note that not all are supported with Firebug Lite.

- + + OpenLayers Debug Example + + + + + + +

Debug Example

+ +
+ +

+ Demonstrate console calls to a Firebug console. Requires Firefox. Mostly for developers. +

+ +
+

To run OpenLayers in debug mode, include the following script + tag before the tag that loads OpenLayers: + +

	<script src="../lib/Firebug/firebug.js"></script>
+ + The path to firebug.js must be relative to your + html file. With this script included calls to OpenLayers.Console + will be displayed in the Firebug console. For browsers without + the Firebug extension, the script creates a Firebug Lite console. + This console can be opened by hitting F12 or Ctrl+Shift+L + (?+Shift+L on a Mac). If you want the Firebug Lite console + to be open when the page loads, add debug="true" to the opening + html tag of your page. Open the console and click on the links below + to see console calls.

+ +

The Firebug website has a complete list of + console calls. + Note that not all are supported with Firebug Lite.

+
+ diff --git a/examples/doubleSetCenter.html b/examples/doubleSetCenter.html index d982532b12..10514ae17b 100644 --- a/examples/doubleSetCenter.html +++ b/examples/doubleSetCenter.html @@ -1,17 +1,36 @@ - - - - -
- - + + OpenLayers Double Set Center Example + + + + + +

Double Set Center Example

+ +
+ +

+ Demonstrate the behavior of two calls to set the center after instatiating the layer object. +

+ +
+ + + +
+ diff --git a/examples/drag-feature.html b/examples/drag-feature.html index 8dfe33958c..ae7fd15365 100644 --- a/examples/drag-feature.html +++ b/examples/drag-feature.html @@ -1,97 +1,108 @@ - - Drag Feature - - - + + +

Drag Feature Example

+ +
+ +

+ Demonstrates point, line and polygon creation and editing. +

+ +
- // --> - - - -

OpenLayers Drag Feature Example

-
-
-
    -
  • - - -
  • -
  • - - -
  • -
  • - - -
  • -
  • - - -
  • -
  • - - -
  • -
-
- +
+
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+
+ +
+ diff --git a/examples/draw-feature.html b/examples/draw-feature.html index 760fef8e71..731a804d66 100644 --- a/examples/draw-feature.html +++ b/examples/draw-feature.html @@ -1,96 +1,108 @@ - - - - - - -

OpenLayers Draw Feature Example

-
-
    -
  • - - -
  • -
  • - - -
  • -
  • - - -
  • -
  • - - -
  • -
-

Feature digitizing is in freehand mode by default. In freehand mode, the mouse is treated as a pen. - Drawing begins on mouse down, continues with every mouse move, and ends with mouse up.

-

To turn freehand mode off, hold down the shift key while digitizing. With freehand mode off, one - vertex is added with each click and double-clicks finish drawing. Freehand mode can be toggled on and off - at any time while drawing.

- - + // --> + + + +

OpenLayers Draw Feature Example

+ +
+ +

+ Demonstrate on-screen digitizing tools for point, line, and polygon creation. +

+ +
+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +
+

Feature digitizing is in freehand mode by default. In freehand mode, the mouse is treated as a pen. + Drawing begins on mouse down, continues with every mouse move, and ends with mouse up.

+

To turn freehand mode off, hold down the shift key while digitizing. With freehand mode off, one + vertex is added with each click and double-clicks finish drawing. Freehand mode can be toggled on and off + at any time while drawing.

+
+ diff --git a/examples/editingtoolbar.html b/examples/editingtoolbar.html index 06e63d9f3b..42b3d27953 100644 --- a/examples/editingtoolbar.html +++ b/examples/editingtoolbar.html @@ -1,40 +1,52 @@ - - - - - + + +

Editing Toolbar Example

- vlayer = new OpenLayers.Layer.Vector( "Editable" ); - map.addLayer(vlayer); - map.addControl(new OpenLayers.Control.PanZoomBar()); - map.addControl(new OpenLayers.Control.EditingToolbar(vlayer)); - - map.setCenter(new OpenLayers.LonLat(lon, lat), zoom); - } - // --> - - - -
-
- +
+ +

+ Demonstrate polygon, polyline and point creation and editing tools. +

+ +
+
+ +
+ diff --git a/examples/example.html b/examples/example.html index 2185a823ff..cbe0c3e05e 100644 --- a/examples/example.html +++ b/examples/example.html @@ -1,5 +1,6 @@ + OpenLayers Example - - + - - -
- + var dm_wms = new OpenLayers.Layer.WMS( "DM Solutions Demo", + "http://www2.dmsolutions.ca/cgi-bin/mswms_gmap", + {layers: "bathymetry,land_fn,park,drain_fn,drainage," + + "prov_bound,fedlimit,rail,road,popplace", + transparent: "true", format: "image/png" }); + + map.addLayers([ol_wms, jpl_wms, dm_wms]); + map.addControl(new OpenLayers.Control.LayerSwitcher()); + // map.setCenter(new OpenLayers.LonLat(0, 0), 0); + map.zoomToMaxExtent(); + } + + + +
+ +
+

Full Screen Example

+ +
+ +

+ Demonstrate a map that fill the entire browser window. +

+ +
+ This example uses CSS to define the dimensions of the map element in order to fill the screen. + When the user resizes the window, the map size changes correspondingly. No scroll bars! +
+
+ diff --git a/examples/fullScreen2.html b/examples/fullScreen2.html new file mode 100644 index 0000000000..900eb17c27 --- /dev/null +++ b/examples/fullScreen2.html @@ -0,0 +1,51 @@ + + + Full Screen Example 2 + + + + + + +
+ +
+

Full Screen Example

+ +
+ +

+ Demonstrate a map that fill the entire browser window. +

+ +
+ This example uses CSS to define the dimensions of the map element in order to fill the screen. + When the user resizes the window, the map size changes correspondingly. No scroll bars! +
+
+ + diff --git a/examples/fullscreen.jpg b/examples/fullscreen.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ca054fd8a1d5d3d28d2ade5c22f754756352589c GIT binary patch literal 104464 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 h{)bcIpKtSD + OpenLayers GeoRSS Marker Example @@ -14,7 +15,7 @@ OpenLayers.ProxyHost = "/proxy/?url="; function init(){ map = new OpenLayers.Map('map', {maxResolution:'auto'}); - layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", + layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} ); map.addLayer(layer); map.setCenter(new OpenLayers.LonLat(0, 0), 0); @@ -28,7 +29,16 @@ -

GeoRSS in OpenLayers

+

GeoRSS Marker Example

+ +
+ +

+ Demonstrate loading a GeoRSS feed using the GeoRSS parser. +

+
+ +
diff --git a/examples/georss-serialize.html b/examples/georss-serialize.html index 94a6925cf5..f68151df53 100644 --- a/examples/georss-serialize.html +++ b/examples/georss-serialize.html @@ -1,9 +1,10 @@ + OpenLayers GeoRSS Serialize Example @@ -20,9 +21,9 @@ function init(){ g = new OpenLayers.Format.GeoRSS(); map = new OpenLayers.Map('map'); - - var wmsLayer = new OpenLayers.Layer.WMS( "OpenLayers WMS", - "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}); + + var wmsLayer = new OpenLayers.Layer.WMS( "OpenLayers WMS", + "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}); var pointLayer = new OpenLayers.Layer.Vector("Point Layer"); pointLayer.onFeatureInsert = serialize; @@ -30,17 +31,27 @@ map.addLayers([wmsLayer, pointLayer]); map.addControl(new OpenLayers.Control.EditingToolbar(pointLayer)); map.addControl(new OpenLayers.Control.MousePosition()); - - + + map.setCenter(new OpenLayers.LonLat(0, 0), 3); } -

OpenLayers Draw Point Example

+

Draw Point Example

+ +
+ +

+ Demonstrate serialization of features in a Vector layer to GeoRSS. +

+
- +
+
+ +
diff --git a/examples/georss.html b/examples/georss.html index de4c66e549..6d737e8781 100644 --- a/examples/georss.html +++ b/examples/georss.html @@ -1,9 +1,10 @@ + OpenLayers GeoRSS Example @@ -14,14 +15,14 @@ OpenLayers.ProxyHost = "/proxy/?url="; function init(){ map = new OpenLayers.Map('map', {maxResolution:'auto'}); - layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", + layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} ); map.addLayer(layer); map.setCenter(new OpenLayers.LonLat(0, 0), 0); map.addControl(new OpenLayers.Control.LayerSwitcher()); } function addUrl() { - var urlObj = OpenLayers.Util.getElement('url'); + var urlObj = OpenLayers.Util.getElement('url'); var value = urlObj.value; var parts = value.split("/"); var newl = new OpenLayers.Layer.GeoRSS( parts[parts.length-1], value); @@ -31,12 +32,31 @@ -

GeoRSS in OpenLayers

-

This demo uses the OpenLayers GeoRSS parser, which supports GeoRSS Simple and W3C GeoRSS. Only points are currently supported. Get the code!

-
- GeoRSS URL: -
-

The above input box allows the input of a URL to a GeoRSS feed. This feed can be local to the HTML page -- for example, entering 'georss.xml' will work by default, because there is a local file in the directory called georss.xml -- or, with a properly set up ProxyHost variable (as is used here), it will be able to load any HTTP URL which contains GeoRSS and display it. Anything else will simply have no effect.

+

GeoRSS Example

+ +
+ +

+ Display a couple of locally cached georss feeds on an a basemap. +

+
+ +
+

This demo uses the OpenLayers GeoRSS parser, which supports GeoRSS Simple and W3C GeoRSS. Only points are + currently supported. The OpenLayers GeoRSS parser will automatically connect an information bubble to the map + markers, similar to Google maps. In addition, the parser can use custom PNG icons for markers. A sample GeoRSS + file (georss.xml) is included. + +

+ GeoRSS URL: + +
+ +

The above input box allows the input of a URL to a GeoRSS feed. This feed can be local to the HTML page -- + for example, entering 'georss.xml' will work by default, because there is a local file in the directory called + georss.xml -- or, with a properly set up ProxyHost variable (as is used here), it will be able to load any + HTTP URL which contains GeoRSS and display it. Anything else will simply have no effect.

+
diff --git a/examples/georss.xml b/examples/georss.xml index 053749b943..fecf77aeac 100644 --- a/examples/georss.xml +++ b/examples/georss.xml @@ -375,4 +375,4 @@ crschmidt 2006-01-03T23:23:26.706763+00:00 - \ No newline at end of file + diff --git a/examples/getfeatureinfo.html b/examples/getfeatureinfo.html index 301398ecc9..e529d3630f 100644 --- a/examples/getfeatureinfo.html +++ b/examples/getfeatureinfo.html @@ -1,25 +1,35 @@ - - - -World Map Query - - -
-
-

CIA Factbook

-

Click a country to see statistics about the country below.

-
+ + OpenLayers Feature Info Example + + + + +

Feature Info Example

+ +
+ +

+ Demonstrates sending a GetFeatureInfo query to an OWS. Returns information about a map feature in the side DIV. +

+ +
+ +
+

CIA Factbook

+

Click a country to see statistics about the country below.

+
+
-
-
+
+ - + +
+
+ diff --git a/examples/gml-layer.html b/examples/gml-layer.html index 4e838451f6..3904c0eb31 100644 --- a/examples/gml-layer.html +++ b/examples/gml-layer.html @@ -1,9 +1,10 @@ + OpenLayers GML Layer Example @@ -16,7 +17,7 @@ function init(){ map = new OpenLayers.Map('map'); - layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", + layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} ); map.addLayer(layer); map.zoomToExtent(new OpenLayers.Bounds(-3.922119,44.335327,4.866943,49.553833)); @@ -25,6 +26,16 @@ +

GML Layer Example

+ +
+ +

+ Loads locally stored GML vector data on a basemap. Includes GML example file. +

+
+ +
diff --git a/examples/gml-serialize.html b/examples/gml-serialize.html index cdf4eaeb63..863af02a63 100644 --- a/examples/gml-serialize.html +++ b/examples/gml-serialize.html @@ -1,5 +1,6 @@ + OpenLayers GML Serialization Example - - - - -

OpenLayers Gutter Example

-
-

- When you render tiles with certain types of symbols, there are artifacts - at tile edges that make symbology not look continuous. In the center of - the above map (when it first loads), the large orange road is split - vertically by a tile. Open the layer switcher and change to the layer - with a 15 pixel gutter to see how the symbology looks nicer. -

- - + + + OpenLayers Gutter Example + + + + + +

Gutter Example

+ +
+ +

+ Demonstrates map tiling artifacts, and OpenLayer's facility for correcting this distortion. +

+ +
+ +
+

+ When you render tiles with certain types of symbols, there are artifacts + at tile edges that make symbology not look continuous. In the center of + the above map (when it first loads), the large orange road is split + vertically by a tile. Open the layer switcher and change to the layer + with a 15 pixel gutter to see how the symbology looks nicer. +

+
+ diff --git a/examples/image-layer.html b/examples/image-layer.html index 39ca3fbdc7..cfeef1273e 100644 --- a/examples/image-layer.html +++ b/examples/image-layer.html @@ -1,12 +1,13 @@ + OpenLayers Image Layer Example @@ -17,7 +18,7 @@ map = new OpenLayers.Map('map'); var options = {numZoomLevels: 3}; - + var graphic = new OpenLayers.Layer.Image( 'City Lights', 'http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif', @@ -26,7 +27,7 @@ options); var jpl_wms = new OpenLayers.Layer.WMS( "NASA Global Mosaic", - "http://t1.hypercube.telascience.org/tiles/landsat7", + "http://t1.hypercube.telascience.org/cgi-bin/landsat7", {layers: "landsat7"}, options); map.addLayers([graphic, jpl_wms]); @@ -36,15 +37,25 @@ -

OpenLayers Image Layer Example

-
-

- The "City Lights" layer above is created from a single web accessible - image. If you construct it without any resolution related options, - the layer will be given a single resolution based on the extent/size. - Otherwise, it behaves much like a regular layer. This is primarily - intended to be used in an overview map - where another layer type - might not make a good overview. +

Image Layer Example

+ +
+ +

+ Demonstrate a single non-tiled image as a selectable base layer.

+ +
+ +
+

+ The "City Lights" layer above is created from a single web accessible + image. If you construct it without any resolution related options, + the layer will be given a single resolution based on the extent/size. + Otherwise, it behaves much like a regular layer. This is primarily + intended to be used in an overview map - where another layer type + might not make a good overview. +

+
diff --git a/examples/kamap.html b/examples/kamap.html index 366a22f169..6c38909c04 100644 --- a/examples/kamap.html +++ b/examples/kamap.html @@ -1,9 +1,10 @@ + OpenLayers KaMap Example @@ -28,7 +29,16 @@ -

OpenLayers Example

+

KaMap Example

+ +
+ +

+ Demonstrate a tiled kamap layer as the base map, which can be pre-cached for higher performance. +

+
+ +
diff --git a/examples/kml-layer.html b/examples/kml-layer.html index da99f9c184..cfc461bbe4 100644 --- a/examples/kml-layer.html +++ b/examples/kml-layer.html @@ -2,8 +2,8 @@ @@ -16,7 +16,7 @@ function init(){ map = new OpenLayers.Map('map'); - layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", + layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} ); map.addLayer(layer); map.addLayer(new OpenLayers.Layer.GML("KML", "kml/lines.kml", {format: OpenLayers.Format.KML})); @@ -25,6 +25,16 @@ +

KML Layer Example

+ +
+ +

+ Demonstrates loading and displaying a KML file on top of a basemap. +

+
+ +
diff --git a/examples/layer-opacity.html b/examples/layer-opacity.html index fc46aaf7ff..2a5b2365a8 100644 --- a/examples/layer-opacity.html +++ b/examples/layer-opacity.html @@ -1,12 +1,13 @@ + OpenLayers Layer Opacity Example +

Basic Single WMS Example

+ +
+ +
Show a Simple Map
+
+ +
+ This example shows a very simple layout with minimal controls. This example uses a single WMS base layer. +
diff --git a/examples/mapserver.html b/examples/mapserver.html index 0cddfe925a..ab51fce6a9 100644 --- a/examples/mapserver.html +++ b/examples/mapserver.html @@ -1,5 +1,6 @@ + MapServer Layer + NavToolbar Demo +

NavToolbar Demo

+

+ Demo the NavToolbar, a subclass of Control.Panel which shows icons for + navigation. +

diff --git a/examples/notile.html b/examples/notile.html index fa8b20bc5b..36edfd8fd0 100644 --- a/examples/notile.html +++ b/examples/notile.html @@ -29,7 +29,11 @@ -

OpenLayers Example

+

Untiled Example

+

+ Create an untiled WMS layer using the singleTile: true, option or the deprecated + WMS.Untiled layer. +

The first layer is an old OpenLayers.Layer.WMS.Untiled layer, using a default ratio value of 1.5. diff --git a/examples/openmnnd.html b/examples/openmnnd.html index 08695673dd..f853f9b4f2 100644 --- a/examples/openmnnd.html +++ b/examples/openmnnd.html @@ -17,60 +17,60 @@ function init(){ OpenLayers.ProxyHost="/proxy/?url="; map = new OpenLayers.Map('map', {'maxResolution':'auto', maxExtent: new OpenLayers.Bounds(-203349.72008129774,4816309.33,1154786.8041952979,5472346.5), projection: 'EPSG:26915' } ); - layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", + layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", ["http://geoint.lmic.state.mn.us/cgi-bin/wms"], {layers: 'fsa'} ); map.addLayer(layer); - + wfs_url = "http://prototype.openmnnd.org/cgi-bin/mapserv.exe?map=openmnndwfs/openmnndwfs.map"; wms = new OpenLayers.Layer.WMS("Minnesota Parcels (WMS)", wfs_url, {'layers':'streams', 'transparent': true, 'format':'image/gif'}); - + map.addLayer(wms); - + wfs = new OpenLayers.Layer.WFS("Minnesota Streams (WFS)", wfs_url, {'typename':'streams'}, {ratio:1.25, minZoomLevel:4}); - - // preFeatureInsert can be used to set style before the feature is drawn - wfs.preFeatureInsert= function(feature) { feature.style.strokeWidth="3"; feature.style.strokeColor="blue"; + + // preFeatureInsert can be used to set style before the feature is drawn + wfs.preFeatureInsert= function(feature) { feature.style.strokeWidth="3"; feature.style.strokeColor="blue"; } wfs.onFeatureInsert = function(feature) { OpenLayers.Util.getElement('stream_features').innerHTML = feature.layer.features.length; - } + } map.addLayer(wfs); - + // Or a style can be set on the layer. pwfsstyle = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style['default']); - OpenLayers.Util.extend(pwfsstyle, {'fillColor': 'green'}); - - pwfs = new OpenLayers.Layer.WFS("Minnesota Plat (WFS)", wfs_url, - {'typename':'plat'}, + OpenLayers.Util.extend(pwfsstyle, {'fillColor': 'green'}); + + pwfs = new OpenLayers.Layer.WFS("Minnesota Plat (WFS)", wfs_url, + {'typename':'plat'}, { - ratio:1.25, - minZoomLevel:8, - extractAttributes: true, + ratio:1.25, + minZoomLevel:8, + extractAttributes: true, style: pwfsstyle }); - - pwfs.onFeatureInsert= function(feature) { + + pwfs.onFeatureInsert= function(feature) { OpenLayers.Util.getElement('plat_features').innerHTML = feature.layer.features.length; } - map.addLayer(pwfs); - + map.addLayer(pwfs); + rstyle = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style['default']); - OpenLayers.Util.extend(rstyle, {'strokeColor': 'white', strokeWIdth: "4"}); - rwfs = new OpenLayers.Layer.WFS("Minnesota Roads (WFS)", wfs_url, {'typename':'roads'}, + OpenLayers.Util.extend(rstyle, {'strokeColor': 'white', strokeWIdth: "4"}); + rwfs = new OpenLayers.Layer.WFS("Minnesota Roads (WFS)", wfs_url, {'typename':'roads'}, {ratio:1.25, minZoomLevel:7, extractAttributes: true, style:rstyle}); - - rwfs.onFeatureInsert= function(feature) { - OpenLayers.Util.getElement('road_features').innerHTML = feature.layer.features.length; + + rwfs.onFeatureInsert= function(feature) { + OpenLayers.Util.getElement('road_features').innerHTML = feature.layer.features.length; } - - map.addLayer(rwfs); - - map.events.register('moveend', null, function() { + + map.addLayer(rwfs); + + map.events.register('moveend', null, function() { OpenLayers.Util.getElement('stream_features').innerHTML = "0"; OpenLayers.Util.getElement('road_features').innerHTML = "0"; OpenLayers.Util.getElement('plat_features').innerHTML = "0"; - }); + }); var ls = new OpenLayers.Control.LayerSwitcher(); map.addControl(ls); @@ -84,10 +84,10 @@ map.addControl(drawControls[key]); } drawControls.selectPlat.activate(); - + map.zoomToExtent(new OpenLayers.Bounds(303232.550864,5082911.694856,305885.161263,5084486.682281)); } - + function toggleControl(element) { for(key in drawControls) { var control = drawControls[key]; @@ -100,13 +100,13 @@ } var displayedFeature = null; function feature_info_hover(feature) { - if (displayedFeature != feature && - (!feature.layer.selectedFeatures.length || + if (displayedFeature != feature && + (!feature.layer.selectedFeatures.length || (feature.layer.selectedFeatures[0] == feature))) { feature_info(feature); displayedFeature = feature; } - } + } function feature_info(feature) { var html = "

    "; for(var i in feature.attributes) @@ -119,13 +119,13 @@
      -
    • Streams: Feature Count 0
    • -
    • Plat: Feature Count 0
    • -
    • Roads: Feature Count 0
    • +
    • Streams: Feature Count 0
    • +
    • Plat: Feature Count 0
    • +
    • Roads: Feature Count 0
    -
      +
      • @@ -134,8 +134,28 @@
      • -
      -
    +
+
+

This example shows the use of a WFS service rendered using the OpenLayers vector library.

+
+

+ This is an example that shows rendering a WFS service using OpenLayer vectors in the browser. The OpenLayers code will download the GML + from the WFS service for each layer, parse it and create features using the OL vector library to draw the features on the map. For + more information on the vector library, please visit vector support wiki. + In this example there are 4 layers shown on the map. The base layer and parcel layer are created from a WMS serivce using the OpenLayers.Layer.WMS object. + The streams, roads, and plat layers are drawn from a WFS service using the OpenLayers.Layer.WFS object. +

+ +

+ Rendering WFS layers into vectors is possible, but you need to be cautions when showing the features on the map. Testing has shown that when + you renderer more than 200 vectors in the browser the performance decreases dramatically. Also features that have a lot of vertices + can cause performance issues. In this example the parcel layer is rendered as a WMS layer because at the time of developing this example + there where a handful of features that had too many vertices to render without killing the browser resources. + + There are a number of properties that can be set for each WFS layer, such color and line weight using style properties such as strokeColor and strokeWidth. + You can also get feature attributes from the WFS services using the extractAttribute property. View the source to see the example code. +

+
diff --git a/examples/outOfRangeMarkers.html b/examples/outOfRangeMarkers.html index a62ef7fd04..6f853de1c3 100644 --- a/examples/outOfRangeMarkers.html +++ b/examples/outOfRangeMarkers.html @@ -1,5 +1,6 @@ + Using maxResolution to control overlays +

Overview Map

+ +
+
+

+ Enable a small Overview Map that moves/interacts with your main map. +

The above map has an overview map control that is created with the default options. Much like a regular map, the map contained by @@ -39,7 +46,7 @@ var jpl = new OpenLayers.Layer.WMS( "NASA Global Mosaic", - "http://t1.hypercube.telascience.org/tiles/landsat7", + "http://t1.hypercube.telascience.org/cgi-bin/landsat7", {layers: "landsat7"} ); diff --git a/examples/panel.html b/examples/panel.html index d042a5946f..da81a4ccf8 100644 --- a/examples/panel.html +++ b/examples/panel.html @@ -84,6 +84,11 @@ +

Custom Control.Panel

+

+ Create a custom control.panel, styled entirely with + CSS, and add your own controls to it. +

diff --git a/examples/popups.html b/examples/popups.html index 943e0f15e9..7cdbd19d5a 100644 --- a/examples/popups.html +++ b/examples/popups.html @@ -120,7 +120,15 @@ -
+

Popup Mayhem

+ +
+
+

+ All kinds of ways to create and interact with Popups. +

+ +

If you open an anchoredbubble and switch to google, it shouldn't crash. If it does, don't release OpenLayers.

click to add Popup to map
click to add a Marker with an AnchoredBubble popup
@@ -129,5 +137,8 @@
click to remove the markers layer
marker.onscreen()?
click to destroy the popup from map
+
+ Detailed description of this example needs to be written. +
diff --git a/examples/projected-map.html b/examples/projected-map.html index 32f2681e9b..f3f3d6c928 100644 --- a/examples/projected-map.html +++ b/examples/projected-map.html @@ -55,6 +55,13 @@ +

Layer Projections

+ +
+
+

+ Use different (not default) projections with your map +

When using alternative projections, you still use OpenLayers.LonLat objects, even though the properties are actually X/Y values at that point.

diff --git a/examples/regular-polygons.html b/examples/regular-polygons.html index 9bf2fc40f2..f9614c0d03 100644 --- a/examples/regular-polygons.html +++ b/examples/regular-polygons.html @@ -79,7 +79,11 @@ -

OpenLayers Regular Polygon Example

+

OpenLayers Regular Polygon Example

+

+ Shows how to use the RegularPolygon handler to draw features with + different numbers of sides. +

diff --git a/examples/resize-features.html b/examples/resize-features.html index 395b833929..e3de79ba1f 100644 --- a/examples/resize-features.html +++ b/examples/resize-features.html @@ -82,6 +82,11 @@ +

Resize Features Programatically

+

+ Demonstration of how to use the geometry resize methods to + change feature sizes programatically. +

This example demonstrates how features can be resized. There is not yet a control built that provides a tool for resizing, but the geometry.resize diff --git a/examples/restricted-extent.html b/examples/restricted-extent.html index 2f8dc0d6ef..a77293961a 100644 --- a/examples/restricted-extent.html +++ b/examples/restricted-extent.html @@ -1,5 +1,6 @@ + OpenLayers Restricted Extent Example @@ -46,10 +36,17 @@ +

TileCache Example

+ +
+ +

+ Demonstrates a TileCache layer that loads tiles from from a web accessible disk-based cache only. +

+
-
- OpenLayers (Read-Only) TileCache Example -
from a web accessible disk-based cache only + +
diff --git a/examples/tms.html b/examples/tms.html index 6215209e14..a6605a868d 100644 --- a/examples/tms.html +++ b/examples/tms.html @@ -1,5 +1,6 @@ + OpenLayers Tiled Map Service Example @@ -13,7 +13,7 @@ function init(){ map = new OpenLayers.Map('map'); - var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", + var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} ); map.addLayer(layer); @@ -24,14 +24,14 @@ var layer_style = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style['default']); layer_style.fillOpacity = 0.2; layer_style.graphicOpacity = 1; - + /* * Blue style */ var style_blue = OpenLayers.Util.extend({}, layer_style); - style_blue.strokeColor = "blue"; - style_blue.fillColor = "blue"; - + style_blue.strokeColor = "blue"; + style_blue.fillColor = "blue"; + /* * Green style */ @@ -61,9 +61,9 @@ style_mark.graphicXOffset = -(style_mark.graphicWidth/2); // this is the default value style_mark.graphicYOffset = -style_mark.graphicHeight; style_mark.externalGraphic = "../img/marker.png"; - + var vectorLayer = new OpenLayers.Layer.Vector("Simple Geometry", {style: layer_style}); - + // create a point feature var point = new OpenLayers.Geometry.Point(-111.04, 45.68); var pointFeature = new OpenLayers.Feature.Vector(point,null,style_blue); @@ -71,7 +71,7 @@ var pointFeature2 = new OpenLayers.Feature.Vector(point2,null,style_green); var point3 = new OpenLayers.Geometry.Point(-105.04, 49.68); var pointFeature3 = new OpenLayers.Feature.Vector(point3,null,style_mark); - + // create a line feature from a list of points var pointList = []; var newPoint = point; @@ -82,7 +82,7 @@ } var lineFeature = new OpenLayers.Feature.Vector( new OpenLayers.Geometry.LineString(pointList),null,style_green); - + // create a polygon feature from a linear ring of points var pointList = []; for(var p=0; p<6; ++p) { @@ -93,12 +93,12 @@ pointList.push(newPoint); } pointList.push(pointList[0]); - + var linearRing = new OpenLayers.Geometry.LinearRing(pointList); var polygonFeature = new OpenLayers.Feature.Vector( new OpenLayers.Geometry.Polygon([linearRing])); - - + + map.addLayer(vectorLayer); map.setCenter(new OpenLayers.LonLat(point.x, point.y), 5); vectorLayer.addFeatures([pointFeature, pointFeature3, pointFeature2, lineFeature, polygonFeature]); @@ -106,10 +106,20 @@ -
+

Drawing Simple Vector Features Example

+ +
+
+

+ Shows the use of the shows drawing simple vector features, in different styles. +

+ +
+ +

This example shows drawing simple vector features -- point, line, polygon in different styles, created 'manually', by constructing the entire style - object, via 'copy', extending the default style object, and by + object, via 'copy', extending the default style object, and by inheriting the default style from the layer.

It also shows how to use external graphic files for point features and how to set their size: If either graphicWidth or graphicHeight is set, @@ -118,5 +128,8 @@ and graphicHeight are omitted, pointRadius will be used to set the size of the image, which will then be twice the value of pointRadius with the original aspect ratio.

+
+ + diff --git a/examples/vector-formats.html b/examples/vector-formats.html index 8890c81efd..3f3d607af4 100644 --- a/examples/vector-formats.html +++ b/examples/vector-formats.html @@ -19,15 +19,15 @@ top: 0; left: 1em; padding: 0; - width: 455px; + width: 517px; } #map { - width: 450px; + width: 512px; height: 225px; border: 1px solid #ccc; } #input { - width: 450px; + width: 512px; } #text { font-size: 0.85em; @@ -38,12 +38,12 @@ #info { position: relative; padding: 2em 0; - margin-left: 470px; + margin-left: 540px; } #output { font-size: 0.8em; width: 100%; - height: 500px; + height: 512px; border: 0; } p { @@ -57,15 +57,15 @@ var map, vectors, formats; function init(){ map = new OpenLayers.Map('map'); - var wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", - "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}); + var wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", + "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}); vectors = new OpenLayers.Layer.Vector("Vector Layer"); map.addLayers([wms, vectors]); map.addControl(new OpenLayers.Control.MousePosition()); map.addControl(new OpenLayers.Control.EditingToolbar(vectors)); - + var options = { hover: true, onSelect: serialize @@ -73,7 +73,7 @@ var select = new OpenLayers.Control.SelectFeature(vectors, options); map.addControl(select); select.activate(); - + formats = { wkt: new OpenLayers.Format.WKT(), geojson: new OpenLayers.Format.GeoJSON(), @@ -81,10 +81,10 @@ gml: new OpenLayers.Format.GML(), kml: new OpenLayers.Format.KML() }; - + map.setCenter(new OpenLayers.LonLat(0, 0), 1); } - + function serialize(feature) { var type = document.getElementById("formatType").value; // second argument for pretty printing (geojson only) @@ -110,7 +110,7 @@ } else { bounds.extend(features[i].geometry.getBounds()); } - + } vectors.addFeatures(features); map.zoomToExtent(bounds); @@ -138,7 +138,14 @@
-

OpenLayers Vector Formats Example

+

Vector Formats Example

+ +
+
+

+ Shows the wide variety of vector formats that open layers supports. +

+

Use the drop-down below to select the input/output format @@ -161,6 +168,10 @@

OpenLayers Vector Formats Example


+ +
+
+

Use the tools to the left to draw new polygons, lines, and points. @@ -168,5 +179,6 @@

OpenLayers Vector Formats Example

serialized version below.

+ diff --git a/examples/wfs-scribble.html b/examples/wfs-scribble.html index 7afc6d672d..8f5e1a080b 100644 --- a/examples/wfs-scribble.html +++ b/examples/wfs-scribble.html @@ -2,8 +2,8 @@ @@ -14,44 +14,44 @@ function init(){ map = new OpenLayers.Map('map', {controls: [ new OpenLayers.Control.PanZoom(), new OpenLayers.Control.Permalink() ]} ); - layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", - "http://labs.metacarta.com/wms-c/Basic.py", + layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", + "http://labs.metacarta.com/wms-c/Basic.py", {layers: 'basic'} ); map.addLayer(layer); - - layer = new OpenLayers.Layer.WFS( "Scribble WFS", - "/geoserver/wfs", - { typename: 'topp:line' }, - { - typename: 'line', - featureNS: 'http://www.openplans.org/topp', + + layer = new OpenLayers.Layer.WFS( "Scribble WFS", + "/geoserver/wfs", + { typename: 'topp:line' }, + { + typename: 'line', + featureNS: 'http://www.openplans.org/topp', extractAttributes: false } ); map.addLayer(layer); - + var p = new OpenLayers.Control.Panel({'displayClass': 'olControlEditingToolbar'}); - + df = new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, {handlerOptions: {'freehand': false}, 'displayClass': 'olControlDrawFeaturePath'}); - df.featureAdded = function(feature) { - feature.state = OpenLayers.State.INSERT; - feature.style['strokeColor'] = "#ff0000"; - feature.layer.drawFeature(feature); + df.featureAdded = function(feature) { + feature.state = OpenLayers.State.INSERT; + feature.style['strokeColor'] = "#ff0000"; + feature.layer.drawFeature(feature); } p.addControls([ new OpenLayers.Control.Navigation(), df ]); - + map.addControl(p); p.activateControl(p.controls[0]) map.setCenter(new OpenLayers.LonLat(0,0), 3); } function save() { - for(var i = 0; i < map.layers[1].features.length; i++) { - var f = map.layers[1].features[i]; - f.style['strokeColor'] = '#ee9900'; - map.layers[1].drawFeature(f); - } + for(var i = 0; i < map.layers[1].features.length; i++) { + var f = map.layers[1].features[i]; + f.style['strokeColor'] = '#ee9900'; + map.layers[1].drawFeature(f); + } map.layers[1].commit(); return false; - } + } function serialize(type) { var xmls = new XMLSerializer(); var serialize = new OpenLayers.Format[type]({},map.layers[1]); @@ -62,18 +62,29 @@ -

Draw Lines, Save to GeoServer

-

Using GeoServer and the WFS-T support in OpenLayers, draw on a map, - save the results, reload the page and see the results still there!
- Hold shift to turn on freehand mode while drawing.

-
- Show WFS Transaction | - Export GML | - Export GeoRSS | - Save | +

Scribble on a WFS Example

+ +
+
+

+ Shows how you can draw features and save to GeoServer. +

+
+ +
+ Using GeoServer and the WFS-T support in OpenLayers, draw on a map, save the results, reload the page and see the results still there! + Hold shift to turn on freehand mode while drawing.
+ +
+ Show WFS Transaction | + Export GML | + Export GeoRSS | + Save | Refresh (removes all newly added lines)

-
+
+ + diff --git a/examples/wfs-states.html b/examples/wfs-states.html index e2b79d6a1f..b5c323f5d5 100644 --- a/examples/wfs-states.html +++ b/examples/wfs-states.html @@ -2,8 +2,8 @@ @@ -13,24 +13,39 @@ var map, layer; function init(){ - OpenLayers.ProxyHost="/cgi-bin/proxy.cgi?url="; + OpenLayers.ProxyHost="/cgi-bin/proxy.cgi?url="; map = new OpenLayers.Map('map', {controls: [ new OpenLayers.Control.PanZoom(), new OpenLayers.Control.Permalink(), new OpenLayers.Control.MouseDefaults() ]} ); - layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", - "http://labs.metacarta.com/wms-c/Basic.py", + layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", + "http://labs.metacarta.com/wms-c/Basic.py", {layers: 'basic'} ); map.addLayer(layer); - - layer = new OpenLayers.Layer.WFS( "States WFS", - "/geoserver/wfs", + + layer = new OpenLayers.Layer.WFS( "States WFS", + "/geoserver/wfs", { typename: 'topp:states' } ); map.addLayer(layer); - + map.zoomToExtent(new OpenLayers.Bounds(-140.444336,25.115234,-44.438477,50.580078)); } -

GeoServer WFS

-
+

WFS United States (GeoServer) Example

+ +
+
+ +

+ Shows the use of the WFS United States (GeoServer) +

+ +
+ +
see: + See WFS for more info on Geoserver and WFS
+ + + +
diff --git a/examples/wfs-t.html b/examples/wfs-t.html index 44796da9f3..eb6fdd2168 100644 --- a/examples/wfs-t.html +++ b/examples/wfs-t.html @@ -2,8 +2,8 @@ @@ -13,57 +13,75 @@ function init(){ map = new OpenLayers.Map('map'); - layer = new OpenLayers.Layer.WMS( "State", + layer = new OpenLayers.Layer.WMS( "State", "http://dev.openlayers.org/geoserver/wms", {layers: 'topp:tasmania_state_boundaries'} ); map.addLayer(layer); - layer = new OpenLayers.Layer.WMS( "Water", + layer = new OpenLayers.Layer.WMS( "Water", "http://dev.openlayers.org/geoserver/wms", {layers: 'topp:tasmania_water_bodies', 'transparent': true, format: 'image/gif' } ); map.addLayer(layer); - rlayer = new OpenLayers.Layer.WFS( "Roads", + rlayer = new OpenLayers.Layer.WFS( "Roads", "http://dev.openlayers.org/geoserver/wfs", {typename: 'topp:tasmania_roads'}, - { - typename: 'tasmania_roads', - featureNS: 'http://www.openplans.org/topp', + { + typename: 'tasmania_roads', + featureNS: 'http://www.openplans.org/topp', extractAttributes: false } ); rlayer.onFeatureInsert=function(feature) { feature.style.strokeColor = "#ff0000"; feature.layer.drawFeature(feature); } map.addLayer(rlayer); - layer = new OpenLayers.Layer.WFS( "Cities", - "http://dev.openlayers.org/geoserver/wfs", {typename: 'topp:tasmania_cities'}, - { - typename: 'tasmania_cities', - featureNS: 'http://www.openplans.org/topp', + layer = new OpenLayers.Layer.WFS( "Cities", + "http://dev.openlayers.org/geoserver/wfs", {typename: 'topp:tasmania_cities'}, + { + typename: 'tasmania_cities', + featureNS: 'http://www.openplans.org/topp', extractAttributes: false } ); map.addLayer(layer); map.addControl(new OpenLayers.Control.LayerSwitcher()); var p = new OpenLayers.Control.Panel({'displayClass': 'olControlEditingToolbar'}); - + df = new OpenLayers.Control.DrawFeature(rlayer, OpenLayers.Handler.Path, {handlerOptions: {'freehand': false}, 'displayClass': 'olControlDrawFeaturePath'}); - df.featureAdded = function(feature) { - feature.state = OpenLayers.State.INSERT; - feature.style['strokeColor'] = "#0000ff"; - feature.layer.drawFeature(feature); + df.featureAdded = function(feature) { + feature.state = OpenLayers.State.INSERT; + feature.style['strokeColor'] = "#0000ff"; + feature.layer.drawFeature(feature); } dp = new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, {handlerOptions: {'freehand': false}, 'displayClass': 'olControlDrawFeaturePoint'}); - dp.featureAdded = function(feature) { + dp.featureAdded = function(feature) { var oldgeom = feature.geometry; feature.layer.renderer.eraseGeometry(oldgeom); feature.geometry = new OpenLayers.Geometry.MultiPoint(oldgeom); - feature.state = OpenLayers.State.INSERT; - feature.style['strokeColor'] = "#0000ff"; - feature.layer.drawFeature(feature); + feature.state = OpenLayers.State.INSERT; + feature.style['strokeColor'] = "#0000ff"; + feature.layer.drawFeature(feature); } p.addControls([ new OpenLayers.Control.Navigation(), df, dp ]); - + map.addControl(p); map.zoomToExtent(new OpenLayers.Bounds(145.51045,-44.0,149.0,-40.5)); } - Save Roads
- Save Cities
-
+ +

WFS Example

+ +
+
+

+ Shows the use of the WFS layer type. +

+ + Save Roads
+ Save Cities
+
+ +
+ This is an example of using a WFS layer type. +
+ + + + + diff --git a/examples/wfs.html b/examples/wfs.html index 9c88880e35..742b7df472 100644 --- a/examples/wfs.html +++ b/examples/wfs.html @@ -29,7 +29,11 @@ -

OpenLayers Example

+

WFS Points

+

+ Using a Layer.WFS with a featureClass, one can take in XML data + from a WFS class and display it any way you like. +

diff --git a/examples/wkt.html b/examples/wkt.html index f2bed8219a..7d8d1a926e 100644 --- a/examples/wkt.html +++ b/examples/wkt.html @@ -10,7 +10,7 @@ } #map { width: 512px; - height: 350px; + height: 512px; border: 1px solid gray; } #controls { @@ -31,15 +31,15 @@ var map, vectors, drawControls, wkt; function init(){ map = new OpenLayers.Map('map'); - var wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", - "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}); + var wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", + "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}); vectors = new OpenLayers.Layer.Vector("Vector Layer"); map.addLayers([wms, vectors]); map.addControl(new OpenLayers.Control.LayerSwitcher()); map.addControl(new OpenLayers.Control.MousePosition()); - + var options = { hover: true, onSelect: displayWKT @@ -54,13 +54,13 @@ OpenLayers.Handler.Polygon), hover: new OpenLayers.Control.SelectFeature(vectors, options) }; - + for(var key in drawControls) { map.addControl(drawControls[key]); } - + wkt = new OpenLayers.Format.WKT(); - + map.setCenter(new OpenLayers.LonLat(0, 0), 3); document.getElementById('noneToggle').checked = true; } @@ -75,7 +75,7 @@ } } } - + function displayWKT(feature) { var str = wkt.write(feature); // not a good idea in general, just for this demo @@ -97,7 +97,7 @@ } else { bounds.extend(features[i].geometry.getBounds()); } - + } vectors.addFeatures(features); map.zoomToExtent(bounds); @@ -110,10 +110,18 @@ -

OpenLayers WKT Example

+

WKT Example

+

+ Shows the use of WKT (Well known text) to draw features in openlayers +

-
+
+ + + +
+

See Wikipedia for a description and examples of WKT.

@@ -145,6 +153,7 @@

OpenLayers WKT Example

+
diff --git a/examples/wms-untiled.html b/examples/wms-untiled.html index bb43316476..796cad25a1 100644 --- a/examples/wms-untiled.html +++ b/examples/wms-untiled.html @@ -2,8 +2,8 @@ @@ -16,7 +16,7 @@ function init(){ map = new OpenLayers.Map( 'map' ); - layer = new OpenLayers.Layer.WMS.Untiled( "OpenLayers WMS", + layer = new OpenLayers.Layer.WMS.Untiled( "OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} ); map.addLayer(layer); @@ -26,6 +26,20 @@ +

WMS Untiled Example

+ +
+
+

+ Shows an example of the Untiled wms layer, which requests a single image for the entire map view. +

+
+ +
+ An untiled map will only request a single image at a time. +
+ + diff --git a/examples/wms.html b/examples/wms.html index b29e51261c..ca58081617 100644 --- a/examples/wms.html +++ b/examples/wms.html @@ -2,8 +2,8 @@ @@ -16,7 +16,7 @@ function init(){ map = new OpenLayers.Map( 'map' ); - layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", + layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} ); map.addLayer(layer); @@ -26,6 +26,23 @@ -
+

WMS Example

+ +
+
+

+ Shows the basic use of openlayers using a WMS layer +

+ +
+ +
+ This is an example of how to add an WMS layer to the OpenLayers window. The images are tiled in this instance if you wanted to not use a tiled WMS + please use this example and pass the option ‘singleTile’ as true. +
+ + + + diff --git a/examples/wmst.html b/examples/wmst.html index a1740c55b7..835d2529f6 100644 --- a/examples/wmst.html +++ b/examples/wmst.html @@ -3,7 +3,7 @@ @@ -13,11 +13,11 @@ function init(){ map = new OpenLayers.Map('map'); - var ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", + var ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'} ); var jpl_wms = new OpenLayers.Layer.WMS( "NASA Global Mosaic", - "http://t1.hypercube.telascience.org/tiles/landsat7", + "http://t1.hypercube.telascience.org/cgi-bin/landsat7", {layers: "landsat7"}); ia_wms = new OpenLayers.Layer.WMS("Nexrad","http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi?",{layers:"nexrad-n0r-wmst",transparent:true,format:'image/png',time:"2005-08-29T13:00:00Z"}); @@ -29,22 +29,31 @@ map.zoomToExtent(new OpenLayers.Bounds(-100.898437,22.148438,-78.398437,39.726563)); } function update_date() { - var string = OpenLayers.Util.getElement('year').value + "-" + - OpenLayers.Util.getElement('month').value + "-" + - OpenLayers.Util.getElement('day').value + "T" + - OpenLayers.Util.getElement('hour').value + ":" + + var string = OpenLayers.Util.getElement('year').value + "-" + + OpenLayers.Util.getElement('month').value + "-" + + OpenLayers.Util.getElement('day').value + "T" + + OpenLayers.Util.getElement('hour').value + ":" + OpenLayers.Util.getElement('minute').value + ":00"; - ia_wms.mergeNewParams({'time':string}); - - } + ia_wms.mergeNewParams({'time':string}); + + } -

OpenLayers Example

-

WMS-T example: update the times, and the radar image will change. Uses Layer.mergeNewParams to update the date element with the strings from the input fields every time one of them is changed.

-

The inputs below describe a timestamp: The minute increments can only be updated in units of 5.

+

WMS Time Example

+
+
+

+ Shows the use of the layer WMS-T (time) layer +

--T::00
+
+ WMS-T example: update the times, and the radar image will change. Uses Layer.mergeNewParams to update the date element with the strings from the input fields every time one of them is changed. + The inputs below describe a timestamp: The minute increments can only be updated in units of 5. +
+ + diff --git a/examples/worldwind.html b/examples/worldwind.html index 88b3889946..ecd698a601 100644 --- a/examples/worldwind.html +++ b/examples/worldwind.html @@ -2,8 +2,8 @@ @@ -13,13 +13,13 @@ function init(){ map = new OpenLayers.Map('map', {'maxResolution': .28125, tileSize: new OpenLayers.Size(512, 512)}); - ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", + ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'} ); - ww = new OpenLayers.Layer.WorldWind( "Bathy", + ww = new OpenLayers.Layer.WorldWind( "Bathy", "http://worldwind25.arc.nasa.gov/tile/tile.aspx?", 36, 4, {T:"bmng.topo.bathy.200406"}); - ww2 = new OpenLayers.Layer.WorldWind( "LANDSAT", + ww2 = new OpenLayers.Layer.WorldWind( "LANDSAT", "http://worldwind25.arc.nasa.gov/tile/tile.aspx", 2.25, 4, {T:"105"}); @@ -32,9 +32,21 @@ -

OpenLayers Example

-
-

This is a demonstration of using Tiled WorldWind layers. WorldWind requires you to define a "LZTD" -- the 3rd param of the constructor -- and the number of zoom levels it supports. When a worldwind layer is not visible at a given tile level, and empty tile is placed there instead. Note that the maxResolution of the map times 512px, must be a multiple of a power of two different from the LZTD -- in this case, .28125 * 512 is 144, which is 36*4, and 2.25*64.

-

This example has a 'Bathy' layer, visible as you zoom out, and a 'landsat' layer, visible as you zoom in, both visible at zoom level 6.

- +

WorldWind layers Example

+ +
+
+

+ Shows the use of the Tiled WorldWind layers. +

+ +
+ +
+ This is a demonstration of using Tiled WorldWind layers. WorldWind requires you to define a "LZTD" -- the 3rd param of the constructor -- and the number of zoom levels it supports. When a worldwind layer is not visible at a given tile level, and empty tile is placed there instead. Note that the maxResolution of the map times 512px, must be a multiple of a power of two different from the LZTD -- in this case, .28125 * 512 is 144, which is 36*4, and 2.25*64. + This example has a 'Bathy' layer, visible as you zoom out, and a 'landsat' layer, visible as you zoom in, both visible at zoom level 6. +
+ + + diff --git a/examples/wrapDateLine.html b/examples/wrapDateLine.html index 49aba45023..6f52fbcfb0 100644 --- a/examples/wrapDateLine.html +++ b/examples/wrapDateLine.html @@ -2,8 +2,8 @@ @@ -12,11 +12,11 @@ var map; function init(){ map = new OpenLayers.Map( 'map', {maxResolution: 1.40625} ); - var mapserv = new OpenLayers.Layer.MapServer( "OpenLayers Basic", + var mapserv = new OpenLayers.Layer.MapServer( "OpenLayers Basic", "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'}, {wrapDateLine: true} ); - + var kamap = new OpenLayers.Layer.KaMap( "Blue Marble NG", "http://www.openlayers.org/world/index.php", {g: "satellite", map: "world"}, @@ -30,7 +30,7 @@ {wrapDateLine: true, reproject: false}); /* TMS is broken, too */ - tms = new OpenLayers.Layer.TMS( "OpenStreetMap", + tms = new OpenLayers.Layer.TMS( "OpenStreetMap", "http://labs.metacarta.com/wms-c/Basic.py/", {layername: 'osm-map', type:'png', wrapDateLine: true} ); @@ -41,7 +41,7 @@ {'maxResolution': .28125, tileSize: new OpenLayers.Size(512, 512), wrapDateLine: true}); - + map.addLayers([mapserv, kamap, wms]); map.addControl(new OpenLayers.Control.LayerSwitcher()); map.addControl(new OpenLayers.Control.MousePosition()); @@ -52,28 +52,28 @@

Wrapping the Date Line

- Related to: - Layer.WMS, - Layer.MapServer, + Related to: + Layer.WMS, + Layer.MapServer, wrapDateLine -
- -

This example shows the use of the wrapDateLine layer option on a number of layer types.

+
+ +

Shows how to work around dateline issues, by wrapping the dateline on a number of layer types.

- This is an example that shows wrapping the date line. Wrapping the - date line is an option on the layer. + This is an example that shows wrapping the date line. Wrapping the + date line is an option on the layer.

You can do it with a 'Layer.WMS' or a 'Layer.MapServer' layer.

-    var mapserv = new OpenLayers.Layer.MapServer( "OpenLayers Basic", 
+    var mapserv = new OpenLayers.Layer.MapServer( "OpenLayers Basic",
                 "http://labs.metacarta.com/wms/vmap0",
                 {layers: 'basic'},
                 {wrapDateLine: true} );
-    
+
diff --git a/examples/xhtml.html b/examples/xhtml.html index a6fc48f9e5..7033aa4782 100644 --- a/examples/xhtml.html +++ b/examples/xhtml.html @@ -6,18 +6,31 @@ -
+

XHTML Example

+ +
+
+

+ Shows openlayers runnig in a XHTML 1.0 Strict Doctype +

+ +
-

- Valid XHTML 1.0! -

+
This example shows openlayers using a XHTML 1.0 Strict Doctype click on the below image to validate. +

+ Valid XHTML 1.0! +

+
+ + + diff --git a/examples/xml.html b/examples/xml.html index 4260535264..a70a841d12 100644 --- a/examples/xml.html +++ b/examples/xml.html @@ -28,21 +28,21 @@ ul li { padding-left: 0; } - + -

OpenLayers XML Example

-

OpenLayers has a very simple XML format class (OpenLayers.Format.XML) - that can be used to read/write XML docs. The methods available on the - XML format (or parser if you like) allow for reading and writing of the - various XML flavors used by the library - in particular the vector data - formats. It is by no means intended to be a full-fledged XML toolset. - Additional methods will be added only as needed elsewhere in the - library.

-

This page loads an XML document and demonstrates a few of the methods - available in the parser.

-

Status: XML document loading...

-

After the XML document loads, see the result of a few of the methods - below. Assume that you start with the following code: -
- - var format = new OpenLayers.Format.XML(); - -

- Sample methods - - Output: -
 
+

XML Format Example

+ +
+
+ +

+ Shows the use of the OpenLayers XML format class +

+ +
+

OpenLayers has a very simple XML format class (OpenLayers.Format.XML) + that can be used to read/write XML docs. The methods available on the + XML format (or parser if you like) allow for reading and writing of the + various XML flavors used by the library - in particular the vector data + formats. It is by no means intended to be a full-fledged XML toolset. + Additional methods will be added only as needed elsewhere in the + library.

+

This page loads an XML document and demonstrates a few of the methods + available in the parser.

+

Status: XML document loading...

+

After the XML document loads, see the result of a few of the methods + below. Assume that you start with the following code: +
+ + var format = new OpenLayers.Format.XML(); + +

+ Sample methods + + Output: +
 
+
+ + diff --git a/examples/yahoo.html b/examples/yahoo.html index f39206b2f1..d4c6c8ebb1 100644 --- a/examples/yahoo.html +++ b/examples/yahoo.html @@ -15,22 +15,36 @@ function init(){ map = new OpenLayers.Map('map'); - yahooLayer = new OpenLayers.Layer.Yahoo( "Yahoo"); + yahooLayer = new OpenLayers.Layer.Yahoo( "Yahoo"); map.addLayer(yahooLayer); - layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", + layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} ); map.addLayer(layer); - map.setCenter(new OpenLayers.LonLat(-5, 40), 4); map.addControl(new OpenLayers.Control.LayerSwitcher()); } -

OpenLayers Example

-
+

Yahoo Base Layer Example

+ +
+
+ +

+ Shows how you would add a yahoo layer and add the LayerSwitcher control +

+ +
+ +
+ This is an example of how to add a yahoo layer to the OpenLayers window. In order to enable a + yahoo layer. Also shown in this example is the LayerSwitcher() control for toggling between both the yahoo layer and + the open layer WMS. +
+ diff --git a/examples/yelp-georss.xml b/examples/yelp-georss.xml index 7ecb1cf4ef..3981069809 100644 --- a/examples/yelp-georss.xml +++ b/examples/yelp-georss.xml @@ -144,4 +144,4 @@ -83.4692993164 42.3581008911 - \ No newline at end of file + diff --git a/examples/zoomLevels.html b/examples/zoomLevels.html index 0468b80e4d..527dec797a 100644 --- a/examples/zoomLevels.html +++ b/examples/zoomLevels.html @@ -2,8 +2,8 @@ @@ -15,8 +15,8 @@ var map, layer; function init(){ - - var options = { + OpenLayers.DOTS_PER_INCH = 72; + var options = { // various ways of specifying similar things // resolutions: [1.40625,0.703125,0.3515625,0.17578125,0.087890625,0.0439453125,0.02197265625,0.010986328125,0.0054931640625,0.00274658203125,0.00137329101], @@ -26,28 +26,58 @@ // maxScale: 10000000, // minScale: 50000000, // numZoomLevels: 5, +// units: "dd", minResolution: "auto", - minExtent: new OpenLayers.Bounds(-1, -1, 1, 1), + minExtent: new OpenLayers.Bounds(-1, -1, 1, 1), maxResolution: "auto", - maxExtent: new OpenLayers.Bounds(-180, -90, 180, 90), + maxExtent: new OpenLayers.Bounds(-180, -90, 180, 90), controls: [new OpenLayers.Control.MouseDefaults()] }; map = new OpenLayers.Map( 'map' , options); - + map.addControl(new OpenLayers.Control.PanZoomBar()); - layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", + layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'}); map.addLayer(layer); map.setCenter(new OpenLayers.LonLat(lon, lat), zoom); } - + +

Zoom Level

+ +
+
+ +

+ This example shows the use of the resolutions layer option on a number of layer types. +

+
+ +
+

+ Set the extent of the viewable map using preset levels of scale available + to the user via the zoom slider bar. You can set the minimum, maximum + scales or resolutions, the number of levels in between and the minimum + and maximum geographic extents in your map's units. +

+

+ Default units for Scale are in inches. Resolution is specified in map units + per pixel where the default map units are decimal degrees(dd).
+ scale = resolution * OpenLayers.INCHES_PER_UNIT[units] * + OpenLayers.DOTS_PER_INCH
+ You can either set the scale or the resolution, there is no need to set both. +

+

+ You can do it with a ... +

+
+ diff --git a/tools/BeautifulSoup.py b/tools/BeautifulSoup.py new file mode 100644 index 0000000000..6ef8ac026a --- /dev/null +++ b/tools/BeautifulSoup.py @@ -0,0 +1,1767 @@ +"""Beautiful Soup +Elixir and Tonic +"The Screen-Scraper's Friend" +http://www.crummy.com/software/BeautifulSoup/ + +Beautiful Soup parses a (possibly invalid) XML or HTML document into a +tree representation. It provides methods and Pythonic idioms that make +it easy to navigate, search, and modify the tree. + +A well-formed XML/HTML document yields a well-formed data +structure. An ill-formed XML/HTML document yields a correspondingly +ill-formed data structure. If your document is only locally +well-formed, you can use this library to find and process the +well-formed part of it. The BeautifulSoup class + +Beautiful Soup works with Python 2.2 and up. It has no external +dependencies, but you'll have more success at converting data to UTF-8 +if you also install these three packages: + +* chardet, for auto-detecting character encodings + http://chardet.feedparser.org/ +* cjkcodecs and iconv_codec, which add more encodings to the ones supported + by stock Python. + http://cjkpython.i18n.org/ + +Beautiful Soup defines classes for two main parsing strategies: + + * BeautifulStoneSoup, for parsing XML, SGML, or your domain-specific + language that kind of looks like XML. + + * BeautifulSoup, for parsing run-of-the-mill HTML code, be it valid + or invalid. This class has web browser-like heuristics for + obtaining a sensible parse tree in the face of common HTML errors. + +Beautiful Soup also defines a class (UnicodeDammit) for autodetecting +the encoding of an HTML or XML document, and converting it to +Unicode. Much of this code is taken from Mark Pilgrim's Universal Feed Parser. + +For more than you ever wanted to know about Beautiful Soup, see the +documentation: +http://www.crummy.com/software/BeautifulSoup/documentation.html + +""" +from __future__ import generators + +__author__ = "Leonard Richardson (leonardr@segfault.org)" +__version__ = "3.0.4" +__copyright__ = "Copyright (c) 2004-2007 Leonard Richardson" +__license__ = "PSF" + +from sgmllib import SGMLParser, SGMLParseError +import codecs +import types +import re +import sgmllib +try: + from htmlentitydefs import name2codepoint +except ImportError: + name2codepoint = {} + +#This hack makes Beautiful Soup able to parse XML with namespaces +sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*') + +DEFAULT_OUTPUT_ENCODING = "utf-8" + +# First, the classes that represent markup elements. + +class PageElement: + """Contains the navigational information for some part of the page + (either a tag or a piece of text)""" + + def setup(self, parent=None, previous=None): + """Sets up the initial relations between this element and + other elements.""" + self.parent = parent + self.previous = previous + self.next = None + self.previousSibling = None + self.nextSibling = None + if self.parent and self.parent.contents: + self.previousSibling = self.parent.contents[-1] + self.previousSibling.nextSibling = self + + def replaceWith(self, replaceWith): + oldParent = self.parent + myIndex = self.parent.contents.index(self) + if hasattr(replaceWith, 'parent') and replaceWith.parent == self.parent: + # We're replacing this element with one of its siblings. + index = self.parent.contents.index(replaceWith) + if index and index < myIndex: + # Furthermore, it comes before this element. That + # means that when we extract it, the index of this + # element will change. + myIndex = myIndex - 1 + self.extract() + oldParent.insert(myIndex, replaceWith) + + def extract(self): + """Destructively rips this element out of the tree.""" + if self.parent: + try: + self.parent.contents.remove(self) + except ValueError: + pass + + #Find the two elements that would be next to each other if + #this element (and any children) hadn't been parsed. Connect + #the two. + lastChild = self._lastRecursiveChild() + nextElement = lastChild.next + + if self.previous: + self.previous.next = nextElement + if nextElement: + nextElement.previous = self.previous + self.previous = None + lastChild.next = None + + self.parent = None + if self.previousSibling: + self.previousSibling.nextSibling = self.nextSibling + if self.nextSibling: + self.nextSibling.previousSibling = self.previousSibling + self.previousSibling = self.nextSibling = None + + def _lastRecursiveChild(self): + "Finds the last element beneath this object to be parsed." + lastChild = self + while hasattr(lastChild, 'contents') and lastChild.contents: + lastChild = lastChild.contents[-1] + return lastChild + + def insert(self, position, newChild): + if (isinstance(newChild, basestring) + or isinstance(newChild, unicode)) \ + and not isinstance(newChild, NavigableString): + newChild = NavigableString(newChild) + + position = min(position, len(self.contents)) + if hasattr(newChild, 'parent') and newChild.parent != None: + # We're 'inserting' an element that's already one + # of this object's children. + if newChild.parent == self: + index = self.find(newChild) + if index and index < position: + # Furthermore we're moving it further down the + # list of this object's children. That means that + # when we extract this element, our target index + # will jump down one. + position = position - 1 + newChild.extract() + + newChild.parent = self + previousChild = None + if position == 0: + newChild.previousSibling = None + newChild.previous = self + else: + previousChild = self.contents[position-1] + newChild.previousSibling = previousChild + newChild.previousSibling.nextSibling = newChild + newChild.previous = previousChild._lastRecursiveChild() + if newChild.previous: + newChild.previous.next = newChild + + newChildsLastElement = newChild._lastRecursiveChild() + + if position >= len(self.contents): + newChild.nextSibling = None + + parent = self + parentsNextSibling = None + while not parentsNextSibling: + parentsNextSibling = parent.nextSibling + parent = parent.parent + if not parent: # This is the last element in the document. + break + if parentsNextSibling: + newChildsLastElement.next = parentsNextSibling + else: + newChildsLastElement.next = None + else: + nextChild = self.contents[position] + newChild.nextSibling = nextChild + if newChild.nextSibling: + newChild.nextSibling.previousSibling = newChild + newChildsLastElement.next = nextChild + + if newChildsLastElement.next: + newChildsLastElement.next.previous = newChildsLastElement + self.contents.insert(position, newChild) + + def findNext(self, name=None, attrs={}, text=None, **kwargs): + """Returns the first item that matches the given criteria and + appears after this Tag in the document.""" + return self._findOne(self.findAllNext, name, attrs, text, **kwargs) + + def findAllNext(self, name=None, attrs={}, text=None, limit=None, + **kwargs): + """Returns all items that match the given criteria and appear + before after Tag in the document.""" + return self._findAll(name, attrs, text, limit, self.nextGenerator) + + def findNextSibling(self, name=None, attrs={}, text=None, **kwargs): + """Returns the closest sibling to this Tag that matches the + given criteria and appears after this Tag in the document.""" + return self._findOne(self.findNextSiblings, name, attrs, text, + **kwargs) + + def findNextSiblings(self, name=None, attrs={}, text=None, limit=None, + **kwargs): + """Returns the siblings of this Tag that match the given + criteria and appear after this Tag in the document.""" + return self._findAll(name, attrs, text, limit, + self.nextSiblingGenerator, **kwargs) + fetchNextSiblings = findNextSiblings # Compatibility with pre-3.x + + def findPrevious(self, name=None, attrs={}, text=None, **kwargs): + """Returns the first item that matches the given criteria and + appears before this Tag in the document.""" + return self._findOne(self.findAllPrevious, name, attrs, text, **kwargs) + + def findAllPrevious(self, name=None, attrs={}, text=None, limit=None, + **kwargs): + """Returns all items that match the given criteria and appear + before this Tag in the document.""" + return self._findAll(name, attrs, text, limit, self.previousGenerator, + **kwargs) + fetchPrevious = findAllPrevious # Compatibility with pre-3.x + + def findPreviousSibling(self, name=None, attrs={}, text=None, **kwargs): + """Returns the closest sibling to this Tag that matches the + given criteria and appears before this Tag in the document.""" + return self._findOne(self.findPreviousSiblings, name, attrs, text, + **kwargs) + + def findPreviousSiblings(self, name=None, attrs={}, text=None, + limit=None, **kwargs): + """Returns the siblings of this Tag that match the given + criteria and appear before this Tag in the document.""" + return self._findAll(name, attrs, text, limit, + self.previousSiblingGenerator, **kwargs) + fetchPreviousSiblings = findPreviousSiblings # Compatibility with pre-3.x + + def findParent(self, name=None, attrs={}, **kwargs): + """Returns the closest parent of this Tag that matches the given + criteria.""" + # NOTE: We can't use _findOne because findParents takes a different + # set of arguments. + r = None + l = self.findParents(name, attrs, 1) + if l: + r = l[0] + return r + + def findParents(self, name=None, attrs={}, limit=None, **kwargs): + """Returns the parents of this Tag that match the given + criteria.""" + + return self._findAll(name, attrs, None, limit, self.parentGenerator, + **kwargs) + fetchParents = findParents # Compatibility with pre-3.x + + #These methods do the real heavy lifting. + + def _findOne(self, method, name, attrs, text, **kwargs): + r = None + l = method(name, attrs, text, 1, **kwargs) + if l: + r = l[0] + return r + + def _findAll(self, name, attrs, text, limit, generator, **kwargs): + "Iterates over a generator looking for things that match." + + if isinstance(name, SoupStrainer): + strainer = name + else: + # Build a SoupStrainer + strainer = SoupStrainer(name, attrs, text, **kwargs) + results = ResultSet(strainer) + g = generator() + while True: + try: + i = g.next() + except StopIteration: + break + if i: + found = strainer.search(i) + if found: + results.append(found) + if limit and len(results) >= limit: + break + return results + + #These Generators can be used to navigate starting from both + #NavigableStrings and Tags. + def nextGenerator(self): + i = self + while i: + i = i.next + yield i + + def nextSiblingGenerator(self): + i = self + while i: + i = i.nextSibling + yield i + + def previousGenerator(self): + i = self + while i: + i = i.previous + yield i + + def previousSiblingGenerator(self): + i = self + while i: + i = i.previousSibling + yield i + + def parentGenerator(self): + i = self + while i: + i = i.parent + yield i + + # Utility methods + def substituteEncoding(self, str, encoding=None): + encoding = encoding or "utf-8" + return str.replace("%SOUP-ENCODING%", encoding) + + def toEncoding(self, s, encoding=None): + """Encodes an object to a string in some encoding, or to Unicode. + .""" + if isinstance(s, unicode): + if encoding: + s = s.encode(encoding) + elif isinstance(s, str): + if encoding: + s = s.encode(encoding) + else: + s = unicode(s) + else: + if encoding: + s = self.toEncoding(str(s), encoding) + else: + s = unicode(s) + return s + +class NavigableString(unicode, PageElement): + + def __getattr__(self, attr): + """text.string gives you text. This is for backwards + compatibility for Navigable*String, but for CData* it lets you + get the string without the CData wrapper.""" + if attr == 'string': + return self + else: + raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__.__name__, attr) + + def __unicode__(self): + return self.__str__(None) + + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING): + if encoding: + return self.encode(encoding) + else: + return self + +class CData(NavigableString): + + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING): + return "" % NavigableString.__str__(self, encoding) + +class ProcessingInstruction(NavigableString): + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING): + output = self + if "%SOUP-ENCODING%" in output: + output = self.substituteEncoding(output, encoding) + return "" % self.toEncoding(output, encoding) + +class Comment(NavigableString): + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING): + return "" % NavigableString.__str__(self, encoding) + +class Declaration(NavigableString): + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING): + return "" % NavigableString.__str__(self, encoding) + +class Tag(PageElement): + + """Represents a found HTML tag with its attributes and contents.""" + + XML_SPECIAL_CHARS_TO_ENTITIES = { "'" : "squot", + '"' : "quote", + "&" : "amp", + "<" : "lt", + ">" : "gt" } + + def __init__(self, parser, name, attrs=None, parent=None, + previous=None): + "Basic constructor." + + # We don't actually store the parser object: that lets extracted + # chunks be garbage-collected + self.parserClass = parser.__class__ + self.isSelfClosing = parser.isSelfClosingTag(name) + self.name = name + if attrs == None: + attrs = [] + self.attrs = attrs + self.contents = [] + self.setup(parent, previous) + self.hidden = False + self.containsSubstitutions = False + + def get(self, key, default=None): + """Returns the value of the 'key' attribute for the tag, or + the value given for 'default' if it doesn't have that + attribute.""" + return self._getAttrMap().get(key, default) + + def has_key(self, key): + return self._getAttrMap().has_key(key) + + def __getitem__(self, key): + """tag[key] returns the value of the 'key' attribute for the tag, + and throws an exception if it's not there.""" + return self._getAttrMap()[key] + + def __iter__(self): + "Iterating over a tag iterates over its contents." + return iter(self.contents) + + def __len__(self): + "The length of a tag is the length of its list of contents." + return len(self.contents) + + def __contains__(self, x): + return x in self.contents + + def __nonzero__(self): + "A tag is non-None even if it has no contents." + return True + + def __setitem__(self, key, value): + """Setting tag[key] sets the value of the 'key' attribute for the + tag.""" + self._getAttrMap() + self.attrMap[key] = value + found = False + for i in range(0, len(self.attrs)): + if self.attrs[i][0] == key: + self.attrs[i] = (key, value) + found = True + if not found: + self.attrs.append((key, value)) + self._getAttrMap()[key] = value + + def __delitem__(self, key): + "Deleting tag[key] deletes all 'key' attributes for the tag." + for item in self.attrs: + if item[0] == key: + self.attrs.remove(item) + #We don't break because bad HTML can define the same + #attribute multiple times. + self._getAttrMap() + if self.attrMap.has_key(key): + del self.attrMap[key] + + def __call__(self, *args, **kwargs): + """Calling a tag like a function is the same as calling its + findAll() method. Eg. tag('a') returns a list of all the A tags + found within this tag.""" + return apply(self.findAll, args, kwargs) + + def __getattr__(self, tag): + #print "Getattr %s.%s" % (self.__class__, tag) + if len(tag) > 3 and tag.rfind('Tag') == len(tag)-3: + return self.find(tag[:-3]) + elif tag.find('__') != 0: + return self.find(tag) + + def __eq__(self, other): + """Returns true iff this tag has the same name, the same attributes, + and the same contents (recursively) as the given tag. + + NOTE: right now this will return false if two tags have the + same attributes in a different order. Should this be fixed?""" + if not hasattr(other, 'name') or not hasattr(other, 'attrs') or not hasattr(other, 'contents') or self.name != other.name or self.attrs != other.attrs or len(self) != len(other): + return False + for i in range(0, len(self.contents)): + if self.contents[i] != other.contents[i]: + return False + return True + + def __ne__(self, other): + """Returns true iff this tag is not identical to the other tag, + as defined in __eq__.""" + return not self == other + + def __repr__(self, encoding=DEFAULT_OUTPUT_ENCODING): + """Renders this tag as a string.""" + return self.__str__(encoding) + + def __unicode__(self): + return self.__str__(None) + + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING, + prettyPrint=False, indentLevel=0): + """Returns a string or Unicode representation of this tag and + its contents. To get Unicode, pass None for encoding. + + NOTE: since Python's HTML parser consumes whitespace, this + method is not certain to reproduce the whitespace present in + the original string.""" + + encodedName = self.toEncoding(self.name, encoding) + + attrs = [] + if self.attrs: + for key, val in self.attrs: + fmt = '%s="%s"' + if isString(val): + if self.containsSubstitutions and '%SOUP-ENCODING%' in val: + val = self.substituteEncoding(val, encoding) + + # The attribute value either: + # + # * Contains no embedded double quotes or single quotes. + # No problem: we enclose it in double quotes. + # * Contains embedded single quotes. No problem: + # double quotes work here too. + # * Contains embedded double quotes. No problem: + # we enclose it in single quotes. + # * Embeds both single _and_ double quotes. This + # can't happen naturally, but it can happen if + # you modify an attribute value after parsing + # the document. Now we have a bit of a + # problem. We solve it by enclosing the + # attribute in single quotes, and escaping any + # embedded single quotes to XML entities. + if '"' in val: + fmt = "%s='%s'" + # This can't happen naturally, but it can happen + # if you modify an attribute value after parsing. + if "'" in val: + val = val.replace("'", "&squot;") + + # Now we're okay w/r/t quotes. But the attribute + # value might also contain angle brackets, or + # ampersands that aren't part of entities. We need + # to escape those to XML entities too. + val = re.sub("([<>]|&(?![^\s]+;))", + lambda x: "&" + self.XML_SPECIAL_CHARS_TO_ENTITIES[x.group(0)[0]] + ";", + val) + + attrs.append(fmt % (self.toEncoding(key, encoding), + self.toEncoding(val, encoding))) + close = '' + closeTag = '' + if self.isSelfClosing: + close = ' /' + else: + closeTag = '' % encodedName + + indentTag, indentContents = 0, 0 + if prettyPrint: + indentTag = indentLevel + space = (' ' * (indentTag-1)) + indentContents = indentTag + 1 + contents = self.renderContents(encoding, prettyPrint, indentContents) + if self.hidden: + s = contents + else: + s = [] + attributeString = '' + if attrs: + attributeString = ' ' + ' '.join(attrs) + if prettyPrint: + s.append(space) + s.append('<%s%s%s>' % (encodedName, attributeString, close)) + if prettyPrint: + s.append("\n") + s.append(contents) + if prettyPrint and contents and contents[-1] != "\n": + s.append("\n") + if prettyPrint and closeTag: + s.append(space) + s.append(closeTag) + if prettyPrint and closeTag and self.nextSibling: + s.append("\n") + s = ''.join(s) + return s + + def prettify(self, encoding=DEFAULT_OUTPUT_ENCODING): + return self.__str__(encoding, True) + + def renderContents(self, encoding=DEFAULT_OUTPUT_ENCODING, + prettyPrint=False, indentLevel=0): + """Renders the contents of this tag as a string in the given + encoding. If encoding is None, returns a Unicode string..""" + s=[] + for c in self: + text = None + if isinstance(c, NavigableString): + text = c.__str__(encoding) + elif isinstance(c, Tag): + s.append(c.__str__(encoding, prettyPrint, indentLevel)) + if text and prettyPrint: + text = text.strip() + if text: + if prettyPrint: + s.append(" " * (indentLevel-1)) + s.append(text) + if prettyPrint: + s.append("\n") + return ''.join(s) + + #Soup methods + + def find(self, name=None, attrs={}, recursive=True, text=None, + **kwargs): + """Return only the first child of this Tag matching the given + criteria.""" + r = None + l = self.findAll(name, attrs, recursive, text, 1, **kwargs) + if l: + r = l[0] + return r + findChild = find + + def findAll(self, name=None, attrs={}, recursive=True, text=None, + limit=None, **kwargs): + """Extracts a list of Tag objects that match the given + criteria. You can specify the name of the Tag and any + attributes you want the Tag to have. + + The value of a key-value pair in the 'attrs' map can be a + string, a list of strings, a regular expression object, or a + callable that takes a string and returns whether or not the + string matches for some custom definition of 'matches'. The + same is true of the tag name.""" + generator = self.recursiveChildGenerator + if not recursive: + generator = self.childGenerator + return self._findAll(name, attrs, text, limit, generator, **kwargs) + findChildren = findAll + + # Pre-3.x compatibility methods + first = find + fetch = findAll + + def fetchText(self, text=None, recursive=True, limit=None): + return self.findAll(text=text, recursive=recursive, limit=limit) + + def firstText(self, text=None, recursive=True): + return self.find(text=text, recursive=recursive) + + #Utility methods + + def append(self, tag): + """Appends the given tag to the contents of this tag.""" + self.contents.append(tag) + + #Private methods + + def _getAttrMap(self): + """Initializes a map representation of this tag's attributes, + if not already initialized.""" + if not getattr(self, 'attrMap'): + self.attrMap = {} + for (key, value) in self.attrs: + self.attrMap[key] = value + return self.attrMap + + #Generator methods + def childGenerator(self): + for i in range(0, len(self.contents)): + yield self.contents[i] + raise StopIteration + + def recursiveChildGenerator(self): + stack = [(self, 0)] + while stack: + tag, start = stack.pop() + if isinstance(tag, Tag): + for i in range(start, len(tag.contents)): + a = tag.contents[i] + yield a + if isinstance(a, Tag) and tag.contents: + if i < len(tag.contents) - 1: + stack.append((tag, i+1)) + stack.append((a, 0)) + break + raise StopIteration + +# Next, a couple classes to represent queries and their results. +class SoupStrainer: + """Encapsulates a number of ways of matching a markup element (tag or + text).""" + + def __init__(self, name=None, attrs={}, text=None, **kwargs): + self.name = name + if isString(attrs): + kwargs['class'] = attrs + attrs = None + if kwargs: + if attrs: + attrs = attrs.copy() + attrs.update(kwargs) + else: + attrs = kwargs + self.attrs = attrs + self.text = text + + def __str__(self): + if self.text: + return self.text + else: + return "%s|%s" % (self.name, self.attrs) + + def searchTag(self, markupName=None, markupAttrs={}): + found = None + markup = None + if isinstance(markupName, Tag): + markup = markupName + markupAttrs = markup + callFunctionWithTagData = callable(self.name) \ + and not isinstance(markupName, Tag) + + if (not self.name) \ + or callFunctionWithTagData \ + or (markup and self._matches(markup, self.name)) \ + or (not markup and self._matches(markupName, self.name)): + if callFunctionWithTagData: + match = self.name(markupName, markupAttrs) + else: + match = True + markupAttrMap = None + for attr, matchAgainst in self.attrs.items(): + if not markupAttrMap: + if hasattr(markupAttrs, 'get'): + markupAttrMap = markupAttrs + else: + markupAttrMap = {} + for k,v in markupAttrs: + markupAttrMap[k] = v + attrValue = markupAttrMap.get(attr) + if not self._matches(attrValue, matchAgainst): + match = False + break + if match: + if markup: + found = markup + else: + found = markupName + return found + + def search(self, markup): + #print 'looking for %s in %s' % (self, markup) + found = None + # If given a list of items, scan it for a text element that + # matches. + if isList(markup) and not isinstance(markup, Tag): + for element in markup: + if isinstance(element, NavigableString) \ + and self.search(element): + found = element + break + # If it's a Tag, make sure its name or attributes match. + # Don't bother with Tags if we're searching for text. + elif isinstance(markup, Tag): + if not self.text: + found = self.searchTag(markup) + # If it's text, make sure the text matches. + elif isinstance(markup, NavigableString) or \ + isString(markup): + if self._matches(markup, self.text): + found = markup + else: + raise Exception, "I don't know how to match against a %s" \ + % markup.__class__ + return found + + def _matches(self, markup, matchAgainst): + #print "Matching %s against %s" % (markup, matchAgainst) + result = False + if matchAgainst == True and type(matchAgainst) == types.BooleanType: + result = markup != None + elif callable(matchAgainst): + result = matchAgainst(markup) + else: + #Custom match methods take the tag as an argument, but all + #other ways of matching match the tag name as a string. + if isinstance(markup, Tag): + markup = markup.name + if markup and not isString(markup): + markup = unicode(markup) + #Now we know that chunk is either a string, or None. + if hasattr(matchAgainst, 'match'): + # It's a regexp object. + result = markup and matchAgainst.search(markup) + elif isList(matchAgainst): + result = markup in matchAgainst + elif hasattr(matchAgainst, 'items'): + result = markup.has_key(matchAgainst) + elif matchAgainst and isString(markup): + if isinstance(markup, unicode): + matchAgainst = unicode(matchAgainst) + else: + matchAgainst = str(matchAgainst) + + if not result: + result = matchAgainst == markup + return result + +class ResultSet(list): + """A ResultSet is just a list that keeps track of the SoupStrainer + that created it.""" + def __init__(self, source): + list.__init__([]) + self.source = source + +# Now, some helper functions. + +def isList(l): + """Convenience method that works with all 2.x versions of Python + to determine whether or not something is listlike.""" + return hasattr(l, '__iter__') \ + or (type(l) in (types.ListType, types.TupleType)) + +def isString(s): + """Convenience method that works with all 2.x versions of Python + to determine whether or not something is stringlike.""" + try: + return isinstance(s, unicode) or isintance(s, basestring) + except NameError: + return isinstance(s, str) + +def buildTagMap(default, *args): + """Turns a list of maps, lists, or scalars into a single map. + Used to build the SELF_CLOSING_TAGS, NESTABLE_TAGS, and + NESTING_RESET_TAGS maps out of lists and partial maps.""" + built = {} + for portion in args: + if hasattr(portion, 'items'): + #It's a map. Merge it. + for k,v in portion.items(): + built[k] = v + elif isList(portion): + #It's a list. Map each item to the default. + for k in portion: + built[k] = default + else: + #It's a scalar. Map it to the default. + built[portion] = default + return built + +# Now, the parser classes. + +class BeautifulStoneSoup(Tag, SGMLParser): + + """This class contains the basic parser and search code. It defines + a parser that knows nothing about tag behavior except for the + following: + + You can't close a tag without closing all the tags it encloses. + That is, "" actually means + "". + + [Another possible explanation is "", but since + this class defines no SELF_CLOSING_TAGS, it will never use that + explanation.] + + This class is useful for parsing XML or made-up markup languages, + or when BeautifulSoup makes an assumption counter to what you were + expecting.""" + + XML_ENTITY_LIST = {} + for i in Tag.XML_SPECIAL_CHARS_TO_ENTITIES.values(): + XML_ENTITY_LIST[i] = True + + SELF_CLOSING_TAGS = {} + NESTABLE_TAGS = {} + RESET_NESTING_TAGS = {} + QUOTE_TAGS = {} + + MARKUP_MASSAGE = [(re.compile('(<[^<>]*)/>'), + lambda x: x.group(1) + ' />'), + (re.compile(']*)>'), + lambda x: '') + ] + + ROOT_TAG_NAME = u'[document]' + + HTML_ENTITIES = "html" + XML_ENTITIES = "xml" + + def __init__(self, markup="", parseOnlyThese=None, fromEncoding=None, + markupMassage=True, smartQuotesTo=XML_ENTITIES, + convertEntities=None, selfClosingTags=None): + """The Soup object is initialized as the 'root tag', and the + provided markup (which can be a string or a file-like object) + is fed into the underlying parser. + + sgmllib will process most bad HTML, and the BeautifulSoup + class has some tricks for dealing with some HTML that kills + sgmllib, but Beautiful Soup can nonetheless choke or lose data + if your data uses self-closing tags or declarations + incorrectly. + + By default, Beautiful Soup uses regexes to sanitize input, + avoiding the vast majority of these problems. If the problems + don't apply to you, pass in False for markupMassage, and + you'll get better performance. + + The default parser massage techniques fix the two most common + instances of invalid HTML that choke sgmllib: + +
(No space between name of closing tag and tag close) + (Extraneous whitespace in declaration) + + You can pass in a custom list of (RE object, replace method) + tuples to get Beautiful Soup to scrub your input the way you + want.""" + + self.parseOnlyThese = parseOnlyThese + self.fromEncoding = fromEncoding + self.smartQuotesTo = smartQuotesTo + self.convertEntities = convertEntities + if self.convertEntities: + # It doesn't make sense to convert encoded characters to + # entities even while you're converting entities to Unicode. + # Just convert it all to Unicode. + self.smartQuotesTo = None + self.instanceSelfClosingTags = buildTagMap(None, selfClosingTags) + SGMLParser.__init__(self) + + if hasattr(markup, 'read'): # It's a file-type object. + markup = markup.read() + self.markup = markup + self.markupMassage = markupMassage + try: + self._feed() + except StopParsing: + pass + self.markup = None # The markup can now be GCed + + def _feed(self, inDocumentEncoding=None): + # Convert the document to Unicode. + markup = self.markup + if isinstance(markup, unicode): + if not hasattr(self, 'originalEncoding'): + self.originalEncoding = None + else: + dammit = UnicodeDammit\ + (markup, [self.fromEncoding, inDocumentEncoding], + smartQuotesTo=self.smartQuotesTo) + markup = dammit.unicode + self.originalEncoding = dammit.originalEncoding + if markup: + if self.markupMassage: + if not isList(self.markupMassage): + self.markupMassage = self.MARKUP_MASSAGE + for fix, m in self.markupMassage: + markup = fix.sub(m, markup) + self.reset() + + SGMLParser.feed(self, markup) + # Close out any unfinished strings and close all the open tags. + self.endData() + while self.currentTag.name != self.ROOT_TAG_NAME: + self.popTag() + + def __getattr__(self, methodName): + """This method routes method call requests to either the SGMLParser + superclass or the Tag superclass, depending on the method name.""" + #print "__getattr__ called on %s.%s" % (self.__class__, methodName) + + if methodName.find('start_') == 0 or methodName.find('end_') == 0 \ + or methodName.find('do_') == 0: + return SGMLParser.__getattr__(self, methodName) + elif methodName.find('__') != 0: + return Tag.__getattr__(self, methodName) + else: + raise AttributeError + + def isSelfClosingTag(self, name): + """Returns true iff the given string is the name of a + self-closing tag according to this parser.""" + return self.SELF_CLOSING_TAGS.has_key(name) \ + or self.instanceSelfClosingTags.has_key(name) + + def reset(self): + Tag.__init__(self, self, self.ROOT_TAG_NAME) + self.hidden = 1 + SGMLParser.reset(self) + self.currentData = [] + self.currentTag = None + self.tagStack = [] + self.quoteStack = [] + self.pushTag(self) + + def popTag(self): + tag = self.tagStack.pop() + # Tags with just one string-owning child get the child as a + # 'string' property, so that soup.tag.string is shorthand for + # soup.tag.contents[0] + if len(self.currentTag.contents) == 1 and \ + isinstance(self.currentTag.contents[0], NavigableString): + self.currentTag.string = self.currentTag.contents[0] + + #print "Pop", tag.name + if self.tagStack: + self.currentTag = self.tagStack[-1] + return self.currentTag + + def pushTag(self, tag): + #print "Push", tag.name + if self.currentTag: + self.currentTag.append(tag) + self.tagStack.append(tag) + self.currentTag = self.tagStack[-1] + + def endData(self, containerClass=NavigableString): + if self.currentData: + currentData = ''.join(self.currentData) + if not currentData.strip(): + if '\n' in currentData: + currentData = '\n' + else: + currentData = ' ' + self.currentData = [] + if self.parseOnlyThese and len(self.tagStack) <= 1 and \ + (not self.parseOnlyThese.text or \ + not self.parseOnlyThese.search(currentData)): + return + o = containerClass(currentData) + o.setup(self.currentTag, self.previous) + if self.previous: + self.previous.next = o + self.previous = o + self.currentTag.contents.append(o) + + + def _popToTag(self, name, inclusivePop=True): + """Pops the tag stack up to and including the most recent + instance of the given tag. If inclusivePop is false, pops the tag + stack up to but *not* including the most recent instqance of + the given tag.""" + #print "Popping to %s" % name + if name == self.ROOT_TAG_NAME: + return + + numPops = 0 + mostRecentTag = None + for i in range(len(self.tagStack)-1, 0, -1): + if name == self.tagStack[i].name: + numPops = len(self.tagStack)-i + break + if not inclusivePop: + numPops = numPops - 1 + + for i in range(0, numPops): + mostRecentTag = self.popTag() + return mostRecentTag + + def _smartPop(self, name): + + """We need to pop up to the previous tag of this type, unless + one of this tag's nesting reset triggers comes between this + tag and the previous tag of this type, OR unless this tag is a + generic nesting trigger and another generic nesting trigger + comes between this tag and the previous tag of this type. + + Examples: +

FooBar

should pop to 'p', not 'b'. +

FooBar

should pop to 'table', not 'p'. +

Foo

Bar

should pop to 'tr', not 'p'. +

FooBar

should pop to 'p', not 'b'. + +

    • *
    • * should pop to 'ul', not the first 'li'. +
  • ** should pop to 'table', not the first 'tr' + tag should + implicitly close the previous tag within the same
    ** should pop to 'tr', not the first 'td' + """ + + nestingResetTriggers = self.NESTABLE_TAGS.get(name) + isNestable = nestingResetTriggers != None + isResetNesting = self.RESET_NESTING_TAGS.has_key(name) + popTo = None + inclusive = True + for i in range(len(self.tagStack)-1, 0, -1): + p = self.tagStack[i] + if (not p or p.name == name) and not isNestable: + #Non-nestable tags get popped to the top or to their + #last occurance. + popTo = name + break + if (nestingResetTriggers != None + and p.name in nestingResetTriggers) \ + or (nestingResetTriggers == None and isResetNesting + and self.RESET_NESTING_TAGS.has_key(p.name)): + + #If we encounter one of the nesting reset triggers + #peculiar to this tag, or we encounter another tag + #that causes nesting to reset, pop up to but not + #including that tag. + popTo = p.name + inclusive = False + break + p = p.parent + if popTo: + self._popToTag(popTo, inclusive) + + def unknown_starttag(self, name, attrs, selfClosing=0): + #print "Start tag %s: %s" % (name, attrs) + if self.quoteStack: + #This is not a real tag. + #print "<%s> is not real!" % name + attrs = ''.join(map(lambda(x, y): ' %s="%s"' % (x, y), attrs)) + self.handle_data('<%s%s>' % (name, attrs)) + return + self.endData() + + if not self.isSelfClosingTag(name) and not selfClosing: + self._smartPop(name) + + if self.parseOnlyThese and len(self.tagStack) <= 1 \ + and (self.parseOnlyThese.text or not self.parseOnlyThese.searchTag(name, attrs)): + return + + tag = Tag(self, name, attrs, self.currentTag, self.previous) + if self.previous: + self.previous.next = tag + self.previous = tag + self.pushTag(tag) + if selfClosing or self.isSelfClosingTag(name): + self.popTag() + if name in self.QUOTE_TAGS: + #print "Beginning quote (%s)" % name + self.quoteStack.append(name) + self.literal = 1 + return tag + + def unknown_endtag(self, name): + #print "End tag %s" % name + if self.quoteStack and self.quoteStack[-1] != name: + #This is not a real end tag. + #print " is not real!" % name + self.handle_data('' % name) + return + self.endData() + self._popToTag(name) + if self.quoteStack and self.quoteStack[-1] == name: + self.quoteStack.pop() + self.literal = (len(self.quoteStack) > 0) + + def handle_data(self, data): + self.currentData.append(data) + + def _toStringSubclass(self, text, subclass): + """Adds a certain piece of text to the tree as a NavigableString + subclass.""" + self.endData() + self.handle_data(text) + self.endData(subclass) + + def handle_pi(self, text): + """Handle a processing instruction as a ProcessingInstruction + object, possibly one with a %SOUP-ENCODING% slot into which an + encoding will be plugged later.""" + if text[:3] == "xml": + text = "xml version='1.0' encoding='%SOUP-ENCODING%'" + self._toStringSubclass(text, ProcessingInstruction) + + def handle_comment(self, text): + "Handle comments as Comment objects." + self._toStringSubclass(text, Comment) + + def handle_charref(self, ref): + "Handle character references as data." + if self.convertEntities in [self.HTML_ENTITIES, + self.XML_ENTITIES]: + data = unichr(int(ref)) + else: + data = '&#%s;' % ref + self.handle_data(data) + + def handle_entityref(self, ref): + """Handle entity references as data, possibly converting known + HTML entity references to the corresponding Unicode + characters.""" + data = None + if self.convertEntities == self.HTML_ENTITIES or \ + (self.convertEntities == self.XML_ENTITIES and \ + self.XML_ENTITY_LIST.get(ref)): + try: + data = unichr(name2codepoint[ref]) + except KeyError: + pass + if not data: + data = '&%s;' % ref + self.handle_data(data) + + def handle_decl(self, data): + "Handle DOCTYPEs and the like as Declaration objects." + self._toStringSubclass(data, Declaration) + + def parse_declaration(self, i): + """Treat a bogus SGML declaration as raw data. Treat a CDATA + declaration as a CData object.""" + j = None + if self.rawdata[i:i+9] == '', i) + if k == -1: + k = len(self.rawdata) + data = self.rawdata[i+9:k] + j = k+3 + self._toStringSubclass(data, CData) + else: + try: + j = SGMLParser.parse_declaration(self, i) + except SGMLParseError: + toHandle = self.rawdata[i:] + self.handle_data(toHandle) + j = i + len(toHandle) + return j + +class BeautifulSoup(BeautifulStoneSoup): + + """This parser knows the following facts about HTML: + + * Some tags have no closing tag and should be interpreted as being + closed as soon as they are encountered. + + * The text inside some tags (ie. 'script') may contain tags which + are not really part of the document and which should be parsed + as text, not tags. If you want to parse the text as tags, you can + always fetch it and parse it explicitly. + + * Tag nesting rules: + + Most tags can't be nested at all. For instance, the occurance of + a

    tag should implicitly close the previous

    tag. + +

    Para1

    Para2 + should be transformed into: +

    Para1

    Para2 + + Some tags can be nested arbitrarily. For instance, the occurance + of a

    tag should _not_ implicitly close the previous +
    tag. + + Alice said:
    Bob said:
    Blah + should NOT be transformed into: + Alice said:
    Bob said:
    Blah + + Some tags can be nested, but the nesting is reset by the + interposition of other tags. For instance, a
    , + but not close a tag in another table. + +
    BlahBlah + should be transformed into: +
    BlahBlah + but, + Blah
    Blah + should NOT be transformed into + Blah
    Blah + + Differing assumptions about tag nesting rules are a major source + of problems with the BeautifulSoup class. If BeautifulSoup is not + treating as nestable a tag your page author treats as nestable, + try ICantBelieveItsBeautifulSoup, MinimalSoup, or + BeautifulStoneSoup before writing your own subclass.""" + + def __init__(self, *args, **kwargs): + if not kwargs.has_key('smartQuotesTo'): + kwargs['smartQuotesTo'] = self.HTML_ENTITIES + BeautifulStoneSoup.__init__(self, *args, **kwargs) + + SELF_CLOSING_TAGS = buildTagMap(None, + ['br' , 'hr', 'input', 'img', 'meta', + 'spacer', 'link', 'frame', 'base']) + + QUOTE_TAGS = {'script': None} + + #According to the HTML standard, each of these inline tags can + #contain another tag of the same type. Furthermore, it's common + #to actually use these tags this way. + NESTABLE_INLINE_TAGS = ['span', 'font', 'q', 'object', 'bdo', 'sub', 'sup', + 'center'] + + #According to the HTML standard, these block tags can contain + #another tag of the same type. Furthermore, it's common + #to actually use these tags this way. + NESTABLE_BLOCK_TAGS = ['blockquote', 'div', 'fieldset', 'ins', 'del'] + + #Lists can contain other lists, but there are restrictions. + NESTABLE_LIST_TAGS = { 'ol' : [], + 'ul' : [], + 'li' : ['ul', 'ol'], + 'dl' : [], + 'dd' : ['dl'], + 'dt' : ['dl'] } + + #Tables can contain other tables, but there are restrictions. + NESTABLE_TABLE_TAGS = {'table' : [], + 'tr' : ['table', 'tbody', 'tfoot', 'thead'], + 'td' : ['tr'], + 'th' : ['tr'], + 'thead' : ['table'], + 'tbody' : ['table'], + 'tfoot' : ['table'], + } + + NON_NESTABLE_BLOCK_TAGS = ['address', 'form', 'p', 'pre'] + + #If one of these tags is encountered, all tags up to the next tag of + #this type are popped. + RESET_NESTING_TAGS = buildTagMap(None, NESTABLE_BLOCK_TAGS, 'noscript', + NON_NESTABLE_BLOCK_TAGS, + NESTABLE_LIST_TAGS, + NESTABLE_TABLE_TAGS) + + NESTABLE_TAGS = buildTagMap([], NESTABLE_INLINE_TAGS, NESTABLE_BLOCK_TAGS, + NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS) + + # Used to detect the charset in a META tag; see start_meta + CHARSET_RE = re.compile("((^|;)\s*charset=)([^;]*)") + + def start_meta(self, attrs): + """Beautiful Soup can detect a charset included in a META tag, + try to convert the document to that charset, and re-parse the + document from the beginning.""" + httpEquiv = None + contentType = None + contentTypeIndex = None + tagNeedsEncodingSubstitution = False + + for i in range(0, len(attrs)): + key, value = attrs[i] + key = key.lower() + if key == 'http-equiv': + httpEquiv = value + elif key == 'content': + contentType = value + contentTypeIndex = i + + if httpEquiv and contentType: # It's an interesting meta tag. + match = self.CHARSET_RE.search(contentType) + if match: + if getattr(self, 'declaredHTMLEncoding') or \ + (self.originalEncoding == self.fromEncoding): + # This is our second pass through the document, or + # else an encoding was specified explicitly and it + # worked. Rewrite the meta tag. + newAttr = self.CHARSET_RE.sub\ + (lambda(match):match.group(1) + + "%SOUP-ENCODING%", value) + attrs[contentTypeIndex] = (attrs[contentTypeIndex][0], + newAttr) + tagNeedsEncodingSubstitution = True + else: + # This is our first pass through the document. + # Go through it again with the new information. + newCharset = match.group(3) + if newCharset and newCharset != self.originalEncoding: + self.declaredHTMLEncoding = newCharset + self._feed(self.declaredHTMLEncoding) + raise StopParsing + tag = self.unknown_starttag("meta", attrs) + if tag and tagNeedsEncodingSubstitution: + tag.containsSubstitutions = True + +class StopParsing(Exception): + pass + +class ICantBelieveItsBeautifulSoup(BeautifulSoup): + + """The BeautifulSoup class is oriented towards skipping over + common HTML errors like unclosed tags. However, sometimes it makes + errors of its own. For instance, consider this fragment: + + FooBar + + This is perfectly valid (if bizarre) HTML. However, the + BeautifulSoup class will implicitly close the first b tag when it + encounters the second 'b'. It will think the author wrote + "FooBar", and didn't close the first 'b' tag, because + there's no real-world reason to bold something that's already + bold. When it encounters '' it will close two more 'b' + tags, for a grand total of three tags closed instead of two. This + can throw off the rest of your document structure. The same is + true of a number of other tags, listed below. + + It's much more common for someone to forget to close a 'b' tag + than to actually use nested 'b' tags, and the BeautifulSoup class + handles the common case. This class handles the not-co-common + case: where you can't believe someone wrote what they did, but + it's valid HTML and BeautifulSoup screwed up by assuming it + wouldn't be.""" + + I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS = \ + ['em', 'big', 'i', 'small', 'tt', 'abbr', 'acronym', 'strong', + 'cite', 'code', 'dfn', 'kbd', 'samp', 'strong', 'var', 'b', + 'big'] + + I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS = ['noscript'] + + NESTABLE_TAGS = buildTagMap([], BeautifulSoup.NESTABLE_TAGS, + I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS, + I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS) + +class MinimalSoup(BeautifulSoup): + """The MinimalSoup class is for parsing HTML that contains + pathologically bad markup. It makes no assumptions about tag + nesting, but it does know which tags are self-closing, that +