mapshaper 0.7.8 → 0.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/mapshaper.js CHANGED
@@ -25683,52 +25683,71 @@ ${svg}
25683
25683
  }
25684
25684
 
25685
25685
  var writerPromise = null;
25686
+ var zstdPromise = null;
25686
25687
  var dynamicImportModule$1 = Function('id', 'return import(id)');
25687
25688
 
25688
25689
  async function exportGeoParquet(dataset, opts, filenameOverride) {
25689
25690
  var writer = await loadGeoParquetWriter();
25691
+ var compression = await getGeoParquetCompression(opts);
25690
25692
  var extension = opts.extension || 'parquet';
25693
+ var files = [];
25691
25694
  if (opts.file) {
25692
25695
  extension = getFileExtension(opts.file) || extension;
25693
25696
  }
25694
- return dataset.layers.map(function(lyr) {
25695
- if (!lyr.geometry_type) {
25696
- stop$1('GeoParquet export requires a geometry layer');
25697
- }
25697
+ dataset.layers.forEach(function(lyr) {
25698
25698
  var features = exportLayerAsGeoJSON(lyr, dataset, opts, true, null);
25699
- var output = buildGeoParquetColumns(features);
25700
- var geoMetadata = buildGeoMetadata(features, dataset);
25701
- var content = writer.parquetWriteBuffer({
25699
+ var hasGeometry = features.some(function(feat) {
25700
+ return !!feat.geometry;
25701
+ });
25702
+ var output = buildGeoParquetColumns(features, hasGeometry);
25703
+ var writeOptions = {
25702
25704
  columnData: output.columnData,
25703
- kvMetadata: [{
25705
+ codec: compression.codec,
25706
+ compressors: compression.compressors,
25707
+ pageSize: compression.pageSize
25708
+ };
25709
+ if (hasGeometry) {
25710
+ writeOptions.kvMetadata = [{
25704
25711
  key: 'geo',
25705
- value: JSON.stringify(geoMetadata)
25706
- }]
25707
- });
25708
- return {
25712
+ value: JSON.stringify(buildGeoMetadata(features, dataset))
25713
+ }];
25714
+ } else {
25715
+ warn('GeoParquet export: layer has no geometry; writing attribute data only.');
25716
+ }
25717
+ var content = writer.parquetWriteBuffer(writeOptions);
25718
+ files.push({
25709
25719
  filename: filenameOverride || (lyr.name + '.' + extension),
25710
25720
  content: content
25711
- };
25721
+ });
25712
25722
  });
25723
+ return files;
25713
25724
  }
25714
25725
 
25715
- function buildGeoParquetColumns(features, writer) {
25726
+ function buildGeoParquetColumns(features, includeGeometry) {
25716
25727
  var geometryName = 'geometry';
25717
25728
  var names = getPropertyNames(features);
25718
25729
  var columnData = [];
25719
- columnData.push({
25720
- name: geometryName,
25721
- data: features.map(function(feat) {
25722
- return feat.geometry || null;
25723
- }),
25724
- type: 'GEOMETRY'
25725
- });
25730
+ if (features.length === 0) {
25731
+ stop$1('GeoParquet export requires at least one record');
25732
+ }
25733
+ if (includeGeometry) {
25734
+ columnData.push({
25735
+ name: geometryName,
25736
+ data: features.map(function(feat) {
25737
+ return feat.geometry || null;
25738
+ }),
25739
+ type: 'GEOMETRY'
25740
+ });
25741
+ }
25726
25742
  names.forEach(function(name) {
25727
25743
  var values = features.map(function(feat) {
25728
25744
  return feat.properties ? feat.properties[name] : null;
25729
25745
  });
25730
25746
  columnData.push(buildAttributeColumn(name, values));
25731
25747
  });
25748
+ if (columnData.length === 0) {
25749
+ stop$1('GeoParquet export requires geometry or attribute data');
25750
+ }
25732
25751
  return {columnData: columnData, geometryColumn: geometryName};
25733
25752
  }
25734
25753
 
@@ -25886,6 +25905,99 @@ ${svg}
25886
25905
  return nodeMod.default && !nodeMod.parquetWriteBuffer ? nodeMod.default : nodeMod;
25887
25906
  }
25888
25907
 
25908
+ async function getGeoParquetCompression(opts) {
25909
+ var codec = normalizeGeoParquetCompression(opts.compression);
25910
+ var level = validateGeoParquetCompressionLevel(opts.level, codec);
25911
+ if (codec != 'ZSTD') {
25912
+ return {codec: codec, compressors: null};
25913
+ }
25914
+ var zstd = await loadZstdLib();
25915
+ if (!zstd || typeof zstd.compress != 'function') {
25916
+ stop$1('GeoParquet ZSTD compressor is not loaded');
25917
+ }
25918
+ return {
25919
+ codec: codec,
25920
+ pageSize: getGeoParquetPageSize(level),
25921
+ compressors: {
25922
+ ZSTD: function(bytes) {
25923
+ return compressZstdPage(zstd, bytes, level);
25924
+ }
25925
+ }
25926
+ };
25927
+ }
25928
+
25929
+ function compressZstdPage(zstd, bytes, level) {
25930
+ var compressed;
25931
+ try {
25932
+ compressed = zstd.compress(bytes, level);
25933
+ } catch (e) {
25934
+ stop$1('Unable to apply GeoParquet ZSTD compression. Try a lower level= value.');
25935
+ }
25936
+ if (!compressed) {
25937
+ stop$1('Unable to apply GeoParquet ZSTD compression. Try a lower level= value.');
25938
+ }
25939
+ return compressed;
25940
+ }
25941
+
25942
+ function getGeoParquetPageSize(level) {
25943
+ return level >= 10 ? 64 * 1024 : undefined;
25944
+ }
25945
+
25946
+ function normalizeGeoParquetCompression(compression) {
25947
+ var str = compression === undefined || compression === null ? 'snappy' : String(compression).toLowerCase();
25948
+ if (str == 'snappy') return 'SNAPPY';
25949
+ if (str == 'zstd') return 'ZSTD';
25950
+ if (str == 'none' || str == 'uncompressed') return null;
25951
+ stop$1('Unsupported GeoParquet compression:', compression);
25952
+ }
25953
+
25954
+ function validateGeoParquetCompressionLevel(level, codec) {
25955
+ if (level === undefined) return undefined;
25956
+ if (codec != 'ZSTD') {
25957
+ stop$1('The level= option only applies with compression=zstd');
25958
+ }
25959
+ if (level >= 1 && level <= 22 && Math.floor(level) === level) {
25960
+ return level;
25961
+ }
25962
+ stop$1('GeoParquet ZSTD level= option must be an integer from 1 to 22');
25963
+ }
25964
+
25965
+ async function loadZstdLib() {
25966
+ var mod;
25967
+ if (runningInBrowser()) {
25968
+ mod = require$1('zstd-codec');
25969
+ } else {
25970
+ if (!zstdPromise) {
25971
+ zstdPromise = dynamicImportModule$1('zstd-codec');
25972
+ }
25973
+ mod = await zstdPromise;
25974
+ }
25975
+ if (mod && mod.default && !mod.ZstdCodec) {
25976
+ mod = mod.default;
25977
+ }
25978
+ if (!mod || !mod.ZstdCodec || typeof mod.ZstdCodec.run != 'function') {
25979
+ stop$1('GeoParquet ZSTD compressor is not loaded');
25980
+ }
25981
+ return initZstdCodec(mod.ZstdCodec);
25982
+ }
25983
+
25984
+ function initZstdCodec(codec) {
25985
+ return new Promise(function(resolve, reject) {
25986
+ try {
25987
+ codec.run(function(zstd) {
25988
+ var simple = new zstd.Simple();
25989
+ resolve({
25990
+ compress: function(bytes, level) {
25991
+ return simple.compress(bytes, level);
25992
+ }
25993
+ });
25994
+ });
25995
+ } catch (e) {
25996
+ reject(e);
25997
+ }
25998
+ });
25999
+ }
26000
+
25889
26001
  function getOutputFormat(dataset, opts) {
25890
26002
  var outFile = opts.file || null,
25891
26003
  inFmt = dataset.info && dataset.info.input_formats && dataset.info.input_formats[0],
@@ -25991,6 +26103,9 @@ ${svg}
25991
26103
  async function exportDatasets(datasets, opts) {
25992
26104
  var format = getOutputFormat(datasets[0], opts);
25993
26105
  var files;
26106
+ if (format != 'geoparquet' && (opts.compression || opts.level !== undefined)) {
26107
+ error('The compression= and level= options only apply to GeoParquet output');
26108
+ }
25994
26109
  if (format == PACKAGE_EXT) {
25995
26110
  opts = utils.defaults({compact: true}, opts);
25996
26111
  return exportPackedDatasets(datasets, opts);
@@ -28669,6 +28784,13 @@ ${svg}
28669
28784
  type: 'flag',
28670
28785
  describe: '[CSV] export numbers with decimal commas not points'
28671
28786
  })
28787
+ .option('compression', {
28788
+ describe: '[GeoParquet] options: snappy,zstd,none (default is snappy)'
28789
+ })
28790
+ .option('level', {
28791
+ describe: '[GeoParquet] zstd compression level',
28792
+ type: 'integer'
28793
+ })
28672
28794
  .option('show-all', {
28673
28795
  type: 'flag',
28674
28796
  describe: '[Snapshot] show all layers in Web UI'
@@ -33675,6 +33797,7 @@ ${svg}
33675
33797
  }
33676
33798
 
33677
33799
  var hyparquetPromise = null;
33800
+ var compressorsPromise = null;
33678
33801
  var dynamicImportModule = Function('id', 'return import(id)');
33679
33802
  var mproj = null;
33680
33803
 
@@ -33685,8 +33808,10 @@ ${svg}
33685
33808
  var file = await getHyparquetFile(source, hyparquet);
33686
33809
  var metadata = await hyparquet.parquetMetadataAsync(file);
33687
33810
  var geo = parseGeoParquetMetadata(metadata);
33811
+ var compressors = await loadHyparquetCompressors();
33688
33812
  var rows = await hyparquet.parquetReadObjects({
33689
33813
  file: file,
33814
+ compressors: compressors,
33690
33815
  rowFormat: 'object'
33691
33816
  });
33692
33817
  var geometryColumn = getGeoParquetGeometryColumn(rows, geo);
@@ -33726,6 +33851,26 @@ ${svg}
33726
33851
  return nodeMod.default && !nodeMod.parquetReadObjects ? nodeMod.default : nodeMod;
33727
33852
  }
33728
33853
 
33854
+ async function loadHyparquetCompressors() {
33855
+ if (runningInBrowser()) {
33856
+ return getHyparquetCompressors(require$1('hyparquet-compressors'));
33857
+ }
33858
+ if (!compressorsPromise) {
33859
+ compressorsPromise = dynamicImportModule('hyparquet-compressors');
33860
+ }
33861
+ return getHyparquetCompressors(await compressorsPromise);
33862
+ }
33863
+
33864
+ function getHyparquetCompressors(mod) {
33865
+ if (mod && mod.default && !mod.compressors) {
33866
+ mod = mod.default;
33867
+ }
33868
+ if (!mod || !mod.compressors) {
33869
+ stop$1('GeoParquet compression library is not loaded');
33870
+ }
33871
+ return mod.compressors;
33872
+ }
33873
+
33729
33874
  function getGeoParquetSource(input) {
33730
33875
  if (!input) return null;
33731
33876
  if (Object.prototype.hasOwnProperty.call(input, 'content')) {
@@ -54267,7 +54412,7 @@ ${svg}
54267
54412
  });
54268
54413
  }
54269
54414
 
54270
- var version = "0.7.8";
54415
+ var version = "0.7.9";
54271
54416
 
54272
54417
  // Parse command line args into commands and run them
54273
54418
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.7.8",
3
+ "version": "0.7.9",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -61,6 +61,7 @@
61
61
  "geographiclib-geodesic": "^2.2.0",
62
62
  "geokdbush": "^1.1.0",
63
63
  "hyparquet": "^1.25.6",
64
+ "hyparquet-compressors": "^1.1.1",
64
65
  "hyparquet-writer": "^0.14.0",
65
66
  "iconv-lite": "^0.6.3",
66
67
  "idb-keyval": "^6.2.0",
@@ -69,7 +70,8 @@
69
70
  "msgpackr": "^1.10.1",
70
71
  "open": "^11.0.0",
71
72
  "rw": "~1.3.3",
72
- "tinyqueue": "^2.0.3"
73
+ "tinyqueue": "^2.0.3",
74
+ "zstd-codec": "^0.1.5"
73
75
  },
74
76
  "devDependencies": {
75
77
  "@rollup/plugin-commonjs": "^28.0.6",
@@ -0,0 +1,62 @@
1
+ (function() {
2
+ var defaultProvider = "anthropic";
3
+
4
+ var common = {
5
+ enabled: true,
6
+ mode: "direct",
7
+ label: "Ask Mapshaper",
8
+ title: "Ask Mapshaper",
9
+ staticContextUrls: [
10
+ "/docs/ai/bot-instructions.html.md",
11
+ "/docs/ai/command-cheatsheet.html.md",
12
+ "/llms-full.txt"
13
+ ],
14
+ sendRuntimeContext: true,
15
+ sendSampleValues: false,
16
+ allowRunCommands: false
17
+ };
18
+
19
+ var providers = {
20
+ anthropic: {
21
+ provider: "anthropic",
22
+ apiKey: "",
23
+ model: "claude-sonnet-4-6",
24
+ cacheControl: {type: "ephemeral"},
25
+ cacheTtl: "1h"
26
+ },
27
+ openai: {
28
+ provider: "openai",
29
+ apiKey: "",
30
+ model: "gpt-4.1-mini"
31
+ },
32
+ gemini: {
33
+ provider: "gemini",
34
+ apiKey: "",
35
+ model: "gemini-3.1-pro-preview"
36
+ },
37
+ mock: {
38
+ mode: "mock",
39
+ provider: "mock"
40
+ }
41
+ };
42
+
43
+ function getSelectedProvider() {
44
+ var params = new URLSearchParams(window.location.search);
45
+ return params.get("ai") || defaultProvider;
46
+ }
47
+
48
+ function mergeConfig() {
49
+ var selected = getSelectedProvider();
50
+ var providerConfig = providers[selected] || providers[defaultProvider];
51
+ return Object.assign({
52
+ selectedProvider: providers[selected] ? selected : defaultProvider
53
+ }, common, providerConfig);
54
+ }
55
+
56
+ window.mapshaperAIConfigs = {
57
+ defaultProvider: defaultProvider,
58
+ common: common,
59
+ providers: providers
60
+ };
61
+ window.mapshaperAI = mergeConfig();
62
+ })();