diff --git a/src/CoordinateInfo/CoordinateInfo.example.md b/src/CoordinateInfo/CoordinateInfo.example.md
index efa1ed25bb..cb2e3e60ff 100644
--- a/src/CoordinateInfo/CoordinateInfo.example.md
+++ b/src/CoordinateInfo/CoordinateInfo.example.md
@@ -9,13 +9,15 @@ import {
Tooltip
} from 'antd';
import * as copy from 'copy-to-clipboard';
+import MapComponent from '@terrestris/react-util/dist/Components/MapComponent/MapComponent';
+import MapContext from '@terrestris/react-util/dist/Context/MapContext/MapContext';
import OlLayerTile from 'ol/layer/Tile';
import OlMap from 'ol/Map';
import { fromLonLat } from 'ol/proj';
import OlSourceOSM from 'ol/source/OSM';
import OlSourceTileWMS from 'ol/source/TileWMS';
import OlView from 'ol/View';
-import * as React from 'react';
+import React, { useEffect,useState } from 'react';
const queryLayer = new OlLayerTile({
name: 'States (USA)',
@@ -30,14 +32,12 @@ const queryLayer = new OlLayerTile({
})
});
-class CoordinateInfoExample extends React.Component {
+const CoordinateInfoExample = () => {
- constructor(props) {
- super(props);
+ const [map, setMap] = useState();
- this.mapDivId = `map-${Math.random()}`;
-
- this.map = new OlMap({
+ useEffect(() => {
+ setMap(new OlMap({
layers: [
new OlLayerTile({
name: 'OSM',
@@ -49,22 +49,21 @@ class CoordinateInfoExample extends React.Component {
center: fromLonLat([-99.4031637, 38.3025695]),
zoom: 4
})
- });
- }
+ }));
+ }, []);
- componentDidMount() {
- this.map.setTarget(this.mapDivId);
+ if (!map) {
+ return null;
}
- render() {
- return (
- <>
-
+ return (
+
+
- >
+
);
- }
}
diff --git a/src/CoordinateInfo/CoordinateInfo.spec.tsx b/src/CoordinateInfo/CoordinateInfo.spec.tsx
index e04e5f8edf..5a27c61ac0 100644
--- a/src/CoordinateInfo/CoordinateInfo.spec.tsx
+++ b/src/CoordinateInfo/CoordinateInfo.spec.tsx
@@ -17,7 +17,7 @@ describe('', () => {
});
it('can be rendered', () => {
- const { container } = render();
+ const { container } = render();
expect(container).toBeVisible();
});
});
diff --git a/src/CoordinateInfo/CoordinateInfo.tsx b/src/CoordinateInfo/CoordinateInfo.tsx
index 068b697c28..645e57de2f 100644
--- a/src/CoordinateInfo/CoordinateInfo.tsx
+++ b/src/CoordinateInfo/CoordinateInfo.tsx
@@ -1,269 +1,51 @@
-import Logger from '@terrestris/base-util/dist/Logger';
-import { isWmsLayer, WmsLayer } from '@terrestris/react-util/dist/Util/typeUtils';
-import _cloneDeep from 'lodash/cloneDeep';
-import _isString from 'lodash/isString';
-import { getUid } from 'ol';
-import { Coordinate as OlCoordinate } from 'ol/coordinate';
-import OlFeature from 'ol/Feature';
-import OlFormatGML2 from 'ol/format/GML2';
-import OlGeometry from 'ol/geom/Geometry';
-import OlBaseLayer from 'ol/layer/Base';
-import OlMap from 'ol/Map';
-import OlMapBrowserEvent from 'ol/MapBrowserEvent';
-import * as React from 'react';
-
-const format = new OlFormatGML2();
-
-export interface CoordinateInfoProps {
- /**
- * List of (WMS) layers that should be queried.
- */
- queryLayers: Array;
-
- /**
- * The number of max. features that should be returned by the GFI request.
- */
- featureCount: number;
-
- /**
- * Whether the GFI control should requests all layers at a given coordinate
- * or just the uppermost one.
- */
- drillDown: boolean;
+import useCoordinateInfo, {
+ CoordinateInfoResult,
+ UseCoordinateInfoArgs
+} from '@terrestris/react-util/dist/Hooks/useCoordinateInfo/useCoordinateInfo';
+import _isNil from 'lodash/isNil';
+import React, { FC } from 'react';
+export type CoordinateInfoProps = {
/**
* The children component that should be rendered. The render prop function
* receives the state of the component (this is the clicked coordinate, the
* list of GFI features if any and the loading state).
*/
- resultRenderer: (childrenProps: CoordinateInfoState) => React.ReactNode;
- /**
- * The ol map.
- */
- map: OlMap;
- /**
- * Optional request options to apply (separated by each query layer, identified
- * by its internal ol id or a callback function).
- */
- fetchOpts:
- | {
- [uid: string]: RequestInit;
- }
- | ((layer: WmsLayer) => RequestInit);
- /**
- * Callback function that gets called if all features are fetched successfully
- * via GetFeatureInfo.
- */
- onSuccess: (features: CoordinateInfoState) => void;
- /**
- * Callback function that gets called if an error occured while fetching the
- * features via GetFeatureInfo.
- */
- onError: (error: any) => void;
-}
-
-export interface CoordinateInfoState {
- clickCoordinate: OlCoordinate | null;
- features: {
- [layerName: string]: OlFeature[];
- };
- loading: boolean;
-}
+ resultRenderer?: (childrenProps: CoordinateInfoResult) => React.ReactNode;
+} & UseCoordinateInfoArgs;
/**
* Constructs a wrapper component for querying features from the clicked
* coordinate. The returned features can be passed to a child component
* to be visualized.
*
- * @class The CoordinateInfo
- * @extends React.Component
*/
-export class CoordinateInfo extends React.Component {
- /**
- * The defaultProps.
- */
- static defaultProps = {
- queryLayers: [],
- featureCount: 1,
- drillDown: true,
- resultRenderer: () => {
- return (
-
- );
- },
- fetchOpts: {},
- onSuccess: () => { },
- onError: () => { }
- };
-
- /**
- * Creates the CoordinateInfo component.
- * @constructs CoordinateInfo
- */
- constructor(props: CoordinateInfoProps) {
- super(props);
-
- this.state = {
- clickCoordinate: null,
- features: {},
- loading: false
- };
-
- this.onMapClick = this.onMapClick.bind(this);
- this.layerFilter = this.layerFilter.bind(this);
- }
-
- componentDidMount() {
- const {
- map
- } = this.props;
-
- map.on('click', this.onMapClick);
+export const CoordinateInfo: FC = ({
+ drillDown = true,
+ featureCount = 1,
+ fetchOpts = {},
+ onError = () => undefined,
+ queryLayers = [],
+ resultRenderer = () => <>>
+}) => {
+
+ const result = useCoordinateInfo({
+ drillDown,
+ featureCount,
+ fetchOpts,
+ onError,
+ queryLayers,
+ });
+
+ if (_isNil(result)) {
+ return null;
}
- componentWillUnmount() {
- const {
- map
- } = this.props;
-
- map.un('click', this.onMapClick);
- }
-
- onMapClick(olEvt: OlMapBrowserEvent) {
- const {
- map,
- featureCount,
- drillDown,
- fetchOpts,
- onSuccess,
- onError
- } = this.props;
-
- const mapView = map.getView();
- const viewResolution = mapView.getResolution();
- const viewProjection = mapView.getProjection();
- const pixel = map.getEventPixel(olEvt.originalEvent);
- const coordinate = olEvt.coordinate;
-
- const promises: Promise[] = [];
-
- const mapLayers =
- map.getAllLayers()
- .filter(this.layerFilter)
- .filter(l => l.getData && l.getData(pixel) && isWmsLayer(l));
- mapLayers.forEach(l => {
- const layerSource = (l as WmsLayer).getSource();
- if (!layerSource) {
- return;
- }
- const featureInfoUrl = layerSource.getFeatureInfoUrl(
- coordinate,
- viewResolution!,
- viewProjection,
- {
- INFO_FORMAT: 'application/vnd.ogc.gml',
- FEATURE_COUNT: featureCount
- }
- );
- if (featureInfoUrl) {
- let opts;
- if (fetchOpts instanceof Function) {
- opts = fetchOpts(l as WmsLayer);
- } else {
- opts = fetchOpts[getUid(l)];
- }
- promises.push(fetch(featureInfoUrl, opts));
- }
-
- return !drillDown;
- });
-
- map.getTargetElement().style.cursor = 'wait';
-
- this.setState({
- loading: true
- });
-
- Promise.all(promises)
- .then((responses: Response[]) => {
- this.setState({
- clickCoordinate: coordinate
- });
- const textResponses = responses.map(response => response.text());
- return Promise.all(textResponses);
- })
- .then((textResponses: string[]) => {
- const features: {[index: string]: OlFeature[]} = {};
-
- textResponses.forEach((featureCollection: string, idx: number) => {
- const fc = format.readFeatures(featureCollection);
- fc.forEach((feature: OlFeature) => {
- const id = feature.getId();
- const featureTypeName = _isString(id) ? id.split('.')[0] : id?.toString() ?? `UNKNOWN-${idx}`;
-
- if (!features[featureTypeName]) {
- features[featureTypeName] = [];
- }
-
- features[featureTypeName].push(feature);
- });
- });
-
- this.setState({
- features: features
- }, () => {
- onSuccess(this.getCoordinateInfoState());
- });
- })
- .catch((error: any) => {
- Logger.error(error);
-
- onError(error);
- })
- .finally(() => {
- map.getTargetElement().style.cursor = '';
-
- this.setState({
- loading: false
- });
- });
- }
-
- layerFilter(layerCandidate: OlBaseLayer) {
- const {
- queryLayers
- } = this.props;
-
- return (queryLayers as OlBaseLayer[]).includes(layerCandidate);
- }
-
- getCoordinateInfoState(): CoordinateInfoState {
- // We're cloning the click coordinate and features to
- // not pass the internal state reference to the parent component.
- // Also note that we explicitly don't use feature.clone() to
- // keep all feature properties (in particular the id) intact.
- const coordinateInfoState: CoordinateInfoState = {
- clickCoordinate: this.state.clickCoordinate ?
- _cloneDeep(this.state.clickCoordinate) :
- null,
- loading: this.state.loading,
- features: _cloneDeep(this.state.features)
- };
-
- return coordinateInfoState;
- };
-
- render() {
- const {
- resultRenderer
- } = this.props;
-
- return (
- <>
- {resultRenderer(this.getCoordinateInfoState())}
- >
- );
- }
-}
+ return (
+ <>
+ {resultRenderer(result)}
+ >
+ );
+};
export default CoordinateInfo;