Skip to content

Commit

Permalink
Add -calc + and -info + options
Browse files Browse the repository at this point in the history
  • Loading branch information
mbloch committed Nov 30, 2023
1 parent 08730d4 commit 38ef6ac
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 17 deletions.
3 changes: 2 additions & 1 deletion src/cli/mapshaper-command-utils.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { dissolveArcs } from '../paths/mapshaper-arc-dissolve';
// Apply a command to an array of target layers
export function applyCommandToEachLayer(func, targetLayers) {
var args = utils.toArray(arguments).slice(2);
return targetLayers.reduce(function(memo, lyr) {
var output = targetLayers.reduce(function(memo, lyr) {
var result = func.apply(null, [lyr].concat(args));
if (utils.isArray(result)) { // some commands return an array of layers
memo = memo.concat(result);
Expand All @@ -15,6 +15,7 @@ export function applyCommandToEachLayer(func, targetLayers) {
}
return memo;
}, []);
return output.length > 0 ? output : null;
}

export function applyCommandToEachTarget(func, targets) {
Expand Down
20 changes: 16 additions & 4 deletions src/cli/mapshaper-options.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,17 @@ export function getOptionParser() {
alias: '+',
type: 'flag',
label: '+, no-replace', // show alias as primary option
// describe: 'retain the original layer(s) instead of replacing'
describe: 'retain both input and output layer(s)'
},
nameOpt2 = { // for -calc and -info
describe: 'name the output layer'
},
noReplaceOpt2 = { // for -calc and -info
alias: '+',
type: 'flag',
label: '+',
describe: 'save output to a new layer'
},
noSnapOpt = {
// describe: 'don't snap points before applying command'
type: 'flag'
Expand Down Expand Up @@ -1911,8 +1919,8 @@ export function getOptionParser() {
type: 'flag'
})
.option('calc', calcOpt)
.option('name', nameOpt)
.option('target', targetOpt)
.option('name', nameOpt)
.option('no-replace', noReplaceOpt);

parser.command('require')
Expand Down Expand Up @@ -2072,7 +2080,9 @@ export function getOptionParser() {
describe: 'functions: sum() average() median() max() min() count()'
})
.option('where', whereOpt)
.option('target', targetOpt);
.option('target', targetOpt)
.option('to-layer', noReplaceOpt2)
.option('name', nameOpt2);

parser.command('colors')
.describe('print list of color scheme names');
Expand Down Expand Up @@ -2102,7 +2112,9 @@ export function getOptionParser() {
.option('save-to', {
describe: 'name of file to save info in JSON format'
})
.option('target', targetOpt);
.option('target', targetOpt)
.option('to-layer', noReplaceOpt2)
.option('name', nameOpt2);

parser.command('inspect')
.describe('print information about a feature')
Expand Down
4 changes: 2 additions & 2 deletions src/cli/mapshaper-run-command.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export async function runCommand(command, job) {
applyCommandToEachLayer(cmd.cluster, targetLayers, arcs, opts);

} else if (name == 'calc') {
applyCommandToEachLayer(cmd.calc, targetLayers, arcs, opts);
outputDataset = cmd.calc(targetLayers, arcs, opts);

} else if (name == 'classify') {
applyCommandToEachLayer(cmd.classify, targetLayers, targetDataset, opts);
Expand Down Expand Up @@ -317,7 +317,7 @@ export async function runCommand(command, job) {
cmd.include(opts);

} else if (name == 'info') {
cmd.info(targets, opts);
outputDataset = cmd.info(targets, opts);

} else if (name == 'inlay') {
outputLayers = cmd.inlay(targetLayers, source, targetDataset, opts);
Expand Down
32 changes: 27 additions & 5 deletions src/commands/mapshaper-calc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ import cmd from '../mapshaper-cmd';
import utils from '../utils/mapshaper-utils';
import { getStashedVar } from '../mapshaper-stash';
import { message, error, stop } from '../utils/mapshaper-logging';
import { DataTable } from '../datatable/mapshaper-data-table';


cmd.calc = function(layers, arcs, opts) {
var arr = layers.map(lyr => applyCalcExpression(lyr, arcs, opts));
if (!opts.to_layer) return null;
return {
info: {},
layers: [{
name: opts.name || 'info',
data: new DataTable(arr)
}]
};
};

// Calculate an expression across a group of features, print and return the result
// Supported functions include sum(), average(), max(), min(), median(), count()
Expand All @@ -14,9 +28,9 @@ import { message, error, stop } from '../utils/mapshaper-logging';
// opts.expression Expression to evaluate
// opts.where Optional filter expression (see -filter command)
//
cmd.calc = function(lyr, arcs, opts) {
export function applyCalcExpression(lyr, arcs, opts) {
var msg = opts.expression,
result, compiled, defs;
result, compiled, defs, d;
if (opts.where) {
// TODO: implement no_replace option for filter() instead of this
lyr = getLayerSelection(lyr, arcs, opts);
Expand All @@ -27,9 +41,17 @@ cmd.calc = function(lyr, arcs, opts) {
defs = getStashedVar('defs');
compiled = compileCalcExpression(lyr, arcs, opts.expression);
result = compiled(null, defs);
message(msg + ": " + result);
return result;
};
if (!opts.to_layer) {
message(msg + ": " + result);
}
d = {
expression: opts.expression,
value: result
};
if (opts.where) d.where = opts.where;
if (lyr.name) d.layer_name = lyr.name;
return d;
}

export function evalCalcExpression(lyr, arcs, exp) {
return compileCalcExpression(lyr, arcs, exp)();
Expand Down
13 changes: 12 additions & 1 deletion src/commands/mapshaper-info.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import geom from '../geom/mapshaper-geom';
import { message } from '../utils/mapshaper-logging';
import { NodeCollection } from '../topology/mapshaper-nodes';
import cmd from '../mapshaper-cmd';
import { DataTable } from '../datatable/mapshaper-data-table';

var MAX_RULE_LEN = 50;

Expand All @@ -17,14 +18,24 @@ cmd.info = function(targets, opts) {
var arr = layers.map(function(o) {
return getLayerInfo(o.layer, o.dataset);
});
message(formatInfo(arr));

if (opts.save_to) {
var output = [{
filename: opts.save_to + (opts.save_to.endsWith('.json') ? '' : '.json'),
content: JSON.stringify(arr, null, 2)
}];
writeFiles(output, opts);
}
if (opts.to_layer) {
return {
info: {},
layers: [{
name: opts.name || 'info',
data: new DataTable(arr)
}]
};
}
message(formatInfo(arr));
};

cmd.printInfo = cmd.info; // old name
Expand Down
14 changes: 10 additions & 4 deletions test/calc-test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ describe('mapshaper-calc.js', function () {
done();
});
})
});

it('+ option creates a new layer', async function() {
var data = [{a: 1}, {a: 3}];
var cmd = 'data.json -calc + "sum(a)" -o out.csv';
var out = await api.applyCommands(cmd, {'data.json': data});
assert.equal(out['out.csv'], 'expression,value,layer_name\nsum(a),4,data')
})
});

describe('evalCalcExpression()', function () {
var data1 = [{foo: -1}, {foo: 3}, {foo: 4}],
Expand Down Expand Up @@ -178,9 +184,9 @@ describe('mapshaper-calc.js', function () {
data: new api.internal.DataTable(data2)
};

var result = api.cmd.calc(lyr2, null,
{no_replace: true, expression: 'average(foo)', where: '!!bar'});
assert.deepEqual(result.data.getRecords(), [{
var result = api.cmd.calc([lyr2], null,
{to_layer: true, expression: 'average(foo)', where: '!!bar'});
assert.deepEqual(result.layers[0].data.getRecords(), [{
value: 1,
where: '!!bar',
expression: 'average(foo)'
Expand Down
11 changes: 11 additions & 0 deletions test/info-test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@ import api from '../mapshaper.js';


describe('mapshaper-info.js', function () {
describe('+ option', function() {
it('simple table', async function() {
var data = [{foo: 'bar'}, {foo: 'baz'}];
var cmd = 'data.json -info + -o format=json';
var out = await api.applyCommands(cmd, {'data.json': data});
var d = JSON.parse(out['info.json'])[0];
assert.equal(d.layer_name, 'data');
assert.equal(d.feature_count, 2);
});

})

describe('save-to option', function() {

Expand Down

0 comments on commit 38ef6ac

Please sign in to comment.