Geo_test.res.mjs

   1// Generated by ReScript, PLEASE EDIT WITH CARE
   2
   3import * as Geo from "../src/lib/Geo.res.mjs";
   4import * as Vitest from "vitest";
   5import * as Core__Option from "@rescript/core/lib/es6/src/Core__Option.res.mjs";
   6import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
   7
   8function makeRoute(id, d, t, param) {
   9  return {
  10    id: id,
  11    d: d,
  12    t: t,
  13    bbox: undefined
  14  };
  15}
  16
  17function makeWaypoint(id, pointOpt, fromOpt, labelOpt, gidOpt, countryOpt, consOpt, nonroutableOpt, errorOpt, param) {
  18  let point = pointOpt !== undefined ? pointOpt : [
  19      0.0,
  20      0.0
  21    ];
  22  let from = fromOpt !== undefined ? Primitive_option.valFromOption(fromOpt) : undefined;
  23  let label = labelOpt !== undefined ? labelOpt : "";
  24  let gid = gidOpt !== undefined ? gidOpt : "";
  25  let country = countryOpt !== undefined ? countryOpt : "";
  26  let cons = consOpt !== undefined ? consOpt : [];
  27  let nonroutable = nonroutableOpt !== undefined ? nonroutableOpt : false;
  28  let error = errorOpt !== undefined ? Primitive_option.valFromOption(errorOpt) : undefined;
  29  return {
  30    id: id,
  31    point: point,
  32    from: from,
  33    label: label,
  34    gid: gid,
  35    country: country,
  36    cons: cons,
  37    nonroutable: nonroutable,
  38    error: error
  39  };
  40}
  41
  42function makeLineGeoJSON(coords) {
  43  return Object.fromEntries([
  44    [
  45      "type",
  46      "Feature"
  47    ],
  48    [
  49      "geometry",
  50      Object.fromEntries([
  51        [
  52          "type",
  53          "LineString"
  54        ],
  55        [
  56          "coordinates",
  57          coords.map(param => [
  58            param[0],
  59            param[1]
  60          ])
  61        ]
  62      ])
  63    ],
  64    [
  65      "properties",
  66      {}
  67    ]
  68  ]);
  69}
  70
  71function makeLineGeoJSONWithProps(coords, distance, time) {
  72  return Object.fromEntries([
  73    [
  74      "type",
  75      "Feature"
  76    ],
  77    [
  78      "geometry",
  79      Object.fromEntries([
  80        [
  81          "type",
  82          "LineString"
  83        ],
  84        [
  85          "coordinates",
  86          coords.map(param => [
  87            param[0],
  88            param[1]
  89          ])
  90        ]
  91      ])
  92    ],
  93    [
  94      "properties",
  95      Object.fromEntries([
  96        [
  97          "distance",
  98          distance
  99        ],
 100        [
 101          "time",
 102          time
 103        ]
 104      ])
 105    ]
 106  ]);
 107}
 108
 109let mapEntries = ((map) => Array.from(map.entries()));
 110
 111function stringArraysEqual(a, b) {
 112  if (a.length !== b.length) {
 113    return false;
 114  }
 115  let _idx = 0;
 116  while (true) {
 117    let idx = _idx;
 118    if (idx >= a.length) {
 119      return true;
 120    }
 121    if (a[idx] !== b[idx]) {
 122      return false;
 123    }
 124    _idx = idx + 1 | 0;
 125    continue;
 126  };
 127}
 128
 129Vitest.describe("Geo.getDistance", () => {
 130  Vitest.test("linear path: calculates fromPreviousWaypoint and single fromStart path", ctx => {
 131    let waypoints = [
 132      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 133      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 134          "A",
 135          {
 136            id: "route-ab",
 137            d: 100.0,
 138            t: undefined,
 139            bbox: undefined
 140          }
 141        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 142      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
 143          "B",
 144          {
 145            id: "route-bc",
 146            d: 50.0,
 147            t: undefined,
 148            bbox: undefined
 149          }
 150        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 151    ];
 152    let geojson = makeLineGeoJSONWithProps([
 153      [
 154        0.0,
 155        0.0
 156      ],
 157      [
 158        1.0,
 159        0.0
 160      ]
 161    ], 111.19, 0.0);
 162    let result = Geo.getDistance([
 163      0.5,
 164      0.0
 165    ], "route-bc", waypoints, geojson);
 166    ctx.expect(result.fromPreviousWaypoint).toBeGreaterThan(0.0);
 167    let entries = mapEntries(result.fromStart);
 168    ctx.expect(entries.length).toBe(1);
 169    let match = entries[0];
 170    if (match !== undefined) {
 171      ctx.expect(stringArraysEqual(match[0], [
 172        "A",
 173        "B"
 174      ])).toBe(true);
 175      ctx.expect(match[1]).toBe(100.0 + result.fromPreviousWaypoint);
 176    } else {
 177      ctx.expect("should have one entry").toBe("");
 178    }
 179    ctx.expect(result.toNextWaypoint).toBeGreaterThan(0.0);
 180    ctx.expect(result.toNextWaypoint).toBeLessThan(111.2);
 181    let total = result.fromPreviousWaypoint + result.toNextWaypoint;
 182    ctx.expect(total).toBeGreaterThan(111.0);
 183    ctx.expect(total).toBeLessThan(111.3);
 184  });
 185  Vitest.test("toNextWaypoint calculates remaining distance correctly", ctx => {
 186    let waypoints = [
 187      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 188      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 189          "A",
 190          {
 191            id: "route-ab",
 192            d: 100.0,
 193            t: undefined,
 194            bbox: undefined
 195          }
 196        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 197      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
 198          "B",
 199          {
 200            id: "route-bc",
 201            d: 50.0,
 202            t: undefined,
 203            bbox: undefined
 204          }
 205        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 206    ];
 207    let geojson = makeLineGeoJSONWithProps([
 208      [
 209        0.0,
 210        0.0
 211      ],
 212      [
 213        1.0,
 214        0.0
 215      ]
 216    ], 111.19, 0.0);
 217    let result = Geo.getDistance([
 218      0.5,
 219      0.0
 220    ], "route-bc", waypoints, geojson);
 221    let diff = Math.abs(result.fromPreviousWaypoint - result.toNextWaypoint);
 222    ctx.expect(diff).toBeLessThan(1.0);
 223    let total = result.fromPreviousWaypoint + result.toNextWaypoint;
 224    ctx.expect(total).toBeGreaterThan(111.0);
 225    ctx.expect(total).toBeLessThan(111.3);
 226  });
 227  Vitest.test("merge path: two routes to source waypoint", ctx => {
 228    let waypoints = [
 229      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 230      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 231          "A",
 232          {
 233            id: "route-ab",
 234            d: 100.0,
 235            t: undefined,
 236            bbox: undefined
 237          }
 238        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 239      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
 240          "B",
 241          {
 242            id: "route-bc",
 243            d: 30.0,
 244            t: undefined,
 245            bbox: undefined
 246          }
 247        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 248      makeWaypoint("D", undefined, Primitive_option.some(Object.fromEntries([
 249        [
 250          "B",
 251          {
 252            id: "route-bd",
 253            d: 60.0,
 254            t: undefined,
 255            bbox: undefined
 256          }
 257        ],
 258        [
 259          "C",
 260          {
 261            id: "route-cd",
 262            d: 20.0,
 263            t: undefined,
 264            bbox: undefined
 265          }
 266        ]
 267      ])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 268      makeWaypoint("E", undefined, Primitive_option.some(Object.fromEntries([[
 269          "D",
 270          {
 271            id: "route-de",
 272            d: 40.0,
 273            t: undefined,
 274            bbox: undefined
 275          }
 276        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 277    ];
 278    let geojson = makeLineGeoJSON([
 279      [
 280        0.0,
 281        0.0
 282      ],
 283      [
 284        0.5,
 285        0.0
 286      ]
 287    ]);
 288    let result = Geo.getDistance([
 289      0.25,
 290      0.0
 291    ], "route-de", waypoints, geojson);
 292    let entries = mapEntries(result.fromStart);
 293    ctx.expect(entries.length).toBe(2);
 294    let findEntry = (expectedPath, expectedDist) => entries.find(param => {
 295      if (stringArraysEqual(param[0], expectedPath)) {
 296        return Math.abs(param[1] - expectedDist) < 0.0001;
 297      } else {
 298        return false;
 299      }
 300    });
 301    ctx.expect(Core__Option.isSome(findEntry([
 302      "A",
 303      "B",
 304      "D"
 305    ], 160.0 + result.fromPreviousWaypoint))).toBe(true);
 306    ctx.expect(Core__Option.isSome(findEntry([
 307      "A",
 308      "B",
 309      "C",
 310      "D"
 311    ], 150.0 + result.fromPreviousWaypoint))).toBe(true);
 312  });
 313  Vitest.test("cycle detection: skips cyclic paths", ctx => {
 314    let waypoints = [
 315      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 316      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([
 317        [
 318          "A",
 319          {
 320            id: "route-ab",
 321            d: 100.0,
 322            t: undefined,
 323            bbox: undefined
 324          }
 325        ],
 326        [
 327          "C",
 328          {
 329            id: "route-cb",
 330            d: 50.0,
 331            t: undefined,
 332            bbox: undefined
 333          }
 334        ]
 335      ])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 336      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
 337          "B",
 338          {
 339            id: "route-bc",
 340            d: 30.0,
 341            t: undefined,
 342            bbox: undefined
 343          }
 344        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 345      makeWaypoint("D", undefined, Primitive_option.some(Object.fromEntries([[
 346          "B",
 347          {
 348            id: "route-bd",
 349            d: 20.0,
 350            t: undefined,
 351            bbox: undefined
 352          }
 353        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 354    ];
 355    let geojson = makeLineGeoJSON([
 356      [
 357        0.0,
 358        0.0
 359      ],
 360      [
 361        0.5,
 362        0.0
 363      ]
 364    ]);
 365    let result = Geo.getDistance([
 366      0.25,
 367      0.0
 368    ], "route-bd", waypoints, geojson);
 369    let entries = mapEntries(result.fromStart);
 370    ctx.expect(entries.length).toBe(1);
 371    let match = entries.find(param => stringArraysEqual(param[0], [
 372      "A",
 373      "B"
 374    ]));
 375    let hasValidPath = match !== undefined;
 376    ctx.expect(hasValidPath).toBe(true);
 377  });
 378  Vitest.test("missing route hash: returns zero/empty result", ctx => {
 379    let waypoints = [
 380      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 381      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 382          "A",
 383          {
 384            id: "route-ab",
 385            d: 100.0,
 386            t: undefined,
 387            bbox: undefined
 388          }
 389        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 390    ];
 391    let geojson = makeLineGeoJSON([
 392      [
 393        0.0,
 394        0.0
 395      ],
 396      [
 397        1.0,
 398        0.0
 399      ]
 400    ]);
 401    let result = Geo.getDistance([
 402      0.5,
 403      0.0
 404    ], "nonexistent-route", waypoints, geojson);
 405    ctx.expect(result.fromPreviousWaypoint).toBe(0.0);
 406    ctx.expect(result.fromStart.size).toBe(0);
 407  });
 408  Vitest.test("origin only: hover on first segment", ctx => {
 409    let waypoints = [makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)];
 410    let geojson = makeLineGeoJSON([
 411      [
 412        0.0,
 413        0.0
 414      ],
 415      [
 416        1.0,
 417        0.0
 418      ]
 419    ]);
 420    let result = Geo.getDistance([
 421      0.5,
 422      0.0
 423    ], "any-route", waypoints, geojson);
 424    ctx.expect(result.fromPreviousWaypoint).toBe(0.0);
 425    ctx.expect(result.fromStart.size).toBe(0);
 426  });
 427  Vitest.test("route keys with :saa suffix (auto-added routes)", ctx => {
 428    let waypoints = [
 429      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 430      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 431          "A:saa",
 432          {
 433            id: "route-ab",
 434            d: 100.0,
 435            t: undefined,
 436            bbox: undefined
 437          }
 438        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 439      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
 440          "B:saa",
 441          {
 442            id: "route-bc",
 443            d: 50.0,
 444            t: undefined,
 445            bbox: undefined
 446          }
 447        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 448    ];
 449    let geojson = makeLineGeoJSON([
 450      [
 451        0.0,
 452        0.0
 453      ],
 454      [
 455        1.0,
 456        0.0
 457      ]
 458    ]);
 459    let result = Geo.getDistance([
 460      0.5,
 461      0.0
 462    ], "route-bc", waypoints, geojson);
 463    ctx.expect(result.fromPreviousWaypoint).toBeGreaterThan(0.0);
 464    let entries = mapEntries(result.fromStart);
 465    ctx.expect(entries.length).toBe(1);
 466    let match = entries[0];
 467    if (match !== undefined) {
 468      ctx.expect(stringArraysEqual(match[0], [
 469        "A",
 470        "B"
 471      ])).toBe(true);
 472      ctx.expect(match[1]).toBe(100.0 + result.fromPreviousWaypoint);
 473    } else {
 474      ctx.expect("should have one entry").toBe("");
 475    }
 476  });
 477  Vitest.test("route keys with adventure constraint suffix", ctx => {
 478    let waypoints = [
 479      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 480      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 481          "A:adv(80)",
 482          {
 483            id: "route-ab",
 484            d: 120.0,
 485            t: undefined,
 486            bbox: undefined
 487          }
 488        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 489      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
 490          "B:adv(80)",
 491          {
 492            id: "route-bc",
 493            d: 60.0,
 494            t: undefined,
 495            bbox: undefined
 496          }
 497        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 498    ];
 499    let geojson = makeLineGeoJSON([
 500      [
 501        0.0,
 502        0.0
 503      ],
 504      [
 505        1.0,
 506        0.0
 507      ]
 508    ]);
 509    let result = Geo.getDistance([
 510      0.5,
 511      0.0
 512    ], "route-bc", waypoints, geojson);
 513    let entries = mapEntries(result.fromStart);
 514    ctx.expect(entries.length).toBe(1);
 515    let match = entries[0];
 516    if (match !== undefined) {
 517      ctx.expect(stringArraysEqual(match[0], [
 518        "A",
 519        "B"
 520      ])).toBe(true);
 521      ctx.expect(match[1]).toBe(120.0 + result.fromPreviousWaypoint);
 522    } else {
 523      ctx.expect("should have one entry").toBe("");
 524    }
 525  });
 526  Vitest.test("route keys with multiple constraints", ctx => {
 527    let waypoints = [
 528      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 529      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 530          "A:adv(80):max(100):saa",
 531          {
 532            id: "route-ab",
 533            d: 110.0,
 534            t: undefined,
 535            bbox: undefined
 536          }
 537        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 538      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
 539          "B:adv(80):max(100):saa",
 540          {
 541            id: "route-bc",
 542            d: 55.0,
 543            t: undefined,
 544            bbox: undefined
 545          }
 546        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 547    ];
 548    let geojson = makeLineGeoJSON([
 549      [
 550        0.0,
 551        0.0
 552      ],
 553      [
 554        1.0,
 555        0.0
 556      ]
 557    ]);
 558    let result = Geo.getDistance([
 559      0.5,
 560      0.0
 561    ], "route-bc", waypoints, geojson);
 562    let entries = mapEntries(result.fromStart);
 563    ctx.expect(entries.length).toBe(1);
 564    let match = entries[0];
 565    if (match !== undefined) {
 566      ctx.expect(stringArraysEqual(match[0], [
 567        "A",
 568        "B"
 569      ])).toBe(true);
 570      ctx.expect(match[1]).toBe(110.0 + result.fromPreviousWaypoint);
 571    } else {
 572      ctx.expect("should have one entry").toBe("");
 573    }
 574  });
 575  Vitest.test("mixed route keys with and without constraints", ctx => {
 576    let waypoints = [
 577      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 578      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 579          "A",
 580          {
 581            id: "route-ab",
 582            d: 100.0,
 583            t: undefined,
 584            bbox: undefined
 585          }
 586        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 587      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
 588          "B:saa",
 589          {
 590            id: "route-bc",
 591            d: 50.0,
 592            t: undefined,
 593            bbox: undefined
 594          }
 595        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 596      makeWaypoint("D", undefined, Primitive_option.some(Object.fromEntries([[
 597          "C:adv(60):saa",
 598          {
 599            id: "route-cd",
 600            d: 30.0,
 601            t: undefined,
 602            bbox: undefined
 603          }
 604        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 605    ];
 606    let geojson = makeLineGeoJSON([
 607      [
 608        0.0,
 609        0.0
 610      ],
 611      [
 612        1.0,
 613        0.0
 614      ]
 615    ]);
 616    let result = Geo.getDistance([
 617      0.5,
 618      0.0
 619    ], "route-cd", waypoints, geojson);
 620    let entries = mapEntries(result.fromStart);
 621    ctx.expect(entries.length).toBe(1);
 622    let match = entries[0];
 623    if (match !== undefined) {
 624      ctx.expect(stringArraysEqual(match[0], [
 625        "A",
 626        "B",
 627        "C"
 628      ])).toBe(true);
 629      ctx.expect(match[1]).toBe(150.0 + result.fromPreviousWaypoint);
 630    } else {
 631      ctx.expect("should have one entry").toBe("");
 632    }
 633  });
 634  Vitest.test("returns correct previousID and nextID", ctx => {
 635    let waypoints = [
 636      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 637      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 638          "A",
 639          {
 640            id: "route-ab",
 641            d: 100.0,
 642            t: undefined,
 643            bbox: undefined
 644          }
 645        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 646      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
 647          "B",
 648          {
 649            id: "route-bc",
 650            d: 50.0,
 651            t: undefined,
 652            bbox: undefined
 653          }
 654        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 655    ];
 656    let geojson = makeLineGeoJSON([
 657      [
 658        0.0,
 659        0.0
 660      ],
 661      [
 662        1.0,
 663        0.0
 664      ]
 665    ]);
 666    let result = Geo.getDistance([
 667      0.5,
 668      0.0
 669    ], "route-bc", waypoints, geojson);
 670    ctx.expect(result.previousID).toEqual("B");
 671    ctx.expect(result.nextID).toEqual("C");
 672  });
 673  Vitest.test("returns None for previousID and nextID when route not found", ctx => {
 674    let waypoints = [
 675      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 676      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 677          "A",
 678          {
 679            id: "route-ab",
 680            d: 100.0,
 681            t: undefined,
 682            bbox: undefined
 683          }
 684        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 685    ];
 686    let geojson = makeLineGeoJSON([
 687      [
 688        0.0,
 689        0.0
 690      ],
 691      [
 692        1.0,
 693        0.0
 694      ]
 695    ]);
 696    let result = Geo.getDistance([
 697      0.5,
 698      0.0
 699    ], "nonexistent-route", waypoints, geojson);
 700    ctx.expect(result.previousID).toEqual(undefined);
 701    ctx.expect(result.nextID).toEqual(undefined);
 702  });
 703});
 704
 705Vitest.describe("Geo.getTime", () => {
 706  Vitest.test("linear path: calculates fromPreviousWaypoint and single fromStart path", ctx => {
 707    let waypoints = [
 708      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 709      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 710          "A",
 711          {
 712            id: "route-ab",
 713            d: 100.0,
 714            t: 3600.0,
 715            bbox: undefined
 716          }
 717        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 718      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
 719          "B",
 720          {
 721            id: "route-bc",
 722            d: 50.0,
 723            t: 1800.0,
 724            bbox: undefined
 725          }
 726        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 727    ];
 728    let geojson = makeLineGeoJSONWithProps([
 729      [
 730        0.0,
 731        0.0
 732      ],
 733      [
 734        1.0,
 735        0.0
 736      ]
 737    ], 111.19, 1800.0);
 738    let result = Geo.getTime([
 739      0.5,
 740      0.0
 741    ], "route-bc", waypoints, geojson);
 742    ctx.expect(result.fromPreviousWaypoint).toBeGreaterThan(0.0);
 743    ctx.expect(result.fromPreviousWaypoint).toBeLessThan(1800.0);
 744    let entries = mapEntries(result.fromStart);
 745    ctx.expect(entries.length).toBe(1);
 746    let match = entries[0];
 747    if (match !== undefined) {
 748      ctx.expect(stringArraysEqual(match[0], [
 749        "A",
 750        "B"
 751      ])).toBe(true);
 752      ctx.expect(match[1]).toBe(3600.0 + result.fromPreviousWaypoint);
 753    } else {
 754      ctx.expect("should have one entry").toBe("");
 755    }
 756    ctx.expect(result.toNextWaypoint).toBeGreaterThan(0.0);
 757    ctx.expect(result.toNextWaypoint).toBeLessThan(1800.0);
 758    let totalTime = result.fromPreviousWaypoint + result.toNextWaypoint;
 759    ctx.expect(Math.abs(totalTime - 1800.0)).toBeLessThan(0.1);
 760  });
 761  Vitest.test("toNextWaypoint calculates remaining time correctly", ctx => {
 762    let waypoints = [
 763      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 764      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 765          "A",
 766          {
 767            id: "route-ab",
 768            d: 100.0,
 769            t: 3600.0,
 770            bbox: undefined
 771          }
 772        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 773      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
 774          "B",
 775          {
 776            id: "route-bc",
 777            d: 50.0,
 778            t: 1800.0,
 779            bbox: undefined
 780          }
 781        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 782    ];
 783    let geojson = makeLineGeoJSONWithProps([
 784      [
 785        0.0,
 786        0.0
 787      ],
 788      [
 789        1.0,
 790        0.0
 791      ]
 792    ], 111.19, 1800.0);
 793    let result = Geo.getTime([
 794      0.5,
 795      0.0
 796    ], "route-bc", waypoints, geojson);
 797    let diff = Math.abs(result.fromPreviousWaypoint - result.toNextWaypoint);
 798    ctx.expect(diff).toBeLessThan(10.0);
 799    let total = result.fromPreviousWaypoint + result.toNextWaypoint;
 800    ctx.expect(Math.abs(total - 1800.0)).toBeLessThan(0.1);
 801  });
 802  Vitest.test("merge path: two routes to source waypoint with different times", ctx => {
 803    let waypoints = [
 804      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 805      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 806          "A",
 807          {
 808            id: "route-ab",
 809            d: 100.0,
 810            t: 3600.0,
 811            bbox: undefined
 812          }
 813        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 814      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
 815          "B",
 816          {
 817            id: "route-bc",
 818            d: 30.0,
 819            t: 1200.0,
 820            bbox: undefined
 821          }
 822        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 823      makeWaypoint("D", undefined, Primitive_option.some(Object.fromEntries([
 824        [
 825          "B",
 826          {
 827            id: "route-bd",
 828            d: 60.0,
 829            t: 2400.0,
 830            bbox: undefined
 831          }
 832        ],
 833        [
 834          "C",
 835          {
 836            id: "route-cd",
 837            d: 20.0,
 838            t: 800.0,
 839            bbox: undefined
 840          }
 841        ]
 842      ])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 843      makeWaypoint("E", undefined, Primitive_option.some(Object.fromEntries([[
 844          "D",
 845          {
 846            id: "route-de",
 847            d: 40.0,
 848            t: 1600.0,
 849            bbox: undefined
 850          }
 851        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 852    ];
 853    let geojson = makeLineGeoJSONWithProps([
 854      [
 855        0.0,
 856        0.0
 857      ],
 858      [
 859        0.5,
 860        0.0
 861      ]
 862    ], 55.6, 1600.0);
 863    let result = Geo.getTime([
 864      0.25,
 865      0.0
 866    ], "route-de", waypoints, geojson);
 867    let entries = mapEntries(result.fromStart);
 868    ctx.expect(entries.length).toBe(2);
 869    let findEntry = (expectedPath, expectedTime) => entries.find(param => {
 870      if (stringArraysEqual(param[0], expectedPath)) {
 871        return Math.abs(param[1] - expectedTime) < 0.0001;
 872      } else {
 873        return false;
 874      }
 875    });
 876    ctx.expect(Core__Option.isSome(findEntry([
 877      "A",
 878      "B",
 879      "D"
 880    ], 6000.0 + result.fromPreviousWaypoint))).toBe(true);
 881    ctx.expect(Core__Option.isSome(findEntry([
 882      "A",
 883      "B",
 884      "C",
 885      "D"
 886    ], 5600.0 + result.fromPreviousWaypoint))).toBe(true);
 887  });
 888  Vitest.test("returns correct previousID and nextID", ctx => {
 889    let waypoints = [
 890      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 891      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 892          "A",
 893          {
 894            id: "route-ab",
 895            d: 100.0,
 896            t: 3600.0,
 897            bbox: undefined
 898          }
 899        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 900      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
 901          "B",
 902          {
 903            id: "route-bc",
 904            d: 50.0,
 905            t: 1800.0,
 906            bbox: undefined
 907          }
 908        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 909    ];
 910    let geojson = makeLineGeoJSONWithProps([
 911      [
 912        0.0,
 913        0.0
 914      ],
 915      [
 916        1.0,
 917        0.0
 918      ]
 919    ], 111.19, 1800.0);
 920    let result = Geo.getTime([
 921      0.5,
 922      0.0
 923    ], "route-bc", waypoints, geojson);
 924    ctx.expect(result.previousID).toEqual("B");
 925    ctx.expect(result.nextID).toEqual("C");
 926  });
 927  Vitest.test("zero total distance: returns zero time for current segment", ctx => {
 928    let waypoints = [
 929      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 930      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 931          "A",
 932          {
 933            id: "route-ab",
 934            d: 100.0,
 935            t: 3600.0,
 936            bbox: undefined
 937          }
 938        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 939    ];
 940    let geojson = makeLineGeoJSONWithProps([
 941      [
 942        0.0,
 943        0.0
 944      ],
 945      [
 946        1.0,
 947        0.0
 948      ]
 949    ], 0.0, 1800.0);
 950    let result = Geo.getTime([
 951      0.5,
 952      0.0
 953    ], "route-ab", waypoints, geojson);
 954    ctx.expect(result.fromPreviousWaypoint).toBe(0.0);
 955  });
 956  Vitest.test("missing route hash: returns zero/empty result", ctx => {
 957    let waypoints = [
 958      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 959      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 960          "A",
 961          {
 962            id: "route-ab",
 963            d: 100.0,
 964            t: 3600.0,
 965            bbox: undefined
 966          }
 967        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
 968    ];
 969    let geojson = makeLineGeoJSONWithProps([
 970      [
 971        0.0,
 972        0.0
 973      ],
 974      [
 975        1.0,
 976        0.0
 977      ]
 978    ], 100.0, 3600.0);
 979    let result = Geo.getTime([
 980      0.5,
 981      0.0
 982    ], "nonexistent-route", waypoints, geojson);
 983    ctx.expect(result.fromPreviousWaypoint).toBe(0.0);
 984    ctx.expect(result.fromStart.size).toBe(0);
 985  });
 986  Vitest.test("route with time of None: uses 0 as default", ctx => {
 987    let waypoints = [
 988      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 989      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
 990          "A",
 991          {
 992            id: "route-ab",
 993            d: 100.0,
 994            t: 3600.0,
 995            bbox: undefined
 996          }
 997        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
 998      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
 999          "B",
1000          {
1001            id: "route-bc",
1002            d: 50.0,
1003            t: undefined,
1004            bbox: undefined
1005          }
1006        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
1007    ];
1008    let geojson = makeLineGeoJSONWithProps([
1009      [
1010        0.0,
1011        0.0
1012      ],
1013      [
1014        1.0,
1015        0.0
1016      ]
1017    ], 111.19, 1800.0);
1018    let result = Geo.getTime([
1019      0.5,
1020      0.0
1021    ], "route-bc", waypoints, geojson);
1022    let entries = mapEntries(result.fromStart);
1023    let match = entries[0];
1024    if (match !== undefined) {
1025      ctx.expect(stringArraysEqual(match[0], [
1026        "A",
1027        "B"
1028      ])).toBe(true);
1029      ctx.expect(match[1]).toBe(3600.0 + result.fromPreviousWaypoint);
1030    } else {
1031      ctx.expect("should have one entry").toBe("");
1032    }
1033  });
1034  Vitest.test("route keys with constraint suffixes work correctly", ctx => {
1035    let waypoints = [
1036      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1037      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
1038          "A:saa",
1039          {
1040            id: "route-ab",
1041            d: 100.0,
1042            t: 3600.0,
1043            bbox: undefined
1044          }
1045        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1046      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
1047          "B:saa",
1048          {
1049            id: "route-bc",
1050            d: 50.0,
1051            t: 1800.0,
1052            bbox: undefined
1053          }
1054        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
1055    ];
1056    let geojson = makeLineGeoJSONWithProps([
1057      [
1058        0.0,
1059        0.0
1060      ],
1061      [
1062        1.0,
1063        0.0
1064      ]
1065    ], 111.19, 1800.0);
1066    let result = Geo.getTime([
1067      0.5,
1068      0.0
1069    ], "route-bc", waypoints, geojson);
1070    let entries = mapEntries(result.fromStart);
1071    ctx.expect(entries.length).toBe(1);
1072    let match = entries[0];
1073    if (match !== undefined) {
1074      ctx.expect(stringArraysEqual(match[0], [
1075        "A",
1076        "B"
1077      ])).toBe(true);
1078      ctx.expect(match[1]).toBe(3600.0 + result.fromPreviousWaypoint);
1079    } else {
1080      ctx.expect("should have one entry").toBe("");
1081    }
1082  });
1083});
1084
1085Vitest.describe("Geo.getDistanceMatrix", () => {
1086  Vitest.test("linear path A -> B -> C: builds correct distance matrix", ctx => {
1087    let waypoints = [
1088      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1089      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
1090          "A",
1091          {
1092            id: "route-ab",
1093            d: 100.0,
1094            t: undefined,
1095            bbox: undefined
1096          }
1097        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1098      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
1099          "B",
1100          {
1101            id: "route-bc",
1102            d: 50.0,
1103            t: undefined,
1104            bbox: undefined
1105          }
1106        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
1107    ];
1108    let matrix = Geo.getDistanceMatrix(waypoints);
1109    let entries = mapEntries(matrix);
1110    ctx.expect(entries.length).toBe(2);
1111    let pathAB = entries.find(param => stringArraysEqual(param[0], [
1112      "A",
1113      "B"
1114    ]));
1115    if (pathAB !== undefined) {
1116      ctx.expect(pathAB[1]).toBe(100.0);
1117    } else {
1118      ctx.expect("should have path [A,B]").toBe("");
1119    }
1120    let pathABC = entries.find(param => stringArraysEqual(param[0], [
1121      "A",
1122      "B",
1123      "C"
1124    ]));
1125    if (pathABC !== undefined) {
1126      ctx.expect(pathABC[1]).toBe(150.0);
1127    } else {
1128      ctx.expect("should have path [A,B,C]").toBe("");
1129    }
1130  });
1131  Vitest.test("multiple paths to same destination: includes all routes", ctx => {
1132    let waypoints = [
1133      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1134      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
1135          "A",
1136          {
1137            id: "route-ab",
1138            d: 100.0,
1139            t: undefined,
1140            bbox: undefined
1141          }
1142        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1143      makeWaypoint("D", undefined, Primitive_option.some(Object.fromEntries([[
1144          "A",
1145          {
1146            id: "route-ad",
1147            d: 80.0,
1148            t: undefined,
1149            bbox: undefined
1150          }
1151        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1152      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([
1153        [
1154          "B",
1155          {
1156            id: "route-bc",
1157            d: 50.0,
1158            t: undefined,
1159            bbox: undefined
1160          }
1161        ],
1162        [
1163          "D",
1164          {
1165            id: "route-dc",
1166            d: 30.0,
1167            t: undefined,
1168            bbox: undefined
1169          }
1170        ]
1171      ])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
1172    ];
1173    let matrix = Geo.getDistanceMatrix(waypoints);
1174    let entries = mapEntries(matrix);
1175    ctx.expect(entries.length).toBe(4);
1176    let pathAB = entries.find(param => stringArraysEqual(param[0], [
1177      "A",
1178      "B"
1179    ]));
1180    if (pathAB !== undefined) {
1181      ctx.expect(pathAB[1]).toBe(100.0);
1182    } else {
1183      ctx.expect("should have path [A,B]").toBe("");
1184    }
1185    let pathAD = entries.find(param => stringArraysEqual(param[0], [
1186      "A",
1187      "D"
1188    ]));
1189    if (pathAD !== undefined) {
1190      ctx.expect(pathAD[1]).toBe(80.0);
1191    } else {
1192      ctx.expect("should have path [A,D]").toBe("");
1193    }
1194    let pathABC = entries.find(param => stringArraysEqual(param[0], [
1195      "A",
1196      "B",
1197      "C"
1198    ]));
1199    if (pathABC !== undefined) {
1200      ctx.expect(pathABC[1]).toBe(150.0);
1201    } else {
1202      ctx.expect("should have path [A,B,C]").toBe("");
1203    }
1204    let pathADC = entries.find(param => stringArraysEqual(param[0], [
1205      "A",
1206      "D",
1207      "C"
1208    ]));
1209    if (pathADC !== undefined) {
1210      ctx.expect(pathADC[1]).toBe(110.0);
1211    } else {
1212      ctx.expect("should have path [A,D,C]").toBe("");
1213    }
1214  });
1215  Vitest.test("non-routable waypoint: excluded from matrix", ctx => {
1216    let waypoints = [
1217      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1218      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
1219          "A",
1220          {
1221            id: "route-ab",
1222            d: 100.0,
1223            t: undefined,
1224            bbox: undefined
1225          }
1226        ]])), undefined, undefined, undefined, undefined, true, undefined, undefined),
1227      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
1228          "B",
1229          {
1230            id: "route-bc",
1231            d: 50.0,
1232            t: undefined,
1233            bbox: undefined
1234          }
1235        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
1236    ];
1237    let matrix = Geo.getDistanceMatrix(waypoints);
1238    let entries = mapEntries(matrix);
1239    ctx.expect(entries.length).toBe(0);
1240  });
1241  Vitest.test("cycle detection: avoids infinite loops", ctx => {
1242    let waypoints = [
1243      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1244      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([
1245        [
1246          "A",
1247          {
1248            id: "route-ab",
1249            d: 100.0,
1250            t: undefined,
1251            bbox: undefined
1252          }
1253        ],
1254        [
1255          "C",
1256          {
1257            id: "route-cb",
1258            d: 50.0,
1259            t: undefined,
1260            bbox: undefined
1261          }
1262        ]
1263      ])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1264      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
1265          "B",
1266          {
1267            id: "route-bc",
1268            d: 30.0,
1269            t: undefined,
1270            bbox: undefined
1271          }
1272        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
1273    ];
1274    let matrix = Geo.getDistanceMatrix(waypoints);
1275    let entries = mapEntries(matrix);
1276    ctx.expect(entries.length).toBe(2);
1277    let hasAB = entries.some(param => stringArraysEqual(param[0], [
1278      "A",
1279      "B"
1280    ]));
1281    ctx.expect(hasAB).toBe(true);
1282    let hasABC = entries.some(param => stringArraysEqual(param[0], [
1283      "A",
1284      "B",
1285      "C"
1286    ]));
1287    ctx.expect(hasABC).toBe(true);
1288    let hasLongPath = entries.some(param => param[0].length > 3);
1289    ctx.expect(hasLongPath).toBe(false);
1290  });
1291  Vitest.test("no origin: returns empty matrix", ctx => {
1292    let waypoints = [
1293      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([[
1294          "X",
1295          {
1296            id: "route-xa",
1297            d: 100.0,
1298            t: undefined,
1299            bbox: undefined
1300          }
1301        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1302      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
1303          "A",
1304          {
1305            id: "route-ab",
1306            d: 50.0,
1307            t: undefined,
1308            bbox: undefined
1309          }
1310        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
1311    ];
1312    let matrix = Geo.getDistanceMatrix(waypoints);
1313    let entries = mapEntries(matrix);
1314    ctx.expect(entries.length).toBe(0);
1315  });
1316  Vitest.test("route keys with suffixes: parses correctly", ctx => {
1317    let waypoints = [
1318      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1319      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
1320          "A:saa",
1321          {
1322            id: "route-ab",
1323            d: 100.0,
1324            t: undefined,
1325            bbox: undefined
1326          }
1327        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1328      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
1329          "B:saa",
1330          {
1331            id: "route-bc",
1332            d: 50.0,
1333            t: undefined,
1334            bbox: undefined
1335          }
1336        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
1337    ];
1338    let matrix = Geo.getDistanceMatrix(waypoints);
1339    let entries = mapEntries(matrix);
1340    ctx.expect(entries.length).toBe(2);
1341    let hasAB = entries.some(param => stringArraysEqual(param[0], [
1342      "A",
1343      "B"
1344    ]));
1345    ctx.expect(hasAB).toBe(true);
1346    let hasABC = entries.some(param => stringArraysEqual(param[0], [
1347      "A",
1348      "B",
1349      "C"
1350    ]));
1351    ctx.expect(hasABC).toBe(true);
1352  });
1353});
1354
1355Vitest.describe("Geo.relativeDistances", () => {
1356  Vitest.test("linear path: calculates correct relative distances", ctx => {
1357    let waypoints = [
1358      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1359      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
1360          "A",
1361          {
1362            id: undefined,
1363            d: 100.0,
1364            t: undefined,
1365            bbox: undefined
1366          }
1367        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1368      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
1369          "B",
1370          {
1371            id: undefined,
1372            d: 50.0,
1373            t: undefined,
1374            bbox: undefined
1375          }
1376        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
1377    ];
1378    let matrix = Geo.getDistanceMatrix(waypoints);
1379    let relative = Geo.relativeDistances("B", matrix);
1380    ctx.expect(relative.get("A")).toEqual(-100.0);
1381    ctx.expect(relative.get("B")).toEqual(0.0);
1382    ctx.expect(relative.get("C")).toEqual(50.0);
1383  });
1384  Vitest.test("reference at origin: all distances positive", ctx => {
1385    let waypoints = [
1386      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1387      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
1388          "A",
1389          {
1390            id: undefined,
1391            d: 100.0,
1392            t: undefined,
1393            bbox: undefined
1394          }
1395        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1396      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
1397          "B",
1398          {
1399            id: undefined,
1400            d: 50.0,
1401            t: undefined,
1402            bbox: undefined
1403          }
1404        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
1405    ];
1406    let matrix = Geo.getDistanceMatrix(waypoints);
1407    let relative = Geo.relativeDistances("A", matrix);
1408    ctx.expect(relative.get("A")).toEqual(0.0);
1409    ctx.expect(relative.get("B")).toEqual(100.0);
1410    ctx.expect(relative.get("C")).toEqual(150.0);
1411  });
1412  Vitest.test("reference at destination: all distances negative", ctx => {
1413    let waypoints = [
1414      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1415      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
1416          "A",
1417          {
1418            id: undefined,
1419            d: 100.0,
1420            t: undefined,
1421            bbox: undefined
1422          }
1423        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1424      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
1425          "B",
1426          {
1427            id: undefined,
1428            d: 50.0,
1429            t: undefined,
1430            bbox: undefined
1431          }
1432        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
1433    ];
1434    let matrix = Geo.getDistanceMatrix(waypoints);
1435    let relative = Geo.relativeDistances("C", matrix);
1436    ctx.expect(relative.get("A")).toEqual(-150.0);
1437    ctx.expect(relative.get("B")).toEqual(-50.0);
1438    ctx.expect(relative.get("C")).toEqual(0.0);
1439  });
1440  Vitest.test("multiple paths: chooses shortest absolute distance", ctx => {
1441    let waypoints = [
1442      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1443      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
1444          "A",
1445          {
1446            id: undefined,
1447            d: 100.0,
1448            t: undefined,
1449            bbox: undefined
1450          }
1451        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1452      makeWaypoint("D", undefined, Primitive_option.some(Object.fromEntries([[
1453          "A",
1454          {
1455            id: undefined,
1456            d: 80.0,
1457            t: undefined,
1458            bbox: undefined
1459          }
1460        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1461      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([
1462        [
1463          "B",
1464          {
1465            id: undefined,
1466            d: 50.0,
1467            t: undefined,
1468            bbox: undefined
1469          }
1470        ],
1471        [
1472          "D",
1473          {
1474            id: undefined,
1475            d: 30.0,
1476            t: undefined,
1477            bbox: undefined
1478          }
1479        ]
1480      ])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
1481    ];
1482    let matrix = Geo.getDistanceMatrix(waypoints);
1483    let relativeToC = Geo.relativeDistances("C", matrix);
1484    ctx.expect(relativeToC.get("A")).toEqual(-110.0);
1485    ctx.expect(relativeToC.get("B")).toEqual(-50.0);
1486    ctx.expect(relativeToC.get("D")).toEqual(-30.0);
1487    ctx.expect(relativeToC.get("C")).toEqual(0.0);
1488    let relativeToB = Geo.relativeDistances("B", matrix);
1489    ctx.expect(relativeToB.get("A")).toEqual(-100.0);
1490    ctx.expect(relativeToB.get("B")).toEqual(0.0);
1491    ctx.expect(relativeToB.get("C")).toEqual(50.0);
1492    ctx.expect(relativeToB.get("D")).toEqual(undefined);
1493  });
1494  Vitest.test("reference not in matrix: returns empty map", ctx => {
1495    let waypoints = [
1496      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1497      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
1498          "A",
1499          {
1500            id: undefined,
1501            d: 100.0,
1502            t: undefined,
1503            bbox: undefined
1504          }
1505        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
1506    ];
1507    let matrix = Geo.getDistanceMatrix(waypoints);
1508    let relative = Geo.relativeDistances("Z", matrix);
1509    ctx.expect(relative.size).toBe(0);
1510  });
1511  Vitest.test("complex branching: correct relative distances", ctx => {
1512    let waypoints = [
1513      makeWaypoint("A", undefined, Primitive_option.some(Object.fromEntries([])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1514      makeWaypoint("B", undefined, Primitive_option.some(Object.fromEntries([[
1515          "A",
1516          {
1517            id: undefined,
1518            d: 10.0,
1519            t: undefined,
1520            bbox: undefined
1521          }
1522        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1523      makeWaypoint("C", undefined, Primitive_option.some(Object.fromEntries([[
1524          "B",
1525          {
1526            id: undefined,
1527            d: 20.0,
1528            t: undefined,
1529            bbox: undefined
1530          }
1531        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1532      makeWaypoint("D", undefined, Primitive_option.some(Object.fromEntries([[
1533          "B",
1534          {
1535            id: undefined,
1536            d: 5.0,
1537            t: undefined,
1538            bbox: undefined
1539          }
1540        ]])), undefined, undefined, undefined, undefined, undefined, undefined, undefined),
1541      makeWaypoint("E", undefined, Primitive_option.some(Object.fromEntries([
1542        [
1543          "C",
1544          {
1545            id: undefined,
1546            d: 30.0,
1547            t: undefined,
1548            bbox: undefined
1549          }
1550        ],
1551        [
1552          "D",
1553          {
1554            id: undefined,
1555            d: 30.0,
1556            t: undefined,
1557            bbox: undefined
1558          }
1559        ]
1560      ])), undefined, undefined, undefined, undefined, undefined, undefined, undefined)
1561    ];
1562    let matrix = Geo.getDistanceMatrix(waypoints);
1563    let relativeToE = Geo.relativeDistances("E", matrix);
1564    ctx.expect(relativeToE.get("A")).toEqual(-45.0);
1565    ctx.expect(relativeToE.get("B")).toEqual(-35.0);
1566    ctx.expect(relativeToE.get("C")).toEqual(-30.0);
1567    ctx.expect(relativeToE.get("D")).toEqual(-30.0);
1568    ctx.expect(relativeToE.get("E")).toEqual(0.0);
1569    let relativeToC = Geo.relativeDistances("C", matrix);
1570    ctx.expect(relativeToC.get("A")).toEqual(-30.0);
1571    ctx.expect(relativeToC.get("B")).toEqual(-20.0);
1572    ctx.expect(relativeToC.get("C")).toEqual(0.0);
1573    ctx.expect(relativeToC.get("D")).toEqual(undefined);
1574    ctx.expect(relativeToC.get("E")).toEqual(30.0);
1575  });
1576});
1577
1578export {
1579  makeRoute,
1580  makeWaypoint,
1581  makeLineGeoJSON,
1582  makeLineGeoJSONWithProps,
1583  mapEntries,
1584  stringArraysEqual,
1585}
1586/*  Not a pure module */