create_map.js

import { createLocation } from "./create_location.js";
import { logger } from "./utils.js";
import { random } from "./random_utils.js";
import { parseSvgMetadata } from "./svg_metadata.js";
import MapModel from "./models/map.js";

/**
 * Fetches an SVG topo map from `url`, extracts its embedded site/label metadata (via
 * parseSvgMetadata — no DOMParser, so this runs in the worker like every other generator),
 * and generates a shimmed Location for every marker. Returns a Map model carrying every
 * generated Location plus the ordered marker instructions a renderer needs to place them
 * on the SVG.
 *
 * @param {Object} [options={}]
 *   @param {String} options.url the SVG's URL
 * @returns {Map} the generated map
 */
export async function createMap({ url } = {}) {
  logger.start("createMap", { url });
  const response = await fetch(url);
  const svgText = await response.text();
  const meta = parseSvgMetadata(svgText);

  const locations = [];
  const markers = [];
  let ptr = 1;

  meta.sites.forEach(({ id, types }) => {
    if (!types.length) return;
    const loc = createLocation({ type: random(types), shim: true });
    locations.push(loc);
    markers.push({ targetIds: [id], kind: "site", ptr: ptr++, locGuid: loc.guid });
  });

  const grouped = new Map();
  const ungrouped = [];
  meta.labels.forEach(({ id, type, group }) => {
    if (group) {
      if (!grouped.has(group)) grouped.set(group, { type, ids: [] });
      grouped.get(group).ids.push(id);
    } else {
      ungrouped.push({ id, type });
    }
  });

  grouped.forEach(({ type, ids }) => {
    const loc = createLocation({ type, shim: true });
    locations.push(loc);
    markers.push({ targetIds: ids, kind: "label", ptr: null, locGuid: loc.guid });
  });

  ungrouped.forEach(({ id, type }) => {
    const loc = createLocation({ type, shim: true });
    locations.push(loc);
    markers.push({ targetIds: [id], kind: "label", ptr: ptr++, locGuid: loc.guid });
  });

  return logger.end(
    new MapModel({
      url,
      name: meta.name,
      width: meta.width,
      height: meta.height,
      locations,
      markers,
    }),
  );
}