<!DOCTYPE html>
<html>
<head>
<title>D3.js GoogleMap Voronoi Diagram</title>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://shimz.me/example/d3js/geo_example/geo_template/topojson.v0.min.js"> </script>
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js? sensor=false"></script>
<style type="text/css">
html, body{
margin: 0px;
padding: 0px;
}
html, body, #map_canvas {
width: 100%;
height: 100%;
}
.SvgOverlay {
position: relative;
width: 900px;
height: 600px;
}
.SvgOverlay svg {
position: absolute;
top: -4000px;
left: -4000px;
width: 8000px;
height: 8000px;
}
</style>
</head>
<body>
<div id="map_canvas"></div>
<br>
<script type="text/javascript">
d3.json('bus-stops.json', function(pointjson){
main(pointjson);
});
function main(pointjson) {
//Google Map
var map = new google.maps.Map(document.getElementById('map_canvas'), {
zoom: 14,
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: new google.maps.LatLng(1.290270,103.851959),
});
var overlay = new google.maps.OverlayView(); //OverLay
overlay.onAdd = function () {
var layer = d3.select(this.getPanes().overlayLayer).append("div").attr("class", "SvgOverlay");
var svg = layer.append("svg");
var svgoverlay = svg.append("g").attr("class", "AdminDivisions");
overlay.draw = function () {
var markerOverlay = this;
var overlayProjection = markerOverlay.getProjection();
//Google Map
var googleMapProjection = function (coordinates) {
var googleCoordinates = new google.maps.LatLng(coordinates[1], coordinates[0]);
var pixelCoordinates = overlayProjection.fromLatLngToDivPixel(googleCoordinates);
return [pixelCoordinates.x + 4000, pixelCoordinates.y + 4000];
}
var pointdata = pointjson.lat;
console.log(pointdata);
var positions = [];
pointdata.forEach(function(d) {
positions.push(googleMapProjection(d.lat,d.lng));
});
var polygons = d3.geom.voronoi(positions);
var pathAttr ={
"d":function(d, i) { return "M" + polygons[i].join("L") + "Z"},
stroke:"red",
fill:"none"
};
svgoverlay.selectAll("path")
.data(pointdata)
.attr(pathAttr)
.enter()
.append("svg:path")
.attr("class", "cell")
.attr(pathAttr)
var circleAttr = {
"cx":function(d, i) { return positions[i][0]; },
"cy":function(d, i) { return positions[i][1]; },
"r":2,
fill:"red"
}
svgoverlay.selectAll("circle")
.data(pointdata)
.attr(circleAttr)
.enter()
.append("svg:circle")
.attr(circleAttr)
};
};
overlay.setMap(map);
};
</script>
</body>
</html>
The data file is of the structure:
[{"no":"10009","lat":"1.28210155945393","lng":"103.81722480263163",
"name":"Bt Merah Int"},
{"no":"10011","lat":"1.2777380589964","lng":"103.83749709165197",
"name":"Opp New Bridge Rd Ter"}]
It is also giving an error in console:
TypeError: pointdata is undefined. I am trying to plot using the example: http://bl.ocks.org/shimizu/5610671
I am getting undefined for pointjson using console.log(). Please suggest how do I get the co-ordinate values in pointjson
Related
I want to add a modified SVG object to a map instead of the original file.
$.ajax({
method: 'get',
url: 'zones.svg',
dataType: 'html'
}).then(function (value) {
var svg = $(value);
var width = svg.attr("width");
var height = svg.attr("height");
var zoneBounds = [[-height, 0], [0, width]];
L.imageOverlay('zones.svg', zoneBounds, {
opacity: 0.5
}).addTo(mymap);
});
Is there a way to override the original imageOverlay() method to accept an object instead of an URL of the file?
Or is there another built-in method?
You can draw some inspiration from the VideoOverlay class : extend ImageOverlay and override _initImage to create a SVG node instead of an image.
Your class definition could look like this
var SVGOverlay = L.ImageOverlay.extend({
options: {
},
_initImage: function () {
var wasElementSupplied = this._url.tagName.toLowerCase() === 'svg';
var svg = this._image = wasElementSupplied ? this._url : L.DomUtil.create('svg');
}
});
and based on your example, you could set it up like this
var svgdomnode = svg.get(0); // your svg looks like a jQuery object, let's use a DOM node
var overlay = new SVGOverlay(svgdomnode, zoneBounds).addTo(mymap);
And a demo where an SVG node is inserted as a layer and modified when you click on a button.
var SVGOverlay = L.ImageOverlay.extend({
options: {
},
_initImage: function () {
var wasElementSupplied = this._url.tagName.toLowerCase() === 'svg';
var svg = this._image = wasElementSupplied ? this._url : L.DomUtil.create('svg');
}
});
var map = L.map('map').setView([37.8, -96], 4);
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
var bounds = L.latLngBounds([[ 32, -130], [ 13, -100]]);
map.fitBounds(bounds);
var svg = document.getElementById('src').content.querySelector('svg');
var overlay = new SVGOverlay(svg, bounds).addTo(map);
var n = 0;
document.querySelector('button').addEventListener('click', function() {
var rect = document.createElementNS("http://www.w3.org/2000/svg", 'rect');
rect.setAttribute('x', 20*n);
rect.setAttribute('width', 20);
rect.setAttribute('height', 20);
rect.setAttribute('fill', '#0000ff');
svg.appendChild(rect);
n++;
});
html, body {
height: 100%;
margin: 0;
}
#svg {
width: 100%;
height: 100%;
}
#map {
width: 100%;
height: 100%;
}
button {position: absolute; left:100px; top: 0;z-index:10000}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.2.0/dist/leaflet.css" integrity="sha512-M2wvCLH6DSRazYeZRIm1JnYyh22purTM+FDB5CsyxtQJYeKq83arPe5wgbNmcFXGqiSH2XR8dT/fJISVA1r/zQ==" crossorigin=""/>
<script src="https://unpkg.com/leaflet#1.2.0/dist/leaflet.js" integrity="sha512-lInM/apFSqyy1o6s89K4iQUKg6ppXEgsVxT35HbzUupEVRh2Eu9Wdl4tHj7dZO0s1uvplcYGmt3498TtHq+log==" crossorigin=""></script>
<template id='src'>
<svg xmlns="http://www.w3.org/2000/svg" width='100' height='100'>
<rect width="100%" height="100%" fill="red"/>
<circle cx="50%" cy="50%" r="30%" fill="green"/>
</svg>
</template>
<div id='map'></div>
<button>Click to add a square</button>
Release 1.5.0 includes this approach as layer type SVGOverlay.
I am new to d3.js ,I made reference this example: http://bl.ocks.org/emeeks/4531633 .
I changed the example's map to Google map ,and want to use SVG to draw bar
chart on the Google map.
<!DOCTYPE html>
<title>test</title>
<meta charset="utf-8">
<style type="text/css">
.gmap{ display: block; width: 1000px; height: 800px; }
.stations, .stations svg { position: absolute; }
.stations svg { width: 120px; height: 30px; padding-right: 100px; font: 12px sans-serif; }
.stations circle { fill: yellow; stroke: black; stroke-width: 1.5px; }
</style>
<body>
<div class="gmap" id="map-canvas"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://maps.googleapis.com/maps/api/js"></script>
<script></script>
<script>
var map;
function initialize() {
var mapOptions = { zoom: 8, center: new
google.maps.LatLng(23.7147979,120.7105502) };
map = new google.maps.Map( document.getElementById('map-canvas') , mapOptions);
}
initialize();
var width = 960, height = 960;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.csv("223-loc.csv", function(data) {
var parse = d3.time.format("%Y/%m/%d").parse;
aaa = d3.nest()
.key(function(d){ return d.birdName;})
.entries(stocks = data);
aaa_copy = d3.nest()
.key(function(d){ return d.birdName;})
.entries(stocks = data);
var position2keep= new Array();
var i=0;
aaa.forEach(function(s)
{
for (nn=0; nn<selectAryy.length; nn++)
{
if (s.key == selectAryy[nn])
{ position2keep.push(i); break; }
}
i++;
});
position2keep.sort();
for (j=aaa_copy.length-1; j>=0; j--)
{
if ( position2keep.indexOf(j) == -1)
aaa_copy.splice(j,1);
}
aaa_copy.forEach(function(s) {
s.values.forEach(function(d) {
for (nn=0; nn<selectAryy.length; nn++){
if (d.birdName== selectAryy[nn]){
d.date = parse(d.date);
d.count = +d.count;
d.lat = +d.lat;
d.lng = +d.lng;
}
}
bars = svg.selectAll("g")
.data(s)
.enter()
.append("g")
.attr("class", "bars")
.attr("transform", function(d) {return "translate("+ d.lat +","+
d.lng+")";});
bars.append("rect")
.attr('height', function(d) {return d.count*1000})
.attr('width', 10)
.attr('y', function(d) {return -(d.count)})
.attr("class", "bars")
.style("fill", "green");
bars.append("text")
.text(function(d) {return d.location})
.attr("x", -10)
.attr("y", 18);
bars.setMap(map);
});
});
});
</script>
</body>
My CSV data: https://drive.google.com/open?id=0B6SUWnrBmDwSWkI4bVNtOTNSOTA
I use d3.csv load data,it works.
But When I want to put the data into SVG to draw bar chart,it didn't work.
Can anyone help me to fix it?
In the provided example there are some issues:
selectAryy is not defined
and most importantly bars.setMap(map); does not seem valid. Do you mean setMap function of google.maps.OverlayView object?
In order to create a bar chart on Google Maps i would recommend to implement it as Custom Overlay.
Having said that the below example demonstrates how to add svg objects (bar chart) into Google Maps using overlay technique:
Example
function BarChartOverlay(chartData, map){
this.map_ = map;
this.chartData_ = chartData;
this.div_=null;
this.setMap(map);
}
BarChartOverlay.prototype = new google.maps.OverlayView();
BarChartOverlay.prototype.onAdd = function(){
var overlayProjection = this.getProjection();
var div = document.createElement('div');
div.setAttribute('id','chartDiv');
var chartArea = d3.select(div).append("svg");
this.chartData_.forEach(function(item){
var pos = overlayProjection.fromLatLngToDivPixel(new google.maps.LatLng(item[0], item[1]));
var bar = chartArea
.append("rect")
.attr("x", pos.x)
.attr("y", pos.y)
.attr("width", 40)
.attr("height", item[2])
.attr("fill-opacity", '0.5')
.attr("fill", 'purple');
});
this.div_ = div;
this.chartArea_ = chartArea;
var panes = this.getPanes();
panes.overlayLayer.appendChild(div);
};
BarChartOverlay.prototype.draw = function(){
var overlayProjection = this.getProjection();
var sw = overlayProjection.fromLatLngToDivPixel(this.map_.getBounds().getSouthWest());
var ne = overlayProjection.fromLatLngToDivPixel(this.map_.getBounds().getNorthEast());
var chartAreaSize = sw.x + ' ' + ne.y + ' ' + (ne.x - sw.x) + ' ' + (sw.y - ne.y);
this.chartArea_.attr('viewBox',chartAreaSize);
};
BarChartOverlay.prototype.onRemove = function(){
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
};
function initialize() {
var mapOptions = {
zoom: 8, center: new
google.maps.LatLng(23.7147979, 120.7105502)
};
var chartData = [
[25.204757,121.6896172,100],
[22.7972447,121.0713702,130],
[24.254972,120.6011066,80]
];
var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
var overlay = new BarChartOverlay(chartData, map);
}
initialize();
.gmap {
display: block;
width: 1000px;
height: 800px;
}
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://maps.googleapis.com/maps/api/js"></script>
<div class="gmap" id="map-canvas"></div>
I am trying to save a d3 chart as a png. Here is the reference I am using. http://techslides.com/demos/d3/svg-to-image-3.html
Here is my html page http://54.186.224.191/Animation/png.html
Here is my code for this page
<!DOCTYPE html>
<meta charset="utf-8">
<link href="nvd3master/nv.d3.css" rel="stylesheet" type="text/css">
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="nvd3master/lib/d3.v3.js"></script>
<script src="nvd3master/nv.d3.js"></script>
<script src="nvd3master/src/tooltip.js"></script>
<script src="nvd3master/src/utils.js"></script>
<script src="nvd3master/src/models/legend.js"></script>
<script src="nvd3master/src/models/axis.js"></script>
<script src="nvd3master/src/models/scatter.js"></script>
<script src="nvd3master/src/models/line.js"></script>
<script src="nvd3master/src/models/lineWithFocusChart.js"></script>
<script src="nvd3master/stream_layers.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style>
path {
stroke: #000;
fill-opacity: .70;
}
body {
overflow-y:scroll;
}
text {
font: 12px sans-serif;
}
svg {
display: block;
}
#chart svg {
height: 400px;
min-width: 100px;
min-height: 100px;
}
</style>
<body>
<div id="svg"></div>
<button id="save">Save as Image</button>
<canvas width="960" height="700" style="display:none"></canvas>
<div id="chart" class='with-3d-shadow with-transitions' >
<svg width="1000" height="800">
</svg>
</div>
<script>
$.ajax({
dataType : "json",
url : "getdata.php",
success : function(data) {
nv.addGraph(function() {
var chart = nv.models.lineWithFocusChart()
.x(function(d) { return d[0] }) //We can modify the data accessor functions...
.y(function(d) { return d[1] }); //...in case your data is formatted differently.
chart.xAxis
.tickFormat(function(d) {
return d3.time.format('%x')(new Date(d))
});
chart.x2Axis
.tickFormat(function(d) {
return d3.time.format('%x')(new Date(d));
});
chart.yAxis
.tickFormat(d3.format(',.1f'));
chart.y2Axis
.tickFormat(d3.format(',.1f'));
d3.select('#chart svg')
.datum(data)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
}
});
d3.select("#save").on("click", function(){
var html = d3.select("svg")
.attr("version", 1.1)
.attr("xmlns", "http://www.w3.org/2000/svg")
.node().parentNode.innerHTML;
//console.log(html);
var imgsrc = 'data:image/svg+xml;base64,'+ btoa(html);
var img = '<img src="'+imgsrc+'">';
var canvas = document.querySelector("canvas"),
context = canvas.getContext("2d");
var image = new Image;
image.src = imgsrc;
image.onload = function() {
context.drawImage(image, 0, 0);
//save and serve it as an actual filename
binaryblob();
var a = document.createElement("a");
a.download = "sample.png";
a.href = canvas.toDataURL("image/png");
a.click();
};
});
function binaryblob(){
var byteString = atob(document.querySelector("canvas").toDataURL().replace(/^data:image\/(png|jpg);base64,/, ""));
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
}
</script>
As you can see, when the button is pushed and the image is rendered and downloaded, the image is not the same. Is this a CSS issue? Does anyone know who to fix this? Your help is most appreciated.
I'm trying to follow the example in this videos:
https://www.youtube.com/watch?v=wqPGFs0cqxI
It's about drawing path with D3.js into Google Maps API. The console shows me the error Uncaught TypeError: Object#<PolylineContext>" has no method 'setCurrent'.
The index.html
<head>
<title>App</title>
<style type="text/css">
html { height: 100% }
body { height: 100%; margin: 0; padding: 0 }
#map-canvas { height: 100%; }
</style>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false">
</script>
<script src="http://d3js.org/d3.v3.js" charset="utf-8"></script>
<script src="polyline_context.js"></script>
<script type="text/javascript">
var map;
var polyline;
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(53.567, 9.944),
zoom: 2,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map-canvas"),
mapOptions);
polyline = new google.maps.Polyline({
map: map
});
d3init();
}
var context;
var width;
var height;
var path;
var graticule;
var equator;
var projection;
function d3init() {
width = map.getDiv().offsetWidth;
height = map.getDiv().offsetHeight;
projection = d3.geo.equirectangular()
.translate([0, 0])
.scale(52.29578)
.precision(2)
context = new PolylineContext();
path = d3.geo.path().projection(projection).context(context);
equator = {type: 'LineString', coordinates: [[-180, 20], [-90, 0], [0, -20], [90, 0], [180, 20]]};
render();
}
function render() {
polyline.setOptions({
strokeColor: 'red',
strokeWeight: 2
});
context.setCurrent(polyline.getPath());
path(equator);
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="map-canvas">
</div>
</body>
The Polyline_context.js
'use strict';
function PolylineContext () {
this.currentPath = null;
this.currentIndex = 0;
}
PolylineContext.prototype.beginPath = function() {};
PolylineContext.prototype.moveTo = function(x, y) {
if (this.currentPath) {
var latLng = new google.maps.LatLng(y, x);
this.currentPath.setAt(this.currentIndex, latLng);
this.currentIndex++;
}
};
PolylineContext.prototype.lineTo = function(x, y) {
if (this.currentPath) {
var latLng = new google.maps.LatLng(y, x);
this.currentPath.setAt(this.currentIndex, latLng);
this.currentIndex++;
}
};
PolylineContext.prototype.arc = function(x, y, radius, startAngle, endAngle) {};
PolylineContext.prototype.closePath = function() {};
Any ideas of what's wrong in here?
Fairly old question. But just getting started with the same topic. Just in case somebody else stumbles upon this. Just put this inside the polyline_context.js file and see it come to live:
PolylineContext.prototype.setCurrent = function (path) {
this.currentPath = path;
};
What was shown in the video and what needs to be implemented is not necessarily in sync.
To set the path just use this code instead:
context.currentPath = polyline.getPath();
I'm using d3.js with google maps in a vain attempt to visualise wireless coverage. the basic idea is that each point on the map would represent an access point and i would use a voronoi diagram from these points as a crude approximation of coverage etc.
so based on this demo, i have the following:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no"/>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.v2.min.js"></script>
<link rel="stylesheet" href="http://mbostock.github.com/d3/ex/colorbrewer.css">
<style type="text/css">
html, body, #map {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.stations {
position: absolute;
}
.stations, .stations svg {
position: absolute;
}
.stations border {
position: absolute;
stroke: black;
stroke-width: 2px;
}
.stations svg {
width: 60px;
height: 20px;
padding-right: 100px;
font: 10px sans-serif;
}
.stations circle {
fill: brown;
stroke: black;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<div id="map"></div>
<script type="text/javascript">
// create map
var map = new google.maps.Map(d3.select("#map").node(), {
zoom: 8,
center: new google.maps.LatLng(37.76487, -122.41948),
mapTypeId: google.maps.MapTypeId.TERRAIN
});
var data = [
{ name: 'pt1', lng: -122.28, lat: 38.2 },
{ name: 'pt2', lng: -122.05, lat: 38.0 },
{ name: 'pt3', lng: -122.12, lat: 37.67 },
{ name: 'pt4', lng: -121.82, lat: 37.7 },
{ name: 'pt5', lng: -121.95, lat: 38.38 },
{ name: 'pt6', lng: -121.78, lat: 36.93 },
{ name: 'pt7', lng: -122.25, lat: 37.52 },
{ name: 'pt8', lng: -122.82, lat: 38.5 },
{ name: 'pt9', lng: -121.92, lat: 37.37 },
{ name: 'pt10', lng: -122.37, lat: 37.62 },
{ name: 'pt11', lng: -121.23, lat: 37.9 },
]
// Load the station data. When the data comes back, create an overlay.
var overlay = new google.maps.OverlayView();
// Add the container when the overlay is added to the map.
overlay.onAdd = function() {
var layer = d3.select(this.getPanes().overlayLayer).append("div")
.attr("height", "100%")
.attr("width", "100%")
.attr("class", "stations")
// Draw each marker as a separate SVG element.
// We could use a single SVG, but what size would it have?
overlay.draw = function() {
var projection = this.getProjection(),
padding = 10;
var marker = layer.selectAll("svg")
.data( data )
.each(transform) // update existing markers
.enter().append("svg:svg")
.each(transform)
.attr("class", "marker")
marker.append("svg:circle")
.attr("r", 4.5)
.attr("cx", padding )
.attr("cy", padding );
// add a label.
marker.append("svg:text")
.attr("x", padding + 7)
.attr("y", padding)
.attr("dy", ".31em")
.text( function(d) {
return d.name; }
);
var v = d3.geom.voronoi( translate(data) );
// console.log( v )
var edges = layer.selectAll("path")
.data( v )
.enter().append("svg:svg")
.attr( "class", "border" )
.append("svg:path")
.attr( "d", function(d){
var e = transform_path(d)
var p = 'M' + e.join('L') + 'Z'
console.log( 'PATH: ' + p)
return p
})
function translate(data) {
var d = []
for( var i=0; i<data.length; i++){
var c = [ data[i].lat, data[i].lng ]
d.push( c )
}
return d
}
function _projection( lat, lng ) {
e = new google.maps.LatLng( lat, lng );
e = projection.fromLatLngToDivPixel(e);
return [ e.x - padding, e.y - padding]
// return [ e.x, e.y ]
}
function transform(d) {
e = _projection( d.lat, d.lng )
console.log("marker " + d.lat +', ' + d.lng + " -> left: " + e[0] +", top: " + e[1] )
return d3.select(this)
.style("left", e[0] + "px")
.style("top", e[1] + "px");
}
function transform_path(data) {
var d = []
console.log(data)
for( var i=0; i<data.length; i++) {
var c = _projection( data[i][0], data[i][2] )
console.log( ' path point: ' + JSON.stringify(data[i]) + ' -> left: ' + c[0] + ", top: " + c[1])
d.push( c )
}
// console.log(d)
return d
}
};
};
// Bind our overlay to the map…
overlay.setMap(map);
</script>
</body>
</html>
However, i can not get any of the path elements showing up. can any one help? i have the above code up on jsfiddle. cheers!
Here you can see the paths showing up. Your problem was not related to D3, but to bad CSS styling : width were set to 0px, styles for the circles were given to the paths, etc.
http://jsfiddle.net/uF9PV/7/
I also noticed design flaws : your dots should not be positioned with CSS through absolute positioning, but rather through SVG positioning (let D3 do the trick for you). My advice would be to separate clearly the overlay and the Google map. The tricky part would be to make sure that they move together (zooms and left-right up-down)
good luck