mapshaper 0.7.50 → 0.7.52
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 +494 -36
- package/package.json +3 -2
- package/www/mapshaper-gui.js +467 -125
- package/www/mapshaper.js +494 -36
- package/www/page.css +5 -0
package/www/mapshaper.js
CHANGED
|
@@ -10465,6 +10465,22 @@
|
|
|
10465
10465
|
return crs;
|
|
10466
10466
|
}
|
|
10467
10467
|
|
|
10468
|
+
// The WKT counterpart of tryParseCrsString(), for a projection read out of a
|
|
10469
|
+
// .prj or .aux.xml file, in either WKT dialect. Returns the proj4 equivalent of
|
|
10470
|
+
// the WKT, or null.
|
|
10471
|
+
function tryParseWktToProj(str) {
|
|
10472
|
+
var revertLogging = getLoggingSetter();
|
|
10473
|
+
var proj = null;
|
|
10474
|
+
setLoggingForCLI();
|
|
10475
|
+
try {
|
|
10476
|
+
proj = wktToProj(str);
|
|
10477
|
+
} catch(e) {
|
|
10478
|
+
// WKT that cannot be converted is one of the expected outcomes here
|
|
10479
|
+
}
|
|
10480
|
+
revertLogging();
|
|
10481
|
+
return proj || null;
|
|
10482
|
+
}
|
|
10483
|
+
|
|
10468
10484
|
function parseCrsString$1(str) {
|
|
10469
10485
|
var defn = getProjDefn(str); // defn is a string or a Proj object
|
|
10470
10486
|
var P;
|
|
@@ -10720,6 +10736,7 @@
|
|
|
10720
10736
|
setProjectionLoader: setProjectionLoader,
|
|
10721
10737
|
toLngLat: toLngLat,
|
|
10722
10738
|
tryParseCrsString: tryParseCrsString,
|
|
10739
|
+
tryParseWktToProj: tryParseWktToProj,
|
|
10723
10740
|
wkt1ToProj: wkt1ToProj,
|
|
10724
10741
|
wkt2ToProj: wkt2ToProj,
|
|
10725
10742
|
wktToProj: wktToProj
|
|
@@ -13129,6 +13146,67 @@
|
|
|
13129
13146
|
toLowerCaseExtension: toLowerCaseExtension
|
|
13130
13147
|
});
|
|
13131
13148
|
|
|
13149
|
+
// GDAL's "persistent auxiliary metadata" sidecar, named after the whole raster
|
|
13150
|
+
// filename rather than replacing its extension (world.tif -> world.tif.aux.xml).
|
|
13151
|
+
// Unlike the .prj file that accompanies a shapefile, this is a sidecar that
|
|
13152
|
+
// GDAL-based software (QGIS included) actually consults for a GeoTIFF's CRS, so
|
|
13153
|
+
// it is where mapshaper puts a projection that GeoTIFF itself cannot describe.
|
|
13154
|
+
var AUX_EXT = '.aux.xml';
|
|
13155
|
+
|
|
13156
|
+
function getAuxFilename(filename) {
|
|
13157
|
+
return filename + AUX_EXT;
|
|
13158
|
+
}
|
|
13159
|
+
|
|
13160
|
+
function isAuxFilename(filename) {
|
|
13161
|
+
return /\.aux\.xml$/i.test(filename || '');
|
|
13162
|
+
}
|
|
13163
|
+
|
|
13164
|
+
// Returns the raster a sidecar belongs to: world.tif.aux.xml -> world.tif
|
|
13165
|
+
function getAuxSourceFilename(filename) {
|
|
13166
|
+
return String(filename).replace(/\.aux\.xml$/i, '');
|
|
13167
|
+
}
|
|
13168
|
+
|
|
13169
|
+
function formatAuxXml(wkt) {
|
|
13170
|
+
return '<PAMDataset>\n <SRS>' + escapeXml(wkt) + '</SRS>\n</PAMDataset>\n';
|
|
13171
|
+
}
|
|
13172
|
+
|
|
13173
|
+
// The CRS a sidecar carries, or null. Everything else GDAL writes to these
|
|
13174
|
+
// files -- statistics, band metadata, overviews -- describes data that
|
|
13175
|
+
// mapshaper reads from the raster itself.
|
|
13176
|
+
function parseAuxCrsString(content) {
|
|
13177
|
+
var xml = typeof content == 'string' ? content :
|
|
13178
|
+
content ? decodeString(content, 'utf8') : '';
|
|
13179
|
+
var match = /<SRS\b[^>]*>([\s\S]*?)<\/SRS>/i.exec(xml);
|
|
13180
|
+
var srs = match ? unescapeXml(match[1]).trim() : '';
|
|
13181
|
+
return srs || null;
|
|
13182
|
+
}
|
|
13183
|
+
|
|
13184
|
+
// GDAL states the SRS in WKT, but a hand-written sidecar may hold an EPSG code
|
|
13185
|
+
// or a proj4 definition instead, which mapshaper can use as they are.
|
|
13186
|
+
function auxCrsIsWkt(str) {
|
|
13187
|
+
return /^\s*[A-Z_]+\[/i.test(str || '');
|
|
13188
|
+
}
|
|
13189
|
+
|
|
13190
|
+
function escapeXml(str) {
|
|
13191
|
+
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
13192
|
+
}
|
|
13193
|
+
|
|
13194
|
+
function unescapeXml(str) {
|
|
13195
|
+
return str.replace(/</g, '<').replace(/>/g, '>')
|
|
13196
|
+
.replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, '&');
|
|
13197
|
+
}
|
|
13198
|
+
|
|
13199
|
+
var GeoTIFFAux = /*#__PURE__*/Object.freeze({
|
|
13200
|
+
__proto__: null,
|
|
13201
|
+
AUX_EXT: AUX_EXT,
|
|
13202
|
+
auxCrsIsWkt: auxCrsIsWkt,
|
|
13203
|
+
formatAuxXml: formatAuxXml,
|
|
13204
|
+
getAuxFilename: getAuxFilename,
|
|
13205
|
+
getAuxSourceFilename: getAuxSourceFilename,
|
|
13206
|
+
isAuxFilename: isAuxFilename,
|
|
13207
|
+
parseAuxCrsString: parseAuxCrsString
|
|
13208
|
+
});
|
|
13209
|
+
|
|
13132
13210
|
// Constants shared between the .msx (mapshaper snapshot) writer in
|
|
13133
13211
|
// mapshaper-pack.mjs and the file-type registry in
|
|
13134
13212
|
// io/mapshaper-file-types.mjs. Living here lets file-types reference the
|
|
@@ -13142,7 +13220,10 @@
|
|
|
13142
13220
|
function guessInputFileType(file) {
|
|
13143
13221
|
var ext = getFileExtension(file || '').toLowerCase(),
|
|
13144
13222
|
type = null;
|
|
13145
|
-
if (
|
|
13223
|
+
if (isAuxFilename(file)) {
|
|
13224
|
+
// Matched ahead of the extension, which is the raster's own (.tif.aux.xml).
|
|
13225
|
+
type = 'aux';
|
|
13226
|
+
} else if (ext == 'dbf' || ext == 'shp' || ext == 'kml' || ext == 'svg' || ext == 'fgb' || ext == 'gpkg' || ext == 'png') {
|
|
13146
13227
|
type = ext;
|
|
13147
13228
|
} else if (ext == 'jpg' || ext == 'jpeg') {
|
|
13148
13229
|
type = 'jpeg';
|
|
@@ -13166,7 +13247,8 @@
|
|
|
13166
13247
|
|
|
13167
13248
|
// File types that can be imported but are not convertible to datasets
|
|
13168
13249
|
function isAuxiliaryInputFileType(type) {
|
|
13169
|
-
return type == 'prj' || type == 'shx' || type == 'cpg' || type == 'world'
|
|
13250
|
+
return type == 'prj' || type == 'shx' || type == 'cpg' || type == 'world' ||
|
|
13251
|
+
type == 'aux';
|
|
13170
13252
|
}
|
|
13171
13253
|
|
|
13172
13254
|
function isRasterImageInputType(type) {
|
|
@@ -34036,11 +34118,6 @@ ${svg}
|
|
|
34036
34118
|
// The raster covers areas, i.e. a coordinate refers to a pixel's corner rather
|
|
34037
34119
|
// than its center. This matches how mapshaper's grid bbox is defined.
|
|
34038
34120
|
var RASTER_TYPE_AREA = 1;
|
|
34039
|
-
// GDAL's "persistent auxiliary metadata" sidecar, which it reads for any raster
|
|
34040
|
-
// it opens. Unlike the .prj file that accompanies a shapefile, this is a
|
|
34041
|
-
// sidecar that GDAL-based software (QGIS included) actually consults for a
|
|
34042
|
-
// GeoTIFF's CRS.
|
|
34043
|
-
var AUX_EXT = '.aux.xml';
|
|
34044
34121
|
|
|
34045
34122
|
function exportGeoTIFF(dataset, opts) {
|
|
34046
34123
|
var extension = opts.extension ||
|
|
@@ -34058,8 +34135,8 @@ ${svg}
|
|
|
34058
34135
|
if (crs.code || crs.geoKeys) return;
|
|
34059
34136
|
if (crs.wkt) {
|
|
34060
34137
|
files.push({
|
|
34061
|
-
filename: filename
|
|
34062
|
-
content:
|
|
34138
|
+
filename: getAuxFilename(filename),
|
|
34139
|
+
content: formatAuxXml(crs.wkt)
|
|
34063
34140
|
});
|
|
34064
34141
|
message('Wrote the CRS of', filename, 'to a', AUX_EXT, 'file, because GeoTIFF has no way to describe this projection. Keep the two files together.');
|
|
34065
34142
|
} else {
|
|
@@ -34169,14 +34246,6 @@ ${svg}
|
|
|
34169
34246
|
}
|
|
34170
34247
|
}
|
|
34171
34248
|
|
|
34172
|
-
function getAuxXml(wkt) {
|
|
34173
|
-
return '<PAMDataset>\n <SRS>' + escapeXml(wkt) + '</SRS>\n</PAMDataset>\n';
|
|
34174
|
-
}
|
|
34175
|
-
|
|
34176
|
-
function escapeXml(str) {
|
|
34177
|
-
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
34178
|
-
}
|
|
34179
|
-
|
|
34180
34249
|
function getOutputFormat(dataset, opts) {
|
|
34181
34250
|
var outFile = opts.file || null,
|
|
34182
34251
|
inFmt = dataset.info && dataset.info.input_formats && dataset.info.input_formats[0],
|
|
@@ -42820,7 +42889,8 @@ ${svg}
|
|
|
42820
42889
|
var dynamicImportModule = Function('id', 'return import(id)');
|
|
42821
42890
|
var DEFAULT_MAX_IMPORT_PIXELS = 16e6;
|
|
42822
42891
|
|
|
42823
|
-
|
|
42892
|
+
// aux: contents of the file's .aux.xml sidecar, if it has one
|
|
42893
|
+
async function importGeoTIFF(input, optsArg, aux) {
|
|
42824
42894
|
var opts = optsArg || {};
|
|
42825
42895
|
var geotiff = await loadGeoTIFFLib();
|
|
42826
42896
|
var source = getGeoTIFFSource(input);
|
|
@@ -42838,7 +42908,7 @@ ${svg}
|
|
|
42838
42908
|
raster: imported.raster
|
|
42839
42909
|
}]
|
|
42840
42910
|
};
|
|
42841
|
-
await importGeoTIFFCrs(dataset, sourceImage);
|
|
42911
|
+
await importGeoTIFFCrs(dataset, sourceImage, aux);
|
|
42842
42912
|
return dataset;
|
|
42843
42913
|
}
|
|
42844
42914
|
|
|
@@ -43187,32 +43257,117 @@ ${svg}
|
|
|
43187
43257
|
// in its own georeferencing, and only the things that need to know what those
|
|
43188
43258
|
// coordinates mean (reprojecting, basemaps, measuring) are unavailable. So a
|
|
43189
43259
|
// CRS that cannot be read is reported and set aside, never raised as an error.
|
|
43190
|
-
|
|
43260
|
+
//
|
|
43261
|
+
// A projection GeoTIFF cannot describe travels beside the file in a .aux.xml
|
|
43262
|
+
// sidecar, which is read when the file's own geo keys come up empty. The keys
|
|
43263
|
+
// come first, matching how GDAL treats the two: the sidecar supplements a file
|
|
43264
|
+
// that says nothing, rather than overruling one that does.
|
|
43265
|
+
async function importGeoTIFFCrs(dataset, image, aux) {
|
|
43191
43266
|
var crsInfo = await getGeoTIFFCrsInfo(image);
|
|
43192
|
-
var
|
|
43193
|
-
|
|
43194
|
-
|
|
43195
|
-
|
|
43196
|
-
|
|
43197
|
-
|
|
43198
|
-
|
|
43199
|
-
|
|
43267
|
+
var fromFile = await readCrsString(crsInfo.crsString);
|
|
43268
|
+
// The sidecar usually states its CRS in WKT, which has to become a proj4
|
|
43269
|
+
// definition before it can be used like the file's own metadata.
|
|
43270
|
+
var sidecarSrs = fromFile || !aux ? null : parseAuxCrsString(aux.content);
|
|
43271
|
+
var sidecarWkt = auxCrsIsWkt(sidecarSrs) ? sidecarSrs : null;
|
|
43272
|
+
var sidecarString = sidecarWkt ? tryParseWktToProj(sidecarWkt) : sidecarSrs;
|
|
43273
|
+
var fromSidecar = await readCrsString(sidecarString);
|
|
43274
|
+
var found = fromFile || fromSidecar;
|
|
43275
|
+
if (!found) {
|
|
43276
|
+
dataset.info = dataset.info || {};
|
|
43277
|
+
reportMissingCrs(dataset, crsInfo, sidecarString);
|
|
43278
|
+
return;
|
|
43279
|
+
}
|
|
43280
|
+
setDatasetCrsInfo(dataset, {crs_string: found.crsString, crs: found.crs});
|
|
43281
|
+
if (found.assumedDatum) {
|
|
43282
|
+
message('This GeoTIFF describes its projection without naming a datum;',
|
|
43283
|
+
'assuming WGS 84.');
|
|
43284
|
+
}
|
|
43285
|
+
if (fromSidecar) {
|
|
43286
|
+
message('Read the CRS from ' + (aux.filename || 'a ' + AUX_EXT + ' file') +
|
|
43287
|
+
', because GeoTIFF has no way to describe this projection.');
|
|
43288
|
+
if (sidecarWkt) {
|
|
43289
|
+
// Keep the sidecar's own wording, so that exporting the raster again
|
|
43290
|
+
// writes back what was read rather than a re-derived approximation.
|
|
43291
|
+
dataset.info.wkt1 = sidecarWkt;
|
|
43200
43292
|
}
|
|
43201
|
-
crs = tryParseCrsString(crsString);
|
|
43202
43293
|
}
|
|
43203
|
-
|
|
43204
|
-
|
|
43205
|
-
|
|
43206
|
-
|
|
43294
|
+
}
|
|
43295
|
+
|
|
43296
|
+
// Returns {crsString, crs}, or null if there is no string or it cannot be used.
|
|
43297
|
+
async function readCrsString(str) {
|
|
43298
|
+
var crs, assumed;
|
|
43299
|
+
if (!str) return null;
|
|
43300
|
+
try {
|
|
43301
|
+
await initProjLibrary({crs: str});
|
|
43302
|
+
} catch(e) {
|
|
43303
|
+
// A projection resource that would not load leaves the string to be
|
|
43304
|
+
// parsed with whatever is already known.
|
|
43305
|
+
}
|
|
43306
|
+
crs = tryParseCrsString(str);
|
|
43307
|
+
if (crs) return {crsString: str, crs: crs};
|
|
43308
|
+
// A projection with no earth to sit on is tried again on a WGS-84 one, the
|
|
43309
|
+
// datum mapshaper assumes for any data that does not name one.
|
|
43310
|
+
assumed = addAssumedDatum(str);
|
|
43311
|
+
crs = assumed ? tryParseCrsString(assumed) : null;
|
|
43312
|
+
return crs ? {crsString: assumed, crs: crs, assumedDatum: true} : null;
|
|
43313
|
+
}
|
|
43314
|
+
|
|
43315
|
+
// GeoTIFF metadata can describe a projection without saying what shape of earth
|
|
43316
|
+
// it applies to: the geo keys carry a transformation and its parameters, and
|
|
43317
|
+
// the datum keys are absent or name something the EPSG database does not have.
|
|
43318
|
+
// Since the geokeys-to-proj4 library ends every definition with +no_defs, proj
|
|
43319
|
+
// will not supply its usual default either, and the whole projection is lost
|
|
43320
|
+
// over a missing ellipsoid. Returns the definition with a WGS-84 datum added,
|
|
43321
|
+
// or null if it already has one (or is not a proj4 definition at all).
|
|
43322
|
+
function addAssumedDatum(str) {
|
|
43323
|
+
if (!/\+proj=/.test(str) || /\+(datum|ellps|a|b|R|nadgrids|init)=/.test(str)) {
|
|
43324
|
+
return null;
|
|
43325
|
+
}
|
|
43326
|
+
return str + ' +datum=WGS84';
|
|
43327
|
+
}
|
|
43328
|
+
|
|
43329
|
+
// What to say about a raster that arrives without a CRS. A file that simply has
|
|
43330
|
+
// no CRS metadata is the ordinary case for an image, and is treated the way a
|
|
43331
|
+
// vector file with no projection metadata is: coordinates in the decimal-degree
|
|
43332
|
+
// range are taken for WGS-84 lat-long when something needs to know, which is
|
|
43333
|
+
// worth saying but not worth warning about. Metadata that mapshaper could not
|
|
43334
|
+
// make sense of is a different matter, and keeps the warning.
|
|
43335
|
+
function reportMissingCrs(dataset, crsInfo, sidecarString) {
|
|
43336
|
+
if (crsInfo.absent && !sidecarString) {
|
|
43337
|
+
if (probablyDecimalDegreeBounds(getDatasetBounds(dataset))) {
|
|
43338
|
+
message('This GeoTIFF has no CRS metadata. Its coordinates are in the',
|
|
43339
|
+
'decimal-degree range, so they are taken for WGS 84 lat-long.');
|
|
43340
|
+
} else {
|
|
43341
|
+
message('This GeoTIFF has no CRS metadata, so what its coordinates refer',
|
|
43342
|
+
'to is unknown, and projecting it or showing it over a basemap are',
|
|
43343
|
+
'unavailable. If you know the CRS, name it with -proj init=<crs>',
|
|
43344
|
+
'crs=<crs>');
|
|
43345
|
+
}
|
|
43207
43346
|
return;
|
|
43208
43347
|
}
|
|
43209
|
-
|
|
43348
|
+
warnOnce(getGeoTIFFCrsWarning(crsInfo,
|
|
43349
|
+
getUnusableCrsDetail(crsInfo.crsString, sidecarString)));
|
|
43350
|
+
}
|
|
43351
|
+
|
|
43352
|
+
function getUnusableCrsDetail(fileString, sidecarString) {
|
|
43353
|
+
var tried = [fileString, sidecarString].filter(Boolean);
|
|
43354
|
+
if (!tried.length) return null;
|
|
43355
|
+
return 'Unable to use projection ' + tried.join(' or ');
|
|
43210
43356
|
}
|
|
43211
43357
|
|
|
43212
43358
|
async function getGeoTIFFCrsInfo(image) {
|
|
43213
43359
|
var keys = image.getGeoKeys && image.getGeoKeys() || {};
|
|
43214
43360
|
var code = keys.ProjectedCSTypeGeoKey;
|
|
43215
43361
|
var customProjection;
|
|
43362
|
+
if (!hasCrsGeoKeys(keys)) {
|
|
43363
|
+
// Asked about a file that says nothing, the geokeys-to-proj4 library
|
|
43364
|
+
// answers "+proj=longlat" rather than nothing, having taken the silence for
|
|
43365
|
+
// a geographic CRS. That is a guess about the coordinates, not something
|
|
43366
|
+
// read out of the file, and mapshaper makes it later and better, from the
|
|
43367
|
+
// coordinate range, in the same way it does for a vector file with no
|
|
43368
|
+
// projection metadata.
|
|
43369
|
+
return {crsString: null, absent: true};
|
|
43370
|
+
}
|
|
43216
43371
|
if (isGeoTIFFAuthorityCode(code)) {
|
|
43217
43372
|
return {crsString: 'EPSG:' + code};
|
|
43218
43373
|
}
|
|
@@ -43230,6 +43385,23 @@ ${svg}
|
|
|
43230
43385
|
return 'The GeoTIFF does not contain usable CRS data';
|
|
43231
43386
|
}
|
|
43232
43387
|
|
|
43388
|
+
// Keys that say something about the CRS itself, as opposed to how the pixels
|
|
43389
|
+
// are laid out (GTRasterTypeGeoKey) or what the file calls things (the
|
|
43390
|
+
// citation keys).
|
|
43391
|
+
var CRS_GEO_KEYS = [
|
|
43392
|
+
'GTModelTypeGeoKey',
|
|
43393
|
+
'GeographicTypeGeoKey', 'GeogGeodeticDatumGeoKey', 'GeogEllipsoidGeoKey',
|
|
43394
|
+
'GeogPrimeMeridianGeoKey', 'GeogSemiMajorAxisGeoKey',
|
|
43395
|
+
'GeogSemiMinorAxisGeoKey', 'GeogInvFlatteningGeoKey',
|
|
43396
|
+
'ProjectedCSTypeGeoKey', 'ProjectionGeoKey', 'ProjCoordTransGeoKey'
|
|
43397
|
+
];
|
|
43398
|
+
|
|
43399
|
+
function hasCrsGeoKeys(keys) {
|
|
43400
|
+
return CRS_GEO_KEYS.some(function(name) {
|
|
43401
|
+
return keys[name] !== undefined && keys[name] !== null;
|
|
43402
|
+
});
|
|
43403
|
+
}
|
|
43404
|
+
|
|
43233
43405
|
function isGeoTIFFAuthorityCode(code) {
|
|
43234
43406
|
return code > 0 && code != 32767;
|
|
43235
43407
|
}
|
|
@@ -44438,6 +44610,14 @@ ${svg}
|
|
|
44438
44610
|
data = obj.prj;
|
|
44439
44611
|
dataset = {layers: [], info: {wkt1: data.content}};
|
|
44440
44612
|
|
|
44613
|
+
} else if (obj.aux) {
|
|
44614
|
+
// A sidecar on its own carries nothing but a CRS, which is still worth
|
|
44615
|
+
// reading: it can be the source of a -proj command, and importing the
|
|
44616
|
+
// raster it belongs to picks it up anyway.
|
|
44617
|
+
dataFmt = 'aux';
|
|
44618
|
+
data = obj.aux;
|
|
44619
|
+
dataset = {layers: [], info: getAuxCrsInfo(data.content)};
|
|
44620
|
+
|
|
44441
44621
|
} else if (obj.kml) {
|
|
44442
44622
|
dataFmt = 'kml';
|
|
44443
44623
|
data = obj.kml;
|
|
@@ -44483,7 +44663,7 @@ ${svg}
|
|
|
44483
44663
|
} else if (obj.geotiff) {
|
|
44484
44664
|
dataFmt = 'geotiff';
|
|
44485
44665
|
data = obj.geotiff;
|
|
44486
|
-
dataset = await importGeoTIFF(data, opts);
|
|
44666
|
+
dataset = await importGeoTIFF(data, opts, obj.aux);
|
|
44487
44667
|
} else if (obj.png || obj.jpeg) {
|
|
44488
44668
|
dataFmt = obj.png ? 'png' : 'jpeg';
|
|
44489
44669
|
data = obj[dataFmt];
|
|
@@ -44545,6 +44725,14 @@ ${svg}
|
|
|
44545
44725
|
return dataset;
|
|
44546
44726
|
}
|
|
44547
44727
|
|
|
44728
|
+
// What a raster sidecar amounts to on its own: a projection, stated where the
|
|
44729
|
+
// contents of a .prj file would go if it is WKT, as a CRS string otherwise.
|
|
44730
|
+
function getAuxCrsInfo(content) {
|
|
44731
|
+
var srs = parseAuxCrsString(content);
|
|
44732
|
+
if (!srs) return {};
|
|
44733
|
+
return auxCrsIsWkt(srs) ? {wkt1: srs} : {crs_string: srs};
|
|
44734
|
+
}
|
|
44735
|
+
|
|
44548
44736
|
function importDbf(input, opts) {
|
|
44549
44737
|
var table;
|
|
44550
44738
|
opts = utils.extend({}, opts);
|
|
@@ -45015,6 +45203,8 @@ ${svg}
|
|
|
45015
45203
|
readShapefileAuxFiles(path, input, cache);
|
|
45016
45204
|
} else if (isRasterImageInputType(fileType)) {
|
|
45017
45205
|
readRasterImageAuxFiles(path, input, cache);
|
|
45206
|
+
} else if (fileType == 'geotiff') {
|
|
45207
|
+
readGeoTIFFAuxFiles(path, input, cache);
|
|
45018
45208
|
}
|
|
45019
45209
|
if (fileType == 'shp' && !input.dbf) {
|
|
45020
45210
|
message(utils.format("[%s] .dbf file is missing - shapes imported without attribute data.", path));
|
|
@@ -45139,6 +45329,13 @@ ${svg}
|
|
|
45139
45329
|
}
|
|
45140
45330
|
}
|
|
45141
45331
|
|
|
45332
|
+
function readGeoTIFFAuxFiles(path, obj, cache) {
|
|
45333
|
+
var auxPath = getAuxFilename(path);
|
|
45334
|
+
if (cli.isFile(auxPath, cache)) {
|
|
45335
|
+
obj.aux = {filename: auxPath, content: cli.readFile(auxPath, 'utf-8', cache)};
|
|
45336
|
+
}
|
|
45337
|
+
}
|
|
45338
|
+
|
|
45142
45339
|
function readRasterImageAuxFiles(path, obj, cache) {
|
|
45143
45340
|
var prjPath = replaceFileExtension(path, 'prj');
|
|
45144
45341
|
var worldPath = findWorldFile(path, cache);
|
|
@@ -45176,11 +45373,15 @@ ${svg}
|
|
|
45176
45373
|
|
|
45177
45374
|
function removeRasterImageSidecars(files) {
|
|
45178
45375
|
var imageBases = {};
|
|
45376
|
+
var rasterPaths = {};
|
|
45179
45377
|
files.forEach(function(file) {
|
|
45180
45378
|
var type = guessInputFileType(file);
|
|
45181
45379
|
if (isRasterImageInputType(type)) {
|
|
45182
45380
|
imageBases[getFileBase(file).toLowerCase()] = true;
|
|
45183
45381
|
}
|
|
45382
|
+
if (isRasterImageInputType(type) || type == 'geotiff') {
|
|
45383
|
+
rasterPaths[file.toLowerCase()] = true;
|
|
45384
|
+
}
|
|
45184
45385
|
});
|
|
45185
45386
|
return files.filter(function(file) {
|
|
45186
45387
|
var ext = getFileExtension(file).toLowerCase();
|
|
@@ -45189,6 +45390,11 @@ ${svg}
|
|
|
45189
45390
|
if ((type == 'prj' || isWorldFileExtension(ext)) && imageBases[base]) {
|
|
45190
45391
|
return false;
|
|
45191
45392
|
}
|
|
45393
|
+
// A sidecar is read along with the raster it names, so importing it
|
|
45394
|
+
// separately would only add an empty layer.
|
|
45395
|
+
if (type == 'aux' && rasterPaths[getAuxSourceFilename(file).toLowerCase()]) {
|
|
45396
|
+
return false;
|
|
45397
|
+
}
|
|
45192
45398
|
return true;
|
|
45193
45399
|
});
|
|
45194
45400
|
}
|
|
@@ -75691,7 +75897,7 @@ ${svg}
|
|
|
75691
75897
|
return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
|
|
75692
75898
|
}
|
|
75693
75899
|
|
|
75694
|
-
var version = "0.7.
|
|
75900
|
+
var version = "0.7.52";
|
|
75695
75901
|
|
|
75696
75902
|
// Parse command line args into commands and run them
|
|
75697
75903
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|
|
@@ -76353,6 +76559,256 @@ ${svg}
|
|
|
76353
76559
|
return false;
|
|
76354
76560
|
}
|
|
76355
76561
|
|
|
76562
|
+
// Utilities for dividing a path at one or two points ("snipping").
|
|
76563
|
+
//
|
|
76564
|
+
// The new arcs are appended to the end of the collection; the original arc is
|
|
76565
|
+
// left in place (it may still be referenced by other shapes). Callers are
|
|
76566
|
+
// responsible for updating the shapes that should use the new arcs.
|
|
76567
|
+
|
|
76568
|
+
// Divide an arc's vertices into slices at one or two cut locations.
|
|
76569
|
+
// n: number of vertices in the arc
|
|
76570
|
+
// cuts: one or two cut objects, ordered from the start of the arc:
|
|
76571
|
+
// {offset: i, point: [x, y]} cuts the segment between vertices i and i + 1
|
|
76572
|
+
// {offset: i, point: null} cuts at vertex i (which is duplicated, so that
|
|
76573
|
+
// both adjacent slices contain it)
|
|
76574
|
+
// Returns slice descriptors {start, end, prefix, suffix}, where start and end
|
|
76575
|
+
// are an inclusive vertex range (empty if start > end) and prefix/suffix are
|
|
76576
|
+
// points inserted at a cut location.
|
|
76577
|
+
function getArcSlices(n, cuts) {
|
|
76578
|
+
var slices = [];
|
|
76579
|
+
var start = 0;
|
|
76580
|
+
var prefix = null;
|
|
76581
|
+
cuts.forEach(function(cut) {
|
|
76582
|
+
var i = cut.offset;
|
|
76583
|
+
if (cut.point) {
|
|
76584
|
+
if (i < 0 || i > n - 2) error('Out-of-range arc cut offset:', i);
|
|
76585
|
+
slices.push({start: start, end: i, prefix: prefix, suffix: cut.point});
|
|
76586
|
+
prefix = cut.point;
|
|
76587
|
+
start = i + 1;
|
|
76588
|
+
} else {
|
|
76589
|
+
if (i < 1 || i > n - 2) error('Out-of-range arc cut offset:', i);
|
|
76590
|
+
slices.push({start: start, end: i, prefix: prefix, suffix: null});
|
|
76591
|
+
prefix = null;
|
|
76592
|
+
start = i;
|
|
76593
|
+
}
|
|
76594
|
+
});
|
|
76595
|
+
slices.push({start: start, end: n - 1, prefix: prefix, suffix: null});
|
|
76596
|
+
return slices;
|
|
76597
|
+
}
|
|
76598
|
+
|
|
76599
|
+
function getArcSliceLength(slice) {
|
|
76600
|
+
var n = slice.end >= slice.start ? slice.end - slice.start + 1 : 0;
|
|
76601
|
+
if (slice.prefix) n++;
|
|
76602
|
+
if (slice.suffix) n++;
|
|
76603
|
+
return n;
|
|
76604
|
+
}
|
|
76605
|
+
|
|
76606
|
+
// Append the slices of a divided arc to an ArcCollection.
|
|
76607
|
+
// Returns the ids of the new arcs, ordered from the start of the original arc.
|
|
76608
|
+
function splitArcAtCuts(arcs, arcId, cuts) {
|
|
76609
|
+
var data = arcs.getVertexData();
|
|
76610
|
+
var zlimit = arcs.getRetainedInterval();
|
|
76611
|
+
var arcStart = data.ii[arcId];
|
|
76612
|
+
var arcCount = data.nn.length;
|
|
76613
|
+
var slices = getArcSlices(data.nn[arcId], cuts);
|
|
76614
|
+
var pointCount = data.xx.length;
|
|
76615
|
+
var added = slices.reduce(function(memo, slice) {
|
|
76616
|
+
return memo + getArcSliceLength(slice);
|
|
76617
|
+
}, 0);
|
|
76618
|
+
var xx2 = new Float64Array(pointCount + added);
|
|
76619
|
+
var yy2 = new Float64Array(pointCount + added);
|
|
76620
|
+
var zz2 = arcs.isFlat() ? null : new Float64Array(pointCount + added);
|
|
76621
|
+
var nn2 = new Uint32Array(arcCount + slices.length);
|
|
76622
|
+
var i = pointCount;
|
|
76623
|
+
var ids = [];
|
|
76624
|
+
|
|
76625
|
+
xx2.set(data.xx);
|
|
76626
|
+
yy2.set(data.yy);
|
|
76627
|
+
nn2.set(data.nn);
|
|
76628
|
+
if (zz2) zz2.set(data.zz);
|
|
76629
|
+
|
|
76630
|
+
slices.forEach(function(slice, sliceId) {
|
|
76631
|
+
var first = i;
|
|
76632
|
+
var j;
|
|
76633
|
+
if (slice.prefix) {
|
|
76634
|
+
xx2[i] = slice.prefix[0];
|
|
76635
|
+
yy2[i] = slice.prefix[1];
|
|
76636
|
+
i++;
|
|
76637
|
+
}
|
|
76638
|
+
for (j = slice.start; j <= slice.end; j++) {
|
|
76639
|
+
xx2[i] = data.xx[arcStart + j];
|
|
76640
|
+
yy2[i] = data.yy[arcStart + j];
|
|
76641
|
+
if (zz2) zz2[i] = data.zz[arcStart + j];
|
|
76642
|
+
i++;
|
|
76643
|
+
}
|
|
76644
|
+
if (slice.suffix) {
|
|
76645
|
+
xx2[i] = slice.suffix[0];
|
|
76646
|
+
yy2[i] = slice.suffix[1];
|
|
76647
|
+
i++;
|
|
76648
|
+
}
|
|
76649
|
+
if (zz2) {
|
|
76650
|
+
// arc endpoints must never be removed by simplification
|
|
76651
|
+
zz2[first] = Infinity;
|
|
76652
|
+
zz2[i - 1] = Infinity;
|
|
76653
|
+
}
|
|
76654
|
+
nn2[arcCount + sliceId] = i - first;
|
|
76655
|
+
ids.push(arcCount + sliceId);
|
|
76656
|
+
});
|
|
76657
|
+
|
|
76658
|
+
arcs.updateVertexData(nn2, xx2, yy2, zz2);
|
|
76659
|
+
arcs.setRetainedInterval(zlimit);
|
|
76660
|
+
return ids;
|
|
76661
|
+
}
|
|
76662
|
+
|
|
76663
|
+
// A cut identifies one location along a path (an array of arc ids):
|
|
76664
|
+
// seq: index into the path of the arc containing the cut
|
|
76665
|
+
// offset: vertex index within that arc, in traversal orientation
|
|
76666
|
+
// point: coords of the cut, or null to cut at vertex <offset>
|
|
76667
|
+
// displayPoint: coords of the cut in the GUI's display CRS, or null
|
|
76668
|
+
// t: position along the segment (0-1), used to order two cuts
|
|
76669
|
+
// that land on the same segment
|
|
76670
|
+
|
|
76671
|
+
// Work out how to divide a path at one cut (open path) or two cuts (ring),
|
|
76672
|
+
// without mutating anything.
|
|
76673
|
+
//
|
|
76674
|
+
// path: array of arc ids
|
|
76675
|
+
// cutsArg: one cut (open path) or two cuts (ring), in any order
|
|
76676
|
+
// arcLen: function(arcId) returning the number of vertices in an arc
|
|
76677
|
+
// baseId: arcs.size(), used to predict the ids of arcs that will be appended
|
|
76678
|
+
// closed: whether the path is a ring
|
|
76679
|
+
//
|
|
76680
|
+
// Returns {splits, a, b}, where splits are arc divisions to perform in order
|
|
76681
|
+
// and a and b are the two resulting paths, or null if the cuts are degenerate
|
|
76682
|
+
// (at the endpoint of an open path, or two cuts in the same place on a ring).
|
|
76683
|
+
//
|
|
76684
|
+
function planPathDivision(path, cutsArg, arcLen, baseId, closed) {
|
|
76685
|
+
var cuts = sortCuts(cutsArg);
|
|
76686
|
+
var boundaryCuts = []; // cuts that fall between two arcs
|
|
76687
|
+
var interiorCuts = {}; // cuts that fall inside an arc, by path index
|
|
76688
|
+
var expanded = []; // path with divided arcs replaced by their pieces
|
|
76689
|
+
var positions = []; // position of each cut within the expanded path
|
|
76690
|
+
var splits = [];
|
|
76691
|
+
var nextArcId = baseId;
|
|
76692
|
+
var a, b, q, q1, q2;
|
|
76693
|
+
|
|
76694
|
+
if (cuts.length != (closed ? 2 : 1)) return null;
|
|
76695
|
+
|
|
76696
|
+
cuts.forEach(function(cut, cutId) {
|
|
76697
|
+
var n = arcLen(absArcId(path[cut.seq]));
|
|
76698
|
+
var seq;
|
|
76699
|
+
if (!cut.point && (cut.offset === 0 || cut.offset === n - 1)) {
|
|
76700
|
+
// a cut at an arc endpoint doesn't divide the arc, it divides the
|
|
76701
|
+
// sequence of arcs
|
|
76702
|
+
seq = cut.offset === 0 ? cut.seq : cut.seq + 1;
|
|
76703
|
+
if (closed && seq === path.length) seq = 0; // a ring's seam is one place
|
|
76704
|
+
boundaryCuts.push({cutId: cutId, seq: seq});
|
|
76705
|
+
} else {
|
|
76706
|
+
if (!interiorCuts[cut.seq]) interiorCuts[cut.seq] = [];
|
|
76707
|
+
interiorCuts[cut.seq].push({cutId: cutId, cut: cut, arcLen: n});
|
|
76708
|
+
}
|
|
76709
|
+
});
|
|
76710
|
+
|
|
76711
|
+
for (var k=0; k<=path.length; k++) {
|
|
76712
|
+
boundaryCuts.forEach(function(item) {
|
|
76713
|
+
if (item.seq === k) positions[item.cutId] = expanded.length;
|
|
76714
|
+
});
|
|
76715
|
+
if (k === path.length) break;
|
|
76716
|
+
if (!interiorCuts[k]) {
|
|
76717
|
+
expanded.push(path[k]);
|
|
76718
|
+
continue;
|
|
76719
|
+
}
|
|
76720
|
+
addDividedArc(interiorCuts[k], path[k]);
|
|
76721
|
+
}
|
|
76722
|
+
|
|
76723
|
+
if (!closed) {
|
|
76724
|
+
q = positions[0];
|
|
76725
|
+
if (q === 0 || q === expanded.length) return null; // cut at a path endpoint
|
|
76726
|
+
a = expanded.slice(0, q);
|
|
76727
|
+
b = expanded.slice(q);
|
|
76728
|
+
} else {
|
|
76729
|
+
q1 = Math.min(positions[0], positions[1]);
|
|
76730
|
+
q2 = Math.max(positions[0], positions[1]);
|
|
76731
|
+
if (q1 === q2) return null; // both cuts in the same place
|
|
76732
|
+
a = expanded.slice(q1, q2);
|
|
76733
|
+
b = expanded.slice(q2).concat(expanded.slice(0, q1));
|
|
76734
|
+
}
|
|
76735
|
+
return {splits: splits, a: a, b: b};
|
|
76736
|
+
|
|
76737
|
+
function addDividedArc(group, arcId) {
|
|
76738
|
+
var fwd = arcId >= 0;
|
|
76739
|
+
var n = group[0].arcLen;
|
|
76740
|
+
// convert traversal offsets to forward-orientation offsets
|
|
76741
|
+
var fwdCuts = group.map(function(item) {
|
|
76742
|
+
return {
|
|
76743
|
+
offset: fwd ? item.cut.offset :
|
|
76744
|
+
n - (item.cut.point ? 2 : 1) - item.cut.offset,
|
|
76745
|
+
point: item.cut.point || null,
|
|
76746
|
+
displayPoint: item.cut.displayPoint || null
|
|
76747
|
+
};
|
|
76748
|
+
});
|
|
76749
|
+
// a reversed arc is traversed from its last vertex, so traversal order is
|
|
76750
|
+
// the reverse of forward order
|
|
76751
|
+
var pieces = getSplitArcIds(nextArcId, group.length, fwd);
|
|
76752
|
+
splits.push({
|
|
76753
|
+
arcId: absArcId(arcId),
|
|
76754
|
+
cuts: fwd ? fwdCuts : fwdCuts.concat().reverse()
|
|
76755
|
+
});
|
|
76756
|
+
nextArcId += group.length + 1;
|
|
76757
|
+
pieces.forEach(function(id, i) {
|
|
76758
|
+
if (i > 0) positions[group[i - 1].cutId] = expanded.length;
|
|
76759
|
+
expanded.push(id);
|
|
76760
|
+
});
|
|
76761
|
+
}
|
|
76762
|
+
}
|
|
76763
|
+
|
|
76764
|
+
// Ids of the pieces of a divided arc, in traversal order
|
|
76765
|
+
function getSplitArcIds(baseId, cutCount, fwd) {
|
|
76766
|
+
var ids = [];
|
|
76767
|
+
for (var i=0; i<=cutCount; i++) {
|
|
76768
|
+
ids.push(fwd ? baseId + i : ~(baseId + cutCount - i));
|
|
76769
|
+
}
|
|
76770
|
+
return ids;
|
|
76771
|
+
}
|
|
76772
|
+
|
|
76773
|
+
function sortCuts(cuts) {
|
|
76774
|
+
return cuts.concat().sort(function(a, b) {
|
|
76775
|
+
return a.seq - b.seq || getCutOffset(a) - getCutOffset(b);
|
|
76776
|
+
});
|
|
76777
|
+
}
|
|
76778
|
+
|
|
76779
|
+
// A sortable offset within one arc. A cut on the segment following a vertex
|
|
76780
|
+
// comes after the vertex itself; two cuts on the same segment are ordered by
|
|
76781
|
+
// their position along it.
|
|
76782
|
+
function getCutOffset(cut) {
|
|
76783
|
+
if (!cut.point) return cut.offset;
|
|
76784
|
+
return cut.offset + 0.5 + (cut.t > 0 ? Math.min(cut.t, 1) : 0) * 0.4;
|
|
76785
|
+
}
|
|
76786
|
+
|
|
76787
|
+
function getPlanarPathLength(ids, arcs) {
|
|
76788
|
+
var iter = arcs.getShapeIter(ids);
|
|
76789
|
+
var len = 0;
|
|
76790
|
+
var x, y;
|
|
76791
|
+
if (iter.hasNext()) {
|
|
76792
|
+
x = iter.x;
|
|
76793
|
+
y = iter.y;
|
|
76794
|
+
}
|
|
76795
|
+
while (iter.hasNext()) {
|
|
76796
|
+
len += distance2D$1(x, y, iter.x, iter.y);
|
|
76797
|
+
x = iter.x;
|
|
76798
|
+
y = iter.y;
|
|
76799
|
+
}
|
|
76800
|
+
return len;
|
|
76801
|
+
}
|
|
76802
|
+
|
|
76803
|
+
var ArcSplit = /*#__PURE__*/Object.freeze({
|
|
76804
|
+
__proto__: null,
|
|
76805
|
+
getArcSliceLength: getArcSliceLength,
|
|
76806
|
+
getArcSlices: getArcSlices,
|
|
76807
|
+
getPlanarPathLength: getPlanarPathLength,
|
|
76808
|
+
planPathDivision: planPathDivision,
|
|
76809
|
+
splitArcAtCuts: splitArcAtCuts
|
|
76810
|
+
});
|
|
76811
|
+
|
|
76356
76812
|
var BOX_BLUR_PASSES = 3;
|
|
76357
76813
|
|
|
76358
76814
|
function blurRasterGrid(raster, optsArg, crs) {
|
|
@@ -77529,6 +77985,7 @@ ${svg}
|
|
|
77529
77985
|
AnchorPoints,
|
|
77530
77986
|
ArcClassifier,
|
|
77531
77987
|
ArcDissolve,
|
|
77988
|
+
ArcSplit,
|
|
77532
77989
|
ArcUtils,
|
|
77533
77990
|
Bbox2Clipping,
|
|
77534
77991
|
BinArray$1,
|
|
@@ -77581,6 +78038,7 @@ ${svg}
|
|
|
77581
78038
|
JoinTables,
|
|
77582
78039
|
JsonImport,
|
|
77583
78040
|
JsonTable,
|
|
78041
|
+
GeoTIFFAux,
|
|
77584
78042
|
GeoTIFFEncode,
|
|
77585
78043
|
GeoTIFFGeoKeys,
|
|
77586
78044
|
KeepShapes,
|