Unexpected lines/polygons when converting JSON to topoJSON - javascript

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.

Related

How do I create axes from categorical and numerical data using d3.js (version 7.0.0)?

Context: I am a beginner in d3.js and have been at this problem for a few days; I have gone through many solutions pertaining to similar problems on here but even though I've replicated the solutions as closely as possible, the issue persists.
I'm trying to create a bar chart with both axes using this simple CSV file:
Food,Yummy
Apples,6
Green Beans,10
Egg Salad Sandwich, 8
Cookies, 2
Liver,1
Burrito,5
I've tried using the following code to create the axes of a bar chart (x-axis: Food, y-axis: Yummy):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3: Loading data from a CSV file</title>
<script type="text/javascript" src="../d3.js"></script>
</head>
<body>
<script type="text/javascript">
//Defining svg dimensions
width = 250;
height = 600;
margin = 20;
//Creating svg element
var svg = d3.select("body")
.append("svg")
.attr("height", height)
.attr("width", width);
//Loading csv data
rowConverter = function(d) {
return {
Food: d.Food,
Yummy: +d.Yummy
};
}
data = d3.csv("food.csv", rowConverter).then(function(data) {
console.log(data);
});
//Creating scales for x and y axis
var xSxale = d3.scaleBand()
.domain(data.map(function(d) {return d.Food;}))
.range([0, w-margin]);
var maxYummy = d3.max(data, function(d) {return +d.Yummy;});
var yScale = d3.scaleLinear()
.domain([0, maxYummy])
.range([h-margin, 0]);
</script>
</body>
</html>
For the x-axis, I'm getting the following error: "data.map is not a function". I can't find any other way to get the different Food labels onto the x-axis.
For the y-axis, I'm getting the following error: "Uncaught TypeError: values is not iterable
at Object.max$3 [as max] (d3.js:641)
at (index):39" which seems to be pertaining to the d3.max() function.
I'm unclear as to why both errors are existing since I've seen them accepted as solutions on other questions. Any help would be much appreciated by this beginner developer. Thanks so much in advance.

D3 and Leaflet - svg circles not showing

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.

Morphing with Velocity.js doesnt work

Hej,
i have this plunker: http://plnkr.co/edit/bKzi6rU3lIXT4Nz2wkR8?p=info
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="stilovi/main.css">
<title>Testing</title>
</head>
<body>
<div id="avengers"></div>
</body>
<script src="snap.svg-min.js"></script>
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://rawgit.com/julianshapiro/velocity/master/velocity.min.js"></script>
<script>
function fetchXML(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function(evt) {
//Do not explicitly handle errors, those should be
//visible via console output in the browser.
if (xhr.readyState === 4) {
callback(xhr.responseXML);
}
};
xhr.send(null);
};
/*var list = ["test3.svg","test.2svg"];*/
//fetch the document
fetchXML("test3.svg", function(newSVGDoc) {
//import it into the current DOM
var n = document.importNode(newSVGDoc.documentElement, true);
document.getElementById("avengers").appendChild(n);
var ironman = document.getElementsByTagName("polygon");
var ironmanlist = Array.prototype.slice.call(ironman);
/*alert(ironmanlist.length);*/
ironmanlist.forEach(function(elem, i) {
/*for (var index = 0; index < elem.points.length; ++index){
//2.test case morphing each point (not working)//
console.log(elem.points[index]);
$.Velocity(elem.points[index],{x:100,y:100},{duration:Math.floor(Math.random() * (3000 - 1000 + 1)) + 1000}, "ease-in-out");
//3.test case morphing each point in another way (not working)//
/*$(elem.points[index])
.velocity({x:100,y:100},{duration:Math.floor(Math.random() * (3000 - 1000 + 1)) + 1000}, "ease-in-out");
console.log(elem.points[index]);
}*/
//1. working test case (translation)//
console.log(elem.points[0].x);
$.Velocity(elem, {
translateX: -300
}, {
duration: Math.floor(Math.random() * (3000 - 1000 + 1)) + 1000
}, "ease-in-out");
//$.Velocity(elem,{rotateZ:"45deg"},{duration:Math.floor(Math.random() * (3000 - 1000 + 1)) + 1000}, "ease-in-out");
console.log(elem.points[0].x);
//End of 1. working test case//
});
console.log(ironmanlist);
});
</script>
</html>
With my code, and some examples. What I want to do is morph each polygon from one SVG image, into another polygon from another SVG image. The translation works, but I'm not sure how to do a morph.
Can anyone help, or check the code and tell me what I am doing wrong?
There are a lot of polygons, and I need it to be fast so i went with velocity.js for this.
I was also thinking of maybe moving it all to three.js, and maybe convert it to a format that would be best to use with three.js. But if it is a possibilty to use it as svg and keep a great performance i would do so.
As far as I'm aware, velocity.js doesn't support this as (taken from their website):
In general, Velocity can animate any property that takes a single numeric value.
As SVG paths usually (always?) consist of multiple values, morphing is way beyond their current scope.
However, I can highly recommend Snap for morphing SVG paths.

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...

Strange bug with OpenLayers + CloudMade

I am trying something fairly simple, you can see a demo here:
http://www.jsfiddle.net/VVe8x/19/
This bug only appears in Firefox, so to see it press either one of the links once (it will take you to either NY or Israel) then press the other link.
The bug is that it will not show me the tiles in that location, instead it will show me the background of the div.
P.S In Chrome this works flawlessly.
I dont know if this is a clue or it might confuse you, if in between pressing either NY or Israel links you press the "view the world" link it will allow you then to see the other location..
Full Source for reference
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<body>
show me NY
show me TLV
show world map(a "workaround"
<div id='myMap' style="height: 600px; width: 600px; position: relative"></div>
<script src="http://openlayers.org/api/OpenLayers.js" type="text/javascript"></script>
<script src="http://developers.cloudmade.com/attachments/download/58/cloudmade.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
map = new OpenLayers.Map("myMap", {
controls: [
new OpenLayers.Control.Navigation(),
new OpenLayers.Control.PanZoomBar()
]
});
var cloudmade = new OpenLayers.Layer.CloudMade("CloudMade", {
key: 'd5da652e33e6486ba62fca3d18ba70c9'
});
map.addLayer(cloudmade);
var epsg4326 = new OpenLayers.Projection("EPSG:4326");
map.setCenter(new OpenLayers.LonLat(40, 32), 2);
show1 = function(){
var bound1 = new OpenLayers.Bounds(-8236567.917898,4972686.066032,-8236148.409989,4972889.624407);
map.zoomToExtent(bound1); // to NY
};
show2 = function(e){
var bound2 = new OpenLayers.Bounds(3874818.203389,3773932.267033,3875217.305962,3774226.370443);
map.zoomToExtent(bound2); // to Israel
return false;
};
</script>
</body>
</html>
The myMap_OpenLayers_Container has the following CSS when the tiles are invisible:
position: absolute; z-index: 749; left: -2.02815e+7px; top: -2007340px;
If you change these around you can see that the correct tiles were loaded, so its likely to be jsFiddle messing them up. The tiles CSS when they don't show also have strange values.
Update:
Testing locally also produces the issue, so that rules out jsFiddle.
A fix would be to set this value after the zoom by calling a function such as:
updateCSS = function(){
OpenLayers_Container = document.getElementById("myMap_OpenLayers_Container").style.left = "0px";
}
This looks like a bug, although if it is in OpenLayers or the CloudMade layer properties is hard to tell - I'd imagine the latter, or it would be a widely reported bug. The relevant code in OpenLayers.js appears to be:
centerLayerContainer: function(lonlat){
var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);
var newPx = this.getViewPortPxFromLonLat(lonlat);
if ((originPx != null) && (newPx != null)) {
this.layerContainerDiv.style.left = Math.round(originPx.x - newPx.x) + "px";
this.layerContainerDiv.style.top = Math.round(originPx.y - newPx.y) + "px";
}
I was running into this problem too, and it turned out I was not setting the map's center as I thought I was. The problem goes away if you first call map.setCenter(). For example:
var newCenter = new OpenLayers.Lonlat(longitude, latitude)
.transform(new OpenLayers.Projection('ESPG:4326'),
this.map.getProjectionObject());
this.map.setCenter(newCenter);
Hope this helps whoever next has the problem.

Categories

Resources