From b30a268c4623e9efc6637638032bb097f3705226 Mon Sep 17 00:00:00 2001 From: Curran Date: Mon, 15 Jun 2020 15:08:45 -0400 Subject: [PATCH 1/3] Upgrade deps --- packages/examples/package.json | 6 +++--- packages/examples/yarn.lock | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/examples/package.json b/packages/examples/package.json index 5df1404..b90278f 100644 --- a/packages/examples/package.json +++ b/packages/examples/package.json @@ -4,9 +4,9 @@ "private": true, "dependencies": { "@datavis-tech/polylabel": "^1.2.0", - "@testing-library/jest-dom": "^5.9.0", - "@testing-library/react": "^10.2.0", - "@testing-library/user-event": "^11.2.0", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^10.2.1", + "@testing-library/user-event": "^11.4.2", "d3-array": "^2.4.0", "d3-color": "^1.4.1", "d3-fetch": "^1.2.0", diff --git a/packages/examples/yarn.lock b/packages/examples/yarn.lock index 1b4624d..fc7cade 100644 --- a/packages/examples/yarn.lock +++ b/packages/examples/yarn.lock @@ -1462,7 +1462,7 @@ dom-accessibility-api "^0.4.5" pretty-format "^25.5.0" -"@testing-library/jest-dom@^5.9.0": +"@testing-library/jest-dom@^5.10.1": version "5.10.1" resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.10.1.tgz#6508a9f007bd74e5d3c0b3135b668027ab663989" integrity sha512-uv9lLAnEFRzwUTN/y9lVVXVXlEzazDkelJtM5u92PsGkEasmdI+sfzhZHxSDzlhZVTrlLfuMh2safMr8YmzXLg== @@ -1477,7 +1477,7 @@ lodash "^4.17.15" redent "^3.0.0" -"@testing-library/react@^10.2.0": +"@testing-library/react@^10.2.1": version "10.2.1" resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-10.2.1.tgz#f0c5ac9072ad54c29672150943f35d6617263f26" integrity sha512-pv2jZhiZgN1/alz1aImhSasZAOPg3er2Kgcfg9fzuw7aKPLxVengqqR1n0CJANeErR1DqORauQaod+gGUgAJOQ== @@ -1485,7 +1485,7 @@ "@babel/runtime" "^7.10.2" "@testing-library/dom" "^7.9.0" -"@testing-library/user-event@^11.2.0": +"@testing-library/user-event@^11.4.2": version "11.4.2" resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-11.4.2.tgz#90d98fd18455ae81d008e9b26e94d25e8d5bf846" integrity sha512-Kut7G1L+ffozEhYTDNjV9C6RFbUfsKA05rGr1arwbSUoDZQ82OMmsyaXEDznT22Qc0PtZ1Hz3soX0pPosu8+Sw== From bea9112925b059d1d20421297ae59f635661e75b Mon Sep 17 00:00:00 2001 From: Curran Date: Wed, 17 Jun 2020 15:37:07 -0400 Subject: [PATCH 2/3] Try gravitating towards centroids --- .../examples/src/pleth/PolygonLabelsLayer.js | 18 +- packages/examples/src/polylabel.js | 181 ++++++++++++++++++ 2 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 packages/examples/src/polylabel.js diff --git a/packages/examples/src/pleth/PolygonLabelsLayer.js b/packages/examples/src/pleth/PolygonLabelsLayer.js index e0f8538..c2bc46f 100644 --- a/packages/examples/src/pleth/PolygonLabelsLayer.js +++ b/packages/examples/src/pleth/PolygonLabelsLayer.js @@ -1,8 +1,11 @@ import React from 'react'; import styled from 'styled-components'; -import polylabel from '@datavis-tech/polylabel'; +import polylabel from '../polylabel'; import { maxIndex } from 'd3-array'; +const centroidWeight = 0.01; +const precision = 1; + const Text = styled.text` pointer-events: none; user-select: none; @@ -33,15 +36,20 @@ export const PolygonLabelsLayer = ({ const id = feature.id; const name = feature.properties.name; if (feature.geometry.type === 'Polygon') { - const coordinates = polylabel([ - feature.geometry.coordinates[0].map(projection), - ]); + const coordinates = polylabel( + [feature.geometry.coordinates[0].map(projection)], + precision, + false, + centroidWeight + ); labels.push({ id, name, coordinates }); } else if (feature.geometry.type === 'MultiPolygon') { const polylabels = feature.geometry.coordinates .map((polygon) => polygon[0].map(projection)) .filter((projectedPolygon) => !projectedPolygon.some((d) => d === null)) - .map((projectedPolygon) => polylabel([projectedPolygon])); + .map((projectedPolygon) => + polylabel([projectedPolygon], precision, false, centroidWeight) + ); if (polylabels.length > 0) { const coordinates = polylabels[maxIndex(polylabels, (d) => d.distance)]; labels.push({ id, name, coordinates }); diff --git a/packages/examples/src/polylabel.js b/packages/examples/src/polylabel.js new file mode 100644 index 0000000..52b0e73 --- /dev/null +++ b/packages/examples/src/polylabel.js @@ -0,0 +1,181 @@ +// This is the version of polylabel in https://github.com/mapbox/polylabel/pull/63 +'use strict'; + +var Queue = require('tinyqueue'); + +if (Queue.default) Queue = Queue.default; // temporary webpack fix + +module.exports = polylabel; +module.exports.default = polylabel; + +function polylabel(polygon, precision, debug, centroidWeight) { + precision = precision || 1.0; + centroidWeight = centroidWeight || 0; + + // find the bounding box of the outer ring + var minX, minY, maxX, maxY; + for (var i = 0; i < polygon[0].length; i++) { + var p = polygon[0][i]; + if (!i || p[0] < minX) minX = p[0]; + if (!i || p[1] < minY) minY = p[1]; + if (!i || p[0] > maxX) maxX = p[0]; + if (!i || p[1] > maxY) maxY = p[1]; + } + + var width = maxX - minX; + var height = maxY - minY; + var cellSize = Math.min(width, height); + var h = cellSize / 2; + + if (cellSize === 0) { + var degeneratePoleOfInaccessibility = [minX, minY]; + degeneratePoleOfInaccessibility.distance = 0; + return degeneratePoleOfInaccessibility; + } + + // a priority queue of cells in order of their "potential" (max distance to polygon) + var cellQueue = new Queue(undefined, compareMax); + + var centroidCell = getCentroidCell(polygon); + + // take centroid as the first best guess + var bestCell = centroidCell; + + // cover polygon with initial cells + for (var x = minX; x < maxX; x += cellSize) { + for (var y = minY; y < maxY; y += cellSize) { + cellQueue.push(new Cell(x + h, y + h, h, polygon, centroidCell)); + } + } + + // the fitness function to be maximized + function fitness(cell) { + return cell.d - cell.distanceToCentroid * centroidWeight; + } + + // special case for rectangular polygons + var bboxCell = new Cell(minX + width / 2, minY + height / 2, 0, polygon, centroidCell); + if (fitness(bboxCell) > fitness(bestCell)) bestCell = bboxCell; + + var numProbes = cellQueue.length; + + while (cellQueue.length) { + // pick the most promising cell from the queue + var cell = cellQueue.pop(); + + // update the best cell if we found a better one + if (fitness(cell) > fitness(bestCell)) { + bestCell = cell; + if (debug) console.log('found best %d after %d probes', Math.round(1e4 * cell.d) / 1e4, numProbes); + } + + // do not drill down further if there's no chance of a better solution + if (cell.max - bestCell.d <= precision) continue; + + // split the cell into four cells + h = cell.h / 2; + cellQueue.push(new Cell(cell.x - h, cell.y - h, h, polygon, centroidCell)); + cellQueue.push(new Cell(cell.x + h, cell.y - h, h, polygon, centroidCell)); + cellQueue.push(new Cell(cell.x - h, cell.y + h, h, polygon, centroidCell)); + cellQueue.push(new Cell(cell.x + h, cell.y + h, h, polygon, centroidCell)); + numProbes += 4; + } + + if (debug) { + console.log('num probes: ' + numProbes); + console.log('best distance: ' + bestCell.d); + } + + var poleOfInaccessibility = [bestCell.x, bestCell.y]; + poleOfInaccessibility.distance = bestCell.d; + return poleOfInaccessibility; +} + +function compareMax(a, b) { + return b.max - a.max; +} + +function Cell(x, y, h, polygon, centroidCell) { + this.x = x; // cell center x + this.y = y; // cell center y + this.h = h; // half the cell size + this.d = pointToPolygonDist(x, y, polygon); // distance from cell center to polygon + this.distanceToCentroid = centroidCell ? pointToPointDist(this, centroidCell) : 0; + this.max = this.d + this.h * Math.SQRT2; // max distance to polygon within a cell +} + +// distance between two cells +function pointToPointDist(cellA, cellB) { + var dx = cellB.x - cellA.x; + var dy = cellB.y - cellA.y; + return Math.sqrt(dx * dx + dy * dy); +} + +// signed distance from point to polygon outline (negative if point is outside) +function pointToPolygonDist(x, y, polygon) { + var inside = false; + var minDistSq = Infinity; + + for (var k = 0; k < polygon.length; k++) { + var ring = polygon[k]; + + for (var i = 0, len = ring.length, j = len - 1; i < len; j = i++) { + var a = ring[i]; + var b = ring[j]; + + if ((a[1] > y !== b[1] > y) && + (x < (b[0] - a[0]) * (y - a[1]) / (b[1] - a[1]) + a[0])) inside = !inside; + + minDistSq = Math.min(minDistSq, getSegDistSq(x, y, a, b)); + } + } + + return minDistSq === 0 ? 0 : (inside ? 1 : -1) * Math.sqrt(minDistSq); +} + +// get polygon centroid +function getCentroidCell(polygon) { + var area = 0; + var x = 0; + var y = 0; + var points = polygon[0]; + + for (var i = 0, len = points.length, j = len - 1; i < len; j = i++) { + var a = points[i]; + var b = points[j]; + var f = a[0] * b[1] - b[0] * a[1]; + x += (a[0] + b[0]) * f; + y += (a[1] + b[1]) * f; + area += f * 3; + } + if (area === 0) return new Cell(points[0][0], points[0][1], 0, polygon); + return new Cell(x / area, y / area, 0, polygon); +} + +// get squared distance from a point to a segment +function getSegDistSq(px, py, a, b) { + + var x = a[0]; + var y = a[1]; + var dx = b[0] - x; + var dy = b[1] - y; + + if (dx !== 0 || dy !== 0) { + + var t = ((px - x) * dx + (py - y) * dy) / (dx * dx + dy * dy); + + if (t > 1) { + x = b[0]; + y = b[1]; + + } else if (t > 0) { + x += dx * t; + y += dy * t; + } + } + + dx = px - x; + dy = py - y; + + return dx * dx + dy * dy; +} From 41ecdaa379c09ed86c199e9f8e45e10dafca9524 Mon Sep 17 00:00:00 2001 From: Curran Date: Fri, 19 Jun 2020 12:05:23 -0400 Subject: [PATCH 3/3] Got things to work. Add Alan's projection. --- packages/examples/package.json | 7 +- .../examples/src/pleth/PolygonLabelsLayer.js | 2 +- .../src/pleth/geoAlbersUsaTerritories.js | 202 ++++++++++++++++++ packages/examples/src/pleth/index.js | 7 +- packages/examples/src/polylabel.js | 181 ---------------- packages/examples/yarn.lock | 53 +++-- 6 files changed, 247 insertions(+), 205 deletions(-) create mode 100644 packages/examples/src/pleth/geoAlbersUsaTerritories.js delete mode 100644 packages/examples/src/polylabel.js diff --git a/packages/examples/package.json b/packages/examples/package.json index b90278f..1e647ae 100644 --- a/packages/examples/package.json +++ b/packages/examples/package.json @@ -3,14 +3,15 @@ "version": "0.1.0", "private": true, "dependencies": { - "@datavis-tech/polylabel": "^1.2.0", + "@datavis-tech/polylabel": "^1.3.0", "@testing-library/jest-dom": "^5.10.1", - "@testing-library/react": "^10.2.1", - "@testing-library/user-event": "^11.4.2", + "@testing-library/react": "^10.3.0", + "@testing-library/user-event": "^12.0.2", "d3-array": "^2.4.0", "d3-color": "^1.4.1", "d3-fetch": "^1.2.0", "d3-geo": "^1.12.1", + "d3-geo-projection": "^2.9.0", "prettier": "^2.0.5", "react": "^16.13.1", "react-dom": "^16.13.1", diff --git a/packages/examples/src/pleth/PolygonLabelsLayer.js b/packages/examples/src/pleth/PolygonLabelsLayer.js index c2bc46f..c25999a 100644 --- a/packages/examples/src/pleth/PolygonLabelsLayer.js +++ b/packages/examples/src/pleth/PolygonLabelsLayer.js @@ -1,6 +1,6 @@ import React from 'react'; import styled from 'styled-components'; -import polylabel from '../polylabel'; +import polylabel from '@datavis-tech/polylabel'; import { maxIndex } from 'd3-array'; const centroidWeight = 0.01; diff --git a/packages/examples/src/pleth/geoAlbersUsaTerritories.js b/packages/examples/src/pleth/geoAlbersUsaTerritories.js new file mode 100644 index 0000000..34164b7 --- /dev/null +++ b/packages/examples/src/pleth/geoAlbersUsaTerritories.js @@ -0,0 +1,202 @@ +// From https://github.com/stamen/dirty-reprojectors/blob/master/projections/albers-usa-territories.js +const d3 = require('d3-geo'); + +const epsilon = 0.000001; + +function multiplex(streams) { + return { + point(x, y) { + for (const s of streams) s.point(x, y); + }, + sphere() { + for (const s of streams) s.sphere(); + }, + lineStart() { + for (const s of streams) s.lineStart(); + }, + lineEnd() { + for (const s of streams) s.lineEnd(); + }, + polygonStart() { + for (const s of streams) s.polygonStart(); + }, + polygonEnd() { + for (const s of streams) s.polygonEnd(); + }, + }; +} + +export function geoAlbersUsaTerritories() { + var cache, + cacheStream, + lower48 = d3.geoAlbers(), + lower48Point, + alaska = d3 + .geoConicEqualArea() + .rotate([154, 0]) + .center([-2, 58.5]) + .parallels([55, 65]), + alaskaPoint, + hawaii = d3 + .geoConicEqualArea() + .rotate([157, 0]) + .center([-3, 19.9]) + .parallels([8, 18]), + hawaiiPoint, + puertoRico = d3 + .geoConicEqualArea() + .rotate([66, 0]) + .center([0, 18]) + .parallels([8, 18]), + puertoRicoPoint, + guamMariana = d3 + .geoConicEqualArea() + .rotate([-145, 0]) + .center([0, 16]) + .parallels([10, 20]), + guamMarianaPoint, + americanSamoa = d3 + .geoConicEqualArea() + .rotate([170, 0]) + .center([0, -14]) + .parallels([-14, 0]), + americanSamoaPoint, + point, + pointStream = { + point: function (x, y) { + point = [x, y]; + }, + }; + + function albersUsaTerritories(coordinates) { + var x = coordinates[0], + y = coordinates[1]; + return ( + (point = null), + (lower48Point.point(x, y), point) || + (alaskaPoint.point(x, y), point) || + (hawaiiPoint.point(x, y), point) || + (puertoRicoPoint.point(x, y), point) || + (guamMarianaPoint.point(x, y), point) || + (americanSamoaPoint.point(x, y), point) + ); + } + + albersUsaTerritories.invert = function (coordinates) { + var k = lower48.scale(), + t = lower48.translate(), + x = (coordinates[0] - t[0]) / k, + y = (coordinates[1] - t[1]) / k; + return (y >= 0.12 && y < 0.234 && x >= -0.425 && x < -0.214 + ? alaska + : y >= 0.166 && y < 0.234 && x >= -0.214 && x < -0.115 + ? hawaii + : y >= 0.204 && y < 0.234 && x >= 0.3 && x < 0.38 + ? puertoRico + : y >= 0.05 && y < 0.204 && x >= 0.34 && x < 0.38 + ? guamMariana + : y >= 0.16 && y < 0.204 && x >= 0.28 && x < 0.34 + ? americanSamoa + : lower48 + ).invert(coordinates); + }; + + albersUsaTerritories.stream = function (stream) { + return cache && cacheStream === stream + ? cache + : (cache = multiplex([ + lower48.stream((cacheStream = stream)), + alaska.stream(stream), + hawaii.stream(stream), + puertoRico.stream(stream), + guamMariana.stream(stream), + americanSamoa.stream(stream), + ])); + }; + + albersUsaTerritories.precision = function (_) { + if (!arguments.length) return lower48.precision(); + lower48.precision(_); + alaska.precision(_); + hawaii.precision(_); + puertoRico.precision(_); + guamMariana.precision(_); + americanSamoa.precision(_); + return reset(); + }; + + albersUsaTerritories.scale = function (_) { + if (!arguments.length) return lower48.scale(); + lower48.scale(_); + alaska.scale(_ * 0.35); + hawaii.scale(_); + puertoRico.scale(_); + guamMariana.scale(_); + americanSamoa.scale(_); + return albersUsaTerritories.translate(lower48.translate()); + }; + + albersUsaTerritories.translate = function (_) { + if (!arguments.length) return lower48.translate(); + var k = lower48.scale(), + x = +_[0], + y = +_[1]; + + lower48Point = lower48 + .translate(_) + .clipExtent([ + [x - 0.455 * k, y - 0.238 * k], + [x + 0.455 * k, y + 0.238 * k], + ]) + .stream(pointStream); + + alaskaPoint = alaska + .translate([x - 0.31 * k, y + 0.201 * k]) + .clipExtent([ + [x - 0.425 * k + epsilon, y + 0.12 * k + epsilon], + [x - 0.214 * k - epsilon, y + 0.234 * k - epsilon], + ]) + .stream(pointStream); + + hawaiiPoint = hawaii + .translate([x - 0.205 * k, y + 0.212 * k]) + .clipExtent([ + [x - 0.214 * k + epsilon, y + 0.166 * k + epsilon], + [x - 0.115 * k - epsilon, y + 0.234 * k - epsilon], + ]) + .stream(pointStream); + + puertoRicoPoint = puertoRico + .translate([x + 0.335 * k, y + 0.224 * k]) + .clipExtent([ + [x + 0.3 * k, y + 0.204 * k], + [x + 0.38 * k, y + 0.234 * k], + ]) + .stream(pointStream); + + guamMarianaPoint = guamMariana + .translate([x + 0.36 * k, y + 0.14 * k]) + .clipExtent([ + [x + 0.34 * k, y + 0.05 * k], + [x + 0.38 * k, y + 0.204 * k], + ]) + .stream(pointStream); + + americanSamoaPoint = americanSamoa + .translate([x + 0.315 * k, y + 0.18 * k]) + .clipExtent([ + [x + 0.28 * k, y + 0.16 * k], + [x + 0.34 * k, y + 0.204 * k], + ]) + .stream(pointStream); + + return reset(); + }; + + function reset() { + cache = cacheStream = null; + return albersUsaTerritories; + } + + return albersUsaTerritories.scale(1070); +} diff --git a/packages/examples/src/pleth/index.js b/packages/examples/src/pleth/index.js index e0db77f..b5e5497 100644 --- a/packages/examples/src/pleth/index.js +++ b/packages/examples/src/pleth/index.js @@ -1,5 +1,6 @@ import React, { useRef, useCallback } from 'react'; -import { geoAlbersUsa, geoPath } from 'd3-geo'; +import { geoPath } from 'd3-geo'; +import { geoAlbersUsaTerritories } from './geoAlbersUsaTerritories'; import { Wrapper } from './styles'; import { useResizeObserver } from './useResizeObserver'; import { useCache } from './useCache'; @@ -17,7 +18,9 @@ const Pleth = ({ layers, dataProviders, activeId }) => { ); // TODO make this dynamic per region. - const projection = geoAlbersUsa(); + const projection = geoAlbersUsaTerritories() + .scale(1300) + .translate([487.5, 305]); const path = geoPath(projection); diff --git a/packages/examples/src/polylabel.js b/packages/examples/src/polylabel.js deleted file mode 100644 index 52b0e73..0000000 --- a/packages/examples/src/polylabel.js +++ /dev/null @@ -1,181 +0,0 @@ -// This is the version of polylabel in https://github.com/mapbox/polylabel/pull/63 -'use strict'; - -var Queue = require('tinyqueue'); - -if (Queue.default) Queue = Queue.default; // temporary webpack fix - -module.exports = polylabel; -module.exports.default = polylabel; - -function polylabel(polygon, precision, debug, centroidWeight) { - precision = precision || 1.0; - centroidWeight = centroidWeight || 0; - - // find the bounding box of the outer ring - var minX, minY, maxX, maxY; - for (var i = 0; i < polygon[0].length; i++) { - var p = polygon[0][i]; - if (!i || p[0] < minX) minX = p[0]; - if (!i || p[1] < minY) minY = p[1]; - if (!i || p[0] > maxX) maxX = p[0]; - if (!i || p[1] > maxY) maxY = p[1]; - } - - var width = maxX - minX; - var height = maxY - minY; - var cellSize = Math.min(width, height); - var h = cellSize / 2; - - if (cellSize === 0) { - var degeneratePoleOfInaccessibility = [minX, minY]; - degeneratePoleOfInaccessibility.distance = 0; - return degeneratePoleOfInaccessibility; - } - - // a priority queue of cells in order of their "potential" (max distance to polygon) - var cellQueue = new Queue(undefined, compareMax); - - var centroidCell = getCentroidCell(polygon); - - // take centroid as the first best guess - var bestCell = centroidCell; - - // cover polygon with initial cells - for (var x = minX; x < maxX; x += cellSize) { - for (var y = minY; y < maxY; y += cellSize) { - cellQueue.push(new Cell(x + h, y + h, h, polygon, centroidCell)); - } - } - - // the fitness function to be maximized - function fitness(cell) { - return cell.d - cell.distanceToCentroid * centroidWeight; - } - - // special case for rectangular polygons - var bboxCell = new Cell(minX + width / 2, minY + height / 2, 0, polygon, centroidCell); - if (fitness(bboxCell) > fitness(bestCell)) bestCell = bboxCell; - - var numProbes = cellQueue.length; - - while (cellQueue.length) { - // pick the most promising cell from the queue - var cell = cellQueue.pop(); - - // update the best cell if we found a better one - if (fitness(cell) > fitness(bestCell)) { - bestCell = cell; - if (debug) console.log('found best %d after %d probes', Math.round(1e4 * cell.d) / 1e4, numProbes); - } - - // do not drill down further if there's no chance of a better solution - if (cell.max - bestCell.d <= precision) continue; - - // split the cell into four cells - h = cell.h / 2; - cellQueue.push(new Cell(cell.x - h, cell.y - h, h, polygon, centroidCell)); - cellQueue.push(new Cell(cell.x + h, cell.y - h, h, polygon, centroidCell)); - cellQueue.push(new Cell(cell.x - h, cell.y + h, h, polygon, centroidCell)); - cellQueue.push(new Cell(cell.x + h, cell.y + h, h, polygon, centroidCell)); - numProbes += 4; - } - - if (debug) { - console.log('num probes: ' + numProbes); - console.log('best distance: ' + bestCell.d); - } - - var poleOfInaccessibility = [bestCell.x, bestCell.y]; - poleOfInaccessibility.distance = bestCell.d; - return poleOfInaccessibility; -} - -function compareMax(a, b) { - return b.max - a.max; -} - -function Cell(x, y, h, polygon, centroidCell) { - this.x = x; // cell center x - this.y = y; // cell center y - this.h = h; // half the cell size - this.d = pointToPolygonDist(x, y, polygon); // distance from cell center to polygon - this.distanceToCentroid = centroidCell ? pointToPointDist(this, centroidCell) : 0; - this.max = this.d + this.h * Math.SQRT2; // max distance to polygon within a cell -} - -// distance between two cells -function pointToPointDist(cellA, cellB) { - var dx = cellB.x - cellA.x; - var dy = cellB.y - cellA.y; - return Math.sqrt(dx * dx + dy * dy); -} - -// signed distance from point to polygon outline (negative if point is outside) -function pointToPolygonDist(x, y, polygon) { - var inside = false; - var minDistSq = Infinity; - - for (var k = 0; k < polygon.length; k++) { - var ring = polygon[k]; - - for (var i = 0, len = ring.length, j = len - 1; i < len; j = i++) { - var a = ring[i]; - var b = ring[j]; - - if ((a[1] > y !== b[1] > y) && - (x < (b[0] - a[0]) * (y - a[1]) / (b[1] - a[1]) + a[0])) inside = !inside; - - minDistSq = Math.min(minDistSq, getSegDistSq(x, y, a, b)); - } - } - - return minDistSq === 0 ? 0 : (inside ? 1 : -1) * Math.sqrt(minDistSq); -} - -// get polygon centroid -function getCentroidCell(polygon) { - var area = 0; - var x = 0; - var y = 0; - var points = polygon[0]; - - for (var i = 0, len = points.length, j = len - 1; i < len; j = i++) { - var a = points[i]; - var b = points[j]; - var f = a[0] * b[1] - b[0] * a[1]; - x += (a[0] + b[0]) * f; - y += (a[1] + b[1]) * f; - area += f * 3; - } - if (area === 0) return new Cell(points[0][0], points[0][1], 0, polygon); - return new Cell(x / area, y / area, 0, polygon); -} - -// get squared distance from a point to a segment -function getSegDistSq(px, py, a, b) { - - var x = a[0]; - var y = a[1]; - var dx = b[0] - x; - var dy = b[1] - y; - - if (dx !== 0 || dy !== 0) { - - var t = ((px - x) * dx + (py - y) * dy) / (dx * dx + dy * dy); - - if (t > 1) { - x = b[0]; - y = b[1]; - - } else if (t > 0) { - x += dx * t; - y += dy * t; - } - } - - dx = px - x; - dy = py - y; - - return dx * dx + dy * dy; -} diff --git a/packages/examples/yarn.lock b/packages/examples/yarn.lock index fc7cade..ae7b118 100644 --- a/packages/examples/yarn.lock +++ b/packages/examples/yarn.lock @@ -1117,10 +1117,10 @@ resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18" integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg== -"@datavis-tech/polylabel@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@datavis-tech/polylabel/-/polylabel-1.2.0.tgz#814d8aa4307d280a71600c9e6ee496620f0fc6bc" - integrity sha512-qpdeaMSMdDd5PwywM2e12UB3iPsLAQfiam8OWhhq5Ci/c56k07OP2vSyI4uisxdBd9g4p5r/BFPs+C4zMovoSQ== +"@datavis-tech/polylabel@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@datavis-tech/polylabel/-/polylabel-1.3.0.tgz#2ddbefaaa70172630c41c9d5ff45397374f56221" + integrity sha512-TBs2CTXILNSOCtSreWeVNfwfLzACtaoSDjyjCBZ/Y3mCOkbBQMrlCrgQcgrxMpnv72PmJda3NGs+wQ3m0OzFSA== dependencies: tinyqueue "^2.0.3" @@ -1452,10 +1452,10 @@ "@svgr/plugin-svgo" "^4.3.1" loader-utils "^1.2.3" -"@testing-library/dom@^7.9.0": - version "7.15.0" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.15.0.tgz#f47d2c391406846177ad5153f51bd5c5ef0c972e" - integrity sha512-H+cQksHNYjxTS62S+exT5ZcBZeJXE3NDHUKs6MTopp4cMgd8DHX78IUohyUGqJRD1AthtgnKujrPTxYdWZ/w9w== +"@testing-library/dom@^7.14.2": + version "7.16.2" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.16.2.tgz#f7a20b5548817e5c7ed26077913372d977be90af" + integrity sha512-4fT5l5L+5gfNhUZVCg0wnSszbRJ7A1ZHEz32v7OzH3mcY5lUsK++brI3IB2L9F5zO4kSDc2TRGEVa8v2hgl9vA== dependencies: "@babel/runtime" "^7.10.2" aria-query "^4.0.2" @@ -1477,18 +1477,18 @@ lodash "^4.17.15" redent "^3.0.0" -"@testing-library/react@^10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-10.2.1.tgz#f0c5ac9072ad54c29672150943f35d6617263f26" - integrity sha512-pv2jZhiZgN1/alz1aImhSasZAOPg3er2Kgcfg9fzuw7aKPLxVengqqR1n0CJANeErR1DqORauQaod+gGUgAJOQ== +"@testing-library/react@^10.3.0": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-10.3.0.tgz#d615385b8d86ef4d76142a423755d471b3673295" + integrity sha512-Rhn5uJK6lYHWzlGVbK6uAvheAW8AUoFYxTLGdDxgsJDaK/PYy5drWfW/6YpMMOKMw+u6jHHl4MNHlt5qLHnm0Q== dependencies: "@babel/runtime" "^7.10.2" - "@testing-library/dom" "^7.9.0" + "@testing-library/dom" "^7.14.2" -"@testing-library/user-event@^11.4.2": - version "11.4.2" - resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-11.4.2.tgz#90d98fd18455ae81d008e9b26e94d25e8d5bf846" - integrity sha512-Kut7G1L+ffozEhYTDNjV9C6RFbUfsKA05rGr1arwbSUoDZQ82OMmsyaXEDznT22Qc0PtZ1Hz3soX0pPosu8+Sw== +"@testing-library/user-event@^12.0.2": + version "12.0.2" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-12.0.2.tgz#66fa2c64a50b47726c008eacecb9a42b43cd9a2b" + integrity sha512-hJLv9saOJ7WJsRINPPTMmgEya85+L55FRRf1xgFO8HznaUO58YOf+cj+yf/POkhls9pVIPLRoGjzxgvmAsptYg== dependencies: "@babel/runtime" "^7.10.2" @@ -3551,7 +3551,17 @@ d3-fetch@^1.2.0: dependencies: d3-dsv "1" -d3-geo@^1.12.1: +d3-geo-projection@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/d3-geo-projection/-/d3-geo-projection-2.9.0.tgz#826db62f748e8ecd67cd00aced4c26a236ec030c" + integrity sha512-ZULvK/zBn87of5rWAfFMc9mJOipeSo57O+BBitsKIXmU4rTVAnX1kSsJkE0R+TxY8pGNoM1nbyRRE7GYHhdOEQ== + dependencies: + commander "2" + d3-array "1" + d3-geo "^1.12.0" + resolve "^1.1.10" + +d3-geo@^1.12.0, d3-geo@^1.12.1: version "1.12.1" resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.12.1.tgz#7fc2ab7414b72e59fbcbd603e80d9adc029b035f" integrity sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg== @@ -9104,6 +9114,13 @@ resolve@1.15.0: dependencies: path-parse "^1.0.6" +resolve@^1.1.10: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.15.1, resolve@^1.3.2, resolve@^1.8.1: version "1.15.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8"