@nshiab/simple-data-analysis-core 0.0.26 → 0.1.0

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.
Files changed (117) hide show
  1. package/esm/class/SimpleDB.d.ts +3 -8
  2. package/esm/class/SimpleDB.d.ts.map +1 -1
  3. package/esm/class/SimpleDB.js +8 -10
  4. package/esm/class/SimpleTable.d.ts +61 -51
  5. package/esm/class/SimpleTable.d.ts.map +1 -1
  6. package/esm/class/SimpleTable.js +132 -188
  7. package/esm/helpers/convertForJS.js +1 -1
  8. package/esm/helpers/findGeoColumn.js +2 -2
  9. package/esm/helpers/hasGeometryColumn.js +1 -1
  10. package/esm/helpers/logData.d.ts.map +1 -1
  11. package/esm/helpers/logData.js +7 -2
  12. package/esm/helpers/parseTypes.d.ts +1 -1
  13. package/esm/helpers/parseTypes.d.ts.map +1 -1
  14. package/esm/helpers/parseTypes.js +3 -1
  15. package/esm/helpers/setDbProps.d.ts +1 -1
  16. package/esm/helpers/setDbProps.d.ts.map +1 -1
  17. package/esm/helpers/setDbProps.js +5 -9
  18. package/esm/helpers/unifyColumns.d.ts +0 -2
  19. package/esm/helpers/unifyColumns.d.ts.map +1 -1
  20. package/esm/helpers/unifyColumns.js +2 -6
  21. package/esm/helpers/writeGeoData.d.ts.map +1 -1
  22. package/esm/helpers/writeGeoData.js +11 -41
  23. package/esm/helpers/writeIndexes.d.ts +3 -0
  24. package/esm/helpers/writeIndexes.d.ts.map +1 -0
  25. package/esm/helpers/writeIndexes.js +16 -0
  26. package/esm/methods/bm25.d.ts.map +1 -1
  27. package/esm/methods/bm25.js +1 -1
  28. package/esm/methods/cache.js +1 -1
  29. package/esm/methods/distanceQuery.d.ts.map +1 -1
  30. package/esm/methods/distanceQuery.js +4 -2
  31. package/esm/methods/fuzzyJoin.d.ts.map +1 -1
  32. package/esm/methods/fuzzyJoin.js +1 -6
  33. package/esm/methods/getGeoData.d.ts.map +1 -1
  34. package/esm/methods/getGeoData.js +1 -10
  35. package/esm/methods/getNbRows.d.ts +3 -1
  36. package/esm/methods/getNbRows.d.ts.map +1 -1
  37. package/esm/methods/getNbRows.js +5 -5
  38. package/esm/methods/join.d.ts.map +1 -1
  39. package/esm/methods/join.js +1 -7
  40. package/esm/methods/joinGeo.d.ts.map +1 -1
  41. package/esm/methods/joinGeo.js +1 -14
  42. package/esm/methods/joinGeoQuery.js +1 -1
  43. package/esm/methods/randomPointQuery.d.ts.map +1 -1
  44. package/esm/methods/randomPointQuery.js +60 -22
  45. package/esm/methods/summarizeQuery.js +1 -1
  46. package/esm/methods/writeGeoDataQuery.js +2 -2
  47. package/package.json +2 -2
  48. package/script/class/SimpleDB.d.ts +3 -8
  49. package/script/class/SimpleDB.d.ts.map +1 -1
  50. package/script/class/SimpleDB.js +8 -10
  51. package/script/class/SimpleTable.d.ts +61 -51
  52. package/script/class/SimpleTable.d.ts.map +1 -1
  53. package/script/class/SimpleTable.js +132 -188
  54. package/script/helpers/convertForJS.js +1 -1
  55. package/script/helpers/findGeoColumn.js +2 -2
  56. package/script/helpers/hasGeometryColumn.js +1 -1
  57. package/script/helpers/logData.d.ts.map +1 -1
  58. package/script/helpers/logData.js +7 -2
  59. package/script/helpers/parseTypes.d.ts +1 -1
  60. package/script/helpers/parseTypes.d.ts.map +1 -1
  61. package/script/helpers/parseTypes.js +3 -1
  62. package/script/helpers/setDbProps.d.ts +1 -1
  63. package/script/helpers/setDbProps.d.ts.map +1 -1
  64. package/script/helpers/setDbProps.js +5 -9
  65. package/script/helpers/unifyColumns.d.ts +0 -2
  66. package/script/helpers/unifyColumns.d.ts.map +1 -1
  67. package/script/helpers/unifyColumns.js +2 -6
  68. package/script/helpers/writeGeoData.d.ts.map +1 -1
  69. package/script/helpers/writeGeoData.js +11 -41
  70. package/script/helpers/writeIndexes.d.ts +3 -0
  71. package/script/helpers/writeIndexes.d.ts.map +1 -0
  72. package/script/helpers/writeIndexes.js +19 -0
  73. package/script/methods/bm25.d.ts.map +1 -1
  74. package/script/methods/bm25.js +1 -1
  75. package/script/methods/cache.js +1 -1
  76. package/script/methods/distanceQuery.d.ts.map +1 -1
  77. package/script/methods/distanceQuery.js +4 -2
  78. package/script/methods/fuzzyJoin.d.ts.map +1 -1
  79. package/script/methods/fuzzyJoin.js +1 -6
  80. package/script/methods/getGeoData.d.ts.map +1 -1
  81. package/script/methods/getGeoData.js +1 -10
  82. package/script/methods/getNbRows.d.ts +3 -1
  83. package/script/methods/getNbRows.d.ts.map +1 -1
  84. package/script/methods/getNbRows.js +5 -5
  85. package/script/methods/join.d.ts.map +1 -1
  86. package/script/methods/join.js +1 -7
  87. package/script/methods/joinGeo.d.ts.map +1 -1
  88. package/script/methods/joinGeo.js +1 -14
  89. package/script/methods/joinGeoQuery.js +1 -1
  90. package/script/methods/randomPointQuery.d.ts.map +1 -1
  91. package/script/methods/randomPointQuery.js +60 -22
  92. package/script/methods/summarizeQuery.js +1 -1
  93. package/script/methods/writeGeoDataQuery.js +2 -2
  94. package/esm/helpers/getProjection.d.ts +0 -3
  95. package/esm/helpers/getProjection.d.ts.map +0 -1
  96. package/esm/helpers/getProjection.js +0 -25
  97. package/esm/helpers/getProjectionParquet.d.ts +0 -3
  98. package/esm/helpers/getProjectionParquet.d.ts.map +0 -1
  99. package/esm/helpers/getProjectionParquet.js +0 -24
  100. package/esm/helpers/shouldFlipBeforeExport.d.ts +0 -2
  101. package/esm/helpers/shouldFlipBeforeExport.d.ts.map +0 -1
  102. package/esm/helpers/shouldFlipBeforeExport.js +0 -3
  103. package/esm/helpers/writeProjectionsAndIndexes.d.ts +0 -3
  104. package/esm/helpers/writeProjectionsAndIndexes.d.ts.map +0 -1
  105. package/esm/helpers/writeProjectionsAndIndexes.js +0 -29
  106. package/script/helpers/getProjection.d.ts +0 -3
  107. package/script/helpers/getProjection.d.ts.map +0 -1
  108. package/script/helpers/getProjection.js +0 -31
  109. package/script/helpers/getProjectionParquet.d.ts +0 -3
  110. package/script/helpers/getProjectionParquet.d.ts.map +0 -1
  111. package/script/helpers/getProjectionParquet.js +0 -30
  112. package/script/helpers/shouldFlipBeforeExport.d.ts +0 -2
  113. package/script/helpers/shouldFlipBeforeExport.d.ts.map +0 -1
  114. package/script/helpers/shouldFlipBeforeExport.js +0 -6
  115. package/script/helpers/writeProjectionsAndIndexes.d.ts +0 -3
  116. package/script/helpers/writeProjectionsAndIndexes.d.ts.map +0 -1
  117. package/script/helpers/writeProjectionsAndIndexes.js +0 -32
@@ -62,7 +62,6 @@ import summarize from "../methods/summarize.js";
62
62
  import correlations from "../methods/correlations.js";
63
63
  import linearRegressions from "../methods/linearRegressions.js";
64
64
  import joinGeo from "../methods/joinGeo.js";
65
- import getProjection from "../helpers/getProjection.js";
66
65
  import cache from "../methods/cache.js";
67
66
  import camelCase from "../helpers/camelCase.js";
68
67
  import formatNumber from "../helpers/formatNumber.js";
@@ -85,7 +84,6 @@ import getIdenticalColumns from "../helpers/getIdenticalColumns.js";
85
84
  import capitalizeQuery from "../methods/capitalizeQuery.js";
86
85
  import truncateQuery from "../methods/truncateQuery.js";
87
86
  import padQuery from "../methods/padQuery.js";
88
- import getProjectionParquet from "../helpers/getProjectionParquet.js";
89
87
  import hasGeometryColumn from "../helpers/hasGeometryColumn.js";
90
88
  import unifyColumns from "../helpers/unifyColumns.js";
91
89
  import accumulateQuery from "../helpers/accumulateQuery.js";
@@ -147,7 +145,6 @@ export default class SimpleTable extends Simple {
147
145
  * Creates an instance of SimpleTable.
148
146
  *
149
147
  * @param name - The name of the table.
150
- * @param projections - An object mapping column names to their geospatial projections.
151
148
  * @param simpleDB - The SimpleDB instance that this table belongs to.
152
149
  * @param options - An optional object with configuration options:
153
150
  * @param options.debug - A boolean indicating whether to enable debug mode.
@@ -156,7 +153,7 @@ export default class SimpleTable extends Simple {
156
153
  * @param options.types - A boolean indicating whether to include data types when logging a table.
157
154
  * @category Constructor
158
155
  */
159
- constructor(name, projections, simpleDB, options = {}) {
156
+ constructor(name, simpleDB, options = {}) {
160
157
  super(options);
161
158
  /**
162
159
  * Name of the table in the database.
@@ -170,19 +167,7 @@ export default class SimpleTable extends Simple {
170
167
  value: void 0
171
168
  });
172
169
  /**
173
- * The projections of the geospatial data, if any.
174
- *
175
- * @defaultValue `{}`
176
- * @category Properties
177
- */
178
- Object.defineProperty(this, "projections", {
179
- enumerable: true,
180
- configurable: true,
181
- writable: true,
182
- value: void 0
183
- });
184
- /**
185
- * The indexes of the table.
170
+ * The indexes of the table, if any.
186
171
  *
187
172
  * @defaultValue `[]`
188
173
  * @category Properties
@@ -194,7 +179,6 @@ export default class SimpleTable extends Simple {
194
179
  value: void 0
195
180
  });
196
181
  this.name = name;
197
- this.projections = projections;
198
182
  this.sdb = simpleDB;
199
183
  this.runQuery = runQuery;
200
184
  this.indexes = [];
@@ -242,8 +226,9 @@ export default class SimpleTable extends Simple {
242
226
  let spatial = "";
243
227
  if (Object.values(types)
244
228
  .map((d) => d.toLowerCase())
245
- .includes("geometry")) {
246
- spatial = "INSTALL spatial; LOAD spatial;\n";
229
+ .some((d) => d.startsWith("geometry"))) {
230
+ spatial =
231
+ "INSTALL spatial; LOAD spatial; SET geometry_always_xy = true;\n";
247
232
  }
248
233
  await queryDB(this, `${spatial}CREATE OR REPLACE TABLE "${this.name}" (${Object.keys(types)
249
234
  .map((d) => `"${d}" ${parseType(types[d])}`)
@@ -398,12 +383,10 @@ export default class SimpleTable extends Simple {
398
383
  }
399
384
  /**
400
385
  * Loads geospatial data from an external file or URL into the table.
401
- * The coordinates of files or URLs ending with `.json` or `.geojson` are automatically flipped to `[latitude, longitude]` axis order.
402
386
  *
403
387
  * @param file - The URL or absolute path to the external file containing the geospatial data.
404
388
  * @param options - An optional object with configuration options:
405
- * @param options.toWGS84 - If `true`, the method will attempt to reproject the data to WGS84 with `[latitude, longitude]` axis order. If the file is `.json` or `.geojson`, coordinates are automatically flipped, and this option has no additional effect. Defaults to `false`.
406
- * @param options.from - An optional string specifying the original projection of the data, if the method is unable to detect it automatically.
389
+ * @param options.toWGS84 - If `true`, the method will attempt to reproject the data to WGS84.
407
390
  * @returns A promise that resolves to the SimpleTable instance after the geospatial data has been loaded.
408
391
  * @category Geospatial
409
392
  *
@@ -434,7 +417,7 @@ export default class SimpleTable extends Simple {
434
417
  async loadGeoData(file, options = {}) {
435
418
  const fileExtension = getExtension(file);
436
419
  if (fileExtension === "geoparquet" || fileExtension === "parquet") {
437
- await queryDB(this, `INSTALL spatial; LOAD spatial;${file.toLowerCase().includes("http")
420
+ await queryDB(this, `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true;${file.toLowerCase().includes("http")
438
421
  ? " INSTALL https; LOAD https;"
439
422
  : ""}
440
423
  CREATE OR REPLACE TABLE "${this.name}" AS SELECT * FROM read_parquet('${cleanPath(file)}');`, mergeOptions(this, {
@@ -442,32 +425,21 @@ export default class SimpleTable extends Simple {
442
425
  method: "loadGeoData()",
443
426
  parameters: { file, options },
444
427
  }));
445
- this.projections = await getProjectionParquet(this, file);
446
- if (options.toWGS84) {
447
- console.log("\nThis file is a parquet. Option toWGS84 has no effect. Use the .reproject() method instead.\n");
448
- }
449
428
  }
450
429
  else {
451
- await queryDB(this, `INSTALL spatial; LOAD spatial;${file.toLowerCase().includes("http")
430
+ await queryDB(this, `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true;${file.toLowerCase().includes("http")
452
431
  ? " INSTALL https; LOAD https;"
453
432
  : ""}
454
- CREATE OR REPLACE TABLE "${this.name}" AS SELECT * FROM ST_Read('${file}');`, mergeOptions(this, {
433
+ CREATE OR REPLACE TABLE "${this.name}" AS SELECT * EXCLUDE OGC_FID FROM ST_Read('${file}');`, mergeOptions(this, {
455
434
  table: this.name,
456
435
  method: "loadGeoData()",
457
436
  parameters: { file, options },
458
437
  }));
459
- // column storing geometries is geom by default
460
- this.projections["geom"] = await getProjection(this.sdb, file);
461
- const extension = getExtension(file);
462
- if (extension === "json" || extension === "geojson") {
463
- await this.flipCoordinates("geom"); // column storing geometries
464
- this.projections["geom"] = "+proj=latlong +datum=WGS84 +no_defs";
465
- if (options.toWGS84) {
466
- console.log("This file is a json or geojson. Option toWGS84 has no effect.");
467
- }
468
- }
469
- else if (options.toWGS84) {
470
- await this.reproject("WGS84", { ...options, column: "geom" }); // column storing geometries is geom by default
438
+ }
439
+ if (options.toWGS84) {
440
+ const proj = await this.getProjection("geom");
441
+ if (proj !== "EPSG:4326") {
442
+ await this.reproject("EPSG:4326");
471
443
  }
472
444
  }
473
445
  return this;
@@ -759,9 +731,8 @@ export default class SimpleTable extends Simple {
759
731
  : [tablesToInsert];
760
732
  if (!await this.sdb.hasTable(this.name)) {
761
733
  await this.setTypes((await array[0].getTypes()));
762
- this.projections = structuredClone(array[0].projections);
763
734
  }
764
- // Checking columns, types and projections
735
+ // Checking columns, types
765
736
  if (!options.unifyColumns) {
766
737
  const thisColumns = (await this.getColumns()).sort().join(",");
767
738
  for (const table of array) {
@@ -773,27 +744,22 @@ export default class SimpleTable extends Simple {
773
744
  }
774
745
  const allTables = [this, ...array];
775
746
  const allTypes = {};
776
- const allProjections = {};
777
747
  for (const table of allTables) {
778
748
  const types = await table.getTypes();
779
749
  for (const key in types) {
780
750
  if (!allTypes[key]) {
781
751
  allTypes[key] = types[key];
782
- allProjections[key] = table.projections[key];
783
752
  }
784
753
  else {
785
754
  if (allTypes[key] !== types[key]) {
786
755
  throw new Error(`The column ${key} has different types in the tables.`);
787
756
  }
788
- else if (allProjections[key] !== table.projections[key]) {
789
- throw new Error(`The column ${key} has different projections in the tables.`);
790
- }
791
757
  }
792
758
  }
793
759
  }
794
760
  let columnsAdded = {};
795
761
  if (options.unifyColumns) {
796
- columnsAdded = await unifyColumns(allTables, allTypes, allProjections);
762
+ columnsAdded = await unifyColumns(allTables, allTypes);
797
763
  }
798
764
  await queryDB(this, array
799
765
  .map((tableToInsert) => `INSERT INTO "${this.name}" BY NAME SELECT * FROM "${tableToInsert.name}";`)
@@ -892,29 +858,16 @@ export default class SimpleTable extends Simple {
892
858
  const columns = typeof nameOrOptions === "object" && nameOrOptions.columns
893
859
  ? stringToArray(nameOrOptions.columns)
894
860
  : [];
895
- // Dealing with projections
896
- const clonedProjections = structuredClone(this.projections);
897
- let newProjections = {};
898
- if (columns.length > 0) {
899
- for (const col of columns) {
900
- if (clonedProjections[col]) {
901
- newProjections[col] = clonedProjections[col];
902
- }
903
- }
904
- }
905
- else {
906
- newProjections = clonedProjections;
907
- }
908
861
  // Delegate to sdb.newTable() so subclasses using tableClass work correctly.
909
862
  let clonedTable;
910
863
  const options = typeof nameOrOptions === "string"
911
864
  ? { outputTable: nameOrOptions }
912
865
  : nameOrOptions;
913
866
  if (typeof options.outputTable === "string") {
914
- clonedTable = this.sdb.newTable(options.outputTable, newProjections);
867
+ clonedTable = this.sdb.newTable(options.outputTable);
915
868
  }
916
869
  else {
917
- clonedTable = this.sdb.newTable(undefined, newProjections);
870
+ clonedTable = this.sdb.newTable(undefined);
918
871
  }
919
872
  await queryDB(this, cloneQuery(this.name, clonedTable.name, columns, options), mergeOptions(this, {
920
873
  table: clonedTable.name,
@@ -945,9 +898,6 @@ export default class SimpleTable extends Simple {
945
898
  method: "cloneColumn()",
946
899
  parameters: { originalColumn, newColumn },
947
900
  }));
948
- if (typeof this.projections[originalColumn] === "string") {
949
- this.projections[newColumn] = this.projections[originalColumn];
950
- }
951
901
  }
952
902
  /**
953
903
  * Clones a column in the table and offsets its values by a specified number of rows.
@@ -1012,9 +962,6 @@ export default class SimpleTable extends Simple {
1012
962
  method: "cloneColumnWithOffset()",
1013
963
  parameters: { originalColumn, newColumn },
1014
964
  }));
1015
- if (typeof this.projections[originalColumn] === "string") {
1016
- this.projections[newColumn] = this.projections[originalColumn];
1017
- }
1018
965
  }
1019
966
  /**
1020
967
  * Fills `NULL` values in specified columns. By default, each `NULL` is replaced with the last non-`NULL` value from the preceding row. When `interpolate` is `true`, `NULL` values are replaced using linear interpolation (or extrapolation at the ends). Pass `interpolateBy` with a real numeric or date column name to use it as the X-axis, so that interpolated values are proportional to the actual distances between X-axis values rather than treating every row as equidistant. When `interpolateBy` is set, `interpolate` is automatically assumed `true`.
@@ -1260,7 +1207,7 @@ export default class SimpleTable extends Simple {
1260
1207
  parameters: { count, options },
1261
1208
  }));
1262
1209
  if (typeof options.outputTable === "string") {
1263
- return this.sdb.newTable(options.outputTable, this.projections);
1210
+ return this.sdb.newTable(options.outputTable);
1264
1211
  }
1265
1212
  else {
1266
1213
  return this;
@@ -1533,15 +1480,6 @@ export default class SimpleTable extends Simple {
1533
1480
  method: "renameColumns()",
1534
1481
  parameters: { names },
1535
1482
  }));
1536
- // Taking care of projections
1537
- const types = await this.getTypes();
1538
- for (let i = 0; i < newNames.length; i++) {
1539
- if (types[newNames[i]] === "GEOMETRY") {
1540
- const projection = this.projections[oldNames[i]];
1541
- delete this.projections[oldNames[i]];
1542
- this.projections[newNames[i]] = projection;
1543
- }
1544
- }
1545
1483
  }
1546
1484
  /**
1547
1485
  * Cleans column names by removing non-alphanumeric characters and formatting them to camel case.
@@ -1758,12 +1696,6 @@ export default class SimpleTable extends Simple {
1758
1696
  method: "removeColumns()",
1759
1697
  parameters: { columns },
1760
1698
  }));
1761
- // Taking care of projections
1762
- for (const col of cols) {
1763
- if (Object.prototype.hasOwnProperty.call(this.projections, col)) {
1764
- delete this.projections[col];
1765
- }
1766
- }
1767
1699
  }
1768
1700
  /**
1769
1701
  * Adds a new column to the table based on a specified data type (JavaScript or SQL types) and a SQL definition.
@@ -1772,7 +1704,6 @@ export default class SimpleTable extends Simple {
1772
1704
  * @param type - The data type for the new column. Can be a JavaScript type (e.g., `"number"`, `"string"`) or a SQL type (e.g., `"integer"`, `"varchar"`).
1773
1705
  * @param definition - A SQL expression defining how the values for the new column should be computed (e.g., `"column1 + column2"`, `"ST_Centroid(geom_column)"`).
1774
1706
  * @param options - An optional object with configuration options:
1775
- * @param options.projection - Required if the new column stores geometries. Specifies the geospatial projection of the new geometry column. You can reuse the projection of an existing geometry column (available in `table.projections`).
1776
1707
  * @returns A promise that resolves when the new column has been added.
1777
1708
  * @category Column Operations
1778
1709
  *
@@ -1785,33 +1716,28 @@ export default class SimpleTable extends Simple {
1785
1716
  * @example
1786
1717
  * ```ts
1787
1718
  * // Add a new geometry column 'centroid' using the centroid of an existing 'country' geometry column
1788
- * // The projection of the new 'centroid' column is set to be the same as 'country'.
1789
- * await table.addColumn("centroid", "geometry", `ST_Centroid("country")`, {
1790
- * projection: table.projections.country,
1791
- * });
1719
+ * await table.addColumn("centroid", "geometry('EPSG:4326')", `ST_Centroid("country")`);
1792
1720
  * ```
1793
1721
  */
1794
- async addColumn(newColumn, type, definition, options = {}) {
1722
+ async addColumn(newColumn, type, definition) {
1795
1723
  const newType = parseType(type);
1724
+ // let spatial = "";
1725
+ // if (newType.toLowerCase().includes("geometry")) {
1726
+ // spatial = "INSTALL spatial; LOAD spatial; SET geometry_always_xy = true;";
1727
+ // }
1796
1728
  await queryDB(this, `ALTER TABLE "${this.name}" ADD "${newColumn}" ${newType};
1797
1729
  UPDATE "${this.name}" SET "${newColumn}" = ${definition}`, mergeOptions(this, {
1798
1730
  table: this.name,
1799
1731
  method: "addColumn()",
1800
1732
  parameters: { newColumn, type, definition },
1801
1733
  }));
1802
- if (newType === "GEOMETRY") {
1803
- if (typeof options.projection === "string") {
1804
- this.projections[newColumn] = options.projection;
1805
- }
1806
- else {
1807
- throw new Error("You are creating a new column storing geometries. You must specify a projection. See examples in documentation.");
1808
- }
1809
- }
1810
1734
  }
1811
1735
  /**
1812
- * Adds a new column to the table containing the row number.
1736
+ * Adds a new column to the table containing the row number, starting at 0 (like an index).
1813
1737
  *
1814
1738
  * @param newColumn - The name of the new column that will store the row number.
1739
+ * @param options - An optional object with configuration options:
1740
+ * @param options.categories - A string or an array of strings representing columns to partition the data by. The row number will restart at 0 for each unique combination of values in these columns.
1815
1741
  * @returns A promise that resolves when the row number column has been added.
1816
1742
  * @category Column Operations
1817
1743
  *
@@ -1820,12 +1746,24 @@ export default class SimpleTable extends Simple {
1820
1746
  * // Add a new column named 'rowNumber' with the row number for each row
1821
1747
  * await table.addRowNumber("rowNumber");
1822
1748
  * ```
1749
+ *
1750
+ * @example
1751
+ * ```ts
1752
+ * // Add a new column named 'rowNumber' with the row number for each 'category'
1753
+ * await table.addRowNumber("rowNumber", { categories: "category" });
1754
+ * ```
1823
1755
  */
1824
- async addRowNumber(newColumn) {
1825
- await queryDB(this, `CREATE OR REPLACE TABLE "${this.name}" AS SELECT *, ROW_NUMBER() OVER() AS ${newColumn} FROM "${this.name}"`, mergeOptions(this, {
1756
+ async addRowNumber(newColumn, options = {}) {
1757
+ const categories = options.categories
1758
+ ? stringToArray(options.categories)
1759
+ : [];
1760
+ const partition = categories.length > 0
1761
+ ? `PARTITION BY ${categories.map((d) => `"${d}"`).join(", ")}`
1762
+ : "";
1763
+ await queryDB(this, `CREATE OR REPLACE TABLE "${this.name}" AS SELECT *, (ROW_NUMBER() OVER(${partition} ORDER BY rowid) - 1) AS "${newColumn}" FROM "${this.name}" ORDER BY rowid`, mergeOptions(this, {
1826
1764
  table: this.name,
1827
1765
  method: "addRowNumber()",
1828
- parameters: { newColumn },
1766
+ parameters: { newColumn, options },
1829
1767
  }));
1830
1768
  }
1831
1769
  /**
@@ -1872,15 +1810,10 @@ export default class SimpleTable extends Simple {
1872
1810
  method: "crossJoin()",
1873
1811
  parameters: { rightTable, options },
1874
1812
  }));
1875
- const allProjections = {
1876
- ...this.projections,
1877
- ...rightTable.projections,
1878
- };
1879
1813
  if (typeof options.outputTable === "string") {
1880
- return this.sdb.newTable(options.outputTable, allProjections);
1814
+ return this.sdb.newTable(options.outputTable);
1881
1815
  }
1882
1816
  else {
1883
- this.projections = allProjections;
1884
1817
  return this;
1885
1818
  }
1886
1819
  }
@@ -3015,7 +2948,7 @@ export default class SimpleTable extends Simple {
3015
2948
  }
3016
2949
  await summarize(this, options);
3017
2950
  if (typeof options.outputTable === "string") {
3018
- return this.sdb.newTable(options.outputTable, this.projections);
2951
+ return this.sdb.newTable(options.outputTable);
3019
2952
  }
3020
2953
  else {
3021
2954
  return this;
@@ -3157,7 +3090,7 @@ export default class SimpleTable extends Simple {
3157
3090
  }
3158
3091
  await correlations(this, options);
3159
3092
  if (typeof options.outputTable === "string") {
3160
- return this.sdb.newTable(options.outputTable, this.projections);
3093
+ return this.sdb.newTable(options.outputTable);
3161
3094
  }
3162
3095
  else {
3163
3096
  return this;
@@ -3214,7 +3147,7 @@ export default class SimpleTable extends Simple {
3214
3147
  }
3215
3148
  await linearRegressions(this, options);
3216
3149
  if (typeof options.outputTable === "string") {
3217
- return this.sdb.newTable(options.outputTable, this.projections);
3150
+ return this.sdb.newTable(options.outputTable);
3218
3151
  }
3219
3152
  else {
3220
3153
  return this;
@@ -3512,6 +3445,8 @@ export default class SimpleTable extends Simple {
3512
3445
  /**
3513
3446
  * Returns the number of rows in the table.
3514
3447
  *
3448
+ * @param options - An optional object with configuration options:
3449
+ * @param options.conditions - The filtering conditions specified as a SQL `WHERE` clause (e.g., `"category = 'Book'"`).
3515
3450
  * @returns A promise that resolves to a number representing the total count of rows.
3516
3451
  * @category Getting Data
3517
3452
  *
@@ -3521,9 +3456,16 @@ export default class SimpleTable extends Simple {
3521
3456
  * const nbRows = await table.getNbRows();
3522
3457
  * console.log(nbRows); // e.g., 100
3523
3458
  * ```
3459
+ *
3460
+ * @example
3461
+ * ```ts
3462
+ * // Get the number of rows where 'category' is 'Book'
3463
+ * const nbBooks = await table.getNbRows({ conditions: "category = 'Book'" });
3464
+ * console.log(nbBooks);
3465
+ * ```
3524
3466
  */
3525
- async getNbRows() {
3526
- return await getNbRows(this);
3467
+ async getNbRows(options = {}) {
3468
+ return await getNbRows(this, options);
3527
3469
  }
3528
3470
  /**
3529
3471
  * Returns the total number of values in the table (number of columns multiplied by the number of rows).
@@ -4060,7 +4002,7 @@ export default class SimpleTable extends Simple {
4060
4002
  }
4061
4003
  // GEOSPATIAL
4062
4004
  /**
4063
- * Creates point geometries from longitude and latitude columns. The geometries will have `[latitude, longitude]` axis order.
4005
+ * Creates point geometries from latitude and longitude columns.
4064
4006
  *
4065
4007
  * @param columnLat - The name of the column storing the latitude values.
4066
4008
  * @param columnLon - The name of the column storing the longitude values.
@@ -4076,13 +4018,12 @@ export default class SimpleTable extends Simple {
4076
4018
  */
4077
4019
  async points(columnLat, columnLon, newColumn) {
4078
4020
  await queryDB(this, (await this.getColumns()).includes(newColumn)
4079
- ? `INSTALL spatial; LOAD spatial; UPDATE "${this.name}" SET "${newColumn}" = ST_Point2D("${columnLat}", "${columnLon}")`
4080
- : `INSTALL spatial; LOAD spatial; ALTER TABLE "${this.name}" ADD COLUMN "${newColumn}" GEOMETRY; UPDATE "${this.name}" SET "${newColumn}" = ST_Point2D("${columnLat}", "${columnLon}")`, mergeOptions(this, {
4021
+ ? `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; UPDATE "${this.name}" SET "${newColumn}" = ST_Point2D("${columnLon}", "${columnLat}")`
4022
+ : `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; ALTER TABLE "${this.name}" ADD COLUMN "${newColumn}" GEOMETRY; UPDATE "${this.name}" SET "${newColumn}" = ST_Point2D("${columnLon}", "${columnLat}")`, mergeOptions(this, {
4081
4023
  table: this.name,
4082
4024
  method: "points()",
4083
4025
  parameters: { columnLat, columnLon, newColumn },
4084
4026
  }));
4085
- this.projections[newColumn] = "+proj=latlong +datum=WGS84 +no_defs";
4086
4027
  }
4087
4028
  /**
4088
4029
  * Adds a column with boolean values indicating the validity of geometries.
@@ -4239,7 +4180,7 @@ export default class SimpleTable extends Simple {
4239
4180
  }
4240
4181
  /**
4241
4182
  * Flips the coordinate order of geometries in a specified column (e.g., from `[lon, lat]` to `[lat, lon]` or vice-versa).
4242
- * **Warning:** This method should be used with caution as it directly manipulates coordinate order and can affect the accuracy of geospatial operations if not used correctly. It also messes up with the projections stored in `table.projections`.
4183
+ * **Warning:** This method should be used with caution as it directly manipulates coordinate order and can affect the accuracy of geospatial operations if not used correctly.
4243
4184
  *
4244
4185
  * @param column - The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.
4245
4186
  * @returns A promise that resolves when the coordinates have been flipped.
@@ -4298,11 +4239,9 @@ export default class SimpleTable extends Simple {
4298
4239
  }
4299
4240
  /**
4300
4241
  * Reprojects the geometries in a specified column to another Spatial Reference System (SRS).
4301
- * If reprojecting to WGS84 (`"WGS84"` or `"EPSG:4326"`), the resulting geometries will have `[latitude, longitude]` axis order.
4302
4242
  *
4303
4243
  * @param to - The target SRS (e.g., `"EPSG:3347"`, `"WGS84"`).
4304
4244
  * @param options - An optional object with configuration options:
4305
- * @param options.from - The original projection of the geometries. If omitted, the method attempts to automatically detect it. Provide this option if auto-detection fails.
4306
4245
  * @param options.column - The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.
4307
4246
  * @returns A promise that resolves when the geometries have been reprojected.
4308
4247
  * @category Geospatial
@@ -4315,43 +4254,24 @@ export default class SimpleTable extends Simple {
4315
4254
  *
4316
4255
  * @example
4317
4256
  * ```ts
4318
- * // Reproject geometries from EPSG:4326 to EPSG:3347, specifying the original projection
4319
- * await table.reproject("EPSG:3347", { from: "EPSG:4326" });
4320
- * ```
4321
- *
4322
- * @example
4323
- * ```ts
4324
4257
  * // Reproject geometries in a specific column named 'myGeom' to EPSG:3347
4325
- * await table.reproject("EPSG:3347", { column: "myGeom", from: "EPSG:4326" });
4258
+ * await table.reproject("EPSG:3347", { column: "myGeom" });
4326
4259
  * ```
4327
4260
  */
4328
4261
  async reproject(to, options = {}) {
4262
+ const cleanedTo = to.replace("WGS84", "EPSG:4326");
4329
4263
  const column = typeof options.column === "string"
4330
4264
  ? options.column
4331
4265
  : await findGeoColumn(this);
4332
- const from = options.from ?? this.projections[column];
4333
- if (typeof from !== "string" || from === "") {
4334
- throw new Error("Method reproject can't determine the original projection. Use the option 'from' to provide one.");
4335
- }
4336
- if (from === "+proj=latlong +datum=WGS84 +no_defs") {
4337
- await this.flipCoordinates(column);
4338
- }
4339
- if (to.toUpperCase() === "WGS84" || to.toUpperCase() === "EPSG:4326") {
4340
- to = "+proj=latlong +datum=WGS84 +no_defs";
4341
- }
4342
- await queryDB(this, `UPDATE "${this.name}" SET "${column}" = ST_Transform("${column}", '${from}', '${to}')`, mergeOptions(this, {
4266
+ await queryDB(this, `ALTER TABLE "${this.name}" ADD COLUMN "${column}_reprojected" GEOMETRY('${cleanedTo}'); UPDATE "${this.name}" SET "${column}_reprojected" = ST_Transform("${column}", '${cleanedTo}'); ALTER TABLE "${this.name}" DROP COLUMN "${column}"; ALTER TABLE "${this.name}" RENAME COLUMN "${column}_reprojected" TO "${column}";`, mergeOptions(this, {
4343
4267
  table: this.name,
4344
4268
  method: "reproject()",
4345
4269
  parameters: { column, to },
4346
4270
  }));
4347
- this.projections[column] = to;
4348
- if (this.projections[column] === "+proj=latlong +datum=WGS84 +no_defs") {
4349
- await this.flipCoordinates(column);
4350
- }
4351
4271
  }
4352
4272
  /**
4353
4273
  * Computes the area of geometries in square meters (`"m2"`) or optionally square kilometers (`"km2"`).
4354
- * The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84), with `[latitude, longitude]` axis order.
4274
+ * The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84).
4355
4275
  *
4356
4276
  * @param newColumn - The name of the new column where the computed areas will be stored.
4357
4277
  * @param options - An optional object with configuration options:
@@ -4390,7 +4310,7 @@ export default class SimpleTable extends Simple {
4390
4310
  }
4391
4311
  /**
4392
4312
  * Computes the length of line geometries in meters (`"m"`) or optionally kilometers (`"km"`).
4393
- * The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84), with `[latitude, longitude]` axis order.
4313
+ * The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84).
4394
4314
  *
4395
4315
  * @param newColumn - The name of the new column where the computed lengths will be stored.
4396
4316
  * @param options - An optional object with configuration options:
@@ -4429,7 +4349,7 @@ export default class SimpleTable extends Simple {
4429
4349
  }
4430
4350
  /**
4431
4351
  * Computes the perimeter of polygon geometries in meters (`"m"`) or optionally kilometers (`"km"`).
4432
- * The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84), with `[latitude, longitude]` axis order.
4352
+ * The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84).
4433
4353
  *
4434
4354
  * @param newColumn - The name of the new column where the computed perimeters will be stored.
4435
4355
  * @param options - An optional object with configuration options:
@@ -4493,14 +4413,14 @@ export default class SimpleTable extends Simple {
4493
4413
  const column = typeof options.column === "string"
4494
4414
  ? options.column
4495
4415
  : await findGeoColumn(this);
4416
+ const proj = await this.getProjection(column);
4496
4417
  await queryDB(this, (await this.getColumns()).includes(newColumn)
4497
- ? `INSTALL spatial; LOAD spatial; UPDATE "${this.name}" SET "${newColumn}" = ST_Buffer("${column}", ${distance})`
4498
- : `INSTALL spatial; LOAD spatial; ALTER TABLE "${this.name}" ADD "${newColumn}" GEOMETRY; UPDATE "${this.name}" SET "${newColumn}" = ST_Buffer("${column}", ${distance})`, mergeOptions(this, {
4418
+ ? `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; UPDATE "${this.name}" SET "${newColumn}" = ST_Buffer("${column}", ${distance})`
4419
+ : `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; ALTER TABLE "${this.name}" ADD "${newColumn}" GEOMETRY('${proj}'); UPDATE "${this.name}" SET "${newColumn}" = ST_Buffer("${column}", ${distance})`, mergeOptions(this, {
4499
4420
  table: this.name,
4500
4421
  method: "buffer()",
4501
4422
  parameters: { column, newColumn, distance },
4502
4423
  }));
4503
- this.projections[newColumn] = this.projections[column];
4504
4424
  }
4505
4425
  /**
4506
4426
  * Merges the data of this table (considered the left table) with another table (the right table) based on a spatial relationship.
@@ -4578,17 +4498,13 @@ export default class SimpleTable extends Simple {
4578
4498
  * ```
4579
4499
  */
4580
4500
  async intersection(column1, column2, newColumn) {
4581
- if (this.projections[column1] !== this.projections[column2]) {
4582
- throw new Error(`${column1} and ${column2} don't have the same projection.\n${column1}: ${this.projections[column1]}\n${column2}: ${this.projections[column2]}`);
4583
- }
4584
4501
  await queryDB(this, (await this.getColumns()).includes(newColumn)
4585
- ? `INSTALL spatial; LOAD spatial; UPDATE "${this.name}" SET "${newColumn}" = ST_Intersection("${column1}", "${column2}")`
4586
- : `INSTALL spatial; LOAD spatial; ALTER TABLE "${this.name}" ADD "${newColumn}" GEOMETRY; UPDATE "${this.name}" SET "${newColumn}" = ST_Intersection("${column1}", "${column2}")`, mergeOptions(this, {
4502
+ ? `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; UPDATE "${this.name}" SET "${newColumn}" = ST_Intersection("${column1}", "${column2}")`
4503
+ : `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; ALTER TABLE "${this.name}" ADD "${newColumn}" GEOMETRY; UPDATE "${this.name}" SET "${newColumn}" = ST_Intersection("${column1}", "${column2}")`, mergeOptions(this, {
4587
4504
  table: this.name,
4588
4505
  method: "intersection()",
4589
4506
  parameters: { column1, column2, newColumn },
4590
4507
  }));
4591
- this.projections[newColumn] = this.projections[column1];
4592
4508
  }
4593
4509
  /**
4594
4510
  * Removes the intersection of two geometries from the first geometry, effectively computing the geometric difference.
@@ -4606,9 +4522,6 @@ export default class SimpleTable extends Simple {
4606
4522
  * ```
4607
4523
  */
4608
4524
  async removeIntersection(column1, column2, newColumn) {
4609
- if (this.projections[column1] !== this.projections[column2]) {
4610
- throw new Error(`${column1} and ${column2} don't have the same projection.\n${column1}: ${this.projections[column1]}\n${column2}: ${this.projections[column2]}`);
4611
- }
4612
4525
  await queryDB(this, (await this.getColumns()).includes(newColumn)
4613
4526
  ? `UPDATE "${this.name}" SET "${newColumn}" = ST_Difference("${column1}", "${column2}")`
4614
4527
  : `ALTER TABLE "${this.name}" ADD "${newColumn}" GEOMETRY; UPDATE "${this.name}" SET "${newColumn}" = ST_Difference("${column1}", "${column2}")`, mergeOptions(this, {
@@ -4616,7 +4529,6 @@ export default class SimpleTable extends Simple {
4616
4529
  method: "removeIntersection()",
4617
4530
  parameters: { column1, column2, newColumn },
4618
4531
  }));
4619
- this.projections[newColumn] = this.projections[column1];
4620
4532
  }
4621
4533
  /**
4622
4534
  * Fills holes in polygon geometries.
@@ -4705,21 +4617,17 @@ export default class SimpleTable extends Simple {
4705
4617
  * ```
4706
4618
  */
4707
4619
  async union(column1, column2, newColumn) {
4708
- if (this.projections[column1] !== this.projections[column2]) {
4709
- throw new Error(`${column1} and ${column2} don't have the same projection.\n${column1}: ${this.projections[column1]}\n${column2}: ${this.projections[column2]}`);
4710
- }
4711
4620
  await queryDB(this, (await this.getColumns()).includes(newColumn)
4712
- ? `INSTALL spatial; LOAD spatial; UPDATE "${this.name}" SET "${newColumn}" = ST_Union("${column1}", "${column2}")`
4713
- : `INSTALL spatial; LOAD spatial; ALTER TABLE "${this.name}" ADD "${newColumn}" GEOMETRY; UPDATE "${this.name}" SET "${newColumn}" = ST_Union("${column1}", "${column2}")`, mergeOptions(this, {
4621
+ ? `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; UPDATE "${this.name}" SET "${newColumn}" = ST_Union("${column1}", "${column2}")`
4622
+ : `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; ALTER TABLE "${this.name}" ADD "${newColumn}" GEOMETRY; UPDATE "${this.name}" SET "${newColumn}" = ST_Union("${column1}", "${column2}")`, mergeOptions(this, {
4714
4623
  table: this.name,
4715
4624
  method: "union()",
4716
4625
  parameters: { column1, column2, newColumn },
4717
4626
  }));
4718
- this.projections[newColumn] = this.projections[column1];
4719
4627
  }
4720
4628
  /**
4721
4629
  * Extracts the latitude and longitude coordinates from point geometries.
4722
- * The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84), with `[latitude, longitude]` axis order.
4630
+ * The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84).
4723
4631
  *
4724
4632
  * @param column - The name of the column storing the point geometries.
4725
4633
  * @param columnLat - The name of the new column where the extracted latitude values will be stored.
@@ -4734,8 +4642,8 @@ export default class SimpleTable extends Simple {
4734
4642
  * ```
4735
4643
  */
4736
4644
  async latLon(column, columnLat, columnLon) {
4737
- await queryDB(this, `ALTER TABLE "${this.name}" ADD "${columnLat}" DOUBLE; UPDATE "${this.name}" SET "${columnLat}" = ST_X("${column}");
4738
- ALTER TABLE "${this.name}" ADD "${columnLon}" DOUBLE; UPDATE "${this.name}" SET "${columnLon}" = ST_Y("${column}");`, mergeOptions(this, {
4645
+ await queryDB(this, `ALTER TABLE "${this.name}" ADD "${columnLat}" DOUBLE; UPDATE "${this.name}" SET "${columnLat}" = ST_Y("${column}");
4646
+ ALTER TABLE "${this.name}" ADD "${columnLon}" DOUBLE; UPDATE "${this.name}" SET "${columnLon}" = ST_X("${column}");`, mergeOptions(this, {
4739
4647
  table: this.name,
4740
4648
  method: "latLon()",
4741
4649
  parameters: { column, columnLon, columnLat },
@@ -4802,13 +4710,12 @@ export default class SimpleTable extends Simple {
4802
4710
  ? options.column
4803
4711
  : await findGeoColumn(this);
4804
4712
  await queryDB(this, (await this.getColumns()).includes(newColumn)
4805
- ? `INSTALL spatial; LOAD spatial; UPDATE "${this.name}" SET "${newColumn}" = ST_Centroid("${column}")`
4806
- : `INSTALL spatial; LOAD spatial; ALTER TABLE "${this.name}" ADD "${newColumn}" GEOMETRY; UPDATE "${this.name}" SET "${newColumn}" = ST_Centroid("${column}")`, mergeOptions(this, {
4713
+ ? `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; UPDATE "${this.name}" SET "${newColumn}" = ST_Centroid("${column}")`
4714
+ : `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; ALTER TABLE "${this.name}" ADD "${newColumn}" GEOMETRY; UPDATE "${this.name}" SET "${newColumn}" = ST_Centroid("${column}")`, mergeOptions(this, {
4807
4715
  table: this.name,
4808
4716
  method: "centroid()",
4809
4717
  parameters: { column, newColumn },
4810
4718
  }));
4811
- this.projections[newColumn] = this.projections[column];
4812
4719
  }
4813
4720
  /**
4814
4721
  * Generates a random point within the geometries of a specified column.
@@ -4817,6 +4724,7 @@ export default class SimpleTable extends Simple {
4817
4724
  * @param nbPointsToTry - The number of points to generate within the bounding box of each geometry to find one that is within the geometry itself.
4818
4725
  * @param options - An optional object with configuration options:
4819
4726
  * @param options.column - The name of the column storing the geometries within which the random points will be generated. If omitted, the method will automatically attempt to find a geometry column.
4727
+ * @param options.try - If `true`, the method will not throw an error if some points cannot be generated. Corresponding rows will have `NULL` in the new column.
4820
4728
  *
4821
4729
  * @example
4822
4730
  * ```ts
@@ -4829,10 +4737,16 @@ export default class SimpleTable extends Simple {
4829
4737
  * // Generate a random point for each geometry in a specific column named 'areaGeom', trying 50 points
4830
4738
  * await table.randomPoint("pointInArea", 50, { column: "areaGeom" });
4831
4739
  * ```
4740
+ *
4741
+ * @example
4742
+ * ```ts
4743
+ * // Generate a random point for each geometry, but don't throw if some points cannot be generated
4744
+ * await table.randomPoint("pointInArea", 1, { try: true });
4745
+ * ```
4832
4746
  */
4833
4747
  async randomPoint(newColumn, nbPointsToTry, options = {}) {
4834
- if (typeof nbPointsToTry !== "number" || nbPointsToTry < 1) {
4835
- throw new Error("nbPointsToTry must be a number greater than 0");
4748
+ if (typeof nbPointsToTry !== "number" || nbPointsToTry < 0) {
4749
+ throw new Error("nbPointsToTry must be a number greater than or equal to 0");
4836
4750
  }
4837
4751
  const column = typeof options.column === "string"
4838
4752
  ? options.column
@@ -4840,15 +4754,20 @@ export default class SimpleTable extends Simple {
4840
4754
  await queryDB(this, randomPointQuery(this.name, column, newColumn, nbPointsToTry), mergeOptions(this, {
4841
4755
  table: this.name,
4842
4756
  method: "randomPoint()",
4843
- parameters: { column, newColumn, nbPointsToTry },
4757
+ parameters: { column, newColumn, nbPointsToTry, options },
4844
4758
  }));
4845
- this.projections[newColumn] = this.projections[column];
4759
+ const nbNulls = await this.getNbRows({
4760
+ conditions: `"${newColumn}" IS NULL`,
4761
+ });
4762
+ if (nbNulls > 0 && !options.try) {
4763
+ throw new Error(`${nbNulls} points could not be generated. Consider increasing nbPointsToTry or set options.try to true.`);
4764
+ }
4846
4765
  }
4847
4766
  /**
4848
4767
  * Computes the distance between geometries in two specified columns.
4849
4768
  * By default, the distance is calculated in the Spatial Reference System (SRS) unit of the input geometries.
4850
4769
  * You can optionally specify `"spheroid"` or `"haversine"` methods to get results in meters or kilometers.
4851
- * If using `"spheroid"` or `"haversine"`, the input geometries must be in the EPSG:4326 coordinate system (WGS84), with `[latitude, longitude]` axis order.
4770
+ * If using `"spheroid"` or `"haversine"`, the input geometries must be in the EPSG:4326 coordinate system (WGS84).
4852
4771
  *
4853
4772
  * @param column1 - The name of the first column storing geometries.
4854
4773
  * @param column2 - The name of the second column storing geometries.
@@ -4964,7 +4883,7 @@ export default class SimpleTable extends Simple {
4964
4883
  parameters: { column, method, options },
4965
4884
  }));
4966
4885
  if (typeof options.outputTable === "string") {
4967
- return this.sdb.newTable(options.outputTable, this.projections);
4886
+ return this.sdb.newTable(options.outputTable);
4968
4887
  }
4969
4888
  else {
4970
4889
  return this;
@@ -4999,7 +4918,7 @@ export default class SimpleTable extends Simple {
4999
4918
  }
5000
4919
  /**
5001
4920
  * Returns the bounding box of geometries in `[minLat, minLon, maxLat, maxLon]` order.
5002
- * By default, the method will try to find the column with the geometries. The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84), with `[latitude, longitude]` axis order.
4921
+ * By default, the method will try to find the column with the geometries. The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84).
5003
4922
  *
5004
4923
  * @param column - The name of the column storing geometries. If omitted, the method will automatically attempt to find a geometry column.
5005
4924
  * @returns A promise that resolves to an array `[minLat, minLon, maxLat, maxLon]` representing the bounding box.
@@ -5022,10 +4941,10 @@ export default class SimpleTable extends Simple {
5022
4941
  async getBoundingBox(column) {
5023
4942
  const col = column ?? (await findGeoColumn(this));
5024
4943
  const result = (await queryDB(this, `SELECT
5025
- MIN(ST_XMin("${col}")) AS minX,
5026
- MIN(ST_YMin("${col}")) AS minY,
5027
- MAX(ST_XMax("${col}")) AS maxX,
5028
- MAX(ST_YMax("${col}")) AS maxY,
4944
+ MIN(ST_YMin("${col}")) AS minX,
4945
+ MIN(ST_XMin("${col}")) AS minY,
4946
+ MAX(ST_YMax("${col}")) AS maxX,
4947
+ MAX(ST_XMax("${col}")) AS maxY,
5029
4948
  from "${this.name}";`, mergeOptions(this, {
5030
4949
  table: this.name,
5031
4950
  method: "getBoundingBox()",
@@ -5037,7 +4956,6 @@ export default class SimpleTable extends Simple {
5037
4956
  /**
5038
4957
  * Returns the table's geospatial data as a GeoJSON object.
5039
4958
  * If the table has multiple geometry columns, you must specify which one to use.
5040
- * If the geometry column's projection is WGS84 or EPSG:4326 (`[latitude, longitude]` axis order), the coordinates will be flipped to follow the RFC7946 standard (`[longitude, latitude]` axis order) in the output GeoJSON.
5041
4959
  *
5042
4960
  * @param column - The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.
5043
4961
  * @param options - An optional object with configuration options:
@@ -5142,8 +5060,6 @@ export default class SimpleTable extends Simple {
5142
5060
  * Writes the table's geospatial data to a file in GeoJSON, GeoParquet, or Shapefile format.
5143
5061
  * If the specified path does not exist, it will be created.
5144
5062
  *
5145
- * For GeoJSON files (`.geojson` or `.json`), if the projection is WGS84 or EPSG:4326 (`[latitude, longitude]` axis order), the coordinates will be flipped to follow the RFC7946 standard (`[longitude, latitude]` axis order) in the output.
5146
- *
5147
5063
  * @param file - The absolute path to the output file (e.g., `"./output.geojson"`, `"./output.geoparquet"`, `"./shapefile-folder/output.shp"`).
5148
5064
  * @param options - An optional object with configuration options:
5149
5065
  * @param options.precision - For GeoJSON, the maximum number of figures after the decimal separator to write in coordinates. Defaults to `undefined` (full precision).
@@ -5397,6 +5313,22 @@ export default class SimpleTable extends Simple {
5397
5313
  console.table(await getDescription(this));
5398
5314
  }
5399
5315
  }
5316
+ /**
5317
+ * Retrieves the projection of a specified geospatial column.
5318
+ *
5319
+ * @param column - The name of the geospatial column for which to retrieve the projection.
5320
+ * @returns A promise that resolves to the projection of the specified column.
5321
+ * @category Geospatial
5322
+ *
5323
+ * @example
5324
+ * ```ts
5325
+ * // Get the projection of the 'geom' column
5326
+ * const projection = await table.getProjection("geom");
5327
+ * ```
5328
+ */
5329
+ async getProjection(column) {
5330
+ return (await this.sdb.customQuery(`SELECT ST_CRS("${column}") AS proj FROM "${this.name}" LIMIT 1;`, { returnDataFrom: "query" }))[0].proj;
5331
+ }
5400
5332
  /**
5401
5333
  * Logs the projections of the geospatial data (if any) to the console.
5402
5334
  *
@@ -5411,7 +5343,19 @@ export default class SimpleTable extends Simple {
5411
5343
  */
5412
5344
  async logProjections() {
5413
5345
  console.log(`\nTable ${this.name} projections:`);
5414
- console.log(this.projections);
5346
+ const types = await this.getTypes();
5347
+ const geoColumns = Object.entries(types)
5348
+ .filter(([_, type]) => type.toLowerCase().includes("geometry"))
5349
+ .map(([column]) => column);
5350
+ if (geoColumns.length === 0) {
5351
+ console.log("No geometry columns found.");
5352
+ }
5353
+ else {
5354
+ for (const column of geoColumns) {
5355
+ const projection = types[column];
5356
+ console.log(`- Column "${column}": ${projection}`);
5357
+ }
5358
+ }
5415
5359
  return await this;
5416
5360
  }
5417
5361
  /**