mapshaper 0.6.93 → 0.6.95

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 CHANGED
@@ -236,8 +236,7 @@
236
236
  if (!obj) return false;
237
237
  if (isArray(obj)) return true;
238
238
  if (isString(obj)) return false;
239
- if (obj.length === 0) return true;
240
- if (obj.length > 0) return true;
239
+ if (obj.length === 0 || obj.length > 0) return true;
241
240
  return false;
242
241
  }
243
242
 
@@ -4660,11 +4659,14 @@
4660
4659
 
4661
4660
  var mproj$1 = require$1('mproj');
4662
4661
 
4663
- // A compound projection, consisting of a default projection and one or more rectangular frames
4664
- // that are projected separately and affine transformed.
4662
+ // Constructor function for a compound projection consisting of a default
4663
+ // projection and one or more rectangular frames that are projected separately
4664
+ // and affine transformed.
4665
4665
  // @mainParams: parameters for main projection, including:
4666
4666
  // proj: Proj string
4667
4667
  // bbox: lat-lon bounding box
4668
+ // Returns a mproj CRS object for the main CRS with the CRS objects of the
4669
+ // embedded projections attached.
4668
4670
  function MixedProjection(mainParams, options) {
4669
4671
  var mainFrame = initFrame(mainParams);
4670
4672
  var mainP = mainFrame.crs;
@@ -5056,6 +5058,9 @@
5056
5058
  return str in projectionAliases;
5057
5059
  }
5058
5060
 
5061
+ // str: projection string in a variety of forms accepted by the -proj command
5062
+ // (e.g. alias, EPSG:XXXX, Proj.4 string, bare Proj.4 projection name)
5063
+ // Returns a properly formatted Proj.4 string or an instantiated mproj object
5059
5064
  function getProjDefn(str) {
5060
5065
  var defn;
5061
5066
  // prepend '+proj=' to bare proj names
@@ -7701,7 +7706,7 @@
7701
7706
  if (currentUnpackr.mapsAsObjects) {
7702
7707
  let object = {};
7703
7708
  for (let i = 0; i < token; i++) {
7704
- let key = readKey$1();
7709
+ let key = readKey();
7705
7710
  if (key === '__proto__')
7706
7711
  key = '__proto_';
7707
7712
  object[key] = read();
@@ -8063,7 +8068,7 @@
8063
8068
  if (currentUnpackr.mapsAsObjects) {
8064
8069
  let object = {};
8065
8070
  for (let i = 0; i < length; i++) {
8066
- let key = readKey$1();
8071
+ let key = readKey();
8067
8072
  if (key === '__proto__')
8068
8073
  key = '__proto_';
8069
8074
  object[key] = read();
@@ -8286,7 +8291,7 @@
8286
8291
  }
8287
8292
 
8288
8293
  var keyCache = new Array(4096);
8289
- function readKey$1() {
8294
+ function readKey() {
8290
8295
  let length = src[position$1++];
8291
8296
  if (length >= 0xa0 && length < 0xc0) {
8292
8297
  // fixstr, potentially use key cache
@@ -11316,9 +11321,11 @@
11316
11321
  function guessInputFileType(file) {
11317
11322
  var ext = getFileExtension(file || '').toLowerCase(),
11318
11323
  type = null;
11319
- if (ext == 'dbf' || ext == 'shp' || ext == 'prj' || ext == 'shx' || ext == 'kml' || ext == 'cpg') {
11324
+ if (ext == 'dbf' || ext == 'shp' || ext == 'kml') {
11320
11325
  type = ext;
11321
- } else if (/json$/.test(ext)) {
11326
+ } else if (isAuxiliaryInputFileType(ext)) {
11327
+ type = ext;
11328
+ } else if (/json$/.test(ext)) { // matches topojson, geojson, json
11322
11329
  type = 'json';
11323
11330
  } else if (ext == 'csv' || ext == 'tsv' || ext == 'txt' || ext == 'tab') {
11324
11331
  type = 'text';
@@ -11328,6 +11335,11 @@
11328
11335
  return type;
11329
11336
  }
11330
11337
 
11338
+ // File types that can be imported but are not convertible to datasets
11339
+ function isAuxiliaryInputFileType(type) {
11340
+ return type == 'prj' || type == 'shx' || type == 'cpg';
11341
+ }
11342
+
11331
11343
  function guessInputContentType(content) {
11332
11344
  var type = null;
11333
11345
  if (utils.isString(content)) {
@@ -11428,6 +11440,7 @@
11428
11440
  var FileTypes = /*#__PURE__*/Object.freeze({
11429
11441
  __proto__: null,
11430
11442
  guessInputFileType: guessInputFileType,
11443
+ isAuxiliaryInputFileType: isAuxiliaryInputFileType,
11431
11444
  guessInputContentType: guessInputContentType,
11432
11445
  guessInputType: guessInputType,
11433
11446
  stringLooksLikeJSON: stringLooksLikeJSON,
@@ -28085,13 +28098,13 @@ ${svg}
28085
28098
 
28086
28099
  var EOF; // undefined is used as an EOF marker
28087
28100
 
28088
- var RESERVE = 0X1000; // RESERVE is the number of bytes to keep in read buffer
28101
+ var RESERVE = 4096; // RESERVE is the number of bytes to keep in read buffer
28089
28102
  var BUFLEN = 1e7; // buffer chunk size
28090
28103
  var MAX_STRLEN = 5e6; // max byte len of a value string (object keys are shorter)
28091
28104
 
28092
- // Parse from a Buffer -- similar to JSON.parse(), used for testing
28093
- function parse(buf) {
28094
- var reader = new BufferReader(buf);
28105
+ // Parse from a Buffer or FileReader
28106
+ function parseJSON(arg) {
28107
+ var reader = isArrayLike(arg) ? new BufferReader(arg) : arg;
28095
28108
  var src = ByteReader(reader, 0);
28096
28109
  skipWS(src);
28097
28110
  var val = readValue(src);
@@ -28102,16 +28115,43 @@ ${svg}
28102
28115
  return val;
28103
28116
  }
28104
28117
 
28105
- // Read and parse JSON objects from a FileReader
28106
- function parseObjects(reader, offset, cb) {
28107
- var src = ByteReader(reader, offset);
28108
- seekObjectStart(src);
28118
+ // Parse data from:
28119
+ // * FeatureCollection
28120
+ // * GeometryCollection
28121
+ // * Single Feature or Geometry
28122
+ // * WS-delimited sequence of Features or geometries
28123
+ //
28124
+ // reader: FileReader or compatible reader
28125
+ // cb: callback function, called once for each parsed Feature or bare geometry
28126
+ //
28127
+ // Returns:
28128
+ // * collections - top-level object with features/geometries array set to null
28129
+ // * others - null
28130
+ //
28131
+ function parseGeoJSON(reader, cb) {
28132
+ var src = ByteReader(reader, 0);
28133
+ var isObject = seekObjectStart(src);
28134
+ if (!isObject) {
28135
+ stop('File is not GeoJSON');
28136
+ }
28137
+ var obj = readObject(src, cb);
28138
+ if (obj.type == 'FeatureCollection' || obj.type == 'GeometryCollection') {
28139
+ return obj;
28140
+ }
28141
+ if (!obj.type) { // TODO: validate type
28142
+ stop('Invalid GeoJSON');
28143
+ }
28144
+ cb(obj);
28145
+ // try to read newline-delimited GeoJSON
28146
+ skipWS(src);
28109
28147
  while (src.peek() == LBRACE) {
28110
- cb(readObject(src));
28111
- readToken(src, COMMA);
28148
+ cb(readObject(src, cb));
28149
+ skipWS(src);
28112
28150
  }
28151
+ return null;
28113
28152
  }
28114
28153
 
28154
+
28115
28155
  function parseError(msg, i) {
28116
28156
  if (i >= 0) {
28117
28157
  msg += ' at position ' + i;
@@ -28166,15 +28206,26 @@ ${svg}
28166
28206
  function readArray(src) {
28167
28207
  var arr = [], c;
28168
28208
  eatChar(src, LBRACK);
28169
- c = readToken(src, RBRACK);
28209
+ c = scanForSyntaxChar(src, RBRACK);
28170
28210
  while (c != RBRACK) {
28171
28211
  src.refresh();
28172
28212
  arr.push(readArrayElement(src));
28173
- c = readAorB(src, COMMA, RBRACK);
28213
+ c = scanForAorB(src, COMMA, RBRACK);
28174
28214
  }
28175
28215
  return arr;
28176
28216
  }
28177
28217
 
28218
+ function readCollectionArray(src, cb) {
28219
+ var c;
28220
+ eatChar(src, LBRACK);
28221
+ c = scanForSyntaxChar(src, RBRACK);
28222
+ while (c != RBRACK) {
28223
+ src.refresh();
28224
+ cb(readArrayElement(src));
28225
+ c = scanForAorB(src, COMMA, RBRACK);
28226
+ }
28227
+ }
28228
+
28178
28229
  // Using this function instead of readValue() to read array elements
28179
28230
  // gives up to a 25% reduction in overall processing time when parsing
28180
28231
  // coordinate-heavy GeoJSON files.
@@ -28205,7 +28256,7 @@ ${svg}
28205
28256
  do {
28206
28257
  src.refresh(); // make make sure long arrays of numbers don't overflow
28207
28258
  arr.push(readValue(src));
28208
- } while(readAorB(src, COMMA, RBRACK) == COMMA);
28259
+ } while(scanForAorB(src, COMMA, RBRACK) == COMMA);
28209
28260
  return arr;
28210
28261
  }
28211
28262
 
@@ -28226,7 +28277,7 @@ ${svg}
28226
28277
  // Reads and returns tok if tok is the next non-whitespace byte,
28227
28278
  // else returns null.
28228
28279
  // Scans past WS chars, both before and after tok
28229
- function readToken(src, tok) {
28280
+ function scanForSyntaxChar(src, tok) {
28230
28281
  skipWS(src);
28231
28282
  var c = src.peek();
28232
28283
  if (c === tok) {
@@ -28237,7 +28288,7 @@ ${svg}
28237
28288
  return null;
28238
28289
  }
28239
28290
 
28240
- // assumes no leading WS
28291
+ // Assumes next char is the first char of a data object (not WS "," ":" "}" "]" etc)
28241
28292
  function readValue(src) {
28242
28293
  var c = src.peek();
28243
28294
  var val;
@@ -28245,14 +28296,29 @@ ${svg}
28245
28296
  else if (c == LBRACK) val = readArray(src);
28246
28297
  else if (c == DQUOTE) val = readString(src);
28247
28298
  else if (c == LBRACE) val = readObject(src);
28248
- else if (c == 110) val = eatChars(src, "null") && null;
28249
- else if (c == 116) val = eatChars(src, "true") && true;
28250
- else if (c == 102) val = eatChars(src, "false") && false;
28299
+ else if (c == 110) val = readNull(src); // "n" -> null
28300
+ else if (c == 116) val = readTrue(src); // "t" -> true
28301
+ else if (c == 102) val = readFalse(src); // "f" -> false
28251
28302
  else unexpectedCharAt(c, src.index());
28252
28303
  return val;
28253
28304
  }
28254
28305
 
28255
- function readAorB(src, a, b) {
28306
+ function readTrue(src) {
28307
+ eatChars(src, 'true');
28308
+ return true;
28309
+ }
28310
+
28311
+ function readFalse(src) {
28312
+ eatChars(src, 'false');
28313
+ return false;
28314
+ }
28315
+
28316
+ function readNull(src) {
28317
+ eatChars(src, 'null');
28318
+ return null;
28319
+ }
28320
+
28321
+ function scanForAorB(src, a, b) {
28256
28322
  skipWS(src);
28257
28323
  var c = src.getChar();
28258
28324
  if (c != a && c != b) unexpectedCharAt(c, src.index() - 1);
@@ -28260,21 +28326,30 @@ ${svg}
28260
28326
  return c;
28261
28327
  }
28262
28328
 
28263
- function readObject(src) {
28329
+
28330
+ // cb: optional callback for returning GeoJSON features or geometries
28331
+ //
28332
+ function readObject(src, cb) {
28264
28333
  var o = {};
28265
28334
  var key, c;
28266
28335
  eatChar(src, LBRACE);
28267
- c = readToken(src, RBRACE);
28336
+ c = scanForSyntaxChar(src, RBRACE);
28268
28337
  while (c != RBRACE) {
28269
28338
  src.refresh();
28270
- key = readKey(src); // use caching for faster key parsing
28339
+ key = readKeywordString(src); // optimization: use caching with object keys
28271
28340
  skipWS(src);
28272
- eatChar(src, 58);
28341
+ eatChar(src, 58); // ":"
28273
28342
  skipWS(src);
28274
- // use caching with GeoJSON "type" params
28275
- o[key] = key == 'type' && src.peek() == DQUOTE ?
28276
- readKey(src) : readValue(src);
28277
- c = readAorB(src, COMMA, RBRACE);
28343
+ if ((key == 'features' || key == 'geometries') &&
28344
+ src.peek() == LBRACK && cb) {
28345
+ readCollectionArray(src, cb);
28346
+ o[key] = null;
28347
+ } else if (key == 'type' && src.peek() == DQUOTE) {
28348
+ o[key] = readKeywordString(src); // use caching with GeoJSON "type" params
28349
+ } else {
28350
+ o[key] = readValue(src);
28351
+ }
28352
+ c = scanForAorB(src, COMMA, RBRACE);
28278
28353
  }
28279
28354
  return o;
28280
28355
  }
@@ -28287,7 +28362,7 @@ ${svg}
28287
28362
  // Uses caching to speed up parsing of repeated strings.
28288
28363
  // The caching scheme used here can give a 20% overall speed improvement
28289
28364
  // when parsing files consisting mostly of attribute data (e.g. typical Point features)
28290
- function readKey(src) {
28365
+ function readKeywordString(src) {
28291
28366
  var MAXLEN = 2000; // must be less than RESERVE
28292
28367
  var i = src.index();
28293
28368
  var cache = src.cache;
@@ -28528,28 +28603,6 @@ ${svg}
28528
28603
  }
28529
28604
  }
28530
28605
 
28531
- // Read GeoJSON Features or geometry objects from a file
28532
- // @reader: a FileReader
28533
- function GeoJSONReader(reader) {
28534
-
28535
- // Read objects synchronously, with callback
28536
- this.readObjects = function(onObject) {
28537
- // Search first x bytes of file for features|geometries key
28538
- // 300 bytes not enough... GeoJSON files can have additional non-standard properties, e.g. 'metadata'
28539
- // var bytesToSearch = 300;
28540
- var bytesToSearch = 5000;
28541
- var start = reader.findString('"features"', bytesToSearch) ||
28542
- reader.findString('"geometries"', bytesToSearch);
28543
- // Assume single Feature or geometry if collection not found
28544
- // (this works for ndjson files too)
28545
- var offset = start ? start.offset : 0;
28546
- T$1.start();
28547
- parseObjects(reader, offset, onObject);
28548
- // parseObjects_native(reader, offset, onObject);
28549
- debug('Parse GeoJSON', T$1.stop());
28550
- };
28551
- }
28552
-
28553
28606
  // Identify JSON type from the initial subset of a JSON string
28554
28607
  function identifyJSONString(str, opts) {
28555
28608
  var maxChars = 1000;
@@ -28583,7 +28636,8 @@ ${svg}
28583
28636
 
28584
28637
  function importGeoJSONFile(fileReader, opts) {
28585
28638
  var importer = new GeoJSONParser(opts);
28586
- new GeoJSONReader(fileReader).readObjects(importer.parseObject);
28639
+ parseGeoJSON(fileReader, importer.parseObject);
28640
+ // TODO: examine top-level objects, like crs
28587
28641
  return importer.done();
28588
28642
  }
28589
28643
 
@@ -28880,7 +28934,7 @@ ${svg}
28880
28934
  }
28881
28935
 
28882
28936
  return {
28883
- info: importInfo(obj.info || {}),
28937
+ info: await importInfo(obj.info || {}),
28884
28938
  layers: layers,
28885
28939
  arcs: arcs
28886
28940
  };
@@ -28920,8 +28974,10 @@ ${svg}
28920
28974
  return arcs;
28921
28975
  }
28922
28976
 
28923
- function importInfo(o) {
28977
+ async function importInfo(o) {
28924
28978
  if (o.crs_string) {
28979
+ // load external files (e.g. epsg definitions) if needed in GUI
28980
+ await initProjLibrary({crs: o.crs_string});
28925
28981
  o.crs = parseCrsString(o.crs_string);
28926
28982
  } else if (o.prj) {
28927
28983
  o.crs = parsePrj(o.prj);
@@ -29068,7 +29124,7 @@ ${svg}
29068
29124
  // don't import .dbf separately if .shp is present
29069
29125
  if (replaceFileExtension(filename, 'shp') in cache) return false;
29070
29126
  }
29071
- return type == 'text' || type == 'json' || type == 'shp' || type == 'dbf' || type == 'kml';
29127
+ return type && !isAuxiliaryInputFileType(type);
29072
29128
  });
29073
29129
  }
29074
29130
 
@@ -45616,7 +45672,7 @@ ${svg}
45616
45672
  });
45617
45673
  }
45618
45674
 
45619
- var version = "0.6.93";
45675
+ var version = "0.6.95";
45620
45676
 
45621
45677
  // Parse command line args into commands and run them
45622
45678
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -46248,8 +46304,8 @@ ${svg}
46248
46304
  Dbf,
46249
46305
  DbfReader,
46250
46306
  DouglasPeucker,
46251
- geojson: GeoJSON,
46252
- json: { parse: parse },
46307
+ parseGeoJSON,
46308
+ json: { parse: parseJSON },
46253
46309
  ShpType,
46254
46310
  topojson: TopoJSON,
46255
46311
  Visvalingam,
@@ -46259,7 +46315,6 @@ ${svg}
46259
46315
  CommandParser,
46260
46316
  DataTable,
46261
46317
  editArcs,
46262
- GeoJSONReader,
46263
46318
  Heap,
46264
46319
  IdLookupIndex,
46265
46320
  NodeCollection,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.93",
3
+ "version": "0.6.95",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -3707,7 +3707,13 @@
3707
3707
  // init simplify button and mode
3708
3708
  gui.addMode('simplify', turnOn, turnOff, menuBtn);
3709
3709
 
3710
- model.on('update', function() {
3710
+ model.on('update', function(e) {
3711
+ // exit simplify mode if data has been changed from outside the simplify
3712
+ // tool
3713
+ // (TODO: try to only respond to changes that might affect simplification)
3714
+ if (e.flags.simplify_method || e.flags.simplify_amount) {
3715
+ return;
3716
+ }
3711
3717
  menuBtn.classed('disabled', !model.getActiveLayer());
3712
3718
  if (gui.getMode() == 'simplify') gui.clearMode();
3713
3719
  });
@@ -5890,7 +5896,7 @@
5890
5896
 
5891
5897
  var menus = {
5892
5898
  standard: ['info', 'selection', 'box'],
5893
- empty: ['edit_points', 'edit_lines', 'edit_polygons', 'box'],
5899
+ empty: ['edit_polygons', 'edit_lines', 'edit_points', 'box'],
5894
5900
  polygons: ['info', 'selection', 'box', 'edit_polygons'],
5895
5901
  rectangles: ['info', 'selection', 'box', 'rectangles', 'edit_polygons'],
5896
5902
  lines: ['info', 'selection', 'box' , 'edit_lines'],
@@ -5908,11 +5914,11 @@
5908
5914
  // mode name -> menu text lookup
5909
5915
  var labels = {
5910
5916
  info: 'inspect features',
5911
- box: 'shift-drag box tool',
5917
+ box: 'rectangle tool',
5912
5918
  data: 'edit attributes',
5913
5919
  labels: 'position labels',
5914
- edit_points: 'draw/edit points',
5915
- edit_lines: 'draw/edit lines',
5920
+ edit_points: 'add/edit points',
5921
+ edit_lines: 'draw/edit polylines',
5916
5922
  edit_polygons: 'draw/edit polygons',
5917
5923
  vertices: 'edit vertices',
5918
5924
  selection: 'selection tool',
@@ -6531,8 +6537,7 @@
6531
6537
  if (!obj) return false;
6532
6538
  if (isArray(obj)) return true;
6533
6539
  if (isString(obj)) return false;
6534
- if (obj.length === 0) return true;
6535
- if (obj.length > 0) return true;
6540
+ if (obj.length === 0 || obj.length > 0) return true;
6536
6541
  return false;
6537
6542
  }
6538
6543
 
@@ -8830,12 +8835,12 @@
8830
8835
  element.addEventListener('mousedown', onAreaDown);
8831
8836
  element.addEventListener('dblclick', onAreaDblClick);
8832
8837
  document.addEventListener('contextmenu', function(e) {
8833
- if (!e.ctrlKey) {
8838
+ if (!(e.ctrlKey && e.altKey)) {
8834
8839
  e.preventDefault();
8835
8840
  }
8836
8841
  });
8837
8842
  element.addEventListener('contextmenu', function(e) {
8838
- if (!e.ctrlKey) {
8843
+ if (!(e.ctrlKey && e.altKey)) {
8839
8844
  _self.dispatchEvent('contextmenu', procMouseEvent(e));
8840
8845
  }
8841
8846
  });
@@ -12142,6 +12147,8 @@
12142
12147
  var popup = gui.container.findChild('.box-tool-options');
12143
12148
  var coords = popup.findChild('.box-coords');
12144
12149
  var _on = false;
12150
+ var instructionsShown = false;
12151
+ var alert;
12145
12152
 
12146
12153
  var infoBtn = new SimpleButton(popup.findChild('.info-btn')).on('click', function() {
12147
12154
  if (coords.visible()) hideCoords(); else showCoords();
@@ -12188,6 +12195,10 @@
12188
12195
  gui.on('interaction_mode_change', function(e) {
12189
12196
  if (e.mode === 'box') {
12190
12197
  gui.enterMode('box_tool');
12198
+ if (!instructionsShown) {
12199
+ instructionsShown = true;
12200
+ showInstructions();
12201
+ }
12191
12202
  } else if (_on) {
12192
12203
  turnOff();
12193
12204
  }
@@ -12198,7 +12209,10 @@
12198
12209
  });
12199
12210
 
12200
12211
  box.on('dragend', function(e) {
12201
- if (_on) popup.show();
12212
+ if (_on) {
12213
+ hideInstructions();
12214
+ popup.show();
12215
+ }
12202
12216
  });
12203
12217
 
12204
12218
  box.on('handle_drag', function() {
@@ -12207,6 +12221,19 @@
12207
12221
  }
12208
12222
  });
12209
12223
 
12224
+ function showInstructions() {
12225
+ var isMac = navigator.userAgent.includes('Mac');
12226
+ var symbol = isMac ? '⌘' : '^';
12227
+ var msg = `Instructions: Shift-drag to draw a rectangle. Drag handles to resize. Shift-drag handles to resize symmetrically.`;
12228
+ alert = showPopupAlert(msg, null, { non_blocking: true, max_width: '360px'});
12229
+ }
12230
+
12231
+ function hideInstructions() {
12232
+ if (!alert) return;
12233
+ alert.close('fade');
12234
+ alert = null;
12235
+ }
12236
+
12210
12237
  function inZoomMode() {
12211
12238
  return !_on && gui.getMode() != 'selection_tool';
12212
12239
  }
@@ -12242,6 +12269,7 @@
12242
12269
 
12243
12270
  function turnOff() {
12244
12271
  box.turnOff();
12272
+ hideInstructions();
12245
12273
  if (gui.interaction.getMode() == 'box') {
12246
12274
  // mode change was not initiated by interactive menu -- turn off interactivity
12247
12275
  gui.interaction.turnOff();
package/www/mapshaper.js CHANGED
@@ -236,8 +236,7 @@
236
236
  if (!obj) return false;
237
237
  if (isArray(obj)) return true;
238
238
  if (isString(obj)) return false;
239
- if (obj.length === 0) return true;
240
- if (obj.length > 0) return true;
239
+ if (obj.length === 0 || obj.length > 0) return true;
241
240
  return false;
242
241
  }
243
242
 
@@ -4660,11 +4659,14 @@
4660
4659
 
4661
4660
  var mproj$1 = require$1('mproj');
4662
4661
 
4663
- // A compound projection, consisting of a default projection and one or more rectangular frames
4664
- // that are projected separately and affine transformed.
4662
+ // Constructor function for a compound projection consisting of a default
4663
+ // projection and one or more rectangular frames that are projected separately
4664
+ // and affine transformed.
4665
4665
  // @mainParams: parameters for main projection, including:
4666
4666
  // proj: Proj string
4667
4667
  // bbox: lat-lon bounding box
4668
+ // Returns a mproj CRS object for the main CRS with the CRS objects of the
4669
+ // embedded projections attached.
4668
4670
  function MixedProjection(mainParams, options) {
4669
4671
  var mainFrame = initFrame(mainParams);
4670
4672
  var mainP = mainFrame.crs;
@@ -5056,6 +5058,9 @@
5056
5058
  return str in projectionAliases;
5057
5059
  }
5058
5060
 
5061
+ // str: projection string in a variety of forms accepted by the -proj command
5062
+ // (e.g. alias, EPSG:XXXX, Proj.4 string, bare Proj.4 projection name)
5063
+ // Returns a properly formatted Proj.4 string or an instantiated mproj object
5059
5064
  function getProjDefn(str) {
5060
5065
  var defn;
5061
5066
  // prepend '+proj=' to bare proj names
@@ -7701,7 +7706,7 @@
7701
7706
  if (currentUnpackr.mapsAsObjects) {
7702
7707
  let object = {};
7703
7708
  for (let i = 0; i < token; i++) {
7704
- let key = readKey$1();
7709
+ let key = readKey();
7705
7710
  if (key === '__proto__')
7706
7711
  key = '__proto_';
7707
7712
  object[key] = read();
@@ -8063,7 +8068,7 @@
8063
8068
  if (currentUnpackr.mapsAsObjects) {
8064
8069
  let object = {};
8065
8070
  for (let i = 0; i < length; i++) {
8066
- let key = readKey$1();
8071
+ let key = readKey();
8067
8072
  if (key === '__proto__')
8068
8073
  key = '__proto_';
8069
8074
  object[key] = read();
@@ -8286,7 +8291,7 @@
8286
8291
  }
8287
8292
 
8288
8293
  var keyCache = new Array(4096);
8289
- function readKey$1() {
8294
+ function readKey() {
8290
8295
  let length = src[position$1++];
8291
8296
  if (length >= 0xa0 && length < 0xc0) {
8292
8297
  // fixstr, potentially use key cache
@@ -11316,9 +11321,11 @@
11316
11321
  function guessInputFileType(file) {
11317
11322
  var ext = getFileExtension(file || '').toLowerCase(),
11318
11323
  type = null;
11319
- if (ext == 'dbf' || ext == 'shp' || ext == 'prj' || ext == 'shx' || ext == 'kml' || ext == 'cpg') {
11324
+ if (ext == 'dbf' || ext == 'shp' || ext == 'kml') {
11320
11325
  type = ext;
11321
- } else if (/json$/.test(ext)) {
11326
+ } else if (isAuxiliaryInputFileType(ext)) {
11327
+ type = ext;
11328
+ } else if (/json$/.test(ext)) { // matches topojson, geojson, json
11322
11329
  type = 'json';
11323
11330
  } else if (ext == 'csv' || ext == 'tsv' || ext == 'txt' || ext == 'tab') {
11324
11331
  type = 'text';
@@ -11328,6 +11335,11 @@
11328
11335
  return type;
11329
11336
  }
11330
11337
 
11338
+ // File types that can be imported but are not convertible to datasets
11339
+ function isAuxiliaryInputFileType(type) {
11340
+ return type == 'prj' || type == 'shx' || type == 'cpg';
11341
+ }
11342
+
11331
11343
  function guessInputContentType(content) {
11332
11344
  var type = null;
11333
11345
  if (utils.isString(content)) {
@@ -11428,6 +11440,7 @@
11428
11440
  var FileTypes = /*#__PURE__*/Object.freeze({
11429
11441
  __proto__: null,
11430
11442
  guessInputFileType: guessInputFileType,
11443
+ isAuxiliaryInputFileType: isAuxiliaryInputFileType,
11431
11444
  guessInputContentType: guessInputContentType,
11432
11445
  guessInputType: guessInputType,
11433
11446
  stringLooksLikeJSON: stringLooksLikeJSON,
@@ -28085,13 +28098,13 @@ ${svg}
28085
28098
 
28086
28099
  var EOF; // undefined is used as an EOF marker
28087
28100
 
28088
- var RESERVE = 0X1000; // RESERVE is the number of bytes to keep in read buffer
28101
+ var RESERVE = 4096; // RESERVE is the number of bytes to keep in read buffer
28089
28102
  var BUFLEN = 1e7; // buffer chunk size
28090
28103
  var MAX_STRLEN = 5e6; // max byte len of a value string (object keys are shorter)
28091
28104
 
28092
- // Parse from a Buffer -- similar to JSON.parse(), used for testing
28093
- function parse(buf) {
28094
- var reader = new BufferReader(buf);
28105
+ // Parse from a Buffer or FileReader
28106
+ function parseJSON(arg) {
28107
+ var reader = isArrayLike(arg) ? new BufferReader(arg) : arg;
28095
28108
  var src = ByteReader(reader, 0);
28096
28109
  skipWS(src);
28097
28110
  var val = readValue(src);
@@ -28102,16 +28115,43 @@ ${svg}
28102
28115
  return val;
28103
28116
  }
28104
28117
 
28105
- // Read and parse JSON objects from a FileReader
28106
- function parseObjects(reader, offset, cb) {
28107
- var src = ByteReader(reader, offset);
28108
- seekObjectStart(src);
28118
+ // Parse data from:
28119
+ // * FeatureCollection
28120
+ // * GeometryCollection
28121
+ // * Single Feature or Geometry
28122
+ // * WS-delimited sequence of Features or geometries
28123
+ //
28124
+ // reader: FileReader or compatible reader
28125
+ // cb: callback function, called once for each parsed Feature or bare geometry
28126
+ //
28127
+ // Returns:
28128
+ // * collections - top-level object with features/geometries array set to null
28129
+ // * others - null
28130
+ //
28131
+ function parseGeoJSON(reader, cb) {
28132
+ var src = ByteReader(reader, 0);
28133
+ var isObject = seekObjectStart(src);
28134
+ if (!isObject) {
28135
+ stop('File is not GeoJSON');
28136
+ }
28137
+ var obj = readObject(src, cb);
28138
+ if (obj.type == 'FeatureCollection' || obj.type == 'GeometryCollection') {
28139
+ return obj;
28140
+ }
28141
+ if (!obj.type) { // TODO: validate type
28142
+ stop('Invalid GeoJSON');
28143
+ }
28144
+ cb(obj);
28145
+ // try to read newline-delimited GeoJSON
28146
+ skipWS(src);
28109
28147
  while (src.peek() == LBRACE) {
28110
- cb(readObject(src));
28111
- readToken(src, COMMA);
28148
+ cb(readObject(src, cb));
28149
+ skipWS(src);
28112
28150
  }
28151
+ return null;
28113
28152
  }
28114
28153
 
28154
+
28115
28155
  function parseError(msg, i) {
28116
28156
  if (i >= 0) {
28117
28157
  msg += ' at position ' + i;
@@ -28166,15 +28206,26 @@ ${svg}
28166
28206
  function readArray(src) {
28167
28207
  var arr = [], c;
28168
28208
  eatChar(src, LBRACK);
28169
- c = readToken(src, RBRACK);
28209
+ c = scanForSyntaxChar(src, RBRACK);
28170
28210
  while (c != RBRACK) {
28171
28211
  src.refresh();
28172
28212
  arr.push(readArrayElement(src));
28173
- c = readAorB(src, COMMA, RBRACK);
28213
+ c = scanForAorB(src, COMMA, RBRACK);
28174
28214
  }
28175
28215
  return arr;
28176
28216
  }
28177
28217
 
28218
+ function readCollectionArray(src, cb) {
28219
+ var c;
28220
+ eatChar(src, LBRACK);
28221
+ c = scanForSyntaxChar(src, RBRACK);
28222
+ while (c != RBRACK) {
28223
+ src.refresh();
28224
+ cb(readArrayElement(src));
28225
+ c = scanForAorB(src, COMMA, RBRACK);
28226
+ }
28227
+ }
28228
+
28178
28229
  // Using this function instead of readValue() to read array elements
28179
28230
  // gives up to a 25% reduction in overall processing time when parsing
28180
28231
  // coordinate-heavy GeoJSON files.
@@ -28205,7 +28256,7 @@ ${svg}
28205
28256
  do {
28206
28257
  src.refresh(); // make make sure long arrays of numbers don't overflow
28207
28258
  arr.push(readValue(src));
28208
- } while(readAorB(src, COMMA, RBRACK) == COMMA);
28259
+ } while(scanForAorB(src, COMMA, RBRACK) == COMMA);
28209
28260
  return arr;
28210
28261
  }
28211
28262
 
@@ -28226,7 +28277,7 @@ ${svg}
28226
28277
  // Reads and returns tok if tok is the next non-whitespace byte,
28227
28278
  // else returns null.
28228
28279
  // Scans past WS chars, both before and after tok
28229
- function readToken(src, tok) {
28280
+ function scanForSyntaxChar(src, tok) {
28230
28281
  skipWS(src);
28231
28282
  var c = src.peek();
28232
28283
  if (c === tok) {
@@ -28237,7 +28288,7 @@ ${svg}
28237
28288
  return null;
28238
28289
  }
28239
28290
 
28240
- // assumes no leading WS
28291
+ // Assumes next char is the first char of a data object (not WS "," ":" "}" "]" etc)
28241
28292
  function readValue(src) {
28242
28293
  var c = src.peek();
28243
28294
  var val;
@@ -28245,14 +28296,29 @@ ${svg}
28245
28296
  else if (c == LBRACK) val = readArray(src);
28246
28297
  else if (c == DQUOTE) val = readString(src);
28247
28298
  else if (c == LBRACE) val = readObject(src);
28248
- else if (c == 110) val = eatChars(src, "null") && null;
28249
- else if (c == 116) val = eatChars(src, "true") && true;
28250
- else if (c == 102) val = eatChars(src, "false") && false;
28299
+ else if (c == 110) val = readNull(src); // "n" -> null
28300
+ else if (c == 116) val = readTrue(src); // "t" -> true
28301
+ else if (c == 102) val = readFalse(src); // "f" -> false
28251
28302
  else unexpectedCharAt(c, src.index());
28252
28303
  return val;
28253
28304
  }
28254
28305
 
28255
- function readAorB(src, a, b) {
28306
+ function readTrue(src) {
28307
+ eatChars(src, 'true');
28308
+ return true;
28309
+ }
28310
+
28311
+ function readFalse(src) {
28312
+ eatChars(src, 'false');
28313
+ return false;
28314
+ }
28315
+
28316
+ function readNull(src) {
28317
+ eatChars(src, 'null');
28318
+ return null;
28319
+ }
28320
+
28321
+ function scanForAorB(src, a, b) {
28256
28322
  skipWS(src);
28257
28323
  var c = src.getChar();
28258
28324
  if (c != a && c != b) unexpectedCharAt(c, src.index() - 1);
@@ -28260,21 +28326,30 @@ ${svg}
28260
28326
  return c;
28261
28327
  }
28262
28328
 
28263
- function readObject(src) {
28329
+
28330
+ // cb: optional callback for returning GeoJSON features or geometries
28331
+ //
28332
+ function readObject(src, cb) {
28264
28333
  var o = {};
28265
28334
  var key, c;
28266
28335
  eatChar(src, LBRACE);
28267
- c = readToken(src, RBRACE);
28336
+ c = scanForSyntaxChar(src, RBRACE);
28268
28337
  while (c != RBRACE) {
28269
28338
  src.refresh();
28270
- key = readKey(src); // use caching for faster key parsing
28339
+ key = readKeywordString(src); // optimization: use caching with object keys
28271
28340
  skipWS(src);
28272
- eatChar(src, 58);
28341
+ eatChar(src, 58); // ":"
28273
28342
  skipWS(src);
28274
- // use caching with GeoJSON "type" params
28275
- o[key] = key == 'type' && src.peek() == DQUOTE ?
28276
- readKey(src) : readValue(src);
28277
- c = readAorB(src, COMMA, RBRACE);
28343
+ if ((key == 'features' || key == 'geometries') &&
28344
+ src.peek() == LBRACK && cb) {
28345
+ readCollectionArray(src, cb);
28346
+ o[key] = null;
28347
+ } else if (key == 'type' && src.peek() == DQUOTE) {
28348
+ o[key] = readKeywordString(src); // use caching with GeoJSON "type" params
28349
+ } else {
28350
+ o[key] = readValue(src);
28351
+ }
28352
+ c = scanForAorB(src, COMMA, RBRACE);
28278
28353
  }
28279
28354
  return o;
28280
28355
  }
@@ -28287,7 +28362,7 @@ ${svg}
28287
28362
  // Uses caching to speed up parsing of repeated strings.
28288
28363
  // The caching scheme used here can give a 20% overall speed improvement
28289
28364
  // when parsing files consisting mostly of attribute data (e.g. typical Point features)
28290
- function readKey(src) {
28365
+ function readKeywordString(src) {
28291
28366
  var MAXLEN = 2000; // must be less than RESERVE
28292
28367
  var i = src.index();
28293
28368
  var cache = src.cache;
@@ -28528,28 +28603,6 @@ ${svg}
28528
28603
  }
28529
28604
  }
28530
28605
 
28531
- // Read GeoJSON Features or geometry objects from a file
28532
- // @reader: a FileReader
28533
- function GeoJSONReader(reader) {
28534
-
28535
- // Read objects synchronously, with callback
28536
- this.readObjects = function(onObject) {
28537
- // Search first x bytes of file for features|geometries key
28538
- // 300 bytes not enough... GeoJSON files can have additional non-standard properties, e.g. 'metadata'
28539
- // var bytesToSearch = 300;
28540
- var bytesToSearch = 5000;
28541
- var start = reader.findString('"features"', bytesToSearch) ||
28542
- reader.findString('"geometries"', bytesToSearch);
28543
- // Assume single Feature or geometry if collection not found
28544
- // (this works for ndjson files too)
28545
- var offset = start ? start.offset : 0;
28546
- T$1.start();
28547
- parseObjects(reader, offset, onObject);
28548
- // parseObjects_native(reader, offset, onObject);
28549
- debug('Parse GeoJSON', T$1.stop());
28550
- };
28551
- }
28552
-
28553
28606
  // Identify JSON type from the initial subset of a JSON string
28554
28607
  function identifyJSONString(str, opts) {
28555
28608
  var maxChars = 1000;
@@ -28583,7 +28636,8 @@ ${svg}
28583
28636
 
28584
28637
  function importGeoJSONFile(fileReader, opts) {
28585
28638
  var importer = new GeoJSONParser(opts);
28586
- new GeoJSONReader(fileReader).readObjects(importer.parseObject);
28639
+ parseGeoJSON(fileReader, importer.parseObject);
28640
+ // TODO: examine top-level objects, like crs
28587
28641
  return importer.done();
28588
28642
  }
28589
28643
 
@@ -28880,7 +28934,7 @@ ${svg}
28880
28934
  }
28881
28935
 
28882
28936
  return {
28883
- info: importInfo(obj.info || {}),
28937
+ info: await importInfo(obj.info || {}),
28884
28938
  layers: layers,
28885
28939
  arcs: arcs
28886
28940
  };
@@ -28920,8 +28974,10 @@ ${svg}
28920
28974
  return arcs;
28921
28975
  }
28922
28976
 
28923
- function importInfo(o) {
28977
+ async function importInfo(o) {
28924
28978
  if (o.crs_string) {
28979
+ // load external files (e.g. epsg definitions) if needed in GUI
28980
+ await initProjLibrary({crs: o.crs_string});
28925
28981
  o.crs = parseCrsString(o.crs_string);
28926
28982
  } else if (o.prj) {
28927
28983
  o.crs = parsePrj(o.prj);
@@ -29068,7 +29124,7 @@ ${svg}
29068
29124
  // don't import .dbf separately if .shp is present
29069
29125
  if (replaceFileExtension(filename, 'shp') in cache) return false;
29070
29126
  }
29071
- return type == 'text' || type == 'json' || type == 'shp' || type == 'dbf' || type == 'kml';
29127
+ return type && !isAuxiliaryInputFileType(type);
29072
29128
  });
29073
29129
  }
29074
29130
 
@@ -45616,7 +45672,7 @@ ${svg}
45616
45672
  });
45617
45673
  }
45618
45674
 
45619
- var version = "0.6.93";
45675
+ var version = "0.6.95";
45620
45676
 
45621
45677
  // Parse command line args into commands and run them
45622
45678
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -46248,8 +46304,8 @@ ${svg}
46248
46304
  Dbf,
46249
46305
  DbfReader,
46250
46306
  DouglasPeucker,
46251
- geojson: GeoJSON,
46252
- json: { parse: parse },
46307
+ parseGeoJSON,
46308
+ json: { parse: parseJSON },
46253
46309
  ShpType,
46254
46310
  topojson: TopoJSON,
46255
46311
  Visvalingam,
@@ -46259,7 +46315,6 @@ ${svg}
46259
46315
  CommandParser,
46260
46316
  DataTable,
46261
46317
  editArcs,
46262
- GeoJSONReader,
46263
46318
  Heap,
46264
46319
  IdLookupIndex,
46265
46320
  NodeCollection,