@guardian/interactive-component-library 0.1.0-alpha.57 → 0.1.0-alpha.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,9 +3,9 @@ import { useRef, useMemo, useId, useState, useLayoutEffect, useEffect, useCallba
3
3
  import { forwardRef, createPortal } from "preact/compat";
4
4
  import { scaleLinear as scaleLinear$1 } from "d3-scale";
5
5
  import { toChildArray, createElement, cloneElement, createContext } from "preact";
6
- import { geoStream, geoAlbers, geoPath, geoContains, geoMercator } from "d3-geo";
7
- import { zoomIdentity, zoom, ZoomTransform } from "d3-zoom";
8
- import { namespace, matcher, selector, selectorAll, selection, style, select } from "d3-selection";
6
+ import { geoStream, geoAlbers, geoPath, geoContains, geoMercator, geoIdentity, geoAlbersUsa } from "d3-geo";
7
+ import { zoomIdentity, ZoomTransform, zoom } from "d3-zoom";
8
+ import { namespace, matcher, selector, selectorAll, selection, style as style$1, select } from "d3-selection";
9
9
  const svg$9 = "_svg_b5io0_1";
10
10
  const group = "_group_b5io0_5";
11
11
  const rotated = "_rotated_b5io0_10";
@@ -331,9 +331,9 @@ function StackedBar({
331
331
  labelType === LabelType.hanging && !hideLabels && hangingLabelConfig.map((config, i) => renderLabel(config, i))
332
332
  ] });
333
333
  }
334
- const svg$8 = "_svg_ihy3w_6";
335
- const previous = "_previous_ihy3w_12";
336
- const next = "_next_ihy3w_16";
334
+ const svg$8 = "_svg_8bh9o_6";
335
+ const previous = "_previous_8bh9o_12";
336
+ const next = "_next_8bh9o_16";
337
337
  const defaultStyles$u = {
338
338
  svg: svg$8,
339
339
  previous,
@@ -497,25 +497,27 @@ const defaultStyles$r = {
497
497
  circle: circle$1,
498
498
  pulse
499
499
  };
500
- const CircleIcon = ({ color: color2, pulse: pulse2 = false, styles: styles2 }) => {
500
+ const CircleIcon = ({ color: color2, pulse: pulse2 = false, diameter = 11, styles: styles2 }) => {
501
501
  styles2 = mergeStyles(defaultStyles$r, styles2);
502
+ let radius = diameter / 2;
503
+ let padding = 2;
502
504
  return /* @__PURE__ */ jsx(
503
505
  "svg",
504
506
  {
505
507
  style: styles2.svg,
506
508
  fill: "none",
507
- height: "11",
508
- viewBox: "0 0 11 11",
509
- width: "11",
509
+ height: diameter + padding,
510
+ viewBox: `0 0 ${diameter + padding} ${diameter + padding}`,
511
+ width: diameter + padding,
510
512
  xmlns: "http://www.w3.org/2000/svg",
511
513
  children: /* @__PURE__ */ jsx(
512
- "rect",
514
+ "circle",
513
515
  {
514
516
  className: [styles2.circle, pulse2 && styles2.pulse].join(" "),
515
517
  style: { fill: color2 },
516
- height: "11",
517
- rx: "5.5",
518
- width: "11"
518
+ r: radius,
519
+ cx: radius + padding / 2,
520
+ cy: radius + padding / 2
519
521
  }
520
522
  )
521
523
  }
@@ -1143,6 +1145,19 @@ function AdSlot({ name: name2, sizeMapping, styles: styles2 }) {
1143
1145
  }
1144
1146
  ) });
1145
1147
  }
1148
+ const style = {
1149
+ "aspect-ratio-box": "_aspect-ratio-box_1e5oy_1"
1150
+ };
1151
+ function AspectRatioBox({ heightAsProportionOfWidth, children }) {
1152
+ return /* @__PURE__ */ jsx(
1153
+ "div",
1154
+ {
1155
+ className: style["aspect-ratio-box"],
1156
+ style: { "--aspect-ratio": heightAsProportionOfWidth },
1157
+ children: /* @__PURE__ */ jsx("div", { style: { position: "absolute", width: "100%", height: "100%" }, children })
1158
+ }
1159
+ );
1160
+ }
1146
1161
  const sortAscending = (accessor) => {
1147
1162
  return (a, b) => {
1148
1163
  const valueA = a[accessor];
@@ -2736,7 +2751,7 @@ function bboxFeature$1(bounds) {
2736
2751
  };
2737
2752
  return feature;
2738
2753
  }
2739
- const MapContext = createContext();
2754
+ const MapContext$1 = createContext();
2740
2755
  function SVGMapProvider({
2741
2756
  id: id2,
2742
2757
  mapRef,
@@ -2815,20 +2830,20 @@ function SVGMapProvider({
2815
2830
  findFeatureAtPoint
2816
2831
  };
2817
2832
  mapRef.current = context;
2818
- return /* @__PURE__ */ jsx(MapContext.Provider, { value: context, children });
2833
+ return /* @__PURE__ */ jsx(MapContext$1.Provider, { value: context, children });
2819
2834
  }
2820
2835
  const path = "_path_1cwd5_9";
2821
2836
  const defaultStyles$1 = {
2822
2837
  path
2823
2838
  };
2824
2839
  function CompositionBorders({ styles: styles2 }) {
2825
- const context = useContext(MapContext);
2840
+ const context = useContext(MapContext$1);
2826
2841
  const { projection } = context;
2827
2842
  styles2 = mergeStyles(defaultStyles$1, styles2);
2828
2843
  return /* @__PURE__ */ jsx("path", { className: styles2.path, d: projection.getCompositionBorders() });
2829
2844
  }
2830
2845
  function SVGRenderer({ children }) {
2831
- const { id: id2, config, size, selectedFeature, padding } = useContext(MapContext);
2846
+ const { id: id2, config, size, selectedFeature, padding } = useContext(MapContext$1);
2832
2847
  return /* @__PURE__ */ jsx(
2833
2848
  "svg",
2834
2849
  {
@@ -3266,7 +3281,7 @@ function Polygon$1({
3266
3281
  zIndex = 0,
3267
3282
  styles: styles2
3268
3283
  }) {
3269
- const context = useContext(MapContext);
3284
+ const context = useContext(MapContext$1);
3270
3285
  const [searchIndex, setSearchIndex] = useState();
3271
3286
  useEffect(() => {
3272
3287
  if (!context.path) return;
@@ -3324,7 +3339,7 @@ function Polygon$1({
3324
3339
  }) });
3325
3340
  }
3326
3341
  function Line({ id: id2, features, stroke = null, strokeWidth = 1, styles: styles2 }) {
3327
- const context = useContext(MapContext);
3342
+ const context = useContext(MapContext$1);
3328
3343
  const draw = (ctx, path2) => {
3329
3344
  for (const feature of features) {
3330
3345
  ctx.beginPath();
@@ -3365,7 +3380,7 @@ function Line({ id: id2, features, stroke = null, strokeWidth = 1, styles: style
3365
3380
  }) });
3366
3381
  }
3367
3382
  function Prerendered({ url }) {
3368
- const context = useContext(MapContext);
3383
+ const context = useContext(MapContext$1);
3369
3384
  const { width, height } = context.contentSize;
3370
3385
  return /* @__PURE__ */ jsx("image", { width, height, href: url });
3371
3386
  }
@@ -3379,7 +3394,7 @@ function Point$1({
3379
3394
  zIndex = 0,
3380
3395
  styles: styles2
3381
3396
  }) {
3382
- const context = useContext(MapContext);
3397
+ const context = useContext(MapContext$1);
3383
3398
  useEffect(() => {
3384
3399
  function findFeatureAtPoint(point) {
3385
3400
  for (const [index, feature] of features.entries()) {
@@ -3597,6 +3612,254 @@ function sizeForElement(element) {
3597
3612
  }
3598
3613
  return size;
3599
3614
  }
3615
+ function bboxFeature(bounds) {
3616
+ const minLon = bounds[0][0];
3617
+ const minLat = bounds[0][1];
3618
+ const maxLon = bounds[1][0];
3619
+ const maxLat = bounds[1][1];
3620
+ const feature = {
3621
+ type: "Feature",
3622
+ properties: {},
3623
+ geometry: {
3624
+ coordinates: [
3625
+ [
3626
+ [minLon, maxLat],
3627
+ [maxLon, maxLat],
3628
+ [maxLon, minLat],
3629
+ [minLon, minLat],
3630
+ [minLon, maxLat]
3631
+ ]
3632
+ ],
3633
+ type: "Polygon"
3634
+ }
3635
+ };
3636
+ return feature;
3637
+ }
3638
+ const BASE_RESOLUTION = 156543.03392;
3639
+ function zoomLevelToZoomScale(zoomLevel, initialResolution) {
3640
+ const resolution = BASE_RESOLUTION / Math.pow(2, zoomLevel);
3641
+ const zoomScale = initialResolution / resolution;
3642
+ return zoomScale;
3643
+ }
3644
+ function zoomLevelForResolution(currentResolution) {
3645
+ const zoomLevel = Math.log2(BASE_RESOLUTION / currentResolution);
3646
+ return zoomLevel;
3647
+ }
3648
+ function haversineDistance(lat1, lon1, lat2, lon2) {
3649
+ const R2 = 6371e3;
3650
+ const toRadians = (degrees2) => degrees2 * Math.PI / 180;
3651
+ const dLat = toRadians(lat2 - lat1);
3652
+ const dLon = toRadians(lon2 - lon1);
3653
+ const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
3654
+ const c2 = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
3655
+ return R2 * c2;
3656
+ }
3657
+ function resolutionForExtent(extent, viewportSize) {
3658
+ const [lonMin, latMin, lonMax, latMax] = extent;
3659
+ const latMid = (latMin + latMax) / 2;
3660
+ const distance = haversineDistance(latMid, lonMin, latMid, lonMax);
3661
+ const resolution = distance / viewportSize[0];
3662
+ return resolution;
3663
+ }
3664
+ const Projection = {
3665
+ geoIdentity: geoIdentity(),
3666
+ geoMercator: geoMercator(),
3667
+ geoAlbersUS: geoAlbersUsa().scale(1070).translate([487.5, 305]),
3668
+ geoAlbersUKComposite: geoAlbersUk(),
3669
+ geoAlbersEngland: geoAlbers().center([0, 52.7]).rotate([1.1743, 0]).parallels([50, 54])
3670
+ };
3671
+ function validateGeometries(geometries) {
3672
+ if (!Array.isArray(geometries)) {
3673
+ throw new Error("geometries must be an array");
3674
+ }
3675
+ geometries.forEach((geometry) => {
3676
+ var _a;
3677
+ if (!geometry.type) {
3678
+ throw new Error("geometry must have a type");
3679
+ }
3680
+ if (!((_a = geometry.coordinates) == null ? void 0 : _a.length)) {
3681
+ throw new Error("geometry must have coordinates");
3682
+ }
3683
+ });
3684
+ }
3685
+ function generateDebugUrl(feature, convertToGeoJSON = true) {
3686
+ const featureGeoJSON = convertToGeoJSON ? feature.getGeoJSON() : feature;
3687
+ const featureCollection = {
3688
+ type: "FeatureCollection",
3689
+ features: [featureGeoJSON]
3690
+ };
3691
+ const jsonString = encodeURIComponent(JSON.stringify(featureCollection));
3692
+ return `https://geojson.io/#data=data:application/json,${jsonString}`;
3693
+ }
3694
+ class View {
3695
+ constructor({
3696
+ projection = Projection.geoIdentity,
3697
+ extent,
3698
+ minZoom = 1,
3699
+ maxZoom = 10,
3700
+ padding = { top: 0, right: 0, bottom: 0, left: 0 }
3701
+ }, debug = false) {
3702
+ this.debug = debug;
3703
+ projection.revision = 0;
3704
+ this.projection = projection;
3705
+ this.extent = extent;
3706
+ this.minZoom = minZoom;
3707
+ this.maxZoom = maxZoom;
3708
+ this._transform = zoomIdentity;
3709
+ this._padding = padding;
3710
+ this._viewPortSize = [0, 0];
3711
+ this.pixelRatio = window.devicePixelRatio;
3712
+ }
3713
+ set viewPortSize(size) {
3714
+ const previousSize = this._viewPortSize;
3715
+ this._viewPortSize = size;
3716
+ if (previousSize !== size) {
3717
+ if (this.extent) {
3718
+ this.fitExtent(this.extent);
3719
+ }
3720
+ }
3721
+ }
3722
+ get viewPortSize() {
3723
+ return this._viewPortSize;
3724
+ }
3725
+ set transform(transform) {
3726
+ this._transform = transform;
3727
+ }
3728
+ get transform() {
3729
+ return new ZoomTransform(
3730
+ this._transform.k,
3731
+ this._transform.x * this.pixelRatio,
3732
+ this._transform.y * this.pixelRatio
3733
+ );
3734
+ }
3735
+ // map size in pixels (i.e. scaled by device pixel ratio)
3736
+ get mapSize() {
3737
+ return scaleSize(this.viewPortSize, this.pixelRatio);
3738
+ }
3739
+ get padding() {
3740
+ return this._padding;
3741
+ }
3742
+ // padding in pixels (i.e. scaled by device pixel ratio)
3743
+ get scaledPadding() {
3744
+ const scaledPadding = { ...this._padding };
3745
+ return scalePadding(scaledPadding, this.pixelRatio);
3746
+ }
3747
+ get baseResolution() {
3748
+ const baseExtent = this.getVisibleExtent(zoomIdentity, this.projection);
3749
+ const baseResolution = resolutionForExtent(baseExtent, this.viewPortSize);
3750
+ return baseResolution;
3751
+ }
3752
+ // calculates the upper and lower zoom scales
3753
+ get scaleExtent() {
3754
+ const maxScale = zoomLevelToZoomScale(this.maxZoom, this.baseResolution);
3755
+ return [1, maxScale];
3756
+ }
3757
+ setProjection(projection) {
3758
+ this.projection = projection;
3759
+ this.fitObject(bboxFeature(this.extent));
3760
+ }
3761
+ // only set the raw projection when it has already been configured with projection.fitExtent()
3762
+ setRawProjection(projection) {
3763
+ this.projection = projection;
3764
+ }
3765
+ fitExtent(extent) {
3766
+ const extentFeature = bboxFeature(extent);
3767
+ this.fitObject(extentFeature);
3768
+ if (this.debug) {
3769
+ console.log("Fit extent", extent, generateDebugUrl(extentFeature, false));
3770
+ }
3771
+ }
3772
+ fitObject(geoJSON) {
3773
+ this.projection.fitExtent(this.getMapExtent(), geoJSON);
3774
+ ++this.projection.revision;
3775
+ }
3776
+ // returns bounds relative to the viewport
3777
+ boundsForExtent(extent) {
3778
+ const SW = this.projection([extent[0], extent[1]]);
3779
+ const NE = this.projection([extent[2], extent[3]]);
3780
+ const minX = SW[0] / this.pixelRatio;
3781
+ const minY = NE[1] / this.pixelRatio;
3782
+ const maxX = NE[0] / this.pixelRatio;
3783
+ const maxY = SW[1] / this.pixelRatio;
3784
+ const width = maxX - minX;
3785
+ const height = maxY - minY;
3786
+ return [
3787
+ [minX, minY],
3788
+ [width, height]
3789
+ ];
3790
+ }
3791
+ invert(point) {
3792
+ const { projection, pixelRatio, transform } = this.getState();
3793
+ const scaledPoint = [point[0] * pixelRatio, point[1] * pixelRatio];
3794
+ const untransformedPoint = transform.invert(scaledPoint);
3795
+ const mapCoordinate = projection.invert(untransformedPoint);
3796
+ return mapCoordinate;
3797
+ }
3798
+ // bounds is defined as [[minX, minY], [maxX, maxY]]
3799
+ invertBounds(bounds) {
3800
+ const topLeft = bounds[0];
3801
+ const topRight = [bounds[1][0], bounds[0][1]];
3802
+ const bottomRight = [bounds[1][0], bounds[1][1]];
3803
+ const bottomLeft = [bounds[0][0], bounds[1][1]];
3804
+ const points = [topLeft, topRight, bottomRight, bottomLeft, topLeft];
3805
+ return points.map((d2) => this.invert(d2));
3806
+ }
3807
+ // map resolution (meters per pixel)
3808
+ getResolution() {
3809
+ return resolutionForExtent(
3810
+ this.getVisibleExtent(this.transform, this.projection),
3811
+ this.viewPortSize
3812
+ );
3813
+ }
3814
+ // map zoom level (0 = the entire world)
3815
+ getZoomLevel() {
3816
+ return zoomLevelForResolution(this.getResolution());
3817
+ }
3818
+ //
3819
+ /**
3820
+ * Function that returns the extent of the view in screen coordinates
3821
+ * The extent is defined as [[minX, minY], [maxX, maxY]]
3822
+ * @function getMapExtent
3823
+ * @returns {[[number, number], [number, number]]}
3824
+ */
3825
+ getMapExtent() {
3826
+ const mapSizeInPixels = this.mapSize;
3827
+ const paddingInPixels = this.scaledPadding;
3828
+ return [
3829
+ [paddingInPixels.left, paddingInPixels.top],
3830
+ sizeMinusPadding(mapSizeInPixels, {
3831
+ ...paddingInPixels,
3832
+ left: 0,
3833
+ top: 0
3834
+ })
3835
+ ];
3836
+ }
3837
+ // visible extent in map coordinates
3838
+ getVisibleExtent(transform, projection) {
3839
+ if (this.projection === Projection.geoIdentity) {
3840
+ const [width2, height2] = this.mapSize;
3841
+ return [0, 0, width2, height2];
3842
+ }
3843
+ const [width, height] = this.mapSize;
3844
+ const southWest = projection.invert(transform.invert([0, height]));
3845
+ const northEast = projection.invert(transform.invert([width, 0]));
3846
+ return [southWest[0], southWest[1], northEast[0], northEast[1]];
3847
+ }
3848
+ getState() {
3849
+ const transform = this.transform;
3850
+ const projection = this.projection;
3851
+ return {
3852
+ transform,
3853
+ projection,
3854
+ zoomLevel: transform.k,
3855
+ pixelRatio: this.pixelRatio,
3856
+ padding: this.padding,
3857
+ viewPortSize: this.viewPortSize,
3858
+ sizeInPixels: scaleSize(this.viewPortSize, this.pixelRatio),
3859
+ visibleExtent: this.getVisibleExtent(transform, projection)
3860
+ };
3861
+ }
3862
+ }
3600
3863
  function arrayEquals(arr1, arr2) {
3601
3864
  const len1 = arr1.length;
3602
3865
  if (len1 !== arr2.length) {
@@ -5265,7 +5528,7 @@ function transition_selection() {
5265
5528
  function styleNull(name2, interpolate2) {
5266
5529
  var string00, string10, interpolate0;
5267
5530
  return function() {
5268
- var string0 = style(this, name2), string1 = (this.style.removeProperty(name2), style(this, name2));
5531
+ var string0 = style$1(this, name2), string1 = (this.style.removeProperty(name2), style$1(this, name2));
5269
5532
  return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : interpolate0 = interpolate2(string00 = string0, string10 = string1);
5270
5533
  };
5271
5534
  }
@@ -5277,15 +5540,15 @@ function styleRemove(name2) {
5277
5540
  function styleConstant(name2, interpolate2, value1) {
5278
5541
  var string00, string1 = value1 + "", interpolate0;
5279
5542
  return function() {
5280
- var string0 = style(this, name2);
5543
+ var string0 = style$1(this, name2);
5281
5544
  return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate2(string00 = string0, value1);
5282
5545
  };
5283
5546
  }
5284
5547
  function styleFunction(name2, interpolate2, value) {
5285
5548
  var string00, string10, interpolate0;
5286
5549
  return function() {
5287
- var string0 = style(this, name2), value1 = value(this), string1 = value1 + "";
5288
- if (value1 == null) string1 = value1 = (this.style.removeProperty(name2), style(this, name2));
5550
+ var string0 = style$1(this, name2), value1 = value(this), string1 = value1 + "";
5551
+ if (value1 == null) string1 = value1 = (this.style.removeProperty(name2), style$1(this, name2));
5289
5552
  return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate2(string00 = string0, value1));
5290
5553
  };
5291
5554
  }
@@ -5476,18 +5739,19 @@ function selection_transition(name2) {
5476
5739
  selection.prototype.interrupt = selection_interrupt;
5477
5740
  selection.prototype.transition = selection_transition;
5478
5741
  let Map$2 = class Map2 {
5479
- constructor(options) {
5480
- this.options = options;
5481
- this.view = options.view;
5482
- this.target = options.target;
5742
+ constructor(config) {
5743
+ if (config.debug) {
5744
+ console.log("Map config", config);
5745
+ }
5746
+ this.options = config;
5747
+ this.view = new View(config.view, config.debug);
5748
+ this.target = config.target;
5483
5749
  this.layers = [];
5484
5750
  this.dispatcher = new Dispatcher(this);
5485
5751
  this._viewport = document.createElement("div");
5486
5752
  this._viewport.className = "gv-map";
5487
5753
  this._viewport.style.position = "relative";
5488
5754
  this._viewport.style.overflow = "hidden";
5489
- this._viewport.style.top = 0;
5490
- this._viewport.style.left = 0;
5491
5755
  this._viewport.style.width = "100%";
5492
5756
  this._viewport.style.height = "100%";
5493
5757
  this.target.appendChild(this._viewport);
@@ -5497,7 +5761,7 @@ let Map$2 = class Map2 {
5497
5761
  });
5498
5762
  this._resizeObserver.observe(this.target);
5499
5763
  this._viewport.addEventListener("touchmove", (event) => {
5500
- if (event.targetTouches.length < 2 && this.collaborativeGesturesEnabled) {
5764
+ if (event.targetTouches.length < 2 && this._collaborativeGesturesEnabled) {
5501
5765
  this._filterEventCallback(true);
5502
5766
  }
5503
5767
  });
@@ -5520,6 +5784,10 @@ let Map$2 = class Map2 {
5520
5784
  return this._isTransitioning;
5521
5785
  }
5522
5786
  /** PUBLIC METHODS */
5787
+ collaborativeGesturesEnabled(enabled) {
5788
+ if (enabled === void 0) return this._collaborativeGesturesEnabled;
5789
+ this._collaborativeGesturesEnabled = enabled;
5790
+ }
5523
5791
  onFilterEvent(callback) {
5524
5792
  this._filterEventCallback = callback;
5525
5793
  }
@@ -5595,6 +5863,18 @@ let Map$2 = class Map2 {
5595
5863
  );
5596
5864
  select(this._viewport).transition().duration(500).call(this._zoomBehaviour.transform, newTransform, focalPoint);
5597
5865
  }
5866
+ /** @param {import("./layers").Layer[]} layers */
5867
+ hasLayers(layers) {
5868
+ if (layers.length !== this.layers.length) {
5869
+ return false;
5870
+ }
5871
+ for (let i = 0; i < layers.length; i++) {
5872
+ if (layers[i] !== this.layers[i]) {
5873
+ return false;
5874
+ }
5875
+ }
5876
+ return true;
5877
+ }
5598
5878
  async resetZoom(options) {
5599
5879
  return this.zoomTo(1, options);
5600
5880
  }
@@ -5691,201 +5971,13 @@ let Map$2 = class Map2 {
5691
5971
  _renderFrame() {
5692
5972
  const frameState = {
5693
5973
  size: this.size,
5694
- viewState: this.view.getState()
5974
+ viewState: this.view.getState(),
5975
+ debug: this.options.debug || false
5695
5976
  };
5696
5977
  this._renderer.renderFrame(frameState);
5697
5978
  this._animationFrameRequestID = null;
5698
5979
  }
5699
5980
  };
5700
- function bboxFeature(bounds) {
5701
- const minLon = bounds[0][0];
5702
- const minLat = bounds[0][1];
5703
- const maxLon = bounds[1][0];
5704
- const maxLat = bounds[1][1];
5705
- const feature = {
5706
- type: "Feature",
5707
- properties: {},
5708
- geometry: {
5709
- coordinates: [
5710
- [
5711
- [minLon, maxLat],
5712
- [maxLon, maxLat],
5713
- [maxLon, minLat],
5714
- [minLon, minLat],
5715
- [minLon, maxLat]
5716
- ]
5717
- ],
5718
- type: "Polygon"
5719
- }
5720
- };
5721
- return feature;
5722
- }
5723
- const BASE_RESOLUTION = 156543.03392;
5724
- function zoomLevelToZoomScale(zoomLevel, initialResolution) {
5725
- const resolution = BASE_RESOLUTION / Math.pow(2, zoomLevel);
5726
- const zoomScale = initialResolution / resolution;
5727
- return zoomScale;
5728
- }
5729
- function zoomLevelForResolution(currentResolution) {
5730
- const zoomLevel = Math.log2(BASE_RESOLUTION / currentResolution);
5731
- return zoomLevel;
5732
- }
5733
- function haversineDistance(lat1, lon1, lat2, lon2) {
5734
- const R2 = 6371e3;
5735
- const toRadians = (degrees2) => degrees2 * Math.PI / 180;
5736
- const dLat = toRadians(lat2 - lat1);
5737
- const dLon = toRadians(lon2 - lon1);
5738
- const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
5739
- const c2 = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
5740
- return R2 * c2;
5741
- }
5742
- function resolutionForExtent(extent, viewportSize) {
5743
- const [lonMin, latMin, lonMax, latMax] = extent;
5744
- const latMid = (latMin + latMax) / 2;
5745
- const distance = haversineDistance(latMid, lonMin, latMid, lonMax);
5746
- const resolution = distance / viewportSize[0];
5747
- return resolution;
5748
- }
5749
- class View {
5750
- constructor({ projection, extent, minZoom, maxZoom, padding }) {
5751
- projection.revision = 0;
5752
- this.projection = projection;
5753
- this.extent = extent;
5754
- this.minZoom = minZoom;
5755
- this.maxZoom = maxZoom;
5756
- this._transform = zoomIdentity;
5757
- this._padding = padding;
5758
- this._viewPortSize = [0, 0];
5759
- this.pixelRatio = window.devicePixelRatio;
5760
- }
5761
- set viewPortSize(size) {
5762
- const previousSize = this._viewPortSize;
5763
- this._viewPortSize = size;
5764
- if (previousSize !== size) {
5765
- this.fitObject(bboxFeature(this.extent));
5766
- const initialExtent = this.getVisibleExtent(zoomIdentity, this.projection);
5767
- this.initialResolution = resolutionForExtent(initialExtent, size);
5768
- }
5769
- }
5770
- get viewPortSize() {
5771
- return this._viewPortSize;
5772
- }
5773
- set transform(transform) {
5774
- this._transform = transform;
5775
- }
5776
- get transform() {
5777
- return new ZoomTransform(
5778
- this._transform.k,
5779
- this._transform.x * this.pixelRatio,
5780
- this._transform.y * this.pixelRatio
5781
- );
5782
- }
5783
- // map size in pixels (i.e. scaled by device pixel ratio)
5784
- get mapSize() {
5785
- return scaleSize(this.viewPortSize, this.pixelRatio);
5786
- }
5787
- get padding() {
5788
- return this._padding;
5789
- }
5790
- // padding in pixels (i.e. scaled by device pixel ratio)
5791
- get scaledPadding() {
5792
- const scaledPadding = { ...this._padding };
5793
- return scalePadding(scaledPadding, this.pixelRatio);
5794
- }
5795
- // calculates the upper and lower zoom scales
5796
- get scaleExtent() {
5797
- const maxScale = zoomLevelToZoomScale(this.maxZoom, this.initialResolution);
5798
- return [1, maxScale];
5799
- }
5800
- setProjection(projection) {
5801
- this.projection = projection;
5802
- this.fitObject(bboxFeature(this.extent));
5803
- }
5804
- // only set the raw projection when it has already been configured with projection.fitExtent()
5805
- setRawProjection(projection) {
5806
- this.projection = projection;
5807
- }
5808
- fitObject(geoJSON) {
5809
- this.projection.fitExtent(this.getMapExtent(), geoJSON);
5810
- ++this.projection.revision;
5811
- }
5812
- // returns bounds relative to the viewport
5813
- boundsForExtent(extent) {
5814
- const SW = this.projection([extent[0], extent[1]]);
5815
- const NE = this.projection([extent[2], extent[3]]);
5816
- const minX = SW[0] / this.pixelRatio;
5817
- const minY = NE[1] / this.pixelRatio;
5818
- const maxX = NE[0] / this.pixelRatio;
5819
- const maxY = SW[1] / this.pixelRatio;
5820
- const width = maxX - minX;
5821
- const height = maxY - minY;
5822
- return [
5823
- [minX, minY],
5824
- [width, height]
5825
- ];
5826
- }
5827
- invert(point) {
5828
- const { projection, pixelRatio, transform } = this.getState();
5829
- const scaledPoint = [point[0] * pixelRatio, point[1] * pixelRatio];
5830
- const untransformedPoint = transform.invert(scaledPoint);
5831
- const mapCoordinate = projection.invert(untransformedPoint);
5832
- return mapCoordinate;
5833
- }
5834
- // bounds is defined as [[minX, minY], [maxX, maxY]]
5835
- invertBounds(bounds) {
5836
- const topLeft = bounds[0];
5837
- const topRight = [bounds[1][0], bounds[0][1]];
5838
- const bottomRight = [bounds[1][0], bounds[1][1]];
5839
- const bottomLeft = [bounds[0][0], bounds[1][1]];
5840
- const points = [topLeft, topRight, bottomRight, bottomLeft, topLeft];
5841
- return points.map((d2) => this.invert(d2));
5842
- }
5843
- // map resolution (meters per pixel)
5844
- getResolution() {
5845
- return resolutionForExtent(
5846
- this.getVisibleExtent(this.transform, this.projection),
5847
- this.viewPortSize
5848
- );
5849
- }
5850
- // map zoom level (0 = the entire world)
5851
- getZoomLevel() {
5852
- return zoomLevelForResolution(this.getResolution());
5853
- }
5854
- // get extent for drawn map
5855
- getMapExtent() {
5856
- const mapSizeInPixels = this.mapSize;
5857
- const paddingInPixels = this.scaledPadding;
5858
- return [
5859
- [paddingInPixels.left, paddingInPixels.top],
5860
- sizeMinusPadding(mapSizeInPixels, {
5861
- ...paddingInPixels,
5862
- left: 0,
5863
- top: 0
5864
- })
5865
- ];
5866
- }
5867
- // visible extent in map coordinates
5868
- getVisibleExtent(transform, projection) {
5869
- const [width, height] = this.mapSize;
5870
- const southWest = projection.invert(transform.invert([0, height]));
5871
- const northEast = projection.invert(transform.invert([width, 0]));
5872
- return [southWest[0], southWest[1], northEast[0], northEast[1]];
5873
- }
5874
- getState() {
5875
- const transform = this.transform;
5876
- const projection = this.projection;
5877
- return {
5878
- transform,
5879
- projection,
5880
- zoomLevel: transform.k,
5881
- pixelRatio: this.pixelRatio,
5882
- padding: this.padding,
5883
- viewPortSize: this.viewPortSize,
5884
- sizeInPixels: scaleSize(this.viewPortSize, this.pixelRatio),
5885
- visibleExtent: this.getVisibleExtent(transform, projection)
5886
- };
5887
- }
5888
- }
5889
5981
  const mapContainer = "_mapContainer_1ogf3_9";
5890
5982
  const helpTextContainer = "_helpTextContainer_1ogf3_15";
5891
5983
  const helpText = "_helpText_1ogf3_15";
@@ -5898,18 +5990,33 @@ const styles$3 = {
5898
5990
  desktopHelpText,
5899
5991
  mobileHelpText: mobileHelpText$1
5900
5992
  };
5993
+ const MapContext = createContext(null);
5994
+ function MapProvider({ map, children }) {
5995
+ const registeredLayers = [];
5996
+ const registerLayer = (layer) => {
5997
+ registeredLayers.push(layer);
5998
+ };
5999
+ useEffect(() => {
6000
+ if (map && !map.hasLayers(registeredLayers)) {
6001
+ map.setLayers(registeredLayers);
6002
+ }
6003
+ }, [map, children]);
6004
+ return /* @__PURE__ */ jsx(MapContext.Provider, { value: { registerLayer }, children });
6005
+ }
5901
6006
  const mobileHelpText = "Use two fingers to zoom";
5902
6007
  const Map$1 = forwardRef(
5903
6008
  ({ config, inModalState = false, onLoad, children }, ref) => {
5904
- const { layers } = children;
5905
6009
  const targetRef = useRef();
5906
- const [map, setMap] = useState();
6010
+ const [map, setMap] = useState(
6011
+ /** @type {_Map | null} */
6012
+ null
6013
+ );
5907
6014
  const [zoomHelpText, setZoomHelpText] = useState("");
5908
6015
  const [showHelpText, setShowHelpText] = useState(false);
5909
6016
  useEffect(() => {
5910
6017
  var _a;
5911
6018
  const map2 = new Map$2({
5912
- view: new View(config.view),
6019
+ ...config,
5913
6020
  target: targetRef.current
5914
6021
  });
5915
6022
  map2.collaborativeGesturesEnabled = true;
@@ -5925,7 +6032,7 @@ const Map$1 = forwardRef(
5925
6032
  map2.destroy();
5926
6033
  setMap(null);
5927
6034
  };
5928
- }, [config.view]);
6035
+ }, [config]);
5929
6036
  useEffect(() => {
5930
6037
  if (!map) return;
5931
6038
  let timeoutID;
@@ -5955,27 +6062,25 @@ const Map$1 = forwardRef(
5955
6062
  }
5956
6063
  };
5957
6064
  }, [map, ref, onLoad]);
5958
- useEffect(() => {
5959
- if (map && layers !== map.layers) {
5960
- map.setLayers(layers);
5961
- }
5962
- }, [map, layers]);
5963
6065
  useEffect(() => {
5964
6066
  if (!map) return;
5965
6067
  map.collaborativeGesturesEnabled = !inModalState;
5966
6068
  }, [map, inModalState]);
5967
- return /* @__PURE__ */ jsx("figure", { ref: targetRef, className: styles$3.mapContainer, children: /* @__PURE__ */ jsxs(
5968
- "div",
5969
- {
5970
- className: styles$3.helpTextContainer,
5971
- style: { opacity: showHelpText ? 1 : 0 },
5972
- "aria-hidden": true,
5973
- children: [
5974
- /* @__PURE__ */ jsx("p", { className: [styles$3.helpText, styles$3.desktopHelpText].join(" "), children: zoomHelpText }),
5975
- /* @__PURE__ */ jsx("p", { className: [styles$3.helpText, styles$3.mobileHelpText].join(" "), children: mobileHelpText })
5976
- ]
5977
- }
5978
- ) });
6069
+ return /* @__PURE__ */ jsxs("figure", { ref: targetRef, className: styles$3.mapContainer, children: [
6070
+ /* @__PURE__ */ jsxs(
6071
+ "div",
6072
+ {
6073
+ className: styles$3.helpTextContainer,
6074
+ style: { opacity: showHelpText ? 1 : 0 },
6075
+ "aria-hidden": true,
6076
+ children: [
6077
+ /* @__PURE__ */ jsx("p", { className: [styles$3.helpText, styles$3.desktopHelpText].join(" "), children: zoomHelpText }),
6078
+ /* @__PURE__ */ jsx("p", { className: [styles$3.helpText, styles$3.mobileHelpText].join(" "), children: mobileHelpText })
6079
+ ]
6080
+ }
6081
+ ),
6082
+ /* @__PURE__ */ jsx(MapProvider, { map, children })
6083
+ ] });
5979
6084
  }
5980
6085
  );
5981
6086
  function IconMinus() {
@@ -6079,11 +6184,6 @@ function ZoomControl({ resetEnabled, onZoomIn, onZoomOut, onReset }) {
6079
6184
  )
6080
6185
  ] });
6081
6186
  }
6082
- const Projection = {
6083
- geoAlbersUKComposite: geoAlbersUk(),
6084
- geoAlbersEngland: geoAlbers().center([0, 52.7]).rotate([1.1743, 0]).parallels([50, 54]),
6085
- geoMercator: geoMercator()
6086
- };
6087
6187
  class FeatureRenderer {
6088
6188
  constructor() {
6089
6189
  this.drawingFunction = geoPath();
@@ -6100,6 +6200,17 @@ class FeatureRenderer {
6100
6200
  this.drawingFunction.context(context);
6101
6201
  context.beginPath();
6102
6202
  const geometries = feature.getProjectedGeometries(projection);
6203
+ if (frameState.debug) {
6204
+ try {
6205
+ validateGeometries(geometries);
6206
+ } catch {
6207
+ console.error(
6208
+ `Invalid geometry. Feature skipped during rendering. Click here to inspect geometry: ${generateDebugUrl(feature)}
6209
+ `,
6210
+ feature
6211
+ );
6212
+ }
6213
+ }
6103
6214
  for (const geometry of geometries) {
6104
6215
  this.drawingFunction(geometry);
6105
6216
  }
@@ -6229,10 +6340,10 @@ class TextLayerRenderer {
6229
6340
  }
6230
6341
  }
6231
6342
  class Style {
6232
- constructor(options) {
6233
- this.stroke = options == null ? void 0 : options.stroke;
6234
- this.fill = options == null ? void 0 : options.fill;
6235
- this.text = options == null ? void 0 : options.text;
6343
+ constructor(properties) {
6344
+ this.stroke = properties == null ? void 0 : properties.stroke;
6345
+ this.fill = properties == null ? void 0 : properties.fill;
6346
+ this.text = properties == null ? void 0 : properties.text;
6236
6347
  }
6237
6348
  clone() {
6238
6349
  return new Style({
@@ -6462,10 +6573,50 @@ class VectorSource {
6462
6573
  }
6463
6574
  }
6464
6575
  class TextLayer {
6576
+ /** @param {TextLayerComponentProps} props */
6577
+ static Component({
6578
+ features,
6579
+ style: style2,
6580
+ minZoom,
6581
+ opacity,
6582
+ declutter,
6583
+ drawCollisionBoxes
6584
+ }) {
6585
+ const { registerLayer } = useContext(MapContext);
6586
+ const layer = useMemo(
6587
+ () => TextLayer.with(features, {
6588
+ style: style2,
6589
+ minZoom,
6590
+ opacity,
6591
+ declutter,
6592
+ drawCollisionBoxes
6593
+ }),
6594
+ // eslint-disable-next-line react-hooks/exhaustive-deps
6595
+ [features, minZoom, opacity, declutter, drawCollisionBoxes]
6596
+ );
6597
+ registerLayer(layer);
6598
+ useEffect(() => {
6599
+ layer.style = style2;
6600
+ }, [style2]);
6601
+ return null;
6602
+ }
6603
+ /**
6604
+ * @param {import("../Feature").Feature[]} features
6605
+ * @param {TextLayerOptions} options
6606
+ */
6465
6607
  static with(features, options) {
6466
6608
  const source = new VectorSource({ features });
6467
6609
  return new TextLayer({ source, ...options });
6468
6610
  }
6611
+ /**
6612
+ * @param {Object} params
6613
+ * @param {VectorSource} params.source
6614
+ * @param {Style} [params.style=undefined]
6615
+ * @param {number} [params.minZoom=0]
6616
+ * @param {number} [params.opacity=1]
6617
+ * @param {boolean} [params.declutter=true]
6618
+ * @param {boolean} [params.drawCollisionBoxes=false]
6619
+ */
6469
6620
  constructor({
6470
6621
  source,
6471
6622
  style: style2,
@@ -6594,10 +6745,41 @@ class VectorLayerRenderer {
6594
6745
  }
6595
6746
  }
6596
6747
  class VectorLayer {
6748
+ /** @param {VectorLayerComponentProps} props */
6749
+ static Component({ features, style: style2, minZoom, opacity, hitDetectionEnabled }) {
6750
+ const { registerLayer } = useContext(MapContext);
6751
+ const layer = useMemo(
6752
+ () => VectorLayer.with(features, {
6753
+ style: style2,
6754
+ minZoom,
6755
+ opacity,
6756
+ hitDetectionEnabled
6757
+ }),
6758
+ // eslint-disable-next-line react-hooks/exhaustive-deps
6759
+ [features, minZoom, opacity, hitDetectionEnabled]
6760
+ );
6761
+ registerLayer(layer);
6762
+ useEffect(() => {
6763
+ layer.style = style2;
6764
+ }, [style2]);
6765
+ return null;
6766
+ }
6767
+ /**
6768
+ * @param {import("../Feature").Feature[]} features
6769
+ * @param {VectorLayerOptions} options
6770
+ */
6597
6771
  static with(features, options) {
6598
6772
  const source = new VectorSource({ features });
6599
6773
  return new VectorLayer({ source, ...options });
6600
6774
  }
6775
+ /**
6776
+ * @param {Object} params
6777
+ * @param {VectorSource} params.source
6778
+ * @param {Style | (() => Style)} [params.style=undefined]
6779
+ * @param {number} [params.minZoom=0]
6780
+ * @param {number} [params.opacity=1]
6781
+ * @param {boolean} [params.hitDetectionEnabled=false]
6782
+ */
6601
6783
  constructor({
6602
6784
  source,
6603
6785
  style: style2,
@@ -6755,7 +6937,7 @@ function interpolateFeatures(currentFeatures, newFeatures, { interpolate: interp
6755
6937
  break;
6756
6938
  }
6757
6939
  }
6758
- feature.setGeometries(geometries);
6940
+ feature.setGeometry(geometries);
6759
6941
  features.push(feature);
6760
6942
  }
6761
6943
  return features;
@@ -6819,6 +7001,15 @@ function interpolateStroke(strokeA, strokeB, interpolateColors, interpolateNumbe
6819
7001
  };
6820
7002
  }
6821
7003
  class Feature {
7004
+ /**
7005
+ * Represents a feature on the map
7006
+ * @constructor
7007
+ * @param {Object} props - The properties for the feature.
7008
+ * @property {string} id - The unique identifier of the feature
7009
+ * @property {Array} geometries - The geometries of the feature
7010
+ * @property {Object} properties - The properties of the feature
7011
+ * @property {import("./styles").Style | import("./styles").StyleFunction} style - The style of the feature
7012
+ */
6822
7013
  constructor({ id: id2, geometries, properties, style: style2 }) {
6823
7014
  this.id = id2;
6824
7015
  this.geometries = geometries;
@@ -6828,14 +7019,14 @@ class Feature {
6828
7019
  }
6829
7020
  getExtent() {
6830
7021
  if (this._extent) return this._extent;
6831
- const extent = this.geometries.reduce((combinedExtent, geometry) => {
6832
- if (!combinedExtent) return geometry.extent;
6833
- return combineExtents(geometry.extent, combinedExtent);
7022
+ const extent = this.geometries.reduce((combinedExtent, geometries) => {
7023
+ if (!combinedExtent) return geometries.extent;
7024
+ return combineExtents(geometries.extent, combinedExtent);
6834
7025
  }, null);
6835
7026
  this._extent = extent;
6836
7027
  return extent;
6837
7028
  }
6838
- setGeometries(geometries) {
7029
+ setgeometries(geometries) {
6839
7030
  this.geometries = geometries;
6840
7031
  this._extent = void 0;
6841
7032
  }
@@ -6856,8 +7047,8 @@ class Feature {
6856
7047
  if (!containsCoordinate(this.getExtent(), coordinate)) {
6857
7048
  return false;
6858
7049
  }
6859
- for (const geometry of this.geometries) {
6860
- if (geoContains(geometry.getGeoJSON(), coordinate)) {
7050
+ for (const geometries of this.geometries) {
7051
+ if (geoContains(geometries.getGeoJSON(), coordinate)) {
6861
7052
  return true;
6862
7053
  }
6863
7054
  }
@@ -6871,14 +7062,83 @@ class Feature {
6871
7062
  style: this.style
6872
7063
  });
6873
7064
  }
7065
+ /**
7066
+ * Returns the geometries as a GeoJSON object
7067
+ * @returns {Object} The GeoJSON representation of the geometries
7068
+ */
7069
+ getGeoJSON() {
7070
+ const geometries = this.geometries.map((d2) => d2.getGeoJSON());
7071
+ if (geometries.length === 1) return geometries[0];
7072
+ return {
7073
+ type: "Feature",
7074
+ geometry: this._getGeometryGeoJSON(),
7075
+ properties: this.properties
7076
+ };
7077
+ }
7078
+ _getGeometryGeoJSON() {
7079
+ const geometries = this.geometries.map((d2) => d2.getGeoJSON());
7080
+ if (geometries.length === 0) throw new Error("Feature has no geometries");
7081
+ if (geometries.length === 1) return geometries[0];
7082
+ if (geometries[0].type === "Polygon") {
7083
+ return {
7084
+ type: "MultiPolygon",
7085
+ coordinates: geometries.map((d2) => d2.coordinates)
7086
+ };
7087
+ } else if (geometries[0].type === "LineString") {
7088
+ return {
7089
+ type: "MultiLineString",
7090
+ coordinates: geometries.map((d2) => d2.coordinates)
7091
+ };
7092
+ } else if (geometries[0].type === "Point") {
7093
+ return {
7094
+ type: "MultiPoint",
7095
+ coordinates: geometries.map((d2) => d2.coordinates)
7096
+ };
7097
+ }
7098
+ throw new Error("Could not determine geometry type");
7099
+ }
6874
7100
  }
6875
- class LineString {
6876
- constructor({ type = "LineString", extent, coordinates }) {
7101
+ class Geometry {
7102
+ /**
7103
+ * Represents vector geometry
7104
+ * @constructor
7105
+ * @param {Object} options
7106
+ * @param {string} options.type - The type of geometry (e.g., 'Point', 'LineString', 'Polygon')
7107
+ * @param {Array} options.extent - The extent of the geometry (e.g., [xmin, ymin, xmax, ymax])
7108
+ * @param {Array} options.coordinates - The coordinates of the geometry (e.g., [[x1, y1], [x2, y2], ...])
7109
+ */
7110
+ constructor({ type, extent, coordinates }) {
6877
7111
  this.type = type;
6878
7112
  this.extent = extent;
6879
7113
  this.coordinates = coordinates;
6880
7114
  this.getProjected = memoise(this._getProjected).bind(this);
6881
7115
  }
7116
+ /**
7117
+ * Returns the geometry as a GeoJSON object
7118
+ * @function
7119
+ * @param {import("../projection").Projection} projection - The projection to use for the geometry
7120
+ * @returns {Object} A GeoJSON representation of the projected geometry
7121
+ * @private
7122
+ */
7123
+ // eslint-disable-next-line no-unused-vars
7124
+ _getProjected(projection) {
7125
+ throw new Error("Not implemented");
7126
+ }
7127
+ /**
7128
+ * Returns the geometry as a GeoJSON object
7129
+ * @returns {Object} The GeoJSON representation of the geometry
7130
+ */
7131
+ getGeoJSON() {
7132
+ return {
7133
+ type: this.type,
7134
+ coordinates: this.coordinates
7135
+ };
7136
+ }
7137
+ }
7138
+ class LineString extends Geometry {
7139
+ constructor({ type = "LineString", extent, coordinates }) {
7140
+ super({ type, extent, coordinates });
7141
+ }
6882
7142
  _getProjected(projection) {
6883
7143
  const projected = [];
6884
7144
  for (const point of this.coordinates) {
@@ -6890,23 +7150,26 @@ class LineString {
6890
7150
  };
6891
7151
  }
6892
7152
  }
6893
- class Polygon {
7153
+ class Polygon extends Geometry {
6894
7154
  constructor({ type = "Polygon", extent, coordinates }) {
6895
- this.type = type;
6896
- this.extent = extent;
6897
- this.coordinates = coordinates;
6898
- this.getProjected = memoise(this._getProjected).bind(this);
7155
+ super({ type, extent, coordinates });
6899
7156
  }
6900
- // eslint-disable-next-line no-unused-vars
6901
- _getProjected(projection, _revision) {
7157
+ _getProjected(projection) {
6902
7158
  const projected = [];
6903
7159
  const rings = this.coordinates;
6904
7160
  for (const ring of rings) {
6905
7161
  const projectedRing = [];
6906
7162
  for (const point of ring) {
6907
- projectedRing.push(projection(point));
7163
+ const projectedPoint = projection(point);
7164
+ if (projectedPoint) {
7165
+ projectedRing.push(projectedPoint);
7166
+ } else {
7167
+ break;
7168
+ }
7169
+ }
7170
+ if (projectedRing.length > 0) {
7171
+ projected.push(projectedRing);
6908
7172
  }
6909
- projected.push(projectedRing);
6910
7173
  }
6911
7174
  return {
6912
7175
  type: this.type,
@@ -6922,12 +7185,6 @@ class Polygon {
6922
7185
  setCoordinates(coordinates) {
6923
7186
  this.coordinates = coordinates;
6924
7187
  }
6925
- getGeoJSON() {
6926
- return {
6927
- type: this.type,
6928
- coordinates: this.coordinates
6929
- };
6930
- }
6931
7188
  clone() {
6932
7189
  return new Polygon({
6933
7190
  extent: this.extent,
@@ -6935,12 +7192,10 @@ class Polygon {
6935
7192
  });
6936
7193
  }
6937
7194
  }
6938
- class Point {
7195
+ class Point extends Geometry {
6939
7196
  constructor({ type = "Point", coordinates }) {
6940
- this.type = type;
7197
+ super({ type, extent: null, coordinates });
6941
7198
  this.extent = [...coordinates, ...coordinates];
6942
- this.coordinates = coordinates;
6943
- this.getProjected = memoise(this._getProjected).bind(this);
6944
7199
  }
6945
7200
  _getProjected(projection) {
6946
7201
  return {
@@ -7463,6 +7718,7 @@ function useTouchOrHover() {
7463
7718
  export {
7464
7719
  AdSlot,
7465
7720
  ArrowButton,
7721
+ AspectRatioBox,
7466
7722
  Button,
7467
7723
  ChangeBar,
7468
7724
  Chevron,