@nshiab/simple-data-analysis-core 0.0.27 → 0.1.1

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 (113) 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 +29 -48
  5. package/esm/class/SimpleTable.d.ts.map +1 -1
  6. package/esm/class/SimpleTable.js +170 -210
  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/join.d.ts.map +1 -1
  36. package/esm/methods/join.js +1 -7
  37. package/esm/methods/joinGeo.d.ts.map +1 -1
  38. package/esm/methods/joinGeo.js +1 -14
  39. package/esm/methods/joinGeoQuery.js +1 -1
  40. package/esm/methods/randomPointQuery.d.ts +1 -1
  41. package/esm/methods/randomPointQuery.d.ts.map +1 -1
  42. package/esm/methods/randomPointQuery.js +3 -3
  43. package/esm/methods/summarizeQuery.js +1 -1
  44. package/esm/methods/writeGeoDataQuery.js +2 -2
  45. package/package.json +2 -2
  46. package/script/class/SimpleDB.d.ts +3 -8
  47. package/script/class/SimpleDB.d.ts.map +1 -1
  48. package/script/class/SimpleDB.js +8 -10
  49. package/script/class/SimpleTable.d.ts +29 -48
  50. package/script/class/SimpleTable.d.ts.map +1 -1
  51. package/script/class/SimpleTable.js +170 -210
  52. package/script/helpers/convertForJS.js +1 -1
  53. package/script/helpers/findGeoColumn.js +2 -2
  54. package/script/helpers/hasGeometryColumn.js +1 -1
  55. package/script/helpers/logData.d.ts.map +1 -1
  56. package/script/helpers/logData.js +7 -2
  57. package/script/helpers/parseTypes.d.ts +1 -1
  58. package/script/helpers/parseTypes.d.ts.map +1 -1
  59. package/script/helpers/parseTypes.js +3 -1
  60. package/script/helpers/setDbProps.d.ts +1 -1
  61. package/script/helpers/setDbProps.d.ts.map +1 -1
  62. package/script/helpers/setDbProps.js +5 -9
  63. package/script/helpers/unifyColumns.d.ts +0 -2
  64. package/script/helpers/unifyColumns.d.ts.map +1 -1
  65. package/script/helpers/unifyColumns.js +2 -6
  66. package/script/helpers/writeGeoData.d.ts.map +1 -1
  67. package/script/helpers/writeGeoData.js +11 -41
  68. package/script/helpers/writeIndexes.d.ts +3 -0
  69. package/script/helpers/writeIndexes.d.ts.map +1 -0
  70. package/script/helpers/writeIndexes.js +19 -0
  71. package/script/methods/bm25.d.ts.map +1 -1
  72. package/script/methods/bm25.js +1 -1
  73. package/script/methods/cache.js +1 -1
  74. package/script/methods/distanceQuery.d.ts.map +1 -1
  75. package/script/methods/distanceQuery.js +4 -2
  76. package/script/methods/fuzzyJoin.d.ts.map +1 -1
  77. package/script/methods/fuzzyJoin.js +1 -6
  78. package/script/methods/getGeoData.d.ts.map +1 -1
  79. package/script/methods/getGeoData.js +1 -10
  80. package/script/methods/join.d.ts.map +1 -1
  81. package/script/methods/join.js +1 -7
  82. package/script/methods/joinGeo.d.ts.map +1 -1
  83. package/script/methods/joinGeo.js +1 -14
  84. package/script/methods/joinGeoQuery.js +1 -1
  85. package/script/methods/randomPointQuery.d.ts +1 -1
  86. package/script/methods/randomPointQuery.d.ts.map +1 -1
  87. package/script/methods/randomPointQuery.js +3 -3
  88. package/script/methods/summarizeQuery.js +1 -1
  89. package/script/methods/writeGeoDataQuery.js +2 -2
  90. package/esm/helpers/getProjection.d.ts +0 -3
  91. package/esm/helpers/getProjection.d.ts.map +0 -1
  92. package/esm/helpers/getProjection.js +0 -25
  93. package/esm/helpers/getProjectionParquet.d.ts +0 -3
  94. package/esm/helpers/getProjectionParquet.d.ts.map +0 -1
  95. package/esm/helpers/getProjectionParquet.js +0 -24
  96. package/esm/helpers/shouldFlipBeforeExport.d.ts +0 -2
  97. package/esm/helpers/shouldFlipBeforeExport.d.ts.map +0 -1
  98. package/esm/helpers/shouldFlipBeforeExport.js +0 -3
  99. package/esm/helpers/writeProjectionsAndIndexes.d.ts +0 -3
  100. package/esm/helpers/writeProjectionsAndIndexes.d.ts.map +0 -1
  101. package/esm/helpers/writeProjectionsAndIndexes.js +0 -29
  102. package/script/helpers/getProjection.d.ts +0 -3
  103. package/script/helpers/getProjection.d.ts.map +0 -1
  104. package/script/helpers/getProjection.js +0 -31
  105. package/script/helpers/getProjectionParquet.d.ts +0 -3
  106. package/script/helpers/getProjectionParquet.d.ts.map +0 -1
  107. package/script/helpers/getProjectionParquet.js +0 -30
  108. package/script/helpers/shouldFlipBeforeExport.d.ts +0 -2
  109. package/script/helpers/shouldFlipBeforeExport.d.ts.map +0 -1
  110. package/script/helpers/shouldFlipBeforeExport.js +0 -6
  111. package/script/helpers/writeProjectionsAndIndexes.d.ts +0 -3
  112. package/script/helpers/writeProjectionsAndIndexes.d.ts.map +0 -1
  113. 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 geoType = await this.getProjection("geom");
441
+ if (geoType !== "GEOMETRY('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,28 +1716,21 @@ 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);
1796
- await queryDB(this, `ALTER TABLE "${this.name}" ADD "${newColumn}" ${newType};
1724
+ let spatial = "";
1725
+ if (newType.toLowerCase().includes("geometry")) {
1726
+ spatial = "INSTALL spatial; LOAD spatial; SET geometry_always_xy = true;";
1727
+ }
1728
+ await queryDB(this, `${spatial} 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
1736
  * Adds a new column to the table containing the row number, starting at 0 (like an index).
@@ -1886,15 +1810,10 @@ export default class SimpleTable extends Simple {
1886
1810
  method: "crossJoin()",
1887
1811
  parameters: { rightTable, options },
1888
1812
  }));
1889
- const allProjections = {
1890
- ...this.projections,
1891
- ...rightTable.projections,
1892
- };
1893
1813
  if (typeof options.outputTable === "string") {
1894
- return this.sdb.newTable(options.outputTable, allProjections);
1814
+ return this.sdb.newTable(options.outputTable);
1895
1815
  }
1896
1816
  else {
1897
- this.projections = allProjections;
1898
1817
  return this;
1899
1818
  }
1900
1819
  }
@@ -3029,7 +2948,7 @@ export default class SimpleTable extends Simple {
3029
2948
  }
3030
2949
  await summarize(this, options);
3031
2950
  if (typeof options.outputTable === "string") {
3032
- return this.sdb.newTable(options.outputTable, this.projections);
2951
+ return this.sdb.newTable(options.outputTable);
3033
2952
  }
3034
2953
  else {
3035
2954
  return this;
@@ -3171,7 +3090,7 @@ export default class SimpleTable extends Simple {
3171
3090
  }
3172
3091
  await correlations(this, options);
3173
3092
  if (typeof options.outputTable === "string") {
3174
- return this.sdb.newTable(options.outputTable, this.projections);
3093
+ return this.sdb.newTable(options.outputTable);
3175
3094
  }
3176
3095
  else {
3177
3096
  return this;
@@ -3228,7 +3147,7 @@ export default class SimpleTable extends Simple {
3228
3147
  }
3229
3148
  await linearRegressions(this, options);
3230
3149
  if (typeof options.outputTable === "string") {
3231
- return this.sdb.newTable(options.outputTable, this.projections);
3150
+ return this.sdb.newTable(options.outputTable);
3232
3151
  }
3233
3152
  else {
3234
3153
  return this;
@@ -4083,7 +4002,7 @@ export default class SimpleTable extends Simple {
4083
4002
  }
4084
4003
  // GEOSPATIAL
4085
4004
  /**
4086
- * 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.
4087
4006
  *
4088
4007
  * @param columnLat - The name of the column storing the latitude values.
4089
4008
  * @param columnLon - The name of the column storing the longitude values.
@@ -4099,13 +4018,12 @@ export default class SimpleTable extends Simple {
4099
4018
  */
4100
4019
  async points(columnLat, columnLon, newColumn) {
4101
4020
  await queryDB(this, (await this.getColumns()).includes(newColumn)
4102
- ? `INSTALL spatial; LOAD spatial; UPDATE "${this.name}" SET "${newColumn}" = ST_Point2D("${columnLat}", "${columnLon}")`
4103
- : `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; CREATE OR REPLACE TABLE "${this.name}" AS SELECT * REPLACE (ST_Point("${columnLon}", "${columnLat}")::GEOMETRY('EPSG:4326') AS "${newColumn}") FROM "${this.name}"`
4022
+ : `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; ALTER TABLE "${this.name}" ADD COLUMN "${newColumn}" GEOMETRY('EPSG:4326'); UPDATE "${this.name}" SET "${newColumn}" = ST_Point("${columnLon}", "${columnLat}");`, mergeOptions(this, {
4104
4023
  table: this.name,
4105
4024
  method: "points()",
4106
4025
  parameters: { columnLat, columnLon, newColumn },
4107
4026
  }));
4108
- this.projections[newColumn] = "+proj=latlong +datum=WGS84 +no_defs";
4109
4027
  }
4110
4028
  /**
4111
4029
  * Adds a column with boolean values indicating the validity of geometries.
@@ -4192,7 +4110,8 @@ export default class SimpleTable extends Simple {
4192
4110
  */
4193
4111
  async fixGeo(column) {
4194
4112
  const col = column ?? (await findGeoColumn(this));
4195
- await queryDB(this, `UPDATE "${this.name}" SET "${col}" = ST_MakeValid("${col}")`, mergeOptions(this, {
4113
+ const geoType = await this.getProjection(col);
4114
+ await queryDB(this, `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; CREATE OR REPLACE TABLE "${this.name}" AS SELECT * REPLACE (ST_MakeValid("${col}")::${geoType} AS "${col}") FROM "${this.name}"`, mergeOptions(this, {
4196
4115
  table: this.name,
4197
4116
  method: "fixGeo()",
4198
4117
  parameters: { column },
@@ -4262,7 +4181,7 @@ export default class SimpleTable extends Simple {
4262
4181
  }
4263
4182
  /**
4264
4183
  * Flips the coordinate order of geometries in a specified column (e.g., from `[lon, lat]` to `[lat, lon]` or vice-versa).
4265
- * **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`.
4184
+ * **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.
4266
4185
  *
4267
4186
  * @param column - The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.
4268
4187
  * @returns A promise that resolves when the coordinates have been flipped.
@@ -4282,7 +4201,8 @@ export default class SimpleTable extends Simple {
4282
4201
  */
4283
4202
  async flipCoordinates(column) {
4284
4203
  const col = column ?? (await findGeoColumn(this));
4285
- await queryDB(this, `UPDATE "${this.name}" SET "${col}" = ST_FlipCoordinates("${col}")`, mergeOptions(this, {
4204
+ const geoType = await this.getProjection(col);
4205
+ await queryDB(this, `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; CREATE OR REPLACE TABLE "${this.name}" AS SELECT * REPLACE (ST_FlipCoordinates("${col}")::${geoType} AS "${col}") FROM "${this.name}"`, mergeOptions(this, {
4286
4206
  table: this.name,
4287
4207
  method: "flipCoordinates()",
4288
4208
  parameters: { column },
@@ -4313,7 +4233,8 @@ export default class SimpleTable extends Simple {
4313
4233
  const column = typeof options.column === "string"
4314
4234
  ? options.column
4315
4235
  : await findGeoColumn(this);
4316
- await queryDB(this, `UPDATE "${this.name}" SET "${column}" = ST_ReducePrecision("${column}", ${1 / Math.pow(10, decimals)})`, mergeOptions(this, {
4236
+ const geoType = await this.getProjection(column);
4237
+ await queryDB(this, `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; CREATE OR REPLACE TABLE "${this.name}" AS SELECT * REPLACE (ST_ReducePrecision("${column}", ${1 / Math.pow(10, decimals)})::${geoType} AS "${column}") FROM "${this.name}"`, mergeOptions(this, {
4317
4238
  table: this.name,
4318
4239
  method: "reducePrecision()",
4319
4240
  parameters: { column, decimals },
@@ -4321,11 +4242,9 @@ export default class SimpleTable extends Simple {
4321
4242
  }
4322
4243
  /**
4323
4244
  * Reprojects the geometries in a specified column to another Spatial Reference System (SRS).
4324
- * If reprojecting to WGS84 (`"WGS84"` or `"EPSG:4326"`), the resulting geometries will have `[latitude, longitude]` axis order.
4325
4245
  *
4326
4246
  * @param to - The target SRS (e.g., `"EPSG:3347"`, `"WGS84"`).
4327
4247
  * @param options - An optional object with configuration options:
4328
- * @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.
4329
4248
  * @param options.column - The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.
4330
4249
  * @returns A promise that resolves when the geometries have been reprojected.
4331
4250
  * @category Geospatial
@@ -4338,43 +4257,25 @@ export default class SimpleTable extends Simple {
4338
4257
  *
4339
4258
  * @example
4340
4259
  * ```ts
4341
- * // Reproject geometries from EPSG:4326 to EPSG:3347, specifying the original projection
4342
- * await table.reproject("EPSG:3347", { from: "EPSG:4326" });
4343
- * ```
4344
- *
4345
- * @example
4346
- * ```ts
4347
4260
  * // Reproject geometries in a specific column named 'myGeom' to EPSG:3347
4348
- * await table.reproject("EPSG:3347", { column: "myGeom", from: "EPSG:4326" });
4261
+ * await table.reproject("EPSG:3347", { column: "myGeom" });
4349
4262
  * ```
4350
4263
  */
4351
4264
  async reproject(to, options = {}) {
4265
+ const cleanedTo = to.replace("WGS84", "EPSG:4326");
4266
+ const targetGeoType = `GEOMETRY${cleanedTo !== "null" ? `('${cleanedTo}')` : ""}`;
4352
4267
  const column = typeof options.column === "string"
4353
4268
  ? options.column
4354
4269
  : await findGeoColumn(this);
4355
- const from = options.from ?? this.projections[column];
4356
- if (typeof from !== "string" || from === "") {
4357
- throw new Error("Method reproject can't determine the original projection. Use the option 'from' to provide one.");
4358
- }
4359
- if (from === "+proj=latlong +datum=WGS84 +no_defs") {
4360
- await this.flipCoordinates(column);
4361
- }
4362
- if (to.toUpperCase() === "WGS84" || to.toUpperCase() === "EPSG:4326") {
4363
- to = "+proj=latlong +datum=WGS84 +no_defs";
4364
- }
4365
- await queryDB(this, `UPDATE "${this.name}" SET "${column}" = ST_Transform("${column}", '${from}', '${to}')`, mergeOptions(this, {
4270
+ await queryDB(this, `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; CREATE OR REPLACE TABLE "${this.name}" AS SELECT * REPLACE (ST_Transform("${column}", '${cleanedTo}')::${targetGeoType} AS "${column}") FROM "${this.name}"`, mergeOptions(this, {
4366
4271
  table: this.name,
4367
4272
  method: "reproject()",
4368
4273
  parameters: { column, to },
4369
4274
  }));
4370
- this.projections[column] = to;
4371
- if (this.projections[column] === "+proj=latlong +datum=WGS84 +no_defs") {
4372
- await this.flipCoordinates(column);
4373
- }
4374
4275
  }
4375
4276
  /**
4376
4277
  * Computes the area of geometries in square meters (`"m2"`) or optionally square kilometers (`"km2"`).
4377
- * The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84), with `[latitude, longitude]` axis order.
4278
+ * The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84).
4378
4279
  *
4379
4280
  * @param newColumn - The name of the new column where the computed areas will be stored.
4380
4281
  * @param options - An optional object with configuration options:
@@ -4413,7 +4314,7 @@ export default class SimpleTable extends Simple {
4413
4314
  }
4414
4315
  /**
4415
4316
  * Computes the length of line geometries in meters (`"m"`) or optionally kilometers (`"km"`).
4416
- * The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84), with `[latitude, longitude]` axis order.
4317
+ * The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84).
4417
4318
  *
4418
4319
  * @param newColumn - The name of the new column where the computed lengths will be stored.
4419
4320
  * @param options - An optional object with configuration options:
@@ -4452,7 +4353,7 @@ export default class SimpleTable extends Simple {
4452
4353
  }
4453
4354
  /**
4454
4355
  * Computes the perimeter of polygon geometries in meters (`"m"`) or optionally kilometers (`"km"`).
4455
- * The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84), with `[latitude, longitude]` axis order.
4356
+ * The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84).
4456
4357
  *
4457
4358
  * @param newColumn - The name of the new column where the computed perimeters will be stored.
4458
4359
  * @param options - An optional object with configuration options:
@@ -4516,14 +4417,22 @@ export default class SimpleTable extends Simple {
4516
4417
  const column = typeof options.column === "string"
4517
4418
  ? options.column
4518
4419
  : await findGeoColumn(this);
4519
- await queryDB(this, (await this.getColumns()).includes(newColumn)
4520
- ? `INSTALL spatial; LOAD spatial; UPDATE "${this.name}" SET "${newColumn}" = ST_Buffer("${column}", ${distance})`
4521
- : `INSTALL spatial; LOAD spatial; ALTER TABLE "${this.name}" ADD "${newColumn}" GEOMETRY; UPDATE "${this.name}" SET "${newColumn}" = ST_Buffer("${column}", ${distance})`, mergeOptions(this, {
4522
- table: this.name,
4523
- method: "buffer()",
4524
- parameters: { column, newColumn, distance },
4525
- }));
4526
- this.projections[newColumn] = this.projections[column];
4420
+ const geoType = await this.getProjection(column);
4421
+ const columns = await this.getColumns();
4422
+ if (columns.includes(newColumn)) {
4423
+ await queryDB(this, `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; CREATE OR REPLACE TABLE "${this.name}" AS SELECT * REPLACE (ST_Buffer("${column}", ${distance})::${geoType} AS "${newColumn}") FROM "${this.name}"`, mergeOptions(this, {
4424
+ table: this.name,
4425
+ method: "buffer()",
4426
+ parameters: { column, newColumn, distance },
4427
+ }));
4428
+ }
4429
+ else {
4430
+ await queryDB(this, `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; ALTER TABLE "${this.name}" ADD "${newColumn}" ${geoType}; UPDATE "${this.name}" SET "${newColumn}" = ST_Buffer("${column}", ${distance})`, mergeOptions(this, {
4431
+ table: this.name,
4432
+ method: "buffer()",
4433
+ parameters: { column, newColumn, distance },
4434
+ }));
4435
+ }
4527
4436
  }
4528
4437
  /**
4529
4438
  * Merges the data of this table (considered the left table) with another table (the right table) based on a spatial relationship.
@@ -4601,17 +4510,22 @@ export default class SimpleTable extends Simple {
4601
4510
  * ```
4602
4511
  */
4603
4512
  async intersection(column1, column2, newColumn) {
4604
- if (this.projections[column1] !== this.projections[column2]) {
4605
- throw new Error(`${column1} and ${column2} don't have the same projection.\n${column1}: ${this.projections[column1]}\n${column2}: ${this.projections[column2]}`);
4513
+ const geoType = await this.getProjection(column1);
4514
+ const columns = await this.getColumns();
4515
+ if (columns.includes(newColumn)) {
4516
+ await queryDB(this, `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; CREATE OR REPLACE TABLE "${this.name}" AS SELECT * REPLACE (ST_Intersection("${column1}", "${column2}")::${geoType} AS "${newColumn}") FROM "${this.name}"`, mergeOptions(this, {
4517
+ table: this.name,
4518
+ method: "intersection()",
4519
+ parameters: { column1, column2, newColumn },
4520
+ }));
4521
+ }
4522
+ else {
4523
+ await queryDB(this, `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; ALTER TABLE "${this.name}" ADD "${newColumn}" ${geoType}; UPDATE "${this.name}" SET "${newColumn}" = ST_Intersection("${column1}", "${column2}")`, mergeOptions(this, {
4524
+ table: this.name,
4525
+ method: "intersection()",
4526
+ parameters: { column1, column2, newColumn },
4527
+ }));
4606
4528
  }
4607
- await queryDB(this, (await this.getColumns()).includes(newColumn)
4608
- ? `INSTALL spatial; LOAD spatial; UPDATE "${this.name}" SET "${newColumn}" = ST_Intersection("${column1}", "${column2}")`
4609
- : `INSTALL spatial; LOAD spatial; ALTER TABLE "${this.name}" ADD "${newColumn}" GEOMETRY; UPDATE "${this.name}" SET "${newColumn}" = ST_Intersection("${column1}", "${column2}")`, mergeOptions(this, {
4610
- table: this.name,
4611
- method: "intersection()",
4612
- parameters: { column1, column2, newColumn },
4613
- }));
4614
- this.projections[newColumn] = this.projections[column1];
4615
4529
  }
4616
4530
  /**
4617
4531
  * Removes the intersection of two geometries from the first geometry, effectively computing the geometric difference.
@@ -4629,17 +4543,22 @@ export default class SimpleTable extends Simple {
4629
4543
  * ```
4630
4544
  */
4631
4545
  async removeIntersection(column1, column2, newColumn) {
4632
- if (this.projections[column1] !== this.projections[column2]) {
4633
- throw new Error(`${column1} and ${column2} don't have the same projection.\n${column1}: ${this.projections[column1]}\n${column2}: ${this.projections[column2]}`);
4546
+ const geoType = await this.getProjection(column1);
4547
+ const columns = await this.getColumns();
4548
+ if (columns.includes(newColumn)) {
4549
+ await queryDB(this, `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; CREATE OR REPLACE TABLE "${this.name}" AS SELECT * REPLACE (ST_Difference("${column1}", "${column2}")::${geoType} AS "${newColumn}") FROM "${this.name}"`, mergeOptions(this, {
4550
+ table: this.name,
4551
+ method: "removeIntersection()",
4552
+ parameters: { column1, column2, newColumn },
4553
+ }));
4554
+ }
4555
+ else {
4556
+ await queryDB(this, `ALTER TABLE "${this.name}" ADD "${newColumn}" ${geoType}; UPDATE "${this.name}" SET "${newColumn}" = ST_Difference("${column1}", "${column2}")`, mergeOptions(this, {
4557
+ table: this.name,
4558
+ method: "removeIntersection()",
4559
+ parameters: { column1, column2, newColumn },
4560
+ }));
4634
4561
  }
4635
- await queryDB(this, (await this.getColumns()).includes(newColumn)
4636
- ? `UPDATE "${this.name}" SET "${newColumn}" = ST_Difference("${column1}", "${column2}")`
4637
- : `ALTER TABLE "${this.name}" ADD "${newColumn}" GEOMETRY; UPDATE "${this.name}" SET "${newColumn}" = ST_Difference("${column1}", "${column2}")`, mergeOptions(this, {
4638
- table: this.name,
4639
- method: "removeIntersection()",
4640
- parameters: { column1, column2, newColumn },
4641
- }));
4642
- this.projections[newColumn] = this.projections[column1];
4643
4562
  }
4644
4563
  /**
4645
4564
  * Fills holes in polygon geometries.
@@ -4728,21 +4647,26 @@ export default class SimpleTable extends Simple {
4728
4647
  * ```
4729
4648
  */
4730
4649
  async union(column1, column2, newColumn) {
4731
- if (this.projections[column1] !== this.projections[column2]) {
4732
- throw new Error(`${column1} and ${column2} don't have the same projection.\n${column1}: ${this.projections[column1]}\n${column2}: ${this.projections[column2]}`);
4650
+ const geoType = await this.getProjection(column1);
4651
+ const columns = await this.getColumns();
4652
+ if (columns.includes(newColumn)) {
4653
+ await queryDB(this, `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; CREATE OR REPLACE TABLE "${this.name}" AS SELECT * REPLACE (ST_Union("${column1}", "${column2}")::${geoType} AS "${newColumn}") FROM "${this.name}"`, mergeOptions(this, {
4654
+ table: this.name,
4655
+ method: "union()",
4656
+ parameters: { column1, column2, newColumn },
4657
+ }));
4658
+ }
4659
+ else {
4660
+ await queryDB(this, `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; ALTER TABLE "${this.name}" ADD "${newColumn}" ${geoType}; UPDATE "${this.name}" SET "${newColumn}" = ST_Union("${column1}", "${column2}")`, mergeOptions(this, {
4661
+ table: this.name,
4662
+ method: "union()",
4663
+ parameters: { column1, column2, newColumn },
4664
+ }));
4733
4665
  }
4734
- await queryDB(this, (await this.getColumns()).includes(newColumn)
4735
- ? `INSTALL spatial; LOAD spatial; UPDATE "${this.name}" SET "${newColumn}" = ST_Union("${column1}", "${column2}")`
4736
- : `INSTALL spatial; LOAD spatial; ALTER TABLE "${this.name}" ADD "${newColumn}" GEOMETRY; UPDATE "${this.name}" SET "${newColumn}" = ST_Union("${column1}", "${column2}")`, mergeOptions(this, {
4737
- table: this.name,
4738
- method: "union()",
4739
- parameters: { column1, column2, newColumn },
4740
- }));
4741
- this.projections[newColumn] = this.projections[column1];
4742
4666
  }
4743
4667
  /**
4744
4668
  * Extracts the latitude and longitude coordinates from point geometries.
4745
- * The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84), with `[latitude, longitude]` axis order.
4669
+ * The input geometry is assumed to be in the EPSG:4326 coordinate system (WGS84).
4746
4670
  *
4747
4671
  * @param column - The name of the column storing the point geometries.
4748
4672
  * @param columnLat - The name of the new column where the extracted latitude values will be stored.
@@ -4757,8 +4681,8 @@ export default class SimpleTable extends Simple {
4757
4681
  * ```
4758
4682
  */
4759
4683
  async latLon(column, columnLat, columnLon) {
4760
- await queryDB(this, `ALTER TABLE "${this.name}" ADD "${columnLat}" DOUBLE; UPDATE "${this.name}" SET "${columnLat}" = ST_X("${column}");
4761
- ALTER TABLE "${this.name}" ADD "${columnLon}" DOUBLE; UPDATE "${this.name}" SET "${columnLon}" = ST_Y("${column}");`, mergeOptions(this, {
4684
+ await queryDB(this, `ALTER TABLE "${this.name}" ADD "${columnLat}" DOUBLE; UPDATE "${this.name}" SET "${columnLat}" = ST_Y("${column}");
4685
+ ALTER TABLE "${this.name}" ADD "${columnLon}" DOUBLE; UPDATE "${this.name}" SET "${columnLon}" = ST_X("${column}");`, mergeOptions(this, {
4762
4686
  table: this.name,
4763
4687
  method: "latLon()",
4764
4688
  parameters: { column, columnLon, columnLat },
@@ -4791,7 +4715,8 @@ export default class SimpleTable extends Simple {
4791
4715
  ? options.column
4792
4716
  : await findGeoColumn(this);
4793
4717
  await this.addRowNumber("rowNumberForSimplify");
4794
- await queryDB(this, `CREATE OR REPLACE TABLE "${this.name}" AS SELECT * REPLACE(ST_CoverageSimplify(ARRAY_AGG("${column}"), ${tolerance}${options.simplifyBoundary === false ? ", FAlSE" : ""}) AS "${column}") FROM "${this.name}" GROUP BY ALL;`, mergeOptions(this, {
4718
+ const geoType = await this.getProjection(column);
4719
+ await queryDB(this, `CREATE OR REPLACE TABLE "${this.name}" AS SELECT * REPLACE(ST_CoverageSimplify(ARRAY_AGG("${column}"), ${tolerance}${options.simplifyBoundary === false ? ", FAlSE" : ""})::${geoType} AS "${column}") FROM "${this.name}" GROUP BY ALL;`, mergeOptions(this, {
4795
4720
  table: this.name,
4796
4721
  method: "simplify()",
4797
4722
  parameters: { column, tolerance },
@@ -4824,14 +4749,22 @@ export default class SimpleTable extends Simple {
4824
4749
  const column = typeof options.column === "string"
4825
4750
  ? options.column
4826
4751
  : await findGeoColumn(this);
4827
- await queryDB(this, (await this.getColumns()).includes(newColumn)
4828
- ? `INSTALL spatial; LOAD spatial; UPDATE "${this.name}" SET "${newColumn}" = ST_Centroid("${column}")`
4829
- : `INSTALL spatial; LOAD spatial; ALTER TABLE "${this.name}" ADD "${newColumn}" GEOMETRY; UPDATE "${this.name}" SET "${newColumn}" = ST_Centroid("${column}")`, mergeOptions(this, {
4830
- table: this.name,
4831
- method: "centroid()",
4832
- parameters: { column, newColumn },
4833
- }));
4834
- this.projections[newColumn] = this.projections[column];
4752
+ const geoType = await this.getProjection(column);
4753
+ const columns = await this.getColumns();
4754
+ if (columns.includes(newColumn)) {
4755
+ await queryDB(this, `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; CREATE OR REPLACE TABLE "${this.name}" AS SELECT * REPLACE (ST_Centroid("${column}")::${geoType} AS "${newColumn}") FROM "${this.name}"`, mergeOptions(this, {
4756
+ table: this.name,
4757
+ method: "centroid()",
4758
+ parameters: { column, newColumn },
4759
+ }));
4760
+ }
4761
+ else {
4762
+ await queryDB(this, `INSTALL spatial; LOAD spatial; SET geometry_always_xy = true; ALTER TABLE "${this.name}" ADD "${newColumn}" ${geoType}; UPDATE "${this.name}" SET "${newColumn}" = ST_Centroid("${column}")`, mergeOptions(this, {
4763
+ table: this.name,
4764
+ method: "centroid()",
4765
+ parameters: { column, newColumn },
4766
+ }));
4767
+ }
4835
4768
  }
4836
4769
  /**
4837
4770
  * Generates a random point within the geometries of a specified column.
@@ -4867,10 +4800,11 @@ export default class SimpleTable extends Simple {
4867
4800
  const column = typeof options.column === "string"
4868
4801
  ? options.column
4869
4802
  : await findGeoColumn(this);
4870
- await queryDB(this, randomPointQuery(this.name, column, newColumn, nbPointsToTry), mergeOptions(this, {
4803
+ const geoType = await this.getProjection(column);
4804
+ await queryDB(this, randomPointQuery(this.name, column, newColumn, nbPointsToTry, geoType), mergeOptions(this, {
4871
4805
  table: this.name,
4872
4806
  method: "randomPoint()",
4873
- parameters: { column, newColumn, nbPointsToTry, options },
4807
+ parameters: { column, newColumn, nbPointsToTry, options, geoType },
4874
4808
  }));
4875
4809
  const nbNulls = await this.getNbRows({
4876
4810
  conditions: `"${newColumn}" IS NULL`,
@@ -4878,13 +4812,12 @@ export default class SimpleTable extends Simple {
4878
4812
  if (nbNulls > 0 && !options.try) {
4879
4813
  throw new Error(`${nbNulls} points could not be generated. Consider increasing nbPointsToTry or set options.try to true.`);
4880
4814
  }
4881
- this.projections[newColumn] = this.projections[column];
4882
4815
  }
4883
4816
  /**
4884
4817
  * Computes the distance between geometries in two specified columns.
4885
4818
  * By default, the distance is calculated in the Spatial Reference System (SRS) unit of the input geometries.
4886
4819
  * You can optionally specify `"spheroid"` or `"haversine"` methods to get results in meters or kilometers.
4887
- * If using `"spheroid"` or `"haversine"`, the input geometries must be in the EPSG:4326 coordinate system (WGS84), with `[latitude, longitude]` axis order.
4820
+ * If using `"spheroid"` or `"haversine"`, the input geometries must be in the EPSG:4326 coordinate system (WGS84).
4888
4821
  *
4889
4822
  * @param column1 - The name of the first column storing geometries.
4890
4823
  * @param column2 - The name of the second column storing geometries.
@@ -5000,7 +4933,7 @@ export default class SimpleTable extends Simple {
5000
4933
  parameters: { column, method, options },
5001
4934
  }));
5002
4935
  if (typeof options.outputTable === "string") {
5003
- return this.sdb.newTable(options.outputTable, this.projections);
4936
+ return this.sdb.newTable(options.outputTable);
5004
4937
  }
5005
4938
  else {
5006
4939
  return this;
@@ -5035,7 +4968,7 @@ export default class SimpleTable extends Simple {
5035
4968
  }
5036
4969
  /**
5037
4970
  * Returns the bounding box of geometries in `[minLat, minLon, maxLat, maxLon]` order.
5038
- * 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.
4971
+ * 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).
5039
4972
  *
5040
4973
  * @param column - The name of the column storing geometries. If omitted, the method will automatically attempt to find a geometry column.
5041
4974
  * @returns A promise that resolves to an array `[minLat, minLon, maxLat, maxLon]` representing the bounding box.
@@ -5058,10 +4991,10 @@ export default class SimpleTable extends Simple {
5058
4991
  async getBoundingBox(column) {
5059
4992
  const col = column ?? (await findGeoColumn(this));
5060
4993
  const result = (await queryDB(this, `SELECT
5061
- MIN(ST_XMin("${col}")) AS minX,
5062
- MIN(ST_YMin("${col}")) AS minY,
5063
- MAX(ST_XMax("${col}")) AS maxX,
5064
- MAX(ST_YMax("${col}")) AS maxY,
4994
+ MIN(ST_YMin("${col}")) AS minX,
4995
+ MIN(ST_XMin("${col}")) AS minY,
4996
+ MAX(ST_YMax("${col}")) AS maxX,
4997
+ MAX(ST_XMax("${col}")) AS maxY,
5065
4998
  from "${this.name}";`, mergeOptions(this, {
5066
4999
  table: this.name,
5067
5000
  method: "getBoundingBox()",
@@ -5073,7 +5006,6 @@ export default class SimpleTable extends Simple {
5073
5006
  /**
5074
5007
  * Returns the table's geospatial data as a GeoJSON object.
5075
5008
  * If the table has multiple geometry columns, you must specify which one to use.
5076
- * 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.
5077
5009
  *
5078
5010
  * @param column - The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.
5079
5011
  * @param options - An optional object with configuration options:
@@ -5178,8 +5110,6 @@ export default class SimpleTable extends Simple {
5178
5110
  * Writes the table's geospatial data to a file in GeoJSON, GeoParquet, or Shapefile format.
5179
5111
  * If the specified path does not exist, it will be created.
5180
5112
  *
5181
- * 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.
5182
- *
5183
5113
  * @param file - The absolute path to the output file (e.g., `"./output.geojson"`, `"./output.geoparquet"`, `"./shapefile-folder/output.shp"`).
5184
5114
  * @param options - An optional object with configuration options:
5185
5115
  * @param options.precision - For GeoJSON, the maximum number of figures after the decimal separator to write in coordinates. Defaults to `undefined` (full precision).
@@ -5433,6 +5363,24 @@ export default class SimpleTable extends Simple {
5433
5363
  console.table(await getDescription(this));
5434
5364
  }
5435
5365
  }
5366
+ /**
5367
+ * Retrieves the projection of a specified geospatial column.
5368
+ *
5369
+ * @param column - The name of the geospatial column for which to retrieve the projection.
5370
+ * @returns A promise that resolves to the projection of the specified column.
5371
+ * @category Geospatial
5372
+ *
5373
+ * @example
5374
+ * ```ts
5375
+ * // Get the projection of the 'geom' column
5376
+ * const projection = await table.getProjection("geom");
5377
+ * ```
5378
+ */
5379
+ async getProjection(column) {
5380
+ const res = (await this.sdb.customQuery(`SELECT ST_CRS("${column}") AS proj FROM "${this.name}" LIMIT 1;`, { returnDataFrom: "query" }))[0];
5381
+ const proj = res && res.proj !== "null" ? res.proj : null;
5382
+ return `GEOMETRY${proj ? `('${proj}')` : ""}`;
5383
+ }
5436
5384
  /**
5437
5385
  * Logs the projections of the geospatial data (if any) to the console.
5438
5386
  *
@@ -5447,7 +5395,19 @@ export default class SimpleTable extends Simple {
5447
5395
  */
5448
5396
  async logProjections() {
5449
5397
  console.log(`\nTable ${this.name} projections:`);
5450
- console.log(this.projections);
5398
+ const types = await this.getTypes();
5399
+ const geoColumns = Object.entries(types)
5400
+ .filter(([_, type]) => type.toLowerCase().includes("geometry"))
5401
+ .map(([column]) => column);
5402
+ if (geoColumns.length === 0) {
5403
+ console.log("No geometry columns found.");
5404
+ }
5405
+ else {
5406
+ for (const column of geoColumns) {
5407
+ const projection = types[column];
5408
+ console.log(`- Column "${column}": ${projection}`);
5409
+ }
5410
+ }
5451
5411
  return await this;
5452
5412
  }
5453
5413
  /**