From da04dc514643ef4ad5bb997652f17b03f9c50227 Mon Sep 17 00:00:00 2001 From: neocarto Date: Mon, 14 Oct 2024 17:53:01 +0200 Subject: [PATCH] grids wip --- docs/global.html | 5 ++-- docs/grid_arbitrary.js.html | 11 +++++--- docs/grid_diamond.js.html | 11 +++++--- docs/grid_dot.js.html | 11 +++++--- docs/grid_h3.js.html | 50 +++++++++++++++++++++++++------------ docs/grid_hexbin.js.html | 11 +++++--- docs/grid_make.js.html | 27 ++++++++++++++------ docs/grid_square.js.html | 11 +++++--- docs/grid_triangle.js.html | 11 +++++--- package-lock.json | 39 +++++++++++++++++++++++++++++ package.json | 1 + src/grid/arbitrary.js | 11 +++++--- src/grid/countdots.js | 42 +++++++++++++++++++++++++++++++ src/grid/diamond.js | 11 +++++--- src/grid/dot.js | 11 +++++--- src/grid/h3.js | 50 +++++++++++++++++++++++++------------ src/grid/hexbin.js | 11 +++++--- src/grid/make.js | 27 ++++++++++++++------ src/grid/square.js | 11 +++++--- src/grid/triangle.js | 11 +++++--- src/index.js | 2 ++ 21 files changed, 289 insertions(+), 86 deletions(-) create mode 100644 src/grid/countdots.js diff --git a/docs/global.html b/docs/global.html index 53adf31..3677048 100644 --- a/docs/global.html +++ b/docs/global.html @@ -83,9 +83,10 @@ geoviz.graticule(svg, { step: 2 }) // where svg is the container svg.graticule({ step: [10,2] }) // where svg is the container svg.plot({ type: "graticule", step: [10,2] }) // where svg is the container -geoviz.graticule({ step: 2 }) // no container

grid/arbitrary()

The grid.arbitrary function allows to create an arbitrary geoJSON grid in SVG coordinates.

Properties
NameTypeAttributesDefaultDescription
stepnumber<optional>
50

step of the grid

widthnumber<optional>
1000

width of the grid

heightnumber<optional>
500

height of the grid

Example
geoviz.grid.arbitrary(50, 1000, 500)

grid/diamond()

The grid.diamond function allows to create a diamond geoJSON grid in SVG coordinates.

Properties
NameTypeAttributesDefaultDescription
stepnumber<optional>
50

step of the grid

widthnumber<optional>
1000

width of the grid

heightnumber<optional>
500

height of the grid

Example
geoviz.grid.diamond(50, 1000, 500)

grid/dot()

The grid.dot function allows to create a geoJSON vith regular dots in SVG coordinates.

Properties
NameTypeAttributesDefaultDescription
stepnumber<optional>
50

step of the grid

widthnumber<optional>
1000

width of the grid

heightnumber<optional>
500

height of the grid

Example
geoviz.grid.dot(50, 1000, 500)

grid/h3()

The grid.h3 function allows to create a hexbin geoJSON grid in geographical coordinates.

Properties
NameTypeAttributesDefaultDescription
levelnumber<optional>
0

level of the grid. Form 0 (large hexagons) to 4 (small hexagons). See: https://h3geo.org

Example
geoviz.grid.h3(1)

grid/hexbin()

The grid.hexbin function allows to create a hexbin geoJSON grid in SVG coordinates.

Properties
NameTypeAttributesDefaultDescription
stepnumber<optional>
50

step of the grid

widthnumber<optional>
1000

width of the grid

heightnumber<optional>
500

height of the grid

Example
geoviz.grid.hexbin(50, 1000, 500)

grid/make()

The grid.make function allows to create a regular grid geoJSON. For all types, For all grid types (except "h3"), the function returns a geojson with svg coordinates in the layout of the page. For type "h3", the function returns geographic coordinates in latitude and longitude.

Properties
NameTypeAttributesDefaultDescription
typestring<optional>
"square"

Type of grid ("square", "dot", "diamond", "hexbin" (or "hex"), "trangle", "arbitrary" (or "randmon"), "h3" (or "h3geo", "hexgeo", "hexbingeo"))

stepnumber<optional>
50

step of grids (except for "h3" type)

levelnumber<optional>
0

level oh geographical hexbin grids ("h3" type only). Form 0 (large hexagons) to 4 (small hexagons). See: https://h3geo.org

Example
// There are several ways to use this function
+geoviz.graticule({ step: 2 }) // no container

grid/arbitrary()

The grid.arbitrary function allows to create an arbitrary geoJSON grid in SVG coordinates.

Properties
NameTypeAttributesDefaultDescription
stepnumber<optional>
50

step of the grid

widthnumber<optional>
1000

width of the grid

heightnumber<optional>
500

height of the grid

Example
geoviz.grid.arbitrary({step: 30})

grid/diamond()

The grid.diamond function allows to create a diamond geoJSON grid in SVG coordinates.

Properties
NameTypeAttributesDefaultDescription
stepnumber<optional>
50

step of the grid

widthnumber<optional>
1000

width of the grid

heightnumber<optional>
500

height of the grid

Example
geoviz.grid.diamond({step: 30})

grid/dot()

The grid.dot function allows to create a geoJSON vith regular dots in SVG coordinates.

Properties
NameTypeAttributesDefaultDescription
stepnumber<optional>
50

step of the grid

widthnumber<optional>
1000

width of the grid

heightnumber<optional>
500

height of the grid

Example
geoviz.grid.dot({step: 30})

grid/h3()

The grid.h3 function allows to create a hexbin geoJSON grid in geographical coordinates.

Properties
NameTypeAttributesDefaultDescription
levelnumber<optional>
0

level of the grid. Form 0 (large hexagons) to 4 (small hexagons). See: https://h3geo.org

domainobject<optional>

a geoJSON to define an extent

rewindboolen<optional>

to rewind the output

Example
geoviz.grid.h3({level: 1})
+geoviz.grid.h3({level: 4, domain: italy})

grid/hexbin()

The grid.hexbin function allows to create a hexbin geoJSON grid in SVG coordinates.

Properties
NameTypeAttributesDefaultDescription
stepnumber<optional>
50

step of the grid

widthnumber<optional>
1000

width of the grid

heightnumber<optional>
500

height of the grid

Example
geoviz.grid.hexbin({step: 30})

grid/make()

The grid.make function allows to create a regular grid geoJSON. For all types, For all grid types (except "h3"), the function returns a geojson with svg coordinates in the layout of the page. For type "h3", the function returns geographic coordinates in latitude and longitude.

Properties
NameTypeAttributesDefaultDescription
typestring<optional>
"square"

Type of grid ("square", "dot", "diamond", "hexbin" (or "hex"), "trangle", "arbitrary" (or "randmon"), "h3" (or "h3geo", "hexgeo", "hexbingeo"))

stepnumber<optional>
50

step of grids (except for "h3" type)

levelnumber<optional>
0

level oh geographical hexbin grids ("h3" type only). Form 0 (large hexagons) to 4 (small hexagons). See: https://h3geo.org

domainobject<optional>

a geoJSON to define an extent (h3 only)

rewindboolen<optional>

to rewind the output (h3 only)

Example
// There are several ways to use this function
 geoviz.grid.make(svg, { type:"diamond", step:100 })  // where svg is the container
-svg.grid.make({ type:"diamond", step:100 })  // where svg is the container

grid/square()

The grid.square function allows to create a square geoJSON grid in SVG coordinates.

Properties
NameTypeAttributesDefaultDescription
stepnumber<optional>
50

step of the grid

widthnumber<optional>
1000

width of the grid

heightnumber<optional>
500

height of the grid

Example
geoviz.grid.square(50, 1000, 500)

grid/triangle()

The grid.triangle function allows to create a triangle geoJSON grid in SVG coordinates.

Properties
NameTypeAttributesDefaultDescription
stepnumber<optional>
50

step of the grid

widthnumber<optional>
1000

width of the grid

heightnumber<optional>
500

height of the grid

Example
geoviz.grid.triangle(50, 1000, 500)

halfcircle()

The halfcircle function allows to create a layer with rotable half-circles from a geoJSON. The function adds a layer to the SVG container and returns the layer identifier. If the container is not defined, then the layer is displayed directly.

Properties
NameTypeAttributesDefaultDescription
dataobject

GeoJSON FeatureCollection

idstring<optional>

id of the layer

posArray.<number><optional>
[0,0]

position of the half-circle to display a single circle

dxnumber<optional>
0

shift in x

dynumber<optional>
0

shift in y

anglenumber<optional>
0

angle of the half circle

rnumber | string<optional>
10

a number or the name of a property containing numerical values

innerRadiusnumber<optional>
10

inner radius

cornerRadiusnumber<optional>
2

corner radius

knumber<optional>
50

radius of the largest half-circle (or corresponding to the value defined by fixmax)

fixmaxnumber<optional>

value matching the half-circle with radius k. Setting this value is useful for making maps comparable with each other

sortstring | function<optional>

the field to sort circles or a sort function

descendingboolean<optional>

circle sorting order

coordsstring<optional>
"geo"

use "svg" if the coordinates are already in the plan of the svg document

fillstring | function<optional>

fill color. To create choropleth maps or typologies, use the tool.choro and tool.typo functions

strokestring | function<optional>

stroke color. To create choropleth maps or typologies, use the tool.choro and tool.typo functions

tipboolean | function<optional>
false

a function to display the tip. Use true tu display all fields

viewboolean<optional>
false

use true and viewof in Observable for this layer to act as Input

tipstyleobject<optional>

tooltip style

**<optional>

other SVG attributes that can be applied (strokeDasharray, strokeWidth, opacity, strokeLinecap...)

svg_**<optional>

parameters of the svg container created if the layer is not called inside a container (e.g svg_width)

Example
// There are several ways to use this function
+svg.grid.make({ type:"diamond", step:100 })  // where svg is the container

grid/square()

The grid.square function allows to create a square geoJSON grid in SVG coordinates.

Properties
NameTypeAttributesDefaultDescription
stepnumber<optional>
50

step of the grid

widthnumber<optional>
1000

width of the grid

heightnumber<optional>
500

height of the grid

Example
geoviz.grid.square({step: 30})

grid/triangle()

The grid.triangle function allows to create a triangle geoJSON grid in SVG coordinates.

Properties
NameTypeAttributesDefaultDescription
stepnumber<optional>
50

step of the grid

widthnumber<optional>
1000

width of the grid

heightnumber<optional>
500

height of the grid

Example
geoviz.grid.triangle({step: 30})

halfcircle()

The halfcircle function allows to create a layer with rotable half-circles from a geoJSON. The function adds a layer to the SVG container and returns the layer identifier. If the container is not defined, then the layer is displayed directly.

Properties
NameTypeAttributesDefaultDescription
dataobject

GeoJSON FeatureCollection

idstring<optional>

id of the layer

posArray.<number><optional>
[0,0]

position of the half-circle to display a single circle

dxnumber<optional>
0

shift in x

dynumber<optional>
0

shift in y

anglenumber<optional>
0

angle of the half circle

rnumber | string<optional>
10

a number or the name of a property containing numerical values

innerRadiusnumber<optional>
10

inner radius

cornerRadiusnumber<optional>
2

corner radius

knumber<optional>
50

radius of the largest half-circle (or corresponding to the value defined by fixmax)

fixmaxnumber<optional>

value matching the half-circle with radius k. Setting this value is useful for making maps comparable with each other

sortstring | function<optional>

the field to sort circles or a sort function

descendingboolean<optional>

circle sorting order

coordsstring<optional>
"geo"

use "svg" if the coordinates are already in the plan of the svg document

fillstring | function<optional>

fill color. To create choropleth maps or typologies, use the tool.choro and tool.typo functions

strokestring | function<optional>

stroke color. To create choropleth maps or typologies, use the tool.choro and tool.typo functions

tipboolean | function<optional>
false

a function to display the tip. Use true tu display all fields

viewboolean<optional>
false

use true and viewof in Observable for this layer to act as Input

tipstyleobject<optional>

tooltip style

**<optional>

other SVG attributes that can be applied (strokeDasharray, strokeWidth, opacity, strokeLinecap...)

svg_**<optional>

parameters of the svg container created if the layer is not called inside a container (e.g svg_width)

Example
// There are several ways to use this function
 geoviz.halfcircle(svg, { pos: [10,20], r: 15 }) // a single half-circle
 geoviz.halfcircle(svg, { data: cities, r: "population" }) // where svg is the container
 svg.halfcircle({ data: cities, r: "population" }) // where svg is the container
diff --git a/docs/grid_arbitrary.js.html b/docs/grid_arbitrary.js.html
index 25a984f..b92890a 100644
--- a/docs/grid_arbitrary.js.html
+++ b/docs/grid_arbitrary.js.html
@@ -11,10 +11,10 @@
  * @property {number} [width = 1000] - width of the grid
  * @property {number} [height = 500] - height of the grid
  * @example
- * geoviz.grid.arbitrary(50, 1000, 500)
+ * geoviz.grid.arbitrary({step: 30})
  */
 
-export function arbitrary(step = 50, width = 1000, height = 500) {
+export function arbitrary({ step = 50, width = 1000, height = 500 } = {}) {
   let grid = [];
   let nb = Math.round((width / step) * (height / step));
   for (let i = 0; i < nb; i++) {
@@ -40,6 +40,11 @@
       },
     };
   });
-  return { type: "FeatureCollection", features: result };
+  return {
+    type: "FeatureCollection",
+    type: "arbitrary",
+    coords: "svg",
+    features: result,
+  };
 }
 
Nicolas Lambert 2024 - MIT License
\ No newline at end of file diff --git a/docs/grid_diamond.js.html b/docs/grid_diamond.js.html index ef8f459..7825161 100644 --- a/docs/grid_diamond.js.html +++ b/docs/grid_diamond.js.html @@ -11,9 +11,9 @@ * @property {number} [width = 1000] - width of the grid * @property {number} [height = 500] - height of the grid * @example - * geoviz.grid.diamond(50, 1000, 500) + * geoviz.grid.diamond({step: 30}) */ -export function diamond(step = 50, width = 1000, height = 500) { +export function diamond({ step = 50, width = 1000, height = 500 } = {}) { let size = step * Math.sqrt(2); // build grid @@ -46,6 +46,11 @@ }, }; }); - return { type: "FeatureCollection", features: result }; + return { + type: "FeatureCollection", + type: "diamond", + coords: "svg", + features: result, + }; }
\ No newline at end of file diff --git a/docs/grid_dot.js.html b/docs/grid_dot.js.html index acc7865..28134a0 100644 --- a/docs/grid_dot.js.html +++ b/docs/grid_dot.js.html @@ -11,9 +11,9 @@ * @property {number} [width = 1000] - width of the grid * @property {number} [height = 500] - height of the grid * @example - * geoviz.grid.dot(50, 1000, 500) + * geoviz.grid.dot({step: 30}) */ -export function dot(step = 30, width = 1000, height = 500) { +export function dot({ step = 30, width = 1000, height = 500 } = {}) { // build grid let y = d3.range(0 + step / 2, height, step).reverse(); let x = d3.range(0 + step / 2, width, step); @@ -32,6 +32,11 @@ }, }; }); - return { type: "FeatureCollection", features: result }; + return { + type: "FeatureCollection", + type: "dot", + coords: "svg", + features: result, + }; }
\ No newline at end of file diff --git a/docs/grid_h3.js.html b/docs/grid_h3.js.html index 08009d0..b38b0ad 100644 --- a/docs/grid_h3.js.html +++ b/docs/grid_h3.js.html @@ -7,28 +7,46 @@ cellToBoundary, } from "h3-js"; +import { featureToH3Set, h3SetToFeatureCollection } from "geojson2h3"; +import { rewind as rrewind } from "../tool/rewind"; + /** * @function grid/h3 * @description The `grid.h3` function allows to create a hexbin geoJSON grid in geographical coordinates. * @see {@link https://observablehq.com/@neocartocnrs/regular-grids} * @property {number} [level = 0] - level of the grid. Form 0 (large hexagons) to 4 (small hexagons). See: https://h3geo.org + * @property {object} [domain] - a geoJSON to define an extent + * @property {boolen} [rewind] - to rewind the output * @example - * geoviz.grid.h3(1) + * geoviz.grid.h3({level: 1}) + * geoviz.grid.h3({level: 4, domain: italy}) */ -export function h3(level = 0) { - return { - type: "FeatureCollection", - features: getRes0Cells() - .map((i) => cellToChildren(i, level)) - .flat() - .map((d) => ({ - type: "Feature", - properties: { id: d, pentagon: isPentagon(d) }, - geometry: { - type: "Polygon", - coordinates: [cellToBoundary(d, true).reverse()], - }, - })), - }; +export function h3({ level = 0, domain = undefined, rewind = undefined } = {}) { + let output; + if (domain) { + rewind = rewind !== undefined ? rewind : true; + const hexagons = featureToH3Set(domain, level); + output = h3SetToFeatureCollection(hexagons, (hex) => ({ + value: hex, + })); + } else { + rewind = rewind !== undefined ? rewind : false; + output = { + type: "FeatureCollection", + features: getRes0Cells() + .map((i) => cellToChildren(i, level)) + .flat() + .map((d) => ({ + type: "Feature", + properties: { id: d, pentagon: isPentagon(d) }, + geometry: { + type: "Polygon", + coordinates: [cellToBoundary(d, true).reverse()], + }, + })), + }; + } + + return rewind ? rrewind(output) : output; }
\ No newline at end of file diff --git a/docs/grid_hexbin.js.html b/docs/grid_hexbin.js.html index 81abe22..cab0c25 100644 --- a/docs/grid_hexbin.js.html +++ b/docs/grid_hexbin.js.html @@ -11,9 +11,9 @@ * @property {number} [width = 1000] - width of the grid * @property {number} [height = 500] - height of the grid * @example - * geoviz.grid.hexbin(50, 1000, 500) + * geoviz.grid.hexbin({step: 30}) */ -export function hexbin(step = 50, width = 1000, height = 500) { +export function hexbin({ step = 50, width = 1000, height = 500 } = {}) { let w = step; let size = w / Math.sqrt(3); let h = 2 * size * (3 / 4); @@ -48,6 +48,11 @@ }, }; }); - return { type: "FeatureCollection", features: result }; + return { + type: "FeatureCollection", + type: "hexbin", + coords: "svg", + features: result, + }; }
\ No newline at end of file diff --git a/docs/grid_make.js.html b/docs/grid_make.js.html index dce6f6d..df5b3d7 100644 --- a/docs/grid_make.js.html +++ b/docs/grid_make.js.html @@ -16,39 +16,50 @@ * @property {string} [type = "square"] - Type of grid ("square", "dot", "diamond", "hexbin" (or "hex"), "trangle", "arbitrary" (or "randmon"), "h3" (or "h3geo", "hexgeo", "hexbingeo")) * @property {number} [step = 50] - step of grids (except for "h3" type) * @property {number} [level = 0] - level oh geographical hexbin grids ("h3" type only). Form 0 (large hexagons) to 4 (small hexagons). See: https://h3geo.org + * @property {object} [domain] - a geoJSON to define an extent (h3 only) + * @property {boolen} [rewind] - to rewind the output (h3 only) * @example * // There are several ways to use this function * geoviz.grid.make(svg, { type:"diamond", step:100 }) // where svg is the container * svg.grid.make({ type:"diamond", step:100 }) // where svg is the container */ -export function make(svg, { step = 50, type = "square", level = 0 } = {}) { +export function make( + svg, + { + step = 50, + type = "square", + level = 0, + domain = undefined, + rewind = undefined, + } = {} +) { switch (type) { case "square": - return square(step, svg.width, svg.height); + return square({ step, width: svg.width, height: svg.height }); break; case "arbitrary": case "random": - return arbitrary(step, svg.width, svg.height); + return arbitrary({ step, width: svg.width, height: svg.height }); break; case "dot": - return dot(step, svg.width, svg.height); + return dot({ step, width: svg.width, height: svg.height }); break; case "diamond": - return diamond(step, svg.width, svg.height); + return diamond({ step, width: svg.width, height: svg.height }); break; case "hexbin": case "hex": - return hexbin(step, svg.width, svg.height); + return hexbin({ step, width: svg.width, height: svg.height }); break; case "triangle": - return triangle(step, svg.width, svg.height); + return triangle({ step, width: svg.width, height: svg.height }); break; case "h3": case "h3geo": case "hexgeo": case "hexbingeo": - return h3(level); + return h3({ level, domain, rewind }); break; } } diff --git a/docs/grid_square.js.html b/docs/grid_square.js.html index be005c1..2a42933 100644 --- a/docs/grid_square.js.html +++ b/docs/grid_square.js.html @@ -11,9 +11,9 @@ * @property {number} [width = 1000] - width of the grid * @property {number} [height = 500] - height of the grid * @example - * geoviz.grid.square(50, 1000, 500) + * geoviz.grid.square({step: 30}) */ -export function square(step = 50, width = 1000, height = 500) { +export function square({ step = 50, width = 1000, height = 500 } = {}) { // build grid let y = d3.range(0 + step / 2, height, step).reverse(); let x = d3.range(0 + step / 2, width, step); @@ -41,6 +41,11 @@ }, }; }); - return { type: "FeatureCollection", features: result }; + return { + type: "FeatureCollection", + type: "square", + coords: "svg", + features: result, + }; }
\ No newline at end of file diff --git a/docs/grid_triangle.js.html b/docs/grid_triangle.js.html index 0fa87e4..fdebded 100644 --- a/docs/grid_triangle.js.html +++ b/docs/grid_triangle.js.html @@ -11,9 +11,9 @@ * @property {number} [width = 1000] - width of the grid * @property {number} [height = 500] - height of the grid * @example - * geoviz.grid.triangle(50, 1000, 500) + * geoviz.grid.triangle({step: 30}) */ -export function triangle(step = 50, width = 1000, height = 500) { +export function triangle({ step = 50, width = 1000, height = 500 } = {}) { let triangletop = (p, size) => { let h = (Math.sqrt(3) / 2) * size; let p1 = [p[0] + size / 2, p[1]]; @@ -62,6 +62,11 @@ }, }; }); - return { type: "FeatureCollection", features: result }; + return { + type: "FeatureCollection", + type: "triangle", + coords: "svg", + features: result, + }; }
\ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4dae6db..ef21d11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "dicopal": "^0.6.4", "docs": "^0.3.2-canary.0", "documentation": "^14.0.3", + "geojson2h3": "^1.2.0", "h3-js": "^4.1.0", "rollup": "^4.10.0", "statsbreaks": "^1.0.6", @@ -2354,6 +2355,29 @@ "node": ">=6.9.0" } }, + "node_modules/geojson2h3": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/geojson2h3/-/geojson2h3-1.2.0.tgz", + "integrity": "sha512-UF8JLT4mPHSSKFWibBrdRbEkN0nQVWawjgodwoKUjB8jTJ/AB9uTkvlVmSC/eE6Ysy0pJdL36ogEg8qCt27r5g==", + "dependencies": { + "h3-js": "^3.6.1" + }, + "engines": { + "node": ">=4", + "npm": ">=3", + "yarn": ">=1.3.0" + } + }, + "node_modules/geojson2h3/node_modules/h3-js": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/h3-js/-/h3-js-3.7.2.tgz", + "integrity": "sha512-LPjlHSwB9zQZrMqKloCZmmmt3yZzIK7nqPcXqwU93zT3TtYG6jP4tZBzAPouxut7lLjdFbMQ75wRBiKfpsnY7w==", + "engines": { + "node": ">=4", + "npm": ">=3", + "yarn": ">=1.3.0" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -7491,6 +7515,21 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" }, + "geojson2h3": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/geojson2h3/-/geojson2h3-1.2.0.tgz", + "integrity": "sha512-UF8JLT4mPHSSKFWibBrdRbEkN0nQVWawjgodwoKUjB8jTJ/AB9uTkvlVmSC/eE6Ysy0pJdL36ogEg8qCt27r5g==", + "requires": { + "h3-js": "^3.6.1" + }, + "dependencies": { + "h3-js": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/h3-js/-/h3-js-3.7.2.tgz", + "integrity": "sha512-LPjlHSwB9zQZrMqKloCZmmmt3yZzIK7nqPcXqwU93zT3TtYG6jP4tZBzAPouxut7lLjdFbMQ75wRBiKfpsnY7w==" + } + } + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", diff --git a/package.json b/package.json index 136bc65..01aaf0e 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "dicopal": "^0.6.4", "docs": "^0.3.2-canary.0", "documentation": "^14.0.3", + "geojson2h3": "^1.2.0", "h3-js": "^4.1.0", "rollup": "^4.10.0", "statsbreaks": "^1.0.6", diff --git a/src/grid/arbitrary.js b/src/grid/arbitrary.js index 3bdf5d8..8628555 100644 --- a/src/grid/arbitrary.js +++ b/src/grid/arbitrary.js @@ -9,10 +9,10 @@ const d3 = Object.assign({}, { Delaunay }); * @property {number} [width = 1000] - width of the grid * @property {number} [height = 500] - height of the grid * @example - * geoviz.grid.arbitrary(50, 1000, 500) + * geoviz.grid.arbitrary({step: 30}) */ -export function arbitrary(step = 50, width = 1000, height = 500) { +export function arbitrary({ step = 50, width = 1000, height = 500 } = {}) { let grid = []; let nb = Math.round((width / step) * (height / step)); for (let i = 0; i < nb; i++) { @@ -38,5 +38,10 @@ export function arbitrary(step = 50, width = 1000, height = 500) { }, }; }); - return { type: "FeatureCollection", features: result }; + return { + type: "FeatureCollection", + type: "arbitrary", + coords: "svg", + features: result, + }; } diff --git a/src/grid/countdots.js b/src/grid/countdots.js new file mode 100644 index 0000000..a92b42d --- /dev/null +++ b/src/grid/countdots.js @@ -0,0 +1,42 @@ +import booleanPointInPolygon from "@turf/boolean-point-in-polygon"; + +export function countdots( + opts = { + dots: undefined, // a FeatureCollection containg points + polygons: undefined, // a FeatureCollection containg polygons (grid) + var: undefined, // a field (optional) + } +) { + let polys = opts.polygons.features; + let dots = opts.dots.features; + let count = new Array(polys.length).fill(0); + let nb = dots.length; + let test = new Array(nb).fill(true); + polys.forEach((p, i) => { + dots.forEach((d, j) => { + if (test[j]) { + if (booleanPointInPolygon(d, p)) { + if (opts.var == undefined) { + count[i] = count[i] + 1; + } else { + count[i] = count[i] + parseFloat(d.properties[opts.var]); + } + test[j] = false; + } + } + }); + }); + + // Rebuild grid + let output = polys + .map((d, i) => ({ + type: d.type, + geometry: d.geometry, + properties: { ...d.properties, count: count[i] }, + })) + .filter((d) => d.properties.count !== 0); + + //const endTime = performance.now(); + //const elapsedTime = endTime - startTime; + return { type: "FeatureCollection", features: output }; +} diff --git a/src/grid/diamond.js b/src/grid/diamond.js index 54a4540..ac23ff1 100644 --- a/src/grid/diamond.js +++ b/src/grid/diamond.js @@ -9,9 +9,9 @@ const d3 = Object.assign({}, { range }); * @property {number} [width = 1000] - width of the grid * @property {number} [height = 500] - height of the grid * @example - * geoviz.grid.diamond(50, 1000, 500) + * geoviz.grid.diamond({step: 30}) */ -export function diamond(step = 50, width = 1000, height = 500) { +export function diamond({ step = 50, width = 1000, height = 500 } = {}) { let size = step * Math.sqrt(2); // build grid @@ -44,5 +44,10 @@ export function diamond(step = 50, width = 1000, height = 500) { }, }; }); - return { type: "FeatureCollection", features: result }; + return { + type: "FeatureCollection", + type: "diamond", + coords: "svg", + features: result, + }; } diff --git a/src/grid/dot.js b/src/grid/dot.js index 1fb9619..c493bae 100644 --- a/src/grid/dot.js +++ b/src/grid/dot.js @@ -9,9 +9,9 @@ const d3 = Object.assign({}, { range }); * @property {number} [width = 1000] - width of the grid * @property {number} [height = 500] - height of the grid * @example - * geoviz.grid.dot(50, 1000, 500) + * geoviz.grid.dot({step: 30}) */ -export function dot(step = 30, width = 1000, height = 500) { +export function dot({ step = 30, width = 1000, height = 500 } = {}) { // build grid let y = d3.range(0 + step / 2, height, step).reverse(); let x = d3.range(0 + step / 2, width, step); @@ -30,5 +30,10 @@ export function dot(step = 30, width = 1000, height = 500) { }, }; }); - return { type: "FeatureCollection", features: result }; + return { + type: "FeatureCollection", + type: "dot", + coords: "svg", + features: result, + }; } diff --git a/src/grid/h3.js b/src/grid/h3.js index 48418b6..2956009 100644 --- a/src/grid/h3.js +++ b/src/grid/h3.js @@ -5,27 +5,45 @@ import { cellToBoundary, } from "h3-js"; +import { featureToH3Set, h3SetToFeatureCollection } from "geojson2h3"; +import { rewind as rrewind } from "../tool/rewind"; + /** * @function grid/h3 * @description The `grid.h3` function allows to create a hexbin geoJSON grid in geographical coordinates. * @see {@link https://observablehq.com/@neocartocnrs/regular-grids} * @property {number} [level = 0] - level of the grid. Form 0 (large hexagons) to 4 (small hexagons). See: https://h3geo.org + * @property {object} [domain] - a geoJSON to define an extent + * @property {boolen} [rewind] - to rewind the output * @example - * geoviz.grid.h3(1) + * geoviz.grid.h3({level: 1}) + * geoviz.grid.h3({level: 4, domain: italy}) */ -export function h3(level = 0) { - return { - type: "FeatureCollection", - features: getRes0Cells() - .map((i) => cellToChildren(i, level)) - .flat() - .map((d) => ({ - type: "Feature", - properties: { id: d, pentagon: isPentagon(d) }, - geometry: { - type: "Polygon", - coordinates: [cellToBoundary(d, true).reverse()], - }, - })), - }; +export function h3({ level = 0, domain = undefined, rewind = undefined } = {}) { + let output; + if (domain) { + rewind = rewind !== undefined ? rewind : true; + const hexagons = featureToH3Set(domain, level); + output = h3SetToFeatureCollection(hexagons, (hex) => ({ + value: hex, + })); + } else { + rewind = rewind !== undefined ? rewind : false; + output = { + type: "FeatureCollection", + features: getRes0Cells() + .map((i) => cellToChildren(i, level)) + .flat() + .map((d) => ({ + type: "Feature", + properties: { id: d, pentagon: isPentagon(d) }, + geometry: { + type: "Polygon", + coordinates: [cellToBoundary(d, true).reverse()], + }, + })), + }; + } + + return rewind ? rrewind(output) : output; } diff --git a/src/grid/hexbin.js b/src/grid/hexbin.js index a3855a3..63fbd0f 100644 --- a/src/grid/hexbin.js +++ b/src/grid/hexbin.js @@ -9,9 +9,9 @@ const d3 = Object.assign({}, { range, max }); * @property {number} [width = 1000] - width of the grid * @property {number} [height = 500] - height of the grid * @example - * geoviz.grid.hexbin(50, 1000, 500) + * geoviz.grid.hexbin({step: 30}) */ -export function hexbin(step = 50, width = 1000, height = 500) { +export function hexbin({ step = 50, width = 1000, height = 500 } = {}) { let w = step; let size = w / Math.sqrt(3); let h = 2 * size * (3 / 4); @@ -46,5 +46,10 @@ export function hexbin(step = 50, width = 1000, height = 500) { }, }; }); - return { type: "FeatureCollection", features: result }; + return { + type: "FeatureCollection", + type: "hexbin", + coords: "svg", + features: result, + }; } diff --git a/src/grid/make.js b/src/grid/make.js index d582632..bbd0f87 100644 --- a/src/grid/make.js +++ b/src/grid/make.js @@ -14,39 +14,50 @@ import { h3 } from "./h3.js"; * @property {string} [type = "square"] - Type of grid ("square", "dot", "diamond", "hexbin" (or "hex"), "trangle", "arbitrary" (or "randmon"), "h3" (or "h3geo", "hexgeo", "hexbingeo")) * @property {number} [step = 50] - step of grids (except for "h3" type) * @property {number} [level = 0] - level oh geographical hexbin grids ("h3" type only). Form 0 (large hexagons) to 4 (small hexagons). See: https://h3geo.org + * @property {object} [domain] - a geoJSON to define an extent (h3 only) + * @property {boolen} [rewind] - to rewind the output (h3 only) * @example * // There are several ways to use this function * geoviz.grid.make(svg, { type:"diamond", step:100 }) // where svg is the container * svg.grid.make({ type:"diamond", step:100 }) // where svg is the container */ -export function make(svg, { step = 50, type = "square", level = 0 } = {}) { +export function make( + svg, + { + step = 50, + type = "square", + level = 0, + domain = undefined, + rewind = undefined, + } = {} +) { switch (type) { case "square": - return square(step, svg.width, svg.height); + return square({ step, width: svg.width, height: svg.height }); break; case "arbitrary": case "random": - return arbitrary(step, svg.width, svg.height); + return arbitrary({ step, width: svg.width, height: svg.height }); break; case "dot": - return dot(step, svg.width, svg.height); + return dot({ step, width: svg.width, height: svg.height }); break; case "diamond": - return diamond(step, svg.width, svg.height); + return diamond({ step, width: svg.width, height: svg.height }); break; case "hexbin": case "hex": - return hexbin(step, svg.width, svg.height); + return hexbin({ step, width: svg.width, height: svg.height }); break; case "triangle": - return triangle(step, svg.width, svg.height); + return triangle({ step, width: svg.width, height: svg.height }); break; case "h3": case "h3geo": case "hexgeo": case "hexbingeo": - return h3(level); + return h3({ level, domain, rewind }); break; } } diff --git a/src/grid/square.js b/src/grid/square.js index 86f370e..fd1b01f 100644 --- a/src/grid/square.js +++ b/src/grid/square.js @@ -9,9 +9,9 @@ const d3 = Object.assign({}, { range }); * @property {number} [width = 1000] - width of the grid * @property {number} [height = 500] - height of the grid * @example - * geoviz.grid.square(50, 1000, 500) + * geoviz.grid.square({step: 30}) */ -export function square(step = 50, width = 1000, height = 500) { +export function square({ step = 50, width = 1000, height = 500 } = {}) { // build grid let y = d3.range(0 + step / 2, height, step).reverse(); let x = d3.range(0 + step / 2, width, step); @@ -39,5 +39,10 @@ export function square(step = 50, width = 1000, height = 500) { }, }; }); - return { type: "FeatureCollection", features: result }; + return { + type: "FeatureCollection", + type: "square", + coords: "svg", + features: result, + }; } diff --git a/src/grid/triangle.js b/src/grid/triangle.js index 3d0ca63..d4c1b55 100644 --- a/src/grid/triangle.js +++ b/src/grid/triangle.js @@ -9,9 +9,9 @@ const d3 = Object.assign({}, { range, max }); * @property {number} [width = 1000] - width of the grid * @property {number} [height = 500] - height of the grid * @example - * geoviz.grid.triangle(50, 1000, 500) + * geoviz.grid.triangle({step: 30}) */ -export function triangle(step = 50, width = 1000, height = 500) { +export function triangle({ step = 50, width = 1000, height = 500 } = {}) { let triangletop = (p, size) => { let h = (Math.sqrt(3) / 2) * size; let p1 = [p[0] + size / 2, p[1]]; @@ -60,5 +60,10 @@ export function triangle(step = 50, width = 1000, height = 500) { }, }; }); - return { type: "FeatureCollection", features: result }; + return { + type: "FeatureCollection", + type: "triangle", + coords: "svg", + features: result, + }; } diff --git a/src/index.js b/src/index.js index 52fd438..f96f989 100644 --- a/src/index.js +++ b/src/index.js @@ -103,6 +103,7 @@ import { h3 } from "./grid/h3.js"; import { hexbin } from "./grid/hexbin.js"; import { square } from "./grid/square.js"; import { triangle } from "./grid/triangle.js"; +import { countdots } from "./grid/countdots.js"; export let grid = { make, arbitrary, @@ -112,6 +113,7 @@ export let grid = { hexbin, square, triangle, + countdots, }; // main