Skip to content

Commit

Permalink
v0.6.41
Browse files Browse the repository at this point in the history
  • Loading branch information
mbloch committed Aug 4, 2023
1 parent 1795ec0 commit a187918
Show file tree
Hide file tree
Showing 21 changed files with 112 additions and 97 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
v0.6.41
* Bug fixes

v0.6.40
* Added support for decimal degree formats in DMS functions, e.g. `"[-]DDD.DDDDD°"`.
* Added `-o svg-bbox=` option, for specifying the extent of SVG maps in projected map units.
Expand Down
8 changes: 6 additions & 2 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -564,8 +564,12 @@ Apply a JavaScript expression to each feature in a layer. Data properties are av
**Utility functions**
Several utility functions are available within expressions.

- `format_dms(coord [, fmt])` Format a latitude or longitude coordinate as DMS. The optional second argument is for custom formats. Examples: `[-]DDDMM.MMMMM` `DdMmSs [EW]` `DD° MM′ SS.SSSSS″ [NS]`
- `parse_dms(string [, fmt])` Parse a DMS string to a numerical coordinate. The optional second argument gives the format to use for parsing.
- `format_dms(coord [, fmt])` Format a latitude or longitude coordinate as a DMS string (degrees, minutes, seconds). The optional second argument lets you specify a custom format. Example format strings:
- `[+-]DDDMM.MMMMM`
- `DdMmSs [EW]`
- `DD° MM′ SS.SSSSS″ [NS]`
- `[-]DD.DDDDD°`
- `parse_dms(string [, fmt])` Parse a DMS string to a numerical coordinate. The optional second argument gives the format to use for parsing. Example (given DMS-formatted fields `latDMS` and `lonDMS`): `-each 'lat = parse_dms(latDMS, "DDDMMSS.SSS[NS]"), lon = parse_dms(lonDMS, "DDDMMSS.SSS[EW]")'`.
- `round(number [, decimals])` Optional second argument gives the number of decimals to use.


Expand Down
2 changes: 1 addition & 1 deletion bin/mapshaper-gui
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var defaultPort = 5555,
.option('-q, --quick-view', 'load files with default options, bypassing import dialog')
.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 visibility of all layers')
.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')
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mapshaper",
"version": "0.6.40",
"version": "0.6.41",
"description": "A tool for editing vector datasets for mapping and GIS.",
"keywords": [
"shapefile",
Expand Down
4 changes: 4 additions & 0 deletions src/cli/mapshaper-options.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,10 @@ export function getOptionParser() {
describe: '[TopoJSON] export coordinates without quantization',
type: 'flag'
})
.option('metadata', {
// describe: '[TopoJSON] Add a metadata object containing CRS information',
type: 'flag'
})
.option('no-point-quantization', {
// describe: '[TopoJSON] export point coordinates without quantization',
type: 'flag'
Expand Down
5 changes: 3 additions & 2 deletions src/cli/mapshaper-run-command.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ import '../commands/mapshaper-split-on-grid';
import '../commands/mapshaper-subdivide';

function commandAcceptsMultipleTargetDatasets(name) {
return name == 'rotate' || name == 'info' || name == 'proj' ||
return name == 'rotate' || name == 'info' || name == 'proj' || name == 'require' ||
name == 'drop' || name == 'target' || name == 'if' || name == 'elif' ||
name == 'else' || name == 'endif' || name == 'run' || name == 'i';
}
Expand Down Expand Up @@ -248,7 +248,8 @@ export async function runCommand(command, job) {
outputLayers = applyCommandToEachLayer(cmd.explodeFeatures, targetLayers, arcs, opts);

} else if (name == 'external') {
cmd.external(opts);
// -require now incorporates -external
cmd.require(targets, opts);

} else if (name == 'filter') {
outputLayers = applyCommandToEachLayer(cmd.filterFeatures, targetLayers, arcs, opts);
Expand Down
36 changes: 11 additions & 25 deletions src/commands/mapshaper-external.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,6 @@ import require from '../mapshaper-require';

var externalCommands = {};

cmd.external = function(opts) {
// TODO: remove duplication with -require command
var _module, moduleFile, moduleName;
if (!opts.module) {
stop('Missing required "module" parameter');
}
if (cli.isFile(opts.module)) {
moduleFile = opts.module;
} else if (cli.isFile(opts.module + '.js')) {
moduleFile = opts.module + '.js';
} else {
moduleName = opts.module;
}
if (moduleFile) {
moduleFile = require('path').join(process.cwd(), moduleFile);
}
try {
_module = require(moduleFile || moduleName);
_module(api);
} catch(e) {
// stop(e);
stop('Unable to load external module:', e.message);
}
};

cmd.registerCommand = function(name, params) {
var defn = {name: name, options: params.options || []};
// Add definitions of options common to all commands (TODO: remove duplication)
Expand All @@ -42,6 +17,14 @@ cmd.registerCommand = function(name, params) {
externalCommands[name] = defn;
};

export function isValidExternalCommand(defn) {
try {
validateExternalCommand(defn);
return true;
} catch(e) {}
return false;
}

function validateExternalCommand(defn) {
var targetTypes = ['layer', 'layers'];
if (typeof defn.command != 'function') {
Expand All @@ -50,6 +33,9 @@ function validateExternalCommand(defn) {
if (!defn.target) {
stop('Missing required "target" parameter');
}
if (!targetTypes.includes(defn.target)) {
stop('Unrecognized command target type:', defn.target);
}
}

cmd.runExternalCommand = function(cmdOpts, catalog) {
Expand Down
15 changes: 9 additions & 6 deletions src/commands/mapshaper-point-to-grid.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ cmd.pointToGrid = function(targetLayers, targetDataset, opts) {
return outputLayers;
};


function getPolygonDataset(pointLyr, gridBBox, opts) {
var points = getPointsInLayer(pointLyr);
var cellSize = opts.interval;
Expand Down Expand Up @@ -80,12 +81,12 @@ function getPolygonDataset(pointLyr, gridBBox, opts) {
return importGeoJSON(geojson, {});
}

function getPointCircleRadius(opts) {
export function getPointCircleRadius(opts) {
var cellRadius = opts.interval * Math.sqrt(1 / Math.PI);
return opts.radius > 0 ? opts.radius : cellRadius;
}

function calcCellProperties(pointIds, weights, calc) {
export function calcCellProperties(pointIds, weights, calc) {
var hitIds = [];
var weight = 0;
var partial;
Expand All @@ -103,7 +104,7 @@ function calcCellProperties(pointIds, weights, calc) {
return d;
}

function calcWeights(cellCenter, cellSize, points, pointIds, pointRadius) {
export function calcWeights(cellCenter, cellSize, points, pointIds, pointRadius) {
var weights = [];
var cellRadius = cellSize * Math.sqrt(1 / Math.PI); // radius of circle with same area as cell
var cellArea = cellSize * cellSize;
Expand All @@ -130,7 +131,7 @@ export function twoCircleIntersection(c1, r1, c2, r2) {
r2sq * Math.acos(d2/r2) - d2 * Math.sqrt(r2sq - d2 * d2);
}

function makeCellPolygon(idx, grid, opts) {
export function makeCellPolygon(idx, grid, opts) {
var coords = opts.circles ?
makeCircleCoords(grid.idxToPoint(idx), opts) :
makeCellCoords(grid.idxToBBox(idx), opts);
Expand All @@ -155,7 +156,9 @@ function makeCircleCoords(center, opts) {
return getPointBufferCoordinates(center, radius, 20, getPlanarSegmentEndpoint);
}

function getPointIndex(points, grid, radius) {
// Returns a function that receives a cell index and returns indices of points
// within a given distance of the cell.
export function getPointIndex(points, grid, radius) {
var Flatbush = require('flatbush');
var gridIndex = new IdTestIndex(grid.cells());
var bboxIndex = new Flatbush(points.length);
Expand Down Expand Up @@ -234,7 +237,7 @@ export function getCenteredGridBounds(bbox, interval) {
}

// TODO: Use this function for other grid-based commands
function getGridData(bbox, interval, opts) {
export function getGridData(bbox, interval, opts) {
var extent = opts && opts.aligned ?
getAlignedGridBounds(bbox, interval) :
getCenteredGridBounds(bbox, interval);
Expand Down
12 changes: 11 additions & 1 deletion src/commands/mapshaper-require.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { stop } from '../utils/mapshaper-logging';
import { getStashedVar } from '../mapshaper-stash';
import cli from '../cli/mapshaper-cli-utils';
import require from '../mapshaper-require';
import api from '../mapshaper-api';
import { isValidExternalCommand } from '../commands/mapshaper-external';

cmd.require = function(targets, opts) {
var defs = getStashedVar('defs');
Expand All @@ -23,8 +25,16 @@ cmd.require = function(targets, opts) {
}
try {
mod = require(moduleFile || moduleName);
if (typeof mod == 'function') {
// -require now includes the functionality of the old -external command
var retn = mod(api);
if (retn && isValidExternalCommand(retn)) {
cmd.registerCommand(retn.name, retn);
}
}
} catch(e) {
stop(e);
// stop(e);
stop('Unable to load external module:', e.message);
}
if (moduleName || opts.alias) {
defs[opts.alias || moduleName] = mod;
Expand Down
24 changes: 5 additions & 19 deletions src/gui/gui-display-layer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -125,25 +125,11 @@ export function getDisplayLayer(layer, dataset, opts) {


function getDisplayBounds(lyr, arcs) {
var arcBounds = arcs ? arcs.getBounds() : new Bounds(),
bounds = arcBounds, // default display extent: all arcs in the dataset
lyrBounds;

if (lyr.geometry_type == 'point') {
lyrBounds = internal.getLayerBounds(lyr);
if (lyrBounds && lyrBounds.hasBounds()) {
if (lyrBounds.area() > 0 || !arcBounds.hasBounds()) {
bounds = lyrBounds;
} else {
// if a point layer has no extent (e.g. contains only a single point),
// then merge with arc bounds, to place the point in context.
bounds = arcBounds.mergeBounds(lyrBounds);
}
}
}

if (!bounds || !bounds.hasBounds()) { // empty layer
bounds = new Bounds();
var bounds = internal.getLayerBounds(lyr, arcs) || new Bounds();
if (lyr.geometry_type == 'point' && arcs && bounds.hasBounds() && bounds.area() > 0 === false) {
// if a point layer has no extent (e.g. contains only a single point),
// then merge with arc bounds, to place the point in context.
bounds = bounds.mergeBounds(arcs.getBounds());
}
return bounds;
}
Expand Down
4 changes: 4 additions & 0 deletions src/gui/gui-map-extent.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ export function MapExtent(_position) {
return calcBounds(_cx, _cy, _scale / (k || 1));
};

this.getFullBounds = function() {
return _fullBounds;
};

// Update the extent of 'full' zoom without navigating the current view
//
this.setFullBounds = function(fullBounds, strictBounds) {
Expand Down
28 changes: 16 additions & 12 deletions src/gui/gui-map.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export function MshpMap(gui) {
_ext = new MapExtent(position),
_nav = new MapNav(gui, _ext, _mouse),
_visibleLayers = [], // cached visible map layers
_fullBounds = null,
_hit,
_basemap,
_intersectionLyr, _activeLyr, _overlayLyr,
Expand Down Expand Up @@ -141,8 +140,8 @@ export function MshpMap(gui) {
updateLayerStyles(getDrawableContentLayers()); // kludge to make sure all layers have styles

// Update map extent (also triggers redraw)
projectMapExtent(_ext, oldCRS, this.getDisplayCRS(), getFullBounds());
_fullBounds = getFullBounds(); // update this so map extent doesn't get reset after next update
projectMapExtent(_ext, oldCRS, this.getDisplayCRS(), calcFullBounds());
updateFullBounds();
};

// Refresh map display in response to data changes, layer selection, etc.
Expand Down Expand Up @@ -185,28 +184,27 @@ export function MshpMap(gui) {
gui.dispatchEvent('popup-needs-refresh');
} else if (_hit) {
_hit.clearSelection();
_hit.setLayer(_activeLyr);
}
_hit.setLayer(_activeLyr); // need this every time, to support dynamic reprojection

updateVisibleMapLayers();
fullBounds = getFullBounds();
fullBounds = calcFullBounds();

if (!prevLyr || !_fullBounds || prevLyr.tabular || _activeLyr.tabular || isFrameView()) {
if (!prevLyr || prevLyr.tabular || _activeLyr.tabular || isFrameView()) {
needReset = true;
} else {
needReset = mapNeedsReset(fullBounds, _fullBounds, _ext.getBounds(), e.flags);
needReset = mapNeedsReset(fullBounds, _ext.getFullBounds(), _ext.getBounds(), e.flags);
}

if (isFrameView()) {
_nav.setZoomFactor(0.05); // slow zooming way down to allow fine-tuning frame placement // 0.03
_ext.setFrame(getFullBounds()); // TODO: remove redundancy with drawLayers()
_ext.setFrame(calcFullBounds()); // TODO: remove redundancy with drawLayers()
needReset = true; // snap to frame extent
} else {
_nav.setZoomFactor(1);
}
_ext.setFullBounds(fullBounds, getStrictBounds()); // update 'home' button extent

_fullBounds = fullBounds;
if (needReset) {
_ext.reset();
}
Expand Down Expand Up @@ -307,12 +305,17 @@ export function MshpMap(gui) {
return null;
}

function getFullBounds() {
function updateFullBounds() {
_ext.setFullBounds(calcFullBounds(), getStrictBounds());
}

function calcFullBounds() {
if (isPreviewView()) {
return internal.getFrameLayerBounds(internal.findFrameLayer(model));
}
var b = new Bounds();
getDrawableContentLayers().forEach(function(lyr) {
var layers = getDrawableContentLayers();
layers.forEach(function(lyr) {
b.mergeBounds(lyr.bounds);
});

Expand Down Expand Up @@ -457,6 +460,7 @@ export function MshpMap(gui) {
// (default) anything could have changed
function drawLayers2(action) {
var layersMayHaveChanged = !action;
var fullBounds;
var contentLayers = getDrawableContentLayers();
var furnitureLayers = getDrawableFurnitureLayers();
if (!(_ext.width() > 0 && _ext.height() > 0)) {
Expand All @@ -467,7 +471,7 @@ export function MshpMap(gui) {
if (layersMayHaveChanged) {
// kludge to handle layer visibility toggling
_ext.setFrame(isPreviewView() ? getFrameData() : null);
_ext.setFullBounds(getFullBounds(), getStrictBounds());
updateFullBounds();
updateLayerStyles(contentLayers);
updateLayerStackOrder(model.getLayers());// update menu_order property of all layers
}
Expand Down
17 changes: 16 additions & 1 deletion src/mapshaper-api.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
import { enableLogging } from './utils/mapshaper-logging';
import { runCommands, applyCommands, runCommandsXL } from './cli/mapshaper-run-commands';

import cmd from './mapshaper-cmd';
import internal from './mapshaper-internal';
import geom from './geom/mapshaper-geom';
import utils from './utils/mapshaper-utils';
import cli from './cli/mapshaper-cli-utils';

// the mapshaper public api only has 4 functions
export default {
var api = {
runCommands,
applyCommands,
runCommandsXL,
enableLogging
};

// Add some namespaces, for easier testability and
// to expose internal functions to the web UI
Object.assign(api, {
cli, cmd, geom, utils, internal,
});

export default api;
Loading

0 comments on commit a187918

Please sign in to comment.