mapshaper 0.5.88 → 0.5.92
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 +19 -9
- package/mapshaper.js +141 -82
- package/package.json +2 -2
- package/www/index.html +1 -1
- package/www/mapshaper-gui.js +563 -527
- package/www/mapshaper.js +141 -82
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() {
|
|
@@ -1206,6 +1232,7 @@
|
|
|
1206
1232
|
}
|
|
1207
1233
|
}
|
|
1208
1234
|
model.updated({select: true});
|
|
1235
|
+
|
|
1209
1236
|
}
|
|
1210
1237
|
|
|
1211
1238
|
function clearQueuedFiles() {
|
|
@@ -1226,34 +1253,6 @@
|
|
|
1226
1253
|
}, []);
|
|
1227
1254
|
}
|
|
1228
1255
|
|
|
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
1256
|
function showQueuedFiles() {
|
|
1258
1257
|
var list = gui.container.findChild('.dropped-file-list').empty();
|
|
1259
1258
|
queuedFiles.forEach(function(f) {
|
|
@@ -1261,16 +1260,14 @@
|
|
|
1261
1260
|
});
|
|
1262
1261
|
}
|
|
1263
1262
|
|
|
1264
|
-
function receiveFiles(files) {
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
addFilesToQueue(files);
|
|
1263
|
+
async function receiveFiles(files) {
|
|
1264
|
+
// TODO: show importing message here?
|
|
1265
|
+
var expanded = await expandFiles(files);
|
|
1266
|
+
addFilesToQueue(expanded);
|
|
1269
1267
|
if (queuedFiles.length === 0) return;
|
|
1270
1268
|
gui.enterMode('import');
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
onSubmit();
|
|
1269
|
+
if (useQuickView()) {
|
|
1270
|
+
importQueuedFiles();
|
|
1274
1271
|
} else {
|
|
1275
1272
|
gui.container.addClass('queued-files');
|
|
1276
1273
|
El('#path-import-options').classed('hidden', !filesMayContainPaths(queuedFiles));
|
|
@@ -1278,25 +1275,51 @@
|
|
|
1278
1275
|
}
|
|
1279
1276
|
}
|
|
1280
1277
|
|
|
1281
|
-
function
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1278
|
+
async function expandFiles(files) {
|
|
1279
|
+
var files2 = [], expanded;
|
|
1280
|
+
for (var f of files) {
|
|
1281
|
+
if (internal.isZipFile(f.name)) {
|
|
1282
|
+
expanded = await readZipFile(f);
|
|
1283
|
+
files2 = files2.concat(expanded);
|
|
1284
|
+
} else {
|
|
1285
|
+
files2.push(f);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
return files2;
|
|
1286
1289
|
}
|
|
1287
1290
|
|
|
1288
|
-
function
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1291
|
+
async function importFiles(files) {
|
|
1292
|
+
var fileData = await readFiles(files);
|
|
1293
|
+
var importOpts = readImportOpts();
|
|
1294
|
+
var groups = groupFilesForImport(fileData, importOpts);
|
|
1295
|
+
for (var group of groups) {
|
|
1296
|
+
if (group.size > 4e7) {
|
|
1297
|
+
gui.showProgressMessage('Importing');
|
|
1298
|
+
await wait(35);
|
|
1299
|
+
}
|
|
1300
|
+
importDataset(group, importOpts);
|
|
1301
|
+
}
|
|
1292
1302
|
}
|
|
1293
1303
|
|
|
1294
|
-
function
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1304
|
+
function importDataset(group, importOpts) {
|
|
1305
|
+
var optStr = GUI.formatCommandOptions(importOpts);
|
|
1306
|
+
var dataset = internal.importContent(group, importOpts);
|
|
1307
|
+
if (datasetIsEmpty(dataset)) return;
|
|
1308
|
+
if (group.layername) {
|
|
1309
|
+
dataset.layers.forEach(lyr => lyr.name = group.layername);
|
|
1298
1310
|
}
|
|
1299
|
-
|
|
1311
|
+
// save import options for use by repair control, etc.
|
|
1312
|
+
dataset.info.import_options = importOpts;
|
|
1313
|
+
model.addDataset(dataset);
|
|
1314
|
+
importCount++;
|
|
1315
|
+
gui.session.fileImported(group.filename, optStr);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
function filesMayContainPaths(files) {
|
|
1319
|
+
return utils.some(files, function(f) {
|
|
1320
|
+
var type = internal.guessInputFileType(f.name);
|
|
1321
|
+
return type == 'shp' || type == 'json' || internal.isZipFile(f.name);
|
|
1322
|
+
});
|
|
1300
1323
|
}
|
|
1301
1324
|
|
|
1302
1325
|
function datasetIsEmpty(dataset) {
|
|
@@ -1305,41 +1328,27 @@
|
|
|
1305
1328
|
});
|
|
1306
1329
|
}
|
|
1307
1330
|
|
|
1308
|
-
function procNextQueuedFile() {
|
|
1309
|
-
if (queuedFiles.length === 0) {
|
|
1310
|
-
gui.clearMode();
|
|
1311
|
-
} else {
|
|
1312
|
-
queuedFiles = sortQueue(queuedFiles);
|
|
1313
|
-
readFile(queuedFiles.shift());
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1316
1331
|
|
|
1317
1332
|
// TODO: support .cpg
|
|
1318
1333
|
function isShapefilePart(name) {
|
|
1319
1334
|
return /\.(shp|shx|dbf|prj)$/i.test(name);
|
|
1320
1335
|
}
|
|
1321
1336
|
|
|
1322
|
-
|
|
1323
1337
|
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();
|
|
1338
|
+
var importOpts;
|
|
1339
|
+
if (useQuickView()) {
|
|
1340
|
+
importOpts = {}; // default opts using quickview
|
|
1341
|
+
} else {
|
|
1342
|
+
var freeform = El('#import-options .advanced-options').node().value;
|
|
1343
|
+
importOpts = GUI.parseFreeformOptions(freeform, 'i');
|
|
1344
|
+
importOpts.no_repair = !El("#repair-intersections-opt").node().checked;
|
|
1345
|
+
importOpts.snap = !!El("#snap-points-opt").node().checked;
|
|
1346
|
+
}
|
|
1347
|
+
return importOpts;
|
|
1339
1348
|
}
|
|
1340
1349
|
|
|
1341
1350
|
// @file a File object
|
|
1342
|
-
function
|
|
1351
|
+
async function readContentFileAsync(file, cb) {
|
|
1343
1352
|
var name = file.name,
|
|
1344
1353
|
reader = new FileReader(),
|
|
1345
1354
|
useBinary = internal.isSupportedBinaryInputType(name) ||
|
|
@@ -1349,9 +1358,9 @@
|
|
|
1349
1358
|
|
|
1350
1359
|
reader.addEventListener('loadend', function(e) {
|
|
1351
1360
|
if (!reader.result) {
|
|
1352
|
-
|
|
1361
|
+
cb(new Error());
|
|
1353
1362
|
} else {
|
|
1354
|
-
|
|
1363
|
+
cb(null, reader.result);
|
|
1355
1364
|
}
|
|
1356
1365
|
});
|
|
1357
1366
|
if (useBinary) {
|
|
@@ -1362,91 +1371,6 @@
|
|
|
1362
1371
|
}
|
|
1363
1372
|
}
|
|
1364
1373
|
|
|
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
1374
|
function handleImportError(e, fileName) {
|
|
1451
1375
|
var msg = utils.isString(e) ? e : e.message;
|
|
1452
1376
|
if (fileName) {
|
|
@@ -1457,34 +1381,6 @@
|
|
|
1457
1381
|
console.error(e);
|
|
1458
1382
|
}
|
|
1459
1383
|
|
|
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
1384
|
function prepFilesForDownload(names) {
|
|
1489
1385
|
var items = names.map(function(name) {
|
|
1490
1386
|
var isUrl = /:\/\//.test(name);
|
|
@@ -1530,37 +1426,107 @@
|
|
|
1530
1426
|
});
|
|
1531
1427
|
}
|
|
1532
1428
|
|
|
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);
|
|
1429
|
+
function wait(ms) {
|
|
1430
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
function runAsync(fn, arg) {
|
|
1434
|
+
return new Promise((resolve, reject) => {
|
|
1435
|
+
fn(arg, function(err, data) {
|
|
1436
|
+
return err ? reject(err) : resolve(data);
|
|
1437
|
+
});
|
|
1546
1438
|
});
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
async function readZipFile(file) {
|
|
1442
|
+
var files;
|
|
1443
|
+
await wait(35); // pause a beat so status message can display
|
|
1444
|
+
try {
|
|
1445
|
+
files = await runAsync(GUI.readZipFile, file);
|
|
1446
|
+
// don't try to import .txt files from zip files
|
|
1447
|
+
// (these would be parsed as dsv and throw errows)
|
|
1448
|
+
files = files.filter(function(f) {
|
|
1449
|
+
return !/\.txt$/i.test(f.name);
|
|
1450
|
+
});
|
|
1451
|
+
} catch(e) {
|
|
1452
|
+
handleImportError(e, file.name);
|
|
1453
|
+
files = [];
|
|
1454
|
+
}
|
|
1455
|
+
return files;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
async function readFileData(file) {
|
|
1459
|
+
try {
|
|
1460
|
+
var content = await runAsync(readContentFileAsync, file);
|
|
1461
|
+
return {
|
|
1462
|
+
content: content,
|
|
1463
|
+
size: content.byteLength || content.length, // ArrayBuffer or string
|
|
1464
|
+
name: file.name,
|
|
1465
|
+
basename: internal.getFileBase(file.name).toLowerCase(),
|
|
1466
|
+
type: internal.guessInputType(file.name, content)
|
|
1467
|
+
};
|
|
1468
|
+
} catch (e) {
|
|
1469
|
+
handleImportError("Web browser was unable to load the file.", file.name);
|
|
1470
|
+
}
|
|
1471
|
+
return null;
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
async function readFiles(files) {
|
|
1475
|
+
var data = [], d;
|
|
1476
|
+
for (var file of files) {
|
|
1477
|
+
d = await readFileData(file);
|
|
1478
|
+
if (d) data.push(d);
|
|
1479
|
+
}
|
|
1480
|
+
return data;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
function groupFilesForImport(data, importOpts) {
|
|
1484
|
+
var names = importOpts.name ? [importOpts.name] : null;
|
|
1485
|
+
if (initialImport && opts.name) { // name from mapshaper-gui --name option
|
|
1486
|
+
names = opts.name.split(',');
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
function key(basename, type) {
|
|
1490
|
+
return basename + '.' + type;
|
|
1491
|
+
}
|
|
1492
|
+
function hasShp(basename) {
|
|
1493
|
+
var shpKey = key(basename, 'shp');
|
|
1494
|
+
return data.some(d => key(d.basename, d.type) == shpKey);
|
|
1495
|
+
}
|
|
1496
|
+
data.forEach(d => {
|
|
1497
|
+
if (d.type == 'shp' || !isShapefilePart(d.name)) {
|
|
1498
|
+
d.group = key(d.basename, d.type);
|
|
1499
|
+
d.filename = d.name;
|
|
1500
|
+
} else if (hasShp(d.basename)) {
|
|
1501
|
+
d.group = key(d.basename, 'shp');
|
|
1502
|
+
} else if (d.type == 'dbf') {
|
|
1503
|
+
d.filename = d.name;
|
|
1504
|
+
d.group = key(d.basename, 'dbf');
|
|
1556
1505
|
} else {
|
|
1557
|
-
|
|
1558
|
-
|
|
1506
|
+
// shapefile part without a .shp file
|
|
1507
|
+
d.group = null;
|
|
1559
1508
|
}
|
|
1560
|
-
next(err, memo);
|
|
1561
1509
|
});
|
|
1562
|
-
|
|
1563
|
-
|
|
1510
|
+
var index = {};
|
|
1511
|
+
var groups = [];
|
|
1512
|
+
data.forEach(d => {
|
|
1513
|
+
if (!d.group) return;
|
|
1514
|
+
var g = index[d.group];
|
|
1515
|
+
if (!g) {
|
|
1516
|
+
g = {};
|
|
1517
|
+
g.layername = names ? names[groups.length] || names[names.length - 1] : null;
|
|
1518
|
+
groups.push(g);
|
|
1519
|
+
index[d.group] = g;
|
|
1520
|
+
}
|
|
1521
|
+
g.size = (g.size || 0) + d.size; // accumulate size
|
|
1522
|
+
g[d.type] = {
|
|
1523
|
+
filename: d.name,
|
|
1524
|
+
content: d.content
|
|
1525
|
+
};
|
|
1526
|
+
// kludge: stash import name for session history
|
|
1527
|
+
if (d.filename) g.filename = d.filename;
|
|
1528
|
+
});
|
|
1529
|
+
return groups;
|
|
1564
1530
|
}
|
|
1565
1531
|
}
|
|
1566
1532
|
|
|
@@ -2222,6 +2188,13 @@
|
|
|
2222
2188
|
// expose this function, so other components can run commands (e.g. box tool)
|
|
2223
2189
|
this.runMapshaperCommands = runMapshaperCommands;
|
|
2224
2190
|
|
|
2191
|
+
this.runInitialCommands = function(str) {
|
|
2192
|
+
str = str.trim();
|
|
2193
|
+
if (!str) return;
|
|
2194
|
+
turnOn();
|
|
2195
|
+
submit(str);
|
|
2196
|
+
};
|
|
2197
|
+
|
|
2225
2198
|
consoleMessage(PROMPT);
|
|
2226
2199
|
gui.keyboard.on('keydown', onKeyDown);
|
|
2227
2200
|
window.addEventListener('beforeunload', saveHistory); // save history if console is open on refresh
|
|
@@ -3584,7 +3557,7 @@
|
|
|
3584
3557
|
|
|
3585
3558
|
// import { cloneShape } from '../paths/mapshaper-shape-utils';
|
|
3586
3559
|
// import { copyRecord } from '../datatable/mapshaper-data-utils';
|
|
3587
|
-
var snapVerticesToPoint
|
|
3560
|
+
var snapVerticesToPoint = internal.snapVerticesToPoint;
|
|
3588
3561
|
var cloneShape = internal.cloneShape;
|
|
3589
3562
|
var copyRecord = internal.copyRecord;
|
|
3590
3563
|
|
|
@@ -3693,7 +3666,7 @@
|
|
|
3693
3666
|
var arcs = target.dataset.arcs;
|
|
3694
3667
|
var p = internal.getVertexCoords(ids[0], arcs);
|
|
3695
3668
|
return function() {
|
|
3696
|
-
snapVerticesToPoint
|
|
3669
|
+
snapVerticesToPoint(ids, p, arcs, true);
|
|
3697
3670
|
};
|
|
3698
3671
|
};
|
|
3699
3672
|
|
|
@@ -4133,13 +4106,38 @@
|
|
|
4133
4106
|
return self;
|
|
4134
4107
|
}
|
|
4135
4108
|
|
|
4136
|
-
function
|
|
4109
|
+
function absArcId(arcId) {
|
|
4110
|
+
return arcId >= 0 ? arcId : ~arcId;
|
|
4111
|
+
}
|
|
4112
|
+
|
|
4113
|
+
function calcArcBounds(xx, yy, start, len) {
|
|
4114
|
+
var i = start | 0,
|
|
4115
|
+
n = isNaN(len) ? xx.length - i : len + i,
|
|
4116
|
+
x, y, xmin, ymin, xmax, ymax;
|
|
4117
|
+
if (n > 0) {
|
|
4118
|
+
xmin = xmax = xx[i];
|
|
4119
|
+
ymin = ymax = yy[i];
|
|
4120
|
+
}
|
|
4121
|
+
for (i++; i<n; i++) {
|
|
4122
|
+
x = xx[i];
|
|
4123
|
+
y = yy[i];
|
|
4124
|
+
if (x < xmin) xmin = x;
|
|
4125
|
+
if (x > xmax) xmax = x;
|
|
4126
|
+
if (y < ymin) ymin = y;
|
|
4127
|
+
if (y > ymax) ymax = y;
|
|
4128
|
+
}
|
|
4129
|
+
return [xmin, ymin, xmax, ymax];
|
|
4130
|
+
}
|
|
4131
|
+
|
|
4132
|
+
function getShapeHitTest(displayLayer, ext, interactionMode) {
|
|
4137
4133
|
var geoType = displayLayer.layer.geometry_type;
|
|
4138
4134
|
var test;
|
|
4139
4135
|
if (geoType == 'point' && displayLayer.style.type == 'styled') {
|
|
4140
4136
|
test = getGraduatedCircleTest(getRadiusFunction(displayLayer.style));
|
|
4141
4137
|
} else if (geoType == 'point') {
|
|
4142
4138
|
test = pointTest;
|
|
4139
|
+
} else if (interactionMode == 'vertices') {
|
|
4140
|
+
test = vertexTest;
|
|
4143
4141
|
} else if (geoType == 'polyline') {
|
|
4144
4142
|
test = polylineTest;
|
|
4145
4143
|
} else if (geoType == 'polygon') {
|
|
@@ -4165,14 +4163,14 @@
|
|
|
4165
4163
|
}
|
|
4166
4164
|
|
|
4167
4165
|
function polygonTest(x, y) {
|
|
4168
|
-
var maxDist = getZoomAdjustedHitBuffer(
|
|
4166
|
+
var maxDist = getZoomAdjustedHitBuffer(10, 1),
|
|
4169
4167
|
cands = findHitCandidates(x, y, maxDist),
|
|
4170
4168
|
hits = [],
|
|
4171
4169
|
cand, hitId;
|
|
4172
4170
|
for (var i=0; i<cands.length; i++) {
|
|
4173
4171
|
cand = cands[i];
|
|
4174
4172
|
if (geom.testPointInPolygon(x, y, cand.shape, displayLayer.arcs)) {
|
|
4175
|
-
hits.push(cand
|
|
4173
|
+
hits.push(cand);
|
|
4176
4174
|
}
|
|
4177
4175
|
}
|
|
4178
4176
|
if (cands.length > 0 && hits.length === 0) {
|
|
@@ -4180,7 +4178,9 @@
|
|
|
4180
4178
|
sortByDistance(x, y, cands, displayLayer.arcs);
|
|
4181
4179
|
hits = pickNearestCandidates(cands, 0, maxDist);
|
|
4182
4180
|
}
|
|
4183
|
-
return
|
|
4181
|
+
return {
|
|
4182
|
+
ids: utils.pluck(hits, 'id')
|
|
4183
|
+
};
|
|
4184
4184
|
}
|
|
4185
4185
|
|
|
4186
4186
|
function pickNearestCandidates(sorted, bufDist, maxDist) {
|
|
@@ -4195,22 +4195,41 @@
|
|
|
4195
4195
|
} else if (cand.dist - minDist > bufDist) {
|
|
4196
4196
|
break;
|
|
4197
4197
|
}
|
|
4198
|
-
hits.push(cand
|
|
4198
|
+
hits.push(cand);
|
|
4199
4199
|
}
|
|
4200
4200
|
return hits;
|
|
4201
4201
|
}
|
|
4202
4202
|
|
|
4203
|
+
function vertexTest(x, y) {
|
|
4204
|
+
var maxDist = getZoomAdjustedHitBuffer(15, 2),
|
|
4205
|
+
bufDist = getZoomAdjustedHitBuffer(0.05), // tiny threshold for hitting almost-identical lines
|
|
4206
|
+
cands = findHitCandidates(x, y, maxDist);
|
|
4207
|
+
sortByDistance(x, y, cands, displayLayer.arcs);
|
|
4208
|
+
cands = pickNearestCandidates(cands, bufDist, maxDist);
|
|
4209
|
+
var arcs = cands.map(function(cand) { return absArcId(cand.info.arcId); });
|
|
4210
|
+
return {
|
|
4211
|
+
arcs: utils.uniq(arcs),
|
|
4212
|
+
ids: utils.pluck(cands, 'id')
|
|
4213
|
+
};
|
|
4214
|
+
}
|
|
4215
|
+
|
|
4203
4216
|
function polylineTest(x, y) {
|
|
4204
4217
|
var maxDist = getZoomAdjustedHitBuffer(15, 2),
|
|
4205
4218
|
bufDist = getZoomAdjustedHitBuffer(0.05), // tiny threshold for hitting almost-identical lines
|
|
4206
4219
|
cands = findHitCandidates(x, y, maxDist);
|
|
4207
4220
|
sortByDistance(x, y, cands, displayLayer.arcs);
|
|
4208
|
-
|
|
4221
|
+
cands = pickNearestCandidates(cands, bufDist, maxDist);
|
|
4222
|
+
return {
|
|
4223
|
+
ids: utils.pluck(cands, 'id')
|
|
4224
|
+
};
|
|
4209
4225
|
}
|
|
4210
4226
|
|
|
4211
4227
|
function sortByDistance(x, y, cands, arcs) {
|
|
4228
|
+
var cand;
|
|
4212
4229
|
for (var i=0; i<cands.length; i++) {
|
|
4213
|
-
|
|
4230
|
+
cand = cands[i];
|
|
4231
|
+
cand.info = geom.getPointToShapeInfo(x, y, cands[i].shape, arcs);
|
|
4232
|
+
cand.dist = cand.info.distance;
|
|
4214
4233
|
}
|
|
4215
4234
|
utils.sortOn(cands, 'dist');
|
|
4216
4235
|
}
|
|
@@ -4238,7 +4257,9 @@
|
|
|
4238
4257
|
}
|
|
4239
4258
|
});
|
|
4240
4259
|
// console.log(hitThreshold, bullseye);
|
|
4241
|
-
return
|
|
4260
|
+
return {
|
|
4261
|
+
ids: utils.uniq(hits) // multipoint features can register multiple hits
|
|
4262
|
+
};
|
|
4242
4263
|
}
|
|
4243
4264
|
|
|
4244
4265
|
function getRadiusFunction(style) {
|
|
@@ -4292,7 +4313,9 @@
|
|
|
4292
4313
|
hits.push(id);
|
|
4293
4314
|
}
|
|
4294
4315
|
});
|
|
4295
|
-
return
|
|
4316
|
+
return {
|
|
4317
|
+
ids: hits
|
|
4318
|
+
};
|
|
4296
4319
|
};
|
|
4297
4320
|
}
|
|
4298
4321
|
|
|
@@ -4429,20 +4452,18 @@
|
|
|
4429
4452
|
|
|
4430
4453
|
}
|
|
4431
4454
|
|
|
4432
|
-
function getPointerHitTest(mapLayer, ext) {
|
|
4455
|
+
function getPointerHitTest(mapLayer, ext, interactionMode) {
|
|
4433
4456
|
var shapeTest, svgTest, targetLayer;
|
|
4434
4457
|
if (!mapLayer || !internal.layerHasGeometry(mapLayer.layer)) {
|
|
4435
|
-
return
|
|
4458
|
+
return function() {return {ids: []};};
|
|
4436
4459
|
}
|
|
4437
|
-
shapeTest = getShapeHitTest(mapLayer, ext);
|
|
4460
|
+
shapeTest = getShapeHitTest(mapLayer, ext, interactionMode);
|
|
4438
4461
|
svgTest = getSvgHitTest(mapLayer);
|
|
4439
4462
|
|
|
4440
4463
|
// e: pointer event
|
|
4441
4464
|
return function(e) {
|
|
4442
4465
|
var p = ext.translatePixelCoords(e.x, e.y);
|
|
4443
|
-
var data = {
|
|
4444
|
-
ids: shapeTest(p[0], p[1]) || []
|
|
4445
|
-
};
|
|
4466
|
+
var data = shapeTest(p[0], p[1]) || {ids:[]};
|
|
4446
4467
|
var svgData = svgTest(e); // null or a data object
|
|
4447
4468
|
if (svgData) { // mouse is over an SVG symbol
|
|
4448
4469
|
utils.extend(data, svgData);
|
|
@@ -4502,22 +4523,25 @@
|
|
|
4502
4523
|
}, !!'capture'); // preempt the layer control's arrow key handler
|
|
4503
4524
|
|
|
4504
4525
|
self.setLayer = function(mapLayer) {
|
|
4505
|
-
hitTest = getPointerHitTest(mapLayer, ext);
|
|
4506
|
-
if (!hitTest) {
|
|
4507
|
-
hitTest = function() {return {ids: []};};
|
|
4508
|
-
}
|
|
4509
4526
|
targetLayer = mapLayer;
|
|
4527
|
+
updateHitTest();
|
|
4510
4528
|
};
|
|
4511
4529
|
|
|
4530
|
+
function updateHitTest() {
|
|
4531
|
+
hitTest = getPointerHitTest(targetLayer, ext, interactionMode);
|
|
4532
|
+
}
|
|
4533
|
+
|
|
4512
4534
|
function turnOn(mode) {
|
|
4513
4535
|
interactionMode = mode;
|
|
4514
4536
|
active = true;
|
|
4537
|
+
updateHitTest();
|
|
4515
4538
|
}
|
|
4516
4539
|
|
|
4517
4540
|
function turnOff() {
|
|
4518
4541
|
if (active) {
|
|
4519
4542
|
updateSelectionState(null); // no hit data, no event
|
|
4520
4543
|
active = false;
|
|
4544
|
+
hitTest = null;
|
|
4521
4545
|
}
|
|
4522
4546
|
}
|
|
4523
4547
|
|
|
@@ -4561,6 +4585,20 @@
|
|
|
4561
4585
|
}
|
|
4562
4586
|
};
|
|
4563
4587
|
|
|
4588
|
+
self.setHoverVertex = function(p) {
|
|
4589
|
+
var p2 = storedData.hit_coordinates;
|
|
4590
|
+
if (!active || !p) return;
|
|
4591
|
+
if (p2 && p2[0] == p[0] && p2[1] == p[1]) return;
|
|
4592
|
+
storedData.hit_coordinates = p;
|
|
4593
|
+
triggerHitEvent('change');
|
|
4594
|
+
};
|
|
4595
|
+
|
|
4596
|
+
self.clearVertexOverlay = function() {
|
|
4597
|
+
if (!storedData.hit_coordinates) return;
|
|
4598
|
+
delete storedData.hit_coordinates;
|
|
4599
|
+
triggerHitEvent('change');
|
|
4600
|
+
};
|
|
4601
|
+
|
|
4564
4602
|
self.clearSelection = function() {
|
|
4565
4603
|
updateSelectionState(null);
|
|
4566
4604
|
};
|
|
@@ -5901,50 +5939,108 @@
|
|
|
5901
5939
|
rec[key] = isString ? String(newVal) : newVal;
|
|
5902
5940
|
}
|
|
5903
5941
|
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
var
|
|
5908
|
-
return ext.translateCoords(coords[0], coords[1]);
|
|
5909
|
-
}
|
|
5942
|
+
function initLabelDragging(gui, ext, hit) {
|
|
5943
|
+
var downEvt;
|
|
5944
|
+
var activeId;
|
|
5945
|
+
var activeRecord;
|
|
5910
5946
|
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
if (!coords || coords.length != 1) {
|
|
5914
|
-
return null;
|
|
5947
|
+
function active(e) {
|
|
5948
|
+
return e.id > -1 && gui.interaction.getMode() == 'labels';
|
|
5915
5949
|
}
|
|
5916
|
-
return coords[0];
|
|
5917
|
-
}
|
|
5918
5950
|
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5951
|
+
hit.on('dragstart', function(e) {
|
|
5952
|
+
if (!active(e)) return;
|
|
5953
|
+
var textNode = getTextTarget3(e);
|
|
5954
|
+
var table = hit.getTargetDataTable();
|
|
5955
|
+
if (!textNode || !table) return false;
|
|
5956
|
+
activeId = e.id;
|
|
5957
|
+
activeRecord = getLabelRecordById(activeId);
|
|
5958
|
+
downEvt = e;
|
|
5959
|
+
gui.dispatchEvent('label_dragstart', {FID: activeId});
|
|
5960
|
+
});
|
|
5924
5961
|
|
|
5962
|
+
hit.on('drag', function(e) {
|
|
5963
|
+
if (!active(e)) return;
|
|
5964
|
+
if (e.id != activeId) {
|
|
5965
|
+
error("Mismatched hit ids:", e.id, activeId);
|
|
5966
|
+
}
|
|
5967
|
+
var scale = ext.getSymbolScale() || 1;
|
|
5968
|
+
var textNode;
|
|
5969
|
+
applyDelta(activeRecord, 'dx', e.dx / scale);
|
|
5970
|
+
applyDelta(activeRecord, 'dy', e.dy / scale);
|
|
5971
|
+
textNode = getTextTarget3(e);
|
|
5972
|
+
if (!isMultilineLabel(textNode)) {
|
|
5973
|
+
// update anchor position of single-line labels based on label position
|
|
5974
|
+
// relative to anchor point, for better placement when eventual display font is
|
|
5975
|
+
// different from mapshaper's font.
|
|
5976
|
+
autoUpdateTextAnchor(textNode, activeRecord, getDisplayCoordsById(activeId, hit.getHitTarget().layer, ext));
|
|
5977
|
+
}
|
|
5978
|
+
// updateSymbol(targetTextNode, activeRecord);
|
|
5979
|
+
updateSymbol2(textNode, activeRecord, activeId);
|
|
5980
|
+
});
|
|
5925
5981
|
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5982
|
+
hit.on('dragend', function(e) {
|
|
5983
|
+
if (!active(e)) return;
|
|
5984
|
+
gui.dispatchEvent('label_dragend', {FID: e.id});
|
|
5985
|
+
activeId = -1;
|
|
5986
|
+
activeRecord = null;
|
|
5987
|
+
downEvt = null;
|
|
5988
|
+
});
|
|
5933
5989
|
|
|
5934
|
-
|
|
5990
|
+
function getDisplayCoordsById(id, layer, ext) {
|
|
5991
|
+
var coords = getPointCoordsById(id, layer);
|
|
5992
|
+
return ext.translateCoords(coords[0], coords[1]);
|
|
5993
|
+
}
|
|
5935
5994
|
|
|
5936
|
-
|
|
5995
|
+
function getPointCoordsById(id, layer) {
|
|
5996
|
+
var coords = layer && layer.geometry_type == 'point' && layer.shapes[id];
|
|
5997
|
+
if (!coords || coords.length != 1) {
|
|
5998
|
+
return null;
|
|
5999
|
+
}
|
|
6000
|
+
return coords[0];
|
|
6001
|
+
}
|
|
5937
6002
|
|
|
5938
|
-
function
|
|
5939
|
-
|
|
6003
|
+
function getTextTarget3(e) {
|
|
6004
|
+
if (e.id > -1 === false || !e.container) return null;
|
|
6005
|
+
return getSymbolNodeById(e.id, e.container);
|
|
5940
6006
|
}
|
|
5941
6007
|
|
|
5942
|
-
function
|
|
5943
|
-
|
|
6008
|
+
function getSymbolNodeById(id, parent) {
|
|
6009
|
+
// TODO: optimize selector
|
|
6010
|
+
var sel = '[data-id="' + id + '"]';
|
|
6011
|
+
return parent.querySelector(sel);
|
|
6012
|
+
}
|
|
6013
|
+
|
|
6014
|
+
function getTextTarget2(e) {
|
|
6015
|
+
var el = e && e.targetSymbol || null;
|
|
6016
|
+
if (el && el.tagName == 'tspan') {
|
|
6017
|
+
el = el.parentNode;
|
|
6018
|
+
}
|
|
6019
|
+
return el && el.tagName == 'text' ? el : null;
|
|
5944
6020
|
}
|
|
5945
6021
|
|
|
5946
|
-
function
|
|
5947
|
-
|
|
6022
|
+
function getTextTarget(e) {
|
|
6023
|
+
var el = e.target;
|
|
6024
|
+
if (el.tagName == 'tspan') {
|
|
6025
|
+
el = el.parentNode;
|
|
6026
|
+
}
|
|
6027
|
+
return el.tagName == 'text' ? el : null;
|
|
6028
|
+
}
|
|
6029
|
+
|
|
6030
|
+
function getLabelRecordById(id) {
|
|
6031
|
+
var table = hit.getTargetDataTable();
|
|
6032
|
+
if (id >= 0 === false || !table) return null;
|
|
6033
|
+
// add dx and dy properties, if not available
|
|
6034
|
+
if (!table.fieldExists('dx')) {
|
|
6035
|
+
table.addField('dx', 0);
|
|
6036
|
+
}
|
|
6037
|
+
if (!table.fieldExists('dy')) {
|
|
6038
|
+
table.addField('dy', 0);
|
|
6039
|
+
}
|
|
6040
|
+
if (!table.fieldExists('text-anchor')) {
|
|
6041
|
+
table.addField('text-anchor', '');
|
|
6042
|
+
}
|
|
6043
|
+
return table.getRecordAt(id);
|
|
5948
6044
|
}
|
|
5949
6045
|
|
|
5950
6046
|
// update symbol by setting attributes
|
|
@@ -5971,249 +6067,167 @@
|
|
|
5971
6067
|
gui.dispatchEvent('popup-needs-refresh');
|
|
5972
6068
|
return node2;
|
|
5973
6069
|
}
|
|
6070
|
+
}
|
|
5974
6071
|
|
|
5975
|
-
|
|
5976
|
-
var downEvt;
|
|
5977
|
-
var eventPriority = 1;
|
|
5978
|
-
|
|
5979
|
-
// inspector and label editing aren't fully synced - stop editing if inspector opens
|
|
5980
|
-
// gui.on('inspector_on', function() {
|
|
5981
|
-
// stopEditing();
|
|
5982
|
-
// });
|
|
5983
|
-
|
|
5984
|
-
gui.on('interaction_mode_change', function(e) {
|
|
5985
|
-
if (e.mode != 'labels') {
|
|
5986
|
-
stopDragging();
|
|
5987
|
-
}
|
|
5988
|
-
gui.undo.clear(); // TODO: put this elsewhere?
|
|
5989
|
-
});
|
|
5990
|
-
|
|
5991
|
-
// down event on svg
|
|
5992
|
-
// a: off text
|
|
5993
|
-
// -> stop editing
|
|
5994
|
-
// b: on text
|
|
5995
|
-
// 1: not editing -> nop
|
|
5996
|
-
// 2: on selected text -> start dragging
|
|
5997
|
-
// 3: on other text -> stop dragging, select new text
|
|
5998
|
-
|
|
5999
|
-
hit.on('dragstart', function(e) {
|
|
6000
|
-
if (e.id >= 0 === false) return;
|
|
6001
|
-
if (labelEditingEnabled() && onLabelDragStart(e)) {
|
|
6002
|
-
triggerGlobalEvent('label_dragstart', e);
|
|
6003
|
-
startDragging();
|
|
6004
|
-
} else if (locationEditingEnabled()) {
|
|
6005
|
-
triggerGlobalEvent('symbol_dragstart', e);
|
|
6006
|
-
startDragging();
|
|
6007
|
-
} else if (vertexEditingEnabled()) {
|
|
6008
|
-
onVertexDragStart(e);
|
|
6009
|
-
triggerGlobalEvent('vertex_dragstart', e);
|
|
6010
|
-
startDragging();
|
|
6011
|
-
}
|
|
6012
|
-
});
|
|
6013
|
-
|
|
6014
|
-
hit.on('drag', function(e) {
|
|
6015
|
-
if (labelEditingEnabled()) {
|
|
6016
|
-
onLabelDrag(e);
|
|
6017
|
-
} else if (locationEditingEnabled()) {
|
|
6018
|
-
onLocationDrag(e);
|
|
6019
|
-
} else if (vertexEditingEnabled()) {
|
|
6020
|
-
onVertexDrag(e);
|
|
6021
|
-
}
|
|
6022
|
-
});
|
|
6023
|
-
|
|
6024
|
-
hit.on('dragend', function(e) {
|
|
6025
|
-
if (locationEditingEnabled()) {
|
|
6026
|
-
triggerGlobalEvent('symbol_dragend', e);
|
|
6027
|
-
stopDragging();
|
|
6028
|
-
} else if (labelEditingEnabled()) {
|
|
6029
|
-
triggerGlobalEvent('label_dragend', e);
|
|
6030
|
-
stopDragging();
|
|
6031
|
-
} else if (vertexEditingEnabled()) {
|
|
6032
|
-
// kludge to get dataset to recalculate internal bounding boxes
|
|
6033
|
-
hit.getHitTarget().arcs.transformPoints(function() {});
|
|
6034
|
-
triggerGlobalEvent('vertex_dragend', e);
|
|
6035
|
-
stopDragging();
|
|
6036
|
-
}
|
|
6037
|
-
});
|
|
6038
|
-
|
|
6039
|
-
hit.on('click', function(e) {
|
|
6040
|
-
if (labelEditingEnabled()) {
|
|
6041
|
-
var target = hit.getHitTarget();
|
|
6042
|
-
onLabelClick(e);
|
|
6043
|
-
}
|
|
6044
|
-
});
|
|
6072
|
+
function initPointDragging(gui, ext, hit) {
|
|
6045
6073
|
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
onVertexHover(e);
|
|
6050
|
-
}
|
|
6051
|
-
}, null, 100);
|
|
6074
|
+
function active(e) {
|
|
6075
|
+
return e.id > -1 && gui.interaction.getMode() == 'location';
|
|
6076
|
+
}
|
|
6052
6077
|
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
var p = ext.translatePixelCoords(e.x, e.y);
|
|
6058
|
-
var o = internal.findInsertionPoint(p, shp, target.arcs, ext.getPixelSize());
|
|
6059
|
-
}
|
|
6078
|
+
hit.on('dragstart', function(e) {
|
|
6079
|
+
if (!active(e)) return;
|
|
6080
|
+
gui.dispatchEvent('symbol_dragstart', {FID: e.id});
|
|
6081
|
+
});
|
|
6060
6082
|
|
|
6083
|
+
hit.on('drag', function(e) {
|
|
6084
|
+
if (!active(e)) return;
|
|
6085
|
+
var lyr = hit.getHitTarget().layer;
|
|
6086
|
+
var p = getPointCoordsById(e.id, lyr);
|
|
6087
|
+
if (!p) return;
|
|
6088
|
+
var diff = translateDeltaDisplayCoords(e.dx, e.dy, ext);
|
|
6089
|
+
p[0] += diff[0];
|
|
6090
|
+
p[1] += diff[1];
|
|
6091
|
+
gui.dispatchEvent('map-needs-refresh');
|
|
6092
|
+
gui.dispatchEvent('symbol_drag', {FID: e.id});
|
|
6093
|
+
});
|
|
6061
6094
|
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
};
|
|
6067
|
-
}
|
|
6095
|
+
hit.on('dragend', function(e) {
|
|
6096
|
+
if (!active(e)) return;
|
|
6097
|
+
gui.dispatchEvent('symbol_dragend', {FID: e.id});
|
|
6098
|
+
});
|
|
6068
6099
|
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
p[0] += diff[0];
|
|
6075
|
-
p[1] += diff[1];
|
|
6076
|
-
triggerRedraw();
|
|
6077
|
-
triggerGlobalEvent('symbol_drag', e);
|
|
6078
|
-
}
|
|
6100
|
+
function translateDeltaDisplayCoords(dx, dy, ext) {
|
|
6101
|
+
var a = ext.translatePixelCoords(0, 0);
|
|
6102
|
+
var b = ext.translatePixelCoords(dx, dy);
|
|
6103
|
+
return [b[0] - a[0], b[1] - a[1]];
|
|
6104
|
+
}
|
|
6079
6105
|
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
activeVertexIds = internal.findNearestVertices(p, shp, target.arcs);
|
|
6085
|
-
activeId = e.id;
|
|
6106
|
+
function getPointCoordsById(id, layer) {
|
|
6107
|
+
var coords = layer && layer.geometry_type == 'point' && layer.shapes[id];
|
|
6108
|
+
if (!coords || coords.length != 1) {
|
|
6109
|
+
return null;
|
|
6086
6110
|
}
|
|
6111
|
+
return coords[0];
|
|
6112
|
+
}
|
|
6113
|
+
}
|
|
6087
6114
|
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
if (gui.keyboard.shiftIsPressed()) {
|
|
6093
|
-
internal.snapPointToArcEndpoint(p, activeVertexIds, target.arcs);
|
|
6094
|
-
}
|
|
6095
|
-
snapVerticesToPoint(activeVertexIds, p, target.arcs);
|
|
6096
|
-
triggerRedraw();
|
|
6097
|
-
}
|
|
6115
|
+
function initVertexDragging(gui, ext, hit) {
|
|
6116
|
+
var activeShapeId = -1;
|
|
6117
|
+
var draggedVertexIds = null;
|
|
6118
|
+
var selectedVertexIds = null;
|
|
6098
6119
|
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
if (textNode && rec && isMultilineLabel(textNode)) {
|
|
6103
|
-
toggleTextAlign(textNode, rec);
|
|
6104
|
-
updateSymbol2(textNode, rec, e.id);
|
|
6105
|
-
// e.stopPropagation(); // prevent pin/unpin on popup
|
|
6106
|
-
}
|
|
6107
|
-
}
|
|
6120
|
+
function active(e) {
|
|
6121
|
+
return e.id > -1 && gui.interaction.getMode() == 'vertices';
|
|
6122
|
+
}
|
|
6108
6123
|
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6124
|
+
function fire(type) {
|
|
6125
|
+
gui.dispatchEvent(type, {
|
|
6126
|
+
FID: activeShapeId,
|
|
6127
|
+
vertex_ids: draggedVertexIds
|
|
6128
|
+
});
|
|
6129
|
+
}
|
|
6112
6130
|
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
layer_name: hit.getHitTarget().layer.name,
|
|
6118
|
-
vertex_ids: activeVertexIds
|
|
6119
|
-
};
|
|
6120
|
-
// fire event to signal external editor that symbol coords have changed
|
|
6121
|
-
gui.dispatchEvent(type, o);
|
|
6122
|
-
}
|
|
6131
|
+
function setHoverVertex(id) {
|
|
6132
|
+
var target = hit.getHitTarget();
|
|
6133
|
+
hit.setHoverVertex(target.arcs.getVertex2(id));
|
|
6134
|
+
}
|
|
6123
6135
|
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
if (!table.fieldExists('dx')) {
|
|
6129
|
-
table.addField('dx', 0);
|
|
6130
|
-
}
|
|
6131
|
-
if (!table.fieldExists('dy')) {
|
|
6132
|
-
table.addField('dy', 0);
|
|
6133
|
-
}
|
|
6134
|
-
if (!table.fieldExists('text-anchor')) {
|
|
6135
|
-
table.addField('text-anchor', '');
|
|
6136
|
-
}
|
|
6137
|
-
return table.getRecordAt(id);
|
|
6138
|
-
}
|
|
6136
|
+
function clearHoverVertex() {
|
|
6137
|
+
hit.clearVertexOverlay();
|
|
6138
|
+
// gui.state.vertex_overlay = null;
|
|
6139
|
+
}
|
|
6139
6140
|
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6141
|
+
function findDraggableVertices(e) {
|
|
6142
|
+
var target = hit.getHitTarget();
|
|
6143
|
+
var shp = target.layer.shapes[e.id];
|
|
6144
|
+
var p = ext.translatePixelCoords(e.x, e.y);
|
|
6145
|
+
var nearestIds = internal.findNearestVertices(p, shp, target.arcs);
|
|
6146
|
+
var p2 = target.arcs.getVertex2(nearestIds[0]);
|
|
6147
|
+
var dist = geom.distance2D(p[0], p[1], p2[0], p2[1]);
|
|
6148
|
+
var pixelDist = dist / ext.getPixelSize();
|
|
6149
|
+
if (pixelDist > 5) {
|
|
6150
|
+
draggedVertexIds = null;
|
|
6151
|
+
return null;
|
|
6148
6152
|
}
|
|
6153
|
+
return nearestIds;
|
|
6154
|
+
}
|
|
6149
6155
|
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
applyDelta(activeRecord, 'dy', e.dy / scale);
|
|
6159
|
-
textNode = getTextTarget3(e);
|
|
6160
|
-
if (!isMultilineLabel(textNode)) {
|
|
6161
|
-
// update anchor position of single-line labels based on label position
|
|
6162
|
-
// relative to anchor point, for better placement when eventual display font is
|
|
6163
|
-
// different from mapshaper's font.
|
|
6164
|
-
autoUpdateTextAnchor(textNode, activeRecord, getDisplayCoordsById(activeId, hit.getHitTarget().layer, ext));
|
|
6165
|
-
}
|
|
6166
|
-
// updateSymbol(targetTextNode, activeRecord);
|
|
6167
|
-
updateSymbol2(textNode, activeRecord, activeId);
|
|
6168
|
-
}
|
|
6156
|
+
hit.on('dragstart', function(e) {
|
|
6157
|
+
if (!active(e)) return;
|
|
6158
|
+
draggedVertexIds = findDraggableVertices(e);
|
|
6159
|
+
if (!draggedVertexIds) return;
|
|
6160
|
+
setHoverVertex(draggedVertexIds[0]);
|
|
6161
|
+
activeShapeId = e.id;
|
|
6162
|
+
fire('vertex_dragstart');
|
|
6163
|
+
});
|
|
6169
6164
|
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6165
|
+
hit.on('drag', function(e) {
|
|
6166
|
+
if (!active(e) || !draggedVertexIds) return;
|
|
6167
|
+
var target = hit.getHitTarget();
|
|
6168
|
+
var p = ext.translatePixelCoords(e.x, e.y);
|
|
6169
|
+
if (gui.keyboard.shiftIsPressed()) {
|
|
6170
|
+
internal.snapPointToArcEndpoint(p, draggedVertexIds, target.arcs);
|
|
6174
6171
|
}
|
|
6172
|
+
internal.snapVerticesToPoint(draggedVertexIds, p, target.arcs);
|
|
6173
|
+
setHoverVertex(draggedVertexIds[0]);
|
|
6174
|
+
// redrawing the whole map updates the data layer as well as the overlay layer
|
|
6175
|
+
// gui.dispatchEvent('map-needs-refresh');
|
|
6176
|
+
});
|
|
6175
6177
|
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
}
|
|
6178
|
+
hit.on('dragend', function(e) {
|
|
6179
|
+
if (!active(e) || !draggedVertexIds) return;
|
|
6180
|
+
// kludge to get dataset to recalculate internal bounding boxes
|
|
6181
|
+
hit.getHitTarget().arcs.transformPoints(function() {});
|
|
6182
|
+
clearHoverVertex();
|
|
6183
|
+
fire('vertex_dragend');
|
|
6184
|
+
draggedVertexIds = null;
|
|
6185
|
+
activeShapeId = -1;
|
|
6186
|
+
// redraw data layer
|
|
6187
|
+
gui.dispatchEvent('map-needs-refresh');
|
|
6188
|
+
});
|
|
6180
6189
|
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
}
|
|
6190
|
+
// select clicked vertices
|
|
6191
|
+
hit.on('click', function(e) {
|
|
6192
|
+
if (!active(e)) return;
|
|
6193
|
+
var vertices = findDraggableVertices(e); // same selection criteria as for dragging
|
|
6194
|
+
// TODO
|
|
6195
|
+
});
|
|
6188
6196
|
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6197
|
+
// highlight hit vertex in path edit mode
|
|
6198
|
+
hit.on('hover', function(e) {
|
|
6199
|
+
if (!active(e) || draggedVertexIds) return; // no hover effect while dragging
|
|
6200
|
+
var vertexIds = findDraggableVertices(e);
|
|
6201
|
+
if (vertexIds) {
|
|
6202
|
+
setHoverVertex(vertexIds[0]);
|
|
6203
|
+
return;
|
|
6195
6204
|
}
|
|
6196
|
-
|
|
6205
|
+
var target = hit.getHitTarget();
|
|
6206
|
+
var shp = target.layer.shapes[e.id];
|
|
6207
|
+
var p = ext.translatePixelCoords(e.x, e.y);
|
|
6208
|
+
var o = internal.findInsertionPoint(p, shp, target.arcs, ext.getPixelSize());
|
|
6209
|
+
console.log('*', o, p);
|
|
6210
|
+
clearHoverVertex();
|
|
6211
|
+
}, null, 100);
|
|
6197
6212
|
|
|
6198
|
-
|
|
6199
|
-
dragging = true;
|
|
6200
|
-
}
|
|
6213
|
+
}
|
|
6201
6214
|
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
activeVertexIds = null;
|
|
6207
|
-
}
|
|
6215
|
+
function initInteractiveEditing(gui, ext, hit) {
|
|
6216
|
+
initLabelDragging(gui, ext, hit);
|
|
6217
|
+
initPointDragging(gui, ext, hit);
|
|
6218
|
+
initVertexDragging(gui, ext, hit);
|
|
6208
6219
|
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
var dy = up.screenY - down.screenY;
|
|
6213
|
-
var dist = Math.sqrt(dx * dx + dy * dy);
|
|
6214
|
-
return dist <= 4 && elapsed < 300;
|
|
6215
|
-
}
|
|
6220
|
+
gui.on('interaction_mode_change', function(e) {
|
|
6221
|
+
gui.undo.clear(); // TODO: put this elsewhere?
|
|
6222
|
+
});
|
|
6216
6223
|
|
|
6224
|
+
// function isClickEvent(up, down) {
|
|
6225
|
+
// var elapsed = Math.abs(down.timeStamp - up.timeStamp);
|
|
6226
|
+
// var dx = up.screenX - down.screenX;
|
|
6227
|
+
// var dy = up.screenY - down.screenY;
|
|
6228
|
+
// var dist = Math.sqrt(dx * dx + dy * dy);
|
|
6229
|
+
// return dist <= 4 && elapsed < 300;
|
|
6230
|
+
// }
|
|
6217
6231
|
}
|
|
6218
6232
|
|
|
6219
6233
|
var darkStroke = "#334",
|
|
@@ -6384,8 +6398,14 @@
|
|
|
6384
6398
|
var geomType = lyr.geometry_type;
|
|
6385
6399
|
var topId = o.id; // pinned id (if pinned) or hover id
|
|
6386
6400
|
var topIdx = -1;
|
|
6387
|
-
var styler = function(
|
|
6388
|
-
utils.extend(
|
|
6401
|
+
var styler = function(style, i) {
|
|
6402
|
+
utils.extend(style, i === topIdx ? topStyle: baseStyle);
|
|
6403
|
+
// kludge to show vertices when editing path shapes
|
|
6404
|
+
if (o.mode == 'vertices') {
|
|
6405
|
+
style.vertices = true;
|
|
6406
|
+
style.vertex_overlay = o.hit_coordinates || null;
|
|
6407
|
+
style.fillColor = null;
|
|
6408
|
+
}
|
|
6389
6409
|
};
|
|
6390
6410
|
var baseStyle = getDefaultStyle(lyr, selectionStyles[geomType]);
|
|
6391
6411
|
var topStyle;
|
|
@@ -6856,19 +6876,25 @@
|
|
|
6856
6876
|
_self.drawVertices = function(shapes, arcs, style, filter) {
|
|
6857
6877
|
var iter = new internal.ShapeIter(arcs);
|
|
6858
6878
|
var t = getScaledTransform(_ext);
|
|
6859
|
-
var radius = (style.strokeWidth * 0.9
|
|
6879
|
+
var radius = (style.strokeWidth > 2 ? style.strokeWidth * 0.9 : 2) * GUI.getPixelRatio() * getScaledLineScale(_ext);
|
|
6860
6880
|
var color = style.strokeColor || 'black';
|
|
6861
|
-
var
|
|
6881
|
+
var radius2 = radius * 2;
|
|
6862
6882
|
_ctx.beginPath();
|
|
6863
6883
|
_ctx.fillStyle = color;
|
|
6864
6884
|
for (var i=0; i<shapes.length; i++) {
|
|
6865
|
-
shp = shapes[i];
|
|
6885
|
+
var shp = shapes[i];
|
|
6866
6886
|
if (!shp || filter && !filter(shp)) continue;
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6887
|
+
for (var j=0; j<shp.length; j++) {
|
|
6888
|
+
iter.init(shp[j]);
|
|
6889
|
+
while (iter.hasNext()) {
|
|
6890
|
+
drawCircle(iter.x * t.mx + t.bx, iter.y * t.my + t.by, radius, _ctx);
|
|
6891
|
+
}
|
|
6870
6892
|
}
|
|
6871
6893
|
}
|
|
6894
|
+
if (style.vertex_overlay) {
|
|
6895
|
+
var p = style.vertex_overlay;
|
|
6896
|
+
drawCircle(p[0] * t.mx + t.bx, p[1] * t.my + t.by, radius2, _ctx);
|
|
6897
|
+
}
|
|
6872
6898
|
_ctx.fill();
|
|
6873
6899
|
_ctx.closePath();
|
|
6874
6900
|
};
|
|
@@ -7921,7 +7947,7 @@
|
|
|
7921
7947
|
_visibleLayers = [], // cached visible map layers
|
|
7922
7948
|
_fullBounds = null,
|
|
7923
7949
|
_intersectionLyr, _activeLyr, _overlayLyr,
|
|
7924
|
-
_inspector, _stack,
|
|
7950
|
+
_inspector, _stack,
|
|
7925
7951
|
_dynamicCRS;
|
|
7926
7952
|
|
|
7927
7953
|
if (gui.options.showMouseCoordinates) {
|
|
@@ -8104,7 +8130,9 @@
|
|
|
8104
8130
|
});
|
|
8105
8131
|
}
|
|
8106
8132
|
|
|
8107
|
-
|
|
8133
|
+
if (gui.interaction) {
|
|
8134
|
+
initInteractiveEditing(gui, _ext, _hit);
|
|
8135
|
+
}
|
|
8108
8136
|
|
|
8109
8137
|
_ext.on('change', function(e) {
|
|
8110
8138
|
if (e.reset) return; // don't need to redraw map here if extent has been reset
|
|
@@ -8124,10 +8152,6 @@
|
|
|
8124
8152
|
function updateOverlayLayer(e) {
|
|
8125
8153
|
var style = getOverlayStyle(_activeLyr.layer, e);
|
|
8126
8154
|
if (style) {
|
|
8127
|
-
// kludge to show vertices when editing path shapes
|
|
8128
|
-
if (gui.state.interaction_mode == 'vertices') {
|
|
8129
|
-
style.vertices = true;
|
|
8130
|
-
}
|
|
8131
8155
|
_overlayLyr = utils.defaults({
|
|
8132
8156
|
layer: filterLayerByIds(_activeLyr.layer, style.ids),
|
|
8133
8157
|
style: style
|
|
@@ -8431,14 +8455,14 @@
|
|
|
8431
8455
|
startEditing();
|
|
8432
8456
|
});
|
|
8433
8457
|
|
|
8434
|
-
function
|
|
8458
|
+
function getManifest() {
|
|
8459
|
+
return window.mapshaper.manifest || {}; // kludge -- bin/mapshaper-gui sets this
|
|
8460
|
+
}
|
|
8461
|
+
|
|
8462
|
+
function getImportOpts(manifest) {
|
|
8435
8463
|
var vars = GUI.getUrlVars();
|
|
8436
8464
|
var opts = {};
|
|
8437
|
-
|
|
8438
|
-
if (Array.isArray(manifest)) {
|
|
8439
|
-
// old-style manifest: an array of filenames
|
|
8440
|
-
opts.files = manifest;
|
|
8441
|
-
} else if (manifest.files) {
|
|
8465
|
+
if (manifest.files) {
|
|
8442
8466
|
opts.files = manifest.files.concat();
|
|
8443
8467
|
} else {
|
|
8444
8468
|
opts.files = [];
|
|
@@ -8452,14 +8476,24 @@
|
|
|
8452
8476
|
opts.display_all = vars['display-all'] || vars.a || !!manifest.display_all;
|
|
8453
8477
|
opts.quick_view = vars['quick-view'] || vars.q || !!manifest.quick_view;
|
|
8454
8478
|
opts.target = vars.target || manifest.target || null;
|
|
8479
|
+
opts.name = vars.name || manifest.name || null;
|
|
8455
8480
|
return opts;
|
|
8456
8481
|
}
|
|
8457
8482
|
|
|
8483
|
+
function getInitialConsoleCommands() {
|
|
8484
|
+
return getManifest().commands || '';
|
|
8485
|
+
}
|
|
8486
|
+
|
|
8458
8487
|
var startEditing = function() {
|
|
8459
8488
|
var dataLoaded = false,
|
|
8460
|
-
|
|
8489
|
+
manifest = getManifest(),
|
|
8490
|
+
importOpts = getImportOpts(manifest),
|
|
8461
8491
|
gui = new GuiInstance('body');
|
|
8462
8492
|
|
|
8493
|
+
if (manifest.blurb) {
|
|
8494
|
+
El('#splash-screen-blurb').text(manifest.blurb);
|
|
8495
|
+
}
|
|
8496
|
+
|
|
8463
8497
|
new AlertControl(gui);
|
|
8464
8498
|
new RepairControl(gui);
|
|
8465
8499
|
new SimplifyControl(gui);
|
|
@@ -8496,6 +8530,8 @@
|
|
|
8496
8530
|
gui.map.setLayerPinning(o, true);
|
|
8497
8531
|
});
|
|
8498
8532
|
}
|
|
8533
|
+
gui.console.runInitialCommands(getInitialConsoleCommands());
|
|
8534
|
+
|
|
8499
8535
|
});
|
|
8500
8536
|
};
|
|
8501
8537
|
|