Using an svg object as a container for a Google map - javascript
As the title says I have an svg object and I want that object to contain a Google map instance. I was trying to do this the same way I usually do with divs or simple elements, using Javascript and getElementByID and placing the map inside the element. however this does not seem to work here. I have done a lot of searching but since Google uses svg for custom icons and overlays all the responses are related to that. I've mostly tried different approaches regarding where to put the id="map_canvas but nothing has worked.
My HTML with the svg as an object:
<div class="row map">
<div class="span12">
<object type="image/svg+xml" id="map_canvas" data="assets/map/MA_map.svg"></object>
</div>
</div>
My JavaScript (this is currently in the <head> tag but I want to move it to the .js document):
<script>
function initialize() {
var mapCanvas = document.getElementById('map_canvas');
var mapOptions = {
center: new google.maps.LatLng(44.5403, -78.5463),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(mapCanvas, mapOptions)
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
And finally this is what my svg file looks like (created in Illustrator):
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
id="map_canvas"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="-79.892 94.942 960 599.864"
enable-background="new -79.892 94.942 960 599.864"
xml:space="preserve">
<filter id="dropshadow" height="130%">
<feGaussianBlur in="SourceAlpha" stdDeviation="3"/> <!-- stdDeviation is how much to blur -->
<feOffset dx="2" dy="2" result="offsetblur"/> <!-- how much to offset -->
<feMerge>
<feMergeNode/> <!-- this contains the offset blurred image -->
<feMergeNode in="SourceGraphic"/> <!-- this contains the element that the filter is applied to -->
</feMerge>
</filter>
<path
id="map_canvas"
style="filter:url(#dropshadow)"
<path //lots of lines of code for the path />
</svg>
Possible solutions you can try:
Use an SVG <foreignObject> with a <clipPath> or <mask>. I am not sure how well this will work though.
Use a CSS Mask (http://caniuse.com/#feat=css-masks).
Create a maps Polygon that has the same colour as the background and which is the size of the map and has a hole the shape of your mask.
Cover the map with an <img> (eg. PNG) that has the same colour or texture as the background and which has a hole the shape of your mask.
Related
How do I get d3.zoom working for inline svg?
I am trying to use d3.zoom on an inline svg, specifically a map of the USA (please note that I am very inexperienced at d3js). After some research I found a simple tutorial for using d3.zoom https://www.datamake.io/blog/d3-zoom/. The issue I have now is that it just doesn't work. There is no error which shows up. It just does nothing. Here is the HTML code with the svg map. <div id="map"> <?xml version="1.0" encoding="UTF-8"?> <svg id="usa_svg" height="800" width="1200" version="1.1" viewBox="0 0 800 600" xmlns="http://www.w3.org/2000/svg"> <g id="USA"> <rect height="600" width="1200"></rect> <!-- paths here --> </g> </svg> </div> Here is the JavaScript var usa = d3.select("svg") var rect = d3.select("rect"); var zoom = d3.zoom().on("zoom", zoomed); rect.call(zoom); function zoomed() { var transform = d3.event.transform; usa.attr("transform", transform.toString()); } I have no clue what I am doing wrong. Is it a syntax error? Am I just missing a vital part of the code? Help would be much appreciated.
I need help understanding how to get s stringified (variable) version of an SVG image
I am currently working with an svg image that I need help stringifying, If that makes sense. Basically how to make the image open in a text/code editor as only variables? Here is an example of what I am looking for: var svgData='PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiB2ZXJzaW9uPSIxLjEiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0ic3Ryb2tl Currently when open an svg image in a text editor I just get something like this. <?xml version="1.0" encoding="utf-8"?> <!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 255.1 227.3" style="enable-background:new 0 0 255.1 227.3;" xml:space="preserve"> <style type="text/css"> Any help is welcome! Thank You.
It looks like svgData is cut a little short. But if I understand your question right, you want that encoded svgData available in a textarea? You can use the native functions btoa and atob (https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding) to encode and decode the svg content. For example using your svgData value: atob("PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiB2ZXJzaW9uPSIxLjEiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0ic3Ryb2tl") = <svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" version="1.1"><defs><linearGradient id="stroke" and we can convert it back with: btoa('<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" version="1.1"><defs><linearGradient id="stroke"') = "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiB2ZXJzaW9uPSIxLjEiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0ic3Ryb2tlIg=="
SVG Marker gets rendered differently on different screens
Update: I figured out the screen issue. device pixel ratio is the culprit. On devices with lower window.devicePixelRatio the icon gets displayed smaller, a solution is to make the size of the icon conditional on window.devicePixelRatio, i.e. : scaledSize: highDevicePixelRatio ? new google.maps.Size(40, 60) : new google.maps.Size(60, 90) resolution might also play a role, but I couldn't test that as of now. The issue with Internet Explorer 11 still exists though. ** End Update ** So this is really absurd and I am somewhat still baffled. I noticed this extremely inconsistent behavior of my custom markers. Drove me crazy, because I couldn't figure out why they would be behaving differently. I just now realized it depends on the screen I am displaying the map/marker on. I am using https://github.com/tomchentw/react-google-maps. I am using a MacBookPro 2015 and a LG 34UC98-W hooked up via HDMI cable. But not only the screen, also the used browser gives different results. It works somewhat fine on chrome (differences in screens), the marker don't show up at all in IE 11 (haven't tested FireFox). Now this is how I currently instantiate my marker: const marker = { position: new google.maps.LatLng(this.state.center.lat,this.state.center.lng), icon: { url: icon_url(this.props.markerIcon,'purple'), anchor: new google.maps.Point(13,42), scaledSize: new google.maps.Size(40, 60) }, draggable: false, } On my MacBook I get the following result: On my LG I get the following result: This is driving me NUTS, is there a way to achieve consistent behavior across screens/browsers? What is the reason for this??? Following the SVG Code: I tried with and without the explicit width and height attributes in the first svg tag. Does not make a difference. <?xml version="1.0" encoding="iso-8859-1"?> <!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="-291 377 28 40" width="28px" height="40px" style="enable-background:new -291 377 28 40;" xml:space="preserve"> <path id="pin" style="fill:#7261C3;stroke:#4B4080;stroke-width:0.6909;stroke-linejoin:round;stroke-miterlimit:10;" d=" M-277,416.286c9.188-11.902,13-17.688,13-25.75c0-7.188-5.062-12.823-13-12.823s-13,5.635-13,12.823 C-290,398.599-286.188,404.384-277,416.286z"/> <g id="subject"> <path style="fill:#FFFFFF;" d="M-285.998,394.741c0.367,0.197,0.788-0.671,0.966-1.095c0.067-0.16,0.137-0.325,0.206-0.484 c0.354-0.812,0.713-1.485,1.1-2.059c0.451-0.67,0.933-1.2,1.473-1.618c0.477-0.37,1.017-0.658,1.603-0.857 c0.28-0.095,0.564-0.167,0.851-0.217c-0.172,0.361-0.319,0.733-0.437,1.11c-0.097,0.31-0.175,0.624-0.233,0.939 c0.208,0.046,0.407,0.131,0.582,0.253c0.072,0.062,0.144,0.125,0.216,0.187c0.122-0.854,0.404-1.684,0.812-2.444 c0.15-0.279,0.316-0.548,0.499-0.807c0.022-0.031,0.025-0.072,0.009-0.106c-0.016-0.034-0.05-0.057-0.088-0.06 c-0.137-0.009-0.275-0.014-0.413-0.014c-1.407,0-2.814,0.473-3.928,1.336c-1.309,1.013-2.173,2.495-2.834,4.012 C-285.864,393.397-286.442,394.479-285.998,394.741z"/> <path style="fill:#FFFFFF;" d="M-267.856,399.462l-0.017-0.056c-0.031-0.104-0.104-0.191-0.2-0.24 c-0.097-0.049-0.209-0.057-0.312-0.021c-0.958,0.332-1.973,0.5-2.987,0.5c-0.685,0-1.37-0.077-2.037-0.233 c0.14,0.316,0.274,0.635,0.4,0.956c0.536,0.09,1.083,0.135,1.637,0.135c0.848,0,1.693-0.107,2.511-0.319 c0.254-0.066,0.504-0.141,0.75-0.226C-267.906,399.889-267.794,399.669-267.856,399.462z"/> <path style="fill:#FFFFFF;" d="M-275.021,394.647c0.312-0.814,0.542-1.659,0.684-2.518c0.022-0.135,0.059-0.271,0.107-0.399 c0.159-0.423,0.284-0.862,0.373-1.304c0.083-0.414,0.134-0.834,0.154-1.256c0.544,0.229,1.111,0.434,1.697,0.611 c0.83,0.252,1.685,0.446,2.542,0.577c0.025,0.004,0.05,0.006,0.075,0.006c0.238,0,0.447-0.174,0.485-0.417 c0.041-0.268-0.143-0.518-0.411-0.559c-0.811-0.124-1.621-0.308-2.406-0.546c-0.777-0.235-1.521-0.522-2.212-0.851 c-0.374-0.178-0.752-0.378-1.117-0.571c-0.055-0.029-0.109-0.058-0.164-0.087l-0.138-0.073c-0.554-0.292-1.078-0.568-1.572-0.901 c-0.407-0.273-0.791-0.586-1.142-0.927c-0.194-0.189-0.505-0.185-0.694,0.01c-0.189,0.194-0.185,0.505,0.01,0.694 c0.393,0.383,0.824,0.732,1.279,1.038c0.459,0.309,0.942,0.573,1.417,0.825c-0.039,0.306-0.084,0.612-0.136,0.914 c-0.088,0.518-0.196,1.038-0.321,1.55c-0.267,0.019-0.533,0.046-0.797,0.08c-0.179,0.023-0.388,0.053-0.598,0.122 c-0.275,0.091-0.502,0.232-0.676,0.419c-0.225,0.243-0.328,0.533-0.404,0.745c-0.028,0.077-0.055,0.154-0.083,0.232 c-0.334-0.326-0.679-0.641-1.034-0.945c-0.238-0.166-0.527-0.257-0.817-0.259c-0.29-0.002-0.58,0.086-0.82,0.249 c-0.258,0.175-0.457,0.436-0.557,0.732c-0.1,0.295-0.101,0.623-0.003,0.919c0.955,2.143,2.443,4.046,4.293,5.489 c1.172,0.914,2.487,1.645,3.882,2.158c0.084,0.031,0.178,0.01,0.241-0.053c0.063-0.063,0.084-0.157,0.053-0.241 c-0.441-1.188-0.985-2.337-1.624-3.431c-0.26,0.242-0.603,0.508-0.997,0.685c-0.001-0.001-0.001-0.001-0.001-0.001 C-275.871,396.518-275.389,395.605-275.021,394.647z M-275.542,391.889c-0.128,0.791-0.338,1.569-0.625,2.318 c-0.107,0.28-0.225,0.555-0.354,0.826c-0.389-0.546-0.803-1.073-1.24-1.581c-0.082,0.171-0.182,0.333-0.3,0.482 c-0.162,0.207-0.356,0.389-0.572,0.538c0.003-0.007,0.006-0.015,0.008-0.022c0.263-0.732,0.53-1.476,0.792-2.21 c0.044-0.124,0.095-0.265,0.148-0.323c0.03-0.032,0.088-0.064,0.16-0.088c0.107-0.035,0.236-0.053,0.369-0.07 c0.342-0.044,0.689-0.074,1.035-0.092c0.129-0.006,0.259,0.014,0.379,0.062c0.046,0.018,0.091,0.035,0.136,0.052 C-275.56,391.798-275.534,391.843-275.542,391.889z"/> <ellipse style="fill:#FFFFFF;" cx="-274.416" cy="385.286" rx="1.486" ry="1.739"/> </g> </svg>
I had a similar issue recently and had to change the meta tag in my html to work across devices. Seems like currently you don't have one so maybe try adding the following to your html: <meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0, user-scalable=0"> If you're only modifying the XML then you may have to use /> to close the tag or use the xml namespace as referenced in this post
Why is my canvas embedded in an inline SVG not updating?
It seems like a contrived problem but I am trying to make a proof of concept of an HTML 5 document, containing inline SVG, which itself contains a <foreignObject> element with an HTML canvas inside. I then want to call a JavaScript function to draw on the canvas. (How I got here: I want to use SVG as a format for defining an animated graphical view which can contain graphs, which are animated by replacing a dummy element in the SVG with a live graph using JavaScript / ECMAScript, and one of the JavaScript libraries we are considering to generate the graphs (Flot) uses an HTML canvas for the view, so I want to inject this canvas into the SVG view. A further complication is we ideally want this system to run on local files, on the file:// protocol, meaning we can't run scripts on external files (e.g. referenced with <object> or <iframe>) because browsers block them, hence the inline SVG.) Here is an HTML source illustrating the problem: <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Canvas Test</title> <script type="text/javascript"> function draw_box() { var canvas = document.getElementById("box_canvas"); var context = canvas.getContext("2d"); context.fillRect(50, 20, 50, 60); } </script> </head> <body> <h1>Canvas Test <p> <svg width="400" viewBox="-10 -10 268 108"> <rect style="fill: #a0a0a0" x="8" y="8" width="240" height="80" rx="40" ry="40"/> <g> <rect style="fill: #0000ff" x="0" y="0" width="240" height="80" rx="40" ry="40"/> <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke-linejoin: round; stroke: #000000" x="0" y="0" width="240" height="80" rx="40" ry="40"/> <foreignObject x="120" y="25" width="150" height="100"> <canvas id="box_canvas" width="150" height="100" style="border:1px dotted;float:left">Canvas alt text</canvas> </foreignObject> </g> </svg> <p> <button type="button" onclick="draw_box();return false">Animate</button> </body> </html> The above page doesn't display the canvas at all in IE 11. In the latest version of Chrome it displays the canvas, but the effects of the JavaScript function called by the button - drawing the black box - only show up if you click on the SVG image. What I need to know: 1) Is there something wrong with what I've written - e.g. am I missing some function call to make the SVG or foreignObject update after I have drawn on the canvas? 2) Is this a lost cause? Is the specification of <foreignObject> etc. immature enough that I could never be confident that this will work, even if we can control what browser the user views the document in? It's actually fine if this is the case because we can just say going forward that we need our graph library to generate SVG and not HTML. Thanks a lot
How to access SVG elements with Javascript
I'm messing around with SVG and I was hoping I could create SVG files in Illustrator and access elements with Javascript. Here's the SVG file Illustrator kicks out (It also seems to add a load of junk to the beginning of the file that I've removed) <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="276.843px" height="233.242px" viewBox="0 0 276.843 233.242" enable-background="new 0 0 276.843 233.242" xml:space="preserve"> <path id="delta" fill="#231F20" d="M34.074,86.094L0,185.354l44.444,38.519l80.741-0.74l29.63-25.186l-26.667-37.037 c0,0-34.815-5.926-37.778-6.667s-13.333-28.889-13.333-28.889l7.407-18.519l31.111-2.963l5.926-21.481l-12.593-38.519l-43.704-5.185 L34.074,86.094z"/> <path id="cargo" fill="#DFB800" d="M68.148,32.761l43.704,4.445l14.815,42.963l-7.407,26.667l-33.333,2.963l-4.444,14.074 l54.074-1.481l22.222,36.296l25.926-3.704l25.926-54.074c0,0-19.259-47.408-21.481-47.408s-31.852-0.741-31.852-0.741 l-19.259-39.259L92.593,8.316L68.148,32.761z"/> <polygon id="beta" fill="#35FF1F" points="86.722,128.316 134.593,124.613 158.296,163.872 190.889,155.724 214.593,100.909 194.593,52.02 227.186,49.057 246.444,92.02 238.297,140.909 216.074,172.761 197.556,188.316 179.778,169.798 164.963,174.983 163.481,197.946 156.815,197.946 134.593,159.428 94.593,151.279 "/> <path class="monkey" id="alpha" fill="#FD00FF" d="M96.315,4.354l42.963,5.185l18.519,42.222l71.852-8.148l20.74,46.667l-5.926,52.593 l-24.444,34.074l-25.185,15.555l-14.074-19.259l-8.889,2.964l-1.481,22.222l-14.074,2.963l-25.186,22.963l-74.074,4.444 l101.481,4.444c0,0,96.297-17.777,109.63-71.852S282.24,53.983,250.389,20.65S96.315,4.354,96.315,4.354z"/> </svg> As you can probably see, each element has an ID, and I was hoping to be able to access individual elements with Javascript so I could change the Fill attribute and respond to events such as click. The HTML is bog basic <!DOCTYPE html> <html> <head> <title>SVG Illustrator Test</title> </head> <body> <object data="alpha.svg" type="image/svg+xml" id="alphasvg" width="100%" height="100%"></object> </body> </html> I guess this is two questions really. Is it possible to do it this way, as opposed to using something like Raphael or jQuery SVG. If it is possible, what's the technique? UPDATE At the moment, I've resorted to using Illustrator to create the SVG file, and I'm using Raphaƫl JS to create paths and simply copying the point data from the SVG file and pasting it into path() function. Creating complex paths such as might be needed for a map, by coding the point data manually is (to my knowledge) prohibitively complex.
Is it possible to do it this way, as opposed to using something like Raphael or jQuery SVG? Definitely. If it is possible, what's the technique? This annotated code snippet works: <!DOCTYPE html> <html> <head> <title>SVG Illustrator Test</title> </head> <body> <object data="alpha.svg" type="image/svg+xml" id="alphasvg" width="100%" height="100%"></object> <script> var a = document.getElementById("alphasvg"); // It's important to add an load event listener to the object, // as it will load the svg doc asynchronously a.addEventListener("load",function(){ // get the inner DOM of alpha.svg var svgDoc = a.contentDocument; // get the inner element by id var delta = svgDoc.getElementById("delta"); // add behaviour delta.addEventListener("mousedown",function(){ alert('hello world!') }, false); }, false); </script> </body> </html> Note that a limitation of this technique is that it is restricted by the same-origin policy, so alpha.svg must be hosted on the same domain as the .html file, otherwise the inner DOM of the object will be inaccessible. Important thing to run this HTML, you need host HTML file to web server like IIS, Tomcat
In case you use jQuery you need to wait for $(window).load, because the embedded SVG document might not be yet loaded at $(document).ready $(window).load(function () { //alert("Document loaded, including graphics and embedded documents (like SVG)"); var a = document.getElementById("alphasvg"); //get the inner DOM of alpha.svg var svgDoc = a.contentDocument; //get the inner element by id var delta = svgDoc.getElementById("delta"); delta.addEventListener("mousedown", function(){ alert('hello world!')}, false); });
If you are using an <img> tag for the SVG, then you cannot manipulate its contents (as far as I know). As the accepted answer shows, using <object> is an option. I needed this recently and used gulp-inject during my gulp build to inject the contents of an SVG file directly into the HTML document as an <svg> element, which is then very easy to work with using CSS selectors and querySelector/getElementBy*.