Conversation
Added projectionCode field to geometry definition.
Added feature type repository and feature source factory helper to the SearchController. Updated constructor and search logic to utilize feature type information.
mprins
left a comment
There was a problem hiding this comment.
I would prefer storing the source data CRS in the SearchIndex object when the index is created (this will require add a column using a new update script under https://github.com/Tailormap/tailormap-api/tree/main/src/main/resources/db/migration)
Also the re-projection/translation should be done server-side so that the source data projection is not need to be exposed. We do the same thing for the eg features and editing endpoints; the API should deliver geometry in the application/map CRS
The search endpoint will also need to be fixed with regard to the search-withing-distance option, see eg. the testcase get_spatial_query_distance that demonstrates the pt and d options
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The datastore resource leak must be resolved before approval.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (2)
What changed in this PR
Adds the feature type CRS to search results so viewers can correctly transform geometries.
Changes:
- Adds nullable
projectionCodeto the OpenAPI schema. - Resolves and propagates the feature type CRS in Solr search results.
- Passes CRS metadata through search handling.
| File | Summary | Findings |
|---|---|---|
src/main/resources/openapi/viewer-api.yaml |
Documents projectionCode. |
None. |
src/main/java/org/tailormap/api/solr/SolrHelper.java |
Adds CRS data to search documents. | Nit (4 votes): Add regression coverage asserting projectionCode. |
src/main/java/org/tailormap/api/controller/SearchController.java |
Resolves the feature-type CRS. | Critical (4 votes): Dispose the datastore after extracting the CRS to prevent resource leaks. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| final String projectionCode; | ||
|
|
||
| try { | ||
| SimpleFeatureSource featureSource = featureSourceFactoryHelper.openGeoToolsFeatureSource(featureType); | ||
|
|
||
| projectionCode = | ||
| GeoToolsHelper.crsToString(featureSource.getSchema().getCoordinateReferenceSystem()); | ||
| } catch (IOException e) { | ||
| logger.error("Unable to determine CRS for search index '{}'", searchIndex.getName(), e); | ||
| throw new ResponseStatusException( | ||
| HttpStatus.INTERNAL_SERVER_ERROR, "Unable to determine CRS for search index", e); | ||
| } |
| searchResponse.addDocumentsItem(new SearchDocument() | ||
| .fid(solrDocument.getFieldValue(SEARCH_ID_FIELD).toString()) | ||
| .geometry(geom.toString()) | ||
| .projectionCode(projectionCode) |
|
Thanks for the review. I've reworked the implementation based on your feedback in commit 4449086. The source CRS is now stored in SearchIndex when the index is built, search result geometries are reprojected server-side to the application CRS, and the spatial pt parameter is transformed back to the source CRS while keeping d unchanged. I've also added focused unit tests covering differing/equal CRS and existing indexes without a stored source CRS. The PR description has been updated to reflect the new implementation. |


Problem
When using a Tailormap search index for a feature type whose CRS differs from the application map CRS, selecting a search result can zoom the map to an incorrect location.
We encountered this with:
POINT (3.7651071548461914 51.742008209228516)Search index geometries are stored in the native CRS of the feature type. Previously, the search endpoint returned that geometry unchanged, so it could be interpreted as if it were already in the application CRS.
Spatial search using
ptanddhad the same CRS issue:ptis supplied in the application CRS, while the Solr geometry is stored in the source CRS.Solution
This change keeps the CRS handling server-side.
source_crstosearch_indexusing a Flyway migration.SearchIndex.sourceCrswhen the search index is built.ptvalue from the application CRS to the source CRS before sending the query to Solr.dunchanged because it is already expressed in the distance units configured for the Solr geometry field.sourceCrsretain the previous behaviour until they are rebuilt.The source CRS therefore does not need to be exposed through the viewer API, and no viewer-side CRS handling is required for this fix.
Testing
SolrHelperTesttests covering:ptand result geometry when source and application CRS differ;dvalue;sourceCrsis not yet stored.