diff --git a/CHANGELOG.md b/CHANGELOG.md index 13d23a845..92482833f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,251 @@ +v0.6.113 +* Support for adding Mapbox vector tile and raster tile basemaps. + +v0.6.112 +* Support for drag-dropping or pasting datafile hyperlinks from a web page onto the web UI. +* Added -o show-all option for exporting snapshots from the CLI with all layers visible in GUI. + +v0.6.111 +* Switched to better-performing robust segment geometry functions using BigInt (instead of big.js). + +v0.6.110 +* Switched to arbitrary-precision math to improve reliability of -clean, -dissolve2 and several other commands. +* Added keep-shapes option to -filter-slivers to retain one ring in multipart polygon features that would otherwise be removed completely. +* Removed the -q/--quick-view option from mapshaper-gui. Now, "quick view" (skipping the import prompt) is the default. +* Added -i/--import-prompt option to mapshaper-gui, to display the import prompt if desired. +* Now, SVG coordinate precision is settable using the -o precision= option (previously SVG precision was always two decimals). + +v0.6.109 +* Fixed a geometry error + +v0.6.108 +* [web] Fix for coordinate precision of drawn rectangles + +v0.6.107 +* [web] Added Display menu to configure basemaps and several other settings. + +v0.6.106 +* Bug fixes + +v0.6.105 +* Updated basemap api keys + +v0.6.104 +* Updated rollup + +v0.6.103 +* Added -join min-overlap-pct= and min-overlap-area= options for polygon-polygon joins. +* Added support for interpolating categorical data fields using `join interpolate=`. +* Switched from browserify to rollup for bundling Node.js libraries for the web UI. + +v0.6.102 +* Added Nicolosi Globular (nicol) projection. + +v0.6.101 +* Added -affine fit-bbox= option, for transforming data to fit within a bounding box. +* Improved arrow symbols. + +v0.6.100 +* Added -frame command, for creating a rectangular map frame layer with a defined display size. Used to set SVG output size and on-screen symbol scale. +* Added -o no-null-props option, which uses "properties":{} instead of "properties":null when outputting GeoJSON features with no attribute data. +* Added support for sub-pixel sizes of pattern fills like hatches and dots (see the -style fill-effect= option). +* Bug fixes + +v0.6.99 +* Bug fixes + +v0.6.98 +* Added remove-empty flag for -filter-slivers command. + +v0.6.97 +* [web] Prompt user to project imported files to match selected layer, when relevant. +* [svg] Add fill="none" to unstyled rectangle layers, to prevent map frame rectangles from displaying with SVG default black polygon fill. + +v0.6.96 +* Bug fix + +v0.6.95 +* Rename "shift/drag box tool" to "rectangle tool" and show instructions when first turned on. +* Bug fixes + +v0.6.94 +* Bug fixes + +v0.6.93 +* Bug fix + +v0.6.92 +* Bug fix + +v0.6.91 +* Bug fix + +v0.6.90 +* [cli] Support for writing files up to 4gb on most computers (2gb was the previous limit). + +v0.6.89 +* Bug fixes + +v0.6.88 +* Bug fixes + +v0.6.87 +* Bug fixes + +v0.6.86 +* Bug fixes + +v0.6.85 +* Added -rectangles bbox= option, which uses an expression to generate rectangle coords for each feature. + +v0.6.84 +* Catch a polygon drawing error. + +v0.6.83 +* Added basemap buttons to the map. + +v0.6.82 +* Added geos (Geostationary Satellite View) projection. + +v0.6.81 +* [web] Add feature deletion to the context menu. +* [web] Automatically close polygons when using Alt/Cmd-drag to draw paths. + +v0.6.80 +* [web] Added pencil-style path drawing using Alt-drag or Cmd-drag. + +v0.6.79 +* [web] Added "delete point" to the point editing context menu. + +v0.6.78 +* [web] Combined "drag points" and "add points" tools into a single "add/edit points" tool. +* [web] Enable point and path drawing when no layers have been added. + +v0.6.77 +* [web] Updated import interface. +* [web] Users can turn on the basemap, use the console and more without first importing a file. +* [web] Added undo to "add points" tool. + +v0.6.76 +* [web] Added right-click/ctrl-click context menu. + +v0.6.75 +* Added auto-detection of GB18030 text encoding (used in some Chinese language Shapefiles). +* Improvements to polygon drawing. + +v0.6.74 +* Bug fixes + +v0.6.73 +* Added drawing/reshaping to polygon layer editing menu. +* Added info box with drawing instructions. + +v0.6.72 +* Bug fix + +v0.6.71 +* [web] Added a new edit mode for polyline layers: line drawing combined with line reshaping (by adding and dragging vertices), removed the old "edit vertices" menu item. +* [web] Added attribute editing to the inspection control, removed the "edit attributes" menu item. +* [web] Added a new button ("Frame") to the box tool buttons, which creates a rectangular map frame. + +v0.6.70 +* Fix for bug affecting -mosaic and -polygon commands (they incorrectly incorporated paths from other layers). + +v0.6.69 +* Web UI bug fixes + +v0.6.68 +* [scalebar] giving a pair of distances creates a dual-unit scale bar (style b only). +* [web] minor bug fixes and improvements. + +v0.6.67 +* -rectangle command with width= option creates a "frame" layer to use as the extent of an SVG map by setting the size of the SVG viewport (an alternative to using -o fit-extent= option). +* [web] Map symbols scale as the map zooms when a frame layer is visible. +* [web] Draggable box tool resizes symmetrically with the shift key pressed. +* [web] Allow selected layer to be temporarily hidden (via layers menu). + +v0.6.66 +* [web] Added ability to edit attribute data of multiple selected features ("select features" tool). +* [web] Added interactive tool for adding points to point-type layer by clicking on the map. +* [web] Added "Add empty layer" button to the layers menu. +* [web] Added center handle for dragging the interactive box tool. +* Added -o fit-extent= option, to use a layer extent (typically a layer containing a single rectangle) for setting the extent of SVG output. + +v0.6.65 +* Added "drag-to-resize" editing mode for resizing rectangles interactively. + +v0.6.64 +* Added more options for the -scalebar command, including two styles, a and b. + +v0.6.63 +* Add support for nested -if/-elif/-else/-endif commands. + +v0.6.62 +* Removed restrictions on the allowable size of the graticule interval (in degrees). +* Added `targets` object to the `-run` command expression context, giving access to multiple target layers. + +v0.6.61 +* Introduced the `fix-geometry` option to the `-snap` and `-o` commands, for removing segment intersections caused by coordinate rounding and TopoJSON quantization. + +v0.6.60 +* Officially added the previously undocumented -scalebar command. + +v0.6.59 +* Second attempted fix for -require error on Windows. + +v0.6.58 +* Fix for -require error on Windows. + +v0.6.57 +* Added io.ifile() method for creating dynamic input files in the run command + +v0.6.56 +* Added support for importing ES modules using the -require command. +* Removed the -require init= function. + +v0.6.55 +* Improvements to the web UI intersections tool. + +v0.6.54 +* Only check for line self-intersections in web UI if user clicks the "Check line intersections" label. +* Added support for curly-bracket delimited expressions to -run command strings. + +v0.6.53 +* Added -classify outer-breaks= option, for limiting the effective data range when calculating equal-interval breaks and continuous color ramps. + +v0.6.52 +* Added -calc + option, which saves calc output to a new layer. +* Added -info + option, which saves info output to a new layer. + +v0.6.51 +* Improved support for running applyCommands() api function in a web browser. + +v0.6.50 +* Fix for label dragging bug. + +v0.6.49 +* Fix for issue #610 (geojson getters in -run and -each should return polygons with RFC 7946 winding order). +* -join interpolate now works correctly when the source layer contains overlapping polygons. + +v0.6.48 +* Added `target.geojson` getter that returns the contents of the target layer as a GeoJSON `FeatureCollection`. Useful in the `-run` command for passing layer data to an external script. + +v0.6.47 +* Added support for using JSON data as an argument to the -i command. +* Added an `io` object with an `io.addInputFile()` method to the `-run` command's expression context, to support loading dynamically generated datasets. + +v0.6.46 +* Added save to clipboard option to web UI export menu. +* In -each expressions, `this.geojson` setter now accepts nulls and FeatureCollections in addition to single Features. + +v0.6.45 +* Added -o hoist= option for moving GeoJSON Feature properties to the root of each Feature. +* Simplification data is removed from snapshot files (except for temporary snapshots in the web UI). + +v0.6.44 +* Added -style css= option for adding inline CSS to SVG symbols. +* Bug fixes + v0.6.43 * Bug fixes diff --git a/LICENSE b/LICENSE index 8429be07b..4f3497ed7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013, Matthew Bloch +Copyright (c) 2025, 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 diff --git a/README.md b/README.md index fdc92488d..7eb88f251 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,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 @@ -80,6 +80,14 @@ 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. diff --git a/REFERENCE.md b/REFERENCE.md index db701e3ef..aa4294187 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -1,6 +1,6 @@ # COMMAND REFERENCE -This documentation applies to version 0.6.39 of mapshaper's command line program. Run `mapshaper -v` to check your version. For an introduction to the command line tool, read [this page](https://github.com/mbloch/mapshaper/wiki/Introduction-to-the-Command-Line-Tool) first. +This documentation applies to version 0.6.112 of mapshaper's command line program. Run `mapshaper -v` to check your version. For an introduction to the command line tool, read [this page](https://github.com/mbloch/mapshaper/wiki/Introduction-to-the-Command-Line-Tool) first. ## Command line syntax @@ -64,6 +64,7 @@ mapshaper states.geojson -filter 'ST == "AK"' + name=alaska -o output/ target=* [-filter-fields](#-filter-fields) [-filter-islands](#-filter-islands) [-filter-slivers](#-filter-slivers) +[-frame](#-frame) [-graticule](#-graticule) [-grid](#-grid) [-include](#-include) @@ -83,6 +84,7 @@ mapshaper states.geojson -filter 'ST == "AK"' + name=alaska -o output/ target=* [-rename-layers](#-rename-layers) [-require](#-require) [-run](#-run) +[-scalebar](#-scalebar) [-shape](#-shape) [-simplify](#-simplify) [-snap](#-snap) @@ -138,7 +140,7 @@ By default, multiple input files are processed separately, as if running mapshap **Options** -`` or `files=` File(s) to input (space-separated list). Use `-` to import TopoJSON or GeoJSON from `/dev/stdin`. +`` or `files=` File(s) to input (space-separated list). Use `-` to import TopoJSON or GeoJSON from `/dev/stdin`. Literal JSON data can also be used instead of a file name. `combine-files` Import multiple files to separate layers with shared topology. Useful for generating a single TopoJSON file containing multiple geometry objects. @@ -209,6 +211,8 @@ Save content of the target layer(s) to a file or files. `precision=` Round all coordinates to a specified precision, e.g. `precision=0.001`. Useful for reducing the size of GeoJSON files. +`fix-geometry` Remove segment intersections caused by rounding (via the `precision=` option) or TopoJSON quantization, by reverting intersecting areas to the original coordinates. In the case of quantized TopoJSON output, this option produces delta-encoded arcs that contain some decimal numbers. Be sure to test your software for compatibility. Note that this option is only applied if the original paths are free of intersections. Also, some kinds of invalid geometry, like spikes, do not get fixed. + `bbox-index` Export a JSON file containing bounding boxes of each output layer. `encoding=` (Shapefile/CSV) Encoding of input text (by default, Shapefile encoding is auto-detected and CSV files are assumed to be UTF-8). @@ -241,6 +245,10 @@ Save content of the target layer(s) to a file or files. `geojson-type=` (GeoJSON) Overrides the default output type. Possible values: "FeatureCollection", "GeometryCollection", "Feature" (for a single feature). +`no-null-props` (GeoJSON) use `"properties": {}` instead of `"properties": null` when outputting a Feature with no attribute data. + +`hoist=` (GeoJSON) Move one or more properties to the root level of each Feature. Hoisting a field named "id" creates an id for each Feature. This option can also be used to create non-standard Feature attributes (as used by the tippecanoe program). + `width=` (SVG/TopoJSON) Set the width of the output dataset in pixels. When used with TopoJSON output, this option switches the output coordinates from geographic units to pixels and flips the Y axis. SVG output is always in pixels (default SVG width is 800). `height=` (SVG/TopoJSON) Similar to the `width` option. If both `height` and `width` are set, content is centered inside the `[0, 0, width, height]` bounding box. @@ -259,13 +267,16 @@ Save content of the target layer(s) to a file or files. `svg-bbox=` (SVG) Bounding box of SVG map in projected map units. By default, the extent of SVG output fits the content; this option lets you provide a custom extent. This could be useful when aligning the SVG output with other content layers, such as images or videos. -`point-symbol=square` (SVG) Use squares instead of circles to symbolize point data. +`fit-extent=` (SVG) Use a layer (typically a layer containing a single rectangle) to set the extent of the map. Paths that overflow this extent are retained in the SVG output. +`point-symbol=square` (SVG) Use squares instead of circles to symbolize point data. `delimiter=` (CSV) Set the field delimiter for CSV/delimited text output; e.g. `delimiter=|`. `decimal-comma` (CSV) Export numbers with decimal commas instead of decimal points (common in Europe and elsewhere). +`show-all` [Snapshot] All layers of the exported snapshot will be displayed when opened in the web UI. + **Example** ```bash # Convert all the Shapefiles in one directory into GeoJSON files in a different directory. @@ -307,7 +318,6 @@ Assign colors or data values to each feature using one of several classification `stops=` A pair of comma-separated numbers (0-100) for limiting the output range of a color ramp. -`range=` A pair of comma-separated numbers giving min and max data values to use when computing class breaks. (By default, the min and max values of the data field being classified are used.) `null-value=` Value (or color) to use for invalid or missing data. @@ -315,6 +325,8 @@ Assign colors or data values to each feature using one of several classification `breaks=` Specify user-defined sequential class breaks (an alternative to automatic classification using `quantile`, `equal-interval`, etc.). +`outer-breaks=` A pair of comma-separated numbers setting min and max breakpoints to use when computing class breaks. This setting overrides the default behavior, which is to use the min and max values of the data field being classified. This setting can be used to prevent extreme data values (outliers) from affecting equal-interval classification. Also useful for setting outside breakpoints for continuous color ramps (when using the `continuous` option). + `method=` Classification method. One of: `quantile`, `equal-interval`, `nice`, `hybrid` (sequential data), `categorical`, `non-adjacent` and `indexed`. This parameter is not required if the classification method can be inferred from other options. For example, the `index-field=` parameter implies indexed classification, the `categories=` parameter implies categorical classification. `quantile` Use quantile classification. Shortcut for `method=quantile`. @@ -584,7 +596,8 @@ All layer types - `this.layer_name` Name of the layer, or `""` if layer is unnamed. - `this.properties` Data properties (also available as local variables) (read/write) - `this.layer` Object with "name" and "data" properties -- `this.geojson` (read/write) Converts each feature to a GeoJSON Feature object. +- `this.geojson` (getter) Returns each feature as a GeoJSON Feature object. +- `this.geojson=` (setter) Update target layer with GeoJSON. Point layers - `this.coordinates` An array of [x, y] coordinates with one or more members, or null (read/write) @@ -599,6 +612,7 @@ Polygon layers - `this.centroidY` Y-coord of centroid - `this.innerX` X-coord of an interior point (for anchoring symbols or labels) - `this.innerY` Y-coord of an interior point +- `this.perimeter` Perimeter of each feature. For lat-long datasets, returns length in meters. Polyline layers - `this.length` Length of each polyline feature. For lat-long datasets, returns length in meters. @@ -606,7 +620,7 @@ Polyline layers Polygon, polyline and point layers - `this.partCount` 1 for single-part features, >1 for multi-part features, 0 for null features - `this.isNull` True if feature has null geometry -- `this.bounds` Bounding box as array [xmin, ymin, xmax, ymax] +- `this.bbox` Bounding box as array [xmin, ymin, xmax, ymax] - `this.width` Width of bounding box - `this.height` Height of bounding box @@ -721,6 +735,23 @@ Remove small polygon rings. [`target=`](#common-options) +### -frame + +Create a rectangular frame layer at a given display width. Frame size is used for scaling symbols and for setting the display size of SVG output. The geographical extent of the frame is based on the `bbox=` option or the bounding box of the target layer or layers, if `bbox=` is omitted. + +`width=` Width of frame (e.g. 5in, 10cm, 600px; default is 800px) + +`height=` Height of frame (in addition to or instead of width= option) + +`aspect-ratio=` Aspect ratio of frame (optional) + +`bbox=` Bounding coordinates of frame contents in projected map coordinates (xmin,ymin,xmax,ymax). If omitted, the bounding box of the target layer(s) is used. + +`offset=` Padding around the frame's `bbox` in display units or pct of width/height, e.g. 5cm 20px 5% + +`offsets=` Comma-sep. list of offsets for each side of the map frame, in l,b,r,t order + +Other options: `name=` `target=` ### -graticule @@ -728,7 +759,7 @@ Create a graticule layer appropriate for a world map centered on longitude 0. `polygon` Create an polygon enclosing the entire area of the graticule. Useful for creating background or outline shapes for clipped projections, like Robinson or Stereographic. -`interval=` Specify the spacing of graticule lines (in degrees). Options include: 5, 10, 15, 30, 45. Default is 10. +`interval=` Specify the spacing of graticule lines (in degrees). Common options are: 5, 10, 15, 30, 45. Default is 10. ### -grid @@ -787,12 +818,16 @@ Join attribute data from a source layer or file to a target layer. If the `keys= `prefix=` Add a prefix to the names of fields joined from the external attribute table. -`interpolate=` (polygon-to-polygon joins only) A list of fields to interpolate/reaggregate based on area of overlap. Intended for fields containing count data, such as population counts or vote counts. Treats data as being uniformally distributed within polygon areas. +`interpolate=` (polygon-to-polygon joins only) A list of fields to interpolate/reaggregate based on area of overlap. Interpolates fields containing count data, such as population counts or vote counts. Treats data as being uniformly distributed within polygon areas. Also interpolates string fields containing categorical data. The value associated with the largest area of overlap between source and target polygons gets copied to the target feature. `point-method` (polygon-to-polygon joins only) Use an alternate method for joining two polygon layers. The default polygon-polygon join method detects areas of overlap between two polygon layers by compositing the two layers internally. This method is simpler -- it generates a temporary point layer from the source layer with the greater number of features (using the same inner-point method as the `-points inner` command), and then performs a point-to-polygon or polygon-to-point join. This method does not support the `interpolate=` option. `largest-overlap` (polygon-to-polygon joins only) selects a single polygon to join when multiple source polygons overlap a target polygon, based on largest area of overlap. +`min-overlap-pct=` (polygon-to-polygon joins only) Only source features with at least this percentage overlap of the target feature (by area) get joined. + +`min-overlap-area=` (polygon-to-polygon joins only) Only source features with at least this much areal overlap of the target feature get joined. + `max-distance=` (point-to-point joins only) Join source layer points within this distance of a target layer point. `duplication` Create duplicate features in the target layer on many-to-one joins. @@ -844,6 +879,13 @@ Common options: `name=` `+` `target=` mapshaper counties.shp -lines STATE_FIPS -o boundaries.shp ``` +```bash +# Example: add the names of neighboring countries to each section of border +mapshaper countries.geojson \ + -lines each='COUNTRIES = A.NAME + (B ? "," + B.NAME : "")' \ + -o borders.geojson +``` + ### -merge-layers @@ -988,6 +1030,8 @@ Create a new layer containing a rectangular polygon for each feature in the laye `aspect-ratio=` Aspect ratio as a number or range (e.g. 2 0.8,1.6 ,2). +`bbox=` Use an expression to generate rectangle bounds for each feature. The expression should evaluate to a GeoJSON-style bbox array. + `offset=` Padding as a distance or percentage of width/height (single value or list). `name=` Assign a name to the newly created layer. @@ -1022,48 +1066,123 @@ mapshaper ne_50m_rivers_lake_centerlines.shp ne_50m_land.shp combine-files \ ### -require -Require a Node module for use in commands like `-each` and `-run`. Required modules are added to the expression context. Named expressions are accessed via thair names or aliases. Unnamed modules have their exported properties added to the expression context. +Require a Node module or ES module for use in commands like `-each` and `-run`. Modules are added to the expression context. When the `alias=` option is given, modules are accessed via their aliases. Modules that are imported by name (e.g. `-require d3`) are accessed via their name, or by their alias if the `alias=` option is used. Module files without an alias name have their exported functions and data added directly to the expression context. -`` or `module=` Name of a Node module or path to a module file. +`` or `module=` Name of an installed module or path to a module file. -`alias=` Use an alias for a named module or module file. - -`init=` JS expression to run after the module loads. +`alias=` Import the module as a custom-named variable. ```bash -# Example: use the underscore module +# Example: use the underscore module (which has been installed locally) $ mapshaper data.json \ -require underscore alias=_ \ -each 'id = _.uniqueId()' \ - -o data.json force + -o data2.json +``` + +```bash +# Example: import a module file containing a user-defined function +$ mapshaper data.json \ + -require scripts/includes.mjs \ + -each 'displayname = getDisplayName(d)' \ + -o data2.json ``` ### -run Create mapshaper commands on-the-fly and run them. -`` or `expression=` A JS expression for generating one or more mapshaper commands. The expression has access to a "target" object with information about the currently targeted layer, as well as modules loaded with the `-require` command. +`` or `expression=` A JS expression or template containing embedded expressions, for generating one or more mapshaper commands. -Common options: `target=` +* Embedded expressions are enclosed in curly braces (see below). +* Expressions can access `target` and `io` objects. +* Expressions can also access functions and data loaded with the `-require` command. +* Functions can be async. -**Example:** Apply a custom projection based on the layer extent +Expression context: + +If command has a single target layer: +`target` object provides data and information about the command's target layer +- `target.layer_name` Name of layer +- `target.geojson` (getter/setter) Returns a GeoJSON FeatureCollection for the layer (getter) or replaces the layer with the contents of a GeoJSON object (setter). +- `target.geometry_type` One of: polygon, polyline, point, `undefined` +- `target.feature_count` Number of features in the layer +- `target.null_shape_count` Number of features with null geometry +- `target.null_data_count` Number of features with no attribute data +- `target.bbox` GeoJSON-style bounding box +- `target.proj4` PROJ-formatted string giving the CRS (coordinate reference system) of the layer + +`targets` object gives access to all layers targetted by the run command. +- by numerical index, like an array (`targets[0]` refers to the first target layer) +- by layer name (`targets.states` refers to a layer named "states") + +`io` object has a method for passing data to the `-i` command. +- `io.ifile(, )` Create a temp file to use as input in a `-run` command (see example 2 below) + +**Example 1:** Apply a custom projection based on the layer extent. ```bash -$ mapshaper -i country.shp -require projection.js -run 'getProjCommand(target)' -o +$ mapshaper -i country.shp -require projection.js -run '-proj {tmerc(target.bbox)}' -o ``` ```javascript // contents of projection.js file -module.exports = { - getProjCommand: function(target) { - var clon = (target.bbox[0] + target.bbox[2]) / 2, - clat = (target.bbox[1] + target.bbox[3]) / 2; - return `-proj +proj=tmerc lat_0=${clat} lon_0=${clon}`; - } +module.exports.tmerc = function(bbox) { + var lon0 = (bbox[0] + bbox[2]) / 2, + lat0 = (bbox[1] + bbox[3]) / 2; + return `+proj=tmerc lat_0=${lat0} lon_0=${lon0}`; }; +``` + +**Example 2:** Convert points to a Voronoi diagram using a template expression +together with an external script. + +```bash +$ mapshaper points.geojson \ + -require script.js \ + -run '-i {io.ifile("voronoi.json", voronoi(target.geojson, target.bbox))}' \ + -o +``` +```javascript +// contents of script.js file +module.exports.voronoi = async function(points, bbox) { + const d3 = await import('d3-delaunay'); // installed locally + const coords = points.features.map(feat => feat.geometry.coordinates); + const voronoi = d3.Delaunay.from(coords).voronoi(bbox); + const features = Array.from(voronoi.cellPolygons()).map(function(ring, i) { + return { + type: 'Feature', + properties: points.features[i].properties, + geometry: { + type: 'Polygon', + coordinates: [ring] + } + }; + }); + return {type: 'FeatureCollection', features: features}; +}; ``` +### -scalebar + +Add a scale bar to an SVG map. The command creates a data-only layer containing the scale bar's data properties. A scale bar is included in the SVG output file if the scale bar layer is included as an output layer. + +The length of the scale bar reflects the scale in the center of the map's rectangular frame. + +`