block by hugolpz f02abc3d4a395f280f6a

World tour WP guidelines

Full Screen

World tour using Wikipedia Map guidelines, more precisely the soon to come new globe locator convention. Inspired from Jason Davies world tour, merged into my localisator code.

V.2 Change log

This code is a second wave of customization.

V.3 ideas

In a 3rd wave of customization, this code could be refactored to reduce code redundancies. The loop on data’s .countries should in order recalculate the bounding box, run the localisator with conditional framing, print the file, before to go to i+1.

References :

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<style>

text {
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 18px;
  font-weight: bold;
  text-anchor: middle;
}

</style>
<body>
<script src="//code.jquery.com/jquery-2.1.0.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.13/d3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/topojson/1.1.0/topojson.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3-geo-projection/0.2.9/d3.geo.projection.min.js"></script>
	
<script src="../js/wikiatlas.js"></script>	
	
<script>
/**
* Created with Wikiatlas.
* User: hugolpz
* Version: 2014.09.04 */
/* ****************************************************** */
/* MATH TOOLKIT ***************************************** */
function parallel(φ, λ0, λ1) {
  if0 > λ1) λ1 += 360;
  var dλ = λ1 - λ0,
      step = dλ / Math.ceil(dλ);
  return d3.range(λ0, λ1 + 0.5 * step, step).map(function(λ) { return [normalise(λ), φ]; });
}
function normalise(x) {
  return (x + 180) % 360 - 180;
}
/* ****************************************************** */
/* LOCALISATOR FN *************************************** */
var localisator = function (hookId,localisator_width, title, WNES0, WNES1, WNES2, WNES3) {
/* Init ************************************************* */
	var width  = 1*localisator_width,
    	height = 1*localisator_width;
	var lon_central = function(){ 
		var num;
		if(WNES2<WNES0){ num= -(WNES0+WNES2)/2+180; }
		else{ num= -(WNES0+WNES2)/2; }
		return num;
	};

	var proj = d3.geo.orthographic()
    	.scale(1/2*localisator_width)
    	.rotate([ lon_central(), -(WNES1+WNES3)/2 +10 ]); // orthographic + 10⁰ to simulate real life globe watching.

	var projection2 = proj
		.translate([width / 2 , height / 2 ])
		.clipAngle(90);

	var path = d3.geo.path()
		.projection(projection2);

/* SVG container **************************************** */
	var svg = d3.select(hookId).append("svg")
		.attr("id", title+"-orthographic_globe_locator_(wikiatlas_2014)")
		.attr("width", width)
		.attr("height", height)
		.call(d3.behavior.drag()
		  .origin(function() { 
			var rotate = projection2.rotate(); 
			return {x: 2 * rotate[0], y: -2 * rotate[1]}; 
		})
		  .on("drag", function() {
			projection2.rotate([d3.event.x / 2, -d3.event.y / 2, projection2.rotate()[2]]);
			svg.selectAll("path").attr("d", path);
		  }))
		.on("dblclick", function() {
			projection2.rotate([ lon_central(), -(WNES1+WNES3)/2 +10 ]);
			svg.selectAll("path").attr("d", path);
		});

/* SVG background *************************************** */
// Blue circle
	svg.append("circle")
		.attr("class", "water")
		.attr("cx", width/2)
		.attr("cy", height/2)
		.attr("r", width/2 )
		.style({'fill':'#C6ECFF'})
		.style({'stroke': '656565', 'stroke-width': 1.5});
	// Gradiant	settings
	var gradient = svg.append("svg:defs")
		.append("svg:linearGradient")
		.attr("id", "gradient")
		.attr("x1", "0%")
		.attr("y1", "0%")
		.attr("fx1", "30%")
		.attr("fy1", "30%")
		.attr("x2", "100%")
		.attr("y2", "100%")
		.attr("spreadMethod", "pad");
	gradient.append("svg:stop") 		// middle step setting
		.attr("offset", "50%")
		.attr("stop-color", "#FFF")
		.attr("stop-opacity", 0.3);
	gradient.append("svg:stop") 		// final step setting
		.attr("offset", "100%")
		.attr("stop-color", "#009")
		.attr("stop-opacity", 0.3);
	// Gradiant-circle
	var circle = svg.append('circle') 	// append gradient to circle
		.attr('cx', width / 2)
		.attr('cy', height / 2)
		.attr('r', width/2 )
		.attr('fill', 'url(#gradient)');
	
/* ****************************************************** */
/* GIS data injection *********************************** */
d3.json("world-110m-ids.json", function(error, world) {
/**/  var countries = topojson.feature(world, world.objects.countries).features,
/**/      i = -1,
/**/      n = countries.length;
	
	var country = svg.selectAll(".country")
		.data(topojson.feature(world, world.objects.countries).features)
	.enter().append("path") 
		.attr("id", function(d){ return d.id } )
		.attr("class", "country")
		.style("fill", "#FDFBEA")
		.attr("d", path);
	
	var	focus = d3.selectAll("#"+title)
			.style("fill", "#B10000"); 
	
	var boundaries = svg.append("path")
        //.datum( topojson.mesh(world, world.objects.countries, function(a,b) { if (a!==b){var ret = b;}return ret;}))
		.datum( topojson.mesh(world, world.objects.countries, function(a,b) { return a!==b; }))
		.attr("class", "boundary")
		.attr("d", path)
		.style({'fill':'none','stroke': '#656565', 'stroke-width': 0.5});
	
	var graticule = svg.append("path")
		.datum(d3.geo.graticule().step([20,20]))
		.attr("class", "graticule")
		.attr("d", path)
		.style({'fill':'none', 'stroke':'#777', 'stroke-width': 0.5, 'stroke-opacity': 0.5});

	var coast = svg.append("path")
        //.datum( topojson.mesh(world, world.objects.countries, function(a,b) { if (a==b){var ret = b;}return ret;}))
		.datum( topojson.mesh(world, world.objects.countries, function(a,b) { return a==b; }))
        .attr("class", "Coast_border")
		.style({'fill': 'none', 'stroke': '#0978AB', 'stroke-linejoin': 'round'})
		.style({'stroke-width': 0.5 })
		.attr("d", path);
	
	/* Red graniticule drawing
	svg.append("path")
		.attr("d", path(d3.geo.graticule()
		.majorExtent([[WNES0, WNES3], [WNES2, WNES1]]).outline()))
		.style({'fill': '#B10000', 'fill-opacity': 0.3, 'stroke': '#B10000', 'stroke-linejoin': 'round'})
		.style({'stroke-width': 1 }); /**/
	
	//* Red polygon drawing
	var redwindow = svg.append("path")
		.datum({type: "Polygon", coordinates: [ //LineString
			[[WNES0,WNES3]]
			  .concat(parallel(WNES1, WNES0, WNES2))
			  .concat(parallel(WNES3, WNES0, WNES2).reverse())
			]})
			.style({'fill': '#B10000', 'fill-opacity': 0.3, 'stroke': '#B10000', 'stroke-linejoin': 'round'})
			.style({'stroke-width': 1 })
			.attr("d", path); /**/
	
	var label = svg.append("text")
		.attr("x", width / 2)
		.attr("text-anchor","middle")
		.text(title)
		.attr("y", height * 57/100 );



var step =  function() {
	  if (++i >= n) { i = 0} ;	
	label.text(countries[i].id);
	svg.transition();
    country.transition()
        .style("fill", function(d, j) { return j === i ? "#B10000" : "#FDFBEA";  });

 var centroid = d3.geo.path()
    .projection(function(d) { return d; })
    .centroid;
var area = d3.geo.path()
    .projection(function(d) { return d; })
    .area;
var bounds = d3.geo.path()
    .projection(function(d) { return d; })
    .bounds;
	  
	  
    d3.transition()
        .delay(250)
        .duration(1250)
        .tween("rotate", function() {
          var point = centroid(countries[i]);
		  var surface  = area(countries[i]);
		  var bb = bounds(countries[i]);
		  console.log("area: "+surface+"; bb: "+ JSON.stringify(bb) );
          return function(t) {
            projection2.rotate([-point[0], -point[1]+10]); // area of interest slide 10⁰ up
            country.attr("d", path);
            boundaries.attr("d", path);
            graticule.attr("d", path);
            coast.attr("d", path);
			// draw polygon (red frame) bigger than bb:
			if (surface <15) { // is visible
				redwindow.datum({type: "Polygon", coordinates: [ //LineString
					[[bb[0][0]-5,bb[0][1]-5]]
					  .concat(parallel(bb[1][1]+5, bb[0][0]-5, bb[1][0]+5))
					  .concat(parallel(bb[0][1]-5, bb[0][0]-5, bb[1][0]+5).reverse())
				  ]})
				.style({'fill': '#B10000', "opacity": 1, 'fill-opacity': 0.3})
				.style({'stroke-width': 1, 'stroke-opacity': 1, 'stroke': '#B10000', 'stroke-linejoin': 'round' })
				.attr("d", path); /**/
			 } else if (surface >15 ) { // isn't visible
				 redwindow.style({'opacity': 0}) 
			 }
		  
		  };
        })
      .transition() // runs transition
	.each("end", function(){ console.log(countries[i].id); return step(); } );
  }
step();

});

};

</script>
<script>
var WNES = { "item":"India", "W": 67.0, "N":37.5, "E": 99.0, "S": 5.0 };
localisator("body",500, WNES.item, WNES.W, WNES.N, WNES.E, WNES.S);
</script>