mapshaper 0.7.0 → 0.7.2

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 (89) hide show
  1. package/README.md +5 -3
  2. package/mapshaper.js +363 -62
  3. package/package.json +6 -2
  4. package/www/.nojekyll +0 -0
  5. package/www/assets/jetbrains-mono-regular.woff2 +0 -0
  6. package/www/assets/static-page.css +179 -0
  7. package/www/docs/_assets/cmd-search.js +213 -0
  8. package/www/docs/_assets/docs.css +722 -0
  9. package/www/docs/_assets/docs.js +75 -0
  10. package/www/docs/_assets/highlight.css +10 -0
  11. package/www/docs/essentials/command-line.html +127 -0
  12. package/www/docs/essentials/command-line.html.md +112 -0
  13. package/www/docs/essentials/web-app.html +138 -0
  14. package/www/docs/essentials/web-app.html.md +106 -0
  15. package/www/docs/examples/basics.html +275 -0
  16. package/www/docs/examples/basics.html.md +370 -0
  17. package/www/docs/examples/data/Makefile +31 -0
  18. package/www/docs/examples/data/globe.msx +0 -0
  19. package/www/docs/examples/data/globe.svg +616 -0
  20. package/www/docs/examples/data/globe.txt +21 -0
  21. package/www/docs/examples/data/globe.zip +0 -0
  22. package/www/docs/examples/data/ne_50m_admin_0_countries.geojson +1 -0
  23. package/www/docs/examples/data/ne_50m_admin_1_states_provinces_lakes.geojson +1 -0
  24. package/www/docs/examples/data/us-states.msx +0 -0
  25. package/www/docs/examples/data/us-states.svg +56 -0
  26. package/www/docs/examples/data/us-states.txt +6 -0
  27. package/www/docs/examples/data/us-states.zip +0 -0
  28. package/www/docs/examples/globe.html +108 -0
  29. package/www/docs/examples/globe.html.md +64 -0
  30. package/www/docs/examples/us-states.html +88 -0
  31. package/www/docs/examples/us-states.html.md +44 -0
  32. package/www/docs/formats/csv.html +127 -0
  33. package/www/docs/formats/csv.html.md +97 -0
  34. package/www/docs/formats/dbf.html +87 -0
  35. package/www/docs/formats/dbf.html.md +39 -0
  36. package/www/docs/formats/flatgeobuf.html +85 -0
  37. package/www/docs/formats/flatgeobuf.html.md +41 -0
  38. package/www/docs/formats/geojson.html +107 -0
  39. package/www/docs/formats/geojson.html.md +65 -0
  40. package/www/docs/formats/geopackage.html +87 -0
  41. package/www/docs/formats/geopackage.html.md +42 -0
  42. package/www/docs/formats/json.html +83 -0
  43. package/www/docs/formats/json.html.md +35 -0
  44. package/www/docs/formats/kml.html +82 -0
  45. package/www/docs/formats/kml.html.md +39 -0
  46. package/www/docs/formats/overview.html +191 -0
  47. package/www/docs/formats/overview.html.md +34 -0
  48. package/www/docs/formats/shapefile.html +123 -0
  49. package/www/docs/formats/shapefile.html.md +84 -0
  50. package/www/docs/formats/snapshot.html +87 -0
  51. package/www/docs/formats/snapshot.html.md +39 -0
  52. package/www/docs/formats/svg.html +99 -0
  53. package/www/docs/formats/svg.html.md +51 -0
  54. package/www/docs/formats/topojson.html +102 -0
  55. package/www/docs/formats/topojson.html.md +54 -0
  56. package/www/docs/gallery/index.html +80 -0
  57. package/www/docs/gallery/index.html.md +29 -0
  58. package/www/docs/guides/combining-layers.html +109 -0
  59. package/www/docs/guides/combining-layers.html.md +89 -0
  60. package/www/docs/guides/expressions.html +600 -0
  61. package/www/docs/guides/expressions.html.md +376 -0
  62. package/www/docs/guides/programmatic.html +117 -0
  63. package/www/docs/guides/programmatic.html.md +91 -0
  64. package/www/docs/guides/projections.html +158 -0
  65. package/www/docs/guides/projections.html.md +118 -0
  66. package/www/docs/guides/simplification.html +110 -0
  67. package/www/docs/guides/simplification.html.md +96 -0
  68. package/www/docs/guides/topology.html +90 -0
  69. package/www/docs/guides/topology.html.md +63 -0
  70. package/www/docs/images/cal-counties.png +0 -0
  71. package/www/docs/images/simplification-detail.png +0 -0
  72. package/www/docs/images/simplification-dp.png +0 -0
  73. package/www/docs/images/simplification-mod2.png +0 -0
  74. package/www/docs/images/tiger-counties.png +0 -0
  75. package/www/docs/index.html +101 -0
  76. package/www/docs/index.html.md +59 -0
  77. package/www/docs/reference.html +1302 -0
  78. package/www/docs/reference.html.md +1817 -0
  79. package/www/docs/whats-new.html +77 -0
  80. package/www/docs/whats-new.html.md +59 -0
  81. package/www/index.html +47 -4
  82. package/www/llms-full.txt +4053 -0
  83. package/www/llms.txt +55 -0
  84. package/www/mapshaper-gui.js +299 -5
  85. package/www/mapshaper.js +363 -62
  86. package/www/page.css +189 -11
  87. package/www/privacy.html +1 -112
  88. package/www/sponsor.html +4 -164
  89. package/www/terms.html +1 -112
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -25,6 +25,8 @@
25
25
  "scripts": {
26
26
  "test": "mocha test",
27
27
  "build": "rollup --config",
28
+ "docs": "node build-docs.mjs",
29
+ "roadmap": "node build-roadmap.mjs",
28
30
  "lint": "eslint --ext mjs src/",
29
31
  "prepublishOnly": "npm test; ./pre-publish",
30
32
  "postpublish": "./release_web_ui; ./release_github_version",
@@ -72,6 +74,8 @@
72
74
  "@rollup/plugin-node-resolve": "^16.0.1",
73
75
  "csv-spectrum": "^1.0.0",
74
76
  "eslint": "^8.16.0",
77
+ "highlight.js": "^11.11.1",
78
+ "marked": "^18.0.2",
75
79
  "mocha": "^10.2.0",
76
80
  "rollup": "^4.44.1",
77
81
  "rollup-plugin-polyfill-node": "^0.13.0",
@@ -81,7 +85,7 @@
81
85
  "mocha": {
82
86
  "reporter": "dot",
83
87
  "node-option": [
84
- "experimental-loader=./test/_loader.js"
88
+ "import=./test/_register.mjs"
85
89
  ],
86
90
  "check-leaks": true,
87
91
  "parallel": true,
package/www/.nojekyll ADDED
File without changes
@@ -0,0 +1,179 @@
1
+ /* Shared styles for the standalone "static" pages on mapshaper.org
2
+ * (sponsor.html, privacy.html, terms.html). These pages live outside
3
+ * the docs build pipeline and outside the web app's own stylesheet, so
4
+ * they each used to inline their own copy of these rules; this file
5
+ * consolidates them.
6
+ */
7
+
8
+ @font-face {
9
+ font-family: 'SourceSans3';
10
+ src: url('SourceSans3-VariableFont_wght.ttf') format('truetype');
11
+ font-display: swap;
12
+ }
13
+
14
+ html, body {
15
+ margin: 0;
16
+ padding: 0;
17
+ background-color: #f8fdff;
18
+ font: 16px/1.55 'SourceSans3', Arial, sans-serif;
19
+ color: #333;
20
+ }
21
+
22
+ a { color: #10699b; }
23
+ a:hover { color: #1A6A96; }
24
+
25
+ /* --- Header ---------------------------------------------------------- */
26
+
27
+ .page-header {
28
+ background-color: #1385B7;
29
+ color: white;
30
+ padding: 0 11px;
31
+ height: 30px;
32
+ display: flex;
33
+ align-items: center;
34
+ justify-content: space-between;
35
+ }
36
+
37
+ .mapshaper-logo,
38
+ .mapshaper-logo:hover {
39
+ font-weight: 600;
40
+ font-size: 17px;
41
+ color: white;
42
+ text-decoration: none;
43
+ }
44
+
45
+ .mapshaper-logo .logo-highlight {
46
+ color: #ffa;
47
+ }
48
+
49
+ .header-back {
50
+ color: white;
51
+ text-decoration: none;
52
+ font-size: 14px;
53
+ height: 30px;
54
+ display: inline-flex;
55
+ align-items: center;
56
+ padding: 0 10px;
57
+ }
58
+
59
+ .header-back:hover {
60
+ background-color: #1A6A96;
61
+ color: white;
62
+ }
63
+
64
+ /* --- Page body ------------------------------------------------------- */
65
+
66
+ main {
67
+ max-width: 640px;
68
+ margin: 0 auto;
69
+ padding: 40px 24px 80px;
70
+ }
71
+
72
+ h1 {
73
+ font-weight: 600;
74
+ font-size: 32px;
75
+ margin: 0 0 12px;
76
+ color: #222;
77
+ }
78
+
79
+ h2 {
80
+ font-weight: 600;
81
+ font-size: 20px;
82
+ margin: 32px 0 12px;
83
+ color: #222;
84
+ }
85
+
86
+ p { margin: 0 0 14px; }
87
+
88
+ ul {
89
+ padding-left: 20px;
90
+ margin: 8px 0 16px;
91
+ }
92
+
93
+ ul li { margin-bottom: 6px; }
94
+
95
+ .updated {
96
+ color: #888;
97
+ font-size: 14px;
98
+ margin: 0 0 28px;
99
+ }
100
+
101
+ .footer-note {
102
+ margin-top: 48px;
103
+ padding-top: 24px;
104
+ border-top: 1px solid #e0e8ec;
105
+ font-size: 14px;
106
+ color: #777;
107
+ }
108
+
109
+ .footer-note a { color: #777; }
110
+
111
+ /* --- Sponsor-page extras --------------------------------------------- */
112
+
113
+ .tagline {
114
+ font-size: 17px;
115
+ color: #555;
116
+ margin: 0 0 32px;
117
+ }
118
+
119
+ .sponsor-buttons {
120
+ display: flex;
121
+ gap: 12px;
122
+ flex-wrap: wrap;
123
+ margin: 24px 0 12px;
124
+ }
125
+
126
+ .btn {
127
+ display: inline-block;
128
+ padding: 12px 22px;
129
+ background-color: #1385B7;
130
+ color: white;
131
+ text-decoration: none;
132
+ font-weight: 600;
133
+ font-size: 16px;
134
+ border-radius: 3px;
135
+ transition: background-color 0.15s;
136
+ }
137
+
138
+ .btn:hover {
139
+ background-color: #1A6A96;
140
+ color: white;
141
+ }
142
+
143
+ .btn.btn-secondary { background-color: #444; }
144
+ .btn.btn-secondary:hover { background-color: #222; }
145
+
146
+ .sponsor-note {
147
+ font-size: 14px;
148
+ color: #666;
149
+ margin: 0 0 32px;
150
+ }
151
+
152
+ ul.support-list {
153
+ padding-left: 20px;
154
+ margin: 8px 0 24px;
155
+ }
156
+
157
+ ul.support-list li { margin-bottom: 6px; }
158
+
159
+ .feedback-cta {
160
+ background-color: #fff;
161
+ border-left: 3px solid #1385B7;
162
+ padding: 14px 18px;
163
+ margin: 24px 0;
164
+ }
165
+
166
+ .signature {
167
+ margin-top: 32px;
168
+ color: #555;
169
+ font-style: italic;
170
+ }
171
+
172
+ /* --- Small screens --------------------------------------------------- */
173
+
174
+ @media (max-width: 480px) {
175
+ main { padding: 24px 18px 60px; }
176
+ h1 { font-size: 26px; }
177
+ .sponsor-buttons { flex-direction: column; align-items: stretch; }
178
+ .btn { text-align: center; }
179
+ }
@@ -0,0 +1,213 @@
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
+ })();