mapshaper 0.7.58 → 0.7.59
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.
- package/mapshaper.js +138 -12
- package/package.json +1 -1
- package/www/mapshaper-gui.js +38 -3
- package/www/mapshaper.js +138 -12
package/mapshaper.js
CHANGED
|
@@ -40383,9 +40383,9 @@ ${svg}
|
|
|
40383
40383
|
.option('no-replace', noReplaceOpt);
|
|
40384
40384
|
|
|
40385
40385
|
parser.command('grid')
|
|
40386
|
-
.describe('create a grid of square, hexagonal, rhombus or
|
|
40386
|
+
.describe('create a grid of square, hexagonal, rhombus, triangle or cairo polygons')
|
|
40387
40387
|
.option('type', {
|
|
40388
|
-
describe: 'square, square2, hex, hex2, rhombus, rhombus2, triangle or
|
|
40388
|
+
describe: 'square, square2, hex, hex2, rhombus, rhombus2, triangle, triangle2 or cairo (default is square)'
|
|
40389
40389
|
})
|
|
40390
40390
|
.option('interval', {
|
|
40391
40391
|
describe: 'side length (e.g. 500m, 12km)',
|
|
@@ -40407,6 +40407,10 @@ ${svg}
|
|
|
40407
40407
|
describe: 'scale factor for cells, between 0 and 2',
|
|
40408
40408
|
type: 'number'
|
|
40409
40409
|
})
|
|
40410
|
+
.option('rotate', {
|
|
40411
|
+
describe: 'rotation angle in degrees (default is 0)',
|
|
40412
|
+
type: 'number'
|
|
40413
|
+
})
|
|
40410
40414
|
// .option('bbox', {
|
|
40411
40415
|
// type: 'bbox',
|
|
40412
40416
|
// describe: 'xmin,ymin,xmax,ymax (default is bbox of data)'
|
|
@@ -74555,7 +74559,7 @@ ${svg}
|
|
|
74555
74559
|
cmd.polygonGrid = function(targetLayers, targetDataset, opts) {
|
|
74556
74560
|
requireProjectedDataset(targetDataset);
|
|
74557
74561
|
var params = getGridParams(targetLayers, targetDataset, opts);
|
|
74558
|
-
var gridDataset = makeGridDataset(params); // grid is a new dataset
|
|
74562
|
+
var gridDataset = makeGridDataset(params, opts); // grid is a new dataset
|
|
74559
74563
|
gridDataset.info = copyDatasetInfo(targetDataset.info);
|
|
74560
74564
|
setOutputLayerName(gridDataset.layers[0], null, 'grid', opts);
|
|
74561
74565
|
if (opts.debug) gridDataset.layers.push(cmd.pointGrid2(targetLayers, targetDataset, opts));
|
|
@@ -74619,35 +74623,67 @@ ${svg}
|
|
|
74619
74623
|
|
|
74620
74624
|
function makeGridDataset(params, opts) {
|
|
74621
74625
|
var geojson, dataset;
|
|
74626
|
+
var rotation = opts.rotate || 0;
|
|
74627
|
+
var genParams = rotation ? getRotatedGridParams(params, rotation) : params;
|
|
74622
74628
|
if (params.type == 'square') {
|
|
74623
|
-
geojson = getSquareGridGeoJSON(getSquareGridCoordinates(
|
|
74629
|
+
geojson = getSquareGridGeoJSON(getSquareGridCoordinates(genParams));
|
|
74624
74630
|
} else if (params.type == 'square2') {
|
|
74625
|
-
geojson = getRotatedSquareGridGeoJSON(
|
|
74631
|
+
geojson = getRotatedSquareGridGeoJSON(genParams);
|
|
74626
74632
|
} else if (params.type == 'hex') {
|
|
74627
|
-
geojson = getHexGridGeoJSON(getHexGridCoordinates(
|
|
74633
|
+
geojson = getHexGridGeoJSON(getHexGridCoordinates(genParams));
|
|
74628
74634
|
} else if (params.type == 'hex2') {
|
|
74629
74635
|
// use rotated grid
|
|
74630
|
-
geojson = getHexGridGeoJSON(getHexGridCoordinates(swapGridParams(
|
|
74636
|
+
geojson = getHexGridGeoJSON(getHexGridCoordinates(swapGridParams(genParams)));
|
|
74631
74637
|
swapPolygonCoords(geojson);
|
|
74632
74638
|
} else if (params.type == 'rhombus') {
|
|
74633
|
-
geojson = getRhombusGridGeoJSON(
|
|
74639
|
+
geojson = getRhombusGridGeoJSON(genParams, false);
|
|
74634
74640
|
} else if (params.type == 'rhombus2') {
|
|
74635
|
-
geojson = getRhombusGridGeoJSON(
|
|
74641
|
+
geojson = getRhombusGridGeoJSON(genParams, true);
|
|
74636
74642
|
} else if (params.type == 'triangle') {
|
|
74637
|
-
geojson = getTriangleGridGeoJSON(
|
|
74643
|
+
geojson = getTriangleGridGeoJSON(genParams, false);
|
|
74638
74644
|
} else if (params.type == 'triangle2') {
|
|
74639
|
-
geojson = getTriangleGridGeoJSON(
|
|
74645
|
+
geojson = getTriangleGridGeoJSON(genParams, true);
|
|
74646
|
+
} else if (params.type == 'cairo') {
|
|
74647
|
+
geojson = getCairoGridGeoJSON(genParams);
|
|
74640
74648
|
} else {
|
|
74641
74649
|
stop$1('Unsupported grid type');
|
|
74642
74650
|
}
|
|
74643
74651
|
scaleGridCells(geojson, params.cellScale);
|
|
74644
74652
|
alignGridToBounds(geojson, params.bbox);
|
|
74653
|
+
if (rotation) {
|
|
74654
|
+
rotateGridCells(geojson, rotation, params.bbox);
|
|
74655
|
+
}
|
|
74645
74656
|
cullGridCells(geojson, params.bbox);
|
|
74646
74657
|
dataset = importGeoJSON(geojson, {});
|
|
74647
74658
|
buildTopology(dataset);
|
|
74648
74659
|
return dataset;
|
|
74649
74660
|
}
|
|
74650
74661
|
|
|
74662
|
+
// Expand the generation rectangle so that, after rotation around the shared
|
|
74663
|
+
// center, the grid still covers the original target bbox.
|
|
74664
|
+
function getRotatedGridParams(params, rotation) {
|
|
74665
|
+
var angle = rotation * Math.PI / 180;
|
|
74666
|
+
var cos = Math.abs(Math.cos(angle));
|
|
74667
|
+
var sin = Math.abs(Math.sin(angle));
|
|
74668
|
+
return utils.defaults({
|
|
74669
|
+
width: params.width * cos + params.height * sin,
|
|
74670
|
+
height: params.width * sin + params.height * cos
|
|
74671
|
+
}, params);
|
|
74672
|
+
}
|
|
74673
|
+
|
|
74674
|
+
function rotateGridCells(geojson, rotation, bbox) {
|
|
74675
|
+
var cx = (bbox[0] + bbox[2]) / 2;
|
|
74676
|
+
var cy = (bbox[1] + bbox[3]) / 2;
|
|
74677
|
+
var transform = getAffineTransform(rotation, 1, [0, 0], [cx, cy]);
|
|
74678
|
+
geojson.geometries.forEach(function(geom) {
|
|
74679
|
+
if (geom.type == 'Polygon') {
|
|
74680
|
+
geom.coordinates[0] = geom.coordinates[0].map(function(xy) {
|
|
74681
|
+
return transform(xy[0], xy[1]);
|
|
74682
|
+
});
|
|
74683
|
+
}
|
|
74684
|
+
});
|
|
74685
|
+
}
|
|
74686
|
+
|
|
74651
74687
|
function swapGridParams(params) {
|
|
74652
74688
|
var bbox = params.bbox;
|
|
74653
74689
|
return utils.defaults({
|
|
@@ -74729,6 +74765,7 @@ ${svg}
|
|
|
74729
74765
|
if (type == 'hex' || type == 'hex2') return 3 * Math.sqrt(3) / 2;
|
|
74730
74766
|
if (type == 'rhombus' || type == 'rhombus2') return Math.sqrt(3) / 2;
|
|
74731
74767
|
if (type == 'triangle' || type == 'triangle2') return Math.sqrt(3) / 4;
|
|
74768
|
+
if (type == 'cairo') return (4 + Math.sqrt(7)) / 4;
|
|
74732
74769
|
stop$1('Unsupported grid type');
|
|
74733
74770
|
}
|
|
74734
74771
|
|
|
@@ -74882,6 +74919,95 @@ ${svg}
|
|
|
74882
74919
|
return geojson;
|
|
74883
74920
|
}
|
|
74884
74921
|
|
|
74922
|
+
// Equilateral type-4 Cairo tessellation. Four-valent (90°) vertices sit on a
|
|
74923
|
+
// square lattice of spacing S = interval * (√7+1)/2; each lattice square
|
|
74924
|
+
// contains a central bar of length `interval`, alternating horizontal and
|
|
74925
|
+
// vertical. Two pentagons share each bar.
|
|
74926
|
+
//
|
|
74927
|
+
// Vertices are computed once and reused. Algebraically equal expressions
|
|
74928
|
+
// such as i*S and (i-1)*S + S are not bit-identical, and independently
|
|
74929
|
+
// computed copies of the same 4-way junction become T-intersections.
|
|
74930
|
+
function getCairoGridGeoJSON(params) {
|
|
74931
|
+
var side = params.interval;
|
|
74932
|
+
var spacing = side * (Math.sqrt(7) + 1) / 2;
|
|
74933
|
+
var halfBar = side / 2;
|
|
74934
|
+
var iMin = -1;
|
|
74935
|
+
var jMin = -1;
|
|
74936
|
+
var iMax = Math.ceil(params.width / spacing) + 1;
|
|
74937
|
+
var jMax = Math.ceil(params.height / spacing) + 1;
|
|
74938
|
+
var lattice = [];
|
|
74939
|
+
var bars = [];
|
|
74940
|
+
var geometries = [];
|
|
74941
|
+
var i, j, y, cx, cy;
|
|
74942
|
+
|
|
74943
|
+
for (j = jMin - 1; j <= jMax + 1; j++) {
|
|
74944
|
+
y = j * spacing;
|
|
74945
|
+
lattice[j] = [];
|
|
74946
|
+
for (i = iMin - 1; i <= iMax + 1; i++) {
|
|
74947
|
+
lattice[j][i] = [i * spacing, y];
|
|
74948
|
+
}
|
|
74949
|
+
}
|
|
74950
|
+
|
|
74951
|
+
for (j = jMin - 1; j < jMax + 1; j++) {
|
|
74952
|
+
bars[j] = [];
|
|
74953
|
+
for (i = iMin - 1; i < iMax + 1; i++) {
|
|
74954
|
+
cx = (i + 0.5) * spacing;
|
|
74955
|
+
cy = (j + 0.5) * spacing;
|
|
74956
|
+
bars[j][i] = (i + j) % 2 === 0 ? {
|
|
74957
|
+
a: [cx - halfBar, cy],
|
|
74958
|
+
b: [cx + halfBar, cy]
|
|
74959
|
+
} : {
|
|
74960
|
+
a: [cx, cy - halfBar],
|
|
74961
|
+
b: [cx, cy + halfBar]
|
|
74962
|
+
};
|
|
74963
|
+
}
|
|
74964
|
+
}
|
|
74965
|
+
|
|
74966
|
+
for (j = jMin; j < jMax; j++) {
|
|
74967
|
+
for (i = iMin; i < iMax; i++) {
|
|
74968
|
+
if ((i + j) % 2 === 0) {
|
|
74969
|
+
appendCairoPentagon(geometries, [
|
|
74970
|
+
lattice[j][i],
|
|
74971
|
+
bars[j - 1][i].b,
|
|
74972
|
+
lattice[j][i + 1],
|
|
74973
|
+
bars[j][i].b,
|
|
74974
|
+
bars[j][i].a
|
|
74975
|
+
]);
|
|
74976
|
+
appendCairoPentagon(geometries, [
|
|
74977
|
+
lattice[j + 1][i],
|
|
74978
|
+
bars[j][i].a,
|
|
74979
|
+
bars[j][i].b,
|
|
74980
|
+
lattice[j + 1][i + 1],
|
|
74981
|
+
bars[j + 1][i].a
|
|
74982
|
+
]);
|
|
74983
|
+
} else {
|
|
74984
|
+
appendCairoPentagon(geometries, [
|
|
74985
|
+
lattice[j][i],
|
|
74986
|
+
bars[j][i].a,
|
|
74987
|
+
bars[j][i].b,
|
|
74988
|
+
lattice[j + 1][i],
|
|
74989
|
+
bars[j][i - 1].b
|
|
74990
|
+
]);
|
|
74991
|
+
appendCairoPentagon(geometries, [
|
|
74992
|
+
lattice[j][i + 1],
|
|
74993
|
+
bars[j][i + 1].a,
|
|
74994
|
+
lattice[j + 1][i + 1],
|
|
74995
|
+
bars[j][i].b,
|
|
74996
|
+
bars[j][i].a
|
|
74997
|
+
]);
|
|
74998
|
+
}
|
|
74999
|
+
}
|
|
75000
|
+
}
|
|
75001
|
+
return {type: 'GeometryCollection', geometries: geometries};
|
|
75002
|
+
}
|
|
75003
|
+
|
|
75004
|
+
function appendCairoPentagon(geometries, verts) {
|
|
75005
|
+
geometries.push({
|
|
75006
|
+
type: 'Polygon',
|
|
75007
|
+
coordinates: [verts.concat([verts[0]])]
|
|
75008
|
+
});
|
|
75009
|
+
}
|
|
75010
|
+
|
|
74885
75011
|
function subdivideHexagon(coords) {
|
|
74886
75012
|
var center = getPolygonRingCenter(coords);
|
|
74887
75013
|
return [
|
|
@@ -80084,7 +80210,7 @@ ${svg}
|
|
|
80084
80210
|
return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
|
|
80085
80211
|
}
|
|
80086
80212
|
|
|
80087
|
-
var version = "0.7.
|
|
80213
|
+
var version = "0.7.59";
|
|
80088
80214
|
|
|
80089
80215
|
// Parse command line args into commands and run them
|
|
80090
80216
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|
package/package.json
CHANGED
package/www/mapshaper-gui.js
CHANGED
|
@@ -11109,6 +11109,8 @@
|
|
|
11109
11109
|
|
|
11110
11110
|
var openMenu;
|
|
11111
11111
|
var openMenuId;
|
|
11112
|
+
// One menu element per parent element, created on demand.
|
|
11113
|
+
var menus = new WeakMap();
|
|
11112
11114
|
// Prefixes shown to the left of a menu item. The checkmark is what a copyable
|
|
11113
11115
|
// item shows after it has been copied.
|
|
11114
11116
|
var BULLET = '• ';
|
|
@@ -11132,14 +11134,26 @@
|
|
|
11132
11134
|
}
|
|
11133
11135
|
}
|
|
11134
11136
|
|
|
11137
|
+
// Menus are re-used, rather than created per click: a discarded menu stays in
|
|
11138
|
+
// the DOM, and an empty one is a 10px sliver at the bottom of the page, which
|
|
11139
|
+
// is enough to make the page scrollable (see the note in ContextMenu()).
|
|
11140
|
+
function getContextMenu(parentArg) {
|
|
11141
|
+
var parent = parentArg || document.querySelector('body');
|
|
11142
|
+
var menu = menus.get(parent);
|
|
11143
|
+
if (!menu) {
|
|
11144
|
+
menu = new ContextMenu(parent);
|
|
11145
|
+
menus.set(parent, menu);
|
|
11146
|
+
}
|
|
11147
|
+
return menu;
|
|
11148
|
+
}
|
|
11149
|
+
|
|
11135
11150
|
function openContextMenu(e, lyr, parent) {
|
|
11136
|
-
var menu = new ContextMenu(parent);
|
|
11137
11151
|
if (e.contextMenuId && e.contextMenuId == openMenuId) {
|
|
11138
11152
|
closeOpenMenu(true);
|
|
11139
11153
|
return;
|
|
11140
11154
|
}
|
|
11141
11155
|
closeOpenMenu(true);
|
|
11142
|
-
|
|
11156
|
+
getContextMenu(parent).open(e, lyr);
|
|
11143
11157
|
openMenuId = e.contextMenuId || null;
|
|
11144
11158
|
}
|
|
11145
11159
|
|
|
@@ -11151,6 +11165,14 @@
|
|
|
11151
11165
|
var _open = false;
|
|
11152
11166
|
var _openCount = 0;
|
|
11153
11167
|
|
|
11168
|
+
// The menu is hidden until it is opened. A visible empty menu is 10px of
|
|
11169
|
+
// padding and border at the end of the page, and the page is exactly as tall
|
|
11170
|
+
// as the window, so those 10px make the document scrollable. The body is
|
|
11171
|
+
// overflow:hidden, but that only stops the user from scrolling: the browser
|
|
11172
|
+
// still scrolls the page to reveal a focused element or a caret, and once it
|
|
11173
|
+
// does, the whole UI stays shifted up (mbloch/mapshaper#702).
|
|
11174
|
+
menu.hide();
|
|
11175
|
+
|
|
11154
11176
|
this.isOpen = function() {
|
|
11155
11177
|
return _open;
|
|
11156
11178
|
};
|
|
@@ -11292,10 +11314,23 @@
|
|
|
11292
11314
|
}
|
|
11293
11315
|
menu.css('top', (e.pageY - offs.top - 15) + 'px');
|
|
11294
11316
|
menu.show();
|
|
11317
|
+
keepInsideWindow();
|
|
11295
11318
|
|
|
11296
11319
|
_open = true;
|
|
11297
11320
|
_openCount++;
|
|
11298
11321
|
|
|
11322
|
+
// A menu opened near the bottom of the window would otherwise hang off the
|
|
11323
|
+
// screen, out of reach and adding to the page's scrollable overflow.
|
|
11324
|
+
function keepInsideWindow() {
|
|
11325
|
+
var pad = 6;
|
|
11326
|
+
var rect = menu.node().getBoundingClientRect();
|
|
11327
|
+
var overflow = rect.bottom + pad - document.documentElement.clientHeight;
|
|
11328
|
+
var shift = Math.min(overflow, rect.top - pad);
|
|
11329
|
+
if (shift > 0) {
|
|
11330
|
+
menu.css('top', (parseFloat(menu.node().style.top) - shift) + 'px');
|
|
11331
|
+
}
|
|
11332
|
+
}
|
|
11333
|
+
|
|
11299
11334
|
function getParentOffset() { // crossbrowser version
|
|
11300
11335
|
if (parent == body) {
|
|
11301
11336
|
return {top: 0, left: 0};
|
|
@@ -25418,7 +25453,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
25418
25453
|
gui.messages = new MessageControl(gui);
|
|
25419
25454
|
gui.basemap = new Basemap(gui);
|
|
25420
25455
|
gui.session = new SessionHistory(gui);
|
|
25421
|
-
gui.contextMenu =
|
|
25456
|
+
gui.contextMenu = getContextMenu();
|
|
25422
25457
|
gui.undo = new Undo(gui);
|
|
25423
25458
|
gui.map = new MshpMap(gui);
|
|
25424
25459
|
// gui.project = new ProjectOptions(gui);
|
package/www/mapshaper.js
CHANGED
|
@@ -40383,9 +40383,9 @@ ${svg}
|
|
|
40383
40383
|
.option('no-replace', noReplaceOpt);
|
|
40384
40384
|
|
|
40385
40385
|
parser.command('grid')
|
|
40386
|
-
.describe('create a grid of square, hexagonal, rhombus or
|
|
40386
|
+
.describe('create a grid of square, hexagonal, rhombus, triangle or cairo polygons')
|
|
40387
40387
|
.option('type', {
|
|
40388
|
-
describe: 'square, square2, hex, hex2, rhombus, rhombus2, triangle or
|
|
40388
|
+
describe: 'square, square2, hex, hex2, rhombus, rhombus2, triangle, triangle2 or cairo (default is square)'
|
|
40389
40389
|
})
|
|
40390
40390
|
.option('interval', {
|
|
40391
40391
|
describe: 'side length (e.g. 500m, 12km)',
|
|
@@ -40407,6 +40407,10 @@ ${svg}
|
|
|
40407
40407
|
describe: 'scale factor for cells, between 0 and 2',
|
|
40408
40408
|
type: 'number'
|
|
40409
40409
|
})
|
|
40410
|
+
.option('rotate', {
|
|
40411
|
+
describe: 'rotation angle in degrees (default is 0)',
|
|
40412
|
+
type: 'number'
|
|
40413
|
+
})
|
|
40410
40414
|
// .option('bbox', {
|
|
40411
40415
|
// type: 'bbox',
|
|
40412
40416
|
// describe: 'xmin,ymin,xmax,ymax (default is bbox of data)'
|
|
@@ -74555,7 +74559,7 @@ ${svg}
|
|
|
74555
74559
|
cmd.polygonGrid = function(targetLayers, targetDataset, opts) {
|
|
74556
74560
|
requireProjectedDataset(targetDataset);
|
|
74557
74561
|
var params = getGridParams(targetLayers, targetDataset, opts);
|
|
74558
|
-
var gridDataset = makeGridDataset(params); // grid is a new dataset
|
|
74562
|
+
var gridDataset = makeGridDataset(params, opts); // grid is a new dataset
|
|
74559
74563
|
gridDataset.info = copyDatasetInfo(targetDataset.info);
|
|
74560
74564
|
setOutputLayerName(gridDataset.layers[0], null, 'grid', opts);
|
|
74561
74565
|
if (opts.debug) gridDataset.layers.push(cmd.pointGrid2(targetLayers, targetDataset, opts));
|
|
@@ -74619,35 +74623,67 @@ ${svg}
|
|
|
74619
74623
|
|
|
74620
74624
|
function makeGridDataset(params, opts) {
|
|
74621
74625
|
var geojson, dataset;
|
|
74626
|
+
var rotation = opts.rotate || 0;
|
|
74627
|
+
var genParams = rotation ? getRotatedGridParams(params, rotation) : params;
|
|
74622
74628
|
if (params.type == 'square') {
|
|
74623
|
-
geojson = getSquareGridGeoJSON(getSquareGridCoordinates(
|
|
74629
|
+
geojson = getSquareGridGeoJSON(getSquareGridCoordinates(genParams));
|
|
74624
74630
|
} else if (params.type == 'square2') {
|
|
74625
|
-
geojson = getRotatedSquareGridGeoJSON(
|
|
74631
|
+
geojson = getRotatedSquareGridGeoJSON(genParams);
|
|
74626
74632
|
} else if (params.type == 'hex') {
|
|
74627
|
-
geojson = getHexGridGeoJSON(getHexGridCoordinates(
|
|
74633
|
+
geojson = getHexGridGeoJSON(getHexGridCoordinates(genParams));
|
|
74628
74634
|
} else if (params.type == 'hex2') {
|
|
74629
74635
|
// use rotated grid
|
|
74630
|
-
geojson = getHexGridGeoJSON(getHexGridCoordinates(swapGridParams(
|
|
74636
|
+
geojson = getHexGridGeoJSON(getHexGridCoordinates(swapGridParams(genParams)));
|
|
74631
74637
|
swapPolygonCoords(geojson);
|
|
74632
74638
|
} else if (params.type == 'rhombus') {
|
|
74633
|
-
geojson = getRhombusGridGeoJSON(
|
|
74639
|
+
geojson = getRhombusGridGeoJSON(genParams, false);
|
|
74634
74640
|
} else if (params.type == 'rhombus2') {
|
|
74635
|
-
geojson = getRhombusGridGeoJSON(
|
|
74641
|
+
geojson = getRhombusGridGeoJSON(genParams, true);
|
|
74636
74642
|
} else if (params.type == 'triangle') {
|
|
74637
|
-
geojson = getTriangleGridGeoJSON(
|
|
74643
|
+
geojson = getTriangleGridGeoJSON(genParams, false);
|
|
74638
74644
|
} else if (params.type == 'triangle2') {
|
|
74639
|
-
geojson = getTriangleGridGeoJSON(
|
|
74645
|
+
geojson = getTriangleGridGeoJSON(genParams, true);
|
|
74646
|
+
} else if (params.type == 'cairo') {
|
|
74647
|
+
geojson = getCairoGridGeoJSON(genParams);
|
|
74640
74648
|
} else {
|
|
74641
74649
|
stop$1('Unsupported grid type');
|
|
74642
74650
|
}
|
|
74643
74651
|
scaleGridCells(geojson, params.cellScale);
|
|
74644
74652
|
alignGridToBounds(geojson, params.bbox);
|
|
74653
|
+
if (rotation) {
|
|
74654
|
+
rotateGridCells(geojson, rotation, params.bbox);
|
|
74655
|
+
}
|
|
74645
74656
|
cullGridCells(geojson, params.bbox);
|
|
74646
74657
|
dataset = importGeoJSON(geojson, {});
|
|
74647
74658
|
buildTopology(dataset);
|
|
74648
74659
|
return dataset;
|
|
74649
74660
|
}
|
|
74650
74661
|
|
|
74662
|
+
// Expand the generation rectangle so that, after rotation around the shared
|
|
74663
|
+
// center, the grid still covers the original target bbox.
|
|
74664
|
+
function getRotatedGridParams(params, rotation) {
|
|
74665
|
+
var angle = rotation * Math.PI / 180;
|
|
74666
|
+
var cos = Math.abs(Math.cos(angle));
|
|
74667
|
+
var sin = Math.abs(Math.sin(angle));
|
|
74668
|
+
return utils.defaults({
|
|
74669
|
+
width: params.width * cos + params.height * sin,
|
|
74670
|
+
height: params.width * sin + params.height * cos
|
|
74671
|
+
}, params);
|
|
74672
|
+
}
|
|
74673
|
+
|
|
74674
|
+
function rotateGridCells(geojson, rotation, bbox) {
|
|
74675
|
+
var cx = (bbox[0] + bbox[2]) / 2;
|
|
74676
|
+
var cy = (bbox[1] + bbox[3]) / 2;
|
|
74677
|
+
var transform = getAffineTransform(rotation, 1, [0, 0], [cx, cy]);
|
|
74678
|
+
geojson.geometries.forEach(function(geom) {
|
|
74679
|
+
if (geom.type == 'Polygon') {
|
|
74680
|
+
geom.coordinates[0] = geom.coordinates[0].map(function(xy) {
|
|
74681
|
+
return transform(xy[0], xy[1]);
|
|
74682
|
+
});
|
|
74683
|
+
}
|
|
74684
|
+
});
|
|
74685
|
+
}
|
|
74686
|
+
|
|
74651
74687
|
function swapGridParams(params) {
|
|
74652
74688
|
var bbox = params.bbox;
|
|
74653
74689
|
return utils.defaults({
|
|
@@ -74729,6 +74765,7 @@ ${svg}
|
|
|
74729
74765
|
if (type == 'hex' || type == 'hex2') return 3 * Math.sqrt(3) / 2;
|
|
74730
74766
|
if (type == 'rhombus' || type == 'rhombus2') return Math.sqrt(3) / 2;
|
|
74731
74767
|
if (type == 'triangle' || type == 'triangle2') return Math.sqrt(3) / 4;
|
|
74768
|
+
if (type == 'cairo') return (4 + Math.sqrt(7)) / 4;
|
|
74732
74769
|
stop$1('Unsupported grid type');
|
|
74733
74770
|
}
|
|
74734
74771
|
|
|
@@ -74882,6 +74919,95 @@ ${svg}
|
|
|
74882
74919
|
return geojson;
|
|
74883
74920
|
}
|
|
74884
74921
|
|
|
74922
|
+
// Equilateral type-4 Cairo tessellation. Four-valent (90°) vertices sit on a
|
|
74923
|
+
// square lattice of spacing S = interval * (√7+1)/2; each lattice square
|
|
74924
|
+
// contains a central bar of length `interval`, alternating horizontal and
|
|
74925
|
+
// vertical. Two pentagons share each bar.
|
|
74926
|
+
//
|
|
74927
|
+
// Vertices are computed once and reused. Algebraically equal expressions
|
|
74928
|
+
// such as i*S and (i-1)*S + S are not bit-identical, and independently
|
|
74929
|
+
// computed copies of the same 4-way junction become T-intersections.
|
|
74930
|
+
function getCairoGridGeoJSON(params) {
|
|
74931
|
+
var side = params.interval;
|
|
74932
|
+
var spacing = side * (Math.sqrt(7) + 1) / 2;
|
|
74933
|
+
var halfBar = side / 2;
|
|
74934
|
+
var iMin = -1;
|
|
74935
|
+
var jMin = -1;
|
|
74936
|
+
var iMax = Math.ceil(params.width / spacing) + 1;
|
|
74937
|
+
var jMax = Math.ceil(params.height / spacing) + 1;
|
|
74938
|
+
var lattice = [];
|
|
74939
|
+
var bars = [];
|
|
74940
|
+
var geometries = [];
|
|
74941
|
+
var i, j, y, cx, cy;
|
|
74942
|
+
|
|
74943
|
+
for (j = jMin - 1; j <= jMax + 1; j++) {
|
|
74944
|
+
y = j * spacing;
|
|
74945
|
+
lattice[j] = [];
|
|
74946
|
+
for (i = iMin - 1; i <= iMax + 1; i++) {
|
|
74947
|
+
lattice[j][i] = [i * spacing, y];
|
|
74948
|
+
}
|
|
74949
|
+
}
|
|
74950
|
+
|
|
74951
|
+
for (j = jMin - 1; j < jMax + 1; j++) {
|
|
74952
|
+
bars[j] = [];
|
|
74953
|
+
for (i = iMin - 1; i < iMax + 1; i++) {
|
|
74954
|
+
cx = (i + 0.5) * spacing;
|
|
74955
|
+
cy = (j + 0.5) * spacing;
|
|
74956
|
+
bars[j][i] = (i + j) % 2 === 0 ? {
|
|
74957
|
+
a: [cx - halfBar, cy],
|
|
74958
|
+
b: [cx + halfBar, cy]
|
|
74959
|
+
} : {
|
|
74960
|
+
a: [cx, cy - halfBar],
|
|
74961
|
+
b: [cx, cy + halfBar]
|
|
74962
|
+
};
|
|
74963
|
+
}
|
|
74964
|
+
}
|
|
74965
|
+
|
|
74966
|
+
for (j = jMin; j < jMax; j++) {
|
|
74967
|
+
for (i = iMin; i < iMax; i++) {
|
|
74968
|
+
if ((i + j) % 2 === 0) {
|
|
74969
|
+
appendCairoPentagon(geometries, [
|
|
74970
|
+
lattice[j][i],
|
|
74971
|
+
bars[j - 1][i].b,
|
|
74972
|
+
lattice[j][i + 1],
|
|
74973
|
+
bars[j][i].b,
|
|
74974
|
+
bars[j][i].a
|
|
74975
|
+
]);
|
|
74976
|
+
appendCairoPentagon(geometries, [
|
|
74977
|
+
lattice[j + 1][i],
|
|
74978
|
+
bars[j][i].a,
|
|
74979
|
+
bars[j][i].b,
|
|
74980
|
+
lattice[j + 1][i + 1],
|
|
74981
|
+
bars[j + 1][i].a
|
|
74982
|
+
]);
|
|
74983
|
+
} else {
|
|
74984
|
+
appendCairoPentagon(geometries, [
|
|
74985
|
+
lattice[j][i],
|
|
74986
|
+
bars[j][i].a,
|
|
74987
|
+
bars[j][i].b,
|
|
74988
|
+
lattice[j + 1][i],
|
|
74989
|
+
bars[j][i - 1].b
|
|
74990
|
+
]);
|
|
74991
|
+
appendCairoPentagon(geometries, [
|
|
74992
|
+
lattice[j][i + 1],
|
|
74993
|
+
bars[j][i + 1].a,
|
|
74994
|
+
lattice[j + 1][i + 1],
|
|
74995
|
+
bars[j][i].b,
|
|
74996
|
+
bars[j][i].a
|
|
74997
|
+
]);
|
|
74998
|
+
}
|
|
74999
|
+
}
|
|
75000
|
+
}
|
|
75001
|
+
return {type: 'GeometryCollection', geometries: geometries};
|
|
75002
|
+
}
|
|
75003
|
+
|
|
75004
|
+
function appendCairoPentagon(geometries, verts) {
|
|
75005
|
+
geometries.push({
|
|
75006
|
+
type: 'Polygon',
|
|
75007
|
+
coordinates: [verts.concat([verts[0]])]
|
|
75008
|
+
});
|
|
75009
|
+
}
|
|
75010
|
+
|
|
74885
75011
|
function subdivideHexagon(coords) {
|
|
74886
75012
|
var center = getPolygonRingCenter(coords);
|
|
74887
75013
|
return [
|
|
@@ -80084,7 +80210,7 @@ ${svg}
|
|
|
80084
80210
|
return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
|
|
80085
80211
|
}
|
|
80086
80212
|
|
|
80087
|
-
var version = "0.7.
|
|
80213
|
+
var version = "0.7.59";
|
|
80088
80214
|
|
|
80089
80215
|
// Parse command line args into commands and run them
|
|
80090
80216
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|