mapshaper 0.7.2 → 0.7.4

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.
Files changed (86) hide show
  1. package/LICENSE +1 -1
  2. package/bin/mapshaper-gui +31 -7
  3. package/mapshaper.js +45 -42
  4. package/package.json +8 -7
  5. package/www/geopackage.js +10 -10
  6. package/www/index.html +32 -4
  7. package/www/mapshaper-gui.js +529 -83
  8. package/www/mapshaper.js +45 -42
  9. package/www/modules.js +5 -5
  10. package/www/page.css +157 -0
  11. package/www/docs/_assets/cmd-search.js +0 -213
  12. package/www/docs/_assets/docs.css +0 -722
  13. package/www/docs/_assets/docs.js +0 -75
  14. package/www/docs/_assets/highlight.css +0 -10
  15. package/www/docs/essentials/command-line.html +0 -127
  16. package/www/docs/essentials/command-line.html.md +0 -112
  17. package/www/docs/essentials/web-app.html +0 -138
  18. package/www/docs/essentials/web-app.html.md +0 -106
  19. package/www/docs/examples/basics.html +0 -275
  20. package/www/docs/examples/basics.html.md +0 -370
  21. package/www/docs/examples/data/Makefile +0 -31
  22. package/www/docs/examples/data/globe.msx +0 -0
  23. package/www/docs/examples/data/globe.svg +0 -616
  24. package/www/docs/examples/data/globe.txt +0 -21
  25. package/www/docs/examples/data/globe.zip +0 -0
  26. package/www/docs/examples/data/ne_50m_admin_0_countries.geojson +0 -1
  27. package/www/docs/examples/data/ne_50m_admin_1_states_provinces_lakes.geojson +0 -1
  28. package/www/docs/examples/data/us-states.msx +0 -0
  29. package/www/docs/examples/data/us-states.svg +0 -56
  30. package/www/docs/examples/data/us-states.txt +0 -6
  31. package/www/docs/examples/data/us-states.zip +0 -0
  32. package/www/docs/examples/globe.html +0 -108
  33. package/www/docs/examples/globe.html.md +0 -64
  34. package/www/docs/examples/us-states.html +0 -88
  35. package/www/docs/examples/us-states.html.md +0 -44
  36. package/www/docs/formats/csv.html +0 -127
  37. package/www/docs/formats/csv.html.md +0 -97
  38. package/www/docs/formats/dbf.html +0 -87
  39. package/www/docs/formats/dbf.html.md +0 -39
  40. package/www/docs/formats/flatgeobuf.html +0 -85
  41. package/www/docs/formats/flatgeobuf.html.md +0 -41
  42. package/www/docs/formats/geojson.html +0 -107
  43. package/www/docs/formats/geojson.html.md +0 -65
  44. package/www/docs/formats/geopackage.html +0 -87
  45. package/www/docs/formats/geopackage.html.md +0 -42
  46. package/www/docs/formats/json.html +0 -83
  47. package/www/docs/formats/json.html.md +0 -35
  48. package/www/docs/formats/kml.html +0 -82
  49. package/www/docs/formats/kml.html.md +0 -39
  50. package/www/docs/formats/overview.html +0 -191
  51. package/www/docs/formats/overview.html.md +0 -34
  52. package/www/docs/formats/shapefile.html +0 -123
  53. package/www/docs/formats/shapefile.html.md +0 -84
  54. package/www/docs/formats/snapshot.html +0 -87
  55. package/www/docs/formats/snapshot.html.md +0 -39
  56. package/www/docs/formats/svg.html +0 -99
  57. package/www/docs/formats/svg.html.md +0 -51
  58. package/www/docs/formats/topojson.html +0 -102
  59. package/www/docs/formats/topojson.html.md +0 -54
  60. package/www/docs/gallery/index.html +0 -80
  61. package/www/docs/gallery/index.html.md +0 -29
  62. package/www/docs/guides/combining-layers.html +0 -109
  63. package/www/docs/guides/combining-layers.html.md +0 -89
  64. package/www/docs/guides/expressions.html +0 -600
  65. package/www/docs/guides/expressions.html.md +0 -376
  66. package/www/docs/guides/programmatic.html +0 -117
  67. package/www/docs/guides/programmatic.html.md +0 -91
  68. package/www/docs/guides/projections.html +0 -158
  69. package/www/docs/guides/projections.html.md +0 -118
  70. package/www/docs/guides/simplification.html +0 -110
  71. package/www/docs/guides/simplification.html.md +0 -96
  72. package/www/docs/guides/topology.html +0 -90
  73. package/www/docs/guides/topology.html.md +0 -63
  74. package/www/docs/images/cal-counties.png +0 -0
  75. package/www/docs/images/simplification-detail.png +0 -0
  76. package/www/docs/images/simplification-dp.png +0 -0
  77. package/www/docs/images/simplification-mod2.png +0 -0
  78. package/www/docs/images/tiger-counties.png +0 -0
  79. package/www/docs/index.html +0 -101
  80. package/www/docs/index.html.md +0 -59
  81. package/www/docs/reference.html +0 -1302
  82. package/www/docs/reference.html.md +0 -1817
  83. package/www/docs/whats-new.html +0 -77
  84. package/www/docs/whats-new.html.md +0 -59
  85. package/www/llms-full.txt +0 -4053
  86. package/www/llms.txt +0 -55
package/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2025, Matthew Bloch
1
+ Copyright (c) 2026, Matthew Bloch
2
2
 
3
3
  This Source Code Form is subject to the terms of the Mozilla Public
4
4
  License, v. 2.0. If a copy of the MPL was not distributed with this
package/bin/mapshaper-gui CHANGED
@@ -16,6 +16,7 @@ var defaultPort = 5555,
16
16
  .option('-t, --target <name>', 'name of layer to select initially')
17
17
  .addOption(new commander.Option('-b, --blurb <text>',
18
18
  'replace the default blurb on the import screen').hideHelp())
19
+ .argument('[file...]', 'data files to load')
19
20
  .helpOption('-h, --help', 'show this help message')
20
21
  .version(require('../package.json').version)
21
22
  .parse(process.argv),
@@ -25,7 +26,6 @@ var defaultPort = 5555,
25
26
  url = require("url"),
26
27
  fs = require("fs"),
27
28
  Cookies = require("cookies"),
28
- opn = require("opn"),
29
29
  webRoot = path.join(__dirname, "../www"),
30
30
  port = parseInt(opts.port, 10) || defaultPort,
31
31
  dataFiles = expandShapefiles(program.args),
@@ -66,10 +66,24 @@ function startServer(port) {
66
66
  // block attempts to load files outside webroot
67
67
  serve404(response);
68
68
  } else if (uri == '/close') {
69
- // end process when page closes, unless page is immediately refreshed
69
+ // End process when page closes, unless page is immediately refreshed.
70
+ // The grace window (1500ms) gives the refreshed page time to send
71
+ // /cancel-close. A short window is not safe here: with sendBeacon,
72
+ // /close frequently arrives at the server *after* the new page's
73
+ // resource fetches have already been served, so we can't rely on
74
+ // those fetches to reset the timer for us.
70
75
  timeout = setTimeout(function() {
71
76
  process.exit(0);
72
- }, 200);
77
+ }, 1500);
78
+ response.writeHead(204); // no content
79
+ response.end();
80
+ } else if (uri == '/cancel-close') {
81
+ // The page-load-time refresh detection in src/gui/gui.mjs sends this
82
+ // when sessionStorage indicates the new page is a refresh of the
83
+ // previous tab incarnation. clearTimeout above already cancelled any
84
+ // pending exit; we just need to acknowledge the request.
85
+ response.writeHead(204);
86
+ response.end();
73
87
  } else if (uri == "/manifest.js") {
74
88
  if (!sessionId && opts.directSave) {
75
89
  // create a session id for authenticating requests to save files
@@ -86,14 +100,24 @@ function startServer(port) {
86
100
  } else if (uri.indexOf('/save') === 0) {
87
101
  saveContent(request, response);
88
102
  } else {
89
- // serve a file from the web root
90
- if (uri == '/') {
91
- uri = '/index.html';
103
+ // serve a file from the web root. Translate directory paths (any URI
104
+ // ending in '/') to their index.html so that links like '/docs/' work
105
+ // the way they do behind a normal static-file host (mapshaper.org).
106
+ if (uri.endsWith('/')) {
107
+ uri += 'index.html';
92
108
  }
93
109
  serveFile(getAssetFilePath(uri), response);
94
110
  }
95
111
  }).listen(port, 'localhost', function() {
96
- opn("http://localhost:" + port);
112
+ // `open` is ESM-only since v9, so import it dynamically from this CJS bin
113
+ // script. Failure to launch a browser is non-fatal: the user can still
114
+ // visit the URL printed (or implied) by the running server.
115
+ import('open').then(function(mod) {
116
+ return mod.default("http://localhost:" + port);
117
+ }).catch(function(err) {
118
+ console.error("Could not open browser automatically; visit http://localhost:" + port + " manually.");
119
+ console.error(err && err.message || err);
120
+ });
97
121
  });
98
122
  }
99
123
 
package/mapshaper.js CHANGED
@@ -12416,8 +12416,8 @@
12416
12416
  return obj;
12417
12417
  };
12418
12418
 
12419
- async function writeFiles(exports, opts) {
12420
- return _writeFiles(exports, opts);
12419
+ async function writeFiles(exports$1, opts) {
12420
+ return _writeFiles(exports$1, opts);
12421
12421
  }
12422
12422
 
12423
12423
  // Used by GUI to replace the CLI version of writeFiles()
@@ -12426,24 +12426,24 @@
12426
12426
  _writeFiles = func;
12427
12427
  }
12428
12428
 
12429
- var _writeFiles = function(exports, opts) {
12430
- if (exports.length > 0 === false) {
12429
+ var _writeFiles = function(exports$1, opts) {
12430
+ if (exports$1.length > 0 === false) {
12431
12431
  message("No files to save");
12432
12432
  } else if (opts.dry_run) ; else if (opts.stdout) {
12433
12433
  // Using async writeFile() function -- synchronous output to stdout can
12434
12434
  // trigger EAGAIN error, e.g. when piped to less.
12435
- require$1('rw').writeFile('/dev/stdout', exports[0].content, function() {});
12435
+ require$1('rw').writeFile('/dev/stdout', exports$1[0].content, function() {});
12436
12436
  } else {
12437
12437
  if (opts.zip) {
12438
- exports = [{
12438
+ exports$1 = [{
12439
12439
  // TODO: add output subdirectory, if relevant
12440
12440
  filename: opts.zipfile || 'output.zip',
12441
- content: zipSync(exports)
12441
+ content: zipSync(exports$1)
12442
12442
  }];
12443
12443
  }
12444
- var paths = getOutputPaths(utils.pluck(exports, 'filename'), opts);
12444
+ var paths = getOutputPaths(utils.pluck(exports$1, 'filename'), opts);
12445
12445
  var inputFiles = getStashedVar('input_files');
12446
- exports.forEach(function(obj, i) {
12446
+ exports$1.forEach(function(obj, i) {
12447
12447
  var path = paths[i];
12448
12448
  if (obj.content instanceof ArrayBuffer) {
12449
12449
  // replacing content so ArrayBuffers can be gc'd
@@ -14993,10 +14993,10 @@
14993
14993
  return d;
14994
14994
  }
14995
14995
 
14996
- var cache$1 = {};
14996
+ var cache = {};
14997
14997
 
14998
14998
  function getParseRxp(fmt) {
14999
- if (fmt in cache$1) return cache$1[fmt];
14999
+ if (fmt in cache) return cache[fmt];
15000
15000
  var rxp = fmt;
15001
15001
  rxp = rxp.replace('[-]', '(?<prefix>-)?'); // optional -
15002
15002
  rxp = rxp.replace(/\[[NSEW, +-]{2,}\]/, '(?<prefix>$&)');
@@ -15020,11 +15020,11 @@
15020
15020
  rxp = '^' + rxp + '$';
15021
15021
  try {
15022
15022
  // TODO: make sure all DMS codes have been matched
15023
- cache$1[fmt] = new RegExp(rxp);
15023
+ cache[fmt] = new RegExp(rxp);
15024
15024
  } catch(e) {
15025
15025
  stop$1('Invalid DMS format string:', fmt);
15026
15026
  }
15027
- return cache$1[fmt];
15027
+ return cache[fmt];
15028
15028
  }
15029
15029
 
15030
15030
  function formatNumber(val, integers, decimals) {
@@ -21150,15 +21150,6 @@
21150
21150
  getSphereEffectParams: getSphereEffectParams
21151
21151
  });
21152
21152
 
21153
- var cache = {};
21154
- function fetchFileSync(url) {
21155
- if (url in cache) return cache[url];
21156
- var res = require$1('sync-request')('GET', url, {timeout: 2000});
21157
- var content = res.getBody().toString();
21158
- cache[url] = content;
21159
- return content;
21160
- }
21161
-
21162
21153
  // convert object properties to definitions for images and hatch fills
21163
21154
  function convertPropertiesToDefinitions(obj, defs) {
21164
21155
  procNode(obj);
@@ -21183,6 +21174,13 @@
21183
21174
  function convertSvgImage(obj, defs) {
21184
21175
  // Same-origin policy prevents embedding images in the web UI
21185
21176
  var href = obj.properties.href;
21177
+ // Remote URLs were previously fetched at export time so the SVG could be
21178
+ // embedded inline. That capability has been removed (it required a
21179
+ // synchronous-HTTP dependency built on a child-process hack); download
21180
+ // the asset first and reference it by local path instead.
21181
+ if (href.indexOf('http') === 0) {
21182
+ stop$1('Remote SVG asset references are not supported. Download the file first and reference it by a local path:', href);
21183
+ }
21186
21184
  // look for a previously added definition to use
21187
21185
  // (assumes that images that share the same href can also use the same defn)
21188
21186
  var item = utils.find(defs, function(item) {return item.href == href;});
@@ -21200,12 +21198,12 @@
21200
21198
  }
21201
21199
  }
21202
21200
 
21203
- // Returns the content of an SVG file from a local path or URL
21204
- // Returns '' if unable to get the content (e.g. due to cross-domain security rules)
21201
+ // Returns the content of an SVG file from a local path.
21202
+ // Returns '' if unable to get the content; the caller leaves the original
21203
+ // <image> tag in the output so the SVG renderer can attempt to load it.
21205
21204
  function serializeSvgImage(href, id) {
21206
21205
  var svg = '';
21207
21206
  try {
21208
- // try to download the SVG content and use that
21209
21207
  svg = convertSvgToDefn(getSvgContent(href), id) + '\n';
21210
21208
  svg = '<!-- ' + href + '-->\n' + svg; // add href as a comment, to aid in debugging
21211
21209
  } catch(e) {
@@ -21216,20 +21214,12 @@
21216
21214
  return svg;
21217
21215
  }
21218
21216
 
21219
- // href: A URL or a local path
21220
- // TODO: download SVG files asynchronously
21221
- // (currently, files are downloaded synchronously, which is obviously undesirable)
21222
- //
21217
+ // href: A local path
21223
21218
  function getSvgContent(href) {
21224
- var content;
21225
- if (href.indexOf('http') === 0) {
21226
- content = fetchFileSync(href);
21227
- } else if (require$1('fs').existsSync(href)) {
21228
- content = require$1('fs').readFileSync(href, 'utf8');
21229
- } else {
21230
- stop$1("Invalid SVG location:", href);
21219
+ if (require$1('fs').existsSync(href)) {
21220
+ return require$1('fs').readFileSync(href, 'utf8');
21231
21221
  }
21232
- return content;
21222
+ stop$1("Invalid SVG location:", href);
21233
21223
  }
21234
21224
 
21235
21225
  function convertSvgToDefn(svg, id) {
@@ -21372,12 +21362,27 @@ ${svg}
21372
21362
  var geojson = exportDatasetAsGeoJSON(d, opts);
21373
21363
  var features = geojson.features || geojson.geometries || (geojson.type ? [geojson] : []);
21374
21364
  var children = importGeoJSONFeatures(features, opts);
21365
+ // Drop empty placeholder <g/> elements (features whose geometry was null in the
21366
+ // source data, collapsed during simplification, or otherwise produced no
21367
+ // visible output). Keep the layer's records in lockstep so that data-*
21368
+ // attributes added via -o svg-data= remain correctly aligned.
21369
+ var records = lyr.data ? lyr.data.getRecords() : null;
21370
+ var keptRecords = records ? [] : null;
21371
+ children = children.filter(function(child, i) {
21372
+ if (isEmptyPlaceholder(child)) return false;
21373
+ if (keptRecords) keptRecords.push(records[i]);
21374
+ return true;
21375
+ });
21375
21376
  if (opts.svg_data && lyr.data) {
21376
- addDataAttributesToSVG(children, lyr.data, opts.svg_data);
21377
+ addDataAttributesToSVG(children, keptRecords, lyr.data.getFields(), opts.svg_data);
21377
21378
  }
21378
21379
  return children;
21379
21380
  }
21380
21381
 
21382
+ function isEmptyPlaceholder(o) {
21383
+ return o.tag == 'g' && (!o.children || o.children.length === 0);
21384
+ }
21385
+
21381
21386
  function validateSvgDataFields(layers, fieldsArg) {
21382
21387
  var missingFields = fieldsArg.reduce(function(memo, field) {
21383
21388
  if (!fieldExists(layers, field)) {
@@ -21397,15 +21402,13 @@ ${svg}
21397
21402
  }
21398
21403
  }
21399
21404
 
21400
- function addDataAttributesToSVG(children, table, fieldsArg) {
21401
- var allFields = table.getFields();
21405
+ function addDataAttributesToSVG(children, records, allFields, fieldsArg) {
21402
21406
  var dataFields = fieldsArg.indexOf('*') > -1 ? allFields.concat() : fieldsArg;
21403
21407
  var missingFields = utils.difference(dataFields, allFields);
21404
21408
  if (missingFields.length > 0) {
21405
21409
  dataFields = utils.difference(dataFields, missingFields);
21406
21410
  // stop("Missing data field(s):", missingFields.join(', '));
21407
21411
  }
21408
- var records = table.getRecords();
21409
21412
  var data = exportDataAttributesForSVG(records, dataFields);
21410
21413
  if (children.length != data.length) {
21411
21414
  error("Mismatch between number of SVG symbols and data attributes");
@@ -52127,7 +52130,7 @@ ${svg}
52127
52130
  });
52128
52131
  }
52129
52132
 
52130
- var version = "0.7.2";
52133
+ var version = "0.7.4";
52131
52134
 
52132
52135
  // Parse command line args into commands and run them
52133
52136
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -20,7 +20,7 @@
20
20
  "url": "git+https://github.com/mbloch/mapshaper.git"
21
21
  },
22
22
  "engines": {
23
- "node": ">=12.0.0"
23
+ "node": ">=20.11.0"
24
24
  },
25
25
  "scripts": {
26
26
  "test": "mocha test",
@@ -38,7 +38,9 @@
38
38
  "/www/**",
39
39
  "!/www/nacis/**",
40
40
  "/mapshaper.js",
41
- "!.DS_Store"
41
+ "!.DS_Store",
42
+ "!/www/docs/**",
43
+ "!/www/llms*"
42
44
  ],
43
45
  "dependencies": {
44
46
  "@ngageoint/geopackage": "^4.2.6",
@@ -47,7 +49,7 @@
47
49
  "@xmldom/xmldom": "^0.8.6",
48
50
  "adm-zip": "^0.5.9",
49
51
  "big.js": "^7.0.1",
50
- "commander": "7.0.0",
52
+ "commander": "^14.0.3",
51
53
  "cookies": "^0.8.0",
52
54
  "d3-color": "3.1.0",
53
55
  "d3-interpolate": "^3.0.1",
@@ -63,9 +65,8 @@
63
65
  "kdbush": "^3.0.0",
64
66
  "mproj": "0.0.40",
65
67
  "msgpackr": "^1.10.1",
66
- "opn": "^5.3.0",
68
+ "open": "^11.0.0",
67
69
  "rw": "~1.3.3",
68
- "sync-request": "6.1.0",
69
70
  "tinyqueue": "^2.0.3"
70
71
  },
71
72
  "devDependencies": {
@@ -76,7 +77,7 @@
76
77
  "eslint": "^8.16.0",
77
78
  "highlight.js": "^11.11.1",
78
79
  "marked": "^18.0.2",
79
- "mocha": "^10.2.0",
80
+ "mocha": "^11.7.5",
80
81
  "rollup": "^4.44.1",
81
82
  "rollup-plugin-polyfill-node": "^0.13.0",
82
83
  "shell-quote": "^1.7.4",