d3.js svg drawing contour lines from (geo/topo)json - json

I am looking for some guidance on how to create SVG image composed of paths based on contour line data coming from (geo/topo)JSON and render it with d3.js.
This is an image of what I'd like to create essentially:
Ultimately, I'd like the image to be able to scale responsively, animate (draw in lines, change color interactively) the paths, and be able to swap out different data files to render other images.
I have looked through several guides, tutorials etc, but it seems I am missing some steps or implementing the wrong procedure thus far, so I thought I'd ask here. Thanks for any help.
I'm trying to clarify the best way to do this and figure out what I'm misinterpreting in terms of the capabilities/procedures in d3.js.
I'm creating contour lines from DEM (Digital Elevation Model) data by extracting contours in QGIS. To get the JSON from these I have tried saving as GeoJSON in QGIS, and converting Shapefiles (ESRI .shp) to GeoJSON in OGR2OGR. I have also tried using Node's Topojson (https://www.npmjs.org/package/topojson). For the most part, I've been able to get JSON files, although there might be something wrong with the way I'm converting them, or the original contour data coming from QGIS that makes it incompatible with what I've tried in d3.
The results that I have gotten when the JSON gets rendered basically look like black boxes (looks like improperly rendered polygons gone wild inside the svg container). If I change the fill color to none I see crazy lines all over. I read somewhere that TopoJSON has to be polygons not lines or polylines since it uses arcs, but I got the same results with GeoJSON.
I'm wondering if this is a problem with the way I've exported the contour lines from QGIS and converted to JSON, perhaps the projections are not lining up? There also might be some errors in the lines that were created automatically when converting, but I'm not sure how to fix these. As you can see in the example image, some of my contours are not closed loops because they extend beyond the boundaries of the area I have data for/want to show. Does it make sense that these are supposed to be rendered as polylines or paths?
Also a note, I am not particularly interested in the "geospaciality" of these lines in terms of their lat-long or geographic location in this project. Basically just using contour lines from real geographic data to display the line patterns.
Here is my code:
<script type="text/javascript">
var width = 500,
height = 500;
var svg = d3.select("#section-1-svg").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("contour.json", function(error, contour) {
console.log(contour);
});
var path = d3.geo.path()
.projection(d3.geo.mercator());
d3.json("contour.json", function(error, json) {
svg.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path);
});
</script>
So other than just getting this to render on a basic level, other questions are:
Will I be able to select individual lines to animate with for example stroke-dashoffset?
Does this source JSON still contain elevation data so I can color lines based on elevation?
Thanks for your help in getting me on track with this! I would appreciate anyone clarifying this for me and informing me on the best way to render this.
EDIT: Using the code from user1614080's example, I'm rendering the lines thus:
svg.selectAll("path")
.data(topo).enter()
.append("path")
.style("fill", "none")
.style("stroke", function(d, i) {
return interp(cScale(d.properties.ELEV));
})
.attr("d", path)
.each(function(d) {
d.totalLength = this.getTotalLength();
console.log(d.totalLength);
})
.attr("stroke-dasharray", function(d) { return d.totalLength + " " + d.totalLength;})
.attr("stroke-dashoffset", function(d) { return d.totalLength; })
.transition()
.delay(function(d, i) { return i * 200; })
.duration(4000)
.ease("linear")
.attr("stroke-dashoffset", "0");
});
But I can't get the effect I need (lines drawing in). They seem to fade in from small dashes instead of drawing the entire dash. I can see in browser that the stroke-dasharray and offset is being assigned correctly, just can't figure out why the transition is not respected.

Following on from what Lar's mentioned I can't really see anything wrong with what you've got there (except that you're two identical d3.json calls but this shouldn't be causing you problems).
The workflow you describe is more or less identical to what I've done before. The only thing that I can think of that might be causing you trouble (although not in the code you've presented) is that topojson's default does not preserve feature properties and you have to use the -p switch.
Anyway, I've been meaning t do a contour example for a while and this seemed like a good opportunity so I created one which you can see here. There's a fair bit of explanation in the code and readme so I hope this helps you along the way.
Once you get your vis working you will be able to create all sorts of interactions etc. For instance in my example you could highlight contours on mouseover events, using the something like the following:
.on("mouseover", highlight)
.on("mouseout", unhighlight(this, d)
function highlight(x) {
var s = d3.select(this);
s.style("stroke", "red");
}
function unhighlight(x,y) {
var old = y.properties.ELEV;
var u = d3.select(x);
u.style("stroke", function(d, i) {
return interp(cScale(old));
})
}
Hope this gets you going

Related

How to automatic alignment Fibers, Volume and Mesh use XTK

I think it is a simple question, but it really confuse me...
In the case which I try to show three object together, like Fibers, Volume and Mesh, just as you can see in this demo web.
Demo Web: http://goo.gl/NP2eUo
But there is a problem, the Fibers object can not automatic alignment with Volume and Mesh. Those files are come from the same source (one subject's DICOM file) so it should can be match together in default.
If only show the Fibers object, it can be put in the center of the view. But if show three object together, then Fibers object will be Shift!!!
Is there any possible way can let three object automatic alignment together?
And here is my source code: http://goo.gl/Ttzc84
What if you try to call reset boundingBox on showtime?
...
// .. add the mesh
r.add(mesh);
r.onShowtime = function() {
r.resetBoundingBox();
}
r.render();
...
How did you generate the surface and the trk file? Freesurfer and DTK?
I suspect the trk file does not provide any IJKToRAS transformation.

SVG Stack not working in Chrome (webkit)

I have been exploring using SVG's for the latest website I've been building - bit behind the times so trying to catch up. I initially setup my file similar to the way I would do a normal sprite. Although this worked, it does seem a little clumsy when you want to take advantage of resizing the vector and then trying to find the new background position in the document!
After doing some research I came across the idea of stacking it via layers - which makes a heap of sense. After getting all excited and successfully doing this I then came across a few posts saying this isn't support in all browsers - typical.
https://code.google.com/p/chromium/issues/detail?id=128055#c6
Here is a great tutorial for stacking SVG images in a single file as well as some work arounds for browsers that don't support it: http://hofmannsven.com/2013/laboratory/svg-stacking/
Although this works fine, is there an alternative to save writing all this extra code and fallbacks?
After thinking about this a little I decided I could take advantage of the Apache server and see if I could simply inject what I needed into the document. The end result? Works perfect in all browsers :)
To start with I added some code in my .htaccess file to capture all .svg requests
RewriteRule ^(.*)\.svg$ /{path-to-file}/svg.php [L]
Then I wrote a few lines to deal with the target layer and inject that into the file
(UPDATE) Added new variable called target-fill to allow for dynamically changing the fill colour of a shape if required
<?php
// Set the SVG header
header('Content-Type: image/svg+xml');
$queryString = Array();
if(isset($_SERVER['QUERY_STRING'])) $queryString = explode('&', $_SERVER['QUERY_STRING']);
// Get target from the query string
$target = $queryString[0];
// Get a fill alternative if available and valid
if(isset($queryString[1]) && hexdec($queryString[1]) !== false) {
$targetFill = '#' . $queryString[1];
} else {
$targetFill = '';
}
// Validate the target - this is your ID in the SVG file
$validTargets = Array('Camera', 'Layer_1');
if(!in_array($target, $validTargets)) $target = false;
// Get contents of the file - tweak this depending on where you have saved this file to relative to the root of your website
$filename = '../..' . $_SERVER['REDIRECT_URL'];
// Get the contents of the file
$contents = file_get_contents($filename);
// Replace the target with the valid target above
// - doing it this way rather than echoing the target in the SVG file as it seemed like a security risk
if($target) $contents = str_replace('g:target', 'g#' . $target, $contents);
// Replace the fill colour if available
$contents = str_replace('target-fill', $targetFill, $contents);
// Output the amended SVG file
echo $contents;
Included near the top of the SVG is the stacking code to hide we don't want displayed and to turn on what we do
<defs>
<style>
svg g { display: none }
svg g:target, svg g:target g { display: inline }
svg g:target * { fill: target-fill; }
</style>
</defs>
And that is it. So now instead of calling your SVG file like (also works as a background image):
<img src="images/svg-file.svg#Camera">
You would do it like this
<img src="images/svg-file.svg?Camera">
The advantage of doing it this way is you can now also do some further checks based on the user agent to return an alternative file altogether if SVG isn't supported.
(UPDATE) You can now also express a second parameter to change the fill colour if required. Use it like this:
<img src="images/svg-file.svg?Camera&cc0000">
Hope this helps someone else out there.

Append Value to Rickshaw Graph Axis and what is ticksTreatment and Preserve

This is my first question on here so please go easy :)
I am trying to implement some line graphs with rickshaw graphs, d3 and jquery UI.
I have some vertical tabs and have successfully gotten the charts to load from external html files.
There was a bit of documentation on Rickshaw but I couldn't find what I was specifically after so I will ask this kind community a few questions if that is ok?
Firstly when loading Tabs in jquery UI from external html files where should I put all of the javascript and css into the page that is embedded (see below historic.html) or into the parent page? I have tried both and they seem to work I was just wanting to know best practice.
<ul>
<li><div id="live-icon"></div>LIVE GRAPHS</li>
<li><div id="historic-icon"></div>HISTORIC DATA</li>
Secondly, I the x-axis on the graph is in milliseconds. I would like to append "ms" to the end of each of the x-axis "ticks". so the x-axis would read 50ms, 100ms, 150ms etc... Can this be done?
And lastly in Rickshaw they have that fan-dangled example (http://code.shutterstock.com/rickshaw/examples/extensions.html) that has all of the bells and whistles. It has two properties that I cannot find any information on.
perserve: true ? and another example has tickFormat and tickTreatment? Could someone please explain what these do.
var graph = new Rickshaw.Graph( {
element: document.getElementById("chart"),
width: 900,
height: 500,
renderer: 'area',
stroke: true,
preserve: true,
Thankyou very much for your help.
Probably no longer relevant for the OP, but since it's still unanswered, I can answer the Rickshaw questions:
To append ms to the end of your ticks, you need to use the tickFormat option. In their tutorial, they set up the axis as follows:
var y_axis = new Rickshaw.Graph.Axis.Y( {
graph: graph,
orientation: 'left',
tickFormat: Rickshaw.Fixtures.Number.formatKMBT,
element: document.getElementById('y_axis'),
} );
Here, they're setting up the tickFormat to be a default they've built in, but in reality, it can take anything that conforms to what d3's axis' tickFormat takes. tickFormat should be a function that accepts a number and outputs a string. You probably want something along the lines of
var y_axis = new Rickshaw.Graph.Axis.Y( {
graph: graph,
orientation: 'left',
tickFormat: function (d) { return d + ' ms'; },
element: document.getElementById('y_axis'),
} );
This will make the ticks simple be the number followed by ms.
This also happens to answer one part of the three part question that follows. The other two parts concern tickTreatment and preserve.
The short answer is that tickTreatment gets added as a class to the ticks. The reason that this is useful is for CSS styling, which Rickshaw takes advantage of. They have some presets you can use for this. The one they're using in that example is called glow, which adds a white glow around the text to make it readable on top of the graph.
preserve is an option that affects whether or not the data you provide is copied before it's used. The relevant section from Rickshaw's source is here:
var preserve = this.preserve;
if (!preserve) {
this.series.forEach( function(series) {
if (series.scale) {
// data must be preserved when a scale is used
preserve = true;
}
} );
}
data = preserve ? Rickshaw.clone(data) : data;
Basically, if you set preserve to true (it defaults to false), it'll make a copy of the data first.

Click path to show/hide div

I'm trying to create a fairly straightforward interface with Raphael, whereby if you click on a given path you'll get a corresponding div to appear. Since I'm likely going to be using irregular shapes I'll be creating the shapes in Illustrator and then converting to paths using readysetraphael.com, but if there's a better way to do it I'm open to that too.
I'm basically looking to capture the functionality you see here, but with raphael objects as the buttons.
http://jsfiddle.net/4xV7b/
Here's my current fiddle -- what I don't understand is what needs to happen during the mouseclick event to show/hide the corresponding divs.
el.click(function() {
//mysterious voodoo magic goes here
});
If you were using Raphael 2, you could use the data method to store arbitrary information with your elements -- as in this example.
Essentially, when you're creating each path, you simply call
el.data( 'div_id', 'greenthing' );
Then, when that element is clicked, you can retrieve the indicated div from the element using
el.click( function()
{
var div_id = this.data( 'div_id' );
// Display the div here. Josemando's approach is perfectly cool and will work universally.
} );
If you wanted to make sure that only one div at a time is displayed, you could do this (primitive but effective):
el.click( function()
{
rsr.forEach( function( subEl )
{
var divId = subEl.data('div_id' );
if ( divId && document.getElementById( divId ) )
document.getElementById( divId ).style.display = 'none';
} );
} );
There are, of course, other ways to skin that cat.
You may also find that your path construction code would be more manageable if you put all of the shape-specific data into a structured object and then render each path in a loop. The fiddle I've provided above demonstrates such a technique, which will pay off quickly once you have more than a handful of shapes.
You can use pure javascript to show a div:
document.getElementById("redthing").style.display = "block";
If you want animations, you can try using jQuery, or even manually creating them with css

how to arrange the divs in the page like google map or other map site

We are now trying to build a map library like google/bing/yahoo,we will use it offline.
However I found that I have no idea about how to arange the divs in the page,since there are some many different types of divs.
1) the map tiles (small image 256X256)
2)the overlayer(marker/informationwindow/polygon...)
3)the control.
I have to try to read the html source codes of google and bing and etc. But I found it is difficult to understand them.
For exmaple,this frangment is copyed from another online map site of China.
As you can see,it is just a exmaple for how to adding a marker to the map.
But take the code,there are so many nested divs,most of them have the property of "width:0;height:0",I do not know why?
Since in my opinion,the marker is just an icon,just put it in the page.
Why use so many nested divs and even the "map" tag?
But I think they must have the advantages which I can not find.
Any one can give some suggestions?
Typically you insert a div in HTML when you want to create a block element but there is no more semantically-loaded element available with the correct meaning.
I think the answer to your question is to use just as many div elements as you need for your purposes. Do not add more just because you can. Sometimes you don't need any div elements at all - you can use other more meaningful elements such as img, ul, p, etc. You can sometimes avoid inserting a wrapping div by using CSS to change an inline element such as a into a block element.
If you need more later then add them later. Don't worry about what Google/Bing/Yahoo do. Their requirements are probably different to yours.
Have you looked at the Google Maps sample code and demo gallery?
http://code.google.com/apis/maps/documentation/javascript/demogallery.html
http://code.google.com/apis/maps/documentation/javascript/examples/index.html
I'm not sure how you would use this "offline" considering the sample you provided makes a call to the internet to get the map. Also all of these types of maps rely heavily on javascript and ajax calls to constantly update the map. Do you mean these pages would be secured and not public?
How about you just use maybe a 5x5 grid of divs, move them as they are dragged out of view, and then texture them dynamically with AJAX calls.
If I am understanding you correctly, all of the layers can be thrown on top of each other with z-index.
<div id="control" style="z-index:-1;"></div>
<div id="overlay" style="z-index:-2;"></div>
<div id="map" style="z-index:-3;"></div>
Then you can use each of these divs as containers for different parts of your map.
As you drag 1 div off to, say, the right, then it will automatically bump itself to the left side of your grid and retexture itself (background-image) through an ajax call.
That's what I would do, at least.
Use the Google Maps API you can see an example of custom tiles here: http://code.google.com/apis/maps/documentation/javascript/examples/maptype-base.html
You would need to copy all the files to your computer to be exceccible offline. Your javascript would look something like this:
function CoordMapType() {
}
CoordMapType.prototype.tileSize = new google.maps.Size(256,256);
CoordMapType.prototype.maxZoom = 19;
CoordMapType.prototype.getTile = function(coord, zoom, ownerDocument) {
var div = ownerDocument.createElement('DIV');
div.style.backgroundImage=coord+'.js';
return div;
};
CoordMapType.prototype.name = "Tile #s";
CoordMapType.prototype.alt = "Tile Coordinate Map Type";
var map;
var chicago = new google.maps.LatLng(41.850033,-87.6500523);
var coordinateMapType = new CoordMapType();
function initialize() {
var mapOptions = {
zoom: 10,
streetViewControl: false,
mapTypeId: 'coordinate',
mapTypeControlOptions: {}
};
map = new google.maps.Map(document.getElementById("map_canvas"),
mapOptions);
google.maps.event.addListener(map, 'maptypeid_changed', function() {
var showStreetViewControl = map.getMapTypeId() != 'coordinate';
map.setOptions({'streetViewControl': showStreetViewControl});
});
// Now attach the coordinate map type to the map's registry
map.mapTypes.set('coordinate', coordinateMapType);
}