fil / d3-geo-voronoi Goto Github PK
View Code? Open in Web Editor NEWVoronoi / Delaunay tessellations on the sphere
License: ISC License
Voronoi / Delaunay tessellations on the sphere
License: ISC License
The last 2 commits have replaced the generic javascript object {} data objects with Map and Set data structures.
This is the last issue of that form.
I am about to attach a PR which show one of two possible approaches
The PR needs discussion as approach one has possible performance implications where as options two involves a backwards compatible breaking change.
So this set of judgement calls needs careful review.
In this PR to keep the return of geo_edges() to be an array I was forced chain and Array.from() into a .map()
return Array.from(_index).map(d => d.split("-").map(Number));
the JIT compiler may look at the chain of language primitives and do the correct thing - looping only once.
but on the face of it the PR introduces a "double looping structure."
My preferred solution is option two in which the return type of array becomes an iterator
... Obviously this would break all existing code bases in the wild.
making things faster is a concern #7
Maybe we should just leave geo_edges alone
Maybe the apparent double looping is acceptable.
Maybe we need to wait until the next major version number update?
Should the mesh contain the long edges? It would be more consistent to have that, and also include the large triangle, so that the union of all triangles would cover the whole globe. It's possible to do so now that the triangulation is clean, but it means we need to insert intermediate points when an edge is 180°.
Ref. daf7b88, reverted in daf7b88
(note: this would be a breaking change)
Hello, would it be possible to export "all" the functions in this library so we can create custom/liter geoDelaunay for example ?
My use-case is to use geoDelaunay
to create the edges on a big set of points but i am not interested in anything after edges = geo_edges(triangles, points),
.
Looking at performance, ~50% of load is used of generating edges and the rest on neighbors,hulls,etc.
Something like this as a basic example is what I am thinking (with export to all the functions inside the .js files):
export * as rawGeoDelaunay from "./src/delaunay.js";
export * as rawGeoVoronoi from "./src/voronoi.js";
export * as rawGeoContour from "./src/contour.js";
When points that are close together (voronoi of my garden)
Hi!
The current output of polygons looks like this:
{
"type":"FeatureCollection",
"features":[
{
"type":"Polygon",
"coordinates": ...
"properties":...
},...
According to http://geojson.org/:
Geometric objects with additional properties are Feature objects.
So to make it compliant it should be instead like:
{
"type":"FeatureCollection",
"features":[
{
"type":"Feature",
"geometry":{
"type":"Polygon",
"coordinates":...
},
"properties":...
},...
...
Thanks!
I have a map with one or more polylines, and want to know the segment that is closest to the cursor. I've successfully done this in another library (https://github.com/Voxel8/pyvoronoi), however (besides preferring to be using JS), I'm seeing that on larger scales the voronoi chart is being warped by the earth's curvature, hence this d3-geo-voronoi seems worth a try. Problem is my understanding of geometry isn't sufficient enough to bridge the gap between generating from points to generating from segments. Does anyone please have some advice? Thanks.
I have been inspecting this code line by line while I port it over to rust.
This issue is very minor I want to refactor some code that uses
object 'key' => 'value' properties
But I think better structures such as Map and Set should be employed.
let me explain something minor that gives me unease.
if (_hull[code]) delete _hull[code];
else _hull[e.join("-")] = true;
}
for example after this loop _hull object will look something like
{ '4-3': true, '3-1': true, '0-4': true, '1-0': true }
A) Given the 'value' in the key: value pair is always a 'true' ... I suggest using a set Set structure so the machine does not need to repeatedly store the value.
B) While this code typical java-script and bug free.. the possible values passes into the if condition are TRUE when the key-value pair is found and NULL/UNDEFINED otherwise ..but if we used the Set method has() then the condition is restricted to just TRUE or FALSE.
Secondly _index is a Map like data structure which is currently a Object.
Anyway I have a patch ...l which I am going to link to this issue.
Consider:
let points = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "north",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
0,
20
]
}
},
{
"type": "Feature",
"id": "south",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
0,
-20
]
}
}
]
}
let voronoi = d3.geoVoronoi()(points).polygons()
voronoi is now:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
0,
0
],
[
-90,
0
],
[
-180,
0
],
[
90,
0
],
[
0,
0
]
]
]
},
"properties": {
"site": {
"type": "Feature",
"id": "north",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
0,
20
]
},
"index": 0
},
"sitecoordinates": [
0,
20
],
"neighbours": [
1
]
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
0,
0
],
[
90,
0
],
[
-180,
0
],
[
-90,
0
],
[
0,
0
]
]
]
},
"properties": {
"site": {
"type": "Feature",
"id": "south",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
0,
-20
]
},
"index": 1
},
"sitecoordinates": [
0,
-20
],
"neighbours": [
0
]
}
}
]
}
As both input points were symmetric to the equator, the voronoi polygons are the north and south hemisphere, but geoVoronoi outputs zero area polygons (lines).
This can be plotted/linted at http://geojsonlint.com/.
I want to revisit our current bench-marking ... I will post a PR in the next few days.. but I want to talk about it publicly first.
By benchmark I mean this file.
https://github.com/Fil/d3-geo-voronoi/blob/master/benchmark/sphere.html
I am making progress porting three modules d3-geo, d3-delaunay, d3-geo-voronoi into rust for performance.
In my testing I am starting to find sphere.html a little limiting.
From my perspective - a benchmark should be a battery of test simple to understand but exercise a surprisingly large set of "critical path" ... code sections.
I want to add a new benchmark - pulled from the internet - The simplest world map drawing code snippet.
https://www.d3-graph-gallery.com/graph/backgroundmap_canvas_basic.html
This adds to the bench-marking as it is CANVAS based. not SVG based.
It covers sections of code in this module that will be found in any performance heavy user code.
Namely writing directly to the canvas's 2d graphics context
Lastly I want to fix the current benchmark - so looking back at sphere.html.
a) The number of patches is fix... I want to add a range slider so the evaluator can increase or decrease the number of points rendered - As the performance characteristics of mobile and laptop and desktops are different.
b) There is a performance kink in the existing code ..in the browser's "dev tools" performance monitoring tab..
if you look at the performance graphs about every 4 seconds the is a glitch and for a short time it starts to drop frames.
I can make to go away with a two line refactor .. if I use requestAnimationFrame instead of redrawing on a timer.
c) It only cover a small patch of the sphere.. it looks better if it cover the whole globe.
When I tried to create a Voronoi diagram from a gridded dataset of 4 points (±90, ±45) using geoVoronoi()
, the cells seem to overlap. @Fil has graciously pointed out that this is because the polygons have some 180° edges but no intermediate points.
Here's a minimal, reproducible example: https://observablehq.com/d/1433b09bb55a4807. Ideally, each Voronoi cell should be colored either orange or blue. Would love for a way to fix this. Thanks!
Hi I love this module.. I am happily using it
There is one fly in the ointment.
I am using it in a typescript project.
Currently that mean I need to fake the index.d.ts files so I can invoke this in *.ts and *.tsx files.
import { geoVoronoi } from 'd3-geo-voronoi';
If I create the relevant files would you review and maybe merge my work?
It would be a big task ... so I just want to check before I do anything?
Before I begin, I should note that the code quality of this module is high
and I really like the attention to detail that is apparent in the test coverage.
So I should say I am talking about bug .. which has slipped through high quality testing.
I found this as I am porting this module to rust.. my port has alsmost the same coverage as the original but a whole set of new bugs
So I am developing new test patterns to identify problems with my rust - and I have found a pattern which breaks the javascript version.
I have provded two branches which hightlight the problem.
The branches modify the benchmarks
The problem can be exposed from the branches by opening in a browser the file ./benchmark/sphereCanvas.html
[from the project root directory]
[The benchmarks normal function is rendering a rotating sphere with a number of random point under the control of the user ... ]
Senario A
A have created a new branch showing the bug.
https://github.com/martinfrances107/d3-geo-voronoi/tree/broken_4_segment_beech_ball
A rotating sphere with 4 sites to display a 4 - segment beach ball. With sites at
[ [-20, -20], [20, -20], [20, 20], [-20,20]] ( degrees )
as can be seen in this branch .. the rotation is all flickerey it appears, at times, that not all of the of the 4 items are returned by the call to poloygon().
Senario B
https://github.com/martinfrances107/d3-geo-voronoi/tree/offset_4_segment_beach_ball
The position of the sites is offset just slightly .. the flicker is disappears and the slightly wonky beach ball is rendered consitently.
[ [-15, -20], [20, -20], [20, 20], [-20,20]] ( degrees )
either directly or through d3-delaunay
related to #7
make it easier to create a spherical heatmap with contours?
Loren Petrich's lib uses an algorithm that seems to be in O(n^2)
. It is unpractical for n > 1000. (d3-voronoi runs in O(n) log(n)
.)
See http://bl.ocks.org/Fil/704bdbac80ab2e5d741d5fa3662bdf82
Three possibilities:
The underlying delaunay has these fake points; we should clean the triangulation so that is only contains triangles of real points, and so that it's completely wrapped.
I am measuring my progress in porting this module into rust by the amount to code coverage I see...
That is compared to this module.
It is a simple change to the package.json file have a new test script, So that when I run
npm run coverage
I see this appended to the usual test result.
-------------------|---------|----------|---------|---------|------------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------|---------|----------|---------|---------|------------------------
All files | 93.85 | 72.02 | 98.75 | 96.39 |
d3-geo-voronoi.js | 93.85 | 72.02 | 98.75 | 96.39 | 31,300-315,474,489,495
-------------------|---------|----------|---------|---------|------------------------
Its adds a new dependancy 'nyc' but it is a popular module with over a million weekly download
https://www.npmjs.com/package/nyc
I am about to generate a PR.
I am getting into position where I need to start running comparative performance test between this module and my port to rust.
What is missing from this project is a standardised set of benchmarks which I can also port.
With a public module what I am concerned about is two stakeholders with different code-bases depending on this "library"
For a given PR one stakeholder can see a improvement whilst the other sees a degradation in performance.
The solution is to have a comprehensive set of samples with a diverse set of possible example uses.
Then we can make balanced judgements.
Philippe Rivière. [ hi :) ] has provided a high quality sample which I suggest we start using
https://bl.ocks.org/Fil/raw/a9ba8d0d023752aa580bd95480b7de60/
it is a rotating sphere onto which a 50 points are mapped.
a) The FPS from a browser can be used as a measure of goodness
b) Time to first paint can be used to track how long it takes to ingest a number of points.
I want to include in this webpage in a "benchmark" directory
but ... which pulls in the contents of the dist directory
For comparison I want to add buttons so we can compare the FPS for 50, 500, 5,000, 50,000 points.
[ 50,000 points ... I know I can dream... but I think 6000 points from the rust port is doable ]
If other readers can suggest other code samples which will challenge the library in alternative ways then we can add to the directory.
Anyway I will post a PR later this weekend
Currently one is able to generate Voronoi polygons by passing a set of points. Unfortunately there is no support for bounding box clipping like the Delaunay.vornoi(bbox)
or the turf.voronoi(points, { bbox })
function. Is it possible that this functionality can be added or is there another way to achieve this?
This polygon has a broken hull (thanks @HarryStevens for the example).
[
[-99.99981816168162,35.88112731556315],
[-99.99981816168162,36.055516432754324],
[-99.99981816168162,36.49965029279292],
[-100.00340745567455,36.49965029279292],
[-100.54539084860848,36.49965029279292],
[-100.95457036380363,36.49965029279292],
[-101.08378494754947,36.49965029279292],
[-101.62217904649046,36.49965029279292],
[-102.03135856168561,36.50050935248352],
[-102.16416243942439,36.50050935248352],
[-103.00405723377233,36.50050935248352],
[-103.0004679397794,36.60273745566455],
[-103.0004679397794,37.00048209241092],
[-102.77793171221711,36.99962303272032],
[-102.69896724437244,36.995327734267335],
[-102.04212644366443,36.99275055519555],
[-102.02776926769268,36.99275055519555],
[-101.5539824606246,36.995327734267335],
[-101.06583847758478,36.99790491333913],
[-100.94380248182482,36.99790491333913],
[-100.63153390443904,36.99962303272032],
[-100.08955051150511,37.002200211792115],
[-100.00340745567455,37.00134115210152],
[-99.5403885305853,36.99962303272032],
[-99.45783476874769,36.99962303272032],
[-99.0019944316443,36.99962303272032],
[-98.54615409454094,36.998763973029725],
[-98.34874292492924,36.99790491333913],
[-98.11184952139521,36.99790491333913],
[-97.80317023800238,36.998763973029725],
[-97.46218730867308,36.998763973029725],
[-97.14632943729437,36.998763973029725],
[-96.75150709807097,36.998763973029725],
[-96.52538157651576,36.998763973029725],
[-96.00134465354652,36.998763973029725],
[-95.96545171361713,36.998763973029725],
[-95.78598701397013,36.99962303272032],
[-95.52396855248551,36.99962303272032],
[-95.40911114471145,36.99962303272032],
[-95.07171750937509,36.99962303272032],
[-95.00711021750217,36.99962303272032],
[-94.61946646626465,36.998763973029725],
[-94.61946646626465,36.76681785656856],
[-94.61946646626465,36.668025992149914],
[-94.61946646626465,36.49965029279292],
[-94.56203776237761,36.16203983438834],
[-94.5512698803988,36.101905656046554],
[-94.49384117651176,35.759140839498386],
[-94.47230541255412,35.638872482814826],
[-94.43641247262472,35.42840285861858],
[-94.43282317863178,35.38630893377933],
[-94.44718035460355,34.93358447683476],
[-94.45435894258942,34.7291282704727],
[-94.4615375305753,34.507490870298696],
[-94.46871611856118,34.18963878477784],
[-94.47589470654707,33.94051147450474],
[-94.48666258852587,33.638122463414625],
[-94.51896623446234,33.61664597114971],
[-94.5871628203282,33.67935732856328],
[-94.64459152421524,33.66818955258552],
[-94.64818081820817,33.687947925469246],
[-94.70919881608816,33.687088865778655],
[-94.73791316803168,33.705988178971786],
[-94.77380610796108,33.75495458133581],
[-94.82764551785517,33.741209626286256],
[-94.87071704577045,33.745504924739244],
[-94.94609221962219,33.8125115806058],
[-94.97121727757278,33.86233704266042],
[-95.05018174541745,33.864055162041616],
[-95.12196762527626,33.93106181790817],
[-95.1542712712127,33.93707523574235],
[-95.22964644506445,33.961128907079065],
[-95.25477150301502,33.902712848118476],
[-95.30861091290913,33.880377296162955],
[-95.34091455884558,33.869209520185194],
[-95.46295055460554,33.872645758947584],
[-95.46295055460554,33.88553165430654],
[-95.53832572845728,33.87951823647236],
[-95.56345078640786,33.93192087759877],
[-95.59934372633725,33.93449805667056],
[-95.68548678216781,33.88982695275952],
[-95.75727266202662,33.89240413183131],
[-95.76086195601955,33.872645758947584],
[-95.77162983799838,33.84343772946729],
[-95.8003441899419,33.861477982969824],
[-95.84700501185011,33.8408605503955],
[-95.93673736167361,33.88724977368773],
[-95.94032665566655,33.861477982969824],
[-96.04800547545474,33.83656525194252],
[-96.10184488534885,33.84773302792027],
[-96.09825559135591,33.830551834108334],
[-96.14850570725707,33.83742431163311],
[-96.1772200592006,33.76010893947939],
[-96.22747017510174,33.74808210381103],
[-96.2956667609676,33.764404237932375],
[-96.32079181891818,33.694820402994026],
[-96.36386334683347,33.69224322392223],
[-96.37822052280522,33.725746551855515],
[-96.42847063870639,33.77900825267252],
[-96.50384581255813,33.77385389452894],
[-96.53256016450165,33.822820296892964],
[-96.57204239842397,33.819384058130574],
[-96.62947110231102,33.845155848848485],
[-96.58998886838867,33.894981310903106],
[-96.67613192421923,33.90872626595265],
[-96.70484627616275,33.83484713256132],
[-96.76945356803567,33.827115595345944],
[-96.78381074400744,33.86319610235102],
[-96.83047156591566,33.87522293801938],
[-96.85200732987329,33.84687396822968],
[-96.88431097580975,33.8683504604946],
[-96.9058467397674,33.949961131101304],
[-96.93456109171092,33.95425642955429],
[-96.94532897368973,33.94910207141071],
[-96.99557908959089,33.94910207141071],
[-96.98481120761207,33.88639071399714],
[-97.04223991149911,33.83742431163311],
[-97.08890073340733,33.85374644575445],
[-97.04941849948499,33.81766593874938],
[-97.0960793213932,33.79876662555625],
[-97.08531143941438,33.74378680535805],
[-97.12479367333673,33.71715595494955],
[-97.16427590725907,33.7291827906179],
[-97.20734743517434,33.80993440153401],
[-97.17145449524494,33.83570619225192],
[-97.17863308323082,33.89240413183131],
[-97.21093672916729,33.91645780316803],
[-97.24682966909668,33.90013566904668],
[-97.25400825708256,33.864055162041616],
[-97.30066907899078,33.880377296162955],
[-97.33297272492725,33.87436387832878],
[-97.37245495884959,33.819384058130574],
[-97.44424083870838,33.82367935658356],
[-97.46218730867308,33.849451147301465],
[-97.45859801468015,33.903571907809074],
[-97.48372307263072,33.91559874347743],
[-97.5626875404754,33.89755848997489],
[-97.5985804804048,33.91817592254922],
[-97.58781259842598,33.953397369863694],
[-97.65600918429183,33.98947787686876],
[-97.6883128302283,33.98690069779697],
[-97.73138435814357,33.93707523574235],
[-97.83547388393883,33.85804174420743],
[-97.87854541185412,33.85031020699206],
[-97.97904564365643,33.88982695275952],
[-97.95392058570586,33.937934295432946],
[-97.97186705567056,33.93707523574235],
[-97.94674199771997,33.988618817178164],
[-97.97186705567056,34.00580001099011],
[-98.01852787757878,33.99377317532175],
[-98.08672446344463,34.003222831918315],
[-98.12261740337402,34.08139726376263],
[-98.09390305143052,34.111464352933524],
[-98.10826022740227,34.15441733746337],
[-98.14056387333873,34.141531442104416],
[-98.16927822528224,34.11404153200532],
[-98.24106410514105,34.13294084519845],
[-98.29490351503514,34.13294084519845],
[-98.36310010090101,34.15699451653516],
[-98.42411809878098,34.083974442834425],
[-98.48513609666097,34.0624979505695],
[-98.57127915249151,34.144967680866806],
[-98.61076138641386,34.15699451653516],
[-98.64665432634325,34.164726053750535],
[-98.68972585425854,34.13294084519845],
[-98.76510102811028,34.13637708396083],
[-98.8117618500185,34.15871263591635],
[-98.85842267192672,34.161289814988145],
[-98.91944066980669,34.18190724756247],
[-98.95174431574316,34.21283339642396],
[-98.98763725567255,34.22142399332993],
[-99.0450659595596,34.19822938168381],
[-99.07736960549605,34.211115277042765],
[-99.12044113341133,34.2016656204462],
[-99.12761972139721,34.218846814258136],
[-99.19222701327013,34.21626963518634],
[-99.21017348323483,34.33653799186991],
[-99.27478077510774,34.384645334543336],
[-99.26042359913599,34.403544647736474],
[-99.31785230302303,34.407839946189455],
[-99.38245959489595,34.45680634855348],
[-99.3968167708677,34.37777285701856],
[-99.43988829878299,34.37433661825618],
[-99.47578123871239,34.396672170211694],
[-99.51885276662766,34.41471242371423],
[-99.57987076450765,34.41643054309542],
[-99.60140652846528,34.37433661825618],
[-99.70908534825348,34.38722251361513],
[-99.79522840408404,34.45422916948169],
[-99.84547851998519,34.5066318106081],
[-99.92803228182281,34.577074705237045],
[-99.99622886768867,34.56075257111571],
[-99.99981816168162,34.746309464284636],
[-99.99981816168162,35.030658221872216],
[-99.99981816168162,35.18271178710786],
[-99.99981816168162,35.4223894407844],
[-99.99981816168162,35.61911410993109],
[-99.99981816168162,35.88112731556315]
];
Ideas for later:
Hi,
I'm trying to build Voronoi diagram for "infinite horizon" map. The points - are the cities with defined coordinates [lat, lon]
.
Input: geoJSON
{
"features": [
{
"geometry": {
"coordinates": [
2.1699187,
41.387917
],
"type": "Point"
},
"properties": {},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
7.205232,
43.66049
],
"type": "Point"
},
"properties": {},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
12.4942486,
41.8905198
],
"type": "Point"
},
"properties": {},
"type": "Feature"
}
],
"type": "FeatureCollection"
}
Links d3.geoVoronoi(geoJSON).links()
Triangles d3.geoVoronoi(geoJSON).triangles()
Polygons d3.geoVoronoi(geoJSON).polygons()
Can't understand what I'm doing wrong. I will appreciate any help on these.
I use this library to take a "point cloud" of data on a sphere and connect them to show the data as a continuous mesh. I am seeing weird singularities at the poles and a circle connecting them as shown here.
This image is generated by using globe.gl and plotting a polygon for each triangle in the triangles element of the output of geoDelaunay. I am giving it a set of points in lat long. I create points by converting the lat-long to Euclidian for the polygons.
I would expect as is shown in this post (https://www.redblobgames.com/x/1842-delaunay-voronoi-sphere/) that after projection and triangulation the only hole after triangulation would be at the point at infinity and that would be patched up at the end.
I took a look into how the projecting is being done and don't see a problem, but am not an expert on this kind of thing. Will look into it more myself, but if anyone has any ideas let me know.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.