Skip to content

Commit

Permalink
Merge pull request #292 from performant-software/feature/cdc248_geore…
Browse files Browse the repository at this point in the history
…ference_layers

CDC #248 - Geo-reference layers
  • Loading branch information
dleadbetter committed Jul 23, 2024
2 parents 0100a8b + 9f29644 commit cf205d5
Show file tree
Hide file tree
Showing 22 changed files with 630 additions and 67 deletions.
6 changes: 3 additions & 3 deletions packages/controlled-vocabulary/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@performant-software/controlled-vocabulary",
"version": "2.2.7",
"version": "2.2.8",
"description": "A package of components to allow user to configure dropdown elements. Use with the \"controlled_vocabulary\" gem.",
"license": "MIT",
"main": "./dist/index.cjs.js",
Expand All @@ -23,8 +23,8 @@
"underscore": "^1.13.2"
},
"peerDependencies": {
"@performant-software/semantic-components": "^2.2.7",
"@performant-software/shared-components": "^2.2.7",
"@performant-software/semantic-components": "^2.2.8",
"@performant-software/shared-components": "^2.2.8",
"react": ">= 16.13.1 < 19.0.0",
"react-dom": ">= 16.13.1 < 19.0.0"
},
Expand Down
6 changes: 3 additions & 3 deletions packages/core-data/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@performant-software/core-data",
"version": "2.2.7",
"version": "2.2.8",
"description": "A package of components used with the Core Data platform.",
"license": "MIT",
"main": "./dist/index.cjs.js",
Expand Down Expand Up @@ -37,8 +37,8 @@
"underscore": "^1.13.2"
},
"peerDependencies": {
"@performant-software/shared-components": "^2.2.7",
"@performant-software/geospatial": "^2.2.7",
"@performant-software/shared-components": "^2.2.8",
"@performant-software/geospatial": "^2.2.8",
"@peripleo/maplibre": "^0.5.2",
"@peripleo/peripleo": "^0.5.2",
"react": ">= 16.13.1 < 19.0.0",
Expand Down
28 changes: 22 additions & 6 deletions packages/core-data/src/components/OverlayLayers.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
// @flow

import { MapStyles } from '@performant-software/geospatial';
import { MapStyles, WarpedImageLayerPeripleo as WarpedImageLayer } from '@performant-software/geospatial';
import { GeoJSONLayer, RasterLayer } from '@peripleo/maplibre';
import React from 'react';
import _ from 'underscore';

type Layer = {
/**
* The type of layer to render.
* (Optional) GeoJSON data to pass to the layer.
*/
layer_type: 'geojson' | 'raster',
content?: { [key: string]: any },

/**
* (Optional) GeoJSON data to pass to the layer.
* The type of layer to render.
*/
data?: { [key: string]: any },
layer_type: 'geojson' | 'raster' | 'georeference',

/**
* Name of the layer.
*/
name: string,

/**
* (Optional) Layer opacity.
*/
opacity?: number,

/**
* (Optional) URL that contains the layer. This can be a URL to GeoJSON data or a Raster tile set.
*/
Expand All @@ -44,7 +49,7 @@ const OverlayLayer = (props: OverlayLayerProps) => {
return (
<GeoJSONLayer
id={overlay.name}
data={overlay.data || overlay.url}
data={overlay.content || overlay.url}
fillStyle={MapStyles.fill}
pointStyle={MapStyles.point}
strokeStyle={MapStyles.stroke}
Expand All @@ -61,6 +66,17 @@ const OverlayLayer = (props: OverlayLayerProps) => {
);
}

if (overlay.layer_type === 'georeference') {
return (
<WarpedImageLayer
id={overlay.name}
manifest={overlay.content}
opacity={overlay.opacity}
url={overlay.url}
/>
);
}

return null;
};

Expand Down
2 changes: 1 addition & 1 deletion packages/core-data/src/components/PlaceLayersSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const PlaceLayersSelector = (props: Props) => {
if (isSelected(layer)) {
setSelectedLayers((prevSelected) => _.filter(prevSelected, (l) => l.url !== layer.url));
} else {
setSelectedLayers((prevSelected) => [...prevSelected, layer]);
setSelectedLayers((prevSelected) => [...prevSelected, { ...layer, content: JSON.parse(layer.content || '{}') }]);
}
}, [isSelected]);

Expand Down
3 changes: 2 additions & 1 deletion packages/geospatial/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@performant-software/geospatial",
"version": "2.2.7",
"version": "2.2.8",
"description": "A package of components for all things map-related.",
"license": "MIT",
"main": "./dist/index.cjs.js",
Expand All @@ -18,6 +18,7 @@
"build": "vite build && flow-copy-source -v src types"
},
"dependencies": {
"@allmaps/maplibre": "^1.0.0-beta.25",
"@mapbox/mapbox-gl-draw": "^1.4.3",
"@maptiler/geocoding-control": "^1.2.2",
"@turf/turf": "^6.5.0",
Expand Down
65 changes: 25 additions & 40 deletions packages/geospatial/src/components/GeoJsonLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,48 +15,33 @@ type Props = {
url?: string
};

const GeoJsonLayer = (props: Props) => {
const [data, setData] = useState(props.data);

/**
* If the data is passed as a URL, fetches the passed URL and sets the response on the state.
*/
useEffect(() => {
if (props.url) {
fetch(props.url)
.then((response) => response.json())
.then((json) => setData(json));
}
}, [props.url]);

return (
<Source
data={data}
type='geojson'
>
<Layer
filter={['!=', '$type', 'Point']}
paint={props.fillStyle}
type='fill'
/>
<Layer
filter={['!=', '$type', 'Point']}
paint={props.lineStyle}
type='line'
/>
<Layer
filter={['==', '$type', 'Point']}
paint={props.pointStyle}
type='circle'
/>
</Source>
);
};
const GeoJsonLayer = (props: Props) => (
<Source
data={props.data || props.url}
type='geojson'
>
<Layer
filter={['!=', '$type', 'Point']}
paint={props.fillStyle}
type='fill'
/>
<Layer
filter={['!=', '$type', 'Point']}
paint={props.lineStyle}
type='line'
/>
<Layer
filter={['==', '$type', 'Point']}
paint={props.pointStyle}
type='circle'
/>
</Source>
);

GeoJsonLayer.defaultProps = {
fillStyle: MapStyles.fill,
pointStyle: MapStyles.point,
strokeStyle: MapStyles.stroke
fillStyle: MapStyles.fill.paint,
pointStyle: MapStyles.point.paint,
strokeStyle: MapStyles.stroke.paint
};

export default GeoJsonLayer;
4 changes: 3 additions & 1 deletion packages/geospatial/src/components/LayerMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import React, {
Children,
useCallback,
useEffect,
useMemo, useRef,
useMemo,
useRef,
useState
} from 'react';
import { BsStack } from 'react-icons/bs';
Expand Down Expand Up @@ -103,6 +104,7 @@ const LayerMenu = (props: Props) => {
position={props.position}
>
<button
aria-label='Toggle Menu'
className='mapbox-gl-draw_ctrl-draw-btn layer-button'
onClick={() => setMenuOpen((prevMenuOpen) => !prevMenuOpen)}
type='button'
Expand Down
15 changes: 14 additions & 1 deletion packages/geospatial/src/components/MapDraw.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ type Props = {
*/
mapStyle: string,

/**
* The maximum pitch of the map (0-85).
*/
maxPitch?: number,

/**
* If `true`, the navigation controls will display.
*/
Expand All @@ -82,6 +87,11 @@ type Props = {
*/
onGeocodingSelection?: (data: any) => void,

/**
* If `true`, the map's canvas can be exported to a PNG using `map.getCanvas().toDataURL()`.
*/
preserveDrawingBuffer?: boolean,

/**
* Map style object.
*/
Expand Down Expand Up @@ -197,8 +207,10 @@ const MapDraw = (props: Props) => {
<Map
attributionControl={false}
cooperativeGestures={props.cooperativeGestures}
onLoad={() => setLoaded(true)}
mapLib={maplibregl}
maxPitch={props.maxPitch}
onLoad={() => setLoaded(true)}
preserveDrawingBuffer={props.preserveDrawingBuffer}
ref={mapRef}
style={style}
mapStyle={mapStyleUrl}
Expand Down Expand Up @@ -241,6 +253,7 @@ const MapDraw = (props: Props) => {
MapDraw.defaultProps = {
buffer: DEFAULT_BUFFER,
cooperativeGestures: true,
preserveDrawingBuffer: false,
zoomDuration: DEFAULT_ZOOM_DELAY
};

Expand Down
72 changes: 72 additions & 0 deletions packages/geospatial/src/components/WarpedImageLayer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// @flow

import { useEffect, useState } from 'react';
import { useMap } from 'react-map-gl';
import MapUtils from '../utils/Map';

type Props = {
/**
* ID of the new layer.
*/
id: string,

/**
* (Optional) IIIF manifest content containing the image and geo-reference annotations.
*/
manifest?: string,

/**
* (Optional) layer opacity.
*/
opacity?: number,

/**
* (Optional) URL to the IIIF manifest.
*/
url?: string
};

const WarpedImageLayer = (props: Props) => {
const [loaded, setLoaded] = useState(false);

const mapRef = useMap();

/**
* Sets the "loaded" attribute on the state based on the current map state.
*/
useEffect(() => {
const instance = mapRef?.current?.getMap();
if (instance && instance.loaded()) {
setLoaded(true);
} else if (instance) {
instance.on('load', () => setLoaded(true));
}
}, []);

/**
* Adds the WarpedMapLayer object to the map as a new layer.
*/
useEffect(() => {
if (!loaded) {
return undefined;
}

const map = mapRef.current.getMap();

MapUtils.addGeoreferenceLayer(map, props.id, {
manifest: props.manifest,
opacity: props.opacity,
url: props.url
});

return () => MapUtils.removeLayer(map, props.id);
}, [loaded]);

return null;
};

WarpedImageLayer.defaultProps = {
opacity: 0.5
};

export default WarpedImageLayer;
56 changes: 56 additions & 0 deletions packages/geospatial/src/components/WarpedImageLayerPeripleo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// @flow

import { useEffect } from 'react';
import { useLoadedMap } from '@peripleo/maplibre';
import MapUtils from '../utils/Map';

type Props = {
/**
* ID of the new layer.
*/
id: string,

/**
* (Optional) IIIF manifest content containing the image and geo-reference annotations.
*/
manifest?: string,

/**
* (Optional) layer opacity.
*/
opacity?: number,

/**
* (Optional) URL to the IIIF manifest.
*/
url?: string
};

const WarpedImageLayer = (props: Props) => {
const map = useLoadedMap();

/**
* Adds the WarpedMapLayer object to the map as a new layer.
*/
useEffect(() => {
if (!map) {
return undefined;
}

MapUtils.addGeoreferenceLayer(map, props.id, {
manifest: props.manifest,
opacity: props.opacity,
url: props.url
});

return () => MapUtils.removeLayer(map, props.id);
}, [map]);

return null;
};

WarpedImageLayer.defaultProps = {
opacity: 0.5
};

export default WarpedImageLayer;
Loading

0 comments on commit cf205d5

Please sign in to comment.