block by hugolpz 6391065

Geographic Bounding Boxes

Full Screen

A geographic bounding box for each country from Natural Earth’s 1:110m Cultural Vectors (Admin 0 - Countries).

A minimum bounding box in geographic coordinates is an area defined by minimum and maximum longitudes and latitudes.

# d3.geo.bounds(feature) -- (source)

Returns the spherical bounding box for the specified feature. The bounding box is represented by a two-dimensional array: [​[left, bottom], [right, top]​], where left is the minimum longitude, bottom is the minimum latitude, right is maximum longitude, and top is the maximum latitude.

Data: see console of full windows view, with data such :
{"W":97.3717371737174; "S":5.688035433543348; "E":105.58055805580562; "N":20.414615115511538; "item":"Thailand"},

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<title>Geographic Bounding Boxes</title>
<style>
/* #map svg {
  cursor: move;
} */
path {
  fill: none;
  stroke: #000;
}


.frame, .land {
  stroke-width: 1px;
}
.frame:hover {
  stroke-width: 2px;
}
.example path.Polygon {
  fill: url(#hatch);
}
.example text {
  font-size: 11px;
  font-family: sans-serif;
}
</style>

<p class="breadcrumbs">Forked from: <a href="//www.jasondavies.com/">Jason Davies</a><a href="//www.jasondavies.com/maps/">Maps</a><a href="//www.jasondavies.com/maps/bounds/">Bounds</a></p>

<h1>Geographic Bounding Boxes</h1>
<div id="map"></div>

<script src="//code.jquery.com/jquery-2.0.2.min.js"></script>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script src="../js/jquery-2.0.1.min.js"></script>
<script src="../js/d3.v3.min.js"></script>
<script src="../js/topojson.v1.min.js"></script>
<script src="../js/wikiatlas.js"></script>

<script>

var width = 600,
    height = width;

var projection = d3.geo.orthographic()
    .translate([width / 2, height / 2])
    .scale(300)
    .clipAngle(90)
    .precision(.1)
    .rotate([0, -25]);
    // There is a clipping bug, fixed in branch geo-clip-good
    //.rotate([-103.5, -20, 0]);

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

var svg = d3.select("#map").append("svg")
    .attr("width", width)
    .attr("height", height)
    .call(d3.behavior.drag()
      .origin(function() { var rotate = projection.rotate(); return {x: 2 * rotate[0], y: -2 * rotate[1]}; })
      .on("drag", function() {
        projection.rotate([d3.event.x / 2, -d3.event.y / 2, projection.rotate()[2]]);
        svg.selectAll("path").attr("d", path)
		svg.selectAll("text")
			.attr("x",    function(d) { return -90<d3.geo.centroid(d)[0]<90?projection(d3.geo.centroid(d))[0]:null; })
			.attr("y",    function(d) { return -90<d3.geo.centroid(d)[1]<90?projection(d3.geo.centroid(d))[1]:null; });
      }));

/* ************************************************* */
/* Death code? ************************************* */
var hatch = svg.append("defs").append("pattern")
    .attr("id", "hatch")
    .attr("patternUnits", "userSpaceOnUse")
    .attr("width", 8)
    .attr("height", 8)
  .append("g");
hatch.append("path").attr("d", "M0,0L8,8");
hatch.append("path").attr("d", "M8,0L0,8");


/* ************************************************* */

// FUNCTION: Get data and process!
d3.json("world-110m-sp.06.json", function(error, json) {
var admin_0   = topojson.feature(json, json.objects.admin_0),
	L0_border = topojson.mesh(json, json.objects.admin_0, function(a,b) { return a!==b;}),
	admin_1   = topojson.feature(json, json.objects.admin_1),
	L1_border = topojson.mesh(json, json.objects.admin_1, function(a,b) { return a!==b;}),
	coast     = topojson.mesh(json, json.objects.admin_0, function(a,b) { return a===b;});

/* **************************************************************** */
var water = svg.append("path")
    .datum({type: "Sphere"})
    	.attr("class", "water")
		.style({'fill':'#C6ECFF'})
		.style({'stroke': '656565', 'stroke-width': 1.5})
		.attr("d", path);
var earthGradient = 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");
  earthGradient.append("svg:stop")
    .attr("offset", "50%")
    .attr("stop-color", "#FFF")
    .attr("stop-opacity", 0.3);
  earthGradient.append("svg:stop")
    .attr("offset", "100%")
    .attr("stop-color", "#009")
    .attr("stop-opacity", 0.2);
 var earthShadow = svg.append('circle')	
	.attr('cx', width / 2)
	.attr('cy', height / 2)
	.attr('r', width/2 )
	.attr('fill', 'url(#gradient)');
	
/* **************************************************************** */
/* **************************************************************** */
var L0 = svg.append("path")
      .attr("class", "")
      .datum(admin_0)
	.style({'fill':'#FDFBEA','stroke':'none'})
      .attr("d", path);
	
var L1_border = svg.append("path")
		.datum(L1_border)
		.attr("class", "")
		.style({'fill':'none','stroke': '#999', 'stroke-width': .5})
		.attr("d", path);
var L0_border = svg.append("path")
		.datum(L0_border)
		.attr("class", "")
		.style({'fill':'none','stroke': '#666', 'stroke-width': 1})
		.attr("d", path);		
var grid = svg.append("path")
		.datum(d3.geo.graticule().step([20,20]))
		.attr("class", "")
		.style({'fill':'none', 'stroke':'#777', 'stroke-width': 0.5, 'stroke-opacity': 0.5})
		.attr("d", path);
	
var antimeridian = svg.append("path")
		.datum({type: "LineString", coordinates: [[180, -90], [180, 0], [180, 90]]})
		.attr("id", "")
		.style({'fill':'none','stroke': '#777', 'stroke-width': 1, 'stroke-dasharray': '5 5'})
		.attr("d", path);
	
var coast = svg.append("path")
        .datum( coast )
        .attr("d", path)
        .attr("class", "")
		.style({'fill': 'none', 'stroke': '#0978AB', 'stroke-linejoin': 'round'})
		.style({'stroke-width': 0.5 })
	

  var frames = svg.selectAll(".frame")
      .data(admin_0.features)
    .enter().append("g").attr('id','frame');
  frames.append("path")
      .datum(boundsPolygon())
  		.attr("class","frame")
		.style({'fill': '#94BF8B', 'fill-opacity': 0.25})
		.style({'stroke': '#94BF8B', 'stroke-width': 1, 'stroke-linejoin': 'round' })
      .attr("d", path);
  
var centros = svg.selectAll(".centros")
      .data(admin_0.features)
    .enter().append("g").attr('id','centros');
	
centros
  .append("text")
  .attr("style", "stroke-width:3,stroke:#F00")
  .attr("class","")
	.attr("x",    function(d) { return projection(d3.geo.centroid(d))[0] })
	.attr("y",    function(d) { return projection(d3.geo.centroid(d))[1] })
	.text("⭑") // ⬤◉⍟☉⚪⚫●⚬◯★☆☆⭐ ⭑ ⭒	
;
/* 	
  centros.append("path")
      .datum( {type: "Point",coordinates: [ d3.geo.centroid[0],d3.geo.centroid[1]]}})
      .attr("d", path)
		.style({'fill': '#94BF8B', 'fill-opacity': 0.25})
		.style({'stroke': '#94BF8B', 'stroke-width': 1, 'stroke-linejoin': 'round' });
		
    var centros = svg.append("g")
	.selectAll("path")
        .data(admin_0.features)
      .enter().append("text")
		.attr("x",    function(d) { return path.centroid(d)[0] })
		.attr("y",    function(d) { return path.centroid(d)[1] })
		.text("⭑") // ⬤◉⍟☉⚪⚫●⚬◯★☆☆⭐ ⭑ ⭒
		.style({'fill':'#333;'});
*/

});

function boundsPolygon() {
  return function(geometry) {
    var bb = d3.geo.bounds(geometry);
  // START WNES JSON GENERATOR ! #########################################################################################
    var WNES = {}; // MUST declare object var ... = {};
      WNES.iso2 = geometry.properties.L0,
      WNES.iso3 = geometry.properties.L0_3,
      WNES.name = geometry.properties.name,
	  WNES.area  = d3.geo.area(geometry);
	// fix NaturalEarth data
	  WNES.iso3==="CYN"?WNES.iso2="X1":"";
	  WNES.iso3==="KAZ"&&WNES.iso2==="-99"?WNES.iso2="X2":"";
	  WNES.iso3==="KAS"?WNES.iso2="X3":"";
	  WNES.iso3==="KOS"?WNES.iso2="X4":"";
	  WNES.iso3==="SOL"?WNES.iso2="X5":"";
	  WNES.iso2==="X2"?WNES.name="Baikonour":"";
	  WNES.iso2==="NC"?WNES.name="New_Caledonia":"";
	  WNES.iso2==="TF"?WNES.name="Kerguelen_Islands":"";
	  WNES.iso2==="GL"?WNES.name="Greenland":"";
	  WNES.iso2==="PR"?WNES.name="Puerto_Rico":"";
	  WNES.iso2==="FK"?WNES.name="Falkland_Islands":""; 
	  WNES.iso2==="PS"?WNES.name="Palestine":""; 
	  WNES.iso2==="GS"?WNES.name="South_Georgia":"";
	  // Area north of 60⁰ to stay on etopo:
	  WNES.source = function(){ return WNES.area>0.015?"etopo":"srtm";};
	  bb[1][1]>60||bb[0][1]<60?WNES.source="etopo":"";
	  WNES.iso2==="CA"?WNES.source:"etopo";
	  WNES.iso2==="GL"?WNES.source:"etopo";
	  WNES.iso2==="IS"?WNES.source:"etopo";
	  WNES.iso2==="NO"?WNES.source:"etopo";
	  WNES.iso2==="SE"?WNES.source:"etopo";
	  WNES.iso2==="FI"?WNES.source:"etopo";
	 // WNES.iso2==="RU"?WNES.area=0:"";
	  //WNES.iso2==="GB"&&WNES.name="United_Kingdom"?WNES.iso2="UK":"";
	  
	  // borders' geo-coordinates (decimal degrees)
      WNES.W = bb[0][0], // Note: D3js is WSEN based.
      WNES.N = bb[1][1],
      WNES.E = bb[1][0],
      WNES.S = bb[0][1],
      // frame's geo-dimensions (decimal degrees)
      WNES.geo_width = (WNES.E - WNES.W), 
      WNES.geo_height= (WNES.N - WNES.S), 
      // center geo-coordinates
      WNES.lat_center = (WNES.S + WNES.N)/2, 
      WNES.lon_center = (WNES.W + WNES.E)/2;
	  // WNES.source= function(){ return WNES.area > 0.015? "large":"small" }();
    // add a 5% padding on all WNES sides
    var WNESplus = {};
      WNESplus.W = WNES.W - WNES.geo_width  * 0.05, 
      WNESplus.N = WNES.N + WNES.geo_height * 0.05,
      WNESplus.E = WNES.E + WNES.geo_width  * 0.05,  
      WNESplus.S = WNES.S - WNES.geo_height * 0.05;
      /* frame+paddings' (decimal degrees)
     var WNESplus.geo_width = (WNESplus.E - WNESplus.W),
      WNESplus.geo_height= (WNESplus.N - WNESplus.S) */;

    //Degree of precision to keep 4 meaningful digits, ie: 048⁰75 OR 0⁰05'44"6.
    var digits, geo_side_max = Math.abs( Math.max( WNES.geo_width, WNES.geo_height) );
    if      ( geo_side_max < 1000 && geo_side_max >= 100   ) { digits = 1; }
    else if ( geo_side_max < 100  && geo_side_max >= 10    ) { digits = 2; }
    else if ( geo_side_max < 10   && geo_side_max >= 1     ) { digits = 3; }
    else if ( geo_side_max < 1    && geo_side_max >= 0.1   ) { digits = 4; }
    else if ( geo_side_max < 0.1  && geo_side_max >= 0.01  ) { digits = 5; }
    else if ( geo_side_max < 0.01 && geo_side_max >= 0.001 ) { digits = 6; };

//PRINT RESULTS (4 meaningful digits): 
//  console.log('"WNES" : { "id":"'+ WNES.id +'", "name":"'+ WNES.name +'", "W":' +  WNES.W.toFixed(digits)    +',"N":' + WNES.N.toFixed(digits)     +',"E":' + WNES.E.toFixed(digits)     +',"S":' + WNES.S.toFixed(digits)     +'}');
//  console.log('"WNES+": { "id":"'+ WNES.id +'", "name":"'+ WNES.name +'", "W+":'+ WNESplus.W.toFixed(digits) +',"N+":'+ WNESplus.N.toFixed(digits) +',"E+":'+ WNESplus.E.toFixed(digits) +',"S+":'+ WNESplus.S.toFixed(digits) +'}');
    console.log('make -f master.makefile ISO2='+ WNES.iso2 +' SOV3='+ WNES.iso3 +' NAME='+ WNES.name.replace(/ /gi,"_") +' WEST='+ WNESplus.W.toFixed(digits) +' SOUTH='+ WNESplus.S.toFixed(digits) +' EAST='+ WNESplus.E.toFixed(digits) +' NORTH='+ WNESplus.N.toFixed(digits) + ' AREA_SIZE='+WNES.area.toFixed(4));
// END WNES JSON #########################################################################################################
	
	// For all Earth::
    if (bb[0][0] === -180 && bb[0][1] === -90 && bb[1][0] === 180 && bb[1][1] === 90) {
     return {type: "Sphere"};
    }
	// For
    if (bb[0][1] === -90) bb[0][1] += 1e-6;
    if (bb[1][1] === 90) bb[0][1] -= 1e-6;
    if (bb[0][1] === bb[1][1]) bb[1][1] += 1e-6; 
		  
    return {
      type: "Polygon",
      coordinates: [
        [bb[0]]
          .concat(parallel(bb[1][1], bb[0][0], bb[1][0]))
          .concat(parallel(bb[0][1], bb[0][0], bb[1][0]).reverse())
      ]
    };
  };
}

function parallel(φ, λ0, λ1) {
  if0 > λ1) λ1 += 360;
  var dλ = λ1 - λ0,
      step = dλ / Math.ceil(dλ);
  return d3.range(λ0, λ1 + .5 * step, step).map(function(λ) { return [normalise(λ), φ]; });
}

function normalise(x) {
  return (x + 180) % 360 - 180;
}
</script>

gist.txt

git@gist.github.com:/6391065.git

topojson.v1.min.js

topojson=function(){function t(t,e){function n(e){var n=t.arcs[e],r=n[0],o=[0,0];return n.forEach(function(t){o[0]+=t[0],o[1]+=t[1]}),[r,o]}var r={},o={},a={};e.forEach(function(t){var e=n(t);(r[e[0]]||(r[e[0]]=[])).push(t),(r[e[1]]||(r[e[1]]=[])).push(~t)}),e.forEach(function(t){var e,r,i=n(t),u=i[0],c=i[1];if(e=a[u])if(delete a[e.end],e.push(t),e.end=c,r=o[c]){delete o[r.start];var s=r===e?e:e.concat(r);o[s.start=e.start]=a[s.end=r.end]=s}else if(r=a[c]){delete o[r.start],delete a[r.end];var s=e.concat(r.map(function(t){return~t}).reverse());o[s.start=e.start]=a[s.end=r.start]=s}else o[e.start]=a[e.end]=e;else if(e=o[c])if(delete o[e.start],e.unshift(t),e.start=u,r=a[u]){delete a[r.end];var f=r===e?e:r.concat(e);o[f.start=r.start]=a[f.end=e.end]=f}else if(r=o[u]){delete o[r.start],delete a[r.end];var f=r.map(function(t){return~t}).reverse().concat(e);o[f.start=r.end]=a[f.end=e.end]=f}else o[e.start]=a[e.end]=e;else if(e=o[u])if(delete o[e.start],e.unshift(~t),e.start=c,r=a[c]){delete a[r.end];var f=r===e?e:r.concat(e);o[f.start=r.start]=a[f.end=e.end]=f}else if(r=o[c]){delete o[r.start],delete a[r.end];var f=r.map(function(t){return~t}).reverse().concat(e);o[f.start=r.end]=a[f.end=e.end]=f}else o[e.start]=a[e.end]=e;else if(e=a[c])if(delete a[e.end],e.push(~t),e.end=u,r=a[u]){delete o[r.start];var s=r===e?e:e.concat(r);o[s.start=e.start]=a[s.end=r.end]=s}else if(r=o[u]){delete o[r.start],delete a[r.end];var s=e.concat(r.map(function(t){return~t}).reverse());o[s.start=e.start]=a[s.end=r.start]=s}else o[e.start]=a[e.end]=e;else e=[t],o[e.start=u]=a[e.end=c]=e});var i=[];for(var u in a)i.push(a[u]);return i}function e(e,n,r){function a(t){0>t&&(t=~t),(l[t]||(l[t]=[])).push(f)}function i(t){t.forEach(a)}function u(t){t.forEach(i)}function c(t){"GeometryCollection"===t.type?t.geometries.forEach(c):t.type in d&&(f=t,d[t.type](t.arcs))}var s=[];if(arguments.length>1){var f,l=[],d={LineString:i,MultiLineString:u,Polygon:u,MultiPolygon:function(t){t.forEach(u)}};c(n),l.forEach(arguments.length<3?function(t,e){s.push(e)}:function(t,e){r(t[0],t[t.length-1])&&s.push(e)})}else for(var p=0,h=e.arcs.length;h>p;++p)s.push(p);return o(e,{type:"MultiLineString",arcs:t(e,s)})}function n(t,e){return"GeometryCollection"===e.type?{type:"FeatureCollection",features:e.geometries.map(function(e){return r(t,e)})}:r(t,e)}function r(t,e){var n={type:"Feature",id:e.id,properties:e.properties||{},geometry:o(t,e)};return null==e.id&&delete n.id,n}function o(t,e){function n(t,e){e.length&&e.pop();for(var n,r=h[0>t?~t:t],o=0,i=r.length,u=0,c=0;i>o;++o)e.push([(u+=(n=r[o])[0])*f+d,(c+=n[1])*l+p]);0>t&&a(e,i)}function r(t){return[t[0]*f+d,t[1]*l+p]}function o(t){for(var e=[],r=0,o=t.length;o>r;++r)n(t[r],e);return e.length<2&&e.push(e[0]),e}function i(t){for(var e=o(t);e.length<4;)e.push(e[0]);return e}function u(t){return t.map(i)}function c(t){var e=t.type;return"GeometryCollection"===e?{type:e,geometries:t.geometries.map(c)}:e in v?{type:e,coordinates:v[e](t)}:null}var s=t.transform,f=s.scale[0],l=s.scale[1],d=s.translate[0],p=s.translate[1],h=t.arcs,v={Point:function(t){return r(t.coordinates)},MultiPoint:function(t){return t.coordinates.map(r)},LineString:function(t){return o(t.arcs)},MultiLineString:function(t){return t.arcs.map(o)},Polygon:function(t){return u(t.arcs)},MultiPolygon:function(t){return t.arcs.map(u)}};return c(e)}function a(t,e){for(var n,r=t.length,o=r-e;o<--r;)n=t[o],t[o++]=t[r],t[r]=n}function i(t,e){for(var n=0,r=t.length;r>n;){var o=n+r>>>1;t[o]<e?n=o+1:r=o}return n}function u(t){function e(t,e){t.forEach(function(t){0>t&&(t=~t);var n=o[t];n?n.push(e):o[t]=[e]})}function n(t,n){t.forEach(function(t){e(t,n)})}function r(t,e){"GeometryCollection"===t.type?t.geometries.forEach(function(t){r(t,e)}):t.type in u&&u[t.type](t.arcs,e)}var o={},a=t.map(function(){return[]}),u={LineString:e,MultiLineString:n,Polygon:n,MultiPolygon:function(t,e){t.forEach(function(t){n(t,e)})}};t.forEach(r);for(var c in o)for(var s=o[c],f=s.length,l=0;f>l;++l)for(var d=l+1;f>d;++d){var p,h=s[l],v=s[d];(p=a[h])[c=i(p,v)]!==v&&p.splice(c,0,v),(p=a[v])[c=i(p,h)]!==h&&p.splice(c,0,h)}return a}return{version:"1.1.4",mesh:e,feature:n,neighbors:u}}();