none
error trying to create an overlay RRS feed

  • Question

  • Hi, I'm starting to migrate a bing maps 7 angular web app written in typescript to bing maps 8, and I need to add a custom overlay to the map.

    I tried to create a class that extends custom overlay 

    export class wmsOverlay extends Microsoft.Maps.CustomOverlay {
    and adding inside the code of the sample from the site, but I receive errors because, I suppose, the angular app is loaded before bing maps js is fully loaded, and I don't know to avoid this.

    I tried to move the code from the sample in a routine inside an angular ts class to create the object, but I obtain this error message

    4153aba0.js?bu=rms+answers+MapsSDK+AnonymousBegin*SDKPluginStart*SDKInfoboxOverlay*Infobox*SDKColor…:1 Uncaught TypeError: Cannot read property 'parentElement' of null
        at TopographicOverlay.n._insertHtmlElement (4153aba0.js?bu=rms+answers+MapsSDK+AnonymousBegin*SDKPluginStart*SDKInfoboxOverlay*Infobox*SDKColor…:1)
        at TopographicOverlay.n.attachMap (4153aba0.js?bu=rms+answers+MapsSDK+AnonymousBegin*SDKPluginStart*SDKInfoboxOverlay*Infobox*SDKColor…:1)
        at n._onLayersChanged (4153aba0.js?bu=rms+answers+MapsSDK+AnonymousBegin*SDKPluginStart*SDKInfoboxOverlay*Infobox*SDKColor…:1)

    Am I missing something? How can I solve this problem?
    Since I need to see the opacity of the overlay it's ok to add
    this.img.style.opacity = ''+this.currentOpacity;

    in the onAdd ?

    Thursday, December 29, 2016 9:21 PM

Answers

  • If you don't load your overlay, does the map load? If it does then it likely isn't an issue with the Bing Maps API not being fully loaded yet. If the map doesn't load then it's possible this is the issue. Can you add a breakpoint on the line of code where you load the map and see if that is the line of code that throws the error. If it is, then you may want to use the callback parameter of the map script URL to call the map load function as this will only fire once the map script has fully loaded.

    Setting the opacity of your image is fine.

    You can find a bunch of custom overlay samples here: https://msdn.microsoft.com/en-us/library/mt762874.aspx

    Also worth noting that TileLayers support WMS. Here is a code sample: https://msdn.microsoft.com/en-us/library/mt762880.aspx


    [Blog] [twitter] [LinkedIn]

    Friday, December 30, 2016 9:50 AM
  • The tryLocationToPixel code is correct. It returns a Point or an array of Point's depending on what is passed in. Your code passes in individual Location objects, so individual Point objects are returned. If property x is undefined when this code runs then the map is likely not loaded.

    Since the library isn't fully loaded when your class is loaded there are two ways to address this. The first is to load the map script URL using the callback function. Here is an example: http://stackoverflow.com/questions/37550278/how-to-add-bing-maps-v8-to-angular-2-0

    The second option is to add some logic that recursively waits for the Microsoft Maps namespace to become available. For example:

    function loadMap(){
    	if(typeof Microsoft !== 'undefined' &&
    	typeof Microsoft.Maps !== 'undefined' &&
    	typeof Microsoft.Maps.Map !== 'undefined'){
    		//Add logic to load the map control.
    	} else {
    		//Map script isn't yet fully loaded, wait for it.
    		setTimeout(loadMap, 100);
    	}
    }


    [Blog] [twitter] [LinkedIn]

    Sunday, January 1, 2017 12:13 PM
  • I've created a number of modules that extend the CustomOverlay class in TypeScript without any issues. Here is one I created.

    Here is the module code:

    /// <reference path="MicrosoftMaps/Microsoft.Maps.d.ts"/>
    
    /**
    * The options that specify how to render a ground overlay on the map.
    */
    interface IGroundOverlayOptions extends Microsoft.Maps.ICustomOverlayOptions {
        /** A background color that fills the bounding box area beneath the ground overlay. */
        backgroundColor?: string | Microsoft.Maps.Color;
    
        /** The bounding box to anchor the ground overlay to. */
        bounds: Microsoft.Maps.LocationRect;
    
        /** The URL to the image to anchor to the map as a ground overlay. */
        imageUrl: string;
    
        /** The opacity of the ground overlay image. */
        opacity?: number;
    
        /** An angle in degrees to rotate the overlay in a counter-clockwise direction where 0 = north, 90 = west, 180 = south, 270 = east */
        rotation?: number;
    
        /** A boolean value indicating if the ground overlay is visible or not. */
        visible?: boolean;
    }
    
    /**
     * A map overlay that binds an image to a bounding box area on the map.
     */
    class GroundOverlay extends Microsoft.Maps.CustomOverlay {
    
        /***********************************
        * Private Properties
        ***********************************/
    
        /** The options used to define how the ground overlay is rendered. */
        private _options: IGroundOverlayOptions = {
            beneathLabels: true,
            backgroundColor: 'transparent',
            bounds: null,
            imageUrl: null,
            opacity: 1,
            rotation: 0,
            visible: true
        };
    
        /** The image DOM element that is used to render the ground overlay. */
        private _img: HTMLImageElement;
    
        private _mapViewChangeEvent: Microsoft.Maps.IHandlerId;
    
        /***********************************
        * Constructor
        ***********************************/
    
        /**
         * @constructor
         * @param options The options used to render the ground overlay.
         */
        constructor(options: IGroundOverlayOptions) {
            super({
                beneathLabels: (typeof options.beneathLabels === 'boolean') ? options.beneathLabels : true
            });
    
            this.setOptions(options);
        }
    
        /***********************************
        * Public Properties
        ***********************************/
    
        /** Optional property to store any additional metadata for this layer. */
        public metadata: any;
    
        /***********************************
        * Public functions
        ***********************************/
    
        /**
         * Clears the ground overlay.
         */
        public clear() {
            //Reset the options back to the defaults.
            this._options = {
                backgroundColor: 'transparent',
                bounds: null,
                imageUrl: null,
                opacity: 1,
                rotation: 0,
                visible: true
            };
    
            this.setHtmlElement(null);
        }
    
        /**
         * Gets the background color of the ground overlay.
         * @returns The background color of the ground overlay.
         */
        public getBackgroundColor(): string | Microsoft.Maps.Color {
            return this._options.backgroundColor;
        }
    
        /**
         * Gets the bounding box that the ground overlay is bounded to.
         * @returns The bounding box that the ground overlay is bounded to.
         */
        public getBounds(): Microsoft.Maps.LocationRect {
            return this._options.bounds;
        }
    
        /**
         * Gets the url to the gorund overlay image.
         * @returns The url to the gorund overlay image.
         */
        public getImageUrl(): string {
            return this._options.imageUrl;
        }
    
        /**
         * Gets the opacity of the ground overlay.
         * @returns The opacity of the ground overlay.
         */
        public getOpacity(): number {
            return this._options.opacity;
        }
    
        /**
         * Gets the rotation of the ground overlay.
         * @returns The rotation of the ground overlay.
         */
        public getRotation(): number {
            return this._options.opacity;
        }
    
        /**
         * Gets a boolean indicating if the ground overlay is visible or not.
         * @returns A boolean indicating if the ground overlay is visible or not.
         */
        public getVisible(): boolean {
            return this._options.visible;
        }
    
        /**
         * Sets the options used to render the ground overlay.
         * @param options The options used to render the ground overlay.
         */
        public setOptions(options: IGroundOverlayOptions): void {
            if (options && typeof options.beneathLabels === 'boolean') {
                this._options.beneathLabels = options.beneathLabels;
            }
    
            if (typeof options.backgroundColor !== 'undefined') {
                this._options.backgroundColor = options.backgroundColor;
            }
    
            if (typeof options.opacity === 'number') {
                this._options.opacity = options.opacity;
                if (this._img) {
                    this._img.style.opacity = this._options.opacity.toString();
                }
            }
    
            if (typeof options.rotation === 'number') {
                this._options.rotation = options.rotation;
                this._rotateGroundOverlay();
            }
    
            var reposition = false;
    
            if (typeof options.visible === 'boolean') {
                this._options.visible = options.visible;
                if (this._img) {
                    if (options.visible) {
                        this._img.style.display = '';
                        reposition = true;
                    } else {
                        this._img.style.display = 'none';
                    }
                }
            }
    
            if (options.bounds instanceof Microsoft.Maps.LocationRect) {
                this._options.bounds = options.bounds;
                reposition = true;
            }
    
            if (typeof options.imageUrl !== 'undefined') {
                this._options.imageUrl = options.imageUrl;
                this._loadGroundOverlayImage();
            }
    
            if (reposition) {
                this._repositionGroundOverlay();
            }
        }
    
        /***********************************
        * Inherited Events
        ***********************************/
    
        /**
         * The event logic that processes when the ground overlay is added to the map.
         */
        public onAdd(): void {
            this._loadGroundOverlayImage();
        }
    
        /**
         * The event logic that processes when the ground overlay is initially loaded.
         */
        public onLoad(): void {
            this._repositionGroundOverlay();
    
            this._mapViewChangeEvent = Microsoft.Maps.Events.addHandler(this.getMap(), 'viewchange', () => { this._repositionGroundOverlay() });
        }
    
        /**
         * The event logic that processes when the ground overlay is removed from the map.
         */
        public onRemove(): void {
            if (this._mapViewChangeEvent) {
                Microsoft.Maps.Events.removeHandler(this._mapViewChangeEvent);
            }
            this.setHtmlElement(null);
        }
    
        /***********************************
        * Private Functions
        ***********************************/
    
        /**
         * Loads the ground overlay image.
         */
        private _loadGroundOverlayImage(): void {
            if (this._options.imageUrl) {
                //Create an image element that will be used as the overlay.
                this._img = document.createElement('img');
                this._img.src = this._options.imageUrl;
                this._img.style.width = '100%';
                this._img.style.height = '100%';
                this._img.style.position = 'absolute';
                this._img.style.display = (this._options.visible) ? '' : 'none';
    
                if (this._options.backgroundColor) {
                    if (this._options.backgroundColor instanceof Microsoft.Maps.Color) {
                        this._img.style.backgroundColor = (<Microsoft.Maps.Color>this._options.backgroundColor).toRgba();
                    } else {
                        this._img.style.backgroundColor = <string>this._options.backgroundColor;
                    }
                }
    
                if (typeof this._options.opacity === 'number') {
                    this._img.style.opacity = this._options.opacity.toString();
                }
    
                this._rotateGroundOverlay();
    
                this.setHtmlElement(this._img);
            } else {
                this.setHtmlElement(null);
            }
        }
    
        /**
         * Rotates the ground overlay image.
         */
        private _rotateGroundOverlay(): void {
            if (this._img) {
                var rotation = this._options.rotation;
                if (!rotation) {
                    rotation = 0;
                }
    
                rotation = rotation;
    
                var rotateTransform = 'rotate(' + rotation + 'deg)';
                this._img.style['-webkit-transform'] = rotateTransform;
                this._img.style['-moz-transform'] = rotateTransform;
                this._img.style['-ms-transform'] = rotateTransform;
                this._img.style['-o-transform'] = rotateTransform;
                this._img.style['transform'] = rotateTransform;
            }
        }
    
        /**
         * Repositions the ground overlay when the map moves.
         */
        private _repositionGroundOverlay(): void {
            var map = this.getMap();
    
            if (map && this._img && this._options.bounds && this._options.visible) {
                //Streach and position the image based on the bounding box pixel coordinates.
                var topLeft = <Microsoft.Maps.Point>map.tryLocationToPixel(this._options.bounds.getNorthwest(), Microsoft.Maps.PixelReference.control);
                var bottomRight = <Microsoft.Maps.Point>map.tryLocationToPixel(this._options.bounds.getSoutheast(), Microsoft.Maps.PixelReference.control);
    
                this._img.style.left = topLeft.x + 'px';
                this._img.style.top = topLeft.y + 'px';
                this._img.style.width = (bottomRight.x - topLeft.x) + 'px';
                this._img.style.width = (bottomRight.x - topLeft.x) + 'px';
                this._img.style.height = (bottomRight.y - topLeft.y) + 'px';
            }
        }
    }
    
    Microsoft.Maps.moduleLoaded('GroundOverlaysModule');

    Here is the web app that implements this:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <style>
            html, body {
                height: 100%;
                margin: 0;
                padding: 0;
            }
    
            #myMap {
                height: 100%;
            }
        </style>
    </head>
    <body>
        <div id="myMap"></div>
        <script>
        var map;
        var groundOverlay;
    
        function initMap() {
            map = new Microsoft.Maps.Map('#myMap', {
                credentials: 'Your Bing Maps Key',
                zoom: 13,
                center: new Microsoft.Maps.Location(40.740, -74.18)
            });
    
            Microsoft.Maps.registerModule('GroundOverlaysModule', '../scripts/GroundOverlayModule.js');
            Microsoft.Maps.loadModule('GroundOverlaysModule', function () {
                groundOverlay = new GroundOverlay({
                    bounds: Microsoft.Maps.LocationRect.fromEdges(40.773941, -74.22655, 40.712216, -74.12544),
                    imageUrl: 'https://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg'
                });
                map.layers.insert(groundOverlay);
            });
        }
        </script>
    
        <script async defer src="https://bing.com/api/maps/mapcontrol?callback=initMap"></script>
    </body>
    </html>


    [Blog] [twitter] [LinkedIn]

    Thursday, January 5, 2017 7:19 PM

All replies

  • If you don't load your overlay, does the map load? If it does then it likely isn't an issue with the Bing Maps API not being fully loaded yet. If the map doesn't load then it's possible this is the issue. Can you add a breakpoint on the line of code where you load the map and see if that is the line of code that throws the error. If it is, then you may want to use the callback parameter of the map script URL to call the map load function as this will only fire once the map script has fully loaded.

    Setting the opacity of your image is fine.

    You can find a bunch of custom overlay samples here: https://msdn.microsoft.com/en-us/library/mt762874.aspx

    Also worth noting that TileLayers support WMS. Here is a code sample: https://msdn.microsoft.com/en-us/library/mt762880.aspx


    [Blog] [twitter] [LinkedIn]

    Friday, December 30, 2016 9:50 AM
  • yes, the problem is library is not fully loaded when angular loads the class I defined.

    I took the code from this example http://www.bing.com/api/maps/sdk/mapcontrol/isdk#imageOverlay+TS and added in the end of the file, outside the class and it works, but the method

    anyway I think there is a small error in this code :

    var topLeft = map.tryLocationToPixel(bounds.getNorthwest(), Microsoft.Maps.PixelReference.control);
    var bottomRight = map.tryLocationToPixel(bounds.getSoutheast(), Microsoft.Maps.PixelReference.control);
    img.style.left = topLeft.x + 'px';

    tryLocationToPixel returns, accordind to the .d.ts file, a Point | Point[] so I cannot read the x property directly.

    I'm not sure the callback approach is a good approach if I try to use bing maps sdk with angular or spa frameworks that use module loaders, especially if I want to use in my code classes derived from library classes.

    If this is already possible, can I have more samples on how to do this? 

    Friday, December 30, 2016 9:13 PM
  • The tryLocationToPixel code is correct. It returns a Point or an array of Point's depending on what is passed in. Your code passes in individual Location objects, so individual Point objects are returned. If property x is undefined when this code runs then the map is likely not loaded.

    Since the library isn't fully loaded when your class is loaded there are two ways to address this. The first is to load the map script URL using the callback function. Here is an example: http://stackoverflow.com/questions/37550278/how-to-add-bing-maps-v8-to-angular-2-0

    The second option is to add some logic that recursively waits for the Microsoft Maps namespace to become available. For example:

    function loadMap(){
    	if(typeof Microsoft !== 'undefined' &&
    	typeof Microsoft.Maps !== 'undefined' &&
    	typeof Microsoft.Maps.Map !== 'undefined'){
    		//Add logic to load the map control.
    	} else {
    		//Map script isn't yet fully loaded, wait for it.
    		setTimeout(loadMap, 100);
    	}
    }


    [Blog] [twitter] [LinkedIn]

    Sunday, January 1, 2017 12:13 PM
  • Ok, I'll try to explain better.

    I took the code of the custom overlay at this address  http://www.bing.com/api/maps/sdk/mapcontrol/isdk#imageOverlay+TS and tried to convert in a Ts class to make more readable.

    I created this class:

    module Palmare.Components {
     
        export class wmsOverlay extends Microsoft.Maps.CustomOverlay {
            private bounds: Microsoft.Maps.LocationRect;
            private image: string;
     
            private img: HTMLImageElement;
            private map: Microsoft.Maps.Map;
            private currentOpacity: number;
     
            public constructor(_map: Microsoft.Maps.Map, _image: string, _bounds: 
    Microsoft.Maps.LocationRect, _currentOpacity: number) {             super();             this.bounds = _bounds;             this.image = _image;             this.map = _map;             this.currentOpacity = _currentOpacity;         }         public onAdd(): void {             this.img = document.createElement('img');             this.img.src = this.image;             this.img.id = 'topographicOverlay';             this.img.style.width = '100%';             this.img.style.height = '100%';             this.img.style.position = 'absolute';             this.img.style.opacity = ''+this.currentOpacity;             this.setHtmlElement(this.img);         }         public onLoad(): void {             this.repositionOverlay();             Microsoft.Maps.Events.addHandler(this.map, 'viewchange', function () {                 this.repositionOverlay();             });          }         private repositionOverlay() {             var topLeft = Helpers.bingHelper.tryLocationToPixel(this.map, 
    this.bounds.getNorthwest(), Microsoft.Maps.PixelReference.control);             var bottomRight = Helpers.bingHelper.tryLocationToPixel(this.map, 
    this.bounds.getSoutheast(), Microsoft.Maps.PixelReference.control);             this.img.style.left = topLeft.x + 'px';             this.img.style.top = topLeft.y + 'px';             this.img.style.width = (bottomRight.x - topLeft.x) + 'px';             this.img.style.width = (bottomRight.x - topLeft.x) + 'px';             this.img.style.height = (bottomRight.y - topLeft.y) + 'px';         }     } }

    ts is configured to create a single file .js that is loaded after the ref to the bingmap js file this way in the _layeout file

    <script type='text/javascript' src='http://www.bing.com/api/maps/mapcontrol?branch=release'>
    </
    script>

    <script src="~/Scripts/angular.js"></script>

    <script src="~/Scripts/angular-sanitize.js"></script>

    <script src="~/Scripts/angular-touch.js"></script>

    <script src="~/Scripts/angular-animate.js"></script>

    <script src="~/Scripts/moment.js"></script>

    <script src="~/Scripts/angular-ui/ui-bootstrap-tpls.js"></script>

    <script src="~/Scripts/ui-grid.js"></script>

    <script src="~/Scripts/toastr.js"></script>

    <script src="~/Scripts/angular-busy.js"></script>

    <script src="~/Scripts/slider.js"></script>

    <script src="~/Scripts/checklist-model.js"></script>

    <script src="~/Scripts/d3/d3.js"></script>

    <script src="~/Scripts/d3effects/bullet.js"></script>

    <script src="~/Scripts/application.js"></script>

    the problem is NOT i'm trying to programmatically execute some code before the library is loaded: the map itself is loaded after a few user interactions of the user in the loaded page.

    The problem is the loading is in the loading of the application.js itself when I open the page

    this is the message error I have LOADING the page on the top of the javascript file generated by typescript: I suppose that is looking for the prototype of the Microsoft.Maps.CustomOverlay class when is not fully loaded.

    If this is the problem, the only way I can use the callback is to use it to inject the link to the js application file.

    I hope I explained the problem



    Tuesday, January 3, 2017 7:15 PM
  • Try adding a break point on your wmsOverlay class and stepping through your code to verify if it is actually that class that it throwing the error. I've written a number of modules in TypeScript so I doubt that is the issue. It is possible that one of the additional libraries you have is causing conflicts, but I'd be surprised. The custom overlay class is pretty low level and doesn't rely on anything libraries, just plain old JavaScript.

    [Blog] [twitter] [LinkedIn]

    Tuesday, January 3, 2017 11:37 PM
  • if I comment or remove that class the application works fine, and I almost completed the migration of the whole procedure.

    This is the only class I use that extendes one of the bing maps api components.

    Thursday, January 5, 2017 3:41 PM
  • I've created a number of modules that extend the CustomOverlay class in TypeScript without any issues. Here is one I created.

    Here is the module code:

    /// <reference path="MicrosoftMaps/Microsoft.Maps.d.ts"/>
    
    /**
    * The options that specify how to render a ground overlay on the map.
    */
    interface IGroundOverlayOptions extends Microsoft.Maps.ICustomOverlayOptions {
        /** A background color that fills the bounding box area beneath the ground overlay. */
        backgroundColor?: string | Microsoft.Maps.Color;
    
        /** The bounding box to anchor the ground overlay to. */
        bounds: Microsoft.Maps.LocationRect;
    
        /** The URL to the image to anchor to the map as a ground overlay. */
        imageUrl: string;
    
        /** The opacity of the ground overlay image. */
        opacity?: number;
    
        /** An angle in degrees to rotate the overlay in a counter-clockwise direction where 0 = north, 90 = west, 180 = south, 270 = east */
        rotation?: number;
    
        /** A boolean value indicating if the ground overlay is visible or not. */
        visible?: boolean;
    }
    
    /**
     * A map overlay that binds an image to a bounding box area on the map.
     */
    class GroundOverlay extends Microsoft.Maps.CustomOverlay {
    
        /***********************************
        * Private Properties
        ***********************************/
    
        /** The options used to define how the ground overlay is rendered. */
        private _options: IGroundOverlayOptions = {
            beneathLabels: true,
            backgroundColor: 'transparent',
            bounds: null,
            imageUrl: null,
            opacity: 1,
            rotation: 0,
            visible: true
        };
    
        /** The image DOM element that is used to render the ground overlay. */
        private _img: HTMLImageElement;
    
        private _mapViewChangeEvent: Microsoft.Maps.IHandlerId;
    
        /***********************************
        * Constructor
        ***********************************/
    
        /**
         * @constructor
         * @param options The options used to render the ground overlay.
         */
        constructor(options: IGroundOverlayOptions) {
            super({
                beneathLabels: (typeof options.beneathLabels === 'boolean') ? options.beneathLabels : true
            });
    
            this.setOptions(options);
        }
    
        /***********************************
        * Public Properties
        ***********************************/
    
        /** Optional property to store any additional metadata for this layer. */
        public metadata: any;
    
        /***********************************
        * Public functions
        ***********************************/
    
        /**
         * Clears the ground overlay.
         */
        public clear() {
            //Reset the options back to the defaults.
            this._options = {
                backgroundColor: 'transparent',
                bounds: null,
                imageUrl: null,
                opacity: 1,
                rotation: 0,
                visible: true
            };
    
            this.setHtmlElement(null);
        }
    
        /**
         * Gets the background color of the ground overlay.
         * @returns The background color of the ground overlay.
         */
        public getBackgroundColor(): string | Microsoft.Maps.Color {
            return this._options.backgroundColor;
        }
    
        /**
         * Gets the bounding box that the ground overlay is bounded to.
         * @returns The bounding box that the ground overlay is bounded to.
         */
        public getBounds(): Microsoft.Maps.LocationRect {
            return this._options.bounds;
        }
    
        /**
         * Gets the url to the gorund overlay image.
         * @returns The url to the gorund overlay image.
         */
        public getImageUrl(): string {
            return this._options.imageUrl;
        }
    
        /**
         * Gets the opacity of the ground overlay.
         * @returns The opacity of the ground overlay.
         */
        public getOpacity(): number {
            return this._options.opacity;
        }
    
        /**
         * Gets the rotation of the ground overlay.
         * @returns The rotation of the ground overlay.
         */
        public getRotation(): number {
            return this._options.opacity;
        }
    
        /**
         * Gets a boolean indicating if the ground overlay is visible or not.
         * @returns A boolean indicating if the ground overlay is visible or not.
         */
        public getVisible(): boolean {
            return this._options.visible;
        }
    
        /**
         * Sets the options used to render the ground overlay.
         * @param options The options used to render the ground overlay.
         */
        public setOptions(options: IGroundOverlayOptions): void {
            if (options && typeof options.beneathLabels === 'boolean') {
                this._options.beneathLabels = options.beneathLabels;
            }
    
            if (typeof options.backgroundColor !== 'undefined') {
                this._options.backgroundColor = options.backgroundColor;
            }
    
            if (typeof options.opacity === 'number') {
                this._options.opacity = options.opacity;
                if (this._img) {
                    this._img.style.opacity = this._options.opacity.toString();
                }
            }
    
            if (typeof options.rotation === 'number') {
                this._options.rotation = options.rotation;
                this._rotateGroundOverlay();
            }
    
            var reposition = false;
    
            if (typeof options.visible === 'boolean') {
                this._options.visible = options.visible;
                if (this._img) {
                    if (options.visible) {
                        this._img.style.display = '';
                        reposition = true;
                    } else {
                        this._img.style.display = 'none';
                    }
                }
            }
    
            if (options.bounds instanceof Microsoft.Maps.LocationRect) {
                this._options.bounds = options.bounds;
                reposition = true;
            }
    
            if (typeof options.imageUrl !== 'undefined') {
                this._options.imageUrl = options.imageUrl;
                this._loadGroundOverlayImage();
            }
    
            if (reposition) {
                this._repositionGroundOverlay();
            }
        }
    
        /***********************************
        * Inherited Events
        ***********************************/
    
        /**
         * The event logic that processes when the ground overlay is added to the map.
         */
        public onAdd(): void {
            this._loadGroundOverlayImage();
        }
    
        /**
         * The event logic that processes when the ground overlay is initially loaded.
         */
        public onLoad(): void {
            this._repositionGroundOverlay();
    
            this._mapViewChangeEvent = Microsoft.Maps.Events.addHandler(this.getMap(), 'viewchange', () => { this._repositionGroundOverlay() });
        }
    
        /**
         * The event logic that processes when the ground overlay is removed from the map.
         */
        public onRemove(): void {
            if (this._mapViewChangeEvent) {
                Microsoft.Maps.Events.removeHandler(this._mapViewChangeEvent);
            }
            this.setHtmlElement(null);
        }
    
        /***********************************
        * Private Functions
        ***********************************/
    
        /**
         * Loads the ground overlay image.
         */
        private _loadGroundOverlayImage(): void {
            if (this._options.imageUrl) {
                //Create an image element that will be used as the overlay.
                this._img = document.createElement('img');
                this._img.src = this._options.imageUrl;
                this._img.style.width = '100%';
                this._img.style.height = '100%';
                this._img.style.position = 'absolute';
                this._img.style.display = (this._options.visible) ? '' : 'none';
    
                if (this._options.backgroundColor) {
                    if (this._options.backgroundColor instanceof Microsoft.Maps.Color) {
                        this._img.style.backgroundColor = (<Microsoft.Maps.Color>this._options.backgroundColor).toRgba();
                    } else {
                        this._img.style.backgroundColor = <string>this._options.backgroundColor;
                    }
                }
    
                if (typeof this._options.opacity === 'number') {
                    this._img.style.opacity = this._options.opacity.toString();
                }
    
                this._rotateGroundOverlay();
    
                this.setHtmlElement(this._img);
            } else {
                this.setHtmlElement(null);
            }
        }
    
        /**
         * Rotates the ground overlay image.
         */
        private _rotateGroundOverlay(): void {
            if (this._img) {
                var rotation = this._options.rotation;
                if (!rotation) {
                    rotation = 0;
                }
    
                rotation = rotation;
    
                var rotateTransform = 'rotate(' + rotation + 'deg)';
                this._img.style['-webkit-transform'] = rotateTransform;
                this._img.style['-moz-transform'] = rotateTransform;
                this._img.style['-ms-transform'] = rotateTransform;
                this._img.style['-o-transform'] = rotateTransform;
                this._img.style['transform'] = rotateTransform;
            }
        }
    
        /**
         * Repositions the ground overlay when the map moves.
         */
        private _repositionGroundOverlay(): void {
            var map = this.getMap();
    
            if (map && this._img && this._options.bounds && this._options.visible) {
                //Streach and position the image based on the bounding box pixel coordinates.
                var topLeft = <Microsoft.Maps.Point>map.tryLocationToPixel(this._options.bounds.getNorthwest(), Microsoft.Maps.PixelReference.control);
                var bottomRight = <Microsoft.Maps.Point>map.tryLocationToPixel(this._options.bounds.getSoutheast(), Microsoft.Maps.PixelReference.control);
    
                this._img.style.left = topLeft.x + 'px';
                this._img.style.top = topLeft.y + 'px';
                this._img.style.width = (bottomRight.x - topLeft.x) + 'px';
                this._img.style.width = (bottomRight.x - topLeft.x) + 'px';
                this._img.style.height = (bottomRight.y - topLeft.y) + 'px';
            }
        }
    }
    
    Microsoft.Maps.moduleLoaded('GroundOverlaysModule');

    Here is the web app that implements this:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <style>
            html, body {
                height: 100%;
                margin: 0;
                padding: 0;
            }
    
            #myMap {
                height: 100%;
            }
        </style>
    </head>
    <body>
        <div id="myMap"></div>
        <script>
        var map;
        var groundOverlay;
    
        function initMap() {
            map = new Microsoft.Maps.Map('#myMap', {
                credentials: 'Your Bing Maps Key',
                zoom: 13,
                center: new Microsoft.Maps.Location(40.740, -74.18)
            });
    
            Microsoft.Maps.registerModule('GroundOverlaysModule', '../scripts/GroundOverlayModule.js');
            Microsoft.Maps.loadModule('GroundOverlaysModule', function () {
                groundOverlay = new GroundOverlay({
                    bounds: Microsoft.Maps.LocationRect.fromEdges(40.773941, -74.22655, 40.712216, -74.12544),
                    imageUrl: 'https://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg'
                });
                map.layers.insert(groundOverlay);
            });
        }
        </script>
    
        <script async defer src="https://bing.com/api/maps/mapcontrol?callback=initMap"></script>
    </body>
    </html>


    [Blog] [twitter] [LinkedIn]

    Thursday, January 5, 2017 7:19 PM
  • hmm, looks like we have different typescript compilation configurations: to use registerModule/loadModule I suppose you compile and/or deploy components as single js files.

    I'm working to an application with more than 100 files, and I configured ts to compile everything in a single js file, so ts can automatically handle all the semantic dependencies and avoid the problem of add every file in the right position in the layout, so all the js is in the same file and I don't think I can use this method.

    I need to create these component inside the main project because I need that they access to the application context and data, so I can't compile them in a different project and deploy as indipendent files: if this is the problem I have to check if and how I can use multiple tsconfig.json files inside the same project and compile just the component files as single js files.

    Anyway, this is just the first attempt to migrate an angular 1.* application to bing api 8: next step is to us it inside a new angular2 + webpack application, and since webpack rebundle everything in its own way, I would like to understand how to make work the module loading of bing maps in this scenario

    Saturday, January 7, 2017 8:21 AM