Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
522 commits
Select commit Hold shift + click to select a range
c282250
Update basemap api keys
mbloch Jun 28, 2025
3a01aa0
v0.6.105
mbloch Jun 28, 2025
142e7d0
Wrap paths with quotes during exec
ChlodAlejandro Jun 30, 2025
168f186
Fix for reported problem running build on linux
mbloch Jul 1, 2025
aa69aa5
Improve hover and selection styles
mbloch Jul 1, 2025
c6f9197
v0.6.106
mbloch Jul 1, 2025
7750da6
v0.6.106
mbloch Jul 1, 2025
e3a7989
Merge pull request #678 from ChlodAlejandro/master
mbloch Jul 1, 2025
ce66158
readme
mbloch Jul 26, 2025
158f335
Fix performance edge case
mbloch Jul 26, 2025
4773868
Convert Basemap menu to Display menu
mbloch Jul 26, 2025
062cebb
v0.6.107
mbloch Jul 26, 2025
dadb2ed
Merge branch 'master' of github.com:mbloch/mapshaper
mbloch Jul 26, 2025
bc1c14e
v0.6.108
mbloch Jul 27, 2025
06ae121
v0.6.109
mbloch Jul 31, 2025
e268e89
v0.6.109
mbloch Jul 31, 2025
f84010f
Switch to variable font
mbloch Aug 1, 2025
991b7e7
index.html
mbloch Aug 1, 2025
26f2c29
Fix HTML validation errors and warnings
mbloch Aug 5, 2025
5001e6a
User-settable SVG coordinate precision
mbloch Aug 5, 2025
9d9fbf2
GUI projection tool WIP
mbloch Aug 5, 2025
c9c4370
Change mapshaper-gui options, old -q is now default
mbloch Aug 13, 2025
319acc3
Minor changes in zoomed-in display
mbloch Aug 13, 2025
216bd51
wip for line snip tool
mbloch Aug 13, 2025
6236194
Improvements to geometry calculations - increased robustness
mbloch Aug 13, 2025
551e444
Add keep-shapes option to -filter-slivers
mbloch Aug 13, 2025
62e3a81
v0.6.110
mbloch Aug 13, 2025
41a6dd3
minor GUI improvements
mbloch Aug 19, 2025
79dbf56
Switch to better-performing robust segment geometry
mbloch Aug 19, 2025
747a32e
v0.6.111
mbloch Aug 19, 2025
7a5e3e6
Add clean tests
mbloch Aug 20, 2025
00eb65b
[web] support dragging and pasting data file urls
mbloch Sep 1, 2025
729ebf9
Add -o show-all option for snapshots
mbloch Sep 2, 2025
80a3d69
Fix a cli command parsing bug
mbloch Sep 3, 2025
18b2a87
Support assigning to target.geojson in template expressions
mbloch Sep 12, 2025
820e527
Show eye icon in gui layer menu with single layer
mbloch Sep 12, 2025
336588d
buffer wip
mbloch Sep 12, 2025
bbbf9e6
v0.6.112
mbloch Sep 12, 2025
99199c6
Improve importing files from pasted HTML
mbloch Sep 14, 2025
346268b
Add custom basemaps to web UI
mbloch Sep 30, 2025
1f42c7f
v0.6.113
mbloch Sep 30, 2025
e053d55
v0.6.113
mbloch Sep 30, 2025
756db52
fix -join bug
mbloch Dec 17, 2025
6bbcedd
Use native imports in more tests
mbloch Mar 27, 2026
ce3f07c
Add preliminary flatgeobuf support
mbloch Mar 28, 2026
3553395
Add support for reading flatgeobuf and geopackage files
mbloch Apr 15, 2026
b63b9dc
Added flatgeobuf export
mbloch Apr 15, 2026
53dbb8e
Add GeoPackage exporting
mbloch Apr 15, 2026
63c0fcc
Update help to include the new formats
mbloch Apr 15, 2026
5fac082
v0.6.114
mbloch Apr 15, 2026
bae3785
Bump version
mbloch Apr 15, 2026
86db2e7
v0.6.115
mbloch Apr 15, 2026
196af30
Prevent concurrency errors in geopackage tests
mbloch Apr 15, 2026
8be0390
v0.6.116
mbloch Apr 15, 2026
a026d12
Add support for importing SVG files
mbloch Apr 15, 2026
4c1392c
v0.6.117
mbloch Apr 15, 2026
c99b4bb
Improved GeoPackage importing
mbloch Apr 16, 2026
42777f9
Improvements to geopackage i/o
mbloch Apr 16, 2026
7be5113
Add layer selection to cli and gui for geopackage import; bugfix
mbloch Apr 16, 2026
a334257
Refactor topology code
mbloch Apr 16, 2026
c907218
v0.6.118
mbloch Apr 16, 2026
e8688aa
Fix a link in command reference
mbloch Apr 17, 2026
9cc9fa5
Performance optimization
mbloch Apr 17, 2026
ec23576
Improve profiling setup
mbloch Apr 17, 2026
88b678d
readme, funding
mbloch Apr 17, 2026
8874d77
Add donate page
mbloch Apr 17, 2026
4a80537
v0.6.119
mbloch Apr 17, 2026
00853e4
v0.6.119
mbloch Apr 17, 2026
73b8f43
Show previously rendered image when panning/zooming a slow-rendering map
mbloch Apr 18, 2026
9cb9da1
Change 'donate' language
mbloch Apr 18, 2026
8e9da57
Map rendering optimizations
mbloch Apr 18, 2026
3389e3f
v0.6.120
mbloch Apr 18, 2026
7575384
v0.6.120
mbloch Apr 18, 2026
ae70e4d
Link language
mbloch Apr 18, 2026
1f450c7
Added privacy policy and TOS
mbloch Apr 18, 2026
56e8f4b
commit web assets
mbloch Apr 18, 2026
cd4135f
Add session history to snapshot files
mbloch Apr 18, 2026
3f8e5e1
v0.6.121
mbloch Apr 18, 2026
4c0eca6
Implement script files (files containing commands)
mbloch Apr 19, 2026
aa1c978
Add late-binding {{VAR}} interpolation with -vars and -defaults commands
mbloch Apr 20, 2026
510d5b2
Use robust dissolve by default in -dissolve and deprecate -dissolve2
mbloch Apr 20, 2026
4e94b60
v0.7.0
mbloch Apr 20, 2026
a815a42
Update REFERENCE.md
mbloch Apr 20, 2026
ac30831
v0.7.0
mbloch Apr 20, 2026
ba062bf
Stop tracking bench/ files (local-only profiling scaffolding).
mbloch Apr 20, 2026
0291312
Probe-first short-circuit in snapAndCut
mbloch Apr 20, 2026
6fee6c2
Improve Flatgeobuf and snapshot CRS support
mbloch Apr 22, 2026
1f3f754
Update testing configuration
mbloch Apr 22, 2026
5e65e09
Support importing inline CSV with -i
mbloch Apr 22, 2026
065d449
Export snapshots with targeted layers set to visible
mbloch Apr 23, 2026
3a9dc0b
Fix .kmz output
mbloch Apr 24, 2026
7eb1f56
Separate -vars and -defaults stores from global expression object
mbloch Apr 24, 2026
19eb163
Add docs pages
mbloch Apr 24, 2026
2918777
v0.7.1
mbloch Apr 24, 2026
79d2542
Fix docs site styling
mbloch Apr 24, 2026
e249778
Added hamburger menu, docs update
mbloch Apr 24, 2026
49b7bd5
Add undo/redo buttons to the GUI in relevant modes
mbloch Apr 25, 2026
de00bbf
Docs update
mbloch Apr 25, 2026
86a3f6d
v0.7.2
mbloch Apr 25, 2026
74822e9
v0.7.3 (prune npm package)
mbloch Apr 25, 2026
5fca362
Docs updates
mbloch Apr 27, 2026
f854832
Stop creating empty <g> tags in SVG output for null geometries
mbloch Apr 27, 2026
91eae0f
Stop loading SVG icons from urls
mbloch Apr 28, 2026
e0145c2
Fix mapshaper-gui termination on tab close
mbloch Apr 28, 2026
e8af6eb
Update dependencies
mbloch Apr 28, 2026
582d0cf
Improvements to mapshaper-gui and session management
mbloch Apr 28, 2026
ad5d69f
Add new messages popup for GUI warnings; warn on incomplete Shapefile
mbloch Apr 28, 2026
15b5f90
v0.7.4
mbloch Apr 28, 2026
03757f7
Add warnings for Shapefile field truncation and projected GeoJSON
mbloch Apr 28, 2026
685b138
Warn on output file name collisions
mbloch Apr 28, 2026
f752107
Add issue tracker link to hamburger menu
mbloch Apr 28, 2026
ea8ee7e
Add warning when field names collide with expression helpers
mbloch Apr 28, 2026
fea6d8b
Improve GUI warnings
mbloch Apr 28, 2026
3c55b67
Add more CLI help examples
mbloch Apr 28, 2026
a794502
Add warning when bboxes of clip/erase source and target are disjoint
mbloch Apr 29, 2026
6966bf2
Improve clip/erase source/target compatibility check
mbloch Apr 29, 2026
19f03d0
Fixed rendering glitch when zooming large datasets
mbloch Apr 29, 2026
aaa3d97
Update docs landing page
mbloch Apr 29, 2026
fad839a
Improve the join message
mbloch Apr 29, 2026
f6aa67f
Delete references to unused objects to free up memory
mbloch Apr 29, 2026
c11bee2
Refactor to remove cyclical dependencies
mbloch Apr 29, 2026
f09ef65
Fix naming collision warning for expressions
mbloch Apr 29, 2026
50d6c1e
v0.7.5
mbloch Apr 29, 2026
9c62bdc
Automatic projection params should be based on bbox of targeted layer/s
mbloch Apr 29, 2026
a32bdb0
Inhibit geojson crs warning when importing generated geojson
mbloch Apr 29, 2026
0778a01
tests
mbloch Apr 29, 2026
440d047
Stop showing -join message when used internally
mbloch Apr 30, 2026
0990e12
export .cpg file alongside other Shapefile components
mbloch Apr 30, 2026
c74a0d1
test
mbloch Apr 30, 2026
66dc82c
Add a warning if -proj and -simplify affect non-targeted layers
mbloch Apr 30, 2026
7ffa767
Add NY State map example
mbloch Apr 30, 2026
0f0b0c6
v0.7.6
mbloch Apr 30, 2026
e27a272
Support reading NDJSON data
mbloch Apr 30, 2026
d5087f3
-points command detects and parses WKT point data
mbloch Apr 30, 2026
5787372
The -o metadata flag outputs SVGs with geospatial metadata
mbloch Apr 30, 2026
a27126e
Make docs pages mobile friendly
mbloch Apr 30, 2026
1a97edd
Tweak docs and mobile layout
mbloch May 1, 2026
790c74c
docs
mbloch May 1, 2026
4f3279d
UI tweaks
mbloch May 1, 2026
a1e7155
v0.7.7
mbloch May 1, 2026
723e89f
docs
mbloch May 2, 2026
9d77ef7
Add support for reading GeoParquet files
mbloch May 2, 2026
63dd554
Support for writing GeoParquet files
mbloch May 2, 2026
09bff18
Improve FlatGeobuf i/o
mbloch May 2, 2026
be8cde2
v0.7.8
mbloch May 2, 2026
f05933a
Add tooling for inspecting the program state
mbloch May 2, 2026
6bc8751
docs
mbloch May 2, 2026
65592f4
Add zstd compression option for GeoParquet
mbloch May 4, 2026
fccc112
docs
mbloch May 4, 2026
454dd6e
docs
mbloch May 4, 2026
945d788
Allow exporting .parquet files without geometry
mbloch May 4, 2026
741dcd8
v0.7.9
mbloch May 4, 2026
9f13282
gitignore
mbloch May 5, 2026
517d118
Add app-wide web UI undo and redo
mbloch May 7, 2026
4f72421
v0.7.10
mbloch May 7, 2026
0c41e43
Add -o reverse-winding option
mbloch May 7, 2026
6e4ffdf
Consolidate GUI undo helpers into gui-app-undo module
mbloch May 7, 2026
1c263cf
Move undo feasibility diagnostics out of gui bundle
mbloch May 7, 2026
341366e
Edit retained data note
mbloch May 7, 2026
e79c249
Quieter mocha tests
mbloch May 7, 2026
60ce8e9
Docs
mbloch May 8, 2026
c2dc5a2
Fix geoparquet errors
mbloch May 8, 2026
c579b58
docs
mbloch May 8, 2026
1fd8d9e
docs
mbloch May 8, 2026
3b0bd86
v0.7.11
mbloch May 8, 2026
29cb363
docs
mbloch May 8, 2026
647f2f4
Auto-fit projection improvements
mbloch May 8, 2026
a019a68
Update tests
mbloch May 8, 2026
0b61245
v0.7.12
mbloch May 8, 2026
3309163
docs
mbloch May 9, 2026
a84afdc
Move layer menu to sidebar
mbloch May 10, 2026
76c21a1
docs
mbloch May 10, 2026
1a8f2b4
Add new icon* style properties
mbloch May 10, 2026
a1446c7
Tweak layer menu widgets
mbloch May 10, 2026
96bf7f3
Add warning when SVG export paths have more than 32,000 vertices
mbloch May 10, 2026
904a730
v0.7.13
mbloch May 10, 2026
5efd452
docs
mbloch May 11, 2026
45d321b
docs
mbloch May 12, 2026
edfb0ce
Add support for GeoTIFF files and georeferenced PNG/JPEG
mbloch May 12, 2026
bbc71b7
v0.7.14
mbloch May 12, 2026
4d7d126
Make the spacebar shortcut only open the console (not the layers tab)
mbloch May 12, 2026
3be1f09
v0.7.15
mbloch May 12, 2026
34ff5db
Update GUI popup when raster has different CRS than current layer
mbloch May 12, 2026
0279268
Remove chinese-language survey link
mbloch May 12, 2026
c0bb2d7
Implement raster layer reprojection
mbloch May 13, 2026
92f2912
docs
mbloch May 13, 2026
470b8af
v0.7.16
mbloch May 13, 2026
a26705c
Label styling wip
mbloch May 15, 2026
870f5ab
Improvements to GUI and label creation popup
mbloch May 15, 2026
771081e
Add 'show info' link in layer menu
mbloch May 15, 2026
5e69b9d
Improve label style panel
mbloch May 15, 2026
35b86eb
v0.7.17
mbloch May 15, 2026
98125c8
update T icon
mbloch May 15, 2026
fc2c817
style panels wip
mbloch May 16, 2026
1a9d0a7
Update color picker presets
mbloch May 18, 2026
41d1f2b
Improve styling panels
mbloch May 18, 2026
d682cfb
make colors=random more random
mbloch May 18, 2026
570ba35
v0.7.18
mbloch May 18, 2026
7b8422c
watch-pr
May 19, 2026
86b517c
GUI style tweaks
mbloch May 20, 2026
674fa5c
v0.7.19
mbloch May 20, 2026
67c1a3c
Added -blur command
mbloch May 21, 2026
ecf76c9
Improve stale undo data cleanup
mbloch May 22, 2026
185f825
allowing multiple tabs to be open in sidebar
May 22, 2026
a51328c
More reliable CRS comparison
mbloch May 23, 2026
fed5043
v0.7.20
mbloch May 23, 2026
5303d77
v0.7.20
mbloch May 23, 2026
4a0dec3
Merge pull request #694 from AgricolaJKB/watch-pr
mbloch May 24, 2026
77b7d44
mapshaper-gui should serve strings as utf-8 not binary
mbloch May 24, 2026
6270aac
Fix for issue #662
mbloch May 24, 2026
dda19b1
Clicking a selected feature in styling mode deselects it
mbloch May 24, 2026
113f971
fix
May 26, 2026
3bf9cdb
saving sidebar separator position between sessions
May 26, 2026
8755928
Fix for clipping bug
mbloch May 27, 2026
6accc3e
v0.7.21
mbloch May 27, 2026
dc9bdb6
Revert buggy JSON parsing code
mbloch May 29, 2026
233c9ad
Add out-of-range seek guard to JSON parser
mbloch May 29, 2026
3ac86ab
v0.7.22
mbloch May 29, 2026
3d22e30
Code style
mbloch May 29, 2026
f2e7297
Merge pull request #696 from TomFevrier/sidebar-multitabs
mbloch Jun 5, 2026
7a3fb2b
Fix sidebar multi-panel regressions
mbloch Jun 5, 2026
a8722bb
Hide sidebar panel resize handle when only one panel is open
mbloch Jun 5, 2026
4e450c2
Stop tracking generated web bundles
mbloch Jun 5, 2026
714bebc
Update deps for Node.js 24 compatibility
mbloch Jun 6, 2026
37473a9
dependency updates
mbloch Jun 6, 2026
bd7041f
Remove deprecated function call
mbloch Jun 13, 2026
a0de53c
Add the -buffer command
mbloch Jun 13, 2026
900fc28
Docs
mbloch Jun 13, 2026
8ac5e2a
v0.7.23
mbloch Jun 13, 2026
8a0b362
docs
mbloch Jun 13, 2026
83b8542
Improve undo/redo data retention
mbloch Jun 14, 2026
76e1e89
Fixed loop-removal optimization for line buffers
mbloch Jun 14, 2026
186bb7d
Optimize polygon buffer creation
mbloch Jun 14, 2026
baa1066
Remove unneeded work
mbloch Jun 14, 2026
a61190c
Unify -buffer options, remove dead code
mbloch Jun 14, 2026
551cc79
v0.7.24
mbloch Jun 14, 2026
6838462
Add new buffer test (fails in browser)
mbloch Jun 15, 2026
192f49d
Bug fix
mbloch Jun 15, 2026
fdf10f3
v0.7.25
mbloch Jun 15, 2026
431ec30
Speed up buffer offset functions
mbloch Jun 15, 2026
14eb363
Add -buffer geodesic option
mbloch Jun 16, 2026
49d93e5
Add geodesic option to -buffer command
mbloch Jun 16, 2026
48d8167
v0.7.26
mbloch Jun 16, 2026
fcd7521
docs
mbloch Jun 16, 2026
f461222
WIP for buffers that reach the poles
mbloch Jun 17, 2026
d8de901
Improve loop removal performance
mbloch Jun 17, 2026
99aaa76
v0.7.27
mbloch Jun 17, 2026
2b253e5
v0.7.27
mbloch Jun 17, 2026
c039b2e
Update dependencies
mbloch Jun 17, 2026
8c0d751
Support importing zipped datasets from stdin
hartblanc Jun 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Funding links surfaced by GitHub in the repo sidebar and the
# "Sponsor this project" button at the top of the repo.

github: [mbloch]
ko_fi: mapshaper
34 changes: 33 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,43 @@ npm-debug.log
.npmignore
.jshintrc
/mapshaper.js
/www/geopackage.js
/www/geoparquet.js
/www/geotiff.js
/www/mapshaper-gui.js
/www/mapshaper.js
/www/node_modules.js
/www/modules.js
/www/docs/
/www/llms.txt
/www/llms-full.txt
# Generated example map assets. Recipes (.txt), Makefile and source data
# stay tracked; outputs are regenerated by `make -C docs/examples/data all`
# (run from release_web_ui before publishing to gh-pages).
/docs/examples/data/*.svg
/docs/examples/data/*.png
/docs/examples/data/*.msx
/docs/examples/data/*.zip
# /www/basemap.js
pre-publish
pre-release.js
release*
nacis
.cursorignore
bench/
/DEVELOPING.md
/DESIGN*
/CITATION*
/RO*
/GO*
/FU*
.cursor*
build-roadmap.mjs
/docs/future-directions.md
docs/troubleshooting.md
TODO.md
www/ai-config.js
test-results/
CLAUDE.md
test/tools/
buffer-inspect/
www/newsletter.html
407 changes: 407 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2013, Matthew Bloch
Copyright (c) 2026, Matthew Bloch

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
Expand Down
30 changes: 26 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ Mapshaper is software for editing Shapefile, GeoJSON, [TopoJSON](https://github.

Mapshaper supports essential map making tasks like simplifying shapes, editing attribute data, clipping, erasing, dissolving, filtering and more.

See the [project wiki](https://github.com/mbloch/mapshaper/wiki) for documentation on how to use mapshaper.
See the [documentation](https://mapshaper.org/docs/) for guides, examples and a complete command reference.

If you're using an AI assistant or another tool that can read documentation, point it at [`mapshaper.org/llms.txt`](https://mapshaper.org/llms.txt) — an [llms.txt](https://llmstxt.org/) index of every docs page in machine-readable form. Each page is also available as Markdown by appending `.md` to its URL (for example, [`mapshaper.org/docs/essentials/command-line.html.md`](https://mapshaper.org/docs/essentials/command-line.html.md)). The full corpus in a single file is at [`mapshaper.org/llms-full.txt`](https://mapshaper.org/llms-full.txt).

To suggest improvements, add an [issue](https://github.com/mbloch/mapshaper/issues).

Expand All @@ -19,9 +21,9 @@ Mapshaper includes several command line programs, which can be run under Mac OS
* `mapshaper-xl` Works the same as `mapshaper`, but runs with more RAM to support larger files.
* `mapshaper-gui` Runs the mapshaper Web interface locally.

The project wiki has an [introduction](https://github.com/mbloch/mapshaper/wiki/Introduction-to-the-Command-Line-Tool) to using the command line tool that includes many simple examples.
The docs include an [introduction](https://mapshaper.org/docs/essentials/command-line.html) to using the command line tool that includes many simple examples.

For a detailed reference, see the [Command Reference](https://github.com/mbloch/mapshaper/wiki/Command-Reference).
For a detailed reference, see the [Command Reference](https://mapshaper.org/docs/reference.html).


## Interactive web interface
Expand All @@ -38,7 +40,7 @@ The web UI works in recent desktop versions of Chrome, Firefox, Safari and Inter

[Here](https://hub.docker.com/r/freifunkhamm/mapshaper) are resources for using mapshaper with Docker, provided by Christian Weiss.

You can find a number of mapshaper tutorials online, including a [two](https://moriartynaps.org/command-carto-part-one/) [part](https://moriartynaps.org/command-carto-part-two/) guide to command line cartography by Dylan Moriarty and [this introduction](https://handsondataviz.org/mapshaper.html) by Jack Dougherty.
You can find a number of mapshaper tutorials online, including a [two](https://moriartynaps.org/command-carto-part-one/) [part](https://moriartynaps.org/command-line-carto-two/) guide to command line cartography by Dylan Moriarty and [this introduction](https://handsondataviz.org/mapshaper.html) by Jack Dougherty.


## Large file support
Expand Down Expand Up @@ -80,12 +82,30 @@ npm run build # bundle source code files
npm link # (optional) add global symlinks so scripts are available systemwide
```

## Using mapshaper with Bun

Mapshaper's command line tools can be run with [Bun](https://bun.sh/) as an alternative to Node.js. The simplest option is to use `bunx`, like this:

```
bunx mapshaper [commands]
```

## Building and testing

From the project directory, run `npm run build` to build both the cli and web UI modules.

Run `npm test` to run mapshaper's tests.

## Supporting mapshaper

Mapshaper is free and open source. If it's useful to you or your organization, please consider supporting its continued development:

* [Ko-fi](https://ko-fi.com/mapshaper) &mdash; one-time or recurring contributions, no account required.
* [GitHub Sponsors](https://github.com/sponsors/mbloch) &mdash; recurring sponsorship.

You can also help by [reporting bugs](https://github.com/mbloch/mapshaper/issues) or [telling me what you'd like to see improved](https://tally.so/r/44Njok).


## License

This software is licensed under [MPL 2.0](http://www.mozilla.org/MPL/2.0/).
Expand All @@ -99,3 +119,5 @@ According to Mozilla's [FAQ](http://www.mozilla.org/MPL/2.0/FAQ.html), "The MPL'
My colleagues at The New York Times, for countless suggestions, bug reports and general helpfulness.

Mark Harrower, for collaborating on the original "MapShaper" program at the University of Wisconsin&ndash;Madison.

[Mapbox](https://www.mapbox.com), for donating basemap services to mapshaper.org.
11 changes: 11 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Security Policy

### Supported Versions

When a vulnerability is fixed, a new patch version will be released (e.g. 0.6.34 -> 0.6.35).

### Reporting a Vulnerability

Please email the developer (Matthew Bloch) at masiyou@gmail.com to report a vulnerability.

If preferred, you can create a new draft security advisory (https://github.com/mbloch/mapshaper/security/advisories)
113 changes: 94 additions & 19 deletions bin/mapshaper-gui
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,50 @@ var defaultPort = 5555,
.name('mapshaper-gui')
.usage('[options] [file ...]')
.option('-p, --port <port>', 'http port of server on localhost', defaultPort)
.option('-q, --quick-view', 'load files with default options, bypassing import dialog')
// .option('-q, --quick-view', 'load files with default options, bypassing import dialog')
.option('-i, --import-prompt', 'prompt for import options')
.option('-s, --direct-save', 'save files outside the browser\'s download folder')
.option('-f, --force-save', 'allow overwriting input files with output files')
.option('-a, --display-all', 'turn on initial visibility of all layers')
.option('-n, --name <name(s)>', 'rename input layer or layers')
.option('-c, --commands <string>', 'console commands to run initially')
.option('-t, --target <name>', 'name of layer to select initially')
.option('-w, --watch', 'watch input files and reload the browser on change')
.addOption(new commander.Option('-b, --blurb <text>',
'replace the default blurb on the import screen').hideHelp())
.argument('[file...]', 'data files to load')
.helpOption('-h, --help', 'show this help message')
.version(require('../package.json').version)
.parse(process.argv),
opts = program.opts(),
http = require("http"),
path = require("path"),
url = require("url"),
fs = require("fs"),
Cookies = require("cookies"),
opn = require("opn"),
webRoot = path.join(__dirname, "../www"),
port = parseInt(opts.port, 10) || defaultPort,
dataFiles = expandShapefiles(program.args),
probeCount = 0,
sessionId = null;
sessionId = null,
sseClients = [],
reloadTimer = null;
validateFiles(dataFiles);

if (opts.watch && dataFiles.length > 0) {
dataFiles.forEach(function(file) {
if (isUrl(file)) return;
fs.watch(file, function() {
// debounce: editors often write files in multiple steps
clearTimeout(reloadTimer);
reloadTimer = setTimeout(function() {
sseClients.forEach(function(res) {
try { res.write('event: reload\ndata: changed\n\n'); } catch(e) {}
});
}, 150);
});
});
}

process.on('uncaughtException', function(err) {
// added 'code' for Node.js v16
if (err.errno === 'EADDRINUSE' || err.code === 'EADDRINUSE') {
Expand Down Expand Up @@ -59,13 +77,30 @@ function startServer(port) {
var timeout;

http.createServer(function(request, response) {
var uri = url.parse(request.url).pathname;
var uri = new URL(request.url, 'http://localhost').pathname;
clearTimeout(timeout);
if (uri == '/close') {
// end process when page closes, unless page is immediately refreshed
if (uri.includes('..')) {
// block attempts to load files outside webroot
serve404(response);
} else if (uri == '/close') {
// End process when page closes, unless page is immediately refreshed.
// The grace window (1500ms) gives the refreshed page time to send
// /cancel-close. A short window is not safe here: with sendBeacon,
// /close frequently arrives at the server *after* the new page's
// resource fetches have already been served, so we can't rely on
// those fetches to reset the timer for us.
timeout = setTimeout(function() {
process.exit(0);
}, 200);
}, 1500);
response.writeHead(204); // no content
response.end();
} else if (uri == '/cancel-close') {
// The page-load-time refresh detection in src/gui/gui.mjs sends this
// when sessionStorage indicates the new page is a refresh of the
// previous tab incarnation. clearTimeout above already cancelled any
// pending exit; we just need to acknowledge the request.
response.writeHead(204);
response.end();
} else if (uri == "/manifest.js") {
if (!sessionId && opts.directSave) {
// create a session id for authenticating requests to save files
Expand All @@ -81,15 +116,52 @@ function startServer(port) {
serveFile(getDataFilePath(uri), response);
} else if (uri.indexOf('/save') === 0) {
saveContent(request, response);
} else if (uri == '/sse' && opts.watch) {
// Server-Sent Events endpoint for file-watch reload notifications
response.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
response.write('data: connected\n\n');
sseClients.push(response);
request.on('close', function() {
sseClients = sseClients.filter(function(c) { return c !== response; });
});
} else {
// serve a file from the web root
if (uri == '/') {
uri = '/index.html';
// serve a file from the web root. Translate directory paths (any URI
// ending in '/') to their index.html so that links like '/docs/' work
// the way they do behind a normal static-file host (mapshaper.org).
if (uri.endsWith('/')) {
uri += 'index.html';
}
if (opts.watch && uri == '/index.html') {
// inject SSE reload script into index.html
var htmlPath = getAssetFilePath('/index.html');
fs.readFile(htmlPath, 'utf8', function(err, content) {
if (err) return serve404(response);
var script = '<script>(function(){' +
'var es=new EventSource("/sse");' +
'es.addEventListener("reload",function(){location.reload();});' +
'})();</script>';
content = content.replace('</body>', script + '\n</body>');
serveContent(content, response, 'text/html');
});
} else {
serveFile(getAssetFilePath(uri), response);
}
serveFile(getAssetFilePath(uri), response);
}
}).listen(port, 'localhost', function() {
opn("http://localhost:" + port);
if (process.env.MAPSHAPER_GUI_NO_OPEN) return;
// `open` is ESM-only since v9, so import it dynamically from this CJS bin
// script. Failure to launch a browser is non-fatal: the user can still
// visit the URL printed (or implied) by the running server.
import('open').then(function(mod) {
return mod.default("http://localhost:" + port);
}).catch(function(err) {
console.error("Could not open browser automatically; visit http://localhost:" + port + " manually.");
console.error(err && err.message || err);
});
});
}

Expand All @@ -116,10 +188,14 @@ function serveError(text, code, response) {
response.end();
}

function serve404(response) {
serveError("404 Not Found\n", 404, response);
}

function serveFile(filename, response) {
fs.readFile(filename, function(err, content) {
if (err) {
serveError("404 Not Found\n", 404, response);
serve404(response);
} else {
serveContent(content, response, getMimeType(filename));
}
Expand All @@ -132,7 +208,7 @@ function serveContent(content, response, mimeType) {
}
response.setHeader('Cache-Control', 'no-cache');
response.writeHead(200);
response.write(content, "binary");
response.write(content, Buffer.isBuffer(content) ? undefined : "utf8");
response.end();
}

Expand All @@ -156,9 +232,8 @@ function findInputFile(ofile) {
function saveContent(req, res) {
var requestId = new Cookies(req, res).get('session_id'),
ip = req.connection.remoteAddress,
urlData = url.parse(req.url),
query = require('querystring').parse(urlData.query),
file = query.file;
urlData = new URL(req.url, 'http://localhost'),
file = urlData.searchParams.get('file');

if (opts.forceSave) {
// match output file to input file (including original path to the input file)
Expand Down Expand Up @@ -212,7 +287,7 @@ function getManifestJS(files, opts) {
target: opts.target,
allow_saving: opts.directSave,
display_all: opts.displayAll,
quick_view: opts.quickView,
quick_view: !opts.importPrompt, // removed: opts.quickView,
name: opts.name,
commands: opts.commands,
blurb: opts.blurb
Expand Down
44 changes: 44 additions & 0 deletions browser-tests/geoparquet-crs-import.spec.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { expect, test } from '@playwright/test';

var GEO_PARQUET_FIXTURE = 'test/data/geoparquet/example-crs_vermont-4326_geo.parquet';
var REQUIRE_ERROR_RX = /require is not defined/i;
var PROJ_ERROR_RX = /Unable to use projection/i;

test('GeoParquet CRS import does not fail with require() error', async function({page}) {
var pageErrors = [];
var consoleErrors = [];

page.on('pageerror', function(err) {
pageErrors.push(String(err && err.message || err));
});
page.on('console', function(msg) {
if (msg.type() == 'error') {
consoleErrors.push(msg.text());
}
});

await page.goto('/?undo=on&undo-test=on&files=' + encodeURIComponent(GEO_PARQUET_FIXTURE));

await page.waitForFunction(function() {
return window.mapshaper &&
window.mapshaper.undoTest &&
window.mapshaper.undoTest.getState().model.datasetCount > 0;
});

var appMessages = await page.evaluate(function() {
return window.mapshaper.undoTest.getMessages();
});
var appMessageText = appMessages.map(function(item) {
return [item && item.title || '', item && item.message || ''].join(' ').trim();
});

expect(pageErrors.some(function(msg) {
return REQUIRE_ERROR_RX.test(msg) || PROJ_ERROR_RX.test(msg) && REQUIRE_ERROR_RX.test(msg);
})).toBe(false);
expect(consoleErrors.some(function(msg) {
return REQUIRE_ERROR_RX.test(msg) || PROJ_ERROR_RX.test(msg) && REQUIRE_ERROR_RX.test(msg);
})).toBe(false);
expect(appMessageText.some(function(msg) {
return REQUIRE_ERROR_RX.test(msg) || PROJ_ERROR_RX.test(msg) && REQUIRE_ERROR_RX.test(msg);
})).toBe(false);
});
Loading