mapshaper 0.6.74 → 0.6.76

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
@@ -749,7 +749,7 @@
749
749
  }
750
750
 
751
751
  function getGenericComparator(asc) {
752
- asc = asc !== false;
752
+ asc = asc !== false && asc != 'descending'; // ascending is the default
753
753
  return function(a, b) {
754
754
  var retn = 0;
755
755
  if (b == null) {
@@ -1362,13 +1362,13 @@
1362
1362
  }
1363
1363
 
1364
1364
  // Format an array of (preferably short) strings in columns for console logging.
1365
- function formatStringsAsGrid(arr) {
1365
+ function formatStringsAsGrid(arr, width) {
1366
1366
  // TODO: variable column width
1367
1367
  var longest = arr.reduce(function(len, str) {
1368
1368
  return Math.max(len, str.length);
1369
1369
  }, 0),
1370
1370
  colWidth = longest + 2,
1371
- perLine = Math.floor(80 / colWidth) || 1;
1371
+ perLine = Math.floor((width || 80) / colWidth) || 1;
1372
1372
  return arr.reduce(function(memo, name, i) {
1373
1373
  var col = i % perLine;
1374
1374
  if (i > 0 && col === 0) memo += '\n';
@@ -3720,8 +3720,6 @@
3720
3720
  }
3721
3721
  var require$1 = f;
3722
3722
 
3723
- // import { createRequire } from "module";
3724
-
3725
3723
  var iconv = require$1('iconv-lite');
3726
3724
 
3727
3725
  // import iconv from 'iconv-lite';
@@ -4289,6 +4287,10 @@
4289
4287
  return layerHasPaths(lyr) || layerHasPoints(lyr);
4290
4288
  }
4291
4289
 
4290
+ function layerIsGeometric(lyr) {
4291
+ return !!lyr.geometry_type; // only checks type, includes empty layers
4292
+ }
4293
+
4292
4294
  function layerHasPaths(lyr) {
4293
4295
  return (lyr.geometry_type == 'polygon' || lyr.geometry_type == 'polyline') &&
4294
4296
  layerHasNonNullShapes(lyr);
@@ -4552,6 +4554,7 @@
4552
4554
  getLayerDataTable: getLayerDataTable,
4553
4555
  layerHasNonNullData: layerHasNonNullData,
4554
4556
  layerHasGeometry: layerHasGeometry,
4557
+ layerIsGeometric: layerIsGeometric,
4555
4558
  layerHasPaths: layerHasPaths,
4556
4559
  layerHasPoints: layerHasPoints,
4557
4560
  layerIsRectangle: layerIsRectangle,
@@ -5493,6 +5496,8 @@
5493
5496
  initLegacyArcs(arguments[0]); // want to phase this out
5494
5497
  } else if (arguments.length == 3) {
5495
5498
  initXYData.apply(this, arguments);
5499
+ } else if (arguments.length === 0) {
5500
+ initLegacyArcs([]); // empty collection
5496
5501
  } else {
5497
5502
  error("ArcCollection() Invalid arguments");
5498
5503
  }
@@ -13184,15 +13189,11 @@
13184
13189
  return findVertexIds(p2.x, p2.y, arcs);
13185
13190
  }
13186
13191
 
13187
-
13188
- function snapVerticesToPoint(ids, p, arcs, final) {
13192
+ function snapVerticesToPoint(ids, p, arcs) {
13189
13193
  var data = arcs.getVertexData();
13190
13194
  ids.forEach(function(idx) {
13191
- if (final) {
13192
- // recalculate bounding box for arc
13193
- arcs.updateArcBounds(findArcIdFromVertexId(idx, data.ii));
13194
- }
13195
13195
  setVertexCoords(p[0], p[1], idx, arcs);
13196
+ arcs.updateArcBounds(findArcIdFromVertexId(idx, data.ii));
13196
13197
  });
13197
13198
  }
13198
13199
 
@@ -19955,8 +19956,8 @@
19955
19956
  }
19956
19957
 
19957
19958
  function parseScalebarUnits(str) {
19958
- var isMiles = /miles?$/.test(str.toLowerCase());
19959
- var isKm = /(k\.m\.|km|kilometers?|kilometres?)$/.test(str.toLowerCase());
19959
+ var isMiles = /(miles?|mi[.]?|英里)$/.test(str.toLowerCase());
19960
+ var isKm = /(k\.m\.|km|kilometers?|kilom.tres?|公里)$/.test(str.toLowerCase());
19960
19961
  var units = isMiles && 'mile' || isKm && 'km' || '';
19961
19962
  return units;
19962
19963
  }
@@ -23046,28 +23047,30 @@ ${svg}
23046
23047
  // @samples Array of buffers containing sample text fields
23047
23048
  // TODO: Improve reliability and number of detectable encodings.
23048
23049
  function detectEncoding(samples) {
23049
- var encoding = null;
23050
- var utf8 = looksLikeUtf8(samples);
23051
- var win1252 = looksLikeWin1252(samples);
23052
- if (utf8 == 2 || utf8 > win1252) {
23053
- encoding = 'utf8';
23054
- } else if (win1252 > 0) {
23055
- encoding = 'win1252';
23056
- } else {
23057
- encoding = 'latin1'; // the original Shapefile encoding, using as an (imperfect) fallback
23058
- }
23059
-
23060
- return {
23061
- encoding: encoding,
23062
- confidence: Math.max(utf8, win1252)
23063
- };
23050
+ // score each encoding as 2 (high confidence) 1 (low confidence) or 0 (fail)
23051
+ var candidates = [{
23052
+ // latin1 is the original Shapefile encoding, using as an imperfect fallback
23053
+ // (sorts to the top only if all other encodings score 0)
23054
+ encoding: 'latin1',
23055
+ confidence: 0
23056
+ },{
23057
+ encoding: 'win1252',
23058
+ confidence: looksLikeWin1252(samples)
23059
+ }, {
23060
+ encoding: 'utf8',
23061
+ confidence: looksLikeUtf8(samples)
23062
+ }, {
23063
+ encoding: 'gb18030',
23064
+ confidence: looksLikeGB18030(samples)
23065
+ }];
23066
+ utils.sortOn(candidates, 'confidence', 'descending');
23067
+ return candidates[0];
23064
23068
  }
23065
23069
 
23066
- // Convert an array of text samples to a single string using a given encoding
23067
23070
  function decodeSamples(enc, samples) {
23068
23071
  return samples.map(function(buf) {
23069
23072
  return decodeString(buf, enc).trim();
23070
- }).join('\n');
23073
+ });
23071
23074
  }
23072
23075
 
23073
23076
  // Win1252 is the same as Latin1, except it replaces a block of control
@@ -23082,45 +23085,67 @@ ${svg}
23082
23085
  //
23083
23086
  function looksLikeWin1252(samples) {
23084
23087
  //common l.c. ascii chars
23085
- var ascii = 'abcdefghijklmnopqrstuvwxyz0123456789.()\'"?+-\n,:;/|_$% ',
23086
- // common extended + NBS (found in the wild)
23087
- extended = 'ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ°–±’‘' + '\xA0',
23088
- str = decodeSamples('win1252', samples),
23089
- asciiScore = getCharScore(str, ascii),
23090
- totalScore = getCharScore(str, extended + ascii);
23091
- return totalScore > 0.98 && asciiScore >= 0.8 && 2 ||
23092
- totalScore > 0.97 && asciiScore >= 0.6 && 1 || 0;
23088
+ var commonAscii = 'abcdefghijklmnopqrstuvwxyz0123456789.()\'"?+-\n,:;/|_$% ',
23089
+ // more common extended chars + NBS (found in the wild)
23090
+ moreChars = 'ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ°–±’‘' + '\xA0',
23091
+ str = decodeSamples('win1252', samples).join(''),
23092
+ commonAsciiPct = calcCharPct(str, commonAscii),
23093
+ expandedPct = calcCharPct(str, moreChars) + commonAsciiPct;
23094
+ var high = expandedPct > 0.98 && commonAsciiPct >= 0.8;
23095
+ var low = expandedPct > 0.97 && commonAsciiPct >= 0.6;
23096
+ return getScore(high, low);
23093
23097
  }
23094
23098
 
23095
- // Reject string if it contains the "replacement character" after decoding to UTF-8
23096
23099
  function looksLikeUtf8(samples) {
23097
- // Remove the byte sequence for the utf-8-encoded replacement char before decoding,
23098
- // in case the file is in utf-8, but contains some previously corrupted text.
23099
- // samples = samples.map(internal.replaceUtf8ReplacementChar);
23100
- var str = decodeSamples('utf8', samples);
23101
- var count = (str.match(/\ufffd/g) || []).length;
23102
- var score = 1 - count / str.length;
23103
- return score == 1 && 2 || score > 0.97 && 1 || 0;
23104
- }
23105
-
23106
- // function replaceUtf8ReplacementChar(buf) {
23107
- // var isCopy = false;
23108
- // for (var i=0, n=buf.length; i<n; i++) {
23109
- // // Check for UTF-8 encoded replacement char (0xEF 0xBF 0xBD)
23110
- // if (buf[i] == 0xef && i + 2 < n && buf[i+1] == 0xbf && buf[i+2] == 0xbd) {
23111
- // if (!isCopy) {
23112
- // buf = utils.createBuffer(buf);
23113
- // isCopy = true;
23114
- // }
23115
- // buf[i] = buf[i+1] = buf[i+2] = 63; // ascii question mark
23116
- // }
23117
- // }
23118
- // return buf;
23119
- // }
23100
+ // Reject string if it contains the "replacement character" after decoding
23101
+ // as utf-8
23102
+ var str = decodeSamples('utf8', samples).join('');
23103
+ var invalidPct = getInvalidPct(str);
23104
+ var high = invalidPct == 0;
23105
+ var low = invalidPct < 0.03;
23106
+ return getScore(high, low);
23107
+ }
23108
+
23109
+ function looksLikeGB18030(samples) {
23110
+ // from Jun Da's frequency table
23111
+ var commonHanZi = '的一了是不我他在人有这来个说上你们到地大着子那就也时道中出得为里下她要么去可以过和看之然后会自没小好生天而起对能还事想都心只家面样把国多又于头年手发如什开前当所无知老但见长已军从方声儿回意作话两点现很成身情十用些走经同进动己三行种向日明女正问此学太打间分因给本眼定二气力被门真法外听实其高先几笑再主将山战才口文最部第它西与全白者便相住公使东等边信像斯机光次感神却死理名重四做别叫王并水月果何位怎马常觉海张少处亲安特美呢色原直望命由候吧让应尔难关许车平师民夫书新接吗路利世比放活快总立队更花爱清五内金带工风克任至指往入空德吃表连解教思飞物电受今完林干代告兵加认通找远非性脸体轻记目令变似反南场跟必石拉士报李火且满该孩字红象即结言员房件万条提写或坐北早失离步陈乎请转近切黑深城办倒各父传音站官半男击合阿英决怕杀未形及算青黄落刚百论谁突交团度义罗始强紧敌八母钱极片化流管惊每题晚虽政兴答司妈夜越啊奇达谈武友数领朝保服曾拿则哪格尽根急语容喜求衣留双影刻制随冷九苦量备布照周故准客船江系姐争功怪星断句龙竟视界讲取古六静底精七河久绝阳识哈台确息期整伤忙娘终剑送计愿欢微您沉装敢云脚消若复收千木乐毛华集树弟皇响希诉号巴穿线汉攻呀警派刘酒雷停史阵错建足显丽另包势破亮首志观病热跑业治田冲运约暗待共院仍区害元哥围屋胡产室调类细议爷注易务众帝市摇乱密姑斗除式示睛楼造朋社持慢般皮京况块忽脑校甚查土怀福单联赶背统喝疑支血饭灵够章群威举兰游器察嘴痛铁掉宝历改推枪念参术帮党据品须居称旁退梦科岁低严引吉睡爸呼追局露维苏证村挥独节谢香亚波角案读图左掌假跳究楚余鬼钟座礼展玉肯既止守广考恩异料段尼画续米草胜存唐医程弹烟商顾招宗堂野初府激雨尚渐诗顿伯孙际沙雪板闻导致护春态基设耳简婚幸味右买演权卡继依恶庄炮亦虎州刀球需兄闪笔否烈玩啦逃排仅弄具散默景顶郑洋丝卫速侠差贵君习妇助恐救湖莫窗险顺封佛委旧印伙妹副宫洞永罪松责组防艺营班试鲁宋靠索灯介翻喊纪秘妻纸银略充戏担某杨射魔遇鱼陆级坏忘乡鲜哭费抓叶醒族纳床咱桌境列午振抱专毫店街怒托剧置秋藏赵划普岛较承脱革忍伸份免齐抗猛园临犯食架选歌按败良隐养属洛朱狂修坚压寻旅谋温探资端投泪阴换药麻施丈冰雄著绿职负短模荣遗农叹川毒吴蒋卖范禁杂富秀县祖梅谷凡刺登蓝奶缓姓牛价规篇劳质婆仙馆摆舞层狗占墙善熟验肉臣状呆圣懂袋唱值迷替归巨讨毕批镇吸森拍灭握伊杰勒卷偷奔省谓危付伦休厅预迎罢恨博亡欲悲宣标闹岸';
23112
+ var str = decodeSamples('gb18030', samples).join('');
23113
+ // Almost all the common Unicode Hanzi are in this range (along with many more uncommon ones)
23114
+ var chineseStr = str.replace(/[^\u4e00-\u9fa5]/g, '');
23115
+ var chinesePct = chineseStr.length / str.length;
23116
+ var commonAsciiStr = extractCommonAsciiChars(str);
23117
+ var commonAsciiPct = commonAsciiStr.length / str.length;
23118
+ // Some encodings get converted almost completely into valid (but mostly
23119
+ // uncommon) Chinese characters by the gb18030 converter.
23120
+ // To guard against this, we're requiring that a certain percentage of
23121
+ // characters be on a list of the most common characters.
23122
+ var commonHanZiPct = calcCharPct(chineseStr, commonHanZi);
23123
+ // check for non-convertible characters
23124
+ var invalidPct = getInvalidPct(str);
23125
+ var high = chinesePct > 0.5 && (chinesePct + commonAsciiPct) > 0.9 &&
23126
+ invalidPct === 0 && commonHanZiPct > 0.25;
23127
+ var low = chinesePct > 0.3 && (chinesePct + commonAsciiPct) > 0.8 &&
23128
+ commonHanZiPct > 0.15;
23129
+ return getScore(high, low);
23130
+ }
23131
+
23132
+ function getScore(high, low) {
23133
+ return high && 2 || low && 1 || 0;
23134
+ }
23135
+
23136
+ function getInvalidPct(str) {
23137
+ // count occurences of the "replacement" character
23138
+ var invalidCount = (str.match(/\ufffd/g) || []).length;
23139
+ return invalidCount / str.length;
23140
+ }
23141
+
23142
+ function extractCommonAsciiChars(str) {
23143
+ return str.replace(/[^a-zA-Z0-9.()'"?+\n,:;/|_$% -]/g, '');
23144
+ }
23120
23145
 
23121
23146
  // Calc percentage of chars in a string that are present in a second string
23122
23147
  // @chars String of chars to look for in @str
23123
- function getCharScore(str, chars) {
23148
+ function calcCharPct(str, chars) {
23124
23149
  var index = {},
23125
23150
  count = 0;
23126
23151
  str = str.toLowerCase();
@@ -23130,9 +23155,16 @@ ${svg}
23130
23155
  for (i=0, n=str.length; i<n; i++) {
23131
23156
  count += index[str[i]] || 0;
23132
23157
  }
23133
- return count / str.length;
23158
+ return count / str.length || 0;
23134
23159
  }
23135
23160
 
23161
+ var EncodingDetection = /*#__PURE__*/Object.freeze({
23162
+ __proto__: null,
23163
+ detectEncodingFromBOM: detectEncodingFromBOM,
23164
+ detectEncoding: detectEncoding,
23165
+ decodeSamples: decodeSamples
23166
+ });
23167
+
23136
23168
  // Convert a string containing delimited text data into a dataset object
23137
23169
  function importDelim(str, opts) {
23138
23170
  return importDelim2({content: str}, opts);
@@ -26644,25 +26676,19 @@ ${svg}
26644
26676
  var info = detectEncoding(samples);
26645
26677
  encoding = info.encoding;
26646
26678
  if (info.confidence < 2) {
26647
- msg = 'Warning: Unable to auto-detect the text encoding of a DBF file with high confidence.';
26679
+ msg = 'Warning: Unable to auto-detect the DBF file text encoding with high confidence.';
26648
26680
  msg += '\n\nDefaulting to: ' + encoding + (encoding in encodingNames ? ' (' + encodingNames[encoding] + ')' : '');
26649
- msg += '\n\nSample of how non-ascii text was imported:';
26650
- msg += '\n' + formatStringsAsGrid(decodeSamples(encoding, samples).split('\n'));
26651
- msg += decodeSamples(encoding, samples);
26681
+ msg += '\n\nSample of how non-ascii text was imported:\n';
26682
+ if (runningInBrowser()) {
26683
+ msg += '<pre>' + formatStringsAsGrid(decodeSamples(encoding, samples), 50) + '</pre>';
26684
+ } else {
26685
+ msg += formatStringsAsGrid(decodeSamples(encoding, samples));
26686
+ }
26652
26687
  msg += '\n\n' + ENCODING_PROMPT + '\n';
26653
26688
  message(msg);
26654
26689
  }
26655
26690
  }
26656
26691
 
26657
- // Show a sample of decoded text if non-ascii-range text has been found
26658
- // if (encoding && samples.length > 0) {
26659
- // msg = "Detected DBF text encoding: " + encoding + (encoding in encodingNames ? " (" + encodingNames[encoding] + ")" : "");
26660
- // message(msg);
26661
- // msg = decodeSamples(encoding, samples);
26662
- // msg = formatStringsAsGrid(msg.split('\n'));
26663
- // msg = "\nSample text containing non-ascii characters:" + (msg.length > 60 ? '\n' : '') + msg;
26664
- // verbose(msg);
26665
- // }
26666
26692
  return encoding;
26667
26693
  }
26668
26694
 
@@ -37783,7 +37809,7 @@ ${svg}
37783
37809
  filteredLyr = copyLayer(filteredLyr);
37784
37810
  }
37785
37811
 
37786
- if (opts.verbose !== false) {
37812
+ if (opts.verbose !== false && !opts.quiet) {
37787
37813
  message(utils.format('Retained %,d of %,d features', getFeatureCount(filteredLyr), n));
37788
37814
  }
37789
37815
 
@@ -37833,14 +37859,15 @@ ${svg}
37833
37859
  }
37834
37860
 
37835
37861
  function combineFilters(a, b) {
37836
- return (a && b && function(id) {
37862
+ return a && b ? function(id) {
37837
37863
  return a(id) && b(id);
37838
- }) || a || b;
37864
+ } : (a || b);
37839
37865
  }
37840
37866
 
37841
- cmd.evaluateEachFeature = function(lyr, dataset, exp, opts) {
37867
+ cmd.evaluateEachFeature = function(lyr, dataset, expArg, opts) {
37842
37868
  var n = getFeatureCount(lyr),
37843
37869
  arcs = dataset.arcs,
37870
+ exp = expArg || '',
37844
37871
  compiled, filter;
37845
37872
 
37846
37873
  var exprOpts = {
@@ -42377,6 +42404,9 @@ ${svg}
42377
42404
  candidates.forEach(function(candId) {
42378
42405
  var hit;
42379
42406
  if (candId == absId) return; // ignore self-intersections
42407
+ if (absArcId(candId) >= arcs.size()) {
42408
+ return;
42409
+ }
42380
42410
  hit = geom.getPointToPathInfo(endpoint.point[0], endpoint.point[1], [candId], arcs);
42381
42411
  if (hit && hit.distance <= maxGapLen && (!target || hit.distance < target.distance)) {
42382
42412
  target = hit;
@@ -42462,7 +42492,9 @@ ${svg}
42462
42492
  // if (opts.gap_tolerance) {
42463
42493
  //opts = utils.defaults({snap_interval: opts.gap_tolerance * 0.1}, opts);
42464
42494
  // }
42465
- addIntersectionCuts(dataset, opts);
42495
+ if (!opts.no_cuts) {
42496
+ addIntersectionCuts(dataset, opts);
42497
+ }
42466
42498
  return layers.map(function(lyr) {
42467
42499
  if (lyr.geometry_type != 'polyline') stop("Expected a polyline layer");
42468
42500
  if (opts.from_rings) {
@@ -42492,7 +42524,12 @@ ${svg}
42492
42524
  }
42493
42525
 
42494
42526
  function createPolygonLayer(lyr, dataset, opts) {
42495
- var nodes = closeUndershoots(lyr, dataset, opts);
42527
+ var nodes;
42528
+ if (opts.no_cuts) {
42529
+ nodes = new NodeCollection(dataset.arcs);
42530
+ } else {
42531
+ nodes = closeUndershoots(lyr, dataset, opts);
42532
+ }
42496
42533
  // ignore arcs that don't belong to this layer
42497
42534
  nodes.setArcFilter(getArcPresenceTest(lyr.shapes, nodes.arcs));
42498
42535
  var data = buildPolygonMosaic(nodes);
@@ -45485,7 +45522,7 @@ ${svg}
45485
45522
  });
45486
45523
  }
45487
45524
 
45488
- var version = "0.6.74";
45525
+ var version = "0.6.76";
45489
45526
 
45490
45527
  // Parse command line args into commands and run them
45491
45528
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -46162,6 +46199,7 @@ ${svg}
46162
46199
  DelimExport,
46163
46200
  DelimImport,
46164
46201
  DelimReader,
46202
+ EncodingDetection,
46165
46203
  Encodings,
46166
46204
  Explode,
46167
46205
  Export,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.74",
3
+ "version": "0.6.76",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
package/www/index.html CHANGED
@@ -39,7 +39,7 @@
39
39
  <svg id="pointer-icon" version="1.1" xmlns="http://www.w3.org/2000/svg" width="15" height="22" viewBox="0 0 15 20"><rect x="7.4126" y="11.0354" width="3.9102" height="7.9841" transform="translate(-5.8889 6.0377) rotate(-27.5536)" fill="#30d4ef"/><polygon points="2.57 2.086 2.718 16.056 13.944 10.198 2.57 2.086" fill="#30d4ef"/></svg>
40
40
 
41
41
  <!-- adjusted height -->
42
- <svg id="ribbon-icon" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="15" height="26" viewBox="0 0 15 20">
42
+ <svg id="ribbon-icon" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="15" height="25" viewBox="0 1 15 20">
43
43
  <polygon points="8 14.5 13 19 13 4 3 4 3 19 8 14.5" fill="#30d4ef"/>
44
44
  </svg>
45
45
 
@@ -112,34 +112,30 @@
112
112
  </div>
113
113
  </div>
114
114
 
115
- <div class="box-tool-options main-area popup-dialog">
116
- <div class="info-box controls">
117
- <div>
118
- <div class="select-btn btn dialog-btn">Select</div>
119
- <div class="clip-btn btn dialog-btn">Clip</div>
120
- <div class="erase-btn btn dialog-btn">Erase</div>
121
- <div class="rect-btn btn dialog-btn">Rectangle</div>
122
- <div class="frame-btn btn dialog-btn">Frame</div>
123
- <div class="info-btn btn dialog-btn">Coords</div>
124
- <!-- <div class="zoom-btn btn dialog-btn">Zoom In</div> -->
125
- <div class="cancel-btn btn dialog-btn">Cancel</div>
126
- </div>
115
+
116
+ <div class="box-tool-options sidebar-buttons">
117
+ <div>
118
+ <div class="select-btn btn sidebar-btn">Select</div>
119
+ <div class="clip-btn btn sidebar-btn">Clip</div>
120
+ <div class="erase-btn btn sidebar-btn">Erase</div>
121
+ <div class="rect-btn btn sidebar-btn">Rectangle</div>
122
+ <div class="frame-btn btn sidebar-btn">Frame</div>
123
+ <div class="info-btn btn sidebar-btn">Coords</div>
127
124
  <div class="box-coords selectable"></div>
125
+ <div class="cancel-btn btn sidebar-btn">Cancel</div>
128
126
  </div>
129
127
  </div>
130
128
 
131
- <div class="selection-tool-options main-area popup-dialog">
132
- <div class="info-box">
133
- <div>
134
- <div class="delete-btn btn dialog-btn">Delete</div>
135
- <div class="filter-btn btn dialog-btn">Keep</div>
136
- <div class="duplicate-btn btn dialog-btn">Duplicate</div>
137
- <div class="split-btn btn dialog-btn">Split</div>
138
- <div class="coords-btn btn dialog-btn toggle-btn">Coords</div>
139
- <div class="data-btn btn dialog-btn toggle-btn">Edit data</div>
140
- <div class="cancel-btn btn dialog-btn">Clear</div>
141
- </div>
129
+ <div class="selection-tool-options sidebar-buttons">
130
+ <div>
131
+ <div class="delete-btn btn sidebar-btn">Delete</div>
132
+ <div class="filter-btn btn sidebar-btn">Keep</div>
133
+ <div class="duplicate-btn btn sidebar-btn">Duplicate</div>
134
+ <div class="split-btn btn sidebar-btn">Split</div>
135
+ <div class="coords-btn btn sidebar-btn toggle-btn">Coords</div>
142
136
  <div class="box-coords selectable"></div>
137
+ <div class="data-btn btn sidebar-btn toggle-btn">Edit data</div>
138
+ <div class="cancel-btn btn sidebar-btn">Clear</div>
143
139
  </div>
144
140
  </div>
145
141
 
@@ -304,9 +300,9 @@ encoding=big5</span>. Click to see all options.</div></div></div></div>
304
300
  </div> <!-- .info-box -->
305
301
  </div> <!-- import-options -->
306
302
 
307
-
308
303
  <!-- TODO: remove #mshp-main-page without causing the map to jitter when resized -->
309
304
  <div id="mshp-main-page">
305
+
310
306
  <div class="console main-area console-area">
311
307
  <div class="console-window"><div class="console-buffer selectable"></div></div>
312
308
  </div>