L'équateur de Marseille s'appuie sur la Canebière et la prolonge dans les deux directions, jusqu'à faire le tour du monde.
Cette approche a été conçue et développée par Jean-Luc Arnaud dans « Marseille et la Canebière - Echelles du monde », 2017. <halshs-01484193>
Visualisation interactive par Philippe Rivière -- visionscarto.net
Forked from mbostock's block: Versor Dragging II
forked from Fil's block: L’Équateur de Marseille
forked from 007Carto's block: Equateur MMSH
xxxxxxxxxx
<html>
<head>
<style>
svg { display: none }
#download { display: none; font-size: small; }
#download { text-decoration: none; color: black; }
body.svg svg { display: block }
body.svg #download { display: inline-block }
body.svg canvas { display: none }
</style>
</head>
<body class="svg">
<div id="svgwrap"> <svg version="1.1"
xmlns="https://www.w3.org/2000/svg"
xmlns:xlink="https://www.w3.org/1999/xlink"
xmlns:inkscape="https://www.inkscape.org/namespaces/inkscape"
width="960" height="600">
</svg></div>
<canvas width="960" height="600"></canvas>
<select style="position: absolute; top: 0; left: 0"></select>
<label style="position: absolute; top: 1.4em; left: 0"><input name="svgcanvas" type="radio" checked="checked">SVG <a id="download" href="#">[download]</a></label>
<label style="position: absolute; top: 2.4em; left: 0"><input name="svgcanvas" type="radio">canvas</label>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script src="versor.js"></script>
<!--
<script src="d3.v4.min.js"></script>
<script src="d3-geo-projection.js"></script>
<script src="topojson.v2.min.js"></script>
<script src="versor.js"></script>
<script src="geoinverseNewton.js"></script>
-->
<script>
d3.selectAll('input').on('change', () => {
d3.select('body').classed('svg', !d3.select('body').classed('svg'));
render();
})
d3.select('#download').on('click', function(){
var b64 = btoa(d3.select("#svgwrap").html());
d3.select('#download')
.attr('href', "data:image/svg+xml;base64,\n" + b64)
.attr('download', "map.svg")
.dispatch('click')
})
var menu = d3.select('select')
.on('change', () => {
var sel = menu.node().parentElement;
projection = d3['geo' + sel.options[sel.selectedIndex].value]()
.precision(0.1)
.rotate([-canebiere[0], -canebiere[1], -angle]);
render();
})
.selectAll('option')
.data([ 'Orthographic', 'Homolosine', 'Mollweide', 'Fahey', 'Sinusoidal', 'Miller', 'InterruptedMollweideHemispheres', 'Equirectangular', 'Mercator', 'Lagrange', 'Larrivee', ])
.enter()
.append('option')
.attr('value', d => d)
.text(d => d);
// import from math
var sin = Math.sin, cos = Math.cos, pi = Math.PI;
var degrees = 180 / pi;
// retina display
var canvas = d3.select("canvas"),
svg = d3.select("svg"),
width = canvas.property("width"),
height = canvas.property("height"),
context = canvas.node().getContext("2d");
var devicePixelRatio = window.devicePixelRatio || 1;
canvas.attr('width', width * devicePixelRatio);
canvas.attr('height', height * devicePixelRatio);
canvas.style('width', width + 'px');
canvas.style('height', height + 'px');
context.setTransform(devicePixelRatio,0,0,devicePixelRatio,0,0)
// modif des paramètres de rotation
//canebiere = [ 5.3796269, 43.2971309 ];
//angle = 25 + 44/60; // 25°44'
//paramètres de rotation pour MMSH
canebiere = [ 5.4166, 43.5166 ]
angle = 48.37
antipode = [canebiere[0]+180, -canebiere[1]]
projection = d3.geoOrthographic()
.precision(0.1)
.rotate([-canebiere[0], -canebiere[1], -angle]);
// rotator = envoie Marseille vers le [0,0] de notre référentiel
// rotator.invert = envoie [0,0] vers Marseille
rotator = d3.geoRotation([-canebiere[0], -canebiere[1], -angle]);
equateur = d3.geoGraticule()
.extentMajor([[-180,-10], [180,10]])
.extentMinor([[-180,-5], [180,5]])
()
equateur.coordinates = equateur.coordinates.slice(4,5).map(d => d.map(rotator.invert))
var graticule = d3.geoGraticule()();
d3.json("https://unpkg.com/visionscarto-world-atlas@0.0.4/world/110m_countries.geojson", function(error, world) {
if (error) throw error;
console.log(world)
//var land = topojson.feature(world, world.objects["-"]);
var land = world;
var defs = svg.append('defs');
defs.append("clipPath")
.attr("id", "clip")
.append("use")
.attr("xlink:href", "#sphere");
defs
.append('path')
.datum({type: "Sphere"})
.attr('id', 'sphere');
svg.append('path')
.datum(land)
.attr('fill', 'black')
.attr('stroke', 'white')
.attr('stroke-width', 1)
.style('clip-path', 'url(#clip)');
svg.append('path')
.datum(graticule)
.attr('fill', 'none')
.attr('stroke-width', 0.5)
.attr('stroke', 'grey')
.style('clip-path', 'url(#clip)');
svg.append('path')
.datum(equateur)
.attr('fill', 'none')
.attr('stroke', 'blue')
.attr('stroke-width', 3)
.style('clip-path', 'url(#clip)');
svg.append('path')
.datum({type: "Sphere"})
.attr('fill', 'none')
.attr('stroke', 'black');
console.log([[0,0], [90,0], [180,0], [-90,0],[0,90],[0,-90]].map(rotator.invert))
svg.append('path')
.datum({
type:"MultiPoint",
coordinates: [[0,0], [90,0], [180,0], [-90,0],[0,90],[0,-90]].map(rotator.invert)
})
.attr('fill', 'orange')
if (0) d3.timer(e => {
projection.rotate([e/150, -canebiere[1]])
svg.selectAll('path').attr('d', path)
}, 100);
render = function() {
var path = d3.geoPath().projection(projection);
if (d3.select('body').classed('svg')) {
svg.selectAll('path,clipPath').attr('d', path);
return;
}
// Cette zone semble affectée à la définition de l'affichage en mode Canvas
path = path.context(context)
context.save();
(context.fillStyle = "#fff"), context.fillRect(0, 0, width, height);
context.beginPath(), path({type: "Sphere"}), context.closePath(), context.clip();
context.beginPath(), path(graticule), (context.lineWidth = 0,5, context.strokeStyle =
"#878787"), context.stroke(), context.lineWidth = 1, context.closePath();
context.beginPath(), path(land), (context.strokeStyle = context.fillStyle =
"#000"), context.fill (), context.lineWidth = 0.1, context.stroke(), context.lineWidth = 1, context.closePath();
context.beginPath(), path(equateur), (context.lineWidth = 1, context.strokeStyle = "Green", context.fillStyle =
"rgba(0,0,0,0)"), context.fill(), context.stroke(), context.closePath(), context.lineWidth = 3;
context.beginPath(), context.lineWidth = 2, path({type: "Sphere"}), (context.strokeStyle =
"black"), context.stroke(), context.closePath();
context.restore();
};
render();
});
canvas.call(d3.drag().on("start", dragstarted).on("drag", dragged));
svg.call(d3.drag().on("start", dragstarted).on("drag", dragged));
var render = function() {},
v0, // Mouse position in Cartesian coordinates at start of drag gesture.
r0, // Projection rotation as Euler angles at start.
q0; // Projection rotation as versor at start.
function dragstarted() {
v0 = versor.cartesian(projection.invert(d3.mouse(this)));
r0 = projection.rotate();
q0 = versor(r0);
}
function dragged() {
var inv = projection.rotate(r0).invert(d3.mouse(this));
if (isNaN(inv[0])) return;
var v1 = versor.cartesian(inv),
q1 = versor.multiply(q0, versor.delta(v0, v1)),
r1 = versor.rotation(q1);
projection.rotate(r1);
render();
}
</script>
</body>
</html>
https://d3js.org/d3.v4.min.js
https://d3js.org/d3-geo-projection.v2.js
https://d3js.org/topojson.v2.min.js