+ ) : (
+ {
+ map.target.scrollWheelZoom.enable();
+
+ var info = new L.Control() as any;
+
+ info.onAdd = function () {
+ this._div = L.DomUtil.create('div', 'info');
+ this.update();
+ return this._div;
+ };
+
+ info.update = function () {
+ this._div.innerHTML = `
${title}
`;
+ };
+
+ if (title) info.addTo(map.target);
+
+ setTimeout(() => map.target.invalidateSize(), 5000);
+ }}
+ >
+ {
+ return { color: geoJsonFeature?.color };
+ }}
+ onEachFeature={onEachFeature}
+ />
+
+
+ );
+}
diff --git a/packages/components/src/include.css b/packages/components/src/include.css
new file mode 100644
index 00000000..c9bc585e
--- /dev/null
+++ b/packages/components/src/include.css
@@ -0,0 +1,6 @@
+/* Temporary fix for a size issue with FlatUiTable loading indicator on Firefox */
+@layer base {
+ svg[tw^='animate-pulse w-12'] {
+ max-width: 100px;
+ }
+}
diff --git a/packages/components/src/index.css b/packages/components/src/index.css
index c61c638e..7a21fb11 100644
--- a/packages/components/src/index.css
+++ b/packages/components/src/index.css
@@ -1,10 +1,7 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-/* Temporary fix for a size issue with FlatUiTable loading indicator on Firefox */
-@layer base {
- svg[tw^='animate-pulse w-12'] {
- max-width: 100px;
- }
-}
+@import "tailwindcss/base";
+@import "tailwindcss/components";
+@import "leaflet";
+@import "include";
+@import "tailwindcss/utilities";
+
diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts
index de07d08e..1302d7e7 100644
--- a/packages/components/src/index.ts
+++ b/packages/components/src/index.ts
@@ -1,7 +1,8 @@
-export * from './components/Table';
-export * from './components/Catalog';
-export * from './components/LineChart';
-export * from './components/Vega';
-export * from './components/VegaLite';
-export * from './components/FlatUiTable';
+export * from "./components/Table";
+export * from "./components/Catalog";
+export * from "./components/LineChart";
+export * from "./components/Vega";
+export * from "./components/VegaLite";
+export * from "./components/FlatUiTable";
export * from './components/OpenLayers/OpenLayers';
+export * from "./components/Map";
diff --git a/packages/components/src/types/GeoJSON.tsx b/packages/components/src/types/GeoJSON.tsx
new file mode 100644
index 00000000..8b751514
--- /dev/null
+++ b/packages/components/src/types/GeoJSON.tsx
@@ -0,0 +1,171 @@
+/**
+ * Typescript types for the GeoJSON RFC7946 specification. This is not fully RFC-compliant due to lack of support for
+ * ranged number data types.
+ *
+ * See https://tools.ietf.org/html/rfc7946
+ */
+export declare namespace GeoJSON {
+ /**
+ * Inside this document, the term "geometry type" refers to seven case-sensitive strings: "Point", "MultiPoint",
+ * "LineString", "MultiLineString", "Polygon", "MultiPolygon", and "GeometryCollection".
+ */
+ export type Geometry = Point | MultiPoint | LineString | MultiLineString | Polygon | MultiPolygon
+ | GeometryCollection;
+ export type GeometryType = Geometry["type"];
+
+ /**
+ * ...the term "GeoJSON types" refers to nine case-sensitive strings: "Feature", "FeatureCollection", and the
+ * geometry types listed above.
+ */
+ export type GeoJson = Geometry | Feature | FeatureCollection;
+ export type GeoJsonType = GeoJson["type"];
+
+ // types
+
+ /**
+ * A position is an array of numbers. There MUST be two or more elements. The first two elements are longitude and
+ * latitude, or easting and northing, precisely in that order and using decimal numbers. Altitude or elevation MAY
+ * be included as an optional third element.
+ *
+ * Implementations SHOULD NOT extend positions beyond three elements because the semantics of extra elements are
+ * unspecified and ambiguous.
+ */
+ export type Position = [longitude: number, latitude: number, elevation?: number]
+
+ export type Record = { [key in string | number]: unknown };
+
+ /**
+ * Properties inherit to all GeoJSON types
+ */
+ export interface GeometryBase extends Record {
+ /**
+ * A GeoJSON object MAY have a member named "bbox" to include information on the coordinate range for its
+ * Geometries, Features, or FeatureCollections. The value of the bbox member MUST be an array of length 2*n
+ * where n is the number of dimensions represented in the contained geometries, with all axes of the most
+ * southwesterly point followed by all axes of the more northeasterly point. The axes order of a bbox follows
+ * the axes order of geometries.
+ */
+ bbox?: number[];
+
+ /**
+ * A GeoJSON object MAY have other members.
+ *
+ * Members not described in this specification ("foreign members") MAY be used in a GeoJSON document. Note that
+ * support for foreign members can vary across implementations, and no normative processing model for foreign
+ * members is defined.
+ */
+ }
+
+ // geometry types
+
+ export interface Point extends GeometryBase {
+ type: "Point";
+ /**
+ * For type "Point", the "coordinates" member is a single position.
+ */
+ coordinates: Position;
+ }
+
+ export interface MultiPoint extends GeometryBase {
+ type: "MultiPoint";
+ /**
+ * For type "MultiPoint", the "coordinates" member is an array of positions.
+ */
+ coordinates: Position[];
+ }
+
+ export interface LineString extends GeometryBase {
+ type: "LineString";
+ /**
+ * For type "LineString", the "coordinates" member is an array of two or more positions.
+ */
+ coordinates: { 0: Position, 1: Position } & Position[]
+ }
+
+ export interface MultiLineString extends GeometryBase {
+ type: "MultiLineString";
+ /**
+ * For type "MultiLineString", the "coordinates" member is an array of LineString coordinate arrays.
+ */
+ coordinates: LineString["coordinates"][];
+ }
+
+ /**
+ * To specify a constraint specific to Polygons, it is useful to introduce the concept of a linear ring:
+ * - A linear ring is a closed LineString with four or more positions.
+ * - The first and last positions are equivalent, and they MUST contain identical values; their representation
+ * SHOULD also be identical.
+ * - A linear ring is the boundary of a surface or the boundary of a hole in a surface.
+ * - A linear ring MUST follow the right-hand rule with respect to the area it bounds, i.e., exterior rings are
+ * counterclockwise, and holes are clockwise.
+ */
+ export type LinearRing = { 0: Position, 1: Position, 2: Position, 3: Position } & Position[];
+
+ export interface Polygon extends GeometryBase {
+ type: "Polygon";
+ /**
+ * For type "Polygon", the "coordinates" member MUST be an array of linear ring coordinate arrays.
+ *
+ * For Polygons with more than one of these rings, the first MUST be the exterior ring, and any others MUST be
+ * interior rings. The exterior ring bounds the surface, and the interior rings (if present) bound holes within
+ * the surface.
+ */
+ coordinates: LinearRing[];
+ }
+
+ export interface MultiPolygon extends GeometryBase {
+ type: "MultiPolygon";
+ /**
+ * For type "MultiPolygon", the "coordinates" member is an array of Polygon coordinate arrays.
+ */
+ coordinates: Polygon["coordinates"][];
+ }
+
+ export interface GeometryCollection {
+ /**
+ * A GeoJSON object with type "GeometryCollection" is a Geometry object.
+ */
+ type: "GeometryCollection";
+ /**
+ * A GeometryCollection has a member with the name "geometries". The value of "geometries" is an array. Each
+ * element of this array is a GeoJSON Geometry object. It is possible for this array to be empty.
+ */
+ geometries: Geometry[];
+ }
+
+ // GeoJSON types
+
+ export interface Feature {
+ /**
+ * A Feature object has a "type" member with the value "Feature".
+ */
+ type: "Feature";
+ /**
+ * If a Feature has a commonly used identifier, that identifier SHOULD be included as a member of the Feature object
+ * with the name "id", and the value of this member is either a JSON string or number.
+ */
+ id?: string | number;
+ /**
+ * A Feature object has a member with the name "geometry". The value of the geometry member SHALL be either a
+ * Geometry object as defined above or, in the case that the Feature is unlocated, a JSON null value.
+ */
+ geometry: Geometry | null;
+ /**
+ * A Feature object has a member with the name "properties". The value of the properties member is an object
+ * (any JSON object or a JSON null value).
+ */
+ properties: Record | null;
+ }
+
+ export interface FeatureCollection {
+ /**
+ * A GeoJSON object with the type "FeatureCollection" is a FeatureCollection object.
+ */
+ type: "FeatureCollection";
+ /**
+ * A FeatureCollection object has a member with the name "features". The value of "features" is a JSON array. Each
+ * element of the array is a Feature object as defined above. It is possible for this array to be empty.
+ */
+ features: Feature[];
+ }
+}
diff --git a/packages/components/stories/Map.stories.ts b/packages/components/stories/Map.stories.ts
new file mode 100644
index 00000000..c39cdc9e
--- /dev/null
+++ b/packages/components/stories/Map.stories.ts
@@ -0,0 +1,55 @@
+import type { Meta, StoryObj } from '@storybook/react';
+
+import { Map, MapProps } from '../src/components/Map';
+
+// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
+const meta: Meta = {
+ title: 'Components/Map',
+ component: Map,
+ tags: ['autodocs'],
+ argTypes: {
+ data: {
+ description:
+ 'Data to be displayed.\n\n GeoJSON Object \n\nOR\n\n URL to GeoJSON Object',
+ },
+ title: {
+ description: 'Title to display on the map. Optional.',
+ },
+ center: {
+ description: 'Initial coordinates of the center of the map',
+ },
+ zoom: {
+ description: 'Zoom level',
+ },
+ tooltip: {
+ description: 'Tooltip settings'
+ }
+ },
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
+export const GeoJSONPolygons: Story = {
+ name: 'GeoJSON polygons map',
+ args: {
+ data: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_geography_marine_polys.geojson',
+ title: 'Seas and Oceans Map',
+ center: { latitude: 45, longitude: 0 },
+ zoom: 2,
+ tooltip: { prop: 'name' },
+ },
+};
+
+export const GeoJSONPoints: Story = {
+ name: 'GeoJSON points map',
+ args: {
+ data: 'https://opendata.arcgis.com/datasets/9c58741995174fbcb017cf46c8a42f4b_25.geojson',
+ title: 'Roads in York',
+ center: { latitude: 53.9614, longitude: -1.0739 },
+ zoom: 12,
+ tooltip: { prop: 'Location' },
+ },
+};
diff --git a/packages/components/tailwind.config.js b/packages/components/tailwind.config.js
index b4304ca9..98fd9860 100644
--- a/packages/components/tailwind.config.js
+++ b/packages/components/tailwind.config.js
@@ -1,8 +1,6 @@
/** @type {import('tailwindcss').Config} */
export default {
- content: ['./index.html', './src/**/*.{js,ts,jsx,tsx,.stories.tsx}'],
- theme: {
- extend: {},
- },
+ content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
+ theme: {},
plugins: [],
};
diff --git a/packages/components/vite.config.ts b/packages/components/vite.config.ts
index 7a93222d..a0604ea1 100644
--- a/packages/components/vite.config.ts
+++ b/packages/components/vite.config.ts
@@ -37,8 +37,7 @@ const app = async (): Promise => {
'vega',
'react-vega',
'ol',
- 'ol/dom.js',
- 'ol/reproj.js',
+ 'leaflet'
],
output: {
manualChunks: undefined,
@@ -46,11 +45,10 @@ const app = async (): Promise => {
react: 'React',
ol: 'ol',
'ol-mapbox-style': 'ol-mapbox-style',
- 'ol/dom.js': 'ol/dom.js',
- 'ol/reproj.js': 'ol/reproj.js',
'react-vega': 'react-vega',
'react-dom': 'ReactDOM',
tailwindcss: 'tailwindcss',
+ leaflet: 'leaflet'
},
},
},