Skip to content

Commit d31c449

Browse files
authored
Mcpods 6782 get spacial search selection (#183)
* implementation * test not working yet * selection tests * clip tests, disable clipping for now due to technical limits * PR comments
1 parent 60afce2 commit d31c449

File tree

10 files changed

+294
-19
lines changed

10 files changed

+294
-19
lines changed

here-naksha-app-service/src/main/java/com/here/naksha/app/service/http/tasks/AbstractApiTask.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -319,14 +319,17 @@ XyzFeatureCollection emptyFeatureCollection() {
319319
}
320320
}
321321

322-
protected <F extends XyzFeature> @NotNull F standardReadFeaturesPreResponseProcessing(
323-
final @NotNull F f, final @Nullable Set<String> propPaths, final boolean clip, final Geometry clipGeo) {
324-
F newF = f;
325-
// Apply prop selection if enabled
326-
if (propPaths != null) newF = applyPropertySelection(newF, propPaths);
327-
// Apply geometry clipping if enabled
328-
if (clip) applyGeometryClipping(newF, clipGeo);
329-
return newF;
322+
protected <F extends XyzFeature> @Nullable F1<F, F> standardReadFeaturesPreResponseProcessing(
323+
final @Nullable Set<String> propPaths, final boolean clip, final Geometry clipGeo) {
324+
if (propPaths == null && !clip) return null;
325+
return f -> {
326+
F newF = f;
327+
// Apply prop selection if enabled
328+
if (propPaths != null) newF = applyPropertySelection(newF, propPaths);
329+
// Apply geometry clipping if enabled
330+
if (clip) applyGeometryClipping(newF, clipGeo);
331+
return newF;
332+
};
330333
}
331334

332335
@SuppressWarnings("unchecked")

here-naksha-app-service/src/main/java/com/here/naksha/app/service/http/tasks/ReadFeatureApiTask.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,8 @@ protected void init() {}
182182
// transform Result to Http FeatureCollection response, restricted by given feature limit
183183
// we will also apply response preprocessing (like property selection and geometry clipping)
184184
// if any of the options is enabled
185-
final F1<XyzFeature, XyzFeature> preResponseProcessing = (propPaths == null && !clip)
186-
? null
187-
: f -> standardReadFeaturesPreResponseProcessing(f, propPaths, clip, bbox);
185+
final F1<XyzFeature, XyzFeature> preResponseProcessing =
186+
standardReadFeaturesPreResponseProcessing(propPaths, clip, bbox);
188187
return transformReadResultToXyzCollectionResponse(
189188
result, XyzFeature.class, 0, limit, null, preResponseProcessing);
190189
}
@@ -295,6 +294,8 @@ protected void init() {}
295294
final String refFeatureId = ApiParams.extractParamAsString(queryParams, REF_FEATURE_ID);
296295
final long radius = ApiParams.extractQueryParamAsLong(queryParams, RADIUS, false, 0);
297296
long limit = ApiParams.extractQueryParamAsLong(queryParams, LIMIT, false, DEF_FEATURE_LIMIT);
297+
final Set<String> propPaths = PropertySelectionUtil.buildPropPathSetFromQueryParams(queryParams);
298+
final boolean clip = ApiParams.extractQueryParamAsBoolean(queryParams, CLIP_GEO, false);
298299
// validate values
299300
limit = (limit < 0 || limit > DEF_FEATURE_LIMIT) ? DEF_FEATURE_LIMIT : limit;
300301
ApiParams.validateLatLon(lat, lon);
@@ -313,8 +314,12 @@ protected void init() {}
313314

314315
// Forward request to NH Space Storage reader instance
315316
final Result result = executeReadRequestFromSpaceStorage(rdRequest);
317+
// TODO pass the correct transformed geometry into this method call, also use the boolean clip
318+
final F1<XyzFeature, XyzFeature> preResponseProcessing =
319+
standardReadFeaturesPreResponseProcessing(propPaths, false, radiusOp.getGeometry());
316320
// transform Result to Http FeatureCollection response, restricted by given feature limit
317-
return transformReadResultToXyzCollectionResponse(result, XyzFeature.class, limit);
321+
return transformReadResultToXyzCollectionResponse(
322+
result, XyzFeature.class, 0, limit, null, preResponseProcessing);
318323
}
319324

320325
private @NotNull XyzGeometry obtainReferenceGeometry(

here-naksha-app-service/src/main/resources/swagger/openapi.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,8 @@ paths:
844844
- $ref: '#/components/parameters/TagList'
845845
- $ref: '#/components/parameters/Limit'
846846
- $ref: '#/components/parameters/PropertiesQuery'
847+
- $ref: '#/components/parameters/PropertiesSelection'
848+
# - $ref: '#/components/parameters/Clip'
847849
responses:
848850
'200':
849851
$ref: '#/components/responses/FeatureCollectionResponse'
@@ -1177,7 +1179,7 @@ components:
11771179
name: radius
11781180
in: query
11791181
description: >-
1180-
Radius in meters which defines the diameter of the search request.
1182+
Radius in meters of the search request.
11811183
schema:
11821184
type: integer
11831185
RefFeatureId:

here-naksha-app-service/src/test/java/com/here/naksha/app/service/ReadFeaturesByRadiusTest.java

Lines changed: 131 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import static com.here.naksha.app.common.CommonApiTestSetup.createSpace;
3838
import static com.here.naksha.app.common.CommonApiTestSetup.setupSpaceAndRelatedResources;
3939
import static com.here.naksha.app.common.TestUtil.loadFileOrFail;
40+
import static com.here.naksha.app.common.TestUtil.urlEncoded;
4041
import static com.here.naksha.app.common.assertions.ResponseAssertions.assertThat;
4142

4243
class ReadFeaturesByRadiusTest extends ApiTest {
@@ -79,6 +80,25 @@ class ReadFeaturesByRadiusTest extends ApiTest {
7980
TC 15 - Invalid Lat (should return 400)
8081
TC 16 - Invalid Lon (should return 400)
8182
TC 17 - RefSpace, RefFeature3 (missing geometry) (should return 404)
83+
TC 18 - Point1, radius=5m, Prop-1, Tag-3, select only p.speedlimit and @ns:com:here:xyz.tags, clip=false (should return feature 3 only)
84+
TC 19 - Point1, radius=5m, Prop-1, Tag-3, select invalid p.unknown_prop (should return feature 3 only, no properties in returned feature)
85+
TC 20 - Wrong delimiter in prop selection (should return 400)
86+
TC 21 - clip=false, see below
87+
TC 22 - clip=true, see below
88+
89+
TC 21 and 22 use the following geometry to test clipping
90+
select
91+
t.geom AS geom, -- used as feature geometry
92+
t.circle AS circle,
93+
st_intersection(st_makevalid(t.geom, 'method=structure'), t.circle) AS clipped_geo_circle, -- expected resultant clipped geometry against radius
94+
st_asgeojson(st_intersection(st_makevalid(t.geom, 'method=structure'), t.circle)) AS clipped_geo_circle_as_json,
95+
st_asgeojson(t.geom) AS geom_json
96+
FROM
97+
(SELECT
98+
st_geomfromgeojson('{"type":"LineString","coordinates":[[5.630866303,6.303472939,0],[5.63092979,6.301495703,0],[5.631035602,6.299623631,0],[5.631480011,6.296447405,0]]}') AS geom,
99+
st_buffer(st_geomfromgeojson('{"type":"Point","coordinates":[5.631480011,6.296447405,0]}')::geography, 500.0) AS circle
100+
) AS t
101+
;
82102
*/
83103

84104
@BeforeAll
@@ -254,17 +274,82 @@ private static Stream<Arguments> standardTestParams() {
254274
),
255275
"ReadFeatures/ByRadius/TC17_withRefFeatureMissingGeometry/feature_response_part.json",
256276
404
277+
),
278+
// TC 18 and 19 are in another params set strictJsonTestParams()
279+
standardTestSpec(
280+
"tc20_withWrongDelimiterInPropSelection",
281+
List.of(
282+
"lon=8.6123&lat=50.1234",
283+
"radius=5",
284+
"tags=tag-3",
285+
"p.speedLimit='60'",
286+
"selection=p.speedLimit+p.length"
287+
288+
),
289+
"ReadFeatures/ByRadius/TC20_withWrongDelimiterInPropSelection/feature_response_part.json",
290+
400
291+
),
292+
standardTestSpec(
293+
"tc21_withClipFalse",
294+
List.of(
295+
"clip=false",
296+
"lon=5.631480011&lat=6.296447405",
297+
"radius=500"
298+
),
299+
"ReadFeatures/ByRadius/TC21_withClipFalse/response.json",
300+
200
301+
// ),
302+
// standardTestSpec(
303+
// "tc22_withClipTrue",
304+
// List.of(
305+
// "clip=true",
306+
// "lon=5.631480011&lat=6.296447405",
307+
// "radius=500"
308+
// ),
309+
// "ReadFeatures/ByRadius/TC22_withClipTrue/response.json",
310+
// 200
257311
)
258312
);
259313

260314
}
261315

262-
@ParameterizedTest
263-
@MethodSource("standardTestParams")
264-
void commonTestExecution(
316+
private static Stream<Arguments> strictJsonTestParams() {
317+
return Stream.of(
318+
standardTestSpec(
319+
"tc18_withLatLonRadiusTagPropSelection",
320+
List.of(
321+
"lon=8.6123&lat=50.1234",
322+
"radius=5",
323+
"tags=tag-3",
324+
"p.speedLimit='60'",
325+
"selection=p.speedLimit,%s".formatted(urlEncoded("p.@ns:com:here:xyz.tags")),
326+
"clip=false"
327+
328+
),
329+
"ReadFeatures/ByRadius/TC18_withLatLonRadiusTagPropSelection/feature_response_part.json",
330+
200
331+
),
332+
standardTestSpec(
333+
"tc19_withInvalidSelectionProp",
334+
List.of(
335+
"lon=8.6123&lat=50.1234",
336+
"radius=5",
337+
"tags=tag-3",
338+
"p.speedLimit='60'",
339+
"selection=p.unknown_prop"
340+
341+
),
342+
"ReadFeatures/ByRadius/TC19_withInvalidSelectionProp/feature_response_part.json",
343+
200
344+
)
345+
);
346+
}
347+
348+
void baseTestExecution(
265349
final @Nullable List<String> queryParamList,
266350
final @NotNull String fPathOfExpectedResBody,
267-
final int expectedResCode) throws Exception {
351+
final int expectedResCode,
352+
boolean strictChecking) throws Exception {
268353
// Given: Request parameters
269354
String urlQueryParams = "";
270355
if (queryParamList!=null && !queryParamList.isEmpty()) {
@@ -273,7 +358,8 @@ void commonTestExecution(
273358
final String streamId = UUID.randomUUID().toString();
274359

275360
// Given: Expected response body
276-
final String expectedBodyPart = loadFileOrFail(fPathOfExpectedResBody);
361+
final String loadedString = loadFileOrFail(fPathOfExpectedResBody);
362+
final String expectedBodyPart = (strictChecking) ? loadedString.replaceAll("\\{\\{streamId}}",streamId) : loadedString;
277363

278364
// When: Get Features By Radius request is submitted to NakshaHub
279365
final HttpResponse<String> response = nakshaClient
@@ -283,9 +369,27 @@ void commonTestExecution(
283369
assertThat(response)
284370
.hasStatus(expectedResCode)
285371
.hasStreamIdHeader(streamId)
286-
.hasJsonBody(expectedBodyPart, "Response body doesn't match");
372+
.hasJsonBody(expectedBodyPart, "Response body doesn't match",strictChecking);
287373
}
288374

375+
@ParameterizedTest
376+
@MethodSource("strictJsonTestParams")
377+
void strictResponseTestExecution(
378+
final @Nullable List<String> queryParamList,
379+
final @NotNull String fPathOfExpectedResBody,
380+
final int expectedResCode) throws Exception {
381+
baseTestExecution(queryParamList,fPathOfExpectedResBody,expectedResCode,true);
382+
}
383+
384+
@ParameterizedTest
385+
@MethodSource("standardTestParams")
386+
void commonTestExecution(
387+
final @Nullable List<String> queryParamList,
388+
final @NotNull String fPathOfExpectedResBody,
389+
final int expectedResCode) throws Exception {
390+
baseTestExecution(queryParamList,fPathOfExpectedResBody,expectedResCode,false);
391+
}
392+
289393
@Test
290394
void tc06_testGetByRadiusWithLatLonRadiusLimit() throws Exception {
291395
// Given: Request parameters
@@ -309,4 +413,25 @@ void tc06_testGetByRadiusWithLatLonRadiusLimit() throws Exception {
309413
.hasFeatureIdsAmongst(List.of("my-custom-id-1","my-custom-id-2","my-custom-id-3"));
310414
}
311415

416+
// @Test
417+
// void tc22_withClipTrue() throws Exception {
418+
// // Given: Request parameters
419+
// final List<String> queryParamList = List.of(
420+
// "lon=5.631480011&lat=6.296447405",
421+
// "radius=500",
422+
// "clip=true"
423+
// );
424+
// final String urlQueryParams = String.join("&", queryParamList);
425+
// final String streamId = UUID.randomUUID().toString();
426+
//
427+
// // When: Get Features By Radius request is submitted to NakshaHub
428+
// final HttpResponse<String> response = nakshaClient
429+
// .get("hub/spaces/" + SPACE_ID + "/spatial?" + urlQueryParams, streamId);
430+
//
431+
// // Then: Perform custom assertions
432+
// assertThat(response)
433+
// .hasStatus(200)
434+
// .hasStreamIdHeader(streamId)
435+
// .hasJsonBody("ReadFeatures/ByRadius/TC22_withClipTrue/response.json");
436+
// }
312437
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"type": "FeatureCollection",
3+
"features": [
4+
{
5+
"id": "my-custom-id-3",
6+
"type": "Feature",
7+
"properties": {
8+
"speedLimit": "60",
9+
"@ns:com:here:xyz": {
10+
"tags": [ "tag-3", "tag-ref" ]
11+
}
12+
},
13+
"geometry": {
14+
"type":"Point",
15+
"coordinates":[ 8.61235, 50.1234, 0.0 ]
16+
}
17+
}
18+
],
19+
"streamId": "{{streamId}}"
20+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"type": "FeatureCollection",
3+
"features": [
4+
{
5+
"id": "my-custom-id-3",
6+
"type": "Feature",
7+
"properties": {},
8+
"geometry": {
9+
"type":"Point",
10+
"coordinates":[ 8.61235, 50.1234, 0.0 ]
11+
}
12+
}
13+
],
14+
"streamId": "{{streamId}}"
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "ErrorResponse",
3+
"error": "IllegalArgument"
4+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"type": "FeatureCollection",
3+
"features": [
4+
{
5+
"id": "my-custom-id-700-9",
6+
"type": "Feature",
7+
"properties": {
8+
"speedLimit": "50"
9+
},
10+
"geometry": {
11+
"type": "LineString",
12+
"coordinates": [
13+
[
14+
5.630866303,
15+
6.303472939,
16+
0.0
17+
],
18+
[
19+
5.63092979,
20+
6.301495703,
21+
0.0
22+
],
23+
[
24+
5.631035602,
25+
6.299623631,
26+
0.0
27+
],
28+
[
29+
5.631480011,
30+
6.296447405,
31+
0.0
32+
]
33+
]
34+
}
35+
}
36+
]
37+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"type": "FeatureCollection",
3+
"features": [
4+
{
5+
"id": "my-custom-id-700-9",
6+
"type": "Feature",
7+
"properties": {
8+
"speedLimit": "50"
9+
},
10+
"geometry": {
11+
"type": "LineString",
12+
"coordinates": [
13+
[
14+
5.630962599,
15+
6.300915231,
16+
0
17+
],
18+
[
19+
5.631035602,
20+
6.299623631,
21+
0
22+
],
23+
[
24+
5.631480011,
25+
6.296447405,
26+
0
27+
]
28+
]
29+
}
30+
}
31+
]
32+
}

0 commit comments

Comments
 (0)