How can I overlay SVG diagrams on Google Maps?

后端 未结 3 1010
迷失自我
迷失自我 2020-12-24 08:11

I would like to add an overlay image on a Google Map. The image is a SVG file I have generated (Python with SVGFig).

I am using the following code:

i         


        
相关标签:
3条回答
  • 2020-12-24 08:40

    This question was briefly discussed on the Google Maps API Group. Here's what they said:

    I've not tried it, but SVG is a subset of XML, so you can read them with GDownloadUrl() and analyse them with GXml.parse(). On some wonky webservers you may have to change the file extension to XML.

    You then have to crawl through the XML DOM, writing the SVG that you find with document.createElementNS() and .setAttribute() calls...

    There are also a some Google Maps SVG examples here and here.

    Good luck!

    0 讨论(0)
  • 2020-12-24 08:47

    Here are some news (I hope it's better to put them here in an answer, instead of editing my questions or to create a new question. Please feel free to move it if needed, or to tell me, so as I can rectify):

    My problem was the following:

    var oldmap = new GGroundOverlay("test.svg", boundaries);
    map.addOverlay(oldmap);
    

    did not work on Safari 3, Firefox and Opera (IE is not enable to draw SVG).

    In fact, this code produce the insertion (in a <div>) of the following element

    <img src="test.svg" style=".....">
    

    And Safari 4 is able to draw a SVG file as an image, but this is not the way to do for the other browser. So the idea is now to create a custom overlay for the SVG, as explained here.

    That's the reason why I asked for this question (I am sorry, but HTML/javascript are not my strongest points).

    And since there is a small bug with Webkit for rendering a SVG with transparent background with <object>element, I need to use <object> or <img> accordingly to the browser (I don't like this, but... for the moment, it's still the quick-and-dirty experiments)

    So I started with this code (still work in progress):

    // create the object
    function myOverlay(SVGurl, bounds)
    {
        this.url_ = SVGurl;
        this.bounds_ = bounds;
    }
    
    // prototype
    myOverlay.prototype = new GOverlay();
    
    // initialize
    myOverlay.prototype.initialize = function(map)
    {
        // create the div
        var div = document.createElement("div");
        div.style.position = "absolute";
        div.setAttribute('id',"SVGdiv");
        div.setAttribute('width',"900px");
        div.setAttribute('height',"900px");
    
        // add it with the same z-index as the map
        this.map_ = map;
        this.div_ = div;
    
        //create new svg root element and set attributes
        var svgRoot;
        if (BrowserDetect.browser=='Safari')
        {
            // Bug in webkit: with <objec> element, Safari put a white background... :-(
            svgRoot = document.createElement("img");
            svgRoot.setAttribute("id", "SVGelement");
            svgRoot.setAttribute("type", "image/svg+xml");
            svgRoot.setAttribute("style","width:900px;height:900px");
            svgRoot.setAttribute("src", "test.svg");
        }
        else //if (BrowserDetect.browser=='Firefox')
        {
            svgRoot = document.createElement("object");
            svgRoot.setAttribute("id", "SVGelement");
            svgRoot.setAttribute("type", "image/svg+xml");
            svgRoot.setAttribute("style","width:900px;height:900px;");
            svgRoot.setAttribute("data", "test.svg");
        }
    
    
        div.appendChild(svgRoot);
        map.getPane(G_MAP_MAP_PANE).appendChild(div);
    
        //this.redraw(true);
    } 
    
    ...
    

    The draw function is not yet written.

    I still have a problem (I progress slowly, thanks to what I read/learn everywhere, and also thanks to people who answer my questions).

    Now, the problem is the following : with the <object> tag, the map is not draggable. All over the <object> element, the mouse pointer is not "the hand icon" to drag the map, but just the normal pointer.

    And I did not find how to correct this. Should I add a new mouse event (I just saw mouse event when a click or a double-click append, but not for dragging the map...) ?

    Or is there another way to add this layer so as to preserve the drag-ability ?

    Thank you for your comments and answers.

    PS: I also try to add one by one the elements of my SVG, but... in fact... I don't know how to add them in the DOM tree. In this example, the SVG is read and parsed with GXml.parse(), and all the elements with a given tag name are obtained (xml.documentElement.getElementsByTagName) and added to the SVG node (svgNode.appendChild(node)). But in my case, I need to add directly the SVG/XML tree (add all its elements), and there are different tags (<defs>, <g>, <circle>, <path>, etc.). It is may be simpler, but I don't know how to do.. :(

    0 讨论(0)
  • 2020-12-24 08:47

    I spend the last evening on this problem, and I finally found the solution to my problem.

    It was not so difficult.

    The idea is, as Chris B. said, to load the SVG file with GDownloadUrl, parse it with GXml.parse() and add in the DOM tree every SVG elements I need

    To simplify, I have supposed that all the SVG elements was put in a big group called "mainGroup". I have also supposed that some elements can be in the file.

    So here is the library, based on the Google Maps Custom Overlays:

    // create the object
    function overlaySVG( svgUrl, bounds)
    {
        this.svgUrl_ = svgUrl;
        this.bounds_ = bounds;
    }
    
    
    // prototype
    overlaySVG.prototype = new GOverlay();
    
    
    // initialize
    overlaySVG.prototype.initialize = function( map)
    {
        //create new div node 
        var svgDiv = document.createElement("div");
        svgDiv.setAttribute( "id", "svgDivison");
        //svgDiv.setAttribute( "style", "position:absolute");
        svgDiv.style.position = "absolute";
        svgDiv.style.top = 0;
        svgDiv.style.left = 0;
        svgDiv.style.height = 0;
        svgDiv.style.width = 0;
        map.getPane(G_MAP_MAP_PANE).appendChild(svgDiv);
    
        // create new svg element and set attributes
        var svgRoot = document.createElementNS( "http://www.w3.org/2000/svg", "svg");
        svgRoot.setAttribute( "id", "svgRoot");
        svgRoot.setAttribute( "width", "100%");
        svgRoot.setAttribute( "height","100%");
        svgDiv.appendChild( svgRoot);
    
        // load the SVG file
        GDownloadUrl( this.svgUrl_, function( data, responseCode)
        {
            var xml = GXml.parse(data);
            // specify the svg attributes
            svgRoot.setAttribute("viewBox", xml.documentElement.getAttribute("viewBox"));
            // append the defs
            var def = xml.documentElement.getElementsByTagName("defs");
            //for( var int=0; i<def.length; i++)
                svgRoot.appendChild(def[0].cloneNode(true));
            //append the main group
            var nodes = xml.documentElement.getElementsByTagName("g");
            for (var i = 0; i < nodes.length; i++)
                if (nodes[i].id=="mainGroup")
                    svgRoot.appendChild(nodes[i].cloneNode(true));
        });
    
        // keep interesting datas
        this.svgDiv_ = svgDiv;
        this.map_ = map;
    
        // set position and zoom
        this.redraw(true);
    }
    
    
    
    // remove from the map pane
    overlaySVG.prototype.remove = function()
    {
        this.div_.parentNode.removeChild( this.div_);
    }
    
    
    // Copy our data to a new overlaySVG...
    overlaySVG.prototype.copy = function()
    {
        return new overlaySVG( this.url_, this.bounds_, this.center_);
    }
    
    
    // Redraw based on the current projection and zoom level...
    overlaySVG.prototype.redraw = function( force)
    {
        // We only need to redraw if the coordinate system has changed
        if (!force) return;
        // get the position in pixels of the bound
        posNE = map.fromLatLngToDivPixel(this.bounds_.getNorthEast());      
        posSW = map.fromLatLngToDivPixel(this.bounds_.getSouthWest());
        // compute the absolute position (in pixels) of the div ...
        this.svgDiv_.style.left = Math.min(posNE.x,posSW.x) + "px";
        this.svgDiv_.style.top = Math.min(posSW.y,posNE.y) + "px";
        // ... and its size
        this.svgDiv_.style.width = Math.abs(posSW.x - posNE.x) + "px";
        this.svgDiv_.style.height = Math.abs(posSW.y - posNE.y) + "px";
    }
    

    And, you can use it with the following code:

    if (GBrowserIsCompatible())
    {
        //load map
        map = new GMap2(document.getElementById("map"), G_NORMAL_MAP);
        // create overlay   
        var boundaries = new GLatLngBounds( new GLatLng(48.2831, 1.9675), new GLatLng(49.1872, 2.7774));
        map.addOverlay( new overlaySVG( "test.svg", boundaries ));
        //add control and set map center
        map.addControl(new GLargeMapControl());
        map.setCenter(new GLatLng(48.8, 2.4), 12);
    }   
    

    So, you can use it exactly as you use the GGroundOverlay function, except that your SVG file should be created with the Mercator projection (but if you apply it on small area, like one city or smaller, you will not see the difference).

    This should work with Safari, Firefox and Opera. You can try my small example here

    Tell me what do you think about it.

    0 讨论(0)
提交回复
热议问题