mapshaper 0.6.120 → 0.6.121

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
@@ -11723,7 +11723,11 @@
11723
11723
  version: 1,
11724
11724
  created: 'YYYY-MM-DDTHH:mm:ss.sssZ', // ISO string
11725
11725
  datasets: [],
11726
- gui: {} // see gui-session-snapshot-control.mjs
11726
+ gui: {}, // see gui-session-snapshot-control.mjs
11727
+ history: { // optional; only present in snapshots created by the GUI
11728
+ commands: ['-i foo.shp', '-simplify 10%', ...],
11729
+ savedAtIndex: 0 // index of the first command after the last save boundary
11730
+ }
11727
11731
  }
11728
11732
  */
11729
11733
 
@@ -11747,12 +11751,16 @@
11747
11751
  // exporting from command line: { compact: true, file: 'tmp.msx', final: true }
11748
11752
  // exporting from gui export menu: {compact: true, format: 'msx'}
11749
11753
  // saving gui temp snapshot: {compact: false}
11754
+ // opts.history: optional GUI session history captured by SessionHistory#getHistorySnapshot
11750
11755
  async function exportDatasetsToPack(datasets, opts) {
11751
11756
  var obj = {
11752
11757
  version: 1,
11753
11758
  created: (new Date).toISOString(),
11754
11759
  datasets: await Promise.all(datasets.map(dataset => exportDataset(dataset, opts)))
11755
11760
  };
11761
+ if (opts.history) {
11762
+ obj.history = opts.history;
11763
+ }
11756
11764
  return obj;
11757
11765
  }
11758
11766
 
@@ -51131,7 +51139,7 @@ ${svg}
51131
51139
  });
51132
51140
  }
51133
51141
 
51134
- var version = "0.6.120";
51142
+ var version = "0.6.121";
51135
51143
 
51136
51144
  // Parse command line args into commands and run them
51137
51145
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.120",
3
+ "version": "0.6.121",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
package/www/CNAME ADDED
@@ -0,0 +1 @@
1
+ mapshaper.org
package/www/index.html CHANGED
@@ -63,7 +63,7 @@
63
63
  <span class="console-btn header-btn btn">Console</span><span class="display-btn header-btn btn">Display</span><span class="separator"></span><span class="separator"></span></span><span class="simplify-btn header-btn btn">Simplify</span><span class="separator"></span><span class="export-btn header-btn btn">Export</span>
64
64
  </div>
65
65
  <div id="splash-buttons" class="page-header-buttons">
66
- <a href="https://github.com/mbloch/mapshaper/wiki"><span id="wiki-btn" class="header-btn btn">Wiki</span></a><span class="separator"></span><a href="https://github.com/mbloch/mapshaper"><span id="github-btn" class="header-btn btn">GitHub</span></a><span class="separator"></span><a href="sponsor.html"><span id="sponsor-btn" class="header-btn btn"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg> Sponsor</span></a>
66
+ <a href="https://github.com/mbloch/mapshaper/wiki"><span id="wiki-btn" class="header-btn btn">Wiki</span></a><span class="separator"></span><a href="https://github.com/mbloch/mapshaper"><span id="github-btn" class="header-btn btn">GitHub</span></a><span class="separator"></span><a href="sponsor.html"><span id="sponsor-btn" class="header-btn btn"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg> Become a sponsor</span></a>
67
67
  </div>
68
68
  </div>
69
69
 
@@ -1580,6 +1580,19 @@
1580
1580
  action: saveSnapshot
1581
1581
  });
1582
1582
 
1583
+ if (!gui.session.isEmpty()) {
1584
+ // Surface the console "history" command via the snapshot menu so users
1585
+ // can browse the session's command history without knowing about the
1586
+ // console keyword. Hidden when there's nothing to show.
1587
+ addMenuLink({
1588
+ slug: 'history',
1589
+ label: 'view session history',
1590
+ action: function(gui) {
1591
+ gui.console.runCommand('history');
1592
+ }
1593
+ });
1594
+ }
1595
+
1583
1596
  // var available = await getAvailableStorage();
1584
1597
  // if (available) {
1585
1598
  // El('div').addClass('save-menu-entry').text(available + ' available').appendTo(menu);
@@ -1703,10 +1716,24 @@
1703
1716
  }
1704
1717
  gui.model.clear();
1705
1718
  importDatasets(data.datasets, gui);
1719
+ // Reinstate the session history (including its saved/unsaved boundary) that
1720
+ // was in effect when the snapshot was taken. If the snapshot has no history
1721
+ // field (e.g. older snapshots), this resets to a clean state.
1722
+ gui.session.restoreHistorySnapshot(data.history);
1706
1723
  gui.clearMode();
1707
1724
  }
1708
1725
 
1709
- // Add datasets to the current project
1726
+ // Import datasets from a packed .msx buffer.
1727
+ // Behavior depends on whether the current session contains data:
1728
+ // - empty session: full project restore -- datasets and any embedded session
1729
+ // history are loaded as if continuing the original session.
1730
+ // - non-empty session: merge -- datasets are added to the current project,
1731
+ // but any embedded session history is discarded (the imported commands
1732
+ // assume different layer indices and a different starting state, so
1733
+ // merging them into the current session would produce a misleading history).
1734
+ // Returns true if a full restore occurred, false if a merge occurred. The
1735
+ // caller uses this to decide whether to record an additional -i command in
1736
+ // the current session's history (see gui-import-control.mjs).
1710
1737
  // TODO: figure out if interface data should be imported (e.g. should
1711
1738
  // visibility flag of imported layers be imported)
1712
1739
  async function importSessionData(buf, gui) {
@@ -1714,7 +1741,12 @@
1714
1741
  buf = new Uint8Array(buf);
1715
1742
  }
1716
1743
  var data = await internal.unpackSessionData(buf);
1744
+ var fullRestore = gui.model.isEmpty();
1717
1745
  importDatasets(data.datasets, gui);
1746
+ if (fullRestore) {
1747
+ gui.session.restoreHistorySnapshot(data.history);
1748
+ }
1749
+ return fullRestore;
1718
1750
  }
1719
1751
 
1720
1752
  function importDatasets(datasets, gui) {
@@ -1730,7 +1762,13 @@
1730
1762
  if (!lyr) return null; // no data -- no snapshot
1731
1763
  // compact: true applies compression to vector coordinates, for ~30% reduction
1732
1764
  // in file size in a typical polygon or polyline file, but longer processing time
1733
- var opts = {compact: false, active_layer: lyr};
1765
+ // history: capture session commands + saved/unsaved boundary so the history
1766
+ // can be reinstated if this snapshot is restored or re-imported later.
1767
+ var opts = {
1768
+ compact: false,
1769
+ active_layer: lyr,
1770
+ history: gui.session.getHistorySnapshot()
1771
+ };
1734
1772
  var datasets = gui.model.getDatasets();
1735
1773
  var obj = await internal.exportDatasetsToPack(datasets, opts);
1736
1774
  obj.gui = getGuiState(gui);
@@ -2348,7 +2386,16 @@
2348
2386
  await wait(35);
2349
2387
  }
2350
2388
  if (group[internal.PACKAGE_EXT]) {
2351
- await importSessionData(group[internal.PACKAGE_EXT].content, gui);
2389
+ var fullRestore = await importSessionData(group[internal.PACKAGE_EXT].content, gui);
2390
+ importCount++;
2391
+ // Skip recording an -i command if the .msx import was a full project
2392
+ // restore: the previous session's history (including the original -i)
2393
+ // has already been reinstated, so adding another entry here would be
2394
+ // misleading. For the merge case, record the import as a regular -i
2395
+ // so the snapshot contributes a CLI-replayable entry to the session.
2396
+ if (!fullRestore) {
2397
+ gui.session.fileImported(group[internal.PACKAGE_EXT].filename, optStr);
2398
+ }
2352
2399
  } else if (await importDataset(group, groupImportOpts)) {
2353
2400
  importCount++;
2354
2401
  gui.session.fileImported(group.filename, optStr);
@@ -3958,13 +4005,18 @@
3958
4005
  // expose this function, so other components can run commands (e.g. box tool)
3959
4006
  this.runMapshaperCommands = runMapshaperCommands;
3960
4007
 
3961
- this.runInitialCommands = function(str) {
4008
+ // Open the console (if closed) and run a command, as if the user had
4009
+ // typed it. Used by UI controls that surface console functionality, e.g.
4010
+ // the "view command history" link in the snapshot menu.
4011
+ this.runCommand = function(str) {
3962
4012
  str = str.trim();
3963
4013
  if (!str) return;
3964
4014
  turnOn();
3965
4015
  submit(str);
3966
4016
  };
3967
4017
 
4018
+ this.runInitialCommands = this.runCommand;
4019
+
3968
4020
  consoleMessage(PROMPT);
3969
4021
  gui.keyboard.on('keydown', onKeyDown);
3970
4022
  window.addEventListener('beforeunload', saveHistory); // save history if console is open on refresh
@@ -4783,6 +4835,17 @@
4783
4835
  await loadGeopackageLib();
4784
4836
  }
4785
4837
  opts.active_layer = gui.model.getActiveLayer().layer; // kludge to support restoring active layer in gui
4838
+ if (opts.format == internal.PACKAGE_EXT) {
4839
+ // Embed the session history in .msx exports so that re-importing the
4840
+ // file into a fresh session restores the original command history.
4841
+ // The .msx file itself is a durable artifact, so mark every captured
4842
+ // command as "saved" -- a user reloading the file shouldn't see an
4843
+ // unsaved-changes warning for work that lives in the file they just
4844
+ // opened.
4845
+ var snapshot = gui.session.getHistorySnapshot();
4846
+ snapshot.savedAtIndex = snapshot.commands.length;
4847
+ opts.history = snapshot;
4848
+ }
4786
4849
  try {
4787
4850
  var files = await internal.exportTargetLayers(model, targets, opts);
4788
4851
  } catch(e) {
@@ -5592,20 +5655,54 @@
5592
5655
 
5593
5656
  function SessionHistory(gui) {
5594
5657
  var commands = [];
5658
+ // index of first command after the last "save" boundary; commands at indices
5659
+ // [savedAtIndex .. commands.length) are considered unsaved
5660
+ var savedAtIndex = 0;
5595
5661
  // commands that can be ignored when checking for unsaved changes
5596
- var nonEditingCommands = 'i,target,info,version,verbose,projections,inspect,help,h,encodings,calc'.split(',');
5662
+ var nonEditingCommands = 'i,target,info,version,verbose,projections,inspect,help,h,encodings,calc,comment'.split(',');
5597
5663
 
5598
5664
  this.unsavedChanges = function() {
5599
- var cmd, cmdName;
5600
- for (var i=commands.length - 1; i >= 0; i--) {
5601
- cmdName = getCommandName(commands[i]);
5602
- if (cmdName == 'o') break;
5665
+ for (var i = commands.length - 1; i >= savedAtIndex; i--) {
5666
+ var cmdName = getCommandName(commands[i]);
5603
5667
  if (nonEditingCommands.includes(cmdName)) continue;
5604
5668
  return true;
5605
5669
  }
5606
5670
  return false;
5607
5671
  };
5608
5672
 
5673
+ this.isEmpty = function() {
5674
+ return commands.length === 0;
5675
+ };
5676
+
5677
+ // Mark the current end of the history as a "saved" boundary -- called after
5678
+ // data has been written somewhere durable (e.g. an -o export). Snapshots
5679
+ // are session-scoped and are NOT durable, so creating one does not mark saved.
5680
+ this.markSaved = function() {
5681
+ savedAtIndex = commands.length;
5682
+ };
5683
+
5684
+ // Capture a serializable copy of the history for inclusion in a snapshot.
5685
+ this.getHistorySnapshot = function() {
5686
+ return {
5687
+ commands: commands.slice(),
5688
+ savedAtIndex: savedAtIndex
5689
+ };
5690
+ };
5691
+
5692
+ // Replace the current history with one captured by getHistorySnapshot().
5693
+ // Used when restoring an in-session snapshot. If the snapshot has no history
5694
+ // (e.g. older snapshots, or external .msx files), starts from a clean state.
5695
+ this.restoreHistorySnapshot = function(obj) {
5696
+ if (obj && Array.isArray(obj.commands)) {
5697
+ commands = obj.commands.slice();
5698
+ savedAtIndex = typeof obj.savedAtIndex == 'number' ?
5699
+ Math.min(obj.savedAtIndex, commands.length) : commands.length;
5700
+ } else {
5701
+ commands = [];
5702
+ savedAtIndex = 0;
5703
+ }
5704
+ };
5705
+
5609
5706
  this.fileImported = function(file, optStr) {
5610
5707
  var cmd = '-i ' + file;
5611
5708
  if (optStr) {
@@ -5665,6 +5762,8 @@
5665
5762
  cmd += ' ' + optStr;
5666
5763
  }
5667
5764
  commands.push(cmd);
5765
+ // -o writes data to a durable location, so treat this as a save boundary
5766
+ savedAtIndex = commands.length;
5668
5767
  };
5669
5768
 
5670
5769
  this.setTargetLayer = function(lyr) {
package/www/mapshaper.js CHANGED
@@ -11723,7 +11723,11 @@
11723
11723
  version: 1,
11724
11724
  created: 'YYYY-MM-DDTHH:mm:ss.sssZ', // ISO string
11725
11725
  datasets: [],
11726
- gui: {} // see gui-session-snapshot-control.mjs
11726
+ gui: {}, // see gui-session-snapshot-control.mjs
11727
+ history: { // optional; only present in snapshots created by the GUI
11728
+ commands: ['-i foo.shp', '-simplify 10%', ...],
11729
+ savedAtIndex: 0 // index of the first command after the last save boundary
11730
+ }
11727
11731
  }
11728
11732
  */
11729
11733
 
@@ -11747,12 +11751,16 @@
11747
11751
  // exporting from command line: { compact: true, file: 'tmp.msx', final: true }
11748
11752
  // exporting from gui export menu: {compact: true, format: 'msx'}
11749
11753
  // saving gui temp snapshot: {compact: false}
11754
+ // opts.history: optional GUI session history captured by SessionHistory#getHistorySnapshot
11750
11755
  async function exportDatasetsToPack(datasets, opts) {
11751
11756
  var obj = {
11752
11757
  version: 1,
11753
11758
  created: (new Date).toISOString(),
11754
11759
  datasets: await Promise.all(datasets.map(dataset => exportDataset(dataset, opts)))
11755
11760
  };
11761
+ if (opts.history) {
11762
+ obj.history = opts.history;
11763
+ }
11756
11764
  return obj;
11757
11765
  }
11758
11766
 
@@ -51131,7 +51139,7 @@ ${svg}
51131
51139
  });
51132
51140
  }
51133
51141
 
51134
- var version = "0.6.120";
51142
+ var version = "0.6.121";
51135
51143
 
51136
51144
  // Parse command line args into commands and run them
51137
51145
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -0,0 +1,203 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Privacy Policy &mdash; mapshaper</title>
7
+ <meta name="description" content="Privacy policy for mapshaper, free open-source software for editing geographic data.">
8
+ <link rel="icon" type="image/png" href="images/icon.png">
9
+
10
+ <style>
11
+ @font-face {
12
+ font-family: 'SourceSans3';
13
+ src: url('assets/SourceSans3-VariableFont_wght.ttf') format('truetype');
14
+ font-display: swap;
15
+ }
16
+
17
+ html, body {
18
+ margin: 0;
19
+ padding: 0;
20
+ background-color: #f8fdff;
21
+ font: 16px/1.55 'SourceSans3', Arial, sans-serif;
22
+ color: #333;
23
+ }
24
+
25
+ a { color: #10699b; }
26
+ a:hover { color: #1A6A96; }
27
+
28
+ .page-header {
29
+ background-color: #1385B7;
30
+ color: white;
31
+ padding: 0 11px;
32
+ height: 29px;
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: space-between;
36
+ }
37
+
38
+ .mapshaper-logo,
39
+ .mapshaper-logo:hover {
40
+ font-weight: 600;
41
+ font-size: 17px;
42
+ color: white;
43
+ text-decoration: none;
44
+ }
45
+
46
+ .mapshaper-logo .logo-highlight {
47
+ color: #ffa;
48
+ }
49
+
50
+ .header-back {
51
+ color: white;
52
+ text-decoration: none;
53
+ font-size: 14px;
54
+ height: 29px;
55
+ display: inline-flex;
56
+ align-items: center;
57
+ padding: 0 10px;
58
+ }
59
+
60
+ .header-back:hover {
61
+ background-color: #1A6A96;
62
+ color: white;
63
+ }
64
+
65
+ main {
66
+ max-width: 640px;
67
+ margin: 0 auto;
68
+ padding: 40px 24px 80px;
69
+ }
70
+
71
+ h1 {
72
+ font-weight: 600;
73
+ font-size: 32px;
74
+ margin: 0 0 8px;
75
+ color: #222;
76
+ }
77
+
78
+ .updated {
79
+ color: #888;
80
+ font-size: 14px;
81
+ margin: 0 0 28px;
82
+ }
83
+
84
+ h2 {
85
+ font-weight: 600;
86
+ font-size: 20px;
87
+ margin: 32px 0 10px;
88
+ color: #222;
89
+ }
90
+
91
+ p {
92
+ margin: 0 0 14px;
93
+ }
94
+
95
+ ul {
96
+ padding-left: 20px;
97
+ margin: 8px 0 16px;
98
+ }
99
+
100
+ ul li {
101
+ margin-bottom: 6px;
102
+ }
103
+
104
+ .footer-note {
105
+ margin-top: 48px;
106
+ padding-top: 24px;
107
+ border-top: 1px solid #e0e8ec;
108
+ font-size: 14px;
109
+ color: #777;
110
+ }
111
+
112
+ .footer-note a {
113
+ color: #777;
114
+ }
115
+
116
+ @media (max-width: 480px) {
117
+ main { padding: 24px 18px 60px; }
118
+ h1 { font-size: 26px; }
119
+ }
120
+ </style>
121
+ </head>
122
+ <body>
123
+
124
+ <header class="page-header">
125
+ <a href="." class="mapshaper-logo">map<span class="logo-highlight">shaper</span></a>
126
+ <a href="." class="header-back">&larr; Back to mapshaper</a>
127
+ </header>
128
+
129
+ <main>
130
+
131
+ <h1>Privacy Policy</h1>
132
+ <p class="updated">Last updated: April 2026</p>
133
+
134
+ <h2>Overview</h2>
135
+ <p>Mapshaper is free, open-source software for editing geographic data, hosted at mapshaper.org. This policy explains what information is collected when you use the site, how it is used, and what choices you have.</p>
136
+ <p>We collect as little information as possible. We do not sell or share your information with advertisers or data brokers.</p>
137
+
138
+ <h2>Information collected</h2>
139
+ <p><strong>On mapshaper.org:</strong></p>
140
+ <ul>
141
+ <li><strong>Web analytics</strong> via Google Analytics. This includes pages visited, browser and device type, approximate location (typically city-level, derived from your IP address), and timestamps. The data helps us understand how the site is used.</li>
142
+ <li><strong>Basemap requests.</strong> When the in-app basemap loads, the application requests assets from Mapbox servers. As with any web request, this transmits your IP address to Mapbox to facilitate data delivery and session accounting.</li>
143
+ <li><strong>Standard server logs</strong> are kept by GitHub Pages, the hosting provider. As with any web request, this records your IP address, browser user-agent string, and the URL requested. GitHub uses these for traffic analysis, security monitoring, and abuse prevention.</li>
144
+ </ul>
145
+ <p><strong>On the support page:</strong></p>
146
+ <ul>
147
+ <li><strong>Email address</strong>, if you provide one for a payment receipt. The address is handled by the payment processor (Stripe via Ko-fi, or GitHub Sponsors), not stored by mapshaper.</li>
148
+ <li><strong>Name and payment information</strong> are handled directly by the payment processor.</li>
149
+ </ul>
150
+
151
+ <h2>Information not collected</h2>
152
+ <ul>
153
+ <li>Files you load into mapshaper for editing remain in your browser. They are not uploaded to our servers or to any third party.</li>
154
+ <li>We do not maintain user accounts or store any user data on our own servers.</li>
155
+ <li>We do not sell, rent, or share your information with advertisers or data brokers.</li>
156
+ </ul>
157
+
158
+ <h2>Third parties</h2>
159
+ <p>The following services receive data when you use mapshaper:</p>
160
+ <ul>
161
+ <li><strong>Google Analytics</strong> &mdash; site usage analytics. <a href="https://policies.google.com/privacy">Privacy Policy</a></li>
162
+ <li><strong>Mapbox</strong> &mdash; basemap tiles. <a href="https://www.mapbox.com/legal/privacy">Privacy Policy</a></li>
163
+ <li><strong>GitHub</strong> &mdash; site hosting (GitHub Pages) and sponsorship platform. <a href="https://docs.github.com/site-policy/privacy-policies/github-general-privacy-statement">Privacy Statement</a></li>
164
+ <li><strong>Ko-fi</strong> &mdash; payment processing. <a href="https://ko-fi.com/manage/privacy">Privacy Policy</a></li>
165
+ <li><strong>Stripe</strong> &mdash; payment processing for Ko-fi contributions. <a href="https://stripe.com/privacy">Privacy Policy</a></li>
166
+ </ul>
167
+
168
+ <h2>Cookies</h2>
169
+ <p>Google Analytics sets cookies to track usage. You can opt out by:</p>
170
+ <ul>
171
+ <li>Adjusting your browser settings to block third-party cookies</li>
172
+ <li>Sending a Do Not Track signal</li>
173
+ <li>Installing the Google Analytics opt-out browser extension</li>
174
+ <li>Using a privacy-focused browser or extension that blocks analytics</li>
175
+ </ul>
176
+
177
+ <h2>EU and UK users (GDPR)</h2>
178
+ <p>If you are in the European Union or United Kingdom, the General Data Protection Regulation gives you the right to access, correct, delete, or export the personal data we hold about you, and to object to processing. Because we do not maintain a user database, the data we hold about any individual is limited to analytics events and (if you have contributed) records held by the payment processors.</p>
179
+ <p>To exercise these rights, contact us using the information below. The legal basis for processing is legitimate interest (analytics) and contract (payment processing).</p>
180
+
181
+ <h2>California residents (CCPA)</h2>
182
+ <p>California residents have the right to know what personal information we collect, to request its deletion, and to opt out of any sale of personal information. We do not sell personal information.</p>
183
+
184
+ <h2>Security</h2>
185
+ <p>All connections to mapshaper.org use TLS encryption (HTTPS). Payment information is handled by PCI-compliant processors (Stripe). We do not store payment information on our own systems.</p>
186
+
187
+ <h2>Children</h2>
188
+ <p>Mapshaper is not directed at children under 13. We do not knowingly collect personal information from children.</p>
189
+
190
+ <h2>Changes to this policy</h2>
191
+ <p>This policy may be updated from time to time. The &ldquo;Last updated&rdquo; date at the top reflects the most recent change. We encourage you to review the policy periodically.</p>
192
+
193
+ <h2>Contact</h2>
194
+ <p>Questions about privacy can be sent to the maintainer via <a href="https://github.com/mbloch/mapshaper/issues">GitHub</a> or <a href="https://ko-fi.com/mapshaper">Ko-fi</a>.</p>
195
+
196
+ <div class="footer-note">
197
+ <a href="terms.html">Terms of Service</a> &middot; <a href="sponsor.html">Support mapshaper</a> &middot; <a href=".">Back to mapshaper</a>
198
+ </div>
199
+
200
+ </main>
201
+
202
+ </body>
203
+ </html>
package/www/sponsor.html CHANGED
@@ -156,6 +156,10 @@
156
156
  color: #777;
157
157
  }
158
158
 
159
+ .footer-note a {
160
+ color: #777;
161
+ }
162
+
159
163
  @media (max-width: 480px) {
160
164
  main { padding: 24px 18px 60px; }
161
165
  h1 { font-size: 26px; }
@@ -167,8 +171,8 @@
167
171
  <body>
168
172
 
169
173
  <header class="page-header">
170
- <a href="index.html" class="mapshaper-logo">map<span class="logo-highlight">shaper</span></a>
171
- <a href="index.html" class="header-back">&larr; Back to mapshaper</a>
174
+ <a href="." class="mapshaper-logo">map<span class="logo-highlight">shaper</span></a>
175
+ <a href="." class="header-back">&larr; Back to mapshaper</a>
172
176
  </header>
173
177
 
174
178
  <main>
@@ -207,6 +211,8 @@
207
211
 
208
212
  <div class="footer-note">
209
213
  Mapshaper&rsquo;s basemap services are generously provided by <a href="https://www.mapbox.com">Mapbox</a>.
214
+ <br>
215
+ <a href="privacy.html">Privacy Policy</a> &middot; <a href="terms.html">Terms of Service</a>
210
216
  </div>
211
217
 
212
218
  </main>
package/www/terms.html ADDED
@@ -0,0 +1,188 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Terms of Service &mdash; mapshaper</title>
7
+ <meta name="description" content="Terms of service for mapshaper, free open-source software for editing geographic data.">
8
+ <link rel="icon" type="image/png" href="images/icon.png">
9
+
10
+ <style>
11
+ @font-face {
12
+ font-family: 'SourceSans3';
13
+ src: url('assets/SourceSans3-VariableFont_wght.ttf') format('truetype');
14
+ font-display: swap;
15
+ }
16
+
17
+ html, body {
18
+ margin: 0;
19
+ padding: 0;
20
+ background-color: #f8fdff;
21
+ font: 16px/1.55 'SourceSans3', Arial, sans-serif;
22
+ color: #333;
23
+ }
24
+
25
+ a { color: #10699b; }
26
+ a:hover { color: #1A6A96; }
27
+
28
+ .page-header {
29
+ background-color: #1385B7;
30
+ color: white;
31
+ padding: 0 11px;
32
+ height: 29px;
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: space-between;
36
+ }
37
+
38
+ .mapshaper-logo,
39
+ .mapshaper-logo:hover {
40
+ font-weight: 600;
41
+ font-size: 17px;
42
+ color: white;
43
+ text-decoration: none;
44
+ }
45
+
46
+ .mapshaper-logo .logo-highlight {
47
+ color: #ffa;
48
+ }
49
+
50
+ .header-back {
51
+ color: white;
52
+ text-decoration: none;
53
+ font-size: 14px;
54
+ height: 29px;
55
+ display: inline-flex;
56
+ align-items: center;
57
+ padding: 0 10px;
58
+ }
59
+
60
+ .header-back:hover {
61
+ background-color: #1A6A96;
62
+ color: white;
63
+ }
64
+
65
+ main {
66
+ max-width: 640px;
67
+ margin: 0 auto;
68
+ padding: 40px 24px 80px;
69
+ }
70
+
71
+ h1 {
72
+ font-weight: 600;
73
+ font-size: 32px;
74
+ margin: 0 0 8px;
75
+ color: #222;
76
+ }
77
+
78
+ .updated {
79
+ color: #888;
80
+ font-size: 14px;
81
+ margin: 0 0 28px;
82
+ }
83
+
84
+ h2 {
85
+ font-weight: 600;
86
+ font-size: 20px;
87
+ margin: 32px 0 10px;
88
+ color: #222;
89
+ }
90
+
91
+ p {
92
+ margin: 0 0 14px;
93
+ }
94
+
95
+ ul {
96
+ padding-left: 20px;
97
+ margin: 8px 0 16px;
98
+ }
99
+
100
+ ul li {
101
+ margin-bottom: 6px;
102
+ }
103
+
104
+ .footer-note {
105
+ margin-top: 48px;
106
+ padding-top: 24px;
107
+ border-top: 1px solid #e0e8ec;
108
+ font-size: 14px;
109
+ color: #777;
110
+ }
111
+
112
+ .footer-note a {
113
+ color: #777;
114
+ }
115
+
116
+ @media (max-width: 480px) {
117
+ main { padding: 24px 18px 60px; }
118
+ h1 { font-size: 26px; }
119
+ }
120
+ </style>
121
+ </head>
122
+ <body>
123
+
124
+ <header class="page-header">
125
+ <a href="." class="mapshaper-logo">map<span class="logo-highlight">shaper</span></a>
126
+ <a href="." class="header-back">&larr; Back to mapshaper</a>
127
+ </header>
128
+
129
+ <main>
130
+
131
+ <h1>Terms of Service</h1>
132
+ <p class="updated">Last updated: April 2026</p>
133
+
134
+ <h2>Acceptance</h2>
135
+ <p>By using mapshaper.org or related services (collectively, &ldquo;the service&rdquo;), you agree to these terms. If you do not agree, please do not use the service.</p>
136
+
137
+ <h2>About mapshaper</h2>
138
+ <p>Mapshaper is free, open-source software for editing geographic data, hosted at mapshaper.org and maintained by Matthew Bloch. The source code is licensed under the <a href="https://github.com/mbloch/mapshaper/blob/master/LICENSE.txt">Mozilla Public License 2.0</a>.</p>
139
+
140
+ <h2>Use of the software</h2>
141
+ <p>The software is provided under the terms of the MPL 2.0. You may use, modify, and redistribute the source code in accordance with that license. These Terms of Service govern your use of mapshaper.org as a hosted service; the MPL 2.0 governs your rights to the source code.</p>
142
+
143
+ <h2>Your responsibilities</h2>
144
+ <p>You are responsible for:</p>
145
+ <ul>
146
+ <li>The accuracy and legality of any data you load into or process with mapshaper</li>
147
+ <li>Ensuring you have the right to use any data you load (copyright, licensing, terms of source data)</li>
148
+ <li>Compliance with all applicable laws in your use of the service</li>
149
+ <li>Not using the service to violate the rights of others, transmit illegal content, or attempt to disrupt the service or its third-party providers</li>
150
+ </ul>
151
+
152
+ <h2>No warranty</h2>
153
+ <p>The service is provided &ldquo;as is&rdquo; and &ldquo;as available,&rdquo; without warranty of any kind, express or implied. We make no warranty that the service will be uninterrupted, secure, error-free, or that any output will be accurate, complete, or fit for any particular purpose.</p>
154
+ <p>Map data, basemaps, and conversion results may contain errors. Do not rely on mapshaper output for navigation, safety-critical decisions, legal boundaries, or other applications where accuracy is essential.</p>
155
+
156
+ <h2>Limitation of liability</h2>
157
+ <p>To the maximum extent permitted by law, mapshaper, its maintainer, and contributors are not liable for any direct, indirect, incidental, consequential, special, or punitive damages arising from your use of (or inability to use) the service, including but not limited to loss of data, business interruption, lost profits, or any other commercial damages.</p>
158
+
159
+ <h2>Contributions</h2>
160
+ <p>Contributions made via Ko-fi or GitHub Sponsors are voluntary. They are not tax-deductible (mapshaper is not a registered charity). Contributions are non-refundable except in cases of fraud or technical error. Refund requests should be directed to the payment processor (Ko-fi, Stripe, or GitHub).</p>
161
+
162
+ <h2>Third-party services</h2>
163
+ <p>The service relies on third-party providers including:</p>
164
+ <ul>
165
+ <li><strong>GitHub Pages</strong> for site hosting</li>
166
+ <li><strong>Mapbox</strong> for basemap tiles</li>
167
+ <li><strong>Google Analytics</strong> for usage analytics</li>
168
+ <li><strong>Stripe</strong>, <strong>Ko-fi</strong>, and <strong>GitHub Sponsors</strong> for payment processing</li>
169
+ </ul>
170
+ <p>When you interact with these services through mapshaper, their terms of service and privacy policies also apply. We are not responsible for the availability, behavior, or content of third-party services.</p>
171
+
172
+ <h2>Changes to these terms</h2>
173
+ <p>These terms may be updated. The &ldquo;Last updated&rdquo; date at the top reflects the most recent change. Continued use of the service after changes constitutes acceptance of the updated terms.</p>
174
+
175
+ <h2>Governing law</h2>
176
+ <p>These terms are governed by the laws of the State of New York, United States, without regard to conflict-of-law principles.</p>
177
+
178
+ <h2>Contact</h2>
179
+ <p>Questions about these terms can be sent to the maintainer via <a href="https://github.com/mbloch/mapshaper/issues">GitHub</a> or <a href="https://ko-fi.com/mapshaper">Ko-fi</a>.</p>
180
+
181
+ <div class="footer-note">
182
+ <a href="privacy.html">Privacy Policy</a> &middot; <a href="sponsor.html">Support mapshaper</a> &middot; <a href=".">Back to mapshaper</a>
183
+ </div>
184
+
185
+ </main>
186
+
187
+ </body>
188
+ </html>