|
// Generated by CoffeeScript 2.0.1 |
|
(function() { |
|
var TH_1, TH_2, base_layer, boundaries_layer, draw_labels, height, in_viewport, init, italy, labels_layer, lod, path, projection, region_capitals, svg, to_bounding_box, transform, width, zoom, zoomable_layer, |
|
indexOf = [].indexOf; |
|
|
|
italy = void 0; |
|
|
|
region_capitals = ["Aosta", "Torino", "Genova", "Milano", "Trento", "Trieste", "Venezia", "Bologna", "Firenze", "Ancona", "Perugia", "Roma", "L'Aquila", "Campobasso", "Napoli", "Potenza", "Bari", "Catanzaro", "Palermo", "Cagliari"]; |
|
|
|
TH_1 = 5; |
|
|
|
TH_2 = 25; |
|
|
|
// SVG group structure |
|
svg = d3.select('svg'); |
|
|
|
width = svg.node().getBoundingClientRect().width; |
|
|
|
height = svg.node().getBoundingClientRect().height; |
|
|
|
zoomable_layer = svg.append('g'); |
|
|
|
base_layer = zoomable_layer.append('g'); |
|
|
|
boundaries_layer = zoomable_layer.append('g'); |
|
|
|
labels_layer = zoomable_layer.append('g'); |
|
|
|
// Zoom behaviour |
|
zoom = d3.zoom().scaleExtent([1, 2e308]).on('zoom', function() { |
|
// Update level of details |
|
lod(d3.event.transform); |
|
// Transform SVG |
|
return zoomable_layer.attrs({ |
|
transform: d3.event.transform |
|
}); |
|
}); |
|
|
|
svg.call(zoom); |
|
|
|
// Geographic projection |
|
projection = d3.geoAzimuthalEqualArea().clipAngle(180 - 1e-3).scale(3000).rotate([-12.22, -42, 0]).translate([width / 2, height / 2]).precision(0.1); |
|
|
|
path = d3.geoPath().projection(projection); |
|
|
|
// Returns a transform for center a bounding box in the browser viewport |
|
// - W and H are the witdh and height of the window |
|
// - w and h are the witdh and height of the bounding box |
|
// - center cointains the coordinates of the bounding box center |
|
// - margin defines the margin of the bounding box once zoomed |
|
to_bounding_box = function(W, H, center, w, h, margin) { |
|
var k, kh, kw, x, y; |
|
kw = (W - margin) / w; |
|
kh = (H - margin) / h; |
|
k = d3.min([kw, kh]); |
|
x = W / 2 - center.x * k; |
|
y = H / 2 - center.y * k; |
|
return d3.zoomIdentity.translate(x, y).scale(k); |
|
}; |
|
|
|
// Filters data outside the current viewport |
|
in_viewport = function(d, x0, x1, y0, y1) { |
|
return d.properties.bbox.x0 < x1 && d.properties.bbox.x1 > x0 && d.properties.bbox.y0 < y1 && d.properties.bbox.y1 > y0; |
|
}; |
|
|
|
// Prepares data for being visualized |
|
transform = function(data) { |
|
var key, obj, provinces, ref, regions, towns; |
|
regions = { |
|
type: 'GeometryCollection', |
|
geometries: data.objects.final.geometries.filter(function(d) { |
|
return d.properties.REGIONE != null; |
|
}) |
|
}; |
|
provinces = { |
|
type: 'GeometryCollection', |
|
geometries: data.objects.final.geometries.filter(function(d) { |
|
return d.properties.PROVINCIA != null; |
|
}) |
|
}; |
|
towns = { |
|
type: 'GeometryCollection', |
|
geometries: data.objects.final.geometries.filter(function(d) { |
|
return d.properties.COMUNE != null; |
|
}) |
|
}; |
|
data.objects = { |
|
country: data.objects.country, |
|
regions: regions, |
|
provinces: provinces, |
|
towns: towns |
|
}; |
|
ref = data.objects; |
|
for (key in ref) { |
|
obj = ref[key]; |
|
topojson.feature(data, obj).features.forEach(function(d, i) { |
|
var bounds, ref1; |
|
bounds = path.bounds(d); |
|
if (d.properties.COMUNE != null) { |
|
d.properties.label = d.properties.COMUNE; |
|
} |
|
if (d.properties.PROVINCIA != null) { |
|
if (d.properties.PROVINCIA === '-') { |
|
d.properties.label = d.properties.DEN_CMPRO; |
|
} else { |
|
d.properties.label = d.properties.PROVINCIA; |
|
} |
|
} |
|
if (d.properties.REGIONE != null) { |
|
d.properties.label = d.properties.REGIONE; |
|
} |
|
d.properties.capital = (ref1 = d.properties.label, indexOf.call(region_capitals, ref1) >= 0); |
|
if (data.objects[key].geometries[i].properties != null) { |
|
data.objects[key].geometries[i].properties.area = d3.geoArea(d); |
|
return data.objects[key].geometries[i].properties.bbox = { |
|
x0: bounds[0][0], |
|
y0: bounds[0][1], |
|
x1: bounds[1][0], |
|
y1: bounds[1][1] |
|
}; |
|
} |
|
}); |
|
} |
|
return data; |
|
}; |
|
|
|
draw_labels = function(data, cls, k) { |
|
var all_labels, en_labels, labels; |
|
labels = labels_layer.selectAll(`.${cls}`).data(data, function(d) { |
|
return d.properties.label; |
|
}); |
|
en_labels = labels.enter().append('text').attrs({ |
|
class: cls |
|
}); |
|
all_labels = en_labels.merge(labels); |
|
all_labels.attrs({ |
|
x: function(d) { |
|
return d.properties.bbox.x0 + (d.properties.bbox.x1 - d.properties.bbox.x0) / 2; |
|
}, |
|
y: function(d) { |
|
return d.properties.bbox.y0 + (d.properties.bbox.y1 - d.properties.bbox.y0) / 2; |
|
}, |
|
dy: '0.35em' |
|
}).styles({ |
|
'font-size': function(d) { |
|
if ((d.properties.region_capital != null) && d.properties.region_capital === 1) { |
|
return 18 / k; |
|
} else { |
|
return 14 / k; |
|
} |
|
} |
|
}).classed('capital', function(d) { |
|
return d.properties.capital; |
|
}).on('click', function(d) { |
|
var center, h, w; |
|
w = d.properties.bbox.x1 - d.properties.bbox.x0; |
|
h = d.properties.bbox.y1 - d.properties.bbox.y0; |
|
center = { |
|
x: d.properties.bbox.x0 + w / 2, |
|
y: d.properties.bbox.y0 + h / 2 |
|
}; |
|
transform = to_bounding_box(width, height, center, w, h, height / 10); |
|
return svg.transition().duration(2000).call(zoom.transform, transform); |
|
}).text(function(d) { |
|
return d.properties.label; |
|
}); |
|
return labels.exit().remove(); |
|
}; |
|
|
|
// Level of Details |
|
// filters useless data from the visualization according to the current viewport |
|
lod = function(transform) { |
|
var data, k, x0, x1, y0, y1; |
|
x0 = -transform.x / transform.k; |
|
y0 = -transform.y / transform.k; |
|
x1 = x0 + width / transform.k; |
|
y1 = y0 + height / transform.k; |
|
k = transform.k; |
|
/* Region labels |
|
*/ |
|
data = []; |
|
if (k <= TH_1) { |
|
data = topojson.feature(italy, italy.objects.regions).features.filter(function(d) { |
|
return in_viewport(d, x0, x1, y0, y1) && d.properties.area > Math.pow(0.015 / k, 2); |
|
}); |
|
} |
|
draw_labels(data, 'region_label', k); |
|
/* Provinces labels |
|
*/ |
|
data = []; |
|
if ((TH_2 > k && k > TH_1)) { |
|
data = topojson.feature(italy, italy.objects.provinces).features.filter(function(d) { |
|
return in_viewport(d, x0, x1, y0, y1) && d.properties.area > Math.pow(0.01 / k, 2); |
|
}); |
|
} |
|
draw_labels(data, 'province_label', k); |
|
/* Towns labels |
|
*/ |
|
data = []; |
|
if (k > TH_2) { |
|
data = topojson.feature(italy, italy.objects.towns).features.filter(function(d) { |
|
return in_viewport(d, x0, x1, y0, y1) && d.properties.area > Math.pow(0.025 / k, 2); |
|
}); |
|
} |
|
return draw_labels(data, 'town_label', k); |
|
}; |
|
|
|
// Initialize the layout displaying |
|
// - the base layer of Italy |
|
// - the internal boundaries of regions, provinces and towns |
|
init = async function(zoomable_layer) { |
|
var all_shapes, en_shapes, shapes; |
|
italy || (italy = (await fetch('italy.topo.json').then(function(response) { |
|
return response.json(); |
|
}).then(function(data) { |
|
return transform(data); |
|
}))); |
|
/* Base Layer |
|
*/ |
|
shapes = base_layer.selectAll('.italy').data(topojson.feature(italy, italy.objects.country).features); |
|
en_shapes = shapes.enter().append('g').attrs({ |
|
class: 'italy' |
|
}); |
|
en_shapes.append('path'); |
|
all_shapes = en_shapes.merge(shapes); |
|
all_shapes.select('path').attrs({ |
|
d: path |
|
}); |
|
shapes.exit().remove(); |
|
/* Boundaries Layer |
|
*/ |
|
// Regions |
|
boundaries_layer.append('path').datum(topojson.mesh(italy, italy.objects.regions, function(a, b) { |
|
return a !== b; |
|
})).attr('class', 'boundaries region').attr('d', path); |
|
// Provinces |
|
boundaries_layer.append('path').datum(topojson.mesh(italy, italy.objects.provinces, function(a, b) { |
|
return a !== b && a.properties.COD_REG === b.properties.COD_REG; |
|
})).attr('class', 'boundaries province').attr('d', path); |
|
// Towns |
|
boundaries_layer.append('path').datum(topojson.mesh(italy, italy.objects.towns, function(a, b) { |
|
return a !== b && a.properties.COD_PRO === b.properties.COD_PRO; |
|
})).attr('class', 'boundaries town').attr('d', path); |
|
return lod(d3.zoomIdentity); |
|
}; |
|
|
|
init(); |
|
|
|
}).call(this); |