[OpenLayers-Commits] r10975 - in trunk/openlayers: examples lib lib/OpenLayers/Layer tests tests/Layer theme/default

commits-20090109 at openlayers.org commits-20090109 at openlayers.org
Fri Dec 17 18:46:22 EST 2010


Author: ahocevar
Date: 2010-12-17 15:46:22 -0800 (Fri, 17 Dec 2010)
New Revision: 10975

Added:
   trunk/openlayers/examples/bing-tiles.html
   trunk/openlayers/examples/bing-tiles.js
   trunk/openlayers/lib/OpenLayers/Layer/Bing.js
   trunk/openlayers/tests/Layer/Bing.html
Modified:
   trunk/openlayers/lib/OpenLayers.js
   trunk/openlayers/lib/OpenLayers/Layer/XYZ.js
   trunk/openlayers/tests/list-tests.html
   trunk/openlayers/theme/default/style.css
Log:
Added Bing layer with direct tile access. p=me,tschaub r=tschaub,me (closes #2975)


Added: trunk/openlayers/examples/bing-tiles.html
===================================================================
--- trunk/openlayers/examples/bing-tiles.html	                        (rev 0)
+++ trunk/openlayers/examples/bing-tiles.html	2010-12-17 23:46:22 UTC (rev 10975)
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>OpenLayers Bing Tiles Example</title>
+    <link rel="stylesheet" href="../theme/default/style.css" type="text/css">
+    <link rel="stylesheet" href="style.css" type="text/css">
+    <style type="text/css">
+    .olControlAttribution {
+        left: 5px;
+        right: inherit;
+        bottom: 5px; 
+    }
+    </style>
+  </head>
+  <body>
+    <h1 id="title">Basic Bing Tiles Example</h1>
+
+    <div id="tags">
+        bing tiles
+    </div>
+
+    <div id="shortdesc">Use Bing with direct tile access</div>
+
+    <div id="map" class="smallmap"></div>
+
+    <div id="docs">
+        <p>This example shows a very simple map with Bing layers that use
+        direct tile access through Bing Maps REST Services.</p><p>See
+        <a target="_blank" href="bing-tiles.js">bing-tiles.js</a> for the
+        source code.</p>
+    </div>
+    <script src="../lib/OpenLayers.js"></script>
+    <script src="bing-tiles.js"></script>
+  </body>
+</html>

Added: trunk/openlayers/examples/bing-tiles.js
===================================================================
--- trunk/openlayers/examples/bing-tiles.js	                        (rev 0)
+++ trunk/openlayers/examples/bing-tiles.js	2010-12-17 23:46:22 UTC (rev 10975)
@@ -0,0 +1,26 @@
+// API key for http://openlayers.org. Please get your own at
+// http://bingmapsportal.com/ and use that instead.
+var apiKey = "AqTGBsziZHIJYYxgivLBf0hVdrAk9mWO5cQcb8Yux8sW5M8c8opEC2lZqKR1ZZXf";
+
+var map = new OpenLayers.Map( 'map');
+
+var road = new OpenLayers.Layer.Bing({
+    key: apiKey,
+    type: "Road"
+});
+var aerial = new OpenLayers.Layer.Bing({
+    key: apiKey,
+    type: "Aerial"
+});
+var hybrid = new OpenLayers.Layer.Bing({
+    key: apiKey,
+    type: "AerialWithLabels",
+    name: "Bing Aerial With Labels"
+});
+
+map.addLayers([road, aerial, hybrid]);
+map.addControl(new OpenLayers.Control.LayerSwitcher());
+map.setCenter(new OpenLayers.LonLat(-71.147, 42.472).transform(
+    new OpenLayers.Projection("EPSG:4326"),
+    map.getProjectionObject()
+), 12);

Added: trunk/openlayers/lib/OpenLayers/Layer/Bing.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Layer/Bing.js	                        (rev 0)
+++ trunk/openlayers/lib/OpenLayers/Layer/Bing.js	2010-12-17 23:46:22 UTC (rev 10975)
@@ -0,0 +1,312 @@
+/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Layer/XYZ.js
+ */
+
+/** 
+ * Class: OpenLayers.Layer.Bing
+ * Bing layer using direct tile access as provided by Bing Maps REST Services.
+ * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more
+ * information.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Layer.XYZ>
+ */
+OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {
+
+    /**
+     * Constant: RESOLUTIONS
+     */
+    RESOLUTIONS: [
+        78271.517,
+        39135.7585,
+        19567.87925,
+        9783.939625,
+        4891.9698125,
+        2445.98490625,
+        1222.992453125,
+        611.4962265625,
+        305.74811328125,
+        152.874056640625,
+        76.4370283203125,
+        38.21851416015625,
+        19.109257080078127,
+        9.554628540039063,
+        4.777314270019532,
+        2.388657135009766,
+        1.194328567504883,
+        0.5971642837524415,
+        0.29858214187622073,
+        0.14929107093811037,
+        0.07464553546905518,
+        0.03732276773452759,
+        0.018661383867263796
+    ],
+
+    /**
+     * Property: attributionTemplate
+     * {String}
+     */
+    attributionTemplate: '<span class="olBingAttribution ${type}">' +
+         '<div><a target="_blank" href="http://www.bing.com/maps/">' +
+         '<img src="${logo}"></img></div></a>${copyrights}' +
+         '<a style="white-space: nowrap" target="_blank" '+
+         'href="http://www.microsoft.com/maps/product/terms.html">' +
+         'Terms of Use</a></span>',
+
+    /**
+     * Property: sphericalMercator
+     * {Boolean} always true for this layer type
+     */
+    sphericalMercator: true,
+    
+    /**
+     * Property: metadata
+     * {Object} Metadata for this layer, as returned by the callback script
+     */
+    metadata: null,
+    
+    /**
+     * APIProperty: type
+     * {String} The layer identifier.  Any non-birdseye imageryType
+     *     from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be
+     *     used.  Default is "Road".
+     */
+    type: "Road",
+
+    /**
+     * Constant: EVENT_TYPES
+     * {Array(String)} Supported application event types.  Register a listener
+     *     for a particular event with the following syntax:
+     * (code)
+     * layer.events.register(type, obj, listener);
+     * (end)
+     *
+     * Listeners will be called with a reference to an event object.  The
+     *     properties of this event depends on exactly what happened.
+     *
+     * All event objects have at least the following properties:
+     * object - {Object} A reference to layer.events.object.
+     * element - {DOMElement} A reference to layer.events.element.
+     *
+     * Supported map event types (in addition to those from <OpenLayers.Layer>):
+     * added - Triggered after the layer is added to a map.  Listeners
+     *      will receive an object with a *map* property referencing the
+     *      map and a *layer* property referencing the layer.
+     */
+    EVENT_TYPES: ["added"],
+
+    /**
+     * Constructor: OpenLayers.Layer.Bing
+     * Create a new Bing layer.
+     *
+     * Example:
+     * (code)
+     * var road = new OpenLayers.Layer.Bing({
+     *     name: "My Bing Aerial Layer",
+     *     type: "Aerial",
+     *     key: "my-api-key-here",
+     * });
+     * (end)
+     *
+     * Parameters:
+     * config - {Object} Configuration properties for the layer.
+     *
+     * Required configuration properties:
+     * key - {String} Bing Maps API key for your application. Get one at
+     *     http://bingmapsportal.com/.
+     * type - {String} The layer identifier.  Any non-birdseye imageryType
+     *     from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be
+     *     used.
+     *
+     * Any other documented layer properties can be provided in the config object.
+     */
+    initialize: function(options) {
+        // concatenate events specific to vector with those from the base
+        this.EVENT_TYPES =
+            OpenLayers.Layer.Bing.prototype.EVENT_TYPES.concat(
+            OpenLayers.Layer.prototype.EVENT_TYPES
+        );
+        var name = options.name || "Bing " + (options.type || this.type);
+        var newArgs = [name, null, options];
+        OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);
+        this.loadMetadata(this.type); 
+    },
+
+    /**
+     * Method: loadMetadata
+     *
+     * Parameters:
+     * imageryType - {String}
+     */
+    loadMetadata: function(imageryType) {
+        this._callbackId = "_callback_" + this.id.replace(/\./g, "_");
+        // link the processMetadata method to the global scope and bind it
+        // to this instance
+        window[this._callbackId] = OpenLayers.Function.bind(
+            OpenLayers.Layer.Bing.processMetadata, this
+        );
+        var url = "http://dev.virtualearth.net/REST/v1/Imagery/Metadata/" +
+            imageryType + "?key=" + this.key + "&jsonp=" + this._callbackId +
+            "&include=ImageryProviders";
+        var script = document.createElement("script");
+        script.type = "text/javascript";
+        script.src = url;
+        script.id = this._callbackId;
+        document.getElementsByTagName("head")[0].appendChild(script);
+    },
+    
+    /**
+     * Method: initLayer
+     *
+     * Sets layer properties according to the metadata provided by the API
+     */
+    initLayer: function() {
+        var res = this.metadata.resourceSets[0].resources[0];
+        var url = res.imageUrl.replace("{quadkey}", "${quadkey}");
+        this.url = [];
+        for (var i=0; i<res.imageUrlSubdomains.length; ++i) {
+            this.url.push(url.replace("{subdomain}", res.imageUrlSubdomains[i]));
+        };
+        
+        this.addOptions({
+            resolutions: this.RESOLUTIONS.slice(res.zoomMin-1, res.zoomMax-1),
+            zoomOffset: res.zoomMin
+        });
+        if (this.map) {
+            this.redraw();
+            this.updateAttribution();
+        }
+    },
+
+    /**
+     * Method: getURL
+     *
+     * Paramters:
+     * bounds - {<OpenLayers.Bounds>}
+     */
+    getURL: function(bounds) {
+        if (!this.url) {
+            return OpenLayers.Util.getImagesLocation() + "blank.gif";
+        }
+        var xyz = this.getXYZ(bounds), x = xyz.x, y = xyz.y, z = xyz.z;
+        var quadDigits = [];
+        for (var i = z; i > 0; --i) {
+            var digit = '0';
+            var mask = 1 << (i - 1);
+            if ((x & mask) != 0) {
+                digit++;
+            }
+            if ((y & mask) != 0) {
+                digit++;
+                digit++;
+            }
+            quadDigits.push(digit);
+        }
+        var quadKey = quadDigits.join("");
+        var url = this.selectUrl('' + x + y + z, this.url);
+
+        return OpenLayers.String.format(url, {'quadkey': quadKey});
+    },
+    
+    /**
+     * Method: updateAttribution
+     * Updates the attribution according to the requirements outlined in
+     * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html
+     */
+    updateAttribution: function() {
+        var metadata = this.metadata;
+        if (!metadata || !this.map) {
+            return;
+        }
+        var res = metadata.resourceSets[0].resources[0];
+        var extent = this.map.getExtent().transform(
+            this.map.getProjectionObject(),
+            new OpenLayers.Projection("EPSG:4326")
+        );
+        var providers = res.imageryProviders, zoom = this.map.getZoom() + 1,
+            copyrights = "", provider, i, ii, j, jj, bbox, coverage;
+        for (i=0,ii=providers.length; i<ii; ++i) {
+            provider = providers[i];
+            for (j=0,jj=provider.coverageAreas.length; j<jj; ++j) {
+                coverage = provider.coverageAreas[j];
+                bbox = OpenLayers.Bounds.fromArray(coverage.bbox);
+                if (extent.intersectsBounds(bbox) &&
+                        zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {
+                    copyrights += provider.attribution + " ";
+                }
+            }
+        }
+        this.attribution = OpenLayers.String.format(this.attributionTemplate, {
+            type: this.type.toLowerCase(),
+            logo: metadata.brandLogoUri,
+            copyrights: copyrights
+        });
+        this.map && this.map.events.triggerEvent("changelayer", {layer: this});
+    },
+    
+    /**
+     * Method: setMap
+     */
+    setMap: function() {
+        OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);
+        if (this.map.getCenter()) {
+            this.updateAttribution();
+        }
+        this.map.events.register("moveend", this, this.updateAttribution);
+        // TODO: move this event to Layer
+        // http://trac.osgeo.org/openlayers/ticket/2983
+        this.events.triggerEvent("added", {map: this.map, layer: this});
+    },
+    
+    /**
+     * APIMethod: clone
+     * 
+     * Parameters:
+     * obj - {Object}
+     * 
+     * Returns:
+     * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing>
+     */
+    clone: function(obj) {
+        if (obj == null) {
+            obj = new OpenLayers.Layer.Bing(this.options);
+        }
+        //get all additions from superclasses
+        obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
+        // copy/set any non-init, non-simple values here
+        return obj;
+    },
+    
+    /**
+     * Method: destroy
+     */
+    destroy: function() {
+        this.map &&
+            this.map.events.unregister("moveend", this, this.updateAttribution);
+        OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments);
+    },
+    
+    CLASS_NAME: "OpenLayers.Layer.Bing"
+});
+
+/**
+ * Function: OpenLayers.Layer.Bing.processMetadata
+ * This function will be bound to an instance, linked to the global scope with
+ * an id, and called by the JSONP script returned by the API.
+ *
+ * Parameters:
+ * metadata - {Object} metadata as returned by the API
+ */
+OpenLayers.Layer.Bing.processMetadata = function(metadata) {
+    this.metadata = metadata;
+    this.initLayer();
+    var script = document.getElementById(this._callbackId);
+    script.parentNode.removeChild(script);
+    window[this._callbackId] = undefined; // cannot delete from window in IE
+    delete this._callbackId;
+};

Modified: trunk/openlayers/lib/OpenLayers/Layer/XYZ.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Layer/XYZ.js	2010-12-17 16:04:06 UTC (rev 10974)
+++ trunk/openlayers/lib/OpenLayers/Layer/XYZ.js	2010-12-17 23:46:22 UTC (rev 10975)
@@ -107,7 +107,7 @@
     },    
 
     /**
-     * Method: getUrl
+     * Method: getURL
      *
      * Parameters:
      * bounds - {<OpenLayers.Bounds>}
@@ -118,6 +118,27 @@
      *          parameters
      */
     getURL: function (bounds) {
+        var xyz = this.getXYZ(bounds);
+        var url = this.url;
+        if (url instanceof Array) {
+            var s = '' + xyz.x + xyz.y + xyz.z;
+            url = this.selectUrl(s, url);
+        }
+        
+        return OpenLayers.String.format(url, xyz);
+    },
+    
+    /**
+     * Method: getXYZ
+     * Calculates x, y and z for the given bounds.
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     *
+     * Returns:
+     * {Object} - an object with x, y and z properties.
+     */
+    getXYZ: function(bounds) {
         var res = this.map.getResolution();
         var x = Math.round((bounds.left - this.maxExtent.left) 
             / (res * this.tileSize.w));
@@ -127,16 +148,7 @@
             OpenLayers.Util.indexOf(this.serverResolutions, res) :
             this.map.getZoom() + this.zoomOffset;
 
-        var url = this.url;
-        var s = '' + x + y + z;
-        if (url instanceof Array)
-        {
-            url = this.selectUrl(s, url);
-        }
-        
-        var path = OpenLayers.String.format(url, {'x': x, 'y': y, 'z': z});
-
-        return path;
+        return {'x': x, 'y': y, 'z': z};
     },
     
     /**

Modified: trunk/openlayers/lib/OpenLayers.js
===================================================================
--- trunk/openlayers/lib/OpenLayers.js	2010-12-17 16:04:06 UTC (rev 10974)
+++ trunk/openlayers/lib/OpenLayers.js	2010-12-17 23:46:22 UTC (rev 10975)
@@ -127,6 +127,7 @@
             "OpenLayers/Layer/GeoRSS.js",
             "OpenLayers/Layer/Boxes.js",
             "OpenLayers/Layer/XYZ.js",
+            "OpenLayers/Layer/Bing.js",
             "OpenLayers/Layer/TMS.js",
             "OpenLayers/Layer/TileCache.js",
             "OpenLayers/Layer/Zoomify.js",

Added: trunk/openlayers/tests/Layer/Bing.html
===================================================================
--- trunk/openlayers/tests/Layer/Bing.html	                        (rev 0)
+++ trunk/openlayers/tests/Layer/Bing.html	2010-12-17 23:46:22 UTC (rev 10975)
@@ -0,0 +1,65 @@
+<html>
+<head>
+  <script src="../../lib/OpenLayers.js"></script>
+  <script type="text/javascript">
+    var layer; 
+
+    var layerType = 'Aerial';
+    var key = "AqTGBsziZHIJYYxgivLBf0hVdrAk9mWO5cQcb8Yux8sW5M8c8opEC2lZqKR1ZZXf";
+    
+    var options = {
+        type: layerType,
+        key: key
+    };
+
+    function test_constructor(t) {
+        t.plan(2);
+                       
+        var origProcessMetadata = OpenLayers.Layer.Bing.processMetadata;
+        var log = [];
+        OpenLayers.Layer.Bing.processMetadata = function(metadata) {
+            log.push(metadata);
+            origProcessMetadata.apply(this, arguments);
+        };
+        layer = new OpenLayers.Layer.Bing(options);
+        t.ok(layer instanceof OpenLayers.Layer.Bing, "returns OpenLayers.Layer.Bing object" );
+        t.delay_call(2, function() {
+            t.eq(log.length, 1, "processMetadata called");
+            OpenLayers.Layer.Bing.processMetadata = origProcessMetadata;
+            layer.destroy();
+        });
+    }
+    
+    function test_attribution(t) {
+        t.plan(3);
+        
+        var log = [];
+        var map = new OpenLayers.Map("map");
+        layer = new OpenLayers.Layer.Bing(options);
+        map.addLayer(layer);
+        map.zoomToMaxExtent();
+        
+        t.delay_call(2, function() {
+            t.ok(layer.attribution.indexOf('olBingAttribution aerial') !== -1, "Attribution has the correct css class");
+            t.ok(layer.attribution.indexOf('<img src="">') == -1, "Attribution contains a logo");
+            t.ok(layer.attribution.indexOf('</img></div></a><a style=') == -1 , "Attribution contains a copyright");
+            map.destroy();
+        });
+    }
+
+    function test_clone(t) {
+        t.plan(1);
+        
+        var clone;
+        
+        layer = new OpenLayers.Layer.Bing(options);
+        clone = layer.clone();
+        t.ok(clone instanceof OpenLayers.Layer.Bing, "clone is a Layer.Bing instance");
+    }
+
+  </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>

Modified: trunk/openlayers/tests/list-tests.html
===================================================================
--- trunk/openlayers/tests/list-tests.html	2010-12-17 16:04:06 UTC (rev 10974)
+++ trunk/openlayers/tests/list-tests.html	2010-12-17 23:46:22 UTC (rev 10975)
@@ -125,6 +125,7 @@
     <li>Layer.html</li>
     <li>Layer/ArcIMS.html</li> 
     <li>Layer/ArcGIS93Rest.html</li>
+    <li>Layer/Bing.html</li>
     <li>Layer/EventPane.html</li>
     <li>Layer/FixedZoomLevels.html</li>
     <li>Layer/GeoRSS.html</li>

Modified: trunk/openlayers/theme/default/style.css
===================================================================
--- trunk/openlayers/theme/default/style.css	2010-12-17 16:04:06 UTC (rev 10974)
+++ trunk/openlayers/theme/default/style.css	2010-12-17 23:46:22 UTC (rev 10975)
@@ -397,3 +397,10 @@
     top: 5px;
     right: 0px;
 }
+
+.olBingAttribution {
+    color: #DDD;
+}
+.olBingAttribution.road {
+    color: #333;
+}



More information about the Commits mailing list