mapshaper 0.5.82 → 0.5.86
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/CHANGELOG.md +13 -0
- package/mapshaper.js +396 -190
- package/package.json +1 -1
- package/www/mapshaper-gui.js +2125 -293
- package/www/mapshaper.js +396 -190
package/mapshaper.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
|
|
3
|
-
var VERSION = "0.5.
|
|
3
|
+
var VERSION = "0.5.85";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
var utils = /*#__PURE__*/Object.freeze({
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
get isFiniteNumber () { return isFiniteNumber; },
|
|
17
17
|
get isNonNegNumber () { return isNonNegNumber; },
|
|
18
18
|
get isInteger () { return isInteger; },
|
|
19
|
+
get isEven () { return isEven; },
|
|
20
|
+
get isOdd () { return isOdd; },
|
|
19
21
|
get isString () { return isString; },
|
|
20
22
|
get isDate () { return isDate; },
|
|
21
23
|
get isBoolean () { return isBoolean; },
|
|
@@ -149,6 +151,14 @@
|
|
|
149
151
|
return isNumber(obj) && ((obj | 0) === obj);
|
|
150
152
|
}
|
|
151
153
|
|
|
154
|
+
function isEven(obj) {
|
|
155
|
+
return (obj % 2) === 0;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function isOdd(obj) {
|
|
159
|
+
return (obj % 2) === 1;
|
|
160
|
+
}
|
|
161
|
+
|
|
152
162
|
function isString(obj) {
|
|
153
163
|
return obj != null && obj.toString === String.prototype.toString;
|
|
154
164
|
// TODO: replace w/ something better.
|
|
@@ -13698,16 +13708,21 @@
|
|
|
13698
13708
|
length: 'number', // e.g. arrow length
|
|
13699
13709
|
rotation: 'number',
|
|
13700
13710
|
radius: 'number',
|
|
13701
|
-
|
|
13702
|
-
|
|
13703
|
-
|
|
13704
|
-
|
|
13705
|
-
|
|
13706
|
-
|
|
13707
|
-
|
|
13708
|
-
'
|
|
13709
|
-
'
|
|
13710
|
-
'
|
|
13711
|
+
radii: null, // string, parsed by function
|
|
13712
|
+
flipped: 'boolean',
|
|
13713
|
+
rotated: 'boolean',
|
|
13714
|
+
direction: 'number',
|
|
13715
|
+
sides: 'number', // polygons and stars
|
|
13716
|
+
points: 'number', // polygons and stars
|
|
13717
|
+
anchor: null, // arrows; takes start, middle, end
|
|
13718
|
+
'head-angle': 'number',
|
|
13719
|
+
'head-width': 'number',
|
|
13720
|
+
'head-length': 'number',
|
|
13721
|
+
'stem-width': 'number',
|
|
13722
|
+
'stem-curve': 'number', // degrees of arc
|
|
13723
|
+
'stem-taper': 'number',
|
|
13724
|
+
'stem-length': 'number',
|
|
13725
|
+
'min-stem-ratio': 'number',
|
|
13711
13726
|
'arrow-scaling': 'number',
|
|
13712
13727
|
effect: null // e.g. "fade"
|
|
13713
13728
|
}, stylePropertyTypes);
|
|
@@ -13743,6 +13758,7 @@
|
|
|
13743
13758
|
function getSymbolDataAccessor(lyr, opts) {
|
|
13744
13759
|
var functions = {};
|
|
13745
13760
|
var properties = [];
|
|
13761
|
+
var fields = lyr.data ? lyr.data.getFields() : [];
|
|
13746
13762
|
|
|
13747
13763
|
Object.keys(opts).forEach(function(optName) {
|
|
13748
13764
|
var svgName = optName.replace(/_/g, '-');
|
|
@@ -13754,6 +13770,8 @@
|
|
|
13754
13770
|
properties.push(svgName);
|
|
13755
13771
|
});
|
|
13756
13772
|
|
|
13773
|
+
// TODO: consider applying values of existing fields with names of symbol properties
|
|
13774
|
+
|
|
13757
13775
|
return function(id) {
|
|
13758
13776
|
var d = {}, name;
|
|
13759
13777
|
for (var i=0; i<properties.length; i++) {
|
|
@@ -13769,8 +13787,9 @@
|
|
|
13769
13787
|
// * ???
|
|
13770
13788
|
//
|
|
13771
13789
|
function mightBeExpression(str, fields) {
|
|
13790
|
+
fields = fields || [];
|
|
13772
13791
|
if (fields.indexOf(str.trim()) > -1) return true;
|
|
13773
|
-
return /[(){}
|
|
13792
|
+
return /[(){}./*?:&|=\[+-]/.test(str);
|
|
13774
13793
|
}
|
|
13775
13794
|
|
|
13776
13795
|
function getSymbolPropertyAccessor(val, svgName, lyr) {
|
|
@@ -13791,6 +13810,7 @@
|
|
|
13791
13810
|
// treating the string as a literal value
|
|
13792
13811
|
literalVal = strVal;
|
|
13793
13812
|
}
|
|
13813
|
+
// console.log("literalVal:", mightBeExpression(strVal, fields), strVal, fields)
|
|
13794
13814
|
if (accessor) return accessor;
|
|
13795
13815
|
if (literalVal !== null) return function(id) {return literalVal;};
|
|
13796
13816
|
stop('Unexpected value for', svgName + ':', strVal);
|
|
@@ -13824,6 +13844,8 @@
|
|
|
13824
13844
|
val = isDashArray(strVal) ? strVal : null;
|
|
13825
13845
|
} else if (type == 'pattern') {
|
|
13826
13846
|
val = isPattern(strVal) ? strVal : null;
|
|
13847
|
+
} else if (type == 'boolean') {
|
|
13848
|
+
val = parseBoolean(strVal);
|
|
13827
13849
|
}
|
|
13828
13850
|
// else {
|
|
13829
13851
|
// // unknown type -- assume literal value
|
|
@@ -13844,10 +13866,17 @@
|
|
|
13844
13866
|
return /^( ?[_a-z][-_a-z0-9]*\b)+$/i.test(str);
|
|
13845
13867
|
}
|
|
13846
13868
|
|
|
13869
|
+
|
|
13847
13870
|
function isSvgNumber(o) {
|
|
13848
13871
|
return utils.isFiniteNumber(o) || utils.isString(o) && /^-?[.0-9]+$/.test(o);
|
|
13849
13872
|
}
|
|
13850
13873
|
|
|
13874
|
+
function parseBoolean(o) {
|
|
13875
|
+
if (o === true || o === 'true') return true;
|
|
13876
|
+
if (o === false || o === 'false') return false;
|
|
13877
|
+
return null;
|
|
13878
|
+
}
|
|
13879
|
+
|
|
13851
13880
|
function isSvgMeasure(o) {
|
|
13852
13881
|
return utils.isFiniteNumber(o) || utils.isString(o) && /^-?[.0-9]+[a-z]*$/.test(o);
|
|
13853
13882
|
}
|
|
@@ -13871,6 +13900,7 @@
|
|
|
13871
13900
|
getSymbolPropertyAccessor: getSymbolPropertyAccessor,
|
|
13872
13901
|
isSvgClassName: isSvgClassName,
|
|
13873
13902
|
isSvgNumber: isSvgNumber,
|
|
13903
|
+
parseBoolean: parseBoolean,
|
|
13874
13904
|
isSvgMeasure: isSvgMeasure,
|
|
13875
13905
|
parseSvgMeasure: parseSvgMeasure,
|
|
13876
13906
|
isSvgColor: isSvgColor
|
|
@@ -14089,6 +14119,9 @@
|
|
|
14089
14119
|
o = importLineString(coords[i]);
|
|
14090
14120
|
o.properties.d = d + o.properties.d + ' Z';
|
|
14091
14121
|
}
|
|
14122
|
+
if (coords.length > 1) {
|
|
14123
|
+
o.properties['fill-rule'] = 'evenodd'; // support polygons with holes
|
|
14124
|
+
}
|
|
14092
14125
|
return o;
|
|
14093
14126
|
}
|
|
14094
14127
|
|
|
@@ -18005,6 +18038,10 @@ ${svg}
|
|
|
18005
18038
|
return chunks;
|
|
18006
18039
|
}
|
|
18007
18040
|
|
|
18041
|
+
function parseNumberList(token) {
|
|
18042
|
+
return token.split(',').map(parseFloat);
|
|
18043
|
+
}
|
|
18044
|
+
|
|
18008
18045
|
// Split comma-delimited list, trim quotes from entire list and
|
|
18009
18046
|
// individual members
|
|
18010
18047
|
function parseStringList(token) {
|
|
@@ -18094,6 +18131,7 @@ ${svg}
|
|
|
18094
18131
|
var OptionParsingUtils = /*#__PURE__*/Object.freeze({
|
|
18095
18132
|
__proto__: null,
|
|
18096
18133
|
splitShellTokens: splitShellTokens,
|
|
18134
|
+
parseNumberList: parseNumberList,
|
|
18097
18135
|
parseStringList: parseStringList,
|
|
18098
18136
|
parseColorList: parseColorList,
|
|
18099
18137
|
cleanArgv: cleanArgv,
|
|
@@ -18285,7 +18323,7 @@ ${svg}
|
|
|
18285
18323
|
} else if (type == 'strings') {
|
|
18286
18324
|
val = parseStringList(token);
|
|
18287
18325
|
} else if (type == 'bbox' || type == 'numbers') {
|
|
18288
|
-
val = token
|
|
18326
|
+
val = parseNumberList(token);
|
|
18289
18327
|
} else if (type == 'percent') {
|
|
18290
18328
|
// val = utils.parsePercent(token);
|
|
18291
18329
|
val = token; // string value is parsed by command function
|
|
@@ -20076,56 +20114,82 @@ ${svg}
|
|
|
20076
20114
|
type: 'distance'
|
|
20077
20115
|
})
|
|
20078
20116
|
.option('sides', {
|
|
20079
|
-
describe: 'sides of a polygon
|
|
20117
|
+
describe: 'number of sides of a polygon symbol',
|
|
20080
20118
|
type: 'number'
|
|
20081
20119
|
})
|
|
20120
|
+
.option('orientation', {
|
|
20121
|
+
// TODO: removed (replaced by flipped and rotated)
|
|
20122
|
+
// describe: 'use orientation=b for a rotated or flipped orientation'
|
|
20123
|
+
})
|
|
20124
|
+
// .option('flipped', {
|
|
20125
|
+
// type: 'flag',
|
|
20126
|
+
// describe: 'symbol is vertically flipped'
|
|
20127
|
+
// })
|
|
20128
|
+
.option('rotated', {
|
|
20129
|
+
type: 'flag',
|
|
20130
|
+
describe: 'symbol is rotated to a different orientation'
|
|
20131
|
+
})
|
|
20082
20132
|
.option('rotation', {
|
|
20083
20133
|
describe: 'rotation of symbol in degrees'
|
|
20084
20134
|
})
|
|
20085
|
-
.option('
|
|
20086
|
-
describe: '
|
|
20087
|
-
})
|
|
20088
|
-
.option('length', {
|
|
20089
|
-
// alias for arrow-length
|
|
20135
|
+
.option('points', {
|
|
20136
|
+
describe: '(star) number of points'
|
|
20090
20137
|
})
|
|
20091
|
-
.option('
|
|
20092
|
-
|
|
20138
|
+
.option('point-ratio', {
|
|
20139
|
+
old_alias: 'star-ratio',
|
|
20140
|
+
describe: '(star) ratio of minor to major radius of star',
|
|
20093
20141
|
type: 'number'
|
|
20094
20142
|
})
|
|
20095
|
-
.option('
|
|
20096
|
-
describe: '
|
|
20143
|
+
.option('radii', {
|
|
20144
|
+
describe: '(ring) comma-sep. list of concentric radii, ascending order'
|
|
20097
20145
|
})
|
|
20098
|
-
.option('
|
|
20099
|
-
|
|
20146
|
+
.option('length', {
|
|
20147
|
+
old_alias: 'arrow-length',
|
|
20148
|
+
describe: '(arrow) length of arrow in pixels'
|
|
20149
|
+
})
|
|
20150
|
+
.option('direction', {
|
|
20151
|
+
old_alias: 'arrow-direction',
|
|
20152
|
+
describe: '(arrow) angle off vertical (-90 = left-pointing)'
|
|
20100
20153
|
})
|
|
20101
|
-
.option('
|
|
20102
|
-
|
|
20154
|
+
.option('head-angle', {
|
|
20155
|
+
old_alias: 'arrow-head-angle',
|
|
20156
|
+
describe: '(arrow) angle of tip of arrow (default is 40 degrees)'
|
|
20103
20157
|
})
|
|
20104
|
-
.option('
|
|
20105
|
-
|
|
20158
|
+
.option('head-width', {
|
|
20159
|
+
old_alias: 'arrow-head-width',
|
|
20160
|
+
describe: '(arrow) width of arrow head from side to side'
|
|
20106
20161
|
})
|
|
20107
|
-
.option('
|
|
20108
|
-
|
|
20162
|
+
.option('head-length', {
|
|
20163
|
+
old_alias: 'arrow-head-width',
|
|
20164
|
+
describe: '(arrow) length of head (alternative to head-angle)'
|
|
20109
20165
|
})
|
|
20110
|
-
.option('
|
|
20166
|
+
.option('head-shape', {
|
|
20111
20167
|
// describe: 'options: a b c'
|
|
20112
20168
|
})
|
|
20113
|
-
.option('
|
|
20114
|
-
|
|
20169
|
+
.option('stem-width', {
|
|
20170
|
+
old_alias: 'arrow-stem-width',
|
|
20171
|
+
describe: '(arrow) width of stem at its widest point'
|
|
20115
20172
|
})
|
|
20116
|
-
.option('
|
|
20117
|
-
|
|
20173
|
+
.option('stem-length', {
|
|
20174
|
+
old_alias: 'arrow-stem-length',
|
|
20175
|
+
describe: '(arrow) alternative to length'
|
|
20118
20176
|
})
|
|
20119
|
-
.option('
|
|
20120
|
-
|
|
20177
|
+
.option('stem-taper', {
|
|
20178
|
+
old_alias: 'arrow-stem-taper',
|
|
20179
|
+
describe: '(arrow) factor for tapering the width of the stem (0-1)'
|
|
20121
20180
|
})
|
|
20122
|
-
.option('
|
|
20123
|
-
|
|
20181
|
+
.option('stem-curve', {
|
|
20182
|
+
old_alias: 'arrow-stem-curve',
|
|
20183
|
+
describe: '(arrow) curvature in degrees (default is 0)'
|
|
20124
20184
|
})
|
|
20125
|
-
.option('
|
|
20126
|
-
|
|
20185
|
+
.option('min-stem-ratio', {
|
|
20186
|
+
old_alias: 'arrow-min-stem',
|
|
20187
|
+
describe: '(arrow) min ratio of stem to total length',
|
|
20127
20188
|
type: 'number'
|
|
20128
20189
|
})
|
|
20190
|
+
.option('anchor', {
|
|
20191
|
+
describe: '(arrow) takes one of: start, middle, end (default is start)'
|
|
20192
|
+
})
|
|
20129
20193
|
.option('stroke', {})
|
|
20130
20194
|
.option('stroke-width', {})
|
|
20131
20195
|
.option('fill', {
|
|
@@ -21660,7 +21724,7 @@ ${svg}
|
|
|
21660
21724
|
collectionType = 'mixed';
|
|
21661
21725
|
}
|
|
21662
21726
|
} else if (currType != t) {
|
|
21663
|
-
stop("Unable to import mixed-geometry
|
|
21727
|
+
stop("Unable to import mixed-geometry features");
|
|
21664
21728
|
}
|
|
21665
21729
|
}
|
|
21666
21730
|
|
|
@@ -21771,6 +21835,33 @@ ${svg}
|
|
|
21771
21835
|
importShp: importShp
|
|
21772
21836
|
});
|
|
21773
21837
|
|
|
21838
|
+
function importGeoJSON(src, optsArg) {
|
|
21839
|
+
var opts = optsArg || {};
|
|
21840
|
+
var supportedGeometries = Object.keys(GeoJSON.pathImporters),
|
|
21841
|
+
srcObj = utils.isString(src) ? JSON.parse(src) : src,
|
|
21842
|
+
importer = new GeoJSONParser(opts),
|
|
21843
|
+
srcCollection, dataset;
|
|
21844
|
+
|
|
21845
|
+
// Convert single feature or geometry into a collection with one member
|
|
21846
|
+
if (srcObj.type == 'Feature') {
|
|
21847
|
+
srcCollection = {
|
|
21848
|
+
type: 'FeatureCollection',
|
|
21849
|
+
features: [srcObj]
|
|
21850
|
+
};
|
|
21851
|
+
} else if (supportedGeometries.includes(srcObj.type)) {
|
|
21852
|
+
srcCollection = {
|
|
21853
|
+
type: 'GeometryCollection',
|
|
21854
|
+
geometries: [srcObj]
|
|
21855
|
+
};
|
|
21856
|
+
} else {
|
|
21857
|
+
srcCollection = srcObj;
|
|
21858
|
+
}
|
|
21859
|
+
(srcCollection.features || srcCollection.geometries || []).forEach(importer.parseObject);
|
|
21860
|
+
dataset = importer.done();
|
|
21861
|
+
importCRS(dataset, srcObj); // TODO: remove this
|
|
21862
|
+
return dataset;
|
|
21863
|
+
}
|
|
21864
|
+
|
|
21774
21865
|
function GeoJSONParser(opts) {
|
|
21775
21866
|
var idField = opts.id_field || GeoJSON.ID_FIELD,
|
|
21776
21867
|
importer = new PathImporter(opts),
|
|
@@ -21792,8 +21883,11 @@ ${svg}
|
|
|
21792
21883
|
geom = o;
|
|
21793
21884
|
}
|
|
21794
21885
|
// TODO: improve so geometry_type option skips features instead of creating null geometries
|
|
21795
|
-
|
|
21796
|
-
|
|
21886
|
+
if (geom && geom.type == 'GeometryCollection') {
|
|
21887
|
+
GeoJSON.importComplexFeature(importer, geom, rec, opts);
|
|
21888
|
+
} else {
|
|
21889
|
+
GeoJSON.importSimpleFeature(importer, geom, rec, opts);
|
|
21890
|
+
}
|
|
21797
21891
|
};
|
|
21798
21892
|
|
|
21799
21893
|
this.done = function() {
|
|
@@ -21801,50 +21895,58 @@ ${svg}
|
|
|
21801
21895
|
};
|
|
21802
21896
|
}
|
|
21803
21897
|
|
|
21804
|
-
function
|
|
21805
|
-
var
|
|
21806
|
-
|
|
21807
|
-
|
|
21808
|
-
|
|
21809
|
-
srcCollection, dataset;
|
|
21810
|
-
|
|
21811
|
-
// Convert single feature or geometry into a collection with one member
|
|
21812
|
-
if (srcObj.type == 'Feature') {
|
|
21813
|
-
srcCollection = {
|
|
21814
|
-
type: 'FeatureCollection',
|
|
21815
|
-
features: [srcObj]
|
|
21816
|
-
};
|
|
21817
|
-
} else if (supportedGeometries.includes(srcObj.type)) {
|
|
21818
|
-
srcCollection = {
|
|
21819
|
-
type: 'GeometryCollection',
|
|
21820
|
-
geometries: [srcObj]
|
|
21821
|
-
};
|
|
21822
|
-
} else {
|
|
21823
|
-
srcCollection = srcObj;
|
|
21898
|
+
GeoJSON.importComplexFeature = function(importer, geom, rec, opts) {
|
|
21899
|
+
var types = divideGeometriesByType(geom.geometries || []);
|
|
21900
|
+
if (types.length === 0) {
|
|
21901
|
+
importer.startShape(rec); // import a feature with null geometry
|
|
21902
|
+
return;
|
|
21824
21903
|
}
|
|
21825
|
-
(
|
|
21826
|
-
|
|
21827
|
-
|
|
21828
|
-
|
|
21904
|
+
types.forEach(function(geometries, i) {
|
|
21905
|
+
importer.startShape(copyRecord(rec));
|
|
21906
|
+
geometries.forEach(function(geom) {
|
|
21907
|
+
GeoJSON.importSimpleGeometry(importer, geom, opts);
|
|
21908
|
+
});
|
|
21909
|
+
});
|
|
21910
|
+
};
|
|
21911
|
+
|
|
21912
|
+
function divideGeometriesByType(geometries, index) {
|
|
21913
|
+
index = index || {};
|
|
21914
|
+
geometries.forEach(function(geom) {
|
|
21915
|
+
if (!geom) return;
|
|
21916
|
+
var mtype = GeoJSON.translateGeoJSONType(geom.type);
|
|
21917
|
+
if (mtype) {
|
|
21918
|
+
if (mtype in index === false) {
|
|
21919
|
+
index[mtype] = [];
|
|
21920
|
+
}
|
|
21921
|
+
index[mtype].push(geom);
|
|
21922
|
+
} else if (geom.type == 'GeometryCollection') {
|
|
21923
|
+
divideGeometriesByType(geom.geometries || [], index);
|
|
21924
|
+
}
|
|
21925
|
+
});
|
|
21926
|
+
return Object.values(index);
|
|
21829
21927
|
}
|
|
21830
21928
|
|
|
21831
|
-
GeoJSON.
|
|
21832
|
-
|
|
21833
|
-
|
|
21929
|
+
GeoJSON.importSimpleFeature = function(importer, geom, rec, opts) {
|
|
21930
|
+
importer.startShape(rec);
|
|
21931
|
+
GeoJSON.importSimpleGeometry(importer, geom, opts);
|
|
21932
|
+
};
|
|
21933
|
+
|
|
21934
|
+
GeoJSON.importSimpleGeometry = function(importer, geom, opts) {
|
|
21935
|
+
var type = geom ? geom.type : null;
|
|
21936
|
+
if (type === null) {
|
|
21937
|
+
// no geometry to import
|
|
21938
|
+
} else if (type in GeoJSON.pathImporters) {
|
|
21834
21939
|
if (opts.geometry_type && opts.geometry_type != GeoJSON.translateGeoJSONType(type)) {
|
|
21835
21940
|
// kludge to filter out all but one type of geometry
|
|
21836
21941
|
return;
|
|
21837
21942
|
}
|
|
21838
21943
|
GeoJSON.pathImporters[type](geom.coordinates, importer);
|
|
21839
|
-
} else if (type == 'GeometryCollection') {
|
|
21840
|
-
geom.geometries.forEach(function(geom) {
|
|
21841
|
-
GeoJSON.importGeometry(geom, importer, opts);
|
|
21842
|
-
});
|
|
21843
21944
|
} else {
|
|
21844
|
-
verbose("
|
|
21945
|
+
verbose("Unsupported geometry type:", geom.type);
|
|
21845
21946
|
}
|
|
21846
21947
|
};
|
|
21847
21948
|
|
|
21949
|
+
|
|
21848
21950
|
// Functions for importing geometry coordinates using a PathImporter
|
|
21849
21951
|
//
|
|
21850
21952
|
GeoJSON.pathImporters = {
|
|
@@ -21883,8 +21985,8 @@ ${svg}
|
|
|
21883
21985
|
|
|
21884
21986
|
var GeojsonImport = /*#__PURE__*/Object.freeze({
|
|
21885
21987
|
__proto__: null,
|
|
21886
|
-
GeoJSONParser: GeoJSONParser,
|
|
21887
21988
|
importGeoJSON: importGeoJSON,
|
|
21989
|
+
GeoJSONParser: GeoJSONParser,
|
|
21888
21990
|
importCRS: importCRS
|
|
21889
21991
|
});
|
|
21890
21992
|
|
|
@@ -37968,108 +38070,131 @@ ${svg}
|
|
|
37968
38070
|
// return [stem, head];
|
|
37969
38071
|
// }
|
|
37970
38072
|
|
|
37971
|
-
function
|
|
37972
|
-
|
|
37973
|
-
|
|
38073
|
+
// function getStickArrowTip(totalLen, curve) {
|
|
38074
|
+
// // curve/2 intersects the arrowhead at 90deg (trigonometry)
|
|
38075
|
+
// var theta = Math.abs(curve/2) / 180 * Math.PI;
|
|
38076
|
+
// var dx = totalLen * Math.sin(theta) * (curve > 0 ? -1 : 1);
|
|
38077
|
+
// var dy = totalLen * Math.cos(theta);
|
|
38078
|
+
// return [dx, dy];
|
|
38079
|
+
// }
|
|
37974
38080
|
|
|
37975
|
-
function
|
|
37976
|
-
|
|
37977
|
-
|
|
37978
|
-
unscaledStemWidth = d['arrow-stem-width'] || 2,
|
|
37979
|
-
unscaledHeadWidth = d['arrow-head-width'] || unscaledStemWidth * 3,
|
|
37980
|
-
unscaledHeadLen = getHeadLength(unscaledHeadWidth, d),
|
|
37981
|
-
stemTaper = d['arrow-stem-taper'] || 0,
|
|
37982
|
-
stemCurve = d['arrow-stem-curve'] || 0;
|
|
38081
|
+
// function addPoints(a, b) {
|
|
38082
|
+
// return [a[0] + b[0], a[1] + b[1]];
|
|
38083
|
+
// }
|
|
37983
38084
|
|
|
37984
|
-
|
|
38085
|
+
function calcStraightArrowCoords(stemLen, headLen, stemDx, headDx, baseDx) {
|
|
38086
|
+
return [[baseDx, 0], [stemDx, stemLen], [headDx, stemLen], [0, stemLen + headLen],
|
|
38087
|
+
[-headDx, stemLen], [-stemDx, stemLen], [-baseDx, 0], [baseDx, 0]];
|
|
38088
|
+
}
|
|
37985
38089
|
|
|
37986
|
-
|
|
38090
|
+
function calcArrowSize(d) {
|
|
38091
|
+
var totalLen = d.radius || d.length || d.r || 0,
|
|
38092
|
+
scale = 1,
|
|
38093
|
+
o = initArrowSize(d); // calc several parameters
|
|
37987
38094
|
|
|
37988
38095
|
if (totalLen > 0) {
|
|
37989
|
-
scale =
|
|
37990
|
-
|
|
37991
|
-
|
|
37992
|
-
|
|
37993
|
-
stemLen = totalLen - headLen;
|
|
37994
|
-
|
|
37995
|
-
} else {
|
|
37996
|
-
headWidth = unscaledHeadWidth;
|
|
37997
|
-
headLen = unscaledHeadLen;
|
|
37998
|
-
stemWidth = unscaledStemWidth;
|
|
37999
|
-
stemLen = d['arrow-stem-length'] || 0;
|
|
38000
|
-
totalLen = headLen + stemLen;
|
|
38096
|
+
scale = calcScale(totalLen, o.headLen, d);
|
|
38097
|
+
o.stemWidth *= scale;
|
|
38098
|
+
o.headWidth *= scale;
|
|
38099
|
+
o.headLen *= scale;
|
|
38100
|
+
o.stemLen = totalLen - o.headLen;
|
|
38001
38101
|
}
|
|
38002
38102
|
|
|
38003
|
-
if (
|
|
38004
|
-
|
|
38005
|
-
var coords;
|
|
38006
|
-
|
|
38007
|
-
var headDx = headWidth / 2,
|
|
38008
|
-
stemDx = stemWidth / 2,
|
|
38009
|
-
baseDx = stemDx * (1 - stemTaper);
|
|
38010
|
-
|
|
38011
|
-
if (unscaledHeadWidth < unscaledStemWidth) {
|
|
38103
|
+
if (o.headWidth < o.stemWidth) {
|
|
38012
38104
|
stop('Arrow head must be at least as wide as the stem.');
|
|
38013
38105
|
}
|
|
38014
|
-
|
|
38015
|
-
if (!stemCurve || Math.abs(stemCurve) > 90) {
|
|
38016
|
-
coords = [[baseDx, 0], [stemDx, stemLen], [headDx, stemLen], [0, stemLen + headLen],
|
|
38017
|
-
[-headDx, stemLen], [-stemDx, stemLen], [-baseDx, 0], [baseDx, 0]];
|
|
38018
|
-
} else {
|
|
38019
|
-
if (direction > 0) stemCurve = -stemCurve;
|
|
38020
|
-
coords = getCurvedArrowCoords(stemLen, headLen, stemCurve, stemDx, headDx, baseDx);
|
|
38021
|
-
}
|
|
38022
|
-
|
|
38023
|
-
rotateCoords(coords, direction);
|
|
38024
|
-
return [coords];
|
|
38106
|
+
return o;
|
|
38025
38107
|
}
|
|
38026
38108
|
|
|
38027
|
-
|
|
38028
|
-
|
|
38109
|
+
function calcScale(totalLen, headLen, d) {
|
|
38110
|
+
var minStemRatio = d['min-stem-ratio'] >= 0 ? d['min-stem-ratio'] : 0;
|
|
38111
|
+
var stemLen = d['stem-length'] || 0;
|
|
38029
38112
|
var maxHeadPct = 1 - minStemRatio;
|
|
38030
38113
|
var headPct = headLen / totalLen;
|
|
38114
|
+
var scale = 1;
|
|
38115
|
+
|
|
38031
38116
|
if (headPct > maxHeadPct) {
|
|
38032
|
-
|
|
38117
|
+
scale = maxHeadPct / headPct;
|
|
38118
|
+
} else if (stemLen + headLen > totalLen) {
|
|
38119
|
+
scale = totalLen / (stemLen + headLen);
|
|
38033
38120
|
}
|
|
38034
|
-
return
|
|
38121
|
+
return scale;
|
|
38035
38122
|
}
|
|
38036
38123
|
|
|
38037
|
-
|
|
38038
|
-
|
|
38039
|
-
|
|
38040
|
-
|
|
38041
|
-
|
|
38042
|
-
|
|
38043
|
-
|
|
38044
|
-
|
|
38045
|
-
|
|
38046
|
-
|
|
38124
|
+
function initArrowSize(d) {
|
|
38125
|
+
var sizeRatio = getHeadSizeRatio(d['head-angle'] || 40); // length to width
|
|
38126
|
+
var o = {
|
|
38127
|
+
stemWidth: d['stem-width'] || 2,
|
|
38128
|
+
stemLen: d['stem-length'] || 0,
|
|
38129
|
+
headWidth: d['head-width'],
|
|
38130
|
+
headLen: d['head-length']
|
|
38131
|
+
};
|
|
38132
|
+
if (!o.headWidth) {
|
|
38133
|
+
if (o.headLen) {
|
|
38134
|
+
o.headWidth = o.headLen / sizeRatio;
|
|
38135
|
+
} else {
|
|
38136
|
+
o.headWidth = o.stemWidth * 3; // assumes stemWidth has been set
|
|
38137
|
+
}
|
|
38138
|
+
}
|
|
38139
|
+
if (!o.headLen) {
|
|
38140
|
+
o.headLen = o.headWidth * sizeRatio;
|
|
38141
|
+
}
|
|
38142
|
+
return o;
|
|
38047
38143
|
}
|
|
38048
38144
|
|
|
38049
38145
|
|
|
38050
|
-
|
|
38051
|
-
|
|
38052
|
-
|
|
38053
|
-
var headAngle = d['arrow-head-angle'] || 40;
|
|
38054
|
-
var headRatio = 1 / Math.tan(Math.PI * headAngle / 180 / 2) / 2; // length-to-width head ratio
|
|
38055
|
-
return headWidth * headRatio;
|
|
38146
|
+
// Returns ratio of head length to head width
|
|
38147
|
+
function getHeadSizeRatio(headAngle) {
|
|
38148
|
+
return 1 / Math.tan(Math.PI * headAngle / 180 / 2) / 2;
|
|
38056
38149
|
}
|
|
38057
38150
|
|
|
38058
|
-
function
|
|
38151
|
+
function getFilledArrowCoords(d) {
|
|
38152
|
+
var direction = d.rotation || d.direction || 0,
|
|
38153
|
+
stemTaper = d['stem-taper'] || 0,
|
|
38154
|
+
curvature = d['stem-curve'] || 0,
|
|
38155
|
+
size = calcArrowSize(d);
|
|
38156
|
+
if (!size) return null;
|
|
38157
|
+
var stemLen = size.stemLen,
|
|
38158
|
+
headLen = size.headLen,
|
|
38159
|
+
headDx = size.headWidth / 2,
|
|
38160
|
+
stemDx = size.stemWidth / 2,
|
|
38161
|
+
baseDx = stemDx * (1 - stemTaper),
|
|
38162
|
+
head, stem, coords, dx, dy;
|
|
38163
|
+
|
|
38164
|
+
if (curvature) {
|
|
38165
|
+
if (direction > 0) curvature = -curvature;
|
|
38166
|
+
var theta = Math.abs(curvature) / 180 * Math.PI;
|
|
38167
|
+
var sign = curvature > 0 ? 1 : -1;
|
|
38168
|
+
var ax = baseDx * Math.cos(theta); // rotate arrow base
|
|
38169
|
+
var ay = baseDx * Math.sin(theta) * -sign;
|
|
38170
|
+
dx = stemLen * Math.sin(theta / 2) * sign;
|
|
38171
|
+
dy = stemLen * Math.cos(theta / 2);
|
|
38172
|
+
var leftStem = getCurvedStemCoords(-ax, -ay, -stemDx + dx, dy, theta);
|
|
38173
|
+
var rightStem = getCurvedStemCoords(ax, ay, stemDx + dx, dy, theta);
|
|
38174
|
+
stem = leftStem.concat(rightStem.reverse());
|
|
38175
|
+
|
|
38176
|
+
} else {
|
|
38177
|
+
dx = 0;
|
|
38178
|
+
dy = stemLen;
|
|
38179
|
+
stem = [[-baseDx, 0], [baseDx, 0], [baseDx, 0]];
|
|
38180
|
+
}
|
|
38181
|
+
|
|
38059
38182
|
// coordinates go counter clockwise, starting from the leftmost head coordinate
|
|
38060
|
-
|
|
38061
|
-
|
|
38062
|
-
|
|
38063
|
-
|
|
38064
|
-
|
|
38065
|
-
|
|
38066
|
-
|
|
38067
|
-
|
|
38068
|
-
|
|
38069
|
-
|
|
38070
|
-
|
|
38071
|
-
|
|
38072
|
-
|
|
38183
|
+
head = [[stemDx + dx, dy], [headDx + dx, dy],
|
|
38184
|
+
[dx, headLen + dy], [-headDx + dx, dy], [-stemDx + dx, dy]];
|
|
38185
|
+
|
|
38186
|
+
coords = stem.concat(head);
|
|
38187
|
+
if (d.anchor == 'end') {
|
|
38188
|
+
scaleAndShiftCoords(coords, 1, [-dx, -dy - headLen]);
|
|
38189
|
+
} else if (d.anchor == 'middle') {
|
|
38190
|
+
scaleAndShiftCoords(coords, 1, [-dx/2, (-dy - headLen)/2]);
|
|
38191
|
+
}
|
|
38192
|
+
|
|
38193
|
+
rotateCoords(coords, direction);
|
|
38194
|
+
if (d.flipped) {
|
|
38195
|
+
flipY(coords);
|
|
38196
|
+
}
|
|
38197
|
+
return [coords];
|
|
38073
38198
|
}
|
|
38074
38199
|
|
|
38075
38200
|
// ax, ay: point on the base
|
|
@@ -38106,43 +38231,31 @@ ${svg}
|
|
|
38106
38231
|
return coords;
|
|
38107
38232
|
}
|
|
38108
38233
|
|
|
38109
|
-
|
|
38110
|
-
|
|
38111
|
-
|
|
38112
|
-
|
|
38113
|
-
|
|
38234
|
+
function getPolygonCoords(d) {
|
|
38235
|
+
var radius = d.radius || d.length || d.r,
|
|
38236
|
+
sides = +d.sides || getSidesByType(d.type),
|
|
38237
|
+
rotated = sides % 2 == 1,
|
|
38238
|
+
coords = [],
|
|
38239
|
+
angle, b;
|
|
38240
|
+
|
|
38114
38241
|
if (radius > 0 === false) return null;
|
|
38115
|
-
|
|
38116
|
-
var sides = +opts.sides || getDefaultSides(type);
|
|
38117
|
-
var isStar = type == 'star';
|
|
38118
|
-
if (isStar && (sides < 6 || sides % 2 !== 0)) {
|
|
38119
|
-
stop(`Invalid number of sides for a star (${sides})`);
|
|
38120
|
-
} else if (sides >= 3 === false) {
|
|
38242
|
+
if (sides >= 3 === false) {
|
|
38121
38243
|
stop(`Invalid number of sides (${sides})`);
|
|
38122
38244
|
}
|
|
38123
|
-
|
|
38124
|
-
|
|
38125
|
-
b = isStar ? 1 : 0.5,
|
|
38126
|
-
theta, even, len;
|
|
38127
|
-
if (opts.orientation == 'b') {
|
|
38128
|
-
b = 0;
|
|
38245
|
+
if (d.orientation == 'b' || d.flipped || d.rotated) {
|
|
38246
|
+
rotated = !rotated;
|
|
38129
38247
|
}
|
|
38248
|
+
b = rotated ? 0 : 0.5;
|
|
38130
38249
|
for (var i=0; i<sides; i++) {
|
|
38131
|
-
|
|
38132
|
-
|
|
38133
|
-
if (isStar && even) {
|
|
38134
|
-
len *= (opts.star_ratio || 0.5);
|
|
38135
|
-
}
|
|
38136
|
-
theta = (i + b) * angle % 360;
|
|
38137
|
-
coords.push(getPlanarSegmentEndpoint(0, 0, theta, len));
|
|
38250
|
+
angle = (i + b) / sides * 360;
|
|
38251
|
+
coords.push(getPlanarSegmentEndpoint(0, 0, angle, radius));
|
|
38138
38252
|
}
|
|
38139
38253
|
coords.push(coords[0].concat());
|
|
38140
38254
|
return [coords];
|
|
38141
38255
|
}
|
|
38142
38256
|
|
|
38143
|
-
function
|
|
38257
|
+
function getSidesByType(type) {
|
|
38144
38258
|
return {
|
|
38145
|
-
star: 10,
|
|
38146
38259
|
circle: 72,
|
|
38147
38260
|
triangle: 3,
|
|
38148
38261
|
square: 4,
|
|
@@ -38155,6 +38268,83 @@ ${svg}
|
|
|
38155
38268
|
}[type] || 4;
|
|
38156
38269
|
}
|
|
38157
38270
|
|
|
38271
|
+
function getStarCoords(d) {
|
|
38272
|
+
var radius = d.radius || d.length || d.r,
|
|
38273
|
+
points = d.points || d.sides && d.sides / 2 || 5,
|
|
38274
|
+
sides = points * 2,
|
|
38275
|
+
minorRadius = getMinorRadius(points) * radius,
|
|
38276
|
+
b = d.orientation == 'b' || d.flipped || d.rotated ? 0 : 1,
|
|
38277
|
+
coords = [],
|
|
38278
|
+
angle, len;
|
|
38279
|
+
|
|
38280
|
+
if (radius > 0 === false) return null;
|
|
38281
|
+
if (points < 5) {
|
|
38282
|
+
stop(`Invalid number of points for a star (${points})`);
|
|
38283
|
+
}
|
|
38284
|
+
for (var i=0; i<sides; i++) {
|
|
38285
|
+
len = i % 2 == 0 ? minorRadius : radius;
|
|
38286
|
+
angle = (i + b) / sides * 360;
|
|
38287
|
+
coords.push(getPlanarSegmentEndpoint(0, 0, angle, len));
|
|
38288
|
+
}
|
|
38289
|
+
coords.push(coords[0].concat());
|
|
38290
|
+
return [coords];
|
|
38291
|
+
}
|
|
38292
|
+
|
|
38293
|
+
function getMinorRadius(points) {
|
|
38294
|
+
var innerAngle = 360 / points;
|
|
38295
|
+
var pointAngle = getDefaultPointAngle(points);
|
|
38296
|
+
var thetaA = Math.PI / 180 * innerAngle / 2;
|
|
38297
|
+
var thetaB = Math.PI / 180 * pointAngle / 2;
|
|
38298
|
+
var a = Math.tan(thetaB) / (Math.tan(thetaB) + Math.tan(thetaA));
|
|
38299
|
+
var c = a / Math.cos(thetaA);
|
|
38300
|
+
return c;
|
|
38301
|
+
}
|
|
38302
|
+
|
|
38303
|
+
function getDefaultPointAngle(points) {
|
|
38304
|
+
var minSkip = 1;
|
|
38305
|
+
var maxSkip = Math.ceil(points / 2) - 2;
|
|
38306
|
+
var skip = Math.floor((maxSkip + minSkip) / 2);
|
|
38307
|
+
return getPointAngle(points, skip);
|
|
38308
|
+
}
|
|
38309
|
+
|
|
38310
|
+
// skip: number of adjacent points to skip when drawing a segment
|
|
38311
|
+
function getPointAngle(points, skip) {
|
|
38312
|
+
var unitAngle = 360 / points;
|
|
38313
|
+
var centerAngle = unitAngle * (skip + 1);
|
|
38314
|
+
return 180 - centerAngle;
|
|
38315
|
+
}
|
|
38316
|
+
|
|
38317
|
+
// Returns GeoJSON MultiPolygon coords
|
|
38318
|
+
function getRingCoords(d) {
|
|
38319
|
+
var radii = parseRings(d.radii || '2');
|
|
38320
|
+
var coords = [];
|
|
38321
|
+
var solidCenter = utils.isOdd(radii.length);
|
|
38322
|
+
var ring, hole;
|
|
38323
|
+
for (var i=0; i<radii.length; i++) {
|
|
38324
|
+
ring = getPolygonCoords({
|
|
38325
|
+
type: 'circle',
|
|
38326
|
+
radius: radii[i]
|
|
38327
|
+
});
|
|
38328
|
+
if (!solidCenter || i > 0) {
|
|
38329
|
+
i++;
|
|
38330
|
+
hole = ring;
|
|
38331
|
+
ring = getPolygonCoords({
|
|
38332
|
+
type: 'circle',
|
|
38333
|
+
radius: radii[i]
|
|
38334
|
+
});
|
|
38335
|
+
ring.push(hole[0]);
|
|
38336
|
+
}
|
|
38337
|
+
coords.push(ring);
|
|
38338
|
+
}
|
|
38339
|
+
return coords;
|
|
38340
|
+
}
|
|
38341
|
+
|
|
38342
|
+
function parseRings(arg) {
|
|
38343
|
+
var arr = Array.isArray(arg) ? arg : parseNumberList(arg);
|
|
38344
|
+
utils.genericSort(arr, true);
|
|
38345
|
+
return utils.uniq(arr);
|
|
38346
|
+
}
|
|
38347
|
+
|
|
38158
38348
|
// TODO: refactor to remove duplication in mapshaper-svg-style.js
|
|
38159
38349
|
cmd.symbols = function(inputLyr, dataset, opts) {
|
|
38160
38350
|
requireSinglePointLayer(inputLyr);
|
|
@@ -38171,9 +38361,15 @@ ${svg}
|
|
|
38171
38361
|
if (!shp) return null;
|
|
38172
38362
|
var d = getSymbolData(i);
|
|
38173
38363
|
var rec = records[i] || {};
|
|
38364
|
+
var geojsonType = 'Polygon';
|
|
38174
38365
|
var coords;
|
|
38175
38366
|
if (d.type == 'arrow') {
|
|
38176
38367
|
coords = getFilledArrowCoords(d);
|
|
38368
|
+
} else if (d.type == 'ring') {
|
|
38369
|
+
coords = getRingCoords(d);
|
|
38370
|
+
geojsonType = 'MultiPolygon';
|
|
38371
|
+
} else if (d.type == 'star') {
|
|
38372
|
+
coords = getStarCoords(d);
|
|
38177
38373
|
} else {
|
|
38178
38374
|
coords = getPolygonCoords(d);
|
|
38179
38375
|
}
|
|
@@ -38188,9 +38384,9 @@ ${svg}
|
|
|
38188
38384
|
if (polygonMode) {
|
|
38189
38385
|
scaleAndShiftCoords(coords, metersPerPx, shp[0]);
|
|
38190
38386
|
if (d.tfill) rec.fill = d.fill;
|
|
38191
|
-
return createGeometry(coords);
|
|
38387
|
+
return createGeometry(coords, geojsonType);
|
|
38192
38388
|
} else {
|
|
38193
|
-
rec['svg-symbol'] =
|
|
38389
|
+
rec['svg-symbol'] = makeSvgPolygonSymbol(coords, d, geojsonType);
|
|
38194
38390
|
}
|
|
38195
38391
|
});
|
|
38196
38392
|
|
|
@@ -38221,9 +38417,9 @@ ${svg}
|
|
|
38221
38417
|
return importGeoJSON(geojson);
|
|
38222
38418
|
}
|
|
38223
38419
|
|
|
38224
|
-
function createGeometry(coords) {
|
|
38420
|
+
function createGeometry(coords, type) {
|
|
38225
38421
|
return {
|
|
38226
|
-
type:
|
|
38422
|
+
type: type,
|
|
38227
38423
|
coordinates: coords
|
|
38228
38424
|
};
|
|
38229
38425
|
}
|
|
@@ -38236,7 +38432,12 @@ ${svg}
|
|
|
38236
38432
|
}
|
|
38237
38433
|
|
|
38238
38434
|
// Returns an svg-symbol data object for one symbol
|
|
38239
|
-
function
|
|
38435
|
+
function makeSvgPolygonSymbol(coords, properties, geojsonType) {
|
|
38436
|
+
if (geojsonType == 'MultiPolygon') {
|
|
38437
|
+
coords = convertMultiPolygonCoords(coords);
|
|
38438
|
+
} else if (geojsonType != 'Polygon') {
|
|
38439
|
+
error('Unsupported type:', geojsonType);
|
|
38440
|
+
}
|
|
38240
38441
|
roundCoordsForSVG(coords);
|
|
38241
38442
|
return {
|
|
38242
38443
|
type: 'polygon',
|
|
@@ -38245,9 +38446,14 @@ ${svg}
|
|
|
38245
38446
|
};
|
|
38246
38447
|
}
|
|
38247
38448
|
|
|
38449
|
+
function convertMultiPolygonCoords(coords) {
|
|
38450
|
+
return coords.reduce(function(memo, poly) {
|
|
38451
|
+
return memo.concat(poly);
|
|
38452
|
+
}, []);
|
|
38453
|
+
}
|
|
38454
|
+
|
|
38248
38455
|
var Symbols = /*#__PURE__*/Object.freeze({
|
|
38249
|
-
__proto__: null
|
|
38250
|
-
makeSvgSymbol: makeSvgSymbol
|
|
38456
|
+
__proto__: null
|
|
38251
38457
|
});
|
|
38252
38458
|
|
|
38253
38459
|
cmd.target = function(catalog, opts) {
|