draw paths using d3 in google maps overlay - javascript
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
Related
Markers are not properly aligned with path in d3 with leaflet
I'm creating d3 markers(flights) and paths(trails) over leaflet map. Input data of Path's endpoint and marker's Geo-location(latitude, longitude) are same but they are not plotting on same location. var defaultlocation = [28.6139, 77.2090]; var defaultzoom = 3; var map = L.map('map', { center: defaultlocation, zoom: defaultzoom, minZoom: 2 }); var mbAttr = '© OpenStreetMap Contributors', mbUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'; var mapTileLayer = L.tileLayer(mbUrl, { attribution: mbAttr }); map.addLayer(mapTileLayer); map._initPathRoot(); var svg = d3.select(map.getPanes().overlayPane).select(".leaflet-zoom-animated"); var trails = [{"type": "LineString", "coordinates": [[28.320282,74.448219], [28.31662,74.290764], [28.312931,74.137733], [28.309067,73.978165], [28.304831,73.820801], [28.300409,73.661285], [28.295382,73.444893], [28.294729,73.425507], [27.648056,67.771759], [27.637573,67.736671], [27.596539,67.599617], [27.552593,67.452942], [27.511908,67.317772], [27.470032,67.179688], [27.426956,67.021629], [27.403152,66.867195], [27.371367,66.663139], [27.368317,66.643532], [27.345901,66.500511], [27.321136,66.344162], [27.281473,66.09417], [27.257034,65.942268], [27.18718,65.515411], [27.159088,65.345146], [27.144058,65.254333], [27.122776,65.12706], [27.097359,64.976524], [27.070406,64.817642], [27.045547,64.671494], [27.020128,64.522865], [26.995422,64.380249], [26.967957,64.227058], [26.964386,64.205864], [26.961731,64.190941], [26.958115,64.17601], [26.951279,64.155296], [26.94548,64.141762], [26.938797,64.128754], [26.879791,64.056152], [26.783798,63.950172], [26.688538,63.845356], [26.58783,63.734749], [26.49353,63.631466], [26.39859,63.527718], [26.303101,63.423916], [26.208252,63.321259], [26.113012,63.218185], [26.018698,63.115242], [25.924072,63.013226], [25.828766,62.910358], [25.733564,62.80827], [25.634903,62.70274], [25.53907,62.600357], [25.44359,62.498528], [25.348764,62.397732], [25.252945,62.296192], [25.153061,62.190655], [25.05835,62.090607], [24.963226,61.990559], [24.862852,61.884949], [24.727203,61.729633], [24.696304,61.653393], [24.62632,61.456017], [24.584427,61.313831], [24.582235,61.274174], [24.582642,61.125286], [24.582933,60.970287], [24.583008,60.818176], [24.582561,60.668732], [24.581863,60.656242], [24.579489,60.636238], [24.576975,60.622456], [24.546249,60.539043], [24.490585,60.410664], [24.432953,60.278675], [24.37587,60.148212], [24.321262,60.02396], [24.265028,59.895905], [24.209492,59.769821], [24.157446,59.65229], [24.103088,59.529675], [24.04953,59.409382], [23.998981,59.296062], [23.948273,59.181976], [23.896957,59.073841], [23.845638,58.969429], [23.820465,58.917797], [23.797989,58.871765], [23.773499,58.822124], [23.751297,58.777264], [23.727163,58.728455], [23.718319,58.710522], [23.709707,58.693161], [23.701094,58.675751], [23.693087,58.659737], [23.685266,58.643673], [23.678055,58.62859], [23.669065,58.609676], [23.664225,58.598484], [23.659698,58.586224], [23.654846,58.570251], [23.651796,58.553085], [23.648987,58.536785], [23.646116,58.519817], [23.643137,58.50256], [23.640064,58.485718], [23.637131,58.469288], [23.634338,58.453278], [23.633127,58.446228], [23.631824,58.438663], [23.630539,58.431446], [23.628159,58.418072], [23.625866,58.405289], [23.624748,58.39933], [23.623585,58.393009], [23.622528,58.387501], [23.62149,58.38171], [23.620512,58.376217], [23.618418,58.364815], [23.61731,58.359222], [23.616137,58.353519], [23.614929,58.347675], [23.613052,58.338573], [23.611176,58.326874], [23.610764,58.320618], [23.610168,58.308918], [23.609711,58.303272], [23.608749,58.292492], [23.607834,58.28186], [23.607384,58.276409], [23.606733,58.26796], [23.60643,58.264294], [23.606127,58.260551], [23.606037,58.259533], [23.605988,58.258877], [23.605968,58.258564], [23.605797,58.258221], [23.605534,58.258141], [23.605053,58.258171], [23.604673,58.258205], [23.604275,58.258247], [23.604012,58.258274], [23.603634,58.258312], [23.60338,58.25853], [23.603394,58.258961], [23.603451,58.259556], [23.603533,58.260719], [23.6036,58.261402], [23.603703,58.262737], [23.603748,58.263424], [23.603863,58.264809], [23.603987,58.266224], [23.604033,58.266861], [23.60408,58.267529], [23.604137,58.268105], [23.604172,58.268661], [23.604219,58.269138], [23.604263,58.26973], [23.604286,58.2701], [23.604343,58.270695], [23.604378,58.271053], [23.604401,58.27137], [23.604393,58.271679], [23.604265,58.271938], [23.60368,58.272057], [23.603222,58.272095], [23.602707,58.272144], [23.602194,58.272198], [23.601124,58.272301], [23.60059,58.27235], [23.600006,58.2724], [23.598948,58.272507], [23.598392,58.272552], [23.597876,58.272598], [23.59742,58.27264], [23.59679,58.272705], [23.596401,58.272743], [23.596062,58.272781], [23.595737,58.272793], [23.595337,58.272884], [23.595177,58.273106], [23.5952,58.273582], [23.595222,58.273918], [23.595257,58.274242], [23.59528,58.274624], [23.595316,58.274971], [23.595348,58.275425], [23.595444,58.276562], [23.595491,58.27721], [23.595543,58.277817], [23.595583,58.278442], [23.595646,58.279087], [23.595703,58.279865], [23.595795,58.28101], [23.59584,58.281582], [23.595898,58.282211], [23.595943,58.282825], [23.596025,58.28392], [23.596069,58.284454], [23.596115,58.284912], [23.596149,58.285435], [23.596184,58.285904], [23.59623,58.2864], [23.59627,58.28685], [23.596298,58.287251], [23.598482,58.289104]] },{"type": "LineString", "coordinates":[[23.953314,83.35218],[23.860163,83.393333],[23.723557,83.44162],[23.591324,83.505318],[23.441519,83.579308],[23.307123,83.645531],[23.116911,83.739166],[23.096466,83.749229],[22.967926,83.812202],[22.834663,83.877357],[22.699539,83.943436],[22.568069,84.007507],[22.426495,84.076332],[22.292999,84.141045],[22.160774,84.204964],[22.029465,84.268486],[21.892868,84.334251],[21.762985,84.396721],[21.626541,84.462341],[21.496948,84.524216],[21.365707,84.586937],[21.228104,84.652855],[21.08835,84.719444],[20.956375,84.782425],[20.82756,84.843048],[20.690933,84.907738],[20.561285,84.968964],[20.433243,85.029518],[20.298964,85.092758],[20.170715,85.153114],[20.036545,85.215599],[19.912537,85.27372],[19.782234,85.334755],[19.654634,85.394829],[19.522568,85.452835],[19.394806,85.524643],[19.277618,85.629456],[19.172678,85.736053],[19.062582,85.847816],[18.958958,85.952934],[18.849234,86.064346],[18.74519,86.169815],[18.637049,86.279427],[18.534073,86.383781],[18.432358,86.486824],[18.327431,86.597382],[18.229523,86.703804],[18.216217,86.719131],[18.206905,86.730667],[18.198029,86.742996],[18.186356,86.761826],[18.1793,86.77507],[18.137264,86.903374],[18.098328,87.055084],[18.073471,87.221474],[17.365723,88.518433],[17.331848,88.543541],[17.202026,88.639381],[17.07699,88.731567],[14.209305,92.040993],[14.101558,92.154861],[13.957664,92.306549],[13.864001,92.401184],[13.830437,92.434822],[13.728255,92.53717],[13.546282,92.719162],[12.948691,93.313538],[12.891944,93.369766],[11.131302,95.441971],[11.065475,95.537491],[10.976257,95.666954],[10.888412,95.794151],[10.798536,95.924332],[10.673823,96.104881],[10.635681,96.16011],[10.54492,96.290688],[10.458939,96.414528],[10.368576,96.544403],[10.27803,96.67466],[10.189866,96.801437],[10.022507,97.041214],[10.01207,97.05513],[9.989552,97.082344],[9.911809,97.15229],[9.900284,97.16127],[9.888748,97.169884],[9.870983,97.182076],[9.859485,97.188896],[9.840775,97.198143],[9.828667,97.203003],[9.815679,97.207413],[9.679793,97.226067],[9.540161,97.243622],[9.400107,97.267738],[9.261335,97.29113],[9.121818,97.312347],[8.986588,97.326485],[8.834793,97.342262],[8.815247,97.344498],[8.801926,97.346359],[8.786453,97.349205],[8.7683,97.354019],[8.755188,97.358559],[8.665237,97.410095],[8.541412,97.48951],[8.416086,97.569771],[8.291931,97.649559],[8.177124,97.735352],[8.078629,97.851913],[7.9818,97.971291],[7.89006,98.084259],[7.792984,98.203758],[7.696426,98.322517],[7.603982,98.436172],[7.507805,98.55584],[7.427456,98.687012],[7.350924,98.812881],[7.27034,98.943665],[7.193298,99.067841],[7.112732,99.197929],[7.032806,99.327438],[6.956863,99.450806],[6.875765,99.583153],[6.797837,99.710777],[6.791519,99.720856],[6.698593,99.832718],[6.594823,99.947311],[6.477356,100.077393],[6.463127,100.092361],[6.441294,100.112869],[6.363327,100.167053],[6.147949,100.292419],[6.08596,100.32843],[5.963196,100.39991],[5.83342,100.475418],[5.704558,100.550377],[5.581787,100.62159],[5.451313,100.697372],[5.328644,100.768509],[5.197449,100.84462],[5.069229,100.918968],[4.944546,100.991203],[4.815224,101.066071],[4.691528,101.13781],[4.562771,101.212303],[4.440664,101.282913],[4.25655,101.38932],[4.220612,101.410057],[4.092919,101.483841],[3.965226,101.557617],[3.837708,101.631218],[3.710493,101.704605],[3.53627,101.805275],[3.449754,101.855667],[3.330487,101.92495],[3.206497,101.997162],[3.069992,102.076485],[3.052505,102.087006],[3.030411,102.102531],[3.015095,102.114845],[2.928927,102.214287],[2.84436,102.323265],[2.753448,102.43885],[2.655246,102.559029],[2.64061,102.573944],[2.568101,102.622063],[2.547013,102.634232],[2.528066,102.647064],[2.518755,102.654121],[2.428299,102.768578],[2.403763,102.823837],[2.345078,102.956093],[2.291301,103.077423],[2.235672,103.203842],[2.181438,103.325706],[2.118027,103.442375],[2.034668,103.550613],[1.955334,103.653877],[1.87829,103.754028],[1.839615,103.803856],[1.793937,103.862991],[1.787018,103.872101],[1.776387,103.885864],[1.768985,103.89373],[1.758371,103.903107],[1.710702,103.926735],[1.658842,103.943451],[1.604376,103.961021],[1.585476,103.966988],[1.566157,103.973091],[1.546921,103.97908],[1.526596,103.985458],[1.510529,103.990486],[1.493408,103.995834],[1.477066,104.001007],[1.461414,104.00605],[1.446611,104.010788],[1.432068,104.015526],[1.417702,104.0196],[1.402572,104.022766],[1.387985,104.025307],[1.373383,104.027817],[1.35886,104.030487],[1.343124,104.03373],[1.329532,104.037071],[1.307513,104.042992],[1.297073,104.045509],[1.289916,104.046776],[1.278809,104.047554],[1.271481,104.046684],[1.264572,104.044441],[1.256287,104.038574],[1.252302,104.033234],[1.249878,104.026978],[1.249283,104.020554],[1.251526,104.011383],[1.252899,104.008965],[1.259079,104.003006],[1.2692,103.997864],[1.274135,103.995445],[1.279023,103.99308],[1.286238,103.989571],[1.294235,103.985687],[1.301102,103.982521],[1.30607,103.980766],[1.31133,103.979958],[1.316916,103.98053],[1.32193,103.982147],[1.326416,103.983963]]}]; var markers = [{lat: 28.320282, lng: 74.448219, rotate: 88},{lat: 23.953314, lng: 83.35218, rotate: 318}]; function update(){ d3.selectAll('.markersgroup').remove(); d3.selectAll('.trailsgroup').remove(); var trailsgroup = svg.append("g").attr("class","trailsgroup"); var markersgroup = svg.append("g").attr("class","markersgroup"); var marker = markersgroup.selectAll(".marker") .data(markers) .enter().append("g") .attr("class", "marker"); marker.append("path") .attr("class", "flight") .attr("d", "m25.21488,3.93375c-0.44355,0 -0.84275,0.18332 -1.17933,0.51592c-0.33397,0.33267 -0.61055,0.80884 -0.84275,1.40377c-0.45922,1.18911 -0.74362,2.85964 -0.89755,4.86085c-0.15655,1.99729 -0.18263,4.32223 -0.11741,6.81118c-5.51835,2.26427 -16.7116,6.93857 -17.60916,7.98223c-1.19759,1.38937 -0.81143,2.98095 -0.32874,4.03902l18.39971,-3.74549c0.38616,4.88048 0.94192,9.7138 1.42461,13.50099c-1.80032,0.52703 -5.1609,1.56679 -5.85232,2.21255c-0.95496,0.88711 -0.95496,3.75718 -0.95496,3.75718l7.53,-0.61316c0.17743,1.23545 0.28701,1.95767 0.28701,1.95767l0.01304,0.06557l0.06002,0l0.13829,0l0.0574,0l0.01043,-0.06557c0,0 0.11218,-0.72222 0.28961,-1.95767l7.53164,0.61316c0,0 0,-2.87006 -0.95496,-3.75718c-0.69044,-0.64577 -4.05363,-1.68813 -5.85133,-2.21516c0.48009,-3.77545 1.03061,-8.58921 1.42198,-13.45404l18.18207,3.70115c0.48009,-1.05806 0.86881,-2.64965 -0.32617,-4.03902c-0.88969,-1.03062 -11.81147,-5.60054 -17.39409,-7.89352c0.06524,-2.52287 0.04175,-4.88024 -0.1148,-6.89989l0,-0.00476c-0.15655,-1.99844 -0.44094,-3.6683 -0.90277,-4.8561c-0.22699,-0.59493 -0.50356,-1.07111 -0.83754,-1.40377c-0.33658,-0.3326 -0.73578,-0.51592 -1.18194,-0.51592l0,0l-0.00001,0l0,0z"); marker.attr("transform", function (d) { var cord = map.latLngToLayerPoint([d.lat, d.lng]); var bb = d3.select(this).node().getBBox(); return "translate(" + (cord.x - (bb.width/2)) + "," + (cord.y - (bb.height/2)) + ") scale(0.6,0.6) rotate(" + d.rotate + ")"; }); function projectPoint(x, y) { var point = map.latLngToLayerPoint(new L.LatLng(x, y)); this.stream.point(point.x, point.y); } var transform = d3.geo.transform({point: projectPoint}); var path = d3.geo.path().projection(transform); trails.forEach(element => { trailsgroup.append("path") .datum(element) .attr("class", "trail") .attr("d", path); }); } update(); map.on("viewreset", function () { update(); }); map.on("moveend", function () { update(); }); html, body, #map { width: 100%; height: 100%; } .flight { fill :yellow; stroke:black; stroke-width: 2; cursor: pointer; } .trail { stroke:black; stroke-width: 2; fill: none; } <link href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/leaflet.css" rel="stylesheet"/> <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/leaflet.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.10/d3.min.js"></script> <div id="map"></div> I have tried to transform markers like this, but i think it still need corrections with scale and angle. marker.attr("transform", function (d) { var cord = map.latLngToLayerPoint([d.lat, d.lng]); var bb = d3.select(this).node().getBBox(); return "translate(" + (cord.x - (bb.width/2)) + "," + (cord.y - (bb.height/2)) + ") scale(0.6,0.6) rotate(" + d.rotate + ")"; }); JSFiddle d3 Markers are not plotting on correct position on map.
You can achieve this by using a matrix transformation on the path or by doing the following: Apply the rotation on the path and translation on the group as such - In order to rotate around the centre you will first need to move the plane to a negative location where the centre of rotation is in the middle and use the same translation values when rotating (you should variables or calculate the bbox) marker.append("path") .attr("class", "flight" .attr('transform', d => `translate(-25, -30)rotate(${d.rotate}, 25, 30)`) after applying the rotation on the plane you can apply the translation on the group marker as such: marker.attr("transform", function (d) { var cord = map.latLngToLayerPoint([d.lat, d.lng]); var bb = d3.select(this).node().getBBox(); return `translate(${cord.x}, ${cord.y})`; }); Here is the updated fiddle: JSFiddle
D3 On click not working for circle
I am using a modified version of D3 + Google Maps, wherein, I'd like a modal to pop up when I click on an SVG circle. Here's the JavaScript: var now = new Date(); var hour = now.getHours(); // Create the Google Map… var map = new google.maps.Map(d3.select("#map").node(), { zoom: 16, center: new google.maps.LatLng(19.134249, 72.913608), mapTypeId: google.maps.MapTypeId.TERRAIN }); function assign_dots() { $("#hour").text(hour); // Load the station data. When the data comes back, create an overlay. d3.json("stations.json", function (error, data) { var overlay = new google.maps.OverlayView(); if (error) throw error; // Add the container when the overlay is added to the map. overlay.onAdd = function () { var layer = d3.select(this.getPanes().overlayMouseTarget).append("div") .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; layer.selectAll("svg").remove(); var marker = layer.selectAll("svg") .data(d3.entries(data)) .each(transform) // update existing markers .enter().append("svg") .each(transform) .attr("class", "marker"); // Add a circle. marker.append("circle") .attr("r", function(d){ return d.value[3][hour]*6; }) .attr("cx", padding) .attr("cy", padding) .on("click", toggleExpand); // Add a label. marker.append("text") .attr("x", padding + 10) .attr("y", padding) .attr("dy", ".31em") .text(function (d) { if (d.value[3][hour]) return d.key; }); function transform(d) { d = new google.maps.LatLng(d.value[1], d.value[0]); d = projection.fromLatLngToDivPixel(d); return d3.select(this) .style("left", (d.x - padding) + "px") .style("top", (d.y - padding) + "px"); } function toggleExpand(d) { $('#myModal').modal('toggle'); console.log(d.value[2]); console.log("Reaches here"); } }; }; overlay.onRemove = function() {}; // Bind our overlay to the map overlay.setMap(map); }); } window.onload = assign_dots(); function progress_time() { hour = hour + 1; if (hour == 24) hour = 0; console.log(hour); d3.selectAll("svg").remove(); assign_dots(); } function regress_time() { hour = hour - 1; if (hour == -1) hour = 23; console.log(hour); d3.selectAll("svg").remove(); assign_dots(); } Basically, in the assign_dots function, when I am appending a circle to the marker, it should set an onclick function to be toggleExpand, but when I am clicking on an svg circle, nothing happens. What can I be missing? Edit Here's the stylesheet: html, body, #map { width: 100%; height: 90%; margin: 0; padding: 10px; } #map { border: solid; } .stations, .stations svg { position: absolute; } .stations svg { width: 60px; height: 20px; padding-right: 100px; font: 10px sans-serif; } .stations circle { fill: blue; stroke: black; stroke-width: 2px; } And I have also included bootstrap. For some reason, when I remove the bootstrap css, the on click starts working. What could the problem be?
how to put bird counts data in d3.js to draw bar-chart on google map
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>
Parsing a JSON file with JavaScript
I am new to JavaScript and coding in general and I have run into an issue trying to resolve a problem with a JSON column. Los Angeles operates a very handy data portal (https://data.lacity.org/A-Safe-City/LAPD-Crime-and-Collision-Raw-Data-2014/eta5-h8qx), the endpoint of which I have been pointing to in my script. However, the lat/long (found under location_1 column at the above link) is in the format (34.0000, -118.0000). I am attempting to make heat maps using this crime data but I need the lat/long values individually to assign as parameters for the library I am attempting to use. Can someone show me how I can separate those values and assign them to individual variables? Here is an excerpt from the script I am borrowing but I would be happy to show the entire thing if you all think it would help. //let Socrata do the sorting: http://data.lacity.org/resource/eta5-h8qx.json var sodaUrl = "http://data.lacity.org/resource/eta5-h8qx.json?crm_cd=330" //get json from the Socrata API $.getJSON( sodaUrl, function( rawData ) { var goodData = []; for(var i=0;i<rawData.length;i++){ rawData[i].value = 0; rawData[i].fresh=true; if(rawData[i].location_1) { rawData[i].lat = parseFloat(rawData[i].location_1.latitude); rawData[i].lng = parseFloat(rawData[i].location_1.longitude); } rawData[i].date = new Date(rawData[i].date_occ + "-04:00"); if(rawData[i].location_1) { if (rawData[i].location_1.latitude && rawData[i].location_1.longitude) { goodData.push(rawData[i]); }; }; }; Any help is greatly appreciated and I am happy to be more explicit if I have done poorly thus far. Edit: here is the whole HTML document. When I try to preview in Dreamweaver I only get the basemap. <!DOCTYPE html> <html> <head> <meta charset=utf-8 /> <title>PGC Crime Heatmap Test</title> <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' /> <style> body { margin:0; padding:0; color:#fff; font-family:verdana,sans-serif; } #map { position:absolute; top:0; bottom:0; width:100%; } #titleBox { position: absolute; height: 50px; width: 800px; background: #666; border-radius: 10px; right: 0; left: 0; top: 20px; margin: 0 auto; border: 2px solid #AFAFAF; font-size: 17px; font-weight: bold; line-height: 46px; text-align: center; } #chartBox { position:absolute; height:100px; width:800px; background:#666; border-radius:10px; right:0; left:0; bottom:20px; margin: 0 auto; border: 2px solid #AFAFAF; } svg text { fill: #fff; } svg line { stroke: #fff; stroke-width: 1px; } svg .domain { stroke: #fff; fill: none; } svg rect { fill: #00AEF7; opacity: .4; stroke: #FFF; } </style> </head> <body> <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" /> <script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script> <script src='heatmap.js'></script> <script src='leaflet-heatmap.js'></script> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> <div id = 'map'></div> <div id = 'titleBox'> Zip Code 11201 Vehicle Collisions Animated Heatmap (September 1 - Now) </div> <div id = 'chartBox'></div> <!-- Example data. --> <script> //heatmap.js config var cfg = { // radius should be small ONLY if scaleRadius is true (or small radius is intended) // if scaleRadius is false it will be the constant radius used in pixels "radius": .001, "maxOpacity": 1, // scales the radius based on map zoom "scaleRadius": true, // if set to false the heatmap uses the global maximum for colorization // if activated: uses the data maximum within the current map boundaries // (there will always be a red spot with useLocalExtremas true) "useLocalExtrema": false, // which field name in your data represents the latitude - default "lat" //latField: 'latitude', // which field name in your data represents the longitude - default "lng" //lngField: 'longitude', // which field name in your data represents the data value - default "value" valueField: 'value' }; var heatmapLayer = new HeatmapOverlay(cfg); var baseLayer = L.tileLayer( 'https://{s}.tiles.mapbox.com/v3/cwhong.map-hziyh867/{z}/{x}/{y}.png',{ attribution: "<a href='https://www.mapbox.com/about/maps/' target='_blank'>© Mapbox © OpenStreetMap</a> <a class='mapbox-improve-map' href='https://www.mapbox.com/map-feedback/' target='_blank'>Improve this map</a>", maxZoom: 18 } ); //setup map and add layers var map = new L.Map('map', { center: new L.LatLng(34.0224, -118.2870), zoom: 14, layers: [baseLayer, heatmapLayer] }); //let Socrata do the sorting: http://data.lacity.org/resource/eta5-h8qx.json var sodaUrl = "http://data.lacity.org/resource/eta5-h8qx.json?crm_cd=330" //get json from the Socrata API $.getJSON( sodaUrl, function( rawData ) { var goodData = []; for(var i=0;i<rawData.length;i++){ rawData[i].value = 0; rawData[i].fresh=true; if(rawData[i].location_1) { rawData[i].lat = parseFloat(rawData[i].location_1.latitude); rawData[i].lng = parseFloat(rawData[i].location_1.longitude); } rawData[i].date = new Date(rawData[i].date_occ + "-04:00"); if(rawData[i].location_1) { if (rawData[i].location_1.latitude && rawData[i].location_1.longitude) { goodData.push(rawData[i]); }; }; }; console.log(goodData); var nextDate = new Date(goodData[0].date); console.log("first nextDate: " + nextDate); //initilaize variables for the D3 chart var countArray = [], svg, day, x, y, margin, height, width, intervalCounter = 10, index = 0, lastDate, data = { max:15, min:0, data:[] }; initializeChart(); //iterate setInterval(function () { //iterates 10 times for each day if (intervalCounter == 10){ intervalCounter = 0; getAnotherDay(); } else { intervalCounter++; } //create new array for live points, push it to the map var newData = []; for(var j=0;j<data.data.length;j++) { var point = data.data[j]; if(point.value >= 10) { point.fresh = false; } //fade in fresh points, fade out unfresh points if(point.fresh) { point.value = point.value + .8; } else { point.value = point.value - .1; } if(point.value > 0) { newData.push(data.data[j]); } } data.data = newData; heatmapLayer.setData(data); //update the chart day = svg.selectAll(".day") .data(countArray) .enter() .append("g") .attr("class", "day") .attr("transform", function (d) { //var yesterday = new Date(d.date); //yesterday = yesterday.setDate(yesterday.getDate() - 1) return "translate(" + x(d.date) + ",0)"; }) .append("rect") .attr("width", 28) .attr("y", function (d) { return height - y(d.count); }) .attr("height", function (d) { return y(d.count); }) .attr("class", function (d) { return (d.date); }) }, 100); function getAnotherDay() { nextDate = new Date(nextDate.setHours(24,0,0,0)); var todayCounter = 0; //iterate over goodData, push today's events to data.data for (;;index++) { var thisDate = goodData[index].date; console.log(thisDate + nextDate); if(thisDate.getTime() < nextDate.getTime()) { data.data.push(goodData[index]); todayCounter++; lastDate = thisDate; } else { //Still need to increment lastDate if there is no data if(todayCounter == 0) { console.log(lastDate); lastDate = lastDate.getDate() - 1; } var todayCount = { date:lastDate, count:todayCounter }; countArray.push(todayCount); break; } } } //sets margins and axes for the D3 chart. Borrowed from Chris Metcalf's example on dev.socrata.com function initializeChart() { // Set our margins margin = { top: 20, right: 20, bottom: 30, left: 60 }, width = 800 - margin.left - margin.right, height = 100 - margin.top - margin.bottom; // Our X scale x = d3.time.scale() .domain([new Date(goodData[0].date), d3.time.day.offset(new Date(goodData[goodData.length - 1].date), 1)]) .rangeRound([0, width - margin.left - margin.right]) //.ticks(d3.time.day, 1); // Our Y scale y = d3.scale.linear() .domain([0,100]) .rangeRound([height, 0]); // Our color bands var color = d3.scale.ordinal() .range(["#308fef", "#5fa9f3", "#1176db"]); // Use our X scale to set a bottom axis var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); // Same for our left axis var yAxis = d3.svg.axis() .scale(y) .orient("left") .tickValues([0,50,100]); // Add our chart to the #chart div svg = d3.select("#chartBox").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") .attr("class", "y axis") .call(yAxis); }; }); </script> </body> </html> It should function exactly like this site, I am merely using a different dataportal and mine is centered on LA. http://chriswhong.github.io/nyc-heatmap/
D3 + Google Maps + multi-point paths
I am plotting points on a Google Map (so far so good) and then plotting a line between each point based on the point order in the underlying data (not so good). Like a trail or route. Unlike the handful of examples I've seen doing this, my data are not in GeoJSON format, and I would really like to keep it that way if at all possible. I have tried to adapt the exampels posted here and here but without success. My results end up with no lines being drawn, and I can't tell if that's because of a projection error or something else syntactical with D3. I have tried to debug through console.log() statements, but I am very week on GIS projections. Here is the code to plots the points <!DOCTYPE html> <html> <head> <meta name="viewport" content="initial-scale=1.0, user-scalable=no"/> <script type="text/javascript" src="https://maps.google.com/maps/api/js?sensor=true"></script> <script src="../js/d3.v3.min.js"></script> <style type="text/css"> html, body, #map { width: 100%; height: 100%; margin: 0; padding: 0; } .markers { position: absolute; } svg.pts { position: absolute; } .markers border { position: absolute; stroke: black; stroke-width: 2px; } .markers svg.pts { width: 60px; height: 20px; padding-right: 100px; font: 10px sans-serif; } .markers circle { fill: brown; stroke: black; stroke-width: 1.5px; } .SvgOverlay path { stroke: Orange; stroke-width: 2px; fill: Orange; fill-opacity: .3; } </style> </head> <body> <div id="map"></div> <script type="text/javascript"> var map = new google.maps.Map(d3.select("#map").node(), { zoom: 15, center: new google.maps.LatLng(29.371397, -81.54938), //N/S E/W mapTypeId: google.maps.MapTypeId.ROADMAP }); var data = [ //note this is not in GeoJSON format {name:"pt1",lng:-81.55082967,lat:29.374915304}, {name:"pt2",lng:-81.55211713,lat:29.373504039}, {name:"pt3",lng:-81.5842252,lat:29.417969924}, {name:"pt4",lng:-81.55230021,lat:29.374245073}, {name:"pt5",lng:-81.55115,lat:29.37263}, {name:"pt6",lng:-81.58737814,lat:29.358476912}, {name:"pt7",lng:-81.59230268,lat:29.359308171}, {name:"pt8",lng:-81.58783883,lat:29.356449048}, {name:"pt9",lng:-81.58189168,lat:29.420264027}, {name:"pt10",lng:-81.58288,lat:29.4202}, {name:"pt11",lng:-81.56079477,lat:29.359527893}, {name:"pt12",lng:-81.55861145,lat:29.356670068}, {name:"pt13",lng:-81.57961314,lat:29.420893275}, {name:"pt14",lng:-81.579302,lat:29.419368}, {name:"pt15",lng:-81.55979967,lat:29.359768002}, {name:"pt16",lng:-81.55823261,lat:29.36122515}, {name:"pt17",lng:-81.58189168,lat:29.420264027}, {name:"pt18",lng:-81.57997524,lat:29.421120323}, {name:"pt19",lng:-81.58148399,lat:29.420030491}, {name:"pt20",lng:-81.57839075,lat:29.420766158}, {name:"pt21",lng:-81.57982489,lat:29.42002304}, {name:"pt22",lng:-81.580266,lat:29.420212}, {name:"pt23",lng:-81.5820392,lat:29.42048164}, {name:"pt24",lng:-81.57894731,lat:29.420509033}, {name:"pt25",lng:-81.57819629,lat:29.418834169} ]; var overlay = new google.maps.OverlayView(); overlay.onAdd = function() { var layer = d3.select(this.getPanes().overlayLayer).append("div") .attr("height", "100%") .attr("width", "100%") .attr("class", "markers") .attr("id", "layer"); layer[0][0].style.width = "1366px"; layer[0][0].parentNode.style.width = "100%"; layer[0][0].parentNode.style.height = "100%"; layer[0][0].parentNode.parentNode.style.width = "100%"; layer[0][0].parentNode.parentNode.style.height = "100%"; layer[0][0].parentNode.parentNode.parentNode.style.width = "100%"; layer[0][0].parentNode.parentNode.parentNode.style.height = "100%"; layer[0][0].parentNode.parentNode.parentNode.parentNode.style.width = "100%"; layer[0][0].parentNode.parentNode.parentNode.parentNode.style.height = "100%"; // Add points overlay.draw = function() { var projection = this.getProjection(), padding = 10; var point = layer.selectAll("svg") .data( data ) .each(transform) // update existing markers .enter().append("svg:svg") .each(transform) .attr("class", "point pts") // Add marker on points point.append("svg:circle") .attr("r", 4.5) .attr("cx", padding ) .attr("cy", padding ); // Add a label on points point.append("svg:text") .attr("x", padding + 7) .attr("y", padding) .attr("dy", ".31em") .text( function(d) { return d.name; } ); //Here is where I'd like to add lines connecting the points, in order //of appearance in the data object 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) { //console.log(d); e = _projection( d.lat, d.lng ) return d3.select(this) .style("left", e[0] + "px") .style("top", e[1] + "px"); } }; }; // Bind overlay to the map… overlay.setMap(map); </script> </body> </html> And here is a JSFiddle Suggestions to get the path added via the data object as presented are most appreciated.
Ok, so I took a look at your code and refactored it a bit. But here is a basic working version of a path drawn between the points: http://jsfiddle.net/AJvt4/3/. There is one caveat though, and that is that the overlayPane doesn't expand when the map pans. I'm not too familiar with google maps so not sure how much I can help there. Here's an explanation of the changes made: First I created a encompassing svg to house all of your d3 elements in the onAdd event: var svg = layer.append('svg') .attr('x', 0) .attr('y', 0) Also in the onAdd event I added a d3 path generator (you can read more here): var lineFn = d3.svg.line() .x(function (d) { e = _projection(d.lat, d.lng); return e[0] + padding }) .y(function (d) { e = _projection(d.lat, d.lng); return e[1] + padding }) To actually draw the line, I added this in the add event handler: var line = svg.selectAll('.path').data([data]) line.enter().append('path') line.attr('class', 'path') .attr('d', lineFn) It's important to note the array around the data ([data]). This is because d3 expects an array of arrays with each inner array holding points to a line. This makes it easier to draw multiple lines. In your case there is only one line. You'll notice a few other changes to make the code a bit more d3-esque. Hope that helps get you started!