feat: support diagonal grid and pins on it

This commit is contained in:
Basile Bruneau
2020-05-05 22:06:39 +02:00
parent 5a12e6e231
commit 67ad6c53da
3 changed files with 57 additions and 22 deletions
+2 -1
View File
@@ -6,8 +6,9 @@
const map = new DottedMap({ const map = new DottedMap({
height, height,
width, // (one of both if enough) width, // (one of both if enough)
countries: ['FRA', 'DEU'] // if not present, whole world is used countries: ['FRA', { id: 'DEU', svgOptions: { color: 'blue' } }] // if not present, whole world is used
region: { lat: {min, max}, lng: {min, max} }, // if not present, it fits the countries or the world region: { lat: {min, max}, lng: {min, max} }, // if not present, it fits the countries or the world
grid: 'vertical' | 'diagonal',
}) })
// → it will cache the points array // → it will cache the points array
+50 -17
View File
@@ -9,7 +9,7 @@ const geojsonByCountry = geojsonWorld.features.reduce((countries, feature) => {
}, {}); }, {});
const DEFAULT_WORLD_REGION = { const DEFAULT_WORLD_REGION = {
lat: { min: -65, max: 78 }, lat: { min: -56, max: 71 },
lng: { min: -179, max: 179 }, lng: { min: -179, max: 179 },
}; };
@@ -51,7 +51,7 @@ const computeGeojsonBox = (geojson) => {
} }
}; };
function DottedMap({ height = 0, width = 0, countries = [], region }) { function DottedMap({ height = 0, width = 0, countries = [], region, grid = 'vertocam' }) {
if (height <= 0 && width <= 0) { if (height <= 0 && width <= 0) {
throw new Error('height or width is required'); throw new Error('height or width is required');
} }
@@ -82,33 +82,66 @@ function DottedMap({ height = 0, width = 0, countries = [], region }) {
const points = {}; const points = {};
for (let x = 0; x < width; x += 1) { const ystep = grid === 'diagonal' ? Math.sqrt(3) / 2 : 1;
for (let y = 0; y < height; y += 1) {
const pointGoogle = [(x / width) * X_RANGE + X_MIN, Y_MAX - (y / height) * Y_RANGE]; for (let y = 0; y * ystep < height; y += 1) {
for (let x = 0; x < width; x += 1) {
const localx = y % 2 === 0 && grid === 'diagonal' ? x + 0.5 : x;
const localy = y * ystep;
const pointGoogle = [(localx / width) * X_RANGE + X_MIN, Y_MAX - (localy / height) * Y_RANGE];
const wgs84Point = proj4(proj4.defs('GOOGLE'), proj4.defs('WGS84'), pointGoogle); const wgs84Point = proj4(proj4.defs('GOOGLE'), proj4.defs('WGS84'), pointGoogle);
if (inside.feature(geojson, wgs84Point) !== -1) { if (inside.feature(geojson, wgs84Point) !== -1) {
points[[x, y].join(';')] = { x, y }; points[[x, y].join(';')] = { x: localx, y: localy };
} }
} }
} }
return { return {
addPin({ lat, lng, data, svgOptions }) { addPin({ lat, lng, data, svgOptions }) {
const [rawX, rawY] = proj4(proj4.defs('GOOGLE'), [lng, lat]); const [googleX, googleY] = proj4(proj4.defs('GOOGLE'), [lng, lat]);
const [x, y] = [Math.round((width * (rawX - X_MIN)) / X_RANGE), Math.round((height * (Y_MAX - rawY)) / Y_RANGE)]; let [rawX, rawY] = [(width * (googleX - X_MIN)) / X_RANGE, (height * (Y_MAX - googleY)) / Y_RANGE];
points[[x, y].join(';')] = { x, y, data, svgOptions }; const y = Math.round(rawY / ystep);
if (y % 2 === 0 && grid === 'diagonal') {
rawX -= 0.5;
}
const x = Math.round(rawX);
let [localx, localy] = [x, Math.round(y) * ystep];
if (y % 2 === 0 && grid === 'diagonal') {
localx += 0.5;
}
points[[x, y].join(';')] = { x: localx, y: localy, data, svgOptions };
}, },
getPoints() { getPoints() {
return Object.values(points); return Object.values(points);
}, },
getSVG({ shape, color = 'current', backgroundColor, radius = 0.5 }) { getSVG({ shape = 'circle', color = 'current', backgroundColor = 'transparent', radius = 0.5 }) {
return `<svg viewBox="0 0 ${width} ${height}" xmlns="http://www.w3.org/2000/svg"> const getPoint = ({ x, y, svgOptions = {} }) => {
${Object.values(points) const pointRadius = svgOptions.radius || radius;
.map( if (shape === 'circle') {
({ x, y, svgOptions = {} }) => return `<circle cx="${x}" cy="${y}" r="${pointRadius}" fill="${svgOptions.color || color}" />`;
`<circle cx="${x}" cy="${y}" r="${svgOptions.radius || radius}" fill="${svgOptions.color || color}" />`, } else if (shape === 'hexagon') {
) const sqrt3radius = Math.sqrt(3) * pointRadius;
.join('\n')}
const polyPoints = [
[x + sqrt3radius, y - pointRadius],
[x + sqrt3radius, y + pointRadius],
[x, y + 2 * pointRadius],
[x - sqrt3radius, y + pointRadius],
[x - sqrt3radius, y - pointRadius],
[x, y - 2 * pointRadius],
];
return `<polyline points="${polyPoints.map((point) => point.join(',')).join(' ')}" fill="${
svgOptions.color || color
}" />`;
}
};
return `<svg viewBox="0 0 ${width} ${height}" xmlns="http://www.w3.org/2000/svg" style="background-color: ${backgroundColor}">
${Object.values(points).map(getPoint).join('\n')}
</svg>`; </svg>`;
}, },
}; };
+5 -4
View File
@@ -1,8 +1,9 @@
const DottedMap = require('./index.js'); const DottedMap = require('./index.js');
const map = new DottedMap({ height: 100, countries: ['FRA'] }); const map = new DottedMap({ height: 100, grid: 'diagonal' });
map.addPin({ lat: 48.85, lng: 2.35, svgOptions: { color: 'red', radius: 0.45 } }); const pinColor = '#D6FF79';
// map.addPin({ lat: 47.65, lng: -2.76, svgOptions: { color: 'red', radius: 0.5 } });
console.log(map.getSVG({ radius: 0.3, color: 'grey' })); map.addPin({ lat: 48.85, lng: 2.35, svgOptions: { color: pinColor, radius: 0.22 } });
console.log(map.getSVG({ radius: 0.22, color: '#423B38', shape: 'circle', backgroundColor: '020300' }));