lakeql 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -5
- package/dist/bin.js +816 -23
- package/dist/{chunk-BFLGC6Y5.js → chunk-2XZJK7EF.js} +398 -71
- package/dist/{chunk-TFD5RFKB.js → chunk-4VJQ56HF.js} +424 -4
- package/dist/{chunk-RZL45ZSN.js → chunk-6XXXYVXT.js} +4048 -3341
- package/dist/chunk-ZVHJD6R3.js +1175 -0
- package/dist/cloudflare.d.ts +20 -3
- package/dist/cloudflare.js +105 -5
- package/dist/{geo-backend-TSQJWAAB.js → geo-backend-MY6MET3L.js} +1 -1
- package/dist/index.d.ts +502 -226
- package/dist/index.js +4 -3
- package/dist/node.d.ts +28 -5
- package/dist/node.js +220 -25
- package/dist/window-backend-DIMZN7EZ.js +905 -0
- package/package.json +10 -8
|
@@ -157,6 +157,147 @@ function fractionForUnit(nanos, unit) {
|
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
// ../core/dist/interval.js
|
|
161
|
+
var NANOS_PER_SECOND = 1000000000n;
|
|
162
|
+
var NANOS_PER_MILLI2 = 1000000n;
|
|
163
|
+
var NANOS_PER_MICRO2 = 1000n;
|
|
164
|
+
function intervalValue(input) {
|
|
165
|
+
const trimmed = input.trim();
|
|
166
|
+
if (trimmed.length === 0)
|
|
167
|
+
throwInterval(input);
|
|
168
|
+
let rest = trimmed;
|
|
169
|
+
let months = 0;
|
|
170
|
+
let days = 0;
|
|
171
|
+
let nanoseconds = 0n;
|
|
172
|
+
let matchedAny = false;
|
|
173
|
+
const leadingTime = /^([+-]?\d{1,2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?)\b/u.exec(rest);
|
|
174
|
+
if (leadingTime?.[1] !== void 0) {
|
|
175
|
+
nanoseconds += parseIntervalTime(leadingTime[1], input);
|
|
176
|
+
rest = rest.slice(leadingTime[0].length).trim();
|
|
177
|
+
matchedAny = true;
|
|
178
|
+
}
|
|
179
|
+
const partPattern = /([+-]?(?:\d+(?:\.\d+)?|\.\d+))\s*(years?|mons?|months?|days?|hours?|hrs?|minutes?|mins?|seconds?|secs?|milliseconds?|millis?|microseconds?|micros?|nanoseconds?|nanos?)\b/giu;
|
|
180
|
+
let cursor = 0;
|
|
181
|
+
for (const match of rest.matchAll(partPattern)) {
|
|
182
|
+
if (match.index === void 0 || rest.slice(cursor, match.index).trim().length > 0) {
|
|
183
|
+
throwInterval(input);
|
|
184
|
+
}
|
|
185
|
+
const amount = Number(match[1]);
|
|
186
|
+
const unit = match[2]?.toLowerCase() ?? "";
|
|
187
|
+
if (!Number.isFinite(amount))
|
|
188
|
+
throwInterval(input);
|
|
189
|
+
if (unit.startsWith("year"))
|
|
190
|
+
months += integerAmount(amount, unit, input) * 12;
|
|
191
|
+
else if (unit === "mon" || unit === "mons" || unit.startsWith("month"))
|
|
192
|
+
months += integerAmount(amount, unit, input);
|
|
193
|
+
else if (unit.startsWith("day"))
|
|
194
|
+
days += integerAmount(amount, unit, input);
|
|
195
|
+
else if (unit.startsWith("hour") || unit === "hr" || unit === "hrs")
|
|
196
|
+
nanoseconds += decimalNanoseconds(match[1] ?? "", 60n * 60n * NANOS_PER_SECOND);
|
|
197
|
+
else if (unit.startsWith("minute") || unit === "min" || unit === "mins")
|
|
198
|
+
nanoseconds += decimalNanoseconds(match[1] ?? "", 60n * NANOS_PER_SECOND);
|
|
199
|
+
else if (unit.startsWith("second") || unit === "sec" || unit === "secs")
|
|
200
|
+
nanoseconds += decimalNanoseconds(match[1] ?? "", NANOS_PER_SECOND);
|
|
201
|
+
else if (unit.startsWith("millisecond") || unit === "milli" || unit === "millis")
|
|
202
|
+
nanoseconds += decimalNanoseconds(match[1] ?? "", NANOS_PER_MILLI2);
|
|
203
|
+
else if (unit.startsWith("microsecond") || unit === "micro" || unit === "micros")
|
|
204
|
+
nanoseconds += decimalNanoseconds(match[1] ?? "", NANOS_PER_MICRO2);
|
|
205
|
+
else if (unit.startsWith("nanosecond") || unit === "nano" || unit === "nanos")
|
|
206
|
+
nanoseconds += BigInt(integerAmount(amount, unit, input));
|
|
207
|
+
else
|
|
208
|
+
throwInterval(input);
|
|
209
|
+
matchedAny = true;
|
|
210
|
+
cursor = match.index + match[0].length;
|
|
211
|
+
const after = rest.slice(cursor).trimStart();
|
|
212
|
+
const time = /^(\d{1,2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?)\b/u.exec(after);
|
|
213
|
+
if (time?.[1] !== void 0) {
|
|
214
|
+
nanoseconds += parseIntervalTime(time[1], input);
|
|
215
|
+
cursor = rest.length - after.slice(time[0].length).length;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (!matchedAny || rest.slice(cursor).trim().length > 0)
|
|
219
|
+
throwInterval(input);
|
|
220
|
+
return { kind: "interval", months, days, nanoseconds: nanoseconds.toString() };
|
|
221
|
+
}
|
|
222
|
+
function isIntervalValue(value) {
|
|
223
|
+
return typeof value === "object" && value !== null && "kind" in value && value.kind === "interval" && "months" in value && Number.isInteger(value.months) && "days" in value && Number.isInteger(value.days) && "nanoseconds" in value && typeof value.nanoseconds === "string" && /^-?[0-9]+$/u.test(value.nanoseconds);
|
|
224
|
+
}
|
|
225
|
+
function isNonNegativeInterval(value) {
|
|
226
|
+
return value.months >= 0 && value.days >= 0 && BigInt(value.nanoseconds) >= 0n;
|
|
227
|
+
}
|
|
228
|
+
function intervalToString(value) {
|
|
229
|
+
const parts = [];
|
|
230
|
+
if (value.months !== 0)
|
|
231
|
+
parts.push(`${value.months} months`);
|
|
232
|
+
if (value.days !== 0)
|
|
233
|
+
parts.push(`${value.days} days`);
|
|
234
|
+
const nanos = BigInt(value.nanoseconds);
|
|
235
|
+
if (nanos !== 0n || parts.length === 0)
|
|
236
|
+
parts.push(`${nanos} nanoseconds`);
|
|
237
|
+
return parts.join(" ");
|
|
238
|
+
}
|
|
239
|
+
function applyIntervalToTimestamp(value, interval, direction) {
|
|
240
|
+
const date = new Date(Number(value.epochNanoseconds / NANOS_PER_MILLI2));
|
|
241
|
+
if (interval.months !== 0)
|
|
242
|
+
shiftUtcMonthsClamped(date, direction * interval.months);
|
|
243
|
+
if (interval.days !== 0)
|
|
244
|
+
date.setUTCDate(date.getUTCDate() + direction * interval.days);
|
|
245
|
+
const nanosWithinMilli = value.epochNanoseconds % NANOS_PER_MILLI2;
|
|
246
|
+
const epochNanoseconds2 = BigInt(date.getTime()) * NANOS_PER_MILLI2 + nanosWithinMilli + BigInt(direction) * BigInt(interval.nanoseconds);
|
|
247
|
+
return new TimestampValue(epochNanoseconds2, value.unit, value.isAdjustedToUTC);
|
|
248
|
+
}
|
|
249
|
+
function integerAmount(value, unit, input) {
|
|
250
|
+
if (!Number.isInteger(value)) {
|
|
251
|
+
throw new LakeqlError("LAKEQL_TYPE_ERROR", `Interval ${unit} component must be an integer`, {
|
|
252
|
+
interval: input,
|
|
253
|
+
unit
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
return value;
|
|
257
|
+
}
|
|
258
|
+
function decimalNanoseconds(value, unitNanos) {
|
|
259
|
+
const match = /^([+-]?)(?:(\d+)(?:\.(\d+))?|\.(\d+))$/u.exec(value);
|
|
260
|
+
if (match === null) {
|
|
261
|
+
throw new LakeqlError("LAKEQL_TYPE_ERROR", "Interval component exceeds nanosecond precision");
|
|
262
|
+
}
|
|
263
|
+
const sign = match[1] === "-" ? -1n : 1n;
|
|
264
|
+
const whole = BigInt(match[2] ?? "0");
|
|
265
|
+
const fraction = match[3] ?? match[4] ?? "";
|
|
266
|
+
const scale = 10n ** BigInt(fraction.length);
|
|
267
|
+
const numerator = whole * scale + BigInt(fraction || "0");
|
|
268
|
+
const scaled = numerator * unitNanos;
|
|
269
|
+
if (scaled % scale !== 0n) {
|
|
270
|
+
throw new LakeqlError("LAKEQL_TYPE_ERROR", "Interval component exceeds nanosecond precision");
|
|
271
|
+
}
|
|
272
|
+
return sign * (scaled / scale);
|
|
273
|
+
}
|
|
274
|
+
function shiftUtcMonthsClamped(date, months) {
|
|
275
|
+
const day = date.getUTCDate();
|
|
276
|
+
const targetMonthStart = new Date(date.getTime());
|
|
277
|
+
targetMonthStart.setUTCDate(1);
|
|
278
|
+
targetMonthStart.setUTCMonth(targetMonthStart.getUTCMonth() + months);
|
|
279
|
+
const daysInTargetMonth = new Date(Date.UTC(targetMonthStart.getUTCFullYear(), targetMonthStart.getUTCMonth() + 1, 0)).getUTCDate();
|
|
280
|
+
date.setUTCFullYear(targetMonthStart.getUTCFullYear(), targetMonthStart.getUTCMonth(), Math.min(day, daysInTargetMonth));
|
|
281
|
+
}
|
|
282
|
+
function parseIntervalTime(value, input) {
|
|
283
|
+
const match = /^([+-]?\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d{1,9}))?)?$/u.exec(value);
|
|
284
|
+
if (match === null || match[1] === void 0 || match[2] === void 0)
|
|
285
|
+
throwInterval(input);
|
|
286
|
+
const sign = match[1].startsWith("-") ? -1n : 1n;
|
|
287
|
+
const hours = BigInt(Math.abs(Number(match[1])));
|
|
288
|
+
const minutes = BigInt(Number(match[2]));
|
|
289
|
+
const seconds = BigInt(Number(match[3] ?? "0"));
|
|
290
|
+
if (minutes >= 60n || seconds >= 60n)
|
|
291
|
+
throwInterval(input);
|
|
292
|
+
const fraction = BigInt((match[4] ?? "").padEnd(9, "0") || "0");
|
|
293
|
+
return sign * ((hours * 60n + minutes) * 60n * NANOS_PER_SECOND + seconds * NANOS_PER_SECOND + fraction);
|
|
294
|
+
}
|
|
295
|
+
function throwInterval(input) {
|
|
296
|
+
throw new LakeqlError("LAKEQL_TYPE_ERROR", `Invalid interval literal ${input}`, {
|
|
297
|
+
interval: input
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
160
301
|
// ../core/dist/regex-functions.js
|
|
161
302
|
function regexpMatchesValue(value, pattern, options = "") {
|
|
162
303
|
const regexOptions = parseRegexOptions("regexp_matches", options, { allowGlobal: false });
|
|
@@ -234,6 +375,7 @@ var GEO_BACKEND_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
|
234
375
|
"st_disjoint",
|
|
235
376
|
"st_contains",
|
|
236
377
|
"st_within",
|
|
378
|
+
"st_dwithin",
|
|
237
379
|
"h3_within",
|
|
238
380
|
"h3_cell",
|
|
239
381
|
"h3_parent"
|
|
@@ -251,7 +393,7 @@ function requireGeoBackend() {
|
|
|
251
393
|
async function loadGeoBackend() {
|
|
252
394
|
if (geoBackend !== null)
|
|
253
395
|
return;
|
|
254
|
-
const module = await import('./geo-backend-
|
|
396
|
+
const module = await import('./geo-backend-MY6MET3L.js');
|
|
255
397
|
module.installGeoBackend();
|
|
256
398
|
}
|
|
257
399
|
async function ensureGeoBackendForExprs(exprs) {
|
|
@@ -341,6 +483,10 @@ function matches(expr, row) {
|
|
|
341
483
|
function jsonSafeValue(value) {
|
|
342
484
|
if (isTimestampValue(value))
|
|
343
485
|
return value.toJSON();
|
|
486
|
+
if (isIntervalValue(value))
|
|
487
|
+
return intervalToString(value);
|
|
488
|
+
if (value instanceof Uint8Array)
|
|
489
|
+
return bytesToHex(value);
|
|
344
490
|
if (typeof value === "bigint") {
|
|
345
491
|
const asNumber = Number(value);
|
|
346
492
|
return Number.isSafeInteger(asNumber) ? asNumber : value.toString();
|
|
@@ -364,7 +510,7 @@ function rowValue(row, name) {
|
|
|
364
510
|
throw new LakeqlError("LAKEQL_UNKNOWN_COLUMN", `Unknown column ${name}`, { column: name });
|
|
365
511
|
}
|
|
366
512
|
const value = row[name];
|
|
367
|
-
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || isTimestampValue(value)) {
|
|
513
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || value instanceof Uint8Array || isTimestampValue(value) || isIntervalValue(value)) {
|
|
368
514
|
return value;
|
|
369
515
|
}
|
|
370
516
|
throw new LakeqlError("LAKEQL_TYPE_ERROR", `Column ${name} is not a scalar value`, {
|
|
@@ -384,6 +530,12 @@ function toSqlBoolean(value) {
|
|
|
384
530
|
function compare(op, left, right) {
|
|
385
531
|
if (left === null || right === null)
|
|
386
532
|
return null;
|
|
533
|
+
if (left instanceof Uint8Array || right instanceof Uint8Array) {
|
|
534
|
+
throw new LakeqlError("LAKEQL_TYPE_ERROR", "Cannot compare binary values", {
|
|
535
|
+
leftType: evalValueType(left),
|
|
536
|
+
rightType: evalValueType(right)
|
|
537
|
+
});
|
|
538
|
+
}
|
|
387
539
|
if (isTimestampValue(left) || isTimestampValue(right)) {
|
|
388
540
|
const leftTimestamp = timestampEvalValue(left);
|
|
389
541
|
const rightTimestamp = timestampEvalValue(right);
|
|
@@ -395,6 +547,12 @@ function compare(op, left, right) {
|
|
|
395
547
|
}
|
|
396
548
|
return compareOrder(op, compareTimestampValues(leftTimestamp, rightTimestamp));
|
|
397
549
|
}
|
|
550
|
+
if (isIntervalValue(left) || isIntervalValue(right)) {
|
|
551
|
+
throw new LakeqlError("LAKEQL_TYPE_ERROR", "Cannot compare interval values", {
|
|
552
|
+
leftType: evalValueType(left),
|
|
553
|
+
rightType: evalValueType(right)
|
|
554
|
+
});
|
|
555
|
+
}
|
|
398
556
|
if (typeof left !== typeof right && !(isNumberLike(left) && isNumberLike(right))) {
|
|
399
557
|
throw new LakeqlError("LAKEQL_TYPE_ERROR", "Cannot compare values of different types", {
|
|
400
558
|
leftType: typeof left,
|
|
@@ -428,6 +586,10 @@ function timestampEvalValue(value) {
|
|
|
428
586
|
return void 0;
|
|
429
587
|
}
|
|
430
588
|
function evalValueType(value) {
|
|
589
|
+
if (value instanceof Uint8Array)
|
|
590
|
+
return "binary";
|
|
591
|
+
if (isIntervalValue(value))
|
|
592
|
+
return "interval";
|
|
431
593
|
return isTimestampValue(value) ? "timestamp" : typeof value;
|
|
432
594
|
}
|
|
433
595
|
function arithmetic(op, left, right) {
|
|
@@ -572,6 +734,8 @@ function callFunction(name, args) {
|
|
|
572
734
|
return spatialPredicate(fn, args, "within");
|
|
573
735
|
case "st_disjoint":
|
|
574
736
|
return spatialPredicate(fn, args, "disjoint");
|
|
737
|
+
case "st_dwithin":
|
|
738
|
+
return stDWithin(fn, args);
|
|
575
739
|
case "st_distance":
|
|
576
740
|
return spatialMeasurement(fn, args, bboxDistance);
|
|
577
741
|
case "st_area":
|
|
@@ -731,6 +895,24 @@ function spatialPredicate(name, args, op) {
|
|
|
731
895
|
return bboxContains(eb, ea) && geo.booleanContains(b, a);
|
|
732
896
|
}
|
|
733
897
|
}
|
|
898
|
+
function stDWithin(name, args) {
|
|
899
|
+
requireArgCount(name, args, 3);
|
|
900
|
+
const left = args[0] ?? null;
|
|
901
|
+
const right = args[1] ?? null;
|
|
902
|
+
const distance = args[2] ?? null;
|
|
903
|
+
if (left === null || right === null || distance === null)
|
|
904
|
+
return null;
|
|
905
|
+
if (typeof distance !== "number" || !Number.isFinite(distance) || distance < 0) {
|
|
906
|
+
throw new LakeqlError("LAKEQL_TYPE_ERROR", "st_dwithin() expects a non-negative distance");
|
|
907
|
+
}
|
|
908
|
+
const a = toGeometry(parseGeometry(left, name), name);
|
|
909
|
+
const b = toGeometry(parseGeometry(right, name), name);
|
|
910
|
+
const ea = envelopeOf(a);
|
|
911
|
+
const eb = envelopeOf(b);
|
|
912
|
+
if (bboxDistance(ea, eb) > distance)
|
|
913
|
+
return false;
|
|
914
|
+
return geometryDistance(a, b) <= distance;
|
|
915
|
+
}
|
|
734
916
|
function envelopeOf(geometry) {
|
|
735
917
|
switch (geometry.type) {
|
|
736
918
|
case "Point":
|
|
@@ -874,8 +1056,10 @@ function envelope(value, name) {
|
|
|
874
1056
|
return envelopeFromGeometry(parseGeometry(value, name), name);
|
|
875
1057
|
}
|
|
876
1058
|
function parseGeometry(value, name) {
|
|
1059
|
+
if (value instanceof Uint8Array)
|
|
1060
|
+
return parseWkbGeometry(value, name);
|
|
877
1061
|
if (typeof value !== "string")
|
|
878
|
-
throwType(name, "GeoJSON
|
|
1062
|
+
throwType(name, "GeoJSON, WKT point, BBox JSON, or WKB bytes", value);
|
|
879
1063
|
const wkt = parseWktGeometry(value);
|
|
880
1064
|
if (wkt !== void 0)
|
|
881
1065
|
return wkt;
|
|
@@ -892,6 +1076,129 @@ function parseGeometry(value, name) {
|
|
|
892
1076
|
}
|
|
893
1077
|
return parsed;
|
|
894
1078
|
}
|
|
1079
|
+
function parseWkbGeometry(value, name) {
|
|
1080
|
+
const reader = new WkbReader(value, name);
|
|
1081
|
+
const geometry = reader.geometry();
|
|
1082
|
+
if (!reader.done()) {
|
|
1083
|
+
throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() received trailing WKB bytes`);
|
|
1084
|
+
}
|
|
1085
|
+
return geometry;
|
|
1086
|
+
}
|
|
1087
|
+
var WkbReader = class {
|
|
1088
|
+
bytes;
|
|
1089
|
+
name;
|
|
1090
|
+
offset = 0;
|
|
1091
|
+
littleEndian = true;
|
|
1092
|
+
constructor(bytes, name) {
|
|
1093
|
+
this.bytes = bytes;
|
|
1094
|
+
this.name = name;
|
|
1095
|
+
}
|
|
1096
|
+
done() {
|
|
1097
|
+
return this.offset === this.bytes.byteLength;
|
|
1098
|
+
}
|
|
1099
|
+
geometry() {
|
|
1100
|
+
this.byteOrder();
|
|
1101
|
+
const header = this.typeHeader();
|
|
1102
|
+
if (header.hasSrid)
|
|
1103
|
+
this.uint32();
|
|
1104
|
+
switch (header.type) {
|
|
1105
|
+
case 1:
|
|
1106
|
+
return { type: "Point", coordinates: this.position(header.dimensions) };
|
|
1107
|
+
case 2:
|
|
1108
|
+
return { type: "LineString", coordinates: this.positions(header.dimensions) };
|
|
1109
|
+
case 3:
|
|
1110
|
+
return {
|
|
1111
|
+
type: "Polygon",
|
|
1112
|
+
coordinates: this.rings(header.dimensions)
|
|
1113
|
+
};
|
|
1114
|
+
default:
|
|
1115
|
+
throw new LakeqlError("LAKEQL_TYPE_ERROR", `${this.name}() supports WKB Point, LineString, or Polygon geometry`, { wkbType: header.rawType });
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
byteOrder() {
|
|
1119
|
+
const order = this.uint8();
|
|
1120
|
+
if (order === 0) {
|
|
1121
|
+
this.littleEndian = false;
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
if (order === 1) {
|
|
1125
|
+
this.littleEndian = true;
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
throw new LakeqlError("LAKEQL_TYPE_ERROR", `${this.name}() WKB byte order is invalid`);
|
|
1129
|
+
}
|
|
1130
|
+
typeHeader() {
|
|
1131
|
+
const rawType = this.uint32();
|
|
1132
|
+
const hasZFlag = (rawType & 2147483648) !== 0;
|
|
1133
|
+
const hasMFlag = (rawType & 1073741824) !== 0;
|
|
1134
|
+
const hasSrid = (rawType & 536870912) !== 0;
|
|
1135
|
+
const baseWithEwkbFlags = rawType & 268435455;
|
|
1136
|
+
const isoFamily = Math.trunc(baseWithEwkbFlags / 1e3);
|
|
1137
|
+
const type = baseWithEwkbFlags % 1e3;
|
|
1138
|
+
const hasZ = hasZFlag || isoFamily === 1 || isoFamily === 3;
|
|
1139
|
+
const hasM = hasMFlag || isoFamily === 2 || isoFamily === 3;
|
|
1140
|
+
return {
|
|
1141
|
+
rawType,
|
|
1142
|
+
type,
|
|
1143
|
+
dimensions: 2 + (hasZ ? 1 : 0) + (hasM ? 1 : 0),
|
|
1144
|
+
hasSrid
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
rings(dimensions) {
|
|
1148
|
+
const count = this.uint32();
|
|
1149
|
+
const rings = [];
|
|
1150
|
+
for (let index = 0; index < count; index += 1) {
|
|
1151
|
+
rings.push(this.positions(dimensions));
|
|
1152
|
+
}
|
|
1153
|
+
return rings;
|
|
1154
|
+
}
|
|
1155
|
+
positions(dimensions) {
|
|
1156
|
+
const count = this.uint32();
|
|
1157
|
+
const points = [];
|
|
1158
|
+
for (let index = 0; index < count; index += 1) {
|
|
1159
|
+
points.push(this.position(dimensions));
|
|
1160
|
+
}
|
|
1161
|
+
return points;
|
|
1162
|
+
}
|
|
1163
|
+
position(dimensions) {
|
|
1164
|
+
if (dimensions < 2) {
|
|
1165
|
+
throw new LakeqlError("LAKEQL_TYPE_ERROR", `${this.name}() WKB coordinate dimension is invalid`);
|
|
1166
|
+
}
|
|
1167
|
+
const x = this.float64();
|
|
1168
|
+
const y = this.float64();
|
|
1169
|
+
for (let index = 2; index < dimensions; index += 1)
|
|
1170
|
+
this.float64();
|
|
1171
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) {
|
|
1172
|
+
throw new LakeqlError("LAKEQL_TYPE_ERROR", `${this.name}() WKB coordinates must be finite`);
|
|
1173
|
+
}
|
|
1174
|
+
return [x, y];
|
|
1175
|
+
}
|
|
1176
|
+
uint8() {
|
|
1177
|
+
this.require(1);
|
|
1178
|
+
const value = this.bytes[this.offset];
|
|
1179
|
+
this.offset += 1;
|
|
1180
|
+
return value ?? 0;
|
|
1181
|
+
}
|
|
1182
|
+
uint32() {
|
|
1183
|
+
this.require(4);
|
|
1184
|
+
const view = new DataView(this.bytes.buffer, this.bytes.byteOffset + this.offset, 4);
|
|
1185
|
+
const value = view.getUint32(0, this.littleEndian);
|
|
1186
|
+
this.offset += 4;
|
|
1187
|
+
return value;
|
|
1188
|
+
}
|
|
1189
|
+
float64() {
|
|
1190
|
+
this.require(8);
|
|
1191
|
+
const view = new DataView(this.bytes.buffer, this.bytes.byteOffset + this.offset, 8);
|
|
1192
|
+
const value = view.getFloat64(0, this.littleEndian);
|
|
1193
|
+
this.offset += 8;
|
|
1194
|
+
return value;
|
|
1195
|
+
}
|
|
1196
|
+
require(bytes) {
|
|
1197
|
+
if (this.offset + bytes > this.bytes.byteLength) {
|
|
1198
|
+
throw new LakeqlError("LAKEQL_TYPE_ERROR", `${this.name}() received truncated WKB`);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
895
1202
|
function parseWktGeometry(value) {
|
|
896
1203
|
const point = /^POINT\s*\(\s*([+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)\s+([+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)\s*\)$/iu.exec(value);
|
|
897
1204
|
if (point !== null) {
|
|
@@ -956,6 +1263,113 @@ function bboxDistance(left, right) {
|
|
|
956
1263
|
const dy = left.maxy < right.miny ? right.miny - left.maxy : right.maxy < left.miny ? left.miny - right.maxy : 0;
|
|
957
1264
|
return Math.hypot(dx, dy);
|
|
958
1265
|
}
|
|
1266
|
+
function geometryDistance(left, right) {
|
|
1267
|
+
if (requireGeoBackend().booleanIntersects(left, right))
|
|
1268
|
+
return 0;
|
|
1269
|
+
const leftSegments = geometrySegments(left);
|
|
1270
|
+
const rightSegments = geometrySegments(right);
|
|
1271
|
+
const leftPoints = geometryPoints(left);
|
|
1272
|
+
const rightPoints = geometryPoints(right);
|
|
1273
|
+
let best = Number.POSITIVE_INFINITY;
|
|
1274
|
+
for (const point of leftPoints) {
|
|
1275
|
+
for (const other of rightPoints)
|
|
1276
|
+
best = Math.min(best, pointDistance(point, other));
|
|
1277
|
+
for (const segment of rightSegments)
|
|
1278
|
+
best = Math.min(best, pointSegmentDistance(point, segment));
|
|
1279
|
+
}
|
|
1280
|
+
for (const point of rightPoints) {
|
|
1281
|
+
for (const segment of leftSegments)
|
|
1282
|
+
best = Math.min(best, pointSegmentDistance(point, segment));
|
|
1283
|
+
}
|
|
1284
|
+
for (const leftSegment of leftSegments) {
|
|
1285
|
+
for (const rightSegment of rightSegments) {
|
|
1286
|
+
best = Math.min(best, segmentDistance(leftSegment, rightSegment));
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
return best;
|
|
1290
|
+
}
|
|
1291
|
+
function geometryPoints(geometry) {
|
|
1292
|
+
switch (geometry.type) {
|
|
1293
|
+
case "Point":
|
|
1294
|
+
return [geometry.coordinates];
|
|
1295
|
+
case "LineString":
|
|
1296
|
+
return geometry.coordinates;
|
|
1297
|
+
case "Polygon":
|
|
1298
|
+
return geometry.coordinates.flat();
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
function geometrySegments(geometry) {
|
|
1302
|
+
switch (geometry.type) {
|
|
1303
|
+
case "Point":
|
|
1304
|
+
return [];
|
|
1305
|
+
case "LineString":
|
|
1306
|
+
return pathSegments(geometry.coordinates, false);
|
|
1307
|
+
case "Polygon":
|
|
1308
|
+
return geometry.coordinates.flatMap((ring) => pathSegments(ring, true));
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
function pathSegments(points, closed) {
|
|
1312
|
+
const out = [];
|
|
1313
|
+
for (let index = 1; index < points.length; index += 1) {
|
|
1314
|
+
const previous = points[index - 1];
|
|
1315
|
+
const current = points[index];
|
|
1316
|
+
if (previous !== void 0 && current !== void 0)
|
|
1317
|
+
out.push([previous, current]);
|
|
1318
|
+
}
|
|
1319
|
+
if (closed && points.length > 1) {
|
|
1320
|
+
const first = points[0];
|
|
1321
|
+
const last = points[points.length - 1];
|
|
1322
|
+
if (first !== void 0 && last !== void 0 && (first[0] !== last[0] || first[1] !== last[1])) {
|
|
1323
|
+
out.push([last, first]);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
return out;
|
|
1327
|
+
}
|
|
1328
|
+
function segmentDistance(left, right) {
|
|
1329
|
+
if (segmentsIntersect(left, right))
|
|
1330
|
+
return 0;
|
|
1331
|
+
return Math.min(pointSegmentDistance(left[0], right), pointSegmentDistance(left[1], right), pointSegmentDistance(right[0], left), pointSegmentDistance(right[1], left));
|
|
1332
|
+
}
|
|
1333
|
+
function pointDistance(left, right) {
|
|
1334
|
+
return Math.hypot(left[0] - right[0], left[1] - right[1]);
|
|
1335
|
+
}
|
|
1336
|
+
function pointSegmentDistance(point, segment) {
|
|
1337
|
+
const [start, end] = segment;
|
|
1338
|
+
const dx = end[0] - start[0];
|
|
1339
|
+
const dy = end[1] - start[1];
|
|
1340
|
+
const lengthSq = dx * dx + dy * dy;
|
|
1341
|
+
if (lengthSq === 0)
|
|
1342
|
+
return pointDistance(point, start);
|
|
1343
|
+
const t = Math.max(0, Math.min(1, ((point[0] - start[0]) * dx + (point[1] - start[1]) * dy) / lengthSq));
|
|
1344
|
+
return pointDistance(point, [start[0] + t * dx, start[1] + t * dy]);
|
|
1345
|
+
}
|
|
1346
|
+
function segmentsIntersect(left, right) {
|
|
1347
|
+
const [a, b] = left;
|
|
1348
|
+
const [c, d] = right;
|
|
1349
|
+
const abC = orientation(a, b, c);
|
|
1350
|
+
const abD = orientation(a, b, d);
|
|
1351
|
+
const cdA = orientation(c, d, a);
|
|
1352
|
+
const cdB = orientation(c, d, b);
|
|
1353
|
+
if (abC === 0 && pointOnSegment(c, left))
|
|
1354
|
+
return true;
|
|
1355
|
+
if (abD === 0 && pointOnSegment(d, left))
|
|
1356
|
+
return true;
|
|
1357
|
+
if (cdA === 0 && pointOnSegment(a, right))
|
|
1358
|
+
return true;
|
|
1359
|
+
if (cdB === 0 && pointOnSegment(b, right))
|
|
1360
|
+
return true;
|
|
1361
|
+
return abC * abD < 0 && cdA * cdB < 0;
|
|
1362
|
+
}
|
|
1363
|
+
function orientation(a, b, c) {
|
|
1364
|
+
const cross = (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
|
|
1365
|
+
if (Math.abs(cross) < Number.EPSILON)
|
|
1366
|
+
return 0;
|
|
1367
|
+
return cross > 0 ? 1 : -1;
|
|
1368
|
+
}
|
|
1369
|
+
function pointOnSegment(point, segment) {
|
|
1370
|
+
const [start, end] = segment;
|
|
1371
|
+
return point[0] >= Math.min(start[0], end[0]) && point[0] <= Math.max(start[0], end[0]) && point[1] >= Math.min(start[1], end[1]) && point[1] <= Math.max(start[1], end[1]);
|
|
1372
|
+
}
|
|
959
1373
|
function geometryArea(geometry, name) {
|
|
960
1374
|
if (geometry.type === "BBox") {
|
|
961
1375
|
const box = bboxFromRecord(geometry, name);
|
|
@@ -1148,5 +1562,11 @@ function throwType(name, expected, value) {
|
|
|
1148
1562
|
function pad(value) {
|
|
1149
1563
|
return String(value).padStart(2, "0");
|
|
1150
1564
|
}
|
|
1565
|
+
function bytesToHex(bytes) {
|
|
1566
|
+
let out = "";
|
|
1567
|
+
for (const byte of bytes)
|
|
1568
|
+
out += byte.toString(16).padStart(2, "0");
|
|
1569
|
+
return out;
|
|
1570
|
+
}
|
|
1151
1571
|
|
|
1152
|
-
export { ERROR_CODES, LakeqlError, TimestampValue, __commonJS, __toESM, bboxIntersects, compareTimestampValues, encodeJsonLine, ensureGeoBackendForExprs, envelopeFromGeometry, envelopeOf, evaluate, isLakeqlError, isTimestampValue, jsonSafeValue, loadGeoBackend, matches, parseGeometry, regexpMatchesValue, regexpReplaceValue, requireGeoBackend, setGeoBackend, timestampEpochForUnit, timestampFromEpoch, timestampToIsoString, timestampValueFromIso, toGeometry };
|
|
1572
|
+
export { ERROR_CODES, LakeqlError, TimestampValue, __commonJS, __toESM, applyIntervalToTimestamp, bboxDistance, bboxIntersects, compareTimestampValues, encodeJsonLine, ensureGeoBackendForExprs, envelopeFromGeometry, envelopeOf, evaluate, geometryDistance, intervalToString, intervalValue, isIntervalValue, isLakeqlError, isNonNegativeInterval, isTimestampValue, jsonSafeValue, loadGeoBackend, matches, parseGeometry, regexpMatchesValue, regexpReplaceValue, requireGeoBackend, setGeoBackend, timestampEpochForUnit, timestampFromEpoch, timestampToIsoString, timestampValueFromIso, toGeometry };
|