D3 and Leaflet - svg circles not showing - javascript

I am trying to draw SVG circles on a map background, but while they are showing up in the elements (using Chrome Dev tools) they are not shown on the page. What am I missing here, why are they hidden?
I have tried to change the fill, opacity of the map and of the circle but I can't figure out why it isn't rendering?
My code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Leaflet and D3 map</title>
<link rel="stylesheet" href="../leaflet.css" />
<script type="text/javascript" src="../leaflet.js"></script>
<script type="text/javascript" src="../d3.js"></script>
<style type="text/css">
#map{
width: 700px;
height: 600px;
}
</style>
</head>
<body>
<div id="map"></div>
<script type="text/javascript">
//
// LOAD THE MAP FROM MAPBOX & LEAFLET
//
var map = L.map("map").setView([50.0755,14.4378], 12);
mapLink = 'Mapbox';
L.tileLayer (
"link to mapbox",{
attribution:"© " + mapLink + " Contributors",
maxZoom:20,
}).addTo(map);
//
// Create the SVG layer on top of the map
//
L.svg().addTo(map);
// Create the standard variables selecting the SVG in the map element
var svg = d3.select("#map").append("svg");
var g = svg.append("g");
//Load the coordinate for the circle
var objects = [ {"circle":{"coordinates":[50.0755,14.4378]}}];
//Loop through to create a LatLng element that can be projected onto Leaflet map
for(var i = 0;i<objects.length;i++){
objects[i].LatLng = new L.LatLng(objects[i].circle.coordinates[0], objects[i].circle.coordinates[1])
};
//Create the circle object and store it in features
var feature = g.selectAll("circle")
.data(objects)
.enter()
.append("circle")
.style("fill", "red")
.attr("r", 20);
//Make the circle dynamic, by calling the update function whenever view is view is reset
map.on("viewreset", update)
//Call the update also on first load of the web page
update();
//Updates the position of the circle every time the map is updated
function update(){
feature.attr("transform",
function(d){
return "translate("+
map.latLngToLayerPoint(d.LatLng).x+","+
map.latLngToLayerPoint(d.LatLng).y+")";
})
};
</script>
</body>
</html>

As you note, your circle is appended:
But, it is invisible not because of opacity or color, but because you don't set the dimensions of the svg. With default dimensions of the svg, your circle is beyond its border and consequently hidden (it resides in the middle of the map, at [350,300], while the default size of an svg is 300x150 likely browser dependent). Try:
var svg = d3.select("#map")
.append("svg")
.attr("width",700)
.attr("height",600)
As your map is 700 pixels across and 600 pixels high.

Related

Unexpected lines/polygons when converting JSON to topoJSON

I want to display the topoJSON on a leaflet map with d3. Doing that, I follow this example hosted on GitHub.
That is the code I derived from the gitHub example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3 Test</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css">
<script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="http://d3js.org/topojson.v1.min.js"></script>
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
</head>
<body>
<div id="map" style="width:600px; height:600px;"></div>
<script>
var map = new L.Map("map", {
center: [37.8, -96.9],
zoom: 4
})
.addLayer(new L.TileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"));
var svg = d3.select(map.getPanes().overlayPane).append('svg');
var g = svg.append('g').attr('class', 'leaflet-zoom-hide');
d3.json("pathToTopotJson", function(error, collection) {
if (error) throw error;
console.log(collection)
var bounds = d3.geo.bounds(topojson.feature(collection, collection.objects.collection));
var path = d3.geo.path().projection(projectPoint);
var feature = g.selectAll('path')
.data(topojson.feature(collection, collection.objects.collection).features)
.enter()
.append('path');
map.on('viewreset', reset);
reset();
// Reposition the SVG to cover the features.
function reset() {
var bottomLeft = projectPoint(bounds[0]),
topRight = projectPoint(bounds[1]);
svg.attr('width', topRight[0] - bottomLeft[0])
.attr('height', bottomLeft[1] - topRight[1])
.style('margin-left', bottomLeft[0] + 'px')
.style('margin-top', topRight[1] + 'px');
var translation = -bottomLeft[0] + ',' + -topRight[1];
g.attr('transform', 'translate(' + -bottomLeft[0] + ',' + -topRight[1] + ')');
feature.attr('d', path);
}
// Use Leaflet to implement a D3 geographic projection.
function projectPoint(x) {
var point = map.latLngToLayerPoint(new L.LatLng(x[1], x[0]));
return [point.x, point.y];
}
})
</script>
The topoJSON gets displayed. But, with unexpected Polygons/Lines:
When I display the JSON the polygons/lines are not there:
Whats going wrong with topoJSON? Is it something in my code or did the converting go wrong?
Whats going wrong with topoJSON? Is it something in my code or did the converting go wrong?
There's nothing wrong, that is a common artifact when a polygon crosses the antimeridian.
Reproject your data to a different map projection to avoid the antimeridian altogether, or use the --spherical and --cartesian topojson command-line options to work around the problem.
You should do a bit of research into antimeridian crossings. Also, try isolating the problematic geometries (i.e. russia), and seeing if that geometry alone gets transformed to TopoJSON and displayed in a proper manner. Isolating problematic geometries will make your life easier, and will make bugs more apparent.

Why does a <h2> Header clip the succeeding SVG element?

I tried a basic visualization with d3.js and dimple from udacity, however, I observe an effect I can't explain:
I run a simple dimple.js visualization which is embedded in an svg element which works fine.
When I preceed the svg element with a header, ... - see line 27
d3.select("body").append("h2").text("World Cup Attendance");
...the chart within the svg element seems to get translated and clipped at the bottom.
Why does adding this line alter the chart?
To my understanding the SVG element is an independent element which has its own relative coordinate system - not affected by preceding html elements...
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://dimplejs.org/dist/dimple.v2.1.2.min.js"></script>
<style>
circle.dimple-series-1 {
fill: red;
}
</style>
<script type="text/javascript">
function draw(data) {
/*
D3.js setup code
*/
"use strict";
var margin = 75,
width = 1400 - margin,
height = 600 - margin;
// this line moves the labels of the x axis
d3.select("body").append("h2").text("World Cup Attendance");
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin)
.attr("height", height + margin)
.append('g')
//.attr("transform", "translate(0,-30)")
.attr('class','chart');
var data = [
{ "year":"1980", "attendance":245000 },
{ "year":"1984", "attendance":245000 },
{ "year":"1988", "attendance":304400 }
];
/*
Dimple.js Chart construction code
*/
var myChart = new dimple.chart(svg, data);
var x = myChart.addTimeAxis("x", "year");
myChart.addMeasureAxis("y", "attendance");
x.dateParseFormat = "%Y";
x.tickFormat = "%Y";
// x.timeInterval = 4;
myChart.addSeries(null, dimple.plot.line);
myChart.addSeries(null, dimple.plot.scatter);
myChart.draw();
};
</script>
</head>
<body>
<script type="text/javascript">
/*
Use D3 (not dimple.js) to load the TSV file
and pass the contents of it to the draw function
*/
draw();
// d3.tsv("world_cup.tsv", draw);
</script>
</body>
</html>
I am going to guess it's because of this dimple method, "dimple._parentHeight". It calculates the height of the parent element of the svg and has a workaround for a firefox issue, so you could see a difference based on the container of the svg (body, in this instance) having or not having another element in it.
Your best bet (and what I've done for a similar reason) is to wrap the svg inside a div which will compute the height correctly :
var svg = d3.select("body")
.append("div")
.append("svg")

Path not showing in d3.js topoJson graph

I am trying to draw a map using topoJson so I followed this example
http://bl.ocks.org/mbostock/4122298
but I am not getting anything drawn.
here's what i wrote
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<style>
path {
fill: #ccc;
stroke: #fff;
stroke-width: .5px;
}
path:hover {
fill: red;
}
</style>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 500;
var path = d3.geo.path();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("tunisia.json", function(error, topology) {
console.log(topology);
svg.selectAll("path")
.data(topojson.feature(topology, topology.objects.governorates).features)
.enter().append("path")
.attr("d", path);
});
</script>
</body>
</html>
After some debugging it turns out that the path in my case is added as follows:
<svg width="960" height="500">
<path></path>
<path></path>
</svg>
whereas it should normally be like this:
<svg width="960" height="500">
<path d="M183.85631949544694,17.16574961388676L184.64695256075555,18.261986556132797L184.24437929962187,21.436416964644536L184.9109502450185,22.72190753660925L183.42733139583214,23.600229178621248L181.43637647772152,23.38526266060535L162.4858998398068,18.04698631290296L162.95134674943927,16.322885588815097L161.24381018256219,15.20848145955324L160.04585728433227,11.701769628478132L161.0879861841512,10.793553936506555L172.9773901748378,14.256236175137701Z"></path>
</svg>
here is the data I am using:
https://raw.githubusercontent.com/mtimet/tnacmaps/master/topojson/tunisia.json
could you please check what I am doing wrong
There is no problem with your json file.
The issue you are having is that you are not defining a projection for your d3.geo.path() which means it falls back to the default. According to the documentation linked above:
#d3.geo.path()
Creates a new geographic path generator with the default settings: the albersUsa projection and a point radius of 4.5 pixels.
Your geo data is for a map of Tunisia, so an albersUsa projection won't contain any of the coordinates in your dataset. That is why the path data is empty in your output.
To fix this, you need to define a projection. You can do this when you load your data, and you can use d3.geo.bounds(), passing in your featureCollection to find the geographic boundaries of your data.
var featureCollection = topojson.feature(topology, topology.objects.governorates);
var bounds = d3.geo.bounds(featureCollection);
Then from these boundaries, you can calculate the center of your featureCollection:
var centerX = d3.sum(bounds, function(d) {return d[0];}) / 2,
centerY = d3.sum(bounds, function(d) {return d[1];}) / 2;
Then you can use this to center your projection. For example, if you're using a mercator projection, you could do the following:
var projection = d3.geo.mercator()
.scale(3000)
.center([centerX, centerY]);
The choice of a scale of 3000 is arbitrary, it just seemed to work well in this case, tweak it to whatever works for you.
Finally, you need to set the .projection() of your path to the projection you made, before actually creating the svg paths.
path.projection(projection);
svg.selectAll("path")
.data(featureCollection.features)
.enter().append("path")
.attr("d", path);
HERE is a working example using your data.

How to drag and drop a line segment (fragment only) in kinetic JS

There is a tut on line drag and drop here :
http://www.html5canvastutorials.com/kineticjs/html5-canvas-drag-and-drop-a-line-with-kineticjs/
But more interestingly how to do drag and drop a line segment (fragment only) in kinetic JS ?
There's no example for doing this.
In my use case the segment stays attached to polyline, it just changes angle. so I don't want to create another polyline with one segment only which would also be a waste of resource.
How to drag one segment of a polyline with the other segments remaining connected
You can create a series of 2-point kinetic.lines (just a start and end point).
Each new start point is the end point of the previous line
This screenshot shows the green segment being dragged and the other segments changing accordingly.
Result: A polyline made up of draggable segments.
Note: after any segment is dragged, the getX()/getY() contain the distance dragged from its original XY.
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/GrEEL/
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Prototype</title>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.5.5.min.js"></script>
<style>
#container{
border:solid 1px #ccc;
margin-top: 10px;
width:300px;
height:300px;
}
</style>
<script>
$(function(){
var stage = new Kinetic.Stage({
container: 'container',
width:300,
height: 300
});
var layer = new Kinetic.Layer();
stage.add(layer);
var colors=['red','green','blue','orange'];
var lines=[];
var points=[];
points.push({x:125,y:50});
points.push({x:75,y:75});
points.push({x:200,y:200});
points.push({x:275,y:100});
for(var i=0;i<points.length-1;i++){
var p1=points[i];
var p2=points[i+1];
addSegment(i,p1.x,p1.y,p2.x,p2.y,colors[i]);
}
layer.draw();
function addSegment(i,x1,y1,x2,y2,color){
var line = new Kinetic.Line({
points: [x1,y1,x2,y2],
stroke:color,
strokeWidth: 25,
lineCap:"round",
lineJoin:"round",
draggable:true
});
line.index=i;
line.on("dragend",function(){
// get the amount of xy drag
var i=this.index;
var dx=this.getX();
var dy=this.getY();
// update the points array
var p0=points[i];
var p1=points[i+1];
p0.x+=dx;
p0.y+=dy;
p1.x+=dx;
p1.y+=dy;
// reset the dragged line
this.setPosition(0,0);
this.setPoints([p0.x,p0.y,p1.x,p1.y]);
layer.draw();
});
line.on("dragmove",function(){
// get the amount of xy drag
var i=this.index;
var dx=this.getX();
var dy=this.getY();
// adjust the ending position of the previous line
if(i>0){
var line=lines[i-1];
var pts=line.getPoints();
pts[1].x=points[i].x+dx;
pts[1].y=points[i].y+dy;
line.setPoints(pts);
}
// adjust the starting position of the next line
if(i<lines.length-1){
var line=lines[i+1];
var pts=line.getPoints();
pts[0].x=points[i+1].x+dx;
pts[0].y=points[i+1].y+dy;
line.setPoints(pts);
}
layer.draw();
});
layer.add(line);
lines.push(line);
}
}); // end $(function(){});
</script>
</head>
<body>
<div id="container"></div>
</body>
</html>

Stop animation in VivaGraph.JS

How I can stop the animation for the nodes in VivaGraph.JS
everything is great but nodes still moving and some nodes come out in the frame.
I can't use this example :
https://github.com/anvaka/VivaGraphJS/blob/master/demos/other/constantLayout.html
because is not good for me the determinate for every node a position.
My code :
<!DOCTYPE html>
<html>
<head>
<title>02. Custom node appearance. Vivagraph SVG tutorial.</title>
<script type="text/javascript" src="../../dist/vivagraph.js"></script>
<script type="text/javascript">
function main () {
// Create a graph:
var graph = Viva.Graph.graph();
for(i=0;i<20;i++)
{
graph.addNode(i, '91bad8ceeec43ae303790f8fe238164b');
}
var graphics = Viva.Graph.View.svgGraphics();
graphics.node(function(node) {
var url = 'https://secure.gravatar.com/avatar/' + node.data;
return Viva.Graph.svg('image')
.attr('width', 24)
.attr('height', 24)
.link(url);
});
graphics.placeNode(function(nodeUI, pos) {
nodeUI.attr('x', pos.x - 12).attr('y', pos.y - 12);
});
// Render the graph with our customized graphics object:
var renderer = Viva.Graph.View.renderer(graph, {
graphics : graphics
});
renderer.run();
}
</script>
<style type="text/css" media="screen">
html, body, svg { width: 100%; height: 100%;}
</style>
</head>
<body onload='main()'>
</body>
</html>
I wish I explain good my problem, someone can help me please.
You can use the following methods of the renderer object to accomplish this.
To start the rendering - renderer.run();
To pause the rendering - renderer.pause();
To resume the rendering - renderer.resume();
To reset the scale of the rendering to fit all elements - renderer.reset();
To remove the rendering - renderer.dispose();
Hope this is helpful...

Categories

Resources