2022-02-08 12:49:51 +01:00
|
|
|
|
import { useState, useCallback, useEffect } from 'react';
|
2022-04-28 23:19:03 +02:00
|
|
|
|
import { httpRequest, fire } from '@utils';
|
2022-02-08 12:49:51 +01:00
|
|
|
|
import type { Feature, FeatureCollection, Geometry } from 'geojson';
|
|
|
|
|
|
|
|
|
|
export const SOURCE_SELECTION_UTILISATEUR = 'selection_utilisateur';
|
|
|
|
|
export const SOURCE_CADASTRE = 'cadastre';
|
|
|
|
|
|
|
|
|
|
export type CreateFeatures = (params: {
|
|
|
|
|
features: Feature<Geometry>[];
|
|
|
|
|
source?: string;
|
|
|
|
|
external?: true;
|
|
|
|
|
}) => void;
|
|
|
|
|
export type UpdateFatures = (params: {
|
|
|
|
|
features: Feature<Geometry>[];
|
|
|
|
|
source?: string;
|
|
|
|
|
external?: true;
|
|
|
|
|
}) => void;
|
|
|
|
|
export type DeleteFeatures = (params: {
|
|
|
|
|
features: Feature<Geometry>[];
|
|
|
|
|
source?: string;
|
|
|
|
|
external?: true;
|
|
|
|
|
}) => void;
|
|
|
|
|
|
|
|
|
|
export function useFeatureCollection(
|
|
|
|
|
initialFeatureCollection: FeatureCollection,
|
2022-04-07 11:41:35 +02:00
|
|
|
|
{ url }: { url: string }
|
2022-02-08 12:49:51 +01:00
|
|
|
|
) {
|
|
|
|
|
const [error, onError] = useError();
|
|
|
|
|
const [featureCollection, setFeatureCollection] = useState(
|
|
|
|
|
initialFeatureCollection
|
|
|
|
|
);
|
2023-06-06 15:54:03 +02:00
|
|
|
|
const refreshFeatureList = useCallback<() => void>(() => {
|
|
|
|
|
httpRequest(url)
|
|
|
|
|
.turbo()
|
|
|
|
|
.catch(() => null);
|
|
|
|
|
}, [url]);
|
|
|
|
|
|
2022-02-08 12:49:51 +01:00
|
|
|
|
const updateFeatureCollection = useCallback<
|
|
|
|
|
(callback: (features: Feature[]) => Feature[]) => void
|
|
|
|
|
>(
|
|
|
|
|
(callback) => {
|
|
|
|
|
setFeatureCollection(({ features }) => ({
|
|
|
|
|
type: 'FeatureCollection',
|
|
|
|
|
features: callback(features)
|
|
|
|
|
}));
|
2023-06-06 15:54:03 +02:00
|
|
|
|
refreshFeatureList();
|
2022-02-08 12:49:51 +01:00
|
|
|
|
},
|
2023-06-06 15:54:03 +02:00
|
|
|
|
[refreshFeatureList, setFeatureCollection]
|
2022-02-08 12:49:51 +01:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const addFeatures = useCallback(
|
|
|
|
|
(features: (Feature & { lid?: string })[], external: boolean) => {
|
|
|
|
|
for (const feature of features) {
|
|
|
|
|
if (feature.lid) {
|
|
|
|
|
fire(document, 'map:internal:draw:setId', {
|
|
|
|
|
lid: feature.lid,
|
|
|
|
|
id: feature.properties?.id
|
|
|
|
|
});
|
|
|
|
|
delete feature.lid;
|
|
|
|
|
}
|
|
|
|
|
if (external) {
|
|
|
|
|
if (feature.properties?.source == SOURCE_SELECTION_UTILISATEUR) {
|
|
|
|
|
fire(document, 'map:internal:draw:add', {
|
|
|
|
|
feature: {
|
|
|
|
|
id: feature.properties.id,
|
|
|
|
|
...feature
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
fire(document, 'map:internal:cadastre:highlight', {
|
|
|
|
|
cid: feature.properties?.cid,
|
|
|
|
|
highlight: true
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const removeFeatures = useCallback(
|
|
|
|
|
(features: Feature[], external: boolean) => {
|
|
|
|
|
if (external) {
|
|
|
|
|
for (const feature of features) {
|
|
|
|
|
if (feature.properties?.source == SOURCE_SELECTION_UTILISATEUR) {
|
|
|
|
|
fire(document, 'map:internal:draw:delete', { id: feature.id });
|
|
|
|
|
} else {
|
|
|
|
|
fire(document, 'map:internal:cadastre:highlight', {
|
|
|
|
|
cid: feature.properties?.cid,
|
|
|
|
|
highlight: false
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const createFeatures = useCallback<CreateFeatures>(
|
|
|
|
|
async ({
|
|
|
|
|
features,
|
|
|
|
|
source = SOURCE_SELECTION_UTILISATEUR,
|
|
|
|
|
external = false
|
|
|
|
|
}) => {
|
|
|
|
|
try {
|
|
|
|
|
const newFeatures: Feature[] = [];
|
|
|
|
|
for (const feature of features) {
|
2022-04-28 23:19:03 +02:00
|
|
|
|
const data = await httpRequest(url, {
|
|
|
|
|
method: 'post',
|
|
|
|
|
json: { feature, source }
|
|
|
|
|
}).json<{ feature: Feature & { lid?: string | number } }>();
|
2022-02-08 12:49:51 +01:00
|
|
|
|
if (data) {
|
|
|
|
|
if (source == SOURCE_SELECTION_UTILISATEUR) {
|
|
|
|
|
data.feature.lid = feature.id;
|
|
|
|
|
}
|
|
|
|
|
newFeatures.push(data.feature);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
addFeatures(newFeatures, external);
|
|
|
|
|
updateFeatureCollection((features) => [...features, ...newFeatures]);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
onError('Le polygone dessiné n’est pas valide.');
|
|
|
|
|
}
|
|
|
|
|
},
|
2022-04-07 11:41:35 +02:00
|
|
|
|
[url, updateFeatureCollection, addFeatures, onError]
|
2022-02-08 12:49:51 +01:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const updateFeatures = useCallback<UpdateFatures>(
|
|
|
|
|
async ({
|
|
|
|
|
features,
|
|
|
|
|
source = SOURCE_SELECTION_UTILISATEUR,
|
|
|
|
|
external = false
|
|
|
|
|
}) => {
|
|
|
|
|
try {
|
|
|
|
|
const newFeatures: Feature[] = [];
|
|
|
|
|
for (const feature of features) {
|
|
|
|
|
const id = feature.properties?.id;
|
|
|
|
|
if (id) {
|
2024-04-22 10:47:47 +02:00
|
|
|
|
await httpRequest(endpointWithId(url, id), {
|
2022-04-28 23:19:03 +02:00
|
|
|
|
method: 'patch',
|
|
|
|
|
json: { feature }
|
|
|
|
|
}).json();
|
2022-02-08 12:49:51 +01:00
|
|
|
|
} else {
|
2022-04-28 23:19:03 +02:00
|
|
|
|
const data = await httpRequest(url, {
|
|
|
|
|
method: 'post',
|
|
|
|
|
json: { feature, source }
|
|
|
|
|
}).json<{ feature: Feature & { lid?: string | number } }>();
|
2022-02-08 12:49:51 +01:00
|
|
|
|
if (data) {
|
|
|
|
|
if (source == SOURCE_SELECTION_UTILISATEUR) {
|
|
|
|
|
data.feature.lid = feature.id;
|
|
|
|
|
}
|
|
|
|
|
newFeatures.push(data.feature);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (newFeatures.length > 0) {
|
|
|
|
|
addFeatures(newFeatures, external);
|
|
|
|
|
updateFeatureCollection((features) => [...features, ...newFeatures]);
|
2023-06-06 15:54:03 +02:00
|
|
|
|
} else {
|
|
|
|
|
refreshFeatureList();
|
2022-02-08 12:49:51 +01:00
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
onError('Le polygone dessiné n’est pas valide.');
|
|
|
|
|
}
|
|
|
|
|
},
|
2023-06-06 15:54:03 +02:00
|
|
|
|
[url, refreshFeatureList, updateFeatureCollection, addFeatures, onError]
|
2022-02-08 12:49:51 +01:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const deleteFeatures = useCallback<DeleteFeatures>(
|
|
|
|
|
async ({ features, external = false }) => {
|
|
|
|
|
try {
|
|
|
|
|
const deletedFeatures = [];
|
|
|
|
|
for (const feature of features) {
|
|
|
|
|
const id = feature.properties?.id;
|
2024-04-22 10:47:47 +02:00
|
|
|
|
await httpRequest(endpointWithId(url, id), {
|
|
|
|
|
method: 'delete'
|
|
|
|
|
}).json();
|
2022-02-08 12:49:51 +01:00
|
|
|
|
deletedFeatures.push(feature);
|
|
|
|
|
}
|
|
|
|
|
removeFeatures(deletedFeatures, external);
|
|
|
|
|
const deletedFeatureIds = deletedFeatures.map(
|
|
|
|
|
({ properties }) => properties?.id
|
|
|
|
|
);
|
|
|
|
|
updateFeatureCollection((features) =>
|
|
|
|
|
features.filter(
|
|
|
|
|
({ properties }) => !deletedFeatureIds.includes(properties?.id)
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
onError('Le polygone n’a pas pu être supprimé.');
|
|
|
|
|
}
|
|
|
|
|
},
|
2022-04-07 11:41:35 +02:00
|
|
|
|
[url, updateFeatureCollection, removeFeatures, onError]
|
2022-02-08 12:49:51 +01:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
featureCollection,
|
|
|
|
|
error,
|
|
|
|
|
createFeatures,
|
|
|
|
|
updateFeatures,
|
|
|
|
|
deleteFeatures
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function useError(): [string | undefined, (message: string) => void] {
|
|
|
|
|
const [error, onError] = useState<string | undefined>();
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const timer = setTimeout(() => onError(undefined), 5000);
|
|
|
|
|
return () => clearTimeout(timer);
|
|
|
|
|
}, [error]);
|
|
|
|
|
|
|
|
|
|
return [error, onError];
|
|
|
|
|
}
|
2024-04-22 10:47:47 +02:00
|
|
|
|
|
|
|
|
|
// We need this because endoint can have query params. For example with /champs/123?row_id=abc we can't juste concatanate id.
|
|
|
|
|
// We want /champs/123/456?row_id=abc not /champs/123?row_id=abc/456
|
|
|
|
|
function endpointWithId(endpoint: string, id: string) {
|
|
|
|
|
const url = new URL(endpoint, document.baseURI);
|
|
|
|
|
url.pathname = `${url.pathname}/${id}`;
|
|
|
|
|
return url.toString();
|
|
|
|
|
}
|