mapshaper 0.7.1 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/LICENSE +1 -1
  2. package/mapshaper.js +1 -1
  3. package/package.json +5 -2
  4. package/www/.nojekyll +0 -0
  5. package/www/index.html +22 -6
  6. package/www/mapshaper-gui.js +292 -4
  7. package/www/mapshaper.js +1 -1
  8. package/www/page.css +188 -10
  9. package/www/docs/_assets/cmd-search.js +0 -213
  10. package/www/docs/_assets/docs.css +0 -712
  11. package/www/docs/_assets/docs.js +0 -75
  12. package/www/docs/_assets/highlight.css +0 -10
  13. package/www/docs/essentials/command-line.html +0 -127
  14. package/www/docs/essentials/command-line.html.md +0 -112
  15. package/www/docs/essentials/web-app.html +0 -138
  16. package/www/docs/essentials/web-app.html.md +0 -106
  17. package/www/docs/examples/basics.html +0 -276
  18. package/www/docs/examples/basics.html.md +0 -371
  19. package/www/docs/examples/data/Makefile +0 -31
  20. package/www/docs/examples/data/globe.msx +0 -0
  21. package/www/docs/examples/data/globe.svg +0 -616
  22. package/www/docs/examples/data/globe.txt +0 -21
  23. package/www/docs/examples/data/globe.zip +0 -0
  24. package/www/docs/examples/data/ne_50m_admin_0_countries.geojson +0 -1
  25. package/www/docs/examples/data/ne_50m_admin_1_states_provinces_lakes.geojson +0 -1
  26. package/www/docs/examples/data/us-states.msx +0 -0
  27. package/www/docs/examples/data/us-states.svg +0 -56
  28. package/www/docs/examples/data/us-states.txt +0 -6
  29. package/www/docs/examples/data/us-states.zip +0 -0
  30. package/www/docs/examples/globe.html +0 -108
  31. package/www/docs/examples/globe.html.md +0 -64
  32. package/www/docs/examples/us-states.html +0 -88
  33. package/www/docs/examples/us-states.html.md +0 -44
  34. package/www/docs/formats/csv.html +0 -127
  35. package/www/docs/formats/csv.html.md +0 -97
  36. package/www/docs/formats/dbf.html +0 -87
  37. package/www/docs/formats/dbf.html.md +0 -39
  38. package/www/docs/formats/flatgeobuf.html +0 -86
  39. package/www/docs/formats/flatgeobuf.html.md +0 -42
  40. package/www/docs/formats/geojson.html +0 -107
  41. package/www/docs/formats/geojson.html.md +0 -65
  42. package/www/docs/formats/geopackage.html +0 -87
  43. package/www/docs/formats/geopackage.html.md +0 -42
  44. package/www/docs/formats/json.html +0 -83
  45. package/www/docs/formats/json.html.md +0 -35
  46. package/www/docs/formats/kml.html +0 -82
  47. package/www/docs/formats/kml.html.md +0 -39
  48. package/www/docs/formats/overview.html +0 -192
  49. package/www/docs/formats/overview.html.md +0 -35
  50. package/www/docs/formats/shapefile.html +0 -123
  51. package/www/docs/formats/shapefile.html.md +0 -84
  52. package/www/docs/formats/snapshot.html +0 -87
  53. package/www/docs/formats/snapshot.html.md +0 -39
  54. package/www/docs/formats/svg.html +0 -99
  55. package/www/docs/formats/svg.html.md +0 -51
  56. package/www/docs/formats/topojson.html +0 -102
  57. package/www/docs/formats/topojson.html.md +0 -54
  58. package/www/docs/gallery/index.html +0 -80
  59. package/www/docs/gallery/index.html.md +0 -29
  60. package/www/docs/guides/combining-layers.html +0 -105
  61. package/www/docs/guides/combining-layers.html.md +0 -81
  62. package/www/docs/guides/expressions.html +0 -600
  63. package/www/docs/guides/expressions.html.md +0 -376
  64. package/www/docs/guides/programmatic.html +0 -117
  65. package/www/docs/guides/programmatic.html.md +0 -91
  66. package/www/docs/guides/projections.html +0 -158
  67. package/www/docs/guides/projections.html.md +0 -118
  68. package/www/docs/guides/simplification.html +0 -110
  69. package/www/docs/guides/simplification.html.md +0 -94
  70. package/www/docs/guides/topology.html +0 -90
  71. package/www/docs/guides/topology.html.md +0 -63
  72. package/www/docs/images/simplification-detail.png +0 -0
  73. package/www/docs/images/simplification-dp.png +0 -0
  74. package/www/docs/images/simplification-mod2.png +0 -0
  75. package/www/docs/index.html +0 -101
  76. package/www/docs/index.html.md +0 -59
  77. package/www/docs/reference.html +0 -1302
  78. package/www/docs/reference.html.md +0 -1817
  79. package/www/docs/whats-new.html +0 -76
  80. package/www/docs/whats-new.html.md +0 -53
  81. package/www/llms-full.txt +0 -4040
  82. package/www/llms.txt +0 -55
package/www/page.css CHANGED
@@ -170,7 +170,7 @@ body.map-view {
170
170
  position: absolute;
171
171
  top: 0px;
172
172
  right: 0px;
173
- margin: 0 6px 3px 0;
173
+ margin: 0 3px 3px 0;
174
174
  }
175
175
 
176
176
  .btn.header-btn {
@@ -207,6 +207,63 @@ body.map-view {
207
207
  vertical-align: -1px;
208
208
  }
209
209
 
210
+ /* --- Hamburger menu (post-edit overflow links) --- */
211
+
212
+ #header-menu-btn {
213
+ cursor: pointer;
214
+ padding: 8px 7px 4px 7px;
215
+ user-select: none;
216
+ /* All header buttons are 29px tall, so top-align the hamburger to keep its
217
+ row baseline-neutral. Without this, the SVG-only button (no text) makes
218
+ its inline-block baseline = bottom edge, which drags the surrounding
219
+ text-containing buttons down to match. */
220
+ vertical-align: top;
221
+ }
222
+
223
+ #header-menu-btn svg {
224
+ display: block;
225
+ fill: currentColor;
226
+ }
227
+
228
+ #header-menu-dropdown {
229
+ position: absolute;
230
+ top: 30px;
231
+ right: 0;
232
+ min-width: 180px;
233
+ background-color: #fff;
234
+ color: #333;
235
+ border: 1px solid #ccc;
236
+ border-radius: 3px;
237
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
238
+ padding: 4px 0;
239
+ z-index: 50;
240
+ }
241
+
242
+ .header-menu-item {
243
+ display: block;
244
+ padding: 4px 14px;
245
+ color: #333;
246
+ font-size: 14px;
247
+ line-height: 1.3;
248
+ text-decoration: none;
249
+ white-space: nowrap;
250
+ }
251
+
252
+ .header-menu-item:hover,
253
+ .header-menu-item:focus {
254
+ background-color: #e6f7ff;
255
+ color: #1A6A96;
256
+ outline: none;
257
+ }
258
+
259
+ .header-menu-item.header-menu-sponsor svg {
260
+ width: 12px;
261
+ height: 12px;
262
+ fill: #d63384;
263
+ vertical-align: -1px;
264
+ margin-right: 6px;
265
+ }
266
+
210
267
  .separator {
211
268
  border-left: 1px solid white;
212
269
  height: 10px;
@@ -427,6 +484,14 @@ div.alert-box {
427
484
  margin: 0 0 5px 0;
428
485
  }
429
486
 
487
+ input[type=text]:focus,
488
+ input[type=number]:focus,
489
+ textarea:focus {
490
+ outline: none;
491
+ border-color: #d3aa00;
492
+ box-shadow: 0 0 0 2px rgba(212, 168, 0, 0.4);
493
+ }
494
+
430
495
  ::placeholder {
431
496
  color: #aaa;
432
497
  opacity: 1;
@@ -495,13 +560,6 @@ div.alert-box {
495
560
  padding-left: 12px;
496
561
  }
497
562
 
498
- .info-box,.popup {
499
- border-radius: 9px;
500
- border: 1px solid #d5d5d5;
501
- box-shadow: 0 1px 7px rgba(0,0,0,0.18);
502
- background-color: #fff;
503
- }
504
-
505
563
  .contextmenu {
506
564
  position: absolute;
507
565
  border-radius: 6px;
@@ -518,6 +576,12 @@ div.alert-box {
518
576
  padding: 6px;
519
577
  }
520
578
 
579
+ .info-box,.popup {
580
+ border-radius: 11px;
581
+ border: 1px solid #d5d5d5;
582
+ box-shadow: 0 1px 7px rgba(0,0,0,0.18);
583
+ background-color: #fff;
584
+ }
521
585
 
522
586
  .info-box {
523
587
  min-width: 230px;
@@ -525,7 +589,8 @@ div.alert-box {
525
589
  text-align: left;
526
590
  margin-top: 12px;
527
591
  margin-right: 20px;
528
- padding: 12px 16px 12px 18px;
592
+ /* padding: 12px 16px 12px 18px; */
593
+ padding: 15px 17px 13px 19px;
529
594
  vertical-align: top;
530
595
  display: inline-block;
531
596
  /* border: 1px solid #aaa; */
@@ -575,7 +640,7 @@ div.alert-box {
575
640
  .info-box p {
576
641
  white-space: pre-line;
577
642
  margin: 0 0 6px 0;
578
- line-height: 1.2;
643
+ line-height: 1.25;
579
644
  /* font-size: 90%; */
580
645
  }
581
646
 
@@ -1552,6 +1617,119 @@ body.pan.panning .map-layers:not(.drawing) {
1552
1617
  cursor: default;
1553
1618
  }
1554
1619
 
1620
+ /* --- FLOATING TOOLBAR --- */
1621
+
1622
+ /*
1623
+ * Anchored at bottom-center of the map area. Multiple toolbars stack
1624
+ * vertically (newest on top). Designed to host buttons and other small
1625
+ * controls; the inner content slot leaves room for a future drag handle.
1626
+ */
1627
+
1628
+ .floating-toolbar-stack {
1629
+ position: absolute;
1630
+ z-index: 25;
1631
+ bottom: 24px;
1632
+ left: 50%;
1633
+ transform: translateX(-50%);
1634
+ display: flex;
1635
+ flex-direction: column-reverse;
1636
+ align-items: center;
1637
+ gap: 8px;
1638
+ pointer-events: none;
1639
+ }
1640
+
1641
+ .floating-toolbar {
1642
+ pointer-events: auto;
1643
+ display: flex;
1644
+ align-items: center;
1645
+ background-color: rgba(255, 255, 255, 0.96);
1646
+ border: 1px solid #ddd;
1647
+ border-radius: 8px;
1648
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
1649
+ padding: 4px;
1650
+ opacity: 0;
1651
+ transform: translateY(6px);
1652
+ transition: opacity 150ms ease, transform 150ms ease;
1653
+ }
1654
+
1655
+ .floating-toolbar.visible {
1656
+ opacity: 1;
1657
+ transform: translateY(0);
1658
+ }
1659
+
1660
+ .floating-toolbar-content {
1661
+ display: flex;
1662
+ align-items: center;
1663
+ gap: 0;
1664
+ }
1665
+
1666
+ .floating-toolbar-separator {
1667
+ width: 1px;
1668
+ height: 18px;
1669
+ margin: 0 4px;
1670
+ background-color: #ddd;
1671
+ }
1672
+
1673
+ .floating-toolbar-btn {
1674
+ position: relative;
1675
+ display: flex;
1676
+ align-items: center;
1677
+ justify-content: center;
1678
+ width: 28px;
1679
+ height: 28px;
1680
+ border-radius: 5px;
1681
+ cursor: pointer;
1682
+ user-select: none;
1683
+ }
1684
+
1685
+ .floating-toolbar-btn svg {
1686
+ width: 24px;
1687
+ height: 24px;
1688
+ }
1689
+
1690
+ .floating-toolbar-btn svg * {
1691
+ fill: #1385B7;
1692
+ transition: fill 100ms ease;
1693
+ }
1694
+
1695
+ .floating-toolbar-btn:hover:not(.disabled) {
1696
+ background-color: #e6f7ff;
1697
+ }
1698
+
1699
+ .floating-toolbar-btn:hover:not(.disabled) svg * {
1700
+ fill: #166689;
1701
+ }
1702
+
1703
+ .floating-toolbar-btn:active:not(.disabled) {
1704
+ background-color: #cdebf9;
1705
+ }
1706
+
1707
+ .floating-toolbar-btn.disabled {
1708
+ cursor: default;
1709
+ }
1710
+
1711
+ .floating-toolbar-btn.disabled svg * {
1712
+ fill: #b8c4ca;
1713
+ }
1714
+
1715
+ /* Tooltip shown above the button on hover */
1716
+ .floating-toolbar-btn[data-tooltip]:hover:not(.disabled)::after {
1717
+ content: attr(data-tooltip);
1718
+ position: absolute;
1719
+ bottom: calc(100% + 8px);
1720
+ left: 50%;
1721
+ transform: translateX(-50%);
1722
+ background-color: rgba(40, 40, 40, 0.92);
1723
+ color: #fff;
1724
+ font-size: 11px;
1725
+ line-height: 1;
1726
+ white-space: nowrap;
1727
+ padding: 5px 7px;
1728
+ border-radius: 4px;
1729
+ pointer-events: none;
1730
+ z-index: 1;
1731
+ }
1732
+
1555
1733
  .zoom-box {
1556
1734
  position: absolute;
1557
1735
  border: 1px solid #cc6acc;
@@ -1,213 +0,0 @@
1
- // Jump-to-command dropdown for the command reference page.
2
- //
3
- // The build script wraps each command in <section class="cmd-section"
4
- // data-id="-clip" data-name="-clip" data-options="..."> and tags the H2
5
- // above each group as <h2 class="cmd-category">. We use that to populate a
6
- // dropdown of matching commands beneath the search input. Selecting an item
7
- // jumps to the corresponding section -- the page itself is never filtered.
8
-
9
- (function () {
10
- 'use strict';
11
-
12
- var input = document.querySelector('.cmd-search-input');
13
- var panel = document.querySelector('.cmd-search-results');
14
- if (!input || !panel) return;
15
-
16
- var statusEl = panel.querySelector('.cmd-search-status');
17
- var listEl = panel.querySelector('.cmd-search-list');
18
- var sections = Array.prototype.slice.call(
19
- document.querySelectorAll('.cmd-section')
20
- );
21
- if (!sections.length) return;
22
-
23
- // Build a flat command index. We walk the article in document order so we
24
- // can attach the most recent h2.cmd-category to each section.
25
- var article = sections[0].closest('.docs-article') || document;
26
- var commands = (function () {
27
- var out = [];
28
- var currentCat = '';
29
- var nodes = article.querySelectorAll('h2.cmd-category, section.cmd-section');
30
- for (var i = 0; i < nodes.length; i++) {
31
- var n = nodes[i];
32
- if (n.tagName === 'H2') {
33
- currentCat = (n.textContent || '').trim();
34
- } else {
35
- var name = n.dataset.name || '';
36
- var opts = n.dataset.options || '';
37
- var id = n.dataset.id || '';
38
- if (!id) continue;
39
- out.push({
40
- id: id,
41
- name: name,
42
- options: opts,
43
- category: currentCat,
44
- haystack: (name + ' ' + opts).toLowerCase(),
45
- el: n
46
- });
47
- }
48
- }
49
- return out;
50
- })();
51
-
52
- var MAX_RESULTS = 60;
53
- var activeIdx = -1;
54
- var results = [];
55
-
56
- function render() {
57
- var raw = input.value.trim();
58
- var tokens = raw.toLowerCase().split(/\s+/).filter(Boolean);
59
-
60
- if (!tokens.length) {
61
- close();
62
- return;
63
- }
64
-
65
- // Score-free matching: every token must appear in the name + options
66
- // string. We deliberately don't search the full body text -- the dropdown
67
- // is for jumping to a known command, not for free-text search.
68
- results = commands.filter(function (c) {
69
- return tokens.every(function (t) { return c.haystack.indexOf(t) !== -1; });
70
- });
71
-
72
- var truncated = results.length > MAX_RESULTS;
73
- var shown = truncated ? results.slice(0, MAX_RESULTS) : results;
74
-
75
- listEl.innerHTML = shown.map(function (c, i) {
76
- return '<li role="option" id="cmd-result-' + i + '" class="cmd-result">' +
77
- '<a href="#' + escapeAttr(c.id) + '" class="cmd-result-link" tabindex="-1">' +
78
- '<span class="cmd-result-name">' + escapeHtml(c.name) + '</span>' +
79
- (c.category
80
- ? '<span class="cmd-result-cat">' + escapeHtml(c.category) + '</span>'
81
- : '') +
82
- '</a>' +
83
- '</li>';
84
- }).join('');
85
-
86
- if (!results.length) {
87
- statusEl.textContent = 'No commands match \u201c' + raw + '\u201d.';
88
- } else if (truncated) {
89
- statusEl.textContent = 'Showing first ' + MAX_RESULTS + ' of ' +
90
- results.length + ' matches.';
91
- } else if (results.length === 1) {
92
- statusEl.textContent = '1 match (press Enter to jump).';
93
- } else {
94
- statusEl.textContent = results.length + ' matches.';
95
- }
96
-
97
- open();
98
- setActive(results.length ? 0 : -1);
99
- }
100
-
101
- function open() {
102
- panel.hidden = false;
103
- input.setAttribute('aria-expanded', 'true');
104
- }
105
-
106
- function close() {
107
- panel.hidden = true;
108
- input.setAttribute('aria-expanded', 'false');
109
- input.removeAttribute('aria-activedescendant');
110
- activeIdx = -1;
111
- results = [];
112
- listEl.innerHTML = '';
113
- statusEl.textContent = '';
114
- }
115
-
116
- function setActive(idx) {
117
- var items = listEl.querySelectorAll('.cmd-result');
118
- if (activeIdx >= 0 && items[activeIdx]) {
119
- items[activeIdx].classList.remove('is-active');
120
- }
121
- activeIdx = idx;
122
- if (idx < 0 || !items[idx]) {
123
- input.removeAttribute('aria-activedescendant');
124
- return;
125
- }
126
- items[idx].classList.add('is-active');
127
- input.setAttribute('aria-activedescendant', 'cmd-result-' + idx);
128
- // Keep the highlighted item visible inside the (scrollable) dropdown.
129
- var el = items[idx];
130
- var top = el.offsetTop;
131
- var bottom = top + el.offsetHeight;
132
- if (top < listEl.scrollTop) {
133
- listEl.scrollTop = top;
134
- } else if (bottom > listEl.scrollTop + listEl.clientHeight) {
135
- listEl.scrollTop = bottom - listEl.clientHeight;
136
- }
137
- }
138
-
139
- function jumpTo(idx) {
140
- if (idx < 0 || idx >= results.length) return;
141
- var c = results[idx];
142
- // Use the anchor's href so the browser updates location.hash and triggers
143
- // its native scroll-to-anchor behaviour (which respects scroll-margin-top).
144
- var link = listEl.querySelectorAll('.cmd-result-link')[idx];
145
- if (link) link.click();
146
- close();
147
- input.blur();
148
- }
149
-
150
- function escapeHtml(s) {
151
- return String(s).replace(/[&<>"']/g, function (c) {
152
- return ({ '&': '&amp;', '<': '&lt;', '>': '&gt;',
153
- '"': '&quot;', "'": '&#39;' })[c];
154
- });
155
- }
156
- function escapeAttr(s) { return escapeHtml(s); }
157
-
158
- input.addEventListener('input', render);
159
- input.addEventListener('focus', function () {
160
- if (input.value.trim()) render();
161
- });
162
-
163
- input.addEventListener('keydown', function (e) {
164
- if (e.key === 'ArrowDown') {
165
- if (panel.hidden) { render(); return; }
166
- e.preventDefault();
167
- if (results.length) setActive((activeIdx + 1) % results.length);
168
- } else if (e.key === 'ArrowUp') {
169
- if (panel.hidden) return;
170
- e.preventDefault();
171
- if (results.length) {
172
- setActive(activeIdx <= 0 ? results.length - 1 : activeIdx - 1);
173
- }
174
- } else if (e.key === 'Enter') {
175
- if (panel.hidden || activeIdx < 0) return;
176
- e.preventDefault();
177
- jumpTo(activeIdx);
178
- } else if (e.key === 'Escape') {
179
- if (!panel.hidden) {
180
- e.preventDefault();
181
- close();
182
- } else if (input.value) {
183
- e.preventDefault();
184
- input.value = '';
185
- }
186
- }
187
- });
188
-
189
- // Mouse interaction with the dropdown.
190
- listEl.addEventListener('mousemove', function (e) {
191
- var li = e.target.closest('.cmd-result');
192
- if (!li) return;
193
- var idx = Array.prototype.indexOf.call(listEl.children, li);
194
- if (idx !== activeIdx) setActive(idx);
195
- });
196
-
197
- listEl.addEventListener('click', function (e) {
198
- var link = e.target.closest('.cmd-result-link');
199
- if (!link) return;
200
- // Let cmd/ctrl/middle-click open in new tab as normal; otherwise
201
- // intercept so we can close the dropdown after navigation.
202
- if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) return;
203
- // Default <a> click handles the scroll; just tidy up the UI.
204
- setTimeout(close, 0);
205
- });
206
-
207
- // Click outside the wrap closes the dropdown.
208
- document.addEventListener('click', function (e) {
209
- if (panel.hidden) return;
210
- if (e.target.closest('.cmd-search-wrap')) return;
211
- close();
212
- });
213
- })();