{"version":3,"sources":["/Users/bdon/workspace/protomaps/protomaps-leaflet/dist/cjs/index.cjs","../../src/frontends/static.ts","../../src/default_style/style.ts","../../src/attribute.ts","../../src/task.ts"],"names":["_StringAttr","c","defaultValue","z","f","__name","StringAttr","_NumberAttr","NumberAttr","_TextAttr","options","_a","retval","labelProps","property","transform","word","TextAttr","_FontAttr","_b","style","_Sheet"],"mappings":"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","file":"/Users/bdon/workspace/protomaps/protomaps-leaflet/dist/cjs/index.cjs","sourcesContent":[null,"import Point from \"@mapbox/point-geometry\";\n\nimport { namedFlavor } from \"@protomaps/basemaps\";\nimport { PMTiles } from \"pmtiles\";\nimport { labelRules, paintRules } from \"../default_style/style\";\nimport { LabelRule, Labeler } from \"../labeler\";\nimport { PaintRule, paint } from \"../painter\";\nimport { PreparedTile, SourceOptions, View, sourcesToViews } from \"../view\";\n\nconst R = 6378137;\nconst MAX_LATITUDE = 85.0511287798;\nconst MAXCOORD = R * Math.PI;\n\nconst project = (latlng: Point): Point => {\n  const d = Math.PI / 180;\n  const constrainedLat = Math.max(\n    Math.min(MAX_LATITUDE, latlng.y),\n    -MAX_LATITUDE,\n  );\n  const sin = Math.sin(constrainedLat * d);\n  return new Point(R * latlng.x * d, (R * Math.log((1 + sin) / (1 - sin))) / 2);\n};\n\nconst unproject = (point: Point) => {\n  const d = 180 / Math.PI;\n  return {\n    lat: (2 * Math.atan(Math.exp(point.y / R)) - Math.PI / 2) * d,\n    lng: (point.x * d) / R,\n  };\n};\n\nconst instancedProject = (origin: Point, displayZoom: number) => {\n  return (latlng: Point) => {\n    const projected = project(latlng);\n    const normalized = new Point(\n      (projected.x + MAXCOORD) / (MAXCOORD * 2),\n      1 - (projected.y + MAXCOORD) / (MAXCOORD * 2),\n    );\n    return normalized.mult(2 ** displayZoom * 256).sub(origin);\n  };\n};\n\nconst instancedUnproject = (origin: Point, displayZoom: number) => {\n  return (point: Point) => {\n    const normalized = new Point(point.x, point.y)\n      .add(origin)\n      .div(2 ** displayZoom * 256);\n    const projected = new Point(\n      normalized.x * (MAXCOORD * 2) - MAXCOORD,\n      (1 - normalized.y) * (MAXCOORD * 2) - MAXCOORD,\n    );\n    return unproject(projected);\n  };\n};\n\nexport const getZoom = (degreesLng: number, cssPixels: number): number => {\n  const d = cssPixels * (360 / degreesLng);\n  return Math.log2(d / 256);\n};\n\ninterface StaticOptions {\n  debug?: string;\n  lang?: string;\n  maxDataZoom?: number;\n  url?: PMTiles | string;\n  sources?: Record<string, SourceOptions>;\n  paintRules?: PaintRule[];\n  labelRules?: LabelRule[];\n  backgroundColor?: string;\n  flavor?: string;\n}\n\nexport class Static {\n  paintRules: PaintRule[];\n  labelRules: LabelRule[];\n  views: Map<string, View>;\n  debug?: string;\n  backgroundColor?: string;\n\n  constructor(options: StaticOptions) {\n    if (options.flavor) {\n      const flavor = namedFlavor(options.flavor);\n      this.paintRules = paintRules(flavor);\n      this.labelRules = labelRules(flavor, options.lang || \"en\");\n      this.backgroundColor = flavor.background;\n    } else {\n      this.paintRules = options.paintRules || [];\n      this.labelRules = options.labelRules || [];\n      this.backgroundColor = options.backgroundColor;\n    }\n\n    this.views = sourcesToViews(options);\n    this.debug = options.debug || \"\";\n  }\n\n  async drawContext(\n    ctx: CanvasRenderingContext2D,\n    width: number,\n    height: number,\n    latlng: Point,\n    displayZoom: number,\n  ) {\n    const center = project(latlng);\n    const normalizedCenter = new Point(\n      (center.x + MAXCOORD) / (MAXCOORD * 2),\n      1 - (center.y + MAXCOORD) / (MAXCOORD * 2),\n    );\n\n    // the origin of the painter call in global Z coordinates\n    const origin = normalizedCenter\n      .clone()\n      .mult(2 ** displayZoom * 256)\n      .sub(new Point(width / 2, height / 2));\n\n    // the bounds of the painter call in global Z coordinates\n    const bbox = {\n      minX: origin.x,\n      minY: origin.y,\n      maxX: origin.x + width,\n      maxY: origin.y + height,\n    };\n\n    const promises = [];\n    for (const [k, v] of this.views) {\n      const promise = v.getBbox(displayZoom, bbox);\n      promises.push({ key: k, promise: promise });\n    }\n    const tileResponses = await Promise.all(\n      promises.map((o) => {\n        return o.promise.then(\n          (v: PreparedTile[]) => {\n            return { status: \"fulfilled\", value: v, key: o.key };\n          },\n          (error: Error) => {\n            return { status: \"rejected\", value: [], reason: error, key: o.key };\n          },\n        );\n      }),\n    );\n\n    const preparedTilemap = new Map<string, PreparedTile[]>();\n    for (const tileResponse of tileResponses) {\n      if (tileResponse.status === \"fulfilled\") {\n        preparedTilemap.set(tileResponse.key, tileResponse.value);\n      }\n    }\n\n    const start = performance.now();\n    const labeler = new Labeler(\n      displayZoom,\n      ctx,\n      this.labelRules,\n      16,\n      undefined,\n    ); // because need ctx to measure\n\n    const layoutTime = labeler.add(preparedTilemap);\n\n    if (this.backgroundColor) {\n      ctx.save();\n      ctx.fillStyle = this.backgroundColor;\n      ctx.fillRect(0, 0, width, height);\n      ctx.restore();\n    }\n\n    const paintRules = this.paintRules;\n\n    const p = paint(\n      ctx,\n      displayZoom,\n      preparedTilemap,\n      labeler.index,\n      paintRules,\n      bbox,\n      origin,\n      true,\n      this.debug,\n    );\n\n    if (this.debug) {\n      ctx.save();\n      ctx.translate(-origin.x, -origin.y);\n      ctx.strokeStyle = this.debug;\n      ctx.fillStyle = this.debug;\n      ctx.font = \"12px sans-serif\";\n      let idx = 0;\n      for (const [k, v] of preparedTilemap) {\n        for (const preparedTile of v) {\n          ctx.strokeRect(\n            preparedTile.origin.x,\n            preparedTile.origin.y,\n            preparedTile.dim,\n            preparedTile.dim,\n          );\n          const dt = preparedTile.dataTile;\n          ctx.fillText(\n            `${k + (k ? \" \" : \"\") + dt.z} ${dt.x} ${dt.y}`,\n            preparedTile.origin.x + 4,\n            preparedTile.origin.y + 14 * (1 + idx),\n          );\n        }\n        idx++;\n      }\n      ctx.restore();\n    }\n\n    // TODO this API isn't so elegant\n    return {\n      elapsed: performance.now() - start,\n      project: instancedProject(origin, displayZoom),\n      unproject: instancedUnproject(origin, displayZoom),\n    };\n  }\n\n  async drawCanvas(\n    canvas: HTMLCanvasElement,\n    latlng: Point,\n    displayZoom: number,\n    options: StaticOptions = {},\n  ) {\n    const dpr = window.devicePixelRatio;\n    const width = canvas.clientWidth;\n    const height = canvas.clientHeight;\n    if (!(canvas.width === width * dpr && canvas.height === height * dpr)) {\n      canvas.width = width * dpr;\n      canvas.height = height * dpr;\n    }\n    if (options.lang) canvas.lang = options.lang;\n    const ctx = canvas.getContext(\"2d\");\n    if (!ctx) {\n      console.error(\"Failed to initialize canvas2d context.\");\n      return;\n    }\n    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n    return this.drawContext(ctx, width, height, latlng, displayZoom);\n  }\n\n  async drawContextBounds(\n    ctx: CanvasRenderingContext2D,\n    topLeft: Point,\n    bottomRight: Point,\n    width: number,\n    height: number,\n  ) {\n    const deltaDegrees = bottomRight.x - topLeft.x;\n    const center = new Point(\n      (topLeft.x + bottomRight.x) / 2,\n      (topLeft.y + bottomRight.y) / 2,\n    );\n    return this.drawContext(\n      ctx,\n      width,\n      height,\n      center,\n      getZoom(deltaDegrees, width),\n    );\n  }\n\n  async drawCanvasBounds(\n    canvas: HTMLCanvasElement,\n    topLeft: Point,\n    bottomRight: Point,\n    width: number,\n    options: StaticOptions = {},\n  ) {\n    const deltaDegrees = bottomRight.x - topLeft.x;\n    const center = new Point(\n      (topLeft.x + bottomRight.x) / 2,\n      (topLeft.y + bottomRight.y) / 2,\n    );\n    return this.drawCanvas(\n      canvas,\n      center,\n      getZoom(deltaDegrees, width),\n      options,\n    );\n  }\n}\n","import { type Flavor } from \"@protomaps/basemaps\";\nimport { mix } from \"color2k\";\nimport { LabelRule } from \"../labeler\";\nimport { PaintRule } from \"../painter\";\nimport {\n  CenteredTextSymbolizer,\n  CircleSymbolizer,\n  GroupSymbolizer,\n  LineLabelSymbolizer,\n  LineSymbolizer,\n  OffsetTextSymbolizer,\n  PolygonSymbolizer,\n  exp,\n  linear,\n} from \"../symbolizer\";\nimport { Feature, GeomType, JsonObject } from \"../tilecache\";\n\nconst getString = (props: JsonObject, key: string): string => {\n  const val = props[key];\n  if (typeof val === \"string\") return val;\n  return \"\";\n};\n\nconst getNumber = (props: JsonObject, key: string): number => {\n  const val = props[key];\n  if (typeof val === \"number\") return val;\n  return 0;\n};\n\nexport const paintRules = (t: Flavor): PaintRule[] => {\n  return [\n    {\n      dataLayer: \"earth\",\n      symbolizer: new PolygonSymbolizer({\n        fill: t.earth,\n      }),\n    },\n    ...(t.landcover\n      ? [\n          {\n            dataLayer: \"landcover\",\n            symbolizer: new PolygonSymbolizer({\n              fill: (z, f) => {\n                const landcover = t.landcover;\n                if (!landcover || !f) return \"\";\n                const kind = getString(f.props, \"kind\");\n                if (kind === \"grassland\") return landcover.grassland;\n                if (kind === \"barren\") return landcover.barren;\n                if (kind === \"urban_area\") return landcover.urban_area;\n                if (kind === \"farmland\") return landcover.farmland;\n                if (kind === \"glacier\") return landcover.glacier;\n                if (kind === \"scrub\") return landcover.scrub;\n                return landcover.forest;\n              },\n              opacity: (z, f) => {\n                if (z === 8) return 0.5;\n                return 1;\n              },\n            }),\n          },\n        ]\n      : []),\n    {\n      dataLayer: \"landuse\",\n      symbolizer: new PolygonSymbolizer({\n        fill: (z, f) => {\n          return mix(t.park_a, t.park_b, Math.min(Math.max(z / 12.0, 12), 0));\n        },\n      }),\n      filter: (z, f) => {\n        const kind = getString(f.props, \"kind\");\n        return [\"allotments\", \"village_green\", \"playground\"].includes(kind);\n      },\n    },\n    {\n      dataLayer: \"landuse\",\n      symbolizer: new PolygonSymbolizer({\n        fill: t.park_b,\n        opacity: (z, f) => {\n          if (z < 8) return 0;\n          if (z === 8) return 0.5;\n          return 1;\n        },\n      }),\n      filter: (z, f) => {\n        const kind = getString(f.props, \"kind\");\n        return [\n          \"national_park\",\n          \"park\",\n          \"cemetery\",\n          \"protected_area\",\n          \"nature_reserve\",\n          \"forest\",\n          \"golf_course\",\n        ].includes(kind);\n      },\n    },\n    {\n      dataLayer: \"landuse\",\n      symbolizer: new PolygonSymbolizer({\n        fill: t.hospital,\n      }),\n      filter: (z, f) => {\n        return f.props.kind === \"hospital\";\n      },\n    },\n    {\n      dataLayer: \"landuse\",\n      symbolizer: new PolygonSymbolizer({\n        fill: t.industrial,\n      }),\n      filter: (z, f) => {\n        return f.props.kind === \"industrial\";\n      },\n    },\n    {\n      dataLayer: \"landuse\",\n      symbolizer: new PolygonSymbolizer({\n        fill: t.school,\n      }),\n      filter: (z, f) => {\n        const kind = getString(f.props, \"kind\");\n        return [\"school\", \"university\", \"college\"].includes(kind);\n      },\n    },\n    {\n      dataLayer: \"landuse\",\n      symbolizer: new PolygonSymbolizer({\n        fill: t.beach,\n      }),\n      filter: (z, f) => {\n        return f.props.kind === \"beach\";\n      },\n    },\n    {\n      dataLayer: \"landuse\",\n      symbolizer: new PolygonSymbolizer({\n        fill: t.zoo,\n      }),\n      filter: (z, f) => {\n        return f.props.kind === \"zoo\";\n      },\n    },\n    {\n      dataLayer: \"landuse\",\n      symbolizer: new PolygonSymbolizer({\n        fill: t.zoo,\n      }),\n      filter: (z, f) => {\n        const kind = getString(f.props, \"kind\");\n        return [\"military\", \"naval_base\", \"airfield\"].includes(kind);\n      },\n    },\n    {\n      dataLayer: \"landuse\",\n      symbolizer: new PolygonSymbolizer({\n        fill: (z, f) => {\n          return mix(t.wood_a, t.wood_b, Math.min(Math.max(z / 12.0, 12), 0));\n        },\n        opacity: (z, f) => {\n          if (z < 8) return 0;\n          if (z === 8) return 0.5;\n          return 1;\n        },\n      }),\n      filter: (z, f) => {\n        const kind = getString(f.props, \"kind\");\n        return [\"wood\", \"nature_reserve\", \"forest\"].includes(kind);\n      },\n    },\n    {\n      dataLayer: \"landuse\",\n      symbolizer: new PolygonSymbolizer({\n        fill: (z, f) => {\n          return mix(t.scrub_a, t.scrub_b, Math.min(Math.max(z / 12.0, 12), 0));\n        },\n      }),\n      filter: (z, f) => {\n        const kind = getString(f.props, \"kind\");\n        return [\"scrub\", \"grassland\", \"grass\"].includes(kind);\n      },\n    },\n    {\n      dataLayer: \"landuse\",\n      symbolizer: new PolygonSymbolizer({\n        fill: t.scrub_b,\n      }),\n      filter: (z, f) => {\n        const kind = getString(f.props, \"kind\");\n        return [\"scrub\", \"grassland\", \"grass\"].includes(kind);\n      },\n    },\n    {\n      dataLayer: \"landuse\",\n      symbolizer: new PolygonSymbolizer({\n        fill: t.glacier,\n      }),\n      filter: (z, f) => {\n        return f.props.kind === \"glacier\";\n      },\n    },\n    {\n      dataLayer: \"landuse\",\n      symbolizer: new PolygonSymbolizer({\n        fill: t.sand,\n        opacity: (z, f) => {\n          if (z < 8) return 0;\n          if (z === 8) return 0.5;\n          return 1;\n        },\n      }),\n      filter: (z, f) => {\n        return f.props.kind === \"sand\";\n      },\n    },\n    {\n      dataLayer: \"landuse\",\n      symbolizer: new PolygonSymbolizer({\n        fill: t.aerodrome,\n      }),\n      filter: (z, f) => {\n        return f.props.kind === \"aerodrome\";\n      },\n    },\n    {\n      dataLayer: \"water\",\n      symbolizer: new PolygonSymbolizer({\n        fill: t.water,\n      }),\n      filter: (z, f) => {\n        return f.geomType === GeomType.Polygon;\n      },\n    },\n    {\n      dataLayer: \"roads\",\n      symbolizer: new LineSymbolizer({\n        color: t.runway,\n        width: (z, f) => {\n          return exp(1.6, [\n            [11, 0],\n            [13, 4],\n            [19, 30],\n          ])(z);\n        },\n      }),\n      filter: (z, f) => {\n        return f.props.kind_detail === \"runway\";\n      },\n    },\n    {\n      dataLayer: \"roads\",\n      symbolizer: new LineSymbolizer({\n        color: t.runway,\n        width: (z, f) => {\n          return exp(1.6, [\n            [14, 0],\n            [14.5, 1],\n            [16, 6],\n          ])(z);\n        },\n      }),\n      filter: (z, f) => {\n        return f.props.kind_detail === \"taxiway\";\n      },\n    },\n    {\n      dataLayer: \"roads\",\n      symbolizer: new LineSymbolizer({\n        color: t.pier,\n        width: (z, f) => {\n          return exp(1.6, [\n            [13, 0],\n            [13.5, 0, 5],\n            [21, 16],\n          ])(z);\n        },\n      }),\n      filter: (z, f) => {\n        return f.props.kind === \"path\" && f.props.kind_detail === \"pier\";\n      },\n    },\n    {\n      dataLayer: \"water\",\n      minzoom: 14,\n      symbolizer: new LineSymbolizer({\n        color: t.water,\n        width: (z, f) => {\n          return exp(1.6, [\n            [9, 0],\n            [9.5, 1.0],\n            [18, 12],\n          ])(z);\n        },\n      }),\n      filter: (z, f) => {\n        return f.geomType === GeomType.Line && f.props.kind === \"river\";\n      },\n    },\n    {\n      dataLayer: \"water\",\n      minzoom: 14,\n      symbolizer: new LineSymbolizer({\n        color: t.water,\n        width: 0.5,\n      }),\n      filter: (z, f) => {\n        return f.geomType === GeomType.Line && f.props.kind === \"stream\";\n      },\n    },\n    {\n      dataLayer: \"landuse\",\n      symbolizer: new PolygonSymbolizer({\n        fill: t.pedestrian,\n      }),\n      filter: (z, f) => {\n        return f.props.kind === \"pedestrian\";\n      },\n    },\n    {\n      dataLayer: \"landuse\",\n      symbolizer: new PolygonSymbolizer({\n        fill: t.pier,\n      }),\n      filter: (z, f) => {\n        return f.props.kind === \"pier\";\n      },\n    },\n    {\n      dataLayer: \"buildings\",\n      symbolizer: new PolygonSymbolizer({\n        fill: t.buildings,\n        opacity: 0.5,\n      }),\n    },\n    {\n      dataLayer: \"roads\",\n      symbolizer: new LineSymbolizer({\n        color: t.major,\n        width: (z, f) => {\n          return exp(1.6, [\n            [14, 0],\n            [20, 7],\n          ])(z);\n        },\n      }),\n      filter: (z, f) => {\n        const kind = getString(f.props, \"kind\");\n        return [\"other\", \"path\"].includes(kind);\n      },\n    },\n    {\n      dataLayer: \"roads\",\n      symbolizer: new LineSymbolizer({\n        color: t.major,\n        width: (z, f) => {\n          return exp(1.6, [\n            [13, 0],\n            [18, 8],\n          ])(z);\n        },\n      }),\n      filter: (z, f) => {\n        return f.props.kind === \"minor_road\";\n      },\n    },\n    {\n      dataLayer: \"roads\",\n      symbolizer: new LineSymbolizer({\n        color: t.major,\n        width: (z, f) => {\n          return exp(1.6, [\n            [6, 0],\n            [12, 1.6],\n            [15, 3],\n            [18, 13],\n          ])(z);\n        },\n      }),\n      filter: (z, f) => {\n        return f.props.kind === \"major_road\";\n      },\n    },\n    {\n      dataLayer: \"roads\",\n      symbolizer: new LineSymbolizer({\n        color: t.major,\n        width: (z, f) => {\n          return exp(1.6, [\n            [3, 0],\n            [6, 1.1],\n            [12, 1.6],\n            [15, 5],\n            [18, 15],\n          ])(z);\n        },\n      }),\n      filter: (z, f) => {\n        return f.props.kind === \"highway\";\n      },\n    },\n    {\n      dataLayer: \"boundaries\",\n      symbolizer: new LineSymbolizer({\n        color: t.boundaries,\n        width: 1,\n      }),\n      filter: (z, f) => {\n        const minAdminLevel = f.props.kind_detail;\n        return typeof minAdminLevel === \"number\" && minAdminLevel <= 2;\n      },\n    },\n    {\n      dataLayer: \"roads\",\n      symbolizer: new LineSymbolizer({\n        dash: [0.3, 0.75],\n        color: t.railway,\n        dashWidth: (z, f) => {\n          return exp(1.6, [\n            [4, 0],\n            [7, 0.15],\n            [19, 9],\n          ])(z);\n        },\n        opacity: 0.5,\n      }),\n      filter: (z, f) => {\n        return f.props.kind === \"rail\";\n      },\n    },\n    {\n      dataLayer: \"boundaries\",\n      symbolizer: new LineSymbolizer({\n        color: t.boundaries,\n        width: 0.5,\n      }),\n      filter: (z, f) => {\n        const minAdminLevel = f.props.kind_detail;\n        return typeof minAdminLevel === \"number\" && minAdminLevel > 2;\n      },\n    },\n  ];\n};\n\nexport const labelRules = (t: Flavor, lang: string): LabelRule[] => {\n  const nametags = [`name:${lang}`, \"name\"];\n\n  return [\n    // {\n    //   id: \"neighbourhood\",\n    //   dataLayer: \"places\",\n    //   symbolizer: languageStack(\n    //     new CenteredTextSymbolizer({\n    //       labelProps: nametags,\n    //       fill: params.neighbourhoodLabel,\n    //       font: \"500 10px sans-serif\",\n    //       textTransform: \"uppercase\",\n    //     }),\n    //     params.neighbourhoodLabel,\n    //   ),\n    //   filter: (z, f) => {\n    //     return f.props[\"kind\"] === \"neighbourhood\";\n    //   },\n    // },\n    {\n      dataLayer: \"roads\",\n      symbolizer: new LineLabelSymbolizer({\n        labelProps: nametags,\n        fill: t.roads_label_minor,\n        font: \"400 12px sans-serif\",\n        width: 2,\n        stroke: t.roads_label_minor_halo,\n      }),\n      // TODO: sort by minzoom\n      minzoom: 16,\n      filter: (z, f) => {\n        const kind = getString(f.props, \"kind\");\n        return [\"minor_road\", \"other\", \"path\"].includes(kind);\n      },\n    },\n    {\n      dataLayer: \"roads\",\n      symbolizer: new LineLabelSymbolizer({\n        labelProps: nametags,\n        fill: t.roads_label_major,\n        font: \"400 12px sans-serif\",\n        width: 2,\n        stroke: t.roads_label_major_halo,\n      }),\n      // TODO: sort by minzoom\n      minzoom: 12,\n      filter: (z, f) => {\n        const kind = getString(f.props, \"kind\");\n        return [\"highway\", \"major_road\"].includes(kind);\n      },\n    },\n    {\n      dataLayer: \"roads\",\n      symbolizer: new LineLabelSymbolizer({\n        labelProps: nametags,\n        fill: t.roads_label_major,\n        font: \"400 12px sans-serif\",\n        width: 2,\n        stroke: t.roads_label_major_halo,\n      }),\n      // TODO: sort by minzoom\n      minzoom: 12,\n      filter: (z, f) => {\n        const kind = getString(f.props, \"kind\");\n        return [\"highway\", \"major_road\"].includes(kind);\n      },\n    },\n    {\n      dataLayer: \"water\",\n      symbolizer: new CenteredTextSymbolizer({\n        labelProps: nametags,\n        fill: t.ocean_label,\n        lineHeight: 1.5,\n        letterSpacing: 1,\n        font: (z, f) => {\n          const size = linear([\n            [3, 10],\n            [10, 12],\n          ])(z);\n          return `400 ${size}px sans-serif`;\n        },\n        textTransform: \"uppercase\",\n      }),\n      filter: (z, f) => {\n        const kind = getString(f.props, \"kind\");\n        return (\n          f.geomType === GeomType.Point &&\n          [\"ocean\", \"bay\", \"strait\", \"fjord\"].includes(kind)\n        );\n      },\n    },\n    {\n      dataLayer: \"water\",\n      symbolizer: new CenteredTextSymbolizer({\n        labelProps: nametags,\n        fill: t.ocean_label,\n        lineHeight: 1.5,\n        letterSpacing: 1,\n        font: (z, f) => {\n          const size = linear([\n            [3, 10],\n            [6, 12],\n            [10, 12],\n          ])(z);\n          return `400 ${size}px sans-serif`;\n        },\n      }),\n      filter: (z, f) => {\n        const kind = getString(f.props, \"kind\");\n        return (\n          f.geomType === GeomType.Point &&\n          [\"sea\", \"lake\", \"water\"].includes(kind)\n        );\n      },\n    },\n    {\n      dataLayer: \"places\",\n      symbolizer: new CenteredTextSymbolizer({\n        labelProps: (z, f) => {\n          if (z < 6) {\n            return [`ref:${lang}`, \"ref\"];\n          }\n          return nametags;\n        },\n        fill: t.state_label,\n        stroke: t.state_label_halo,\n        width: 1,\n        lineHeight: 1.5,\n        font: (z: number, f?: Feature) => {\n          return \"400 12px sans-serif\";\n        },\n        textTransform: \"uppercase\",\n      }),\n      filter: (z, f) => {\n        return f.props.kind === \"region\";\n      },\n    },\n    {\n      dataLayer: \"places\",\n      symbolizer: new CenteredTextSymbolizer({\n        labelProps: nametags,\n        fill: t.country_label,\n        lineHeight: 1.5,\n        font: (z: number, f?: Feature) => {\n          if (z < 6) return \"600 12px sans-serif\";\n          return \"600 12px sans-serif\";\n        },\n        textTransform: \"uppercase\",\n      }),\n      filter: (z, f) => {\n        return f.props.kind === \"country\";\n      },\n    },\n    {\n      // places_locality\n      dataLayer: \"places\",\n      minzoom: 9,\n      symbolizer: new CenteredTextSymbolizer({\n        labelProps: nametags,\n        fill: t.city_label,\n        lineHeight: 1.5,\n        font: (z: number, f?: Feature) => {\n          if (!f) return \"400 12px sans-serif\";\n          const minZoom = f.props.min_zoom;\n          let weight = 400;\n          if (minZoom && minZoom <= 5) {\n            weight = 600;\n          }\n          let size = 12;\n          const popRank = f.props.population_rank;\n          if (popRank && popRank > 9) {\n            size = 16;\n          }\n          return `${weight} ${size}px sans-serif`;\n        },\n      }),\n      sort: (a, b) => {\n        const aRank = getNumber(a, \"min_zoom\");\n        const bRank = getNumber(b, \"min_zoom\");\n        return aRank - bRank;\n      },\n      filter: (z, f) => {\n        return f.props.kind === \"locality\";\n      },\n    },\n    {\n      dataLayer: \"places\",\n      maxzoom: 8,\n      symbolizer: new GroupSymbolizer([\n        new CircleSymbolizer({\n          radius: 2,\n          fill: t.city_label,\n          stroke: t.city_label_halo,\n          width: 1.5,\n        }),\n        new OffsetTextSymbolizer({\n          labelProps: nametags,\n          fill: t.city_label,\n          stroke: t.city_label_halo,\n          width: 1,\n          offsetX: 6,\n          offsetY: 4.5,\n          font: (z, f) => {\n            return \"400 12px sans-serif\";\n          },\n        }),\n      ]),\n      filter: (z, f) => {\n        return f.props.kind === \"locality\";\n      },\n    },\n  ];\n};\n","import { Feature } from \"./tilecache\";\n\nexport type AttrOption<T> = T | ((z: number, f?: Feature) => T);\n\nexport class StringAttr<T extends string = string> {\n  str: AttrOption<T>;\n  perFeature: boolean;\n\n  constructor(c: AttrOption<T> | undefined, defaultValue: T) {\n    this.str = c ?? defaultValue;\n    this.perFeature = typeof this.str === \"function\" && this.str.length === 2;\n  }\n\n  public get(z: number, f?: Feature): T {\n    if (typeof this.str === \"function\") {\n      return this.str(z, f);\n    }\n    return this.str;\n  }\n}\n\nexport class NumberAttr {\n  value: AttrOption<number>;\n  perFeature: boolean;\n\n  constructor(c: AttrOption<number> | undefined, defaultValue = 1) {\n    this.value = c ?? defaultValue;\n    this.perFeature =\n      typeof this.value === \"function\" && this.value.length === 2;\n  }\n\n  public get(z: number, f?: Feature): number {\n    if (typeof this.value === \"function\") {\n      return this.value(z, f);\n    }\n    return this.value;\n  }\n}\n\nexport interface TextAttrOptions {\n  labelProps?: AttrOption<string[]>;\n  textTransform?: AttrOption<string>;\n}\n\nexport class TextAttr {\n  labelProps: AttrOption<string[]>;\n  textTransform?: AttrOption<string>;\n\n  constructor(options?: TextAttrOptions) {\n    this.labelProps = options?.labelProps ?? [\"name\"];\n    this.textTransform = options?.textTransform;\n  }\n\n  public get(z: number, f: Feature): string | undefined {\n    let retval: string | undefined;\n\n    let labelProps: string[];\n    if (typeof this.labelProps === \"function\") {\n      labelProps = this.labelProps(z, f);\n    } else {\n      labelProps = this.labelProps;\n    }\n    for (const property of labelProps) {\n      if (\n        Object.prototype.hasOwnProperty.call(f.props, property) &&\n        typeof f.props[property] === \"string\"\n      ) {\n        retval = f.props[property] as string;\n        break;\n      }\n    }\n    let transform: string | ((z: number, f: Feature) => string) | undefined;\n    if (typeof this.textTransform === \"function\") {\n      transform = this.textTransform(z, f);\n    } else {\n      transform = this.textTransform;\n    }\n    if (retval && transform === \"uppercase\") retval = retval.toUpperCase();\n    else if (retval && transform === \"lowercase\") retval = retval.toLowerCase();\n    else if (retval && transform === \"capitalize\") {\n      const wordsArray = retval.toLowerCase().split(\" \");\n      const capsArray = wordsArray.map((word: string) => {\n        return word[0].toUpperCase() + word.slice(1);\n      });\n      retval = capsArray.join(\" \");\n    }\n    return retval;\n  }\n}\n\nexport interface FontAttrOptions {\n  font?: AttrOption<string>;\n  fontFamily?: AttrOption<string>;\n  fontSize?: AttrOption<number>;\n  fontWeight?: AttrOption<number>;\n  fontStyle?: AttrOption<string>;\n}\n\nexport class FontAttr {\n  family?: AttrOption<string>;\n  size?: AttrOption<number>;\n  weight?: AttrOption<number>;\n  style?: AttrOption<string>;\n  font?: AttrOption<string>;\n\n  constructor(options?: FontAttrOptions) {\n    if (options?.font) {\n      this.font = options.font;\n    } else {\n      this.family = options?.fontFamily ?? \"sans-serif\";\n      this.size = options?.fontSize ?? 12;\n      this.weight = options?.fontWeight;\n      this.style = options?.fontStyle;\n    }\n  }\n\n  public get(z: number, f?: Feature) {\n    if (this.font) {\n      if (typeof this.font === \"function\") {\n        return this.font(z, f);\n      }\n      return this.font;\n    }\n    let style = \"\";\n    if (this.style) {\n      if (typeof this.style === \"function\") {\n        style = `${this.style(z, f)} `;\n      } else {\n        style = `${this.style} `;\n      }\n    }\n\n    let weight = \"\";\n    if (this.weight) {\n      if (typeof this.weight === \"function\") {\n        weight = `${this.weight(z, f)} `;\n      } else {\n        weight = `${this.weight} `;\n      }\n    }\n\n    let size: number | ((z: number, f: Feature) => number) | undefined;\n    if (typeof this.size === \"function\") {\n      size = this.size(z, f);\n    } else {\n      size = this.size;\n    }\n\n    let family: string | ((z: number, f: Feature) => string) | undefined;\n    if (typeof this.family === \"function\") {\n      family = this.family(z, f);\n    } else {\n      family = this.family;\n    }\n\n    return `${style}${weight}${size}px ${family}`;\n  }\n}\n\nexport class ArrayAttr<T = number> {\n  value: AttrOption<T[]>;\n  perFeature: boolean;\n\n  constructor(c: AttrOption<T[]>, defaultValue: T[] = []) {\n    this.value = c ?? defaultValue;\n    this.perFeature =\n      typeof this.value === \"function\" && this.value.length === 2;\n  }\n\n  public get(z: number, f?: Feature): T[] {\n    if (typeof this.value === \"function\") {\n      return this.value(z, f);\n    }\n    return this.value;\n  }\n}\n","import potpack from \"potpack\";\n\n// https://github.com/tangrams/tangram/blob/master/src/styles/text/font_manager.js\nexport const Font = (name: string, url: string, weight: string) => {\n  const ff = new FontFace(name, `url(${url})`, { weight: weight });\n  document.fonts.add(ff);\n  return ff.load();\n};\n\ninterface Sprite {\n  x: number;\n  y: number;\n  w: number;\n  h: number;\n}\n\ninterface PotPackInput {\n  x?: number;\n  y?: number;\n  w: number;\n  h: number;\n  id: string;\n  img: HTMLImageElement;\n}\n\nconst mkimg = async (src: string): Promise<HTMLImageElement> => {\n  return new Promise((resolve, reject) => {\n    const img = new Image();\n    img.onload = () => resolve(img);\n    img.onerror = () => reject(\"Invalid SVG\");\n    img.src = src;\n  });\n};\n\nconst MISSING = `\n<svg width=\"20px\" height=\"20px\" viewBox=\"0 0 50 50\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n    <rect width=\"50\" height=\"50\" fill=\"#cccccc\"/>\n    <g transform=\"translate(5,5)\">\n        <path fill=\"none\" stroke=\"#666666\" stroke-width=\"7\" d=\"m11,12a8.5,8 0 1,1 17,0q0,4-4,6t-4.5,4.5-.4,4v.2m0,3v7\"/>\n    </g>\n</svg>\n`;\n\nexport class Sheet {\n  src: string;\n  canvas: HTMLCanvasElement;\n  mapping: Map<string, Sprite>;\n  missingBox: Sprite;\n\n  constructor(src: string) {\n    this.src = src;\n    this.canvas = document.createElement(\"canvas\");\n    this.mapping = new Map<string, Sprite>();\n    this.missingBox = { x: 0, y: 0, w: 0, h: 0 };\n  }\n\n  async load() {\n    let src = this.src;\n    const scale = window.devicePixelRatio;\n    if (src.endsWith(\".html\")) {\n      const c = await fetch(src);\n      src = await c.text();\n    }\n    const tree = new window.DOMParser().parseFromString(src, \"text/html\");\n    const icons = Array.from(tree.body.children);\n\n    const missingImg = await mkimg(\n      `data:image/svg+xml;base64,${btoa(MISSING)}`,\n    );\n\n    const boxes: PotPackInput[] = [\n      {\n        w: missingImg.width * scale,\n        h: missingImg.height * scale,\n        img: missingImg,\n        id: \"\",\n      },\n    ];\n\n    const serializer = new XMLSerializer();\n    for (const ps of icons) {\n      const svg64 = btoa(serializer.serializeToString(ps));\n      const image64 = `data:image/svg+xml;base64,${svg64}`;\n      const img = await mkimg(image64);\n      boxes.push({\n        w: img.width * scale,\n        h: img.height * scale,\n        img: img,\n        id: ps.id,\n      });\n    }\n\n    const packresult = potpack(boxes);\n    this.canvas.width = packresult.w;\n    this.canvas.height = packresult.h;\n    const ctx = this.canvas.getContext(\"2d\");\n    if (ctx) {\n      for (const box of boxes) {\n        if (box.x !== undefined && box.y !== undefined) {\n          ctx.drawImage(box.img, box.x, box.y, box.w, box.h);\n          if (box.id)\n            this.mapping.set(box.id, {\n              x: box.x,\n              y: box.y,\n              w: box.w,\n              h: box.h,\n            });\n          else this.missingBox = { x: box.x, y: box.y, w: box.w, h: box.h };\n        }\n      }\n    }\n    return this;\n  }\n\n  get(name: string): Sprite {\n    let result = this.mapping.get(name);\n    if (!result) result = this.missingBox;\n    return result;\n  }\n}\n"]}