Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ mcp add tool gpf_wmts_layers
* [@ignfab/gpf-schema-store](https://www.npmjs.com/package/@ignfab/gpf-schema-store) pour le **catalogue de schémas embarqué** utilisé par les outils d'exploration WFS.
* [MiniSearch](https://github.com/lucaong/minisearch) pour la **recherche par mot clé** utilisée dans `@ignfab/gpf-schema-store`.
* [jsts](https://bjornharrtell.github.io/jsts/) pour les **traitements géométriques** (ex : tri des réponses par distance au point recherché).
* [turfjs/distance](https://turfjs.org/docs/api/distance) pour les **calculs de distance** avec la [formule de Haversine](https://en.wikipedia.org/wiki/Haversine_formula).

## Licence

Expand Down
49 changes: 45 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
"dependencies": {
"@ignfab/gpf-schema-store": "^0.1.3",
"@rgrove/parse-xml": "^4.2.0",
"@turf/distance": "^7.3.4",
"@turf/helpers": "^7.3.4",
"https-proxy-agent": "^7.0.6",
"jsts": "^2.12.1",
"lodash": "^4.17.21",
Expand Down
4 changes: 2 additions & 2 deletions src/gpf/urbanisme.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export async function getUrbanisme(lon, lat, fetcher) {
return features.map((feature) => {
const item = {
...mapWfsFeature(feature, URBANISME_TYPES),
distance: distance(sourceGeom, feature.geometry) * 1000.0,
distance: distance(sourceGeom, feature.geometry),
};
return sanitizeUrbanismeItem(item);
});
Expand Down Expand Up @@ -89,6 +89,6 @@ export async function getAssiettesServitudes(lon, lat, fetcher) {
const features = await fetchWfsFeatures(ASSIETTES_SUP_TYPES, cql_filter, 'Urbanisme', fetcher);
return features.map((feature) => ({
...mapWfsFeature(feature, ASSIETTES_SUP_TYPES),
distance: distance(sourceGeom, feature.geometry) * 1000.0,
distance: distance(sourceGeom, feature.geometry),
}));
}
32 changes: 26 additions & 6 deletions src/helpers/distance.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
import GeoJSONReader from 'jsts/org/locationtech/jts/io/GeoJSONReader.js'
import { DistanceOp } from 'jsts/org/locationtech/jts/operation/distance.js'

import turfDistance from '@turf/distance'
import {point as turfPoint} from '@turf/helpers'

/**
* Compute approximative distance in km between gA and gB.
* Compute approximative distance in meters between gA and gB.
*
* TODO: replace the lon/lat planar nearest-point step with a geodesic geometry distance.
*
* @param {object} gA GeoJSON Point
* @param {object} gA GeoJSON Geometry
* @param {object} gB GeoJSON Geometry
*/
export default function distance(gA, gB) {
const geojsonReader = new GeoJSONReader()
const a = geojsonReader.read(gA);
const b = geojsonReader.read(gB);

// converts to kilometers assuming earth is a sphere
const distanceInDegree = DistanceOp.distance(a, b);
return 6480.0 * ( distanceInDegree * 2.0 * Math.PI / 360.0 ) ;
}
/*
* Get the 2 nearest points between a and b
*
* Note that it will project according to longitude and latitude axis,
* so it is not really accurate, but it is a good approximation
*/
const nearestPoints = DistanceOp.nearestPoints(a, b);
if ( nearestPoints.length !== 2 ) {
throw new Error('DistanceOp.nearestPoints should return 2 points');
}

/*
* haversine distance between the 2 nearest points (see https://turfjs.org/docs/api/distance)
*/
return turfDistance(
turfPoint([nearestPoints[0].x, nearestPoints[0].y]),
turfPoint([nearestPoints[1].x, nearestPoints[1].y]),
{ units: 'meters' }
);
}
2 changes: 1 addition & 1 deletion src/tools/AssietteSupTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const assietteSupResultSchema = z
id: z.string().describe("L'identifiant de l'assiette."),
bbox: z.array(z.number()).describe("La boîte englobante de l'assiette.").optional(),
feature_ref: featureRefSchema.describe("Référence WFS réutilisable, notamment avec `gpf_wfs_get_features` et `spatial_operator = \"intersects_feature\"`.").optional(),
distance: z.number().describe("La distance entre le point demandé et l'assiette retenue."),
distance: z.number().describe("La distance en mètres entre le point demandé et l'assiette retenue."),
})
.catchall(z.unknown());

Expand Down
10 changes: 8 additions & 2 deletions src/tools/CadastreTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const cadastreResultSchema = z
id: z.string().describe("L'identifiant de l'objet cadastral."),
bbox: z.array(z.number()).describe("La boîte englobante de l'objet cadastral.").optional(),
feature_ref: featureRefSchema.describe("Référence WFS réutilisable, notamment avec `gpf_wfs_get_features` et `spatial_operator = \"intersects_feature\"`."),
distance: z.number().describe("La distance entre le point demandé et l'objet cadastral retenu."),
distance: z.number().describe("La distance en mètres entre le point demandé et l'objet cadastral retenu."),
source: z.string().describe("La source des données cadastrales."),
})
.catchall(z.unknown());
Expand All @@ -27,11 +27,17 @@ const cadastreOutputSchema = z.object({
results: z.array(cadastreResultSchema).describe("La liste des objets cadastraux les plus proches du point demandé."),
});

const CADASTRE_TOOL_DESCRIPTION = [
`Renvoie, pour un point donné par sa longitude et sa latitude, la liste des objets cadastraux (${PARCELLAIRE_EXPRESS_TYPES.join(', ')}) les plus proches, avec leurs informations associées.`,
`Les résultats sont retournés au plus une fois par type lorsqu'ils sont disponibles et incluent un \`feature_ref\` WFS réutilisable.`,
'La distance de recherche est fixée à 10 mètres.'
]

class CadastreTool extends MCPTool<CadastreInput> {
name = "cadastre";
title = "Informations cadastrales";
annotations = READ_ONLY_OPEN_WORLD_TOOL_ANNOTATIONS;
description = `Renvoie, pour un point donné par sa longitude et sa latitude, la liste des objets cadastraux (${PARCELLAIRE_EXPRESS_TYPES.join(', ')}) les plus proches, avec leurs informations associées. Les résultats sont retournés au plus une fois par type lorsqu'ils sont disponibles et incluent un \`feature_ref\` WFS réutilisable. (source : ${PARCELLAIRE_EXPRESS_SOURCE}).`;
description = CADASTRE_TOOL_DESCRIPTION.join("\n");
protected outputSchemaShape = cadastreOutputSchema;

schema = cadastreInputSchema;
Expand Down
2 changes: 1 addition & 1 deletion src/tools/UrbanismeTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const urbanismeResultSchema = z
id: z.string().describe("L'identifiant de l'objet d'urbanisme."),
bbox: z.array(z.number()).describe("La boîte englobante de l'objet d'urbanisme.").optional(),
feature_ref: featureRefSchema.describe("Référence WFS réutilisable, notamment avec `gpf_wfs_get_features` et `spatial_operator = \"intersects_feature\"`.").optional(),
distance: z.number().describe("La distance entre le point demandé et l'objet d'urbanisme retenu."),
distance: z.number().describe("La distance en mètres entre le point demandé et l'objet d'urbanisme retenu."),
})
.catchall(z.unknown());

Expand Down
27 changes: 23 additions & 4 deletions test/helpers/distance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,34 @@ import {paris, marseille, besancon, parisMarseille} from '../samples';

describe("Test distance",() => {
describe("Test distance(Point,Point)", () => {
it("should return ~718.8km from Paris to Marseille",() => {
it("should return 662489.3m from Paris to Marseille",() => {
const result = distance(paris,marseille);
expect(result).toBeCloseTo(718.8,1);
expect(result).toBeCloseTo(662489.3,1);
});
});
describe("Test distance(Point,LineString)", () => {
it("should return ~276.7km from Besançon to [Paris,Marseille]",() => {
it("should return 209731.2m from Besançon to [Paris,Marseille]",() => {
const result = distance(besancon,parisMarseille);
expect(result).toBeCloseTo(276.7,1);
expect(result).toBeCloseTo(209731.2,1);
});
});

describe("Test distance(Point,Polygon)", () => {
it("should return 0m from Paris point to a polygon containing Paris",() => {
const polygonContainingParis = {
"type": "Polygon",
"coordinates": [
[
[2.0, 48.0],
[3.0, 48.0],
[3.0, 49.0],
[2.0, 49.0],
[2.0, 48.0]
]
]
};
const result = distance(paris, polygonContainingParis);
expect(result).toEqual(0);
});
});
});
Loading