Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3 map SVG performance

I've been struggling the past few days to optimize performance on a D3 map, especially on mobile. I am using SVG transforms for zooming and panning but made the following observation: the overkill comes from path strokes used to fake spacing between countries.

I have uploaded a pair of sample maps for comparison:

http://www.nicksotiriadis.gr/d3/d3-map-1.html

http://www.nicksotiriadis.gr/d3/d3-map-2.html

The only difference between the two maps is the stroke path along the country paths, and the difference in performance is even noticeable on desktop devices - but more obvious on mobile. Removing the path strokes makes mobile performance a breeze..

I tried all kinds of svg stroke shape-rendering options without significant results.

Now to the question. Is there any way to remove a thin border from each country to fake the spacing between countries instead of using a stroke?

If anyone else has a different suggestion I'd love to hear it!

Update: Attaching explanation photo.

What I have drawn is this. The red arrow points to the country joints. When adding a stroke in a color same as the background to the country paths (here depicted in dark grey color) it creates the sense that the countries are seprated - however this adds a serious performance hit on mobile devices. What I am looking for is somehow re-shape the countries paths so that their borderlines are where the blue arrow points, but without having a stroke.

enter image description here

Update 2: People seem not to be able to understand what I am looking for, so I am updating this in order to make the question even clearer.

enter image description here

Let's assume that the original countries paths are shown on the left of this image. What I am looking for is a way that I can somehow 'contract' the paths inwards so that the newly created paths shown in red, leave enough empty space between them that will 'emulate' a stroke between them.

Doing this, will leave no use to having an extra layer of strokes, thus gain performance from only using paths instead of paths+strokes.

Update 2: Hello again, I seem to have found a half-solution to my problem. I managed to extract the topojson to shapefile, edit the shapefile the way I want (used a program named OpenJump), but the conversion takes away all the topojson properties I need - id, country name, so I can't convert back to the original topojson.

Does anyone have any suggestions?

like image 282
scooterlord Avatar asked Nov 06 '15 09:11

scooterlord


3 Answers

D3 has a thing just for that: topojson.mesh() (see documentation). The idea is that since most countries share borders, there's no need to draw the shared borders twice. If you can draw each border only once, you get as much as 80% reduction in the number of strokes you have to draw. The mesh method does the javascript processing to turn a bunch of closed shapes (countries) into the multiline path of just the borders between them. You can then draw that multiline path into a single <path> object that you position on top of the fills.

The mesh looks like this. Here's another example.

like image 51
meetamit Avatar answered Nov 20 '22 06:11

meetamit


Finally found the answer. This radically improves d3 map performance!

1) I got my topojson file and extracted to shapefile using mapshaper.org. This gives 3 files: .shp, .shx, .dbf . From what I realized the .dbf file holds all the TopoJSON properties/attributes.

2) Opened the .shp shape file to OpenJUMP http://www.openjump.org/ - Which automatically imports the .dbf file as well.

3) I selected the countries layer and went to Tools > Analysis > Buffer.

4) Checked the Update geometry in source layer box so that the geometry is edited without losing the rest of the attributes/properties and added a negative Fixed Distance -0.1. This shrinked all the country geometries to the result I was looking for.

5) Saved Dataset as ESRI Shapefile

6) Reimported BOTH .shp and .dbf that were produced from OpenJUMP back to mapshaper.org - careful, BOTH files.

7) Exported as TopoJSON. Contains new shape and all original properties/attributes!

The following link has been updated with the new produced map; we have a 'bordered' look without the need of strokes.

http://v7.nicksotiriadis.gr/d3/d3-map-1.html

Compare the performance to this link that has the original shapes + stroke. Please try on mobile to see the performance difference!

http://v7.nicksotiriadis.gr/d3/d3-map-2.html

Also, here is the updated world map TopoJSON file in case someone wants some extra performance! :D

http://v7.nicksotiriadis.gr/d3/js/world-topo-bordered.json

like image 22
scooterlord Avatar answered Nov 20 '22 05:11

scooterlord


There might be a couple of reasons of this behaviour (on my computer, everything is working fine at the same speed ):

Browser

Which browser do you use ? On Chrome, your exemples are working perfectly.

TopoJson

eg. previous answer.

Animation

You are launching the animation when the page is loading. You might want to add a delay (animation().delay(in ms)). There is also a function in D3: queue(), https://github.com/mbostock/queue which load the data before launching a function.

--

If none of this change your problem, and if you want it to work fine on mobile, you can try to mix D3 and Leaflet (map for mobiles), which is great in term of performance by loading tiles.

One example:

http://bl.ocks.org/zross/6a31f4ef9e778d94c204

Hope it helps

like image 1
Yoann Avatar answered Nov 20 '22 05:11

Yoann