Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

svg output point-symbol=line and -style rotate= #300

Open
wants to merge 576 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
576 commits
Select commit Hold shift + click to select a range
6192359
Code readability
mbloch Apr 26, 2018
59365ad
CSs
mbloch Apr 26, 2018
469df8a
Add ability to dismiss intersection widget
mbloch Apr 27, 2018
b43e677
Fix prj warning on error menu
mbloch Apr 27, 2018
cbc3e2c
v0.4.68
mbloch Apr 27, 2018
19f09a5
Filter off-screen styled shapes
mbloch May 1, 2018
d014748
Add support for reading .shx files
mbloch May 3, 2018
52731d1
v0.4.69
mbloch May 3, 2018
9be7a4e
Remove calls to deprecated Buffer constructor
mbloch May 3, 2018
abbf22a
Reworked messages related to non-standard .shp packing
mbloch May 5, 2018
193d464
Preserve field order in DBF output
mbloch May 5, 2018
c4dc6b6
v0.4.70
mbloch May 5, 2018
b62bfef
v0.4.71
mbloch May 7, 2018
713a3b7
v0.4.72
mbloch May 7, 2018
4d27e7b
Add data-aggregation-test.js
mbloch May 11, 2018
44d049a
Add -merge-layers force option
mbloch May 11, 2018
85f6d61
v0.4.73
mbloch May 12, 2018
db446e3
Add colorizer example
mbloch May 12, 2018
b96c8bb
-join fields=* joins all fields, including source table key
mbloch May 13, 2018
f641376
Add join tests
mbloch May 13, 2018
d359e93
v0.4.74
mbloch May 14, 2018
aac506b
Fix error in GUI popup
mbloch May 14, 2018
1b7694b
-drop command accepts multiple input datasets
mbloch May 14, 2018
02203be
-merge-layers ignores empty layers
mbloch May 15, 2018
a8ff1b5
Fix canvas drawing bug in GUI
mbloch May 17, 2018
d5e1482
Join command refinements
mbloch May 18, 2018
db593c7
Fix UI intersections after merge-layers command
mbloch May 18, 2018
7f4bb4c
Refactoring
mbloch May 20, 2018
9c1f658
v0.4.75
mbloch May 20, 2018
695d444
Comments
mbloch May 20, 2018
c122d3a
UI colors
mbloch May 21, 2018
1146bdf
Warning instead of error when reading an empty CSV file
mbloch May 21, 2018
b850340
Increase maximum CSV output file size
mbloch May 22, 2018
527e54c
v0.4.76
mbloch May 22, 2018
a9b5f1f
CSV parsing improvements
mbloch May 22, 2018
8ff8d68
Comments
mbloch May 23, 2018
0f467c5
Add direct saving to mapshaper-gui
mbloch May 24, 2018
6f9f045
Remove package-lock
mbloch May 24, 2018
3f480ad
v0.4.77
mbloch May 25, 2018
0f3447c
v0.4.78
mbloch May 25, 2018
9f5b9fb
Fix problems exporting many layers in web UI
mbloch May 25, 2018
761f08b
add -include command
mbloch May 25, 2018
cce83df
Block -include from running in browser console
mbloch May 26, 2018
ed21f7b
v0.4.79
mbloch May 26, 2018
26c0699
Restrict mapshaper-gui saving to requests from localhost
mbloch May 26, 2018
314b84b
Expose -calc results to -each commands
mbloch May 26, 2018
a71d9d4
Isolate -include JS from program context
mbloch May 27, 2018
766a41b
Readability
mbloch May 27, 2018
d87a8cb
Optimize layer menu to better handle many layers
mbloch May 28, 2018
a5b64d6
Changed `open` package to `opn`, fixes #282
jlaamanen May 29, 2018
c414877
Layer code tweaks
mbloch May 29, 2018
68c8dfb
Merge branch 'npm-audit-vulnerability-fix' of https://github.com/jlaa…
mbloch May 29, 2018
eaaf888
Merge branch 'jlaamanen-npm-audit-vulnerability-fix'
mbloch May 29, 2018
8a0fc8e
v0.4.80
mbloch May 29, 2018
661eec0
Update deps, fix tests
mbloch May 29, 2018
fa6f93b
Stop testing Node v0.12 on Travis
mbloch May 29, 2018
49c13ef
Support node 4.x
mbloch May 29, 2018
397c3b7
Another shot at supporting v4.x
mbloch May 29, 2018
7a283a6
Add -lines where= and -innerlines where= options
mbloch Jun 6, 2018
1ec6630
v0.4.81
mbloch Jun 6, 2018
b9d67a3
v0.4.82
mbloch Jun 6, 2018
146792d
Delete key deletes pinned feature in gui
mbloch Jun 8, 2018
e401826
Add ways to update svg label text-anchor interactively
mbloch Jun 8, 2018
d6ddf74
better click detection
mbloch Jun 8, 2018
e619cef
Omit undefined/null SVG properties from output
mbloch Jun 8, 2018
25c2373
Refactor gui map layer stack
mbloch Jun 10, 2018
80c7be9
Add max-count= option to -uniq
mbloch Jun 13, 2018
94c30fb
Add -uniq max-count= test
mbloch Jun 13, 2018
1a32ead
Add ability to view multiple layers together
mbloch Jun 13, 2018
4ec65ce
Improve fidelity between canvas-rendered shapes in UI and SVG output
mbloch Jun 13, 2018
775baea
Add button to show/hide all layers
mbloch Jun 14, 2018
0068acf
Show delete layer button on hover only
mbloch Jun 14, 2018
ada9562
show labels on reference layers
mbloch Jun 14, 2018
b52382d
v0.4.83
mbloch Jun 14, 2018
8f22d3d
Fix bug displaying multiple label layers
mbloch Jun 14, 2018
9c66408
Allow mapshaper-gui to load files from one level of parent directory
mbloch Jun 14, 2018
e187c2b
Prelimary work for changing layer order
mbloch Jun 16, 2018
1d2aa31
Extend -rectangle command to create bounding rectangles around layers
mbloch Jun 16, 2018
e7f7f6d
Fix layer menu pin all button visibility
mbloch Jun 16, 2018
573a355
Refactor DOM element caching for layer menu
mbloch Jun 16, 2018
6324160
Update cmd help
mbloch Jun 17, 2018
605716f
Refactor map layers
mbloch Jun 18, 2018
616d5aa
Fix runtime error
mbloch Jun 18, 2018
71031e7
Don't let offsets of unprojected rectangles go off-world
mbloch Jun 19, 2018
45258b8
Now -points can convert polylines to points
mbloch Jun 19, 2018
bee44b2
v0.4.84
mbloch Jun 19, 2018
080f0a3
Add -rectangles command
mbloch Jun 19, 2018
9263a24
Add -style stroke-dasharray= option
mbloch Jun 20, 2018
e67680d
Add -o max-height= option
mbloch Jun 21, 2018
d906fd6
Refactoring to reduce code duplication
mbloch Jun 21, 2018
f696bc3
Support -style stroke-dasharray=
mbloch Jun 21, 2018
f8f07d2
Add undocumented -filter cleanup option
mbloch Jun 22, 2018
e4df1ba
Fix a bug affecting SVG output layer order
mbloch Jun 22, 2018
f62144b
v0.4.85
mbloch Jun 22, 2018
98cbc0f
Add undocumented -rectangle(s) aspect-ratio= option
mbloch Jun 23, 2018
a8799dc
Allow re-ordering layers by dragging in menu
mbloch Jun 24, 2018
c721193
mapshaper-gui -a option displays all layers initially
mbloch Jun 24, 2018
3dec354
Allow -target command to target multiple layers
mbloch Jun 24, 2018
9296ef6
Update layer order as layer is dragged in menu
mbloch Jun 24, 2018
2314697
Stop sorting input files alphabetically in web UI
mbloch Jun 25, 2018
c01c383
[gui] Export map layers in same order as layer menu
mbloch Jun 25, 2018
b0f7e1f
Improve layer dragging
mbloch Jun 26, 2018
f69d85f
v0.4.86
mbloch Jun 26, 2018
7a62edc
Fix layer menu glitches
mbloch Jun 27, 2018
9bee153
Fix layer menu glitch
mbloch Jun 27, 2018
734a835
Prevent browser pinch-zoom in Chrome
mbloch Jun 27, 2018
ce2766e
Added undocumented -run command
mbloch Jun 27, 2018
4887651
Change -run parameters
mbloch Jun 28, 2018
bfef3db
[join] Print warning instead of erroring if no records join
mbloch Jun 28, 2018
3df78ef
Add 'd' as an alias for 'this.properties' in expressions
mbloch Jun 28, 2018
e20720c
Add undocumented -require command
mbloch Jun 29, 2018
52e66b5
Fix -run
mbloch Jun 30, 2018
45eada5
Initial implementation of SVG symbols
mbloch Jun 30, 2018
d5c473a
v0.4.87
mbloch Jun 30, 2018
c109be5
Add tests for -run and -require commands
mbloch Jun 30, 2018
89d29b4
Add default label styles to symbol layers
mbloch Jun 30, 2018
fd5a6b6
Disable text dragging on symbol layers
mbloch Jun 30, 2018
3092384
Embed SVG icons in SVG output files
mbloch Jun 30, 2018
5963124
Edit command line help
mbloch Jul 2, 2018
6f7ec84
Support embedding SVG from relative URLs
mbloch Jul 2, 2018
a29f773
Add 'group'-type symbol
mbloch Jul 2, 2018
c9aa3b8
Now mapshaper-gui can display svg images loaded from relative paths
mbloch Jul 2, 2018
bf8bb5e
Initial work on -frame command
mbloch Jul 3, 2018
e54fbf3
Support for building callout symbols
mbloch Jul 3, 2018
a44cf44
Better handling of empty layers in gui
mbloch Jul 3, 2018
b764e10
Add -simplify target= option
mbloch Jul 5, 2018
5cdc764
Deep-copy object records when a data table is cloned
mbloch Jul 5, 2018
1edca2c
Scale SVG symbols when a map frame is active
mbloch Jul 5, 2018
2ef020f
Try not to cache assets when running mapshaper-gui
mbloch Jul 5, 2018
9127fbc
Improve map frame code
mbloch Jul 5, 2018
657adf0
Scale canvas shapes in frame view
mbloch Jul 5, 2018
b408011
Use xlink:href in svg output
mbloch Jul 5, 2018
3de0e28
Switch to transform property for positioning svg labels
mbloch Jul 5, 2018
0b51bba
On layer menu, don't let tiny drag prevent click-select
mbloch Jul 6, 2018
9baaf16
Fix bug preventing display of intersection dots
mbloch Jul 6, 2018
802278c
Allow hiding currently selected layer
mbloch Jul 9, 2018
4ca637d
Add -scalebar command and cli output for furniture layers
mbloch Jul 13, 2018
eb63781
Fix SVG text vertical alignment
mbloch Jul 13, 2018
86e5eea
Render furniture layers
mbloch Jul 13, 2018
e39733a
Add -scalebar options label-position,label-text,right,bottom etc
mbloch Jul 13, 2018
9112404
Add frame view mode for positioning frame vs. other layers
mbloch Jul 18, 2018
dc8b744
Move crs warning to -frame command
mbloch Jul 18, 2018
6cab746
Move old mbloch utils into src/ tree, pass jshint checks
mbloch Jul 18, 2018
8cac62b
Fix error in reduced builds
mbloch Jul 18, 2018
fe0fa93
Snap to frame view
mbloch Jul 18, 2018
5fc591b
Fix scalebar label positioning in Safari and AI
mbloch Jul 18, 2018
d3e731f
[gui] Use compact layer menu with many layers
mbloch Jul 19, 2018
8577676
Remove unused files and vars
mbloch Jul 19, 2018
96e2c51
Start converting gui/ files
mbloch Jul 19, 2018
2df533d
Update mapshaper-simplify-control
mbloch Jul 19, 2018
5934c98
Update mapshaper-repair-control
mbloch Jul 19, 2018
5e2da72
Update more GUI files
mbloch Jul 19, 2018
3b567dc
Update mapshaper-map.js
mbloch Jul 19, 2018
ce3c005
More files updated
mbloch Jul 20, 2018
1c03761
Update more files to support gui instances
mbloch Jul 20, 2018
03f41d9
Add concept of active/focused gui instance
mbloch Jul 20, 2018
70d59b5
Non-active gui instances ignore keyboard events
mbloch Jul 20, 2018
c5d3966
Fix CSS error
mbloch Jul 20, 2018
28e3367
Refactoring
mbloch Jul 21, 2018
d8eee2d
Refactor
mbloch Jul 21, 2018
953841a
Clean up old mbloch utils
mbloch Jul 23, 2018
c557b26
Improve support for multiple instances
mbloch Aug 1, 2018
98dd6e8
Add option to remove inspector
mbloch Aug 1, 2018
4708694
Remove some distances from scalebar
mbloch Aug 1, 2018
ba85085
Handle a resizing error in [gui]
mbloch Aug 1, 2018
2385b20
Fix scalebar labeling
mbloch Aug 2, 2018
229b2e9
Update NACIS presentation version
mbloch Aug 3, 2018
645eea0
Sanitize DBF output field names
mbloch Aug 3, 2018
85b3d57
CHANGELOG
mbloch Aug 3, 2018
ade296c
v0.4.88
mbloch Aug 3, 2018
42e2ae6
v0.4.89
mbloch Aug 3, 2018
744a640
v0.4.90
mbloch Aug 8, 2018
b1be49d
Add $.layer_name to expression context
mbloch Aug 10, 2018
d83f128
v0.4.91
mbloch Aug 10, 2018
e4e5b88
Fix height range in -frame command
mbloch Aug 24, 2018
7f555ec
GUI editing updates, ongoing
mbloch Aug 24, 2018
7780bad
Fix scalebar label
mbloch Aug 24, 2018
dc09ad7
Fix error clipping mixed-type datasets
mbloch Aug 24, 2018
f938f5b
Add -frame area= option
mbloch Aug 29, 2018
2d2c668
Add pixels option to -frame and -o commands
mbloch Aug 30, 2018
7d147a1
v0.4.92
mbloch Aug 30, 2018
5de48a1
v0.4.92
mbloch Aug 30, 2018
24fa37a
v0.4.93
mbloch Sep 13, 2018
0aa1f1e
v0.4.94
mbloch Sep 13, 2018
a085605
Clean up redundant function exports
mbloch Sep 25, 2018
c5e91c6
Improve robustness of clipping (and other pathfinder functions)
mbloch Oct 2, 2018
2735427
Comment out unused function
mbloch Oct 2, 2018
f561ba6
v0.4.95
mbloch Oct 2, 2018
fc1fff3
Change extent of single-point layers, etc
mbloch Oct 4, 2018
598f72f
v0.4.96
mbloch Oct 4, 2018
d91117e
Add test for issue 304
mbloch Oct 4, 2018
320ab07
Prevent error when initial layer is empty
mbloch Oct 6, 2018
c2dfc89
Switch from rbush to flatbush
mbloch Oct 10, 2018
f3c1ea2
Add bertin1953 projection
mbloch Oct 12, 2018
69cada9
Fix a performance regression
mbloch Oct 15, 2018
6031ed0
v0.4.98
mbloch Oct 15, 2018
ace303e
Support for larger CSV files
mbloch Oct 24, 2018
8dc9cd8
Optimize drawing many points (as square dots)
mbloch Oct 24, 2018
0a95ac2
v0.4.99
mbloch Oct 24, 2018
4e070fb
Small optimization in forEachPoint()
mbloch Oct 25, 2018
b1f691c
Add undocumented fuzzy-join command
mbloch Oct 26, 2018
ca4a657
Popup table formatting
mbloch Nov 1, 2018
f04644a
Fix: hand-editing fields in imported shapefiles
mbloch Nov 1, 2018
b799ffe
Start working on variable simpification
mbloch Nov 13, 2018
917d583
Add variable simplification with 'variable' flag
mbloch Nov 15, 2018
3dc93da
Add variable simplification source file and test
mbloch Nov 15, 2018
321312d
changelog, document csv- import options
mbloch Nov 16, 2018
2be03ff
Update browserify version
mbloch Nov 16, 2018
df82dc3
v0.4.100
mbloch Nov 16, 2018
1460047
v0.4.101
mbloch Nov 16, 2018
cddcc20
Update deps
mbloch Nov 16, 2018
23b3c74
Fix require() problem in bin/mapshaper
mbloch Nov 16, 2018
1cc022e
Rebuild topology after -merge-layers if needed
mbloch Nov 16, 2018
24c66a6
v0.4.102
mbloch Nov 19, 2018
666d99f
Add -points vertices2
mbloch Nov 19, 2018
8b63816
Add errors when joining tables using incompatible key fields
mbloch Nov 19, 2018
5f846d0
Add -filter-slivers weighted option and compactness metric
mbloch Nov 21, 2018
9fffade
v0.4.103
mbloch Nov 21, 2018
b713bb8
changelog
mbloch Nov 21, 2018
141813a
Improve -join message
mbloch Nov 28, 2018
c3cfc7d
Added -uniq invert option
mbloch Nov 28, 2018
edfb136
changelog
mbloch Nov 28, 2018
535b02f
refactor pointer coordinates display
mbloch Nov 28, 2018
84f0c66
pass jshint
mbloch Dec 20, 2018
2119a9e
Add albersusa2 projection to shift Puerto Rico
mbloch Dec 21, 2018
36ab7dc
v0.4.104
mbloch Dec 21, 2018
9396651
Update docs
mbloch Dec 24, 2018
6c09e45
oops
mbloch Dec 26, 2018
26a8503
Improve error message for unreadable file types
mbloch Dec 30, 2018
cd6fcb3
CHANGELOG
mbloch Dec 30, 2018
4a7b6a7
Finish interactive editing update
mbloch Jan 10, 2019
ae9de7a
Merge branch 'master' into svg-symbol-events
mbloch Jan 10, 2019
2414292
Fix popup unpin bug
mbloch Jan 10, 2019
8e61127
Fix label dragging when close to anchor
mbloch Jan 10, 2019
8c0e961
Update coordinate display when dragging symbols
mbloch Jan 10, 2019
7040cb9
v0.4.105
mbloch Jan 10, 2019
12f0c80
Improve edit mode menu interaction
mbloch Jan 10, 2019
c9f8699
Improve how edit mode menu updates
mbloch Jan 13, 2019
dd1e0df
gui updates
mbloch Jan 16, 2019
4002222
Add a way to disable navigation and show/hide buttons
mbloch Jan 16, 2019
9668dd2
Add undocumented option to add metadata object to TopoJSON
mbloch Jan 17, 2019
25e788f
Add showMouseCoordinates option to gui options
mbloch Jan 17, 2019
fbaae17
Improve click-to-select behavior
mbloch Jan 23, 2019
12623b4
Add undocumented -i json-path option
mbloch Jan 23, 2019
fae3a46
Let external applications control symbol dragging
mbloch Jan 23, 2019
c9fb2b4
Trigger global events related to symbol dragging
mbloch Jan 23, 2019
d679afc
Add initial support for on-the-fly reprojection
mbloch Jan 28, 2019
20cb3ca
svg output point-symbol=line and -style rotate=
cedricsam Feb 6, 2019
a7c598f
update test
cedricsam Feb 6, 2019
3b2d804
** is not plain js (is es7)
cedricsam Feb 6, 2019
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
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ npm-debug.log
/www/mapshaper.js
/www/node_modules.js
release*
nacis
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
language: node_js
sudo: false
node_js:
- '0.12'
- '4.2.0'
- '6.9.0'
- 'node'
343 changes: 343 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

31 changes: 26 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

### Introduction

Mapshaper is software for editing Shapefile, GeoJSON, [TopoJSON](https://github.com/mbostock/topojson/wiki) and several other data formats, written in JavaScript.
Mapshaper is software for editing Shapefile, GeoJSON, [TopoJSON](https://github.com/mbostock/topojson/wiki), CSV and several other data formats, written in JavaScript.

The `mapshaper` command line program supports essential map making tasks like simplifying shapes, editing attribute data, clipping, erasing, dissolving, filtering and more.

Expand All @@ -22,14 +22,33 @@ The project wiki has an [introduction](https://github.com/mbloch/mapshaper/wiki/

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

### Interactive tool

The web UI works in recent versions of Chrome and Firefox as well as IE 10+. Exporting is not supported in Safari. If you encounter out-of-memory errors using Chrome, try Firefox, which can handle Shapefiles larger than 1GB.

### Interactive web interface

The web UI works in recent desktop versions of Chrome, Firefox, Safari and Internet Explorer. Safari before v10.1 and IE before v10 are not supported.

The mapshaper distribution includes the script `mapshaper-gui`, which runs mapshaper's web interface locally. You can also visit [mapshaper.org](http://www.mapshaper.org) to use mapshaper online.

All processing is done in the browser, so your data stays private, even when using the public website.

### Large file support

**Web interface**

Firefox is able to load Shapefiles and GeoJSON files larger than 1GB. Chrome has improved in recent versions, but is still prone to out-of-memory errors when importing files larger than several hundred megabytes.

**Command line interface**

When working with very large files, mapshaper may become unresponsive or crash with the message "JavaScript heap out of memory."

One option is to run `mapshaper-xl` (added in v0.4.63), which allocates more memory than the standard `mapshaper` program.

Another solution is to run Node directly with the `--max-old-space-size` option. The following example (Mac or Linux) allocates 8GB of memory:
```bash
$ node --max-old-space-size=8192 `which mapshaper` <mapshaper commands>
```

### Installation

Mapshaper requires [Node.js](http://nodejs.org).
Expand Down Expand Up @@ -62,8 +81,10 @@ This software is licensed under [MPL 2.0](http://www.mozilla.org/MPL/2.0/).

According to Mozilla's [FAQ](http://www.mozilla.org/MPL/2.0/FAQ.html), "The MPL's ‘file-level’ copyleft is designed to encourage contributors to share modifications they make to your code, while still allowing them to combine your code with code under other licenses (open or proprietary) with minimal restrictions."



### Acknowledgements

Gregor Aisch, Mike Bostock, Shan Carter and Zhou Yi, for suggesting improvements and general helpfulness.
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.
Mark Harrower, for collaborating on the original "MapShaper" program at the University of Wisconsin&ndash;Madison.
2 changes: 1 addition & 1 deletion bin/mapshaper
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env node

var mapshaper = require("../");
var mapshaper = require("../mapshaper.js");
mapshaper.enableLogging();
mapshaper.runCommands(process.argv.slice(2), done);

Expand Down
154 changes: 135 additions & 19 deletions bin/mapshaper-gui
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ var http = require("http"),
path = require("path"),
url = require("url"),
fs = require("fs"),
open = require("open"),
Cookies = require("cookies"),
opn = require("opn"),
optimist = require("optimist"),
defaultPort = 5555,
webRoot = path.join(__dirname, "../www"),
Expand All @@ -19,6 +20,21 @@ var http = require("http"),
default: defaultPort,
describe: "http port of server on localhost"
})
.options("s", {
alias: "direct-save",
describe: "allow saving files directly (outside Download folder)",
'boolean': true
})
.options("f", {
alias: "force-save",
describe: "allow overwriting input files with output files",
'boolean': true
})
.options("a", {
alias: "display-all",
describe: "turn on visibility of all layers",
'boolean': true
})
.check(function(argv) {
if (argv.h) {
optimist.showHelp();
Expand All @@ -27,7 +43,8 @@ var http = require("http"),
}).argv,
port = parseInt(opts.port, 10) || defaultPort,
probeCount = 0,
dataFiles = expandShapefiles(opts._);
dataFiles = expandShapefiles(opts._),
sessionId = null;

validateFiles(dataFiles);

Expand All @@ -48,8 +65,12 @@ process.on('uncaughtException', function(err) {

startServer(port);

function getRandomSessionId() {
var str = Math.random().toString(16) + Math.random().toString(16);
return str.replace(/0\./g, '').substr(0, 20);
}

function startServer(port) {
var dataPath = '/data/';
var timeout;

http.createServer(function(request, response) {
Expand All @@ -61,30 +82,59 @@ function startServer(port) {
process.exit(0);
}, 200);
} else if (uri == "/manifest.js") {
if (!sessionId && opts.s) {
// create a session id for authenticating requests to save files
// (see saveContent())
sessionId = getRandomSessionId();
new Cookies(request, response).set('session_id', sessionId);
}
// serve JS file containing manifest of files for mapshaper to load
serveContent(getManifestJS(dataFiles), response, getMimeType(uri));
} else if (uri.indexOf(dataPath) === 0) {
serveContent(getManifestJS(dataFiles, opts), response, getMimeType(uri));
} else if (uri.indexOf('/data/') === 0) {
// serve a file from a path relative to this script
// assumed to be a data file from the cmd line (!)
serveFile(uri.replace(dataPath, ''), response);
serveFile(getDataFilePath(uri), response);
} else if (uri.indexOf('/save') === 0) {
saveContent(request, response);
} else {
// serve a file from the web root
if (uri == '/') {
uri = '/index.html';
}
serveFile(path.join(webRoot, uri), response);
serveFile(getAssetFilePath(uri), response);
}
}).listen(port, function() {
open("http://localhost:" + port);
opn("http://localhost:" + port);
});
}

function getAssetFilePath(uri) {
// allowing loading of assets from paths relative to the cwd (in addition to
// the www/ directory of the mapshaper intall); this is useful
// for displaying svg icons in the web ui; svg output then embeds the icons
// in the output file.
var webPath = path.join(webRoot, uri),
relPath = path.join(process.cwd(), uri);
return fs.existsSync(webPath) ? webPath : relPath;
}

function getDataFilePath(uri) {
// tilde is added by the web ui; allows accessing a single level of parent
// directory when loading data files (an attempt to add a bit of flexibility
// without giving access to the entire filesystem)
return decodeURI(uri).replace('/~/', '/../').replace('/data/', '');
}

function serveError(text, code, response) {
response.writeHead(code, {"Content-Type": "text/plain"});
response.write(text);
response.end();
}

function serveFile(filename, response) {
fs.readFile(filename, "binary", function(err, content) {
fs.readFile(filename, function(err, content) {
if (err) {
response.writeHead(404, {"Content-Type": "text/plain"});
response.write("404 Not Found\n");
response.end();
serveError("404 Not Found\n", 404, response);
} else {
serveContent(content, response, getMimeType(filename));
}
Expand All @@ -93,20 +143,76 @@ function serveFile(filename, response) {

function serveContent(content, response, mimeType) {
if (mimeType) {
response.setHeader("Content-Type", mimeType);
response.setHeader('Content-Type', mimeType);
}
response.setHeader('Cache-Control', 'no-cache');
response.writeHead(200);
response.write(content, "binary");
response.end();
}

function getManifestJS(files) {
if (files.length > 0) {
return "mapshaper.manifest = " + JSON.stringify(files) + ";\n";
function readPostData(req, cb) {
var buffers = [];
if (req.method != 'POST') return cb('expected a post request');
// TODO: consider quitting if a size limit is reached
req.on('data', function(data) {buffers.push(data);});
req.on('end', function () {
cb(null, Buffer.concat(buffers));
});
}

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;
// Only requests containing a cookie with the original session id are allowed to save.
// This should limit direct saving to the user who is running mapshaper-gui,
// preventing another user who has the URL of this Node service from saving.
if (!sessionId || requestId != sessionId) return fail('invalid session id');
if (ip != '::ffff:127.0.0.1' && ip != '::1' && ip != '127.0.0.1') return fail('saving is only allowed from localhost');
if (err = validateOutputFile(file)) return fail(err);
readPostData(req, function(err, buf) {
if (err) return fail(err);
if (!Buffer.isBuffer(buf)) return fail('malformed file content');
try {
fs.writeFileSync(file, buf);
} catch(e) {
return fail(e);
}
serveContent('File saved', res, 'text/plain');
});

function fail(err) {
console.error('Unable to save ' + file + ":", err);
serveError(err, 400, res);
}
}

function validateOutputFile(file) {
var relPath = path.relative('.', file);
// TODO: remove path restrictions?
if (relPath.indexOf('..') > -1) {
return 'parent directories are blocked';
}
if (!opts.f) {
for (var i=0; i<dataFiles.length; i++) {
if (!path.relative(file, dataFiles[i])) {
return 'tried to overwrite a source file';
}
}
}
return '';
}

function getManifestJS(files, opts) {
var o = {files: files};
if (opts.s) o.allow_saving = true;
if (opts.a) o.display_all = true;
return "mapshaper.manifest = " + JSON.stringify(o) + ";\n";
}

// print an error and exit if a file is unreadable
function validateFiles(files) {
files.forEach(function(f) {
Expand All @@ -121,7 +227,7 @@ function validateFiles(files) {
msg = 'File not found';
}
if (msg) {
console.error(msg + ":", f);
console.error(msg + ": " + f);
process.exit(1);
}
});
Expand All @@ -144,7 +250,8 @@ function getMimeType(filename) {
css: 'text/css',
js: 'application/javascript',
html: 'text/html',
png: 'image/png'
png: 'image/png',
svg: 'image/svg+xml'
}[extname(filename)] || null;
}

Expand All @@ -156,13 +263,22 @@ function isUrl(name) {
return /:\/\//.test(name);
}

function containsStringCI(arr, str) {
str = str.toLowerCase();
for (var i=0; i<arr.length; i++) {
if (arr[i].toLowerCase() == str) return true;
}
return false;
}

// append auxiliary file, if it exists in the filesystem but not in the file list
function addAuxFile(files, file, ext) {
var extRx = /\.[^.]+$/;
// handle UC and LC extensions
// TODO: match any combination of UC and LC characters in filename
var aux = file.replace(extRx, ext.toLowerCase());
var AUX = file.replace(extRx, ext.toUpperCase());
if (files.indexOf(aux) == -1 && files.indexOf(AUX) == -1) {
if (!containsStringCI(files, aux)) {
if (fs.existsSync(aux)) {
files.push(aux);
} else if (fs.existsSync(AUX)) {
Expand Down
10 changes: 10 additions & 0 deletions bin/mapshaper-xl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env node

// Run node in a child process with more memory
// note: require('v8').getHeapStatistics().heap_size_limit seems to give the current limit
var megabytes = 8000;
var mapshaper = require('path').join(__dirname, 'mapshaper'); // path of mapshaper executable
var opts = {stdio: 'inherit'};
var args = ['--max-old-space-size=' + megabytes, mapshaper].concat(process.argv.slice(2));
console.error("Allocating", megabytes, "megabytes of heap memory");
require('child_process').spawn(process.argv[0], args, opts);
21 changes: 9 additions & 12 deletions build
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,31 @@

var version = require('./package.json').version;
var follow = process.argv.indexOf('-f') > -1;
var buildAll = process.argv.indexOf('all') > -1;
var requiredModules = [
'mproj',
'buffer',
'iconv-lite',
'fs',
'rbush',
'flatbush',
'rw',
'path',
'd3-dsv'
];


require("catty")({follow: follow})
.addLibrary("lib")
.addLibrary("src")
.cat("src/gui/mapshaper-gui.js", './www/mapshaper-gui.js')
.prepend("var VERSION = '" + version + "';")
.prepend("VERSION = '" + version + "';")
.cat("src/mapshaper.js", onCat);

if (buildAll) {
require('browserify')()
.require(requiredModules)
.bundle(function(err, buf) {
if (err) throw err;
write('./www/node_modules.js', buf);
});
}

require('browserify')()
.require(requiredModules)
.bundle(function(err, buf) {
if (err) throw err;
write('./www/modules.js', buf);
});


function onCat(err, js) {
Expand Down
Loading