mapshaper 0.5.88 → 0.5.89
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/bin/mapshaper-gui +4 -1
- package/mapshaper.js +28 -36
- package/package.json +1 -1
- package/www/mapshaper-gui.js +202 -236
- package/www/mapshaper.js +28 -36
package/bin/mapshaper-gui
CHANGED
|
@@ -9,6 +9,7 @@ var defaultPort = 5555,
|
|
|
9
9
|
.option('-s, --direct-save', 'save files outside the browser\'s download folder')
|
|
10
10
|
.option('-f, --force-save', 'allow overwriting input files with output files')
|
|
11
11
|
.option('-a, --display-all', 'turn on visibility of all layers')
|
|
12
|
+
.option('-n, --name <name(s)>', 'rename input layer or layers')
|
|
12
13
|
.option('-t, --target <name>', 'name of layer to select initially')
|
|
13
14
|
.helpOption('-h, --help', 'show this help message')
|
|
14
15
|
.version(require('../package.json').version)
|
|
@@ -29,7 +30,8 @@ var defaultPort = 5555,
|
|
|
29
30
|
validateFiles(dataFiles);
|
|
30
31
|
|
|
31
32
|
process.on('uncaughtException', function(err) {
|
|
32
|
-
|
|
33
|
+
// added 'code' for Node.js v16
|
|
34
|
+
if (err.errno === 'EADDRINUSE' || err.code === 'EADDRINUSE') {
|
|
33
35
|
// probe for an open port, unless user has specified a non-default port
|
|
34
36
|
if (port == defaultPort && probeCount < 10) {
|
|
35
37
|
probeCount++;
|
|
@@ -207,6 +209,7 @@ function getManifestJS(files, opts) {
|
|
|
207
209
|
if (opts.directSave) o.allow_saving = true;
|
|
208
210
|
if (opts.displayAll) o.display_all = true;
|
|
209
211
|
if (opts.quickView) o.quick_view = true;
|
|
212
|
+
if (opts.name) o.name = opts.name;
|
|
210
213
|
return "mapshaper.manifest = " + JSON.stringify(o) + ";\n";
|
|
211
214
|
}
|
|
212
215
|
|
package/mapshaper.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
|
|
3
|
-
var VERSION = "0.5.
|
|
3
|
+
var VERSION = "0.5.88";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
var utils = /*#__PURE__*/Object.freeze({
|
|
@@ -9350,16 +9350,12 @@
|
|
|
9350
9350
|
var getters = {
|
|
9351
9351
|
name: lyr.name,
|
|
9352
9352
|
data: records,
|
|
9353
|
-
type: lyr.geometry_type
|
|
9353
|
+
type: lyr.geometry_type,
|
|
9354
|
+
size: getFeatureCount(lyr),
|
|
9355
|
+
empty: getFeatureCount(lyr) === 0
|
|
9354
9356
|
};
|
|
9355
9357
|
addGetters(obj, getters);
|
|
9356
9358
|
addBBoxGetter(obj, lyr, arcs);
|
|
9357
|
-
obj.empty = function() {
|
|
9358
|
-
return getFeatureCount(lyr) === 0;
|
|
9359
|
-
};
|
|
9360
|
-
obj.size = function() {
|
|
9361
|
-
return getFeatureCount(lyr);
|
|
9362
|
-
};
|
|
9363
9359
|
return obj;
|
|
9364
9360
|
}
|
|
9365
9361
|
|
|
@@ -20116,33 +20112,22 @@ ${svg}
|
|
|
20116
20112
|
.option('target', targetOpt);
|
|
20117
20113
|
|
|
20118
20114
|
parser.command('symbols')
|
|
20119
|
-
|
|
20115
|
+
.describe('symbolize points as arrows, circles, stars, polygons, etc.')
|
|
20120
20116
|
.option('type', {
|
|
20121
|
-
describe: 'symbol type (e.g. star, polygon,
|
|
20122
|
-
})
|
|
20123
|
-
.option('scale', {
|
|
20124
|
-
describe: 'scale symbols by a factor',
|
|
20125
|
-
type: 'number'
|
|
20117
|
+
describe: 'symbol type (e.g. arrow, circle, square, star, polygon, ring)'
|
|
20126
20118
|
})
|
|
20127
|
-
.option('
|
|
20128
|
-
|
|
20129
|
-
|
|
20119
|
+
.option('stroke', {})
|
|
20120
|
+
.option('stroke-width', {})
|
|
20121
|
+
.option('fill', {
|
|
20122
|
+
describe: 'symbol fill color'
|
|
20130
20123
|
})
|
|
20131
20124
|
.option('polygons', {
|
|
20132
20125
|
describe: 'generate symbols as polygons instead of SVG objects',
|
|
20133
20126
|
type: 'flag'
|
|
20134
20127
|
})
|
|
20135
|
-
.option('
|
|
20136
|
-
describe: '
|
|
20137
|
-
type: '
|
|
20138
|
-
})
|
|
20139
|
-
.option('sides', {
|
|
20140
|
-
describe: 'number of sides of a polygon symbol',
|
|
20141
|
-
type: 'number'
|
|
20142
|
-
})
|
|
20143
|
-
.option('orientation', {
|
|
20144
|
-
// TODO: removed (replaced by flipped and rotated)
|
|
20145
|
-
// describe: 'use orientation=b for a rotated or flipped orientation'
|
|
20128
|
+
.option('pixel-scale', {
|
|
20129
|
+
describe: 'set symbol scale in meters-per-pixel (for polygons option)',
|
|
20130
|
+
type: 'number',
|
|
20146
20131
|
})
|
|
20147
20132
|
// .option('flipped', {
|
|
20148
20133
|
// type: 'flag',
|
|
@@ -20150,11 +20135,23 @@ ${svg}
|
|
|
20150
20135
|
// })
|
|
20151
20136
|
.option('rotated', {
|
|
20152
20137
|
type: 'flag',
|
|
20153
|
-
describe: 'symbol is rotated to
|
|
20138
|
+
describe: 'symbol is rotated to an alternate orientation'
|
|
20154
20139
|
})
|
|
20155
20140
|
.option('rotation', {
|
|
20156
20141
|
describe: 'rotation of symbol in degrees'
|
|
20157
20142
|
})
|
|
20143
|
+
.option('scale', {
|
|
20144
|
+
describe: 'scale symbols by a multiplier',
|
|
20145
|
+
type: 'number'
|
|
20146
|
+
})
|
|
20147
|
+
.option('radius', {
|
|
20148
|
+
describe: 'distance from center to farthest point on the symbol',
|
|
20149
|
+
type: 'distance'
|
|
20150
|
+
})
|
|
20151
|
+
.option('sides', {
|
|
20152
|
+
describe: '(polygon) number of sides of a (regular) polygon symbol',
|
|
20153
|
+
type: 'number'
|
|
20154
|
+
})
|
|
20158
20155
|
.option('points', {
|
|
20159
20156
|
describe: '(star) number of points'
|
|
20160
20157
|
})
|
|
@@ -20172,7 +20169,7 @@ ${svg}
|
|
|
20172
20169
|
})
|
|
20173
20170
|
.option('direction', {
|
|
20174
20171
|
old_alias: 'arrow-direction',
|
|
20175
|
-
describe: '(arrow) angle off vertical (-90 = left-pointing)'
|
|
20172
|
+
describe: '(arrow) angle off of vertical (-90 = left-pointing)'
|
|
20176
20173
|
})
|
|
20177
20174
|
.option('head-angle', {
|
|
20178
20175
|
old_alias: 'arrow-head-angle',
|
|
@@ -20207,17 +20204,12 @@ ${svg}
|
|
|
20207
20204
|
})
|
|
20208
20205
|
.option('min-stem-ratio', {
|
|
20209
20206
|
old_alias: 'arrow-min-stem',
|
|
20210
|
-
describe: '(arrow)
|
|
20207
|
+
describe: '(arrow) minimum ratio of stem to total length',
|
|
20211
20208
|
type: 'number'
|
|
20212
20209
|
})
|
|
20213
20210
|
.option('anchor', {
|
|
20214
20211
|
describe: '(arrow) takes one of: start, middle, end (default is start)'
|
|
20215
20212
|
})
|
|
20216
|
-
.option('stroke', {})
|
|
20217
|
-
.option('stroke-width', {})
|
|
20218
|
-
.option('fill', {
|
|
20219
|
-
describe: 'symbol fill color'
|
|
20220
|
-
})
|
|
20221
20213
|
.option('effect', {})
|
|
20222
20214
|
// .option('where', whereOpt)
|
|
20223
20215
|
.option('name', nameOpt)
|
package/package.json
CHANGED
package/www/mapshaper-gui.js
CHANGED
|
@@ -233,6 +233,30 @@
|
|
|
233
233
|
return parsed[0].options;
|
|
234
234
|
};
|
|
235
235
|
|
|
236
|
+
// Convert an options object to a command line options string
|
|
237
|
+
// (used by gui-import-control.js)
|
|
238
|
+
// TODO: handle options with irregular string <-> object conversion
|
|
239
|
+
GUI.formatCommandOptions = function(o) {
|
|
240
|
+
var arr = [];
|
|
241
|
+
Object.keys(o).forEach(function(key) {
|
|
242
|
+
var name = key.replace(/_/g, '-');
|
|
243
|
+
var val = o[key];
|
|
244
|
+
var str;
|
|
245
|
+
// TODO: quote values that contain spaces
|
|
246
|
+
if (Array.isArray(val)) {
|
|
247
|
+
str = name + '=' + val.join(',');
|
|
248
|
+
} else if (val === true) {
|
|
249
|
+
str = name;
|
|
250
|
+
} else if (val === false) {
|
|
251
|
+
return;
|
|
252
|
+
} else {
|
|
253
|
+
str = name + '=' + val;
|
|
254
|
+
}
|
|
255
|
+
arr.push(str);
|
|
256
|
+
});
|
|
257
|
+
return arr.join(' ');
|
|
258
|
+
};
|
|
259
|
+
|
|
236
260
|
// @file: Zip file
|
|
237
261
|
// @cb: function(err, <files>)
|
|
238
262
|
//
|
|
@@ -1107,20 +1131,19 @@
|
|
|
1107
1131
|
|
|
1108
1132
|
function ImportControl(gui, opts) {
|
|
1109
1133
|
var model = gui.model;
|
|
1134
|
+
var initialImport = true;
|
|
1110
1135
|
var importCount = 0;
|
|
1111
1136
|
var importTotal = 0;
|
|
1112
1137
|
var overQuickView = false;
|
|
1113
|
-
var useQuickView = opts.quick_view; // may be set by mapshaper-gui
|
|
1114
1138
|
var queuedFiles = [];
|
|
1115
1139
|
var manifestFiles = opts.files || [];
|
|
1116
|
-
var cachedFiles = {};
|
|
1117
1140
|
var catalog;
|
|
1118
1141
|
|
|
1119
1142
|
if (opts.catalog) {
|
|
1120
1143
|
catalog = new CatalogControl(gui, opts.catalog, downloadFiles);
|
|
1121
1144
|
}
|
|
1122
1145
|
|
|
1123
|
-
new SimpleButton('#import-buttons .submit-btn').on('click',
|
|
1146
|
+
new SimpleButton('#import-buttons .submit-btn').on('click', importQueuedFiles);
|
|
1124
1147
|
new SimpleButton('#import-buttons .cancel-btn').on('click', gui.clearMode);
|
|
1125
1148
|
new DropControl(gui, 'body', receiveFiles);
|
|
1126
1149
|
new FileChooser('#file-selection-btn', receiveFiles);
|
|
@@ -1128,7 +1151,7 @@
|
|
|
1128
1151
|
new FileChooser('#add-file-btn', receiveFiles);
|
|
1129
1152
|
initDropArea('#import-quick-drop', true);
|
|
1130
1153
|
initDropArea('#import-drop');
|
|
1131
|
-
gui.keyboard.onMenuSubmit(El('#import-options'),
|
|
1154
|
+
gui.keyboard.onMenuSubmit(El('#import-options'), importQueuedFiles);
|
|
1132
1155
|
|
|
1133
1156
|
gui.addMode('import', turnOn, turnOff);
|
|
1134
1157
|
gui.enterMode('import');
|
|
@@ -1140,6 +1163,10 @@
|
|
|
1140
1163
|
}
|
|
1141
1164
|
});
|
|
1142
1165
|
|
|
1166
|
+
function useQuickView() {
|
|
1167
|
+
return initialImport && (opts.quick_view || overQuickView);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1143
1170
|
function initDropArea(el, isQuick) {
|
|
1144
1171
|
var area = El(el)
|
|
1145
1172
|
.on('dragleave', onout)
|
|
@@ -1157,15 +1184,19 @@
|
|
|
1157
1184
|
}
|
|
1158
1185
|
}
|
|
1159
1186
|
|
|
1160
|
-
function
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1187
|
+
async function importQueuedFiles() {
|
|
1188
|
+
gui.container.removeClass('queued-files');
|
|
1189
|
+
gui.container.removeClass('splash-screen');
|
|
1190
|
+
var files = queuedFiles;
|
|
1191
|
+
try {
|
|
1192
|
+
if (files.length > 0) {
|
|
1193
|
+
queuedFiles = [];
|
|
1194
|
+
await importFiles(files);
|
|
1195
|
+
}
|
|
1196
|
+
} catch(e) {
|
|
1197
|
+
console.error(e);
|
|
1198
|
+
}
|
|
1199
|
+
gui.clearMode();
|
|
1169
1200
|
}
|
|
1170
1201
|
|
|
1171
1202
|
function turnOn() {
|
|
@@ -1186,13 +1217,8 @@
|
|
|
1186
1217
|
importCount = 0;
|
|
1187
1218
|
}
|
|
1188
1219
|
gui.clearProgressMessage();
|
|
1189
|
-
|
|
1190
|
-
close();
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
function close() {
|
|
1220
|
+
initialImport = false; // unset 'quick view' mode, if on
|
|
1194
1221
|
clearQueuedFiles();
|
|
1195
|
-
cachedFiles = {};
|
|
1196
1222
|
}
|
|
1197
1223
|
|
|
1198
1224
|
function onImportComplete() {
|
|
@@ -1226,34 +1252,6 @@
|
|
|
1226
1252
|
}, []);
|
|
1227
1253
|
}
|
|
1228
1254
|
|
|
1229
|
-
// When a Shapefile component is at the head of the queue, move the entire
|
|
1230
|
-
// Shapefile to the front of the queue, sorted in reverse alphabetical order,
|
|
1231
|
-
// (a kludge), so .shp is read before .dbf and .prj
|
|
1232
|
-
// (If a .dbf file is imported before a .shp, it becomes a separate dataset)
|
|
1233
|
-
// TODO: import Shapefile parts without relying on this kludge
|
|
1234
|
-
function sortQueue(queue) {
|
|
1235
|
-
var nextFile = queue[0];
|
|
1236
|
-
var basename, parts;
|
|
1237
|
-
if (!isShapefilePart(nextFile.name)) {
|
|
1238
|
-
return queue;
|
|
1239
|
-
}
|
|
1240
|
-
basename = internal.getFileBase(nextFile.name).toLowerCase();
|
|
1241
|
-
parts = [];
|
|
1242
|
-
queue = queue.filter(function(file) {
|
|
1243
|
-
if (internal.getFileBase(file.name).toLowerCase() == basename) {
|
|
1244
|
-
parts.push(file);
|
|
1245
|
-
return false;
|
|
1246
|
-
}
|
|
1247
|
-
return true;
|
|
1248
|
-
});
|
|
1249
|
-
parts.sort(function(a, b) {
|
|
1250
|
-
// Sorting on LC filename so Shapefiles with mixed-case
|
|
1251
|
-
// extensions are sorted correctly
|
|
1252
|
-
return a.name.toLowerCase() < b.name.toLowerCase() ? 1 : -1;
|
|
1253
|
-
});
|
|
1254
|
-
return parts.concat(queue);
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
1255
|
function showQueuedFiles() {
|
|
1258
1256
|
var list = gui.container.findChild('.dropped-file-list').empty();
|
|
1259
1257
|
queuedFiles.forEach(function(f) {
|
|
@@ -1261,16 +1259,14 @@
|
|
|
1261
1259
|
});
|
|
1262
1260
|
}
|
|
1263
1261
|
|
|
1264
|
-
function receiveFiles(files) {
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
addFilesToQueue(files);
|
|
1262
|
+
async function receiveFiles(files) {
|
|
1263
|
+
// TODO: show importing message here?
|
|
1264
|
+
var expanded = await expandFiles(files);
|
|
1265
|
+
addFilesToQueue(expanded);
|
|
1269
1266
|
if (queuedFiles.length === 0) return;
|
|
1270
1267
|
gui.enterMode('import');
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
onSubmit();
|
|
1268
|
+
if (useQuickView()) {
|
|
1269
|
+
importQueuedFiles();
|
|
1274
1270
|
} else {
|
|
1275
1271
|
gui.container.addClass('queued-files');
|
|
1276
1272
|
El('#path-import-options').classed('hidden', !filesMayContainPaths(queuedFiles));
|
|
@@ -1278,25 +1274,51 @@
|
|
|
1278
1274
|
}
|
|
1279
1275
|
}
|
|
1280
1276
|
|
|
1281
|
-
function
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1277
|
+
async function expandFiles(files) {
|
|
1278
|
+
var files2 = [], expanded;
|
|
1279
|
+
for (var f of files) {
|
|
1280
|
+
if (internal.isZipFile(f.name)) {
|
|
1281
|
+
expanded = await readZipFile(f);
|
|
1282
|
+
files2 = files2.concat(expanded);
|
|
1283
|
+
} else {
|
|
1284
|
+
files2.push(f);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
return files2;
|
|
1286
1288
|
}
|
|
1287
1289
|
|
|
1288
|
-
function
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1290
|
+
async function importFiles(files) {
|
|
1291
|
+
var fileData = await readFiles(files);
|
|
1292
|
+
var importOpts = readImportOpts();
|
|
1293
|
+
var groups = groupFilesForImport(fileData, importOpts);
|
|
1294
|
+
for (var group of groups) {
|
|
1295
|
+
if (group.size > 4e7) {
|
|
1296
|
+
gui.showProgressMessage('Importing');
|
|
1297
|
+
await wait(35);
|
|
1298
|
+
}
|
|
1299
|
+
importDataset(group, importOpts);
|
|
1300
|
+
}
|
|
1292
1301
|
}
|
|
1293
1302
|
|
|
1294
|
-
function
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1303
|
+
function importDataset(group, importOpts) {
|
|
1304
|
+
var optStr = GUI.formatCommandOptions(importOpts);
|
|
1305
|
+
var dataset = internal.importContent(group, importOpts);
|
|
1306
|
+
if (datasetIsEmpty(dataset)) return;
|
|
1307
|
+
if (group.layername) {
|
|
1308
|
+
dataset.layers.forEach(lyr => lyr.name = group.layername);
|
|
1298
1309
|
}
|
|
1299
|
-
|
|
1310
|
+
// save import options for use by repair control, etc.
|
|
1311
|
+
dataset.info.import_options = importOpts;
|
|
1312
|
+
model.addDataset(dataset);
|
|
1313
|
+
importCount++;
|
|
1314
|
+
gui.session.fileImported(group.filename, optStr);
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
function filesMayContainPaths(files) {
|
|
1318
|
+
return utils.some(files, function(f) {
|
|
1319
|
+
var type = internal.guessInputFileType(f.name);
|
|
1320
|
+
return type == 'shp' || type == 'json' || internal.isZipFile(f.name);
|
|
1321
|
+
});
|
|
1300
1322
|
}
|
|
1301
1323
|
|
|
1302
1324
|
function datasetIsEmpty(dataset) {
|
|
@@ -1305,41 +1327,27 @@
|
|
|
1305
1327
|
});
|
|
1306
1328
|
}
|
|
1307
1329
|
|
|
1308
|
-
function procNextQueuedFile() {
|
|
1309
|
-
if (queuedFiles.length === 0) {
|
|
1310
|
-
gui.clearMode();
|
|
1311
|
-
} else {
|
|
1312
|
-
queuedFiles = sortQueue(queuedFiles);
|
|
1313
|
-
readFile(queuedFiles.shift());
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1316
1330
|
|
|
1317
1331
|
// TODO: support .cpg
|
|
1318
1332
|
function isShapefilePart(name) {
|
|
1319
1333
|
return /\.(shp|shx|dbf|prj)$/i.test(name);
|
|
1320
1334
|
}
|
|
1321
1335
|
|
|
1322
|
-
|
|
1323
1336
|
function readImportOpts() {
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
if (useQuickView) return '';
|
|
1335
|
-
var freeform = El('#import-options .advanced-options').node().value;
|
|
1336
|
-
var opts = readImportOpts();
|
|
1337
|
-
if (opts.snap) freeform = 'snap ' + freeform;
|
|
1338
|
-
return freeform.trim();
|
|
1337
|
+
var importOpts;
|
|
1338
|
+
if (useQuickView()) {
|
|
1339
|
+
importOpts = {}; // default opts using quickview
|
|
1340
|
+
} else {
|
|
1341
|
+
var freeform = El('#import-options .advanced-options').node().value;
|
|
1342
|
+
importOpts = GUI.parseFreeformOptions(freeform, 'i');
|
|
1343
|
+
importOpts.no_repair = !El("#repair-intersections-opt").node().checked;
|
|
1344
|
+
importOpts.snap = !!El("#snap-points-opt").node().checked;
|
|
1345
|
+
}
|
|
1346
|
+
return importOpts;
|
|
1339
1347
|
}
|
|
1340
1348
|
|
|
1341
1349
|
// @file a File object
|
|
1342
|
-
function
|
|
1350
|
+
async function readContentFileAsync(file, cb) {
|
|
1343
1351
|
var name = file.name,
|
|
1344
1352
|
reader = new FileReader(),
|
|
1345
1353
|
useBinary = internal.isSupportedBinaryInputType(name) ||
|
|
@@ -1349,9 +1357,9 @@
|
|
|
1349
1357
|
|
|
1350
1358
|
reader.addEventListener('loadend', function(e) {
|
|
1351
1359
|
if (!reader.result) {
|
|
1352
|
-
|
|
1360
|
+
cb(new Error());
|
|
1353
1361
|
} else {
|
|
1354
|
-
|
|
1362
|
+
cb(null, reader.result);
|
|
1355
1363
|
}
|
|
1356
1364
|
});
|
|
1357
1365
|
if (useBinary) {
|
|
@@ -1362,91 +1370,6 @@
|
|
|
1362
1370
|
}
|
|
1363
1371
|
}
|
|
1364
1372
|
|
|
1365
|
-
function importFileContent(fileName, content) {
|
|
1366
|
-
var fileType = internal.guessInputType(fileName, content),
|
|
1367
|
-
importOpts = readImportOpts(),
|
|
1368
|
-
matches = findMatchingShp(fileName),
|
|
1369
|
-
dataset, lyr;
|
|
1370
|
-
|
|
1371
|
-
// Add dbf data to a previously imported .shp file with a matching name
|
|
1372
|
-
// (.shp should have been queued before .dbf)
|
|
1373
|
-
if (fileType == 'dbf' && matches.length > 0) {
|
|
1374
|
-
// find an imported .shp layer that is missing attribute data
|
|
1375
|
-
// (if multiple matches, try to use the most recently imported one)
|
|
1376
|
-
dataset = matches.reduce(function(memo, d) {
|
|
1377
|
-
if (!d.layers[0].data) {
|
|
1378
|
-
memo = d;
|
|
1379
|
-
}
|
|
1380
|
-
return memo;
|
|
1381
|
-
}, null);
|
|
1382
|
-
if (dataset) {
|
|
1383
|
-
lyr = dataset.layers[0];
|
|
1384
|
-
lyr.data = new internal.ShapefileTable(content, importOpts.encoding);
|
|
1385
|
-
if (lyr.shapes && lyr.data.size() != lyr.shapes.length) {
|
|
1386
|
-
stop("Different number of records in .shp and .dbf files");
|
|
1387
|
-
}
|
|
1388
|
-
if (!lyr.geometry_type) {
|
|
1389
|
-
// kludge: trigger display of table cells if .shp has null geometry
|
|
1390
|
-
// TODO: test case if lyr is not the current active layer
|
|
1391
|
-
model.updated({});
|
|
1392
|
-
}
|
|
1393
|
-
procNextQueuedFile();
|
|
1394
|
-
return;
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
|
-
if (fileType == 'shx') {
|
|
1399
|
-
// save .shx for use when importing .shp
|
|
1400
|
-
// (queue should be sorted so that .shx is processed before .shp)
|
|
1401
|
-
cachedFiles[fileName.toLowerCase()] = {filename: fileName, content: content};
|
|
1402
|
-
procNextQueuedFile();
|
|
1403
|
-
return;
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
// Add .prj file to previously imported .shp file
|
|
1407
|
-
if (fileType == 'prj') {
|
|
1408
|
-
matches.forEach(function(d) {
|
|
1409
|
-
if (!d.info.prj) {
|
|
1410
|
-
d.info.prj = content;
|
|
1411
|
-
}
|
|
1412
|
-
});
|
|
1413
|
-
procNextQueuedFile();
|
|
1414
|
-
return;
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
|
-
importNewDataset(fileType, fileName, content, importOpts);
|
|
1418
|
-
}
|
|
1419
|
-
|
|
1420
|
-
function importNewDataset(fileType, fileName, content, importOpts) {
|
|
1421
|
-
var size = content.byteLength || content.length, // ArrayBuffer or string
|
|
1422
|
-
delay = 0;
|
|
1423
|
-
|
|
1424
|
-
// show importing message if file is large
|
|
1425
|
-
if (size > 4e7) {
|
|
1426
|
-
gui.showProgressMessage('Importing');
|
|
1427
|
-
delay = 35;
|
|
1428
|
-
}
|
|
1429
|
-
setTimeout(function() {
|
|
1430
|
-
var dataset;
|
|
1431
|
-
var input = {};
|
|
1432
|
-
try {
|
|
1433
|
-
input[fileType] = {filename: fileName, content: content};
|
|
1434
|
-
if (fileType == 'shp') {
|
|
1435
|
-
// shx file should already be cached, if it was added together with the shp
|
|
1436
|
-
input.shx = cachedFiles[fileName.replace(/shp$/i, 'shx').toLowerCase()] || null;
|
|
1437
|
-
}
|
|
1438
|
-
dataset = internal.importContent(input, importOpts);
|
|
1439
|
-
// save import options for use by repair control, etc.
|
|
1440
|
-
dataset.info.import_options = importOpts;
|
|
1441
|
-
gui.session.fileImported(fileName, readImportOptsAsString());
|
|
1442
|
-
addDataset(dataset);
|
|
1443
|
-
|
|
1444
|
-
} catch(e) {
|
|
1445
|
-
handleImportError(e, fileName);
|
|
1446
|
-
}
|
|
1447
|
-
}, delay);
|
|
1448
|
-
}
|
|
1449
|
-
|
|
1450
1373
|
function handleImportError(e, fileName) {
|
|
1451
1374
|
var msg = utils.isString(e) ? e : e.message;
|
|
1452
1375
|
if (fileName) {
|
|
@@ -1457,34 +1380,6 @@
|
|
|
1457
1380
|
console.error(e);
|
|
1458
1381
|
}
|
|
1459
1382
|
|
|
1460
|
-
function handleZipFiles(files) {
|
|
1461
|
-
return files.filter(function(file) {
|
|
1462
|
-
var isZip = internal.isZipFile(file.name);
|
|
1463
|
-
if (isZip) {
|
|
1464
|
-
importZipFile(file);
|
|
1465
|
-
}
|
|
1466
|
-
return !isZip;
|
|
1467
|
-
});
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1470
|
-
function importZipFile(file) {
|
|
1471
|
-
// gui.showProgressMessage('Importing');
|
|
1472
|
-
setTimeout(function() {
|
|
1473
|
-
GUI.readZipFile(file, function(err, files) {
|
|
1474
|
-
if (err) {
|
|
1475
|
-
handleImportError(err, file.name);
|
|
1476
|
-
} else {
|
|
1477
|
-
// don't try to import .txt files from zip files
|
|
1478
|
-
// (these would be parsed as dsv and throw errows)
|
|
1479
|
-
files = files.filter(function(f) {
|
|
1480
|
-
return !/\.txt$/i.test(f.name);
|
|
1481
|
-
});
|
|
1482
|
-
receiveFiles(files);
|
|
1483
|
-
}
|
|
1484
|
-
});
|
|
1485
|
-
}, 35);
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
1383
|
function prepFilesForDownload(names) {
|
|
1489
1384
|
var items = names.map(function(name) {
|
|
1490
1385
|
var isUrl = /:\/\//.test(name);
|
|
@@ -1530,37 +1425,107 @@
|
|
|
1530
1425
|
});
|
|
1531
1426
|
}
|
|
1532
1427
|
|
|
1533
|
-
function
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
req.addEventListener('progress', function(e) {
|
|
1543
|
-
if (!e.lengthComputable) return;
|
|
1544
|
-
var pct = e.loaded / e.total;
|
|
1545
|
-
if (catalog) catalog.progress(pct);
|
|
1428
|
+
function wait(ms) {
|
|
1429
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
function runAsync(fn, arg) {
|
|
1433
|
+
return new Promise((resolve, reject) => {
|
|
1434
|
+
fn(arg, function(err, data) {
|
|
1435
|
+
return err ? reject(err) : resolve(data);
|
|
1436
|
+
});
|
|
1546
1437
|
});
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
async function readZipFile(file) {
|
|
1441
|
+
var files;
|
|
1442
|
+
await wait(35); // pause a beat so status message can display
|
|
1443
|
+
try {
|
|
1444
|
+
files = await runAsync(GUI.readZipFile, file);
|
|
1445
|
+
// don't try to import .txt files from zip files
|
|
1446
|
+
// (these would be parsed as dsv and throw errows)
|
|
1447
|
+
files = files.filter(function(f) {
|
|
1448
|
+
return !/\.txt$/i.test(f.name);
|
|
1449
|
+
});
|
|
1450
|
+
} catch(e) {
|
|
1451
|
+
handleImportError(e, file.name);
|
|
1452
|
+
files = [];
|
|
1453
|
+
}
|
|
1454
|
+
return files;
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
async function readFileData(file) {
|
|
1458
|
+
try {
|
|
1459
|
+
var content = await runAsync(readContentFileAsync, file);
|
|
1460
|
+
return {
|
|
1461
|
+
content: content,
|
|
1462
|
+
size: content.byteLength || content.length, // ArrayBuffer or string
|
|
1463
|
+
name: file.name,
|
|
1464
|
+
basename: internal.getFileBase(file.name).toLowerCase(),
|
|
1465
|
+
type: internal.guessInputType(file.name, content)
|
|
1466
|
+
};
|
|
1467
|
+
} catch (e) {
|
|
1468
|
+
handleImportError("Web browser was unable to load the file.", file.name);
|
|
1469
|
+
}
|
|
1470
|
+
return null;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
async function readFiles(files) {
|
|
1474
|
+
var data = [], d;
|
|
1475
|
+
for (var file of files) {
|
|
1476
|
+
d = await readFileData(file);
|
|
1477
|
+
if (d) data.push(d);
|
|
1478
|
+
}
|
|
1479
|
+
return data;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
function groupFilesForImport(data, importOpts) {
|
|
1483
|
+
var names = importOpts.name ? [importOpts.name] : null;
|
|
1484
|
+
if (initialImport && opts.name) { // name from mapshaper-gui --name option
|
|
1485
|
+
names = opts.name.split(',');
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
function key(basename, type) {
|
|
1489
|
+
return basename + '.' + type;
|
|
1490
|
+
}
|
|
1491
|
+
function hasShp(basename) {
|
|
1492
|
+
var shpKey = key(basename, 'shp');
|
|
1493
|
+
return data.some(d => key(d.basename, d.type) == shpKey);
|
|
1494
|
+
}
|
|
1495
|
+
data.forEach(d => {
|
|
1496
|
+
if (d.type == 'shp' || !isShapefilePart(d.name)) {
|
|
1497
|
+
d.group = key(d.basename, d.type);
|
|
1498
|
+
d.filename = d.name;
|
|
1499
|
+
} else if (hasShp(d.basename)) {
|
|
1500
|
+
d.group = key(d.basename, 'shp');
|
|
1501
|
+
} else if (d.type == 'dbf') {
|
|
1502
|
+
d.filename = d.name;
|
|
1503
|
+
d.group = key(d.basename, 'dbf');
|
|
1556
1504
|
} else {
|
|
1557
|
-
|
|
1558
|
-
|
|
1505
|
+
// shapefile part without a .shp file
|
|
1506
|
+
d.group = null;
|
|
1559
1507
|
}
|
|
1560
|
-
next(err, memo);
|
|
1561
1508
|
});
|
|
1562
|
-
|
|
1563
|
-
|
|
1509
|
+
var index = {};
|
|
1510
|
+
var groups = [];
|
|
1511
|
+
data.forEach(d => {
|
|
1512
|
+
if (!d.group) return;
|
|
1513
|
+
var g = index[d.group];
|
|
1514
|
+
if (!g) {
|
|
1515
|
+
g = {};
|
|
1516
|
+
g.layername = names ? names[groups.length] || names[names.length - 1] : null;
|
|
1517
|
+
groups.push(g);
|
|
1518
|
+
index[d.group] = g;
|
|
1519
|
+
}
|
|
1520
|
+
g.size = (g.size || 0) + d.size; // accumulate size
|
|
1521
|
+
g[d.type] = {
|
|
1522
|
+
filename: d.name,
|
|
1523
|
+
content: d.content
|
|
1524
|
+
};
|
|
1525
|
+
// kludge: stash import name for session history
|
|
1526
|
+
if (d.filename) g.filename = d.filename;
|
|
1527
|
+
});
|
|
1528
|
+
return groups;
|
|
1564
1529
|
}
|
|
1565
1530
|
}
|
|
1566
1531
|
|
|
@@ -8452,6 +8417,7 @@
|
|
|
8452
8417
|
opts.display_all = vars['display-all'] || vars.a || !!manifest.display_all;
|
|
8453
8418
|
opts.quick_view = vars['quick-view'] || vars.q || !!manifest.quick_view;
|
|
8454
8419
|
opts.target = vars.target || manifest.target || null;
|
|
8420
|
+
opts.name = vars.name || manifest.name || null;
|
|
8455
8421
|
return opts;
|
|
8456
8422
|
}
|
|
8457
8423
|
|
package/www/mapshaper.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
|
|
3
|
-
var VERSION = "0.5.
|
|
3
|
+
var VERSION = "0.5.88";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
var utils = /*#__PURE__*/Object.freeze({
|
|
@@ -9350,16 +9350,12 @@
|
|
|
9350
9350
|
var getters = {
|
|
9351
9351
|
name: lyr.name,
|
|
9352
9352
|
data: records,
|
|
9353
|
-
type: lyr.geometry_type
|
|
9353
|
+
type: lyr.geometry_type,
|
|
9354
|
+
size: getFeatureCount(lyr),
|
|
9355
|
+
empty: getFeatureCount(lyr) === 0
|
|
9354
9356
|
};
|
|
9355
9357
|
addGetters(obj, getters);
|
|
9356
9358
|
addBBoxGetter(obj, lyr, arcs);
|
|
9357
|
-
obj.empty = function() {
|
|
9358
|
-
return getFeatureCount(lyr) === 0;
|
|
9359
|
-
};
|
|
9360
|
-
obj.size = function() {
|
|
9361
|
-
return getFeatureCount(lyr);
|
|
9362
|
-
};
|
|
9363
9359
|
return obj;
|
|
9364
9360
|
}
|
|
9365
9361
|
|
|
@@ -20116,33 +20112,22 @@ ${svg}
|
|
|
20116
20112
|
.option('target', targetOpt);
|
|
20117
20113
|
|
|
20118
20114
|
parser.command('symbols')
|
|
20119
|
-
|
|
20115
|
+
.describe('symbolize points as arrows, circles, stars, polygons, etc.')
|
|
20120
20116
|
.option('type', {
|
|
20121
|
-
describe: 'symbol type (e.g. star, polygon,
|
|
20122
|
-
})
|
|
20123
|
-
.option('scale', {
|
|
20124
|
-
describe: 'scale symbols by a factor',
|
|
20125
|
-
type: 'number'
|
|
20117
|
+
describe: 'symbol type (e.g. arrow, circle, square, star, polygon, ring)'
|
|
20126
20118
|
})
|
|
20127
|
-
.option('
|
|
20128
|
-
|
|
20129
|
-
|
|
20119
|
+
.option('stroke', {})
|
|
20120
|
+
.option('stroke-width', {})
|
|
20121
|
+
.option('fill', {
|
|
20122
|
+
describe: 'symbol fill color'
|
|
20130
20123
|
})
|
|
20131
20124
|
.option('polygons', {
|
|
20132
20125
|
describe: 'generate symbols as polygons instead of SVG objects',
|
|
20133
20126
|
type: 'flag'
|
|
20134
20127
|
})
|
|
20135
|
-
.option('
|
|
20136
|
-
describe: '
|
|
20137
|
-
type: '
|
|
20138
|
-
})
|
|
20139
|
-
.option('sides', {
|
|
20140
|
-
describe: 'number of sides of a polygon symbol',
|
|
20141
|
-
type: 'number'
|
|
20142
|
-
})
|
|
20143
|
-
.option('orientation', {
|
|
20144
|
-
// TODO: removed (replaced by flipped and rotated)
|
|
20145
|
-
// describe: 'use orientation=b for a rotated or flipped orientation'
|
|
20128
|
+
.option('pixel-scale', {
|
|
20129
|
+
describe: 'set symbol scale in meters-per-pixel (for polygons option)',
|
|
20130
|
+
type: 'number',
|
|
20146
20131
|
})
|
|
20147
20132
|
// .option('flipped', {
|
|
20148
20133
|
// type: 'flag',
|
|
@@ -20150,11 +20135,23 @@ ${svg}
|
|
|
20150
20135
|
// })
|
|
20151
20136
|
.option('rotated', {
|
|
20152
20137
|
type: 'flag',
|
|
20153
|
-
describe: 'symbol is rotated to
|
|
20138
|
+
describe: 'symbol is rotated to an alternate orientation'
|
|
20154
20139
|
})
|
|
20155
20140
|
.option('rotation', {
|
|
20156
20141
|
describe: 'rotation of symbol in degrees'
|
|
20157
20142
|
})
|
|
20143
|
+
.option('scale', {
|
|
20144
|
+
describe: 'scale symbols by a multiplier',
|
|
20145
|
+
type: 'number'
|
|
20146
|
+
})
|
|
20147
|
+
.option('radius', {
|
|
20148
|
+
describe: 'distance from center to farthest point on the symbol',
|
|
20149
|
+
type: 'distance'
|
|
20150
|
+
})
|
|
20151
|
+
.option('sides', {
|
|
20152
|
+
describe: '(polygon) number of sides of a (regular) polygon symbol',
|
|
20153
|
+
type: 'number'
|
|
20154
|
+
})
|
|
20158
20155
|
.option('points', {
|
|
20159
20156
|
describe: '(star) number of points'
|
|
20160
20157
|
})
|
|
@@ -20172,7 +20169,7 @@ ${svg}
|
|
|
20172
20169
|
})
|
|
20173
20170
|
.option('direction', {
|
|
20174
20171
|
old_alias: 'arrow-direction',
|
|
20175
|
-
describe: '(arrow) angle off vertical (-90 = left-pointing)'
|
|
20172
|
+
describe: '(arrow) angle off of vertical (-90 = left-pointing)'
|
|
20176
20173
|
})
|
|
20177
20174
|
.option('head-angle', {
|
|
20178
20175
|
old_alias: 'arrow-head-angle',
|
|
@@ -20207,17 +20204,12 @@ ${svg}
|
|
|
20207
20204
|
})
|
|
20208
20205
|
.option('min-stem-ratio', {
|
|
20209
20206
|
old_alias: 'arrow-min-stem',
|
|
20210
|
-
describe: '(arrow)
|
|
20207
|
+
describe: '(arrow) minimum ratio of stem to total length',
|
|
20211
20208
|
type: 'number'
|
|
20212
20209
|
})
|
|
20213
20210
|
.option('anchor', {
|
|
20214
20211
|
describe: '(arrow) takes one of: start, middle, end (default is start)'
|
|
20215
20212
|
})
|
|
20216
|
-
.option('stroke', {})
|
|
20217
|
-
.option('stroke-width', {})
|
|
20218
|
-
.option('fill', {
|
|
20219
|
-
describe: 'symbol fill color'
|
|
20220
|
-
})
|
|
20221
20213
|
.option('effect', {})
|
|
20222
20214
|
// .option('where', whereOpt)
|
|
20223
20215
|
.option('name', nameOpt)
|