dotted-map 2.0.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
package/src/index.js DELETED
@@ -1,186 +0,0 @@
1
- import proj4 from 'proj4';
2
- import inside from '@turf/boolean-point-in-polygon';
3
- import geojsonWorld from './countries.geo.json';
4
-
5
- // TODO
6
- // [ ] Handle prefill cache →
7
- // [ ] Method to export a cache
8
- // [ ] Method to import a cache
9
- // [ ] Make two exports: one with countries, one without (reduce bundle size)
10
-
11
- const geojsonByCountry = geojsonWorld.features.reduce((countries, feature) => {
12
- countries[feature.id] = feature;
13
- return countries;
14
- }, {});
15
-
16
- const geojsonToMultiPolygons = (geojson) => {
17
- const coordinates = geojson.features.reduce(
18
- (poly, feature) =>
19
- poly.concat(feature.geometry.type === 'Polygon' ? [feature.geometry.coordinates] : feature.geometry.coordinates),
20
- [],
21
- );
22
- return { type: 'Feature', geometry: { type: 'MultiPolygon', coordinates } };
23
- };
24
-
25
- const CACHE = {};
26
-
27
- const DEFAULT_WORLD_REGION = {
28
- lat: { min: -56, max: 71 },
29
- lng: { min: -179, max: 179 },
30
- };
31
-
32
- const computeGeojsonBox = (geojson) => {
33
- const { type, features, geometry, coordinates } = geojson;
34
- if (type === 'FeatureCollection') {
35
- const boxes = features.map(computeGeojsonBox);
36
- return {
37
- lat: {
38
- min: Math.min(...boxes.map((box) => box.lat.min)),
39
- max: Math.max(...boxes.map((box) => box.lat.max)),
40
- },
41
- lng: {
42
- min: Math.min(...boxes.map((box) => box.lng.min)),
43
- max: Math.max(...boxes.map((box) => box.lng.max)),
44
- },
45
- };
46
- } else if (type == 'Feature') {
47
- return computeGeojsonBox(geometry);
48
- } else if (type === 'MultiPolygon') {
49
- return computeGeojsonBox({ type: 'Polygon', coordinates: coordinates.flat() });
50
- } else if (type == 'Polygon') {
51
- const coords = coordinates.flat();
52
- const latitudes = coords.map(([_lng, lat]) => lat);
53
- const longitudes = coords.map(([lng, _lat]) => lng);
54
-
55
- return {
56
- lat: {
57
- min: Math.min(...latitudes),
58
- max: Math.max(...latitudes),
59
- },
60
- lng: {
61
- min: Math.min(...longitudes),
62
- max: Math.max(...longitudes),
63
- },
64
- };
65
- } else {
66
- throw new Error(`Unknown geojson type ${type}`);
67
- }
68
- };
69
-
70
- function DottedMap({ height = 0, width = 0, countries = [], region, grid = 'vertical', avoidOuterPins = false }) {
71
- if (height <= 0 && width <= 0) {
72
- throw new Error('height or width is required');
73
- }
74
-
75
- let geojson = geojsonWorld;
76
- if (countries.length > 0) {
77
- geojson = {
78
- type: 'FeatureCollection',
79
- features: countries.map((country) => geojsonByCountry[country]),
80
- };
81
- if (!region) {
82
- region = computeGeojsonBox(geojson);
83
- }
84
- } else if (!region) {
85
- region = DEFAULT_WORLD_REGION;
86
- }
87
-
88
- const poly = geojsonToMultiPolygons(geojson);
89
-
90
- const cacheKey = [JSON.stringify(region), grid, height, width, JSON.stringify(countries)].join(' ');
91
-
92
- const [X_MIN, Y_MIN] = proj4(proj4.defs('GOOGLE'), [region.lng.min, region.lat.min]);
93
- const [X_MAX, Y_MAX] = proj4(proj4.defs('GOOGLE'), [region.lng.max, region.lat.max]);
94
- const X_RANGE = X_MAX - X_MIN;
95
- const Y_RANGE = Y_MAX - Y_MIN;
96
-
97
- if (width <= 0) {
98
- width = Math.round((height * X_RANGE) / Y_RANGE);
99
- } else if (height <= 0) {
100
- height = Math.round((width * Y_RANGE) / X_RANGE);
101
- }
102
-
103
- const points = { ...CACHE[cacheKey] } || {};
104
- const ystep = grid === 'diagonal' ? Math.sqrt(3) / 2 : 1;
105
-
106
- if (!CACHE[cacheKey]) {
107
- for (let y = 0; y * ystep < height; y += 1) {
108
- for (let x = 0; x < width; x += 1) {
109
- const localx = y % 2 === 0 && grid === 'diagonal' ? x + 0.5 : x;
110
- const localy = y * ystep;
111
-
112
- const pointGoogle = [(localx / width) * X_RANGE + X_MIN, Y_MAX - (localy / height) * Y_RANGE];
113
- const wgs84Point = proj4(proj4.defs('GOOGLE'), proj4.defs('WGS84'), pointGoogle);
114
-
115
- if (inside(wgs84Point, poly)) {
116
- points[[x, y].join(';')] = { x: localx, y: localy };
117
- }
118
- }
119
- }
120
-
121
- CACHE[cacheKey] = { ...points };
122
- }
123
-
124
- return {
125
- addPin({ lat, lng, data, svgOptions }) {
126
- const [googleX, googleY] = proj4(proj4.defs('GOOGLE'), [lng, lat]);
127
- if (avoidOuterPins) {
128
- const wgs84Point = proj4(proj4.defs('GOOGLE'), proj4.defs('WGS84'), [googleX, googleY]);
129
- if (!inside(wgs84Point, poly)) return;
130
- }
131
- let [rawX, rawY] = [(width * (googleX - X_MIN)) / X_RANGE, (height * (Y_MAX - googleY)) / Y_RANGE];
132
- const y = Math.round(rawY / ystep);
133
- if (y % 2 === 0 && grid === 'diagonal') {
134
- rawX -= 0.5;
135
- }
136
- const x = Math.round(rawX);
137
- let [localx, localy] = [x, Math.round(y) * ystep];
138
- if (y % 2 === 0 && grid === 'diagonal') {
139
- localx += 0.5;
140
- }
141
-
142
- const point = { x: localx, y: localy, data, svgOptions };
143
-
144
- points[[x, y].join(';')] = point;
145
-
146
- return point;
147
- },
148
- getPoints() {
149
- return Object.values(points);
150
- },
151
- getSVG({ shape = 'circle', color = 'current', backgroundColor = 'transparent', radius = 0.5 }) {
152
- const getPoint = ({ x, y, svgOptions = {} }) => {
153
- const pointRadius = svgOptions.radius || radius;
154
- if (shape === 'circle') {
155
- return `<circle cx="${x}" cy="${y}" r="${pointRadius}" fill="${svgOptions.color || color}" />`;
156
- } else if (shape === 'hexagon') {
157
- const sqrt3radius = Math.sqrt(3) * pointRadius;
158
-
159
- const polyPoints = [
160
- [x + sqrt3radius, y - pointRadius],
161
- [x + sqrt3radius, y + pointRadius],
162
- [x, y + 2 * pointRadius],
163
- [x - sqrt3radius, y + pointRadius],
164
- [x - sqrt3radius, y - pointRadius],
165
- [x, y - 2 * pointRadius],
166
- ];
167
-
168
- return `<polyline points="${polyPoints.map((point) => point.join(',')).join(' ')}" fill="${
169
- svgOptions.color || color
170
- }" />`;
171
- }
172
- };
173
-
174
- return `<svg viewBox="0 0 ${width} ${height}" xmlns="http://www.w3.org/2000/svg" style="background-color: ${backgroundColor}">
175
- ${Object.values(points).map(getPoint).join('\n')}
176
- </svg>`;
177
- },
178
- image: {
179
- region,
180
- width,
181
- height,
182
- },
183
- };
184
- }
185
-
186
- export default DottedMap;