This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch CAMEL-26170-geocoder-zero-results
in repository https://gitbox.apache.org/repos/asf/camel.git

commit c6ae28d7dc2e4ab8209fd811ed3b7dfc15d723ee
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Nov 2 23:30:57 2026 +0100

    CAMEL-26170: camel-geocoder - Nominatim sets ZERO_RESULTS when it finds no 
place
    
    Nominatim answers [] for an address it does not find. The producer set the
    status OK, cut off the brackets and parsed the empty text, which failed the
    exchange with an IllegalArgumentException. An empty array is now
    ZERO_RESULTS. A latitude and longitude with no place ({"error":"Unable to
    geocode"}) is ZERO_RESULTS as well, and another error answer is ERROR;
    both were OK before.
    
    The Nominatim tests use a local HTTP server with recorded answers instead of
    nominatim.openstreetmap.org.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../apache/camel/catalog/components/geocoder.json  |  2 +-
 .../camel/catalog/docs/geocoder-component.adoc     |  2 +
 .../apache/camel/component/geocoder/geocoder.json  |  2 +-
 .../src/main/docs/geocoder-component.adoc          |  2 +
 .../camel/component/geocoder/GeoCoderEndpoint.java |  4 +-
 .../geocoder/GeoCoderNominatimProducer.java        | 25 +++++--
 .../geocoder/GeoCoderNominatimAddressTest.java     | 11 +--
 .../component/geocoder/GeoCoderNominatimTest.java  | 10 +--
 .../geocoder/GeoCoderNominatimTestSupport.java     | 84 ++++++++++++++++++++++
 .../geocoder/GeoCoderNominatimZeroResultsTest.java | 81 +++++++++++++++++++++
 .../test/resources/nominatim/reverse-sevilla.json  |  1 +
 .../nominatim/search-calle-marie-curie.json        |  1 +
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |  7 ++
 13 files changed, 214 insertions(+), 18 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/geocoder.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/geocoder.json
index d8fd1660170a..459f1b883f46 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/geocoder.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/geocoder.json
@@ -43,7 +43,7 @@
   },
   "bodyTypes": {
     "producerIn": { "carriers": [ "any" ], "description": "The body is not 
read: the address or the latitude and longitude come from the 
CamelGeoCoderAddress or CamelGeoCoderLatlng header, or else from the endpoint" 
},
-    "producerOut": { "carriers": [ "list", "unchanged" ], "byOption": [ { 
"option": "type", "value": "NOMINATIM", "carriers": [ "text" ], "javaType": 
"java.lang.String" }, { "option": "headersOnly", "value": "true", "carriers": [ 
"unchanged" ] } ], "description": "With Google, the array of 
com.google.maps.model.GeocodingResult, empty for no results; unchanged when 
Google denies the request, with the status in the CamelGeoCoderStatus header. 
With Nominatim, its jsonv2 response as text: an [...]
+    "producerOut": { "carriers": [ "list", "unchanged" ], "byOption": [ { 
"option": "type", "value": "NOMINATIM", "carriers": [ "text" ], "javaType": 
"java.lang.String" }, { "option": "headersOnly", "value": "true", "carriers": [ 
"unchanged" ] } ], "description": "With Google, the array of 
com.google.maps.model.GeocodingResult, empty for no results; unchanged when 
Google denies the request, with the status in the CamelGeoCoderStatus header. 
With Nominatim, its jsonv2 response as text: an [...]
   },
   "properties": {
     "address": { "index": 0, "kind": "path", "displayName": "Address", 
"group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "description": "The geo address which should be prefixed with 
address:" },
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/geocoder-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/geocoder-component.adoc
index 8da79a403ab9..d59b7e832c36 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/geocoder-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/geocoder-component.adoc
@@ -54,6 +54,8 @@ mode in use (`address` vs. `latlng`).
 
 Camel will deliver the body as a String with the JSON response of Nominatim 
(the `jsonv2` format):
 an array with one place for an address, and an object for a latitude and 
longitude.
+When Nominatim finds no place (an empty array for an address, or 
`{"error":"Unable to geocode"}` for a latitude and longitude),
+the `CamelGeoCoderStatus` header is `ZERO_RESULTS`, and when it answers with 
an error, the header is `ERROR`.
 
 === Body using a Google Server
 
diff --git 
a/components/camel-geocoder/src/generated/resources/META-INF/org/apache/camel/component/geocoder/geocoder.json
 
b/components/camel-geocoder/src/generated/resources/META-INF/org/apache/camel/component/geocoder/geocoder.json
index d8fd1660170a..459f1b883f46 100644
--- 
a/components/camel-geocoder/src/generated/resources/META-INF/org/apache/camel/component/geocoder/geocoder.json
+++ 
b/components/camel-geocoder/src/generated/resources/META-INF/org/apache/camel/component/geocoder/geocoder.json
@@ -43,7 +43,7 @@
   },
   "bodyTypes": {
     "producerIn": { "carriers": [ "any" ], "description": "The body is not 
read: the address or the latitude and longitude come from the 
CamelGeoCoderAddress or CamelGeoCoderLatlng header, or else from the endpoint" 
},
-    "producerOut": { "carriers": [ "list", "unchanged" ], "byOption": [ { 
"option": "type", "value": "NOMINATIM", "carriers": [ "text" ], "javaType": 
"java.lang.String" }, { "option": "headersOnly", "value": "true", "carriers": [ 
"unchanged" ] } ], "description": "With Google, the array of 
com.google.maps.model.GeocodingResult, empty for no results; unchanged when 
Google denies the request, with the status in the CamelGeoCoderStatus header. 
With Nominatim, its jsonv2 response as text: an [...]
+    "producerOut": { "carriers": [ "list", "unchanged" ], "byOption": [ { 
"option": "type", "value": "NOMINATIM", "carriers": [ "text" ], "javaType": 
"java.lang.String" }, { "option": "headersOnly", "value": "true", "carriers": [ 
"unchanged" ] } ], "description": "With Google, the array of 
com.google.maps.model.GeocodingResult, empty for no results; unchanged when 
Google denies the request, with the status in the CamelGeoCoderStatus header. 
With Nominatim, its jsonv2 response as text: an [...]
   },
   "properties": {
     "address": { "index": 0, "kind": "path", "displayName": "Address", 
"group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "description": "The geo address which should be prefixed with 
address:" },
diff --git a/components/camel-geocoder/src/main/docs/geocoder-component.adoc 
b/components/camel-geocoder/src/main/docs/geocoder-component.adoc
index 8da79a403ab9..d59b7e832c36 100644
--- a/components/camel-geocoder/src/main/docs/geocoder-component.adoc
+++ b/components/camel-geocoder/src/main/docs/geocoder-component.adoc
@@ -54,6 +54,8 @@ mode in use (`address` vs. `latlng`).
 
 Camel will deliver the body as a String with the JSON response of Nominatim 
(the `jsonv2` format):
 an array with one place for an address, and an object for a latitude and 
longitude.
+When Nominatim finds no place (an empty array for an address, or 
`{"error":"Unable to geocode"}` for a latitude and longitude),
+the `CamelGeoCoderStatus` header is `ZERO_RESULTS`, and when it answers with 
an error, the header is `ERROR`.
 
 === Body using a Google Server
 
diff --git 
a/components/camel-geocoder/src/main/java/org/apache/camel/component/geocoder/GeoCoderEndpoint.java
 
b/components/camel-geocoder/src/main/java/org/apache/camel/component/geocoder/GeoCoderEndpoint.java
index a1dacf752942..c4f13de0717e 100644
--- 
a/components/camel-geocoder/src/main/java/org/apache/camel/component/geocoder/GeoCoderEndpoint.java
+++ 
b/components/camel-geocoder/src/main/java/org/apache/camel/component/geocoder/GeoCoderEndpoint.java
@@ -51,8 +51,8 @@ import org.apache.camel.support.DefaultEndpoint;
                                    description = "With Google, the array of 
com.google.maps.model.GeocodingResult, empty for"
                                                  + " no results; unchanged 
when Google denies the request, with the status in"
                                                  + " the CamelGeoCoderStatus 
header. With Nominatim, its jsonv2 response as text:"
-                                                 + " an array of one place for 
an address, an object for a latitude and"
-                                                 + " longitude. The first 
result is also in the headers"))
+                                                 + " an array of one place for 
an address (empty for no results), an object"
+                                                 + " for a latitude and 
longitude. The first result is also in the headers"))
 public class GeoCoderEndpoint extends DefaultEndpoint implements 
EndpointServiceLocation {
 
     @UriPath
diff --git 
a/components/camel-geocoder/src/main/java/org/apache/camel/component/geocoder/GeoCoderNominatimProducer.java
 
b/components/camel-geocoder/src/main/java/org/apache/camel/component/geocoder/GeoCoderNominatimProducer.java
index b30ee8f23c08..af3932601e5d 100644
--- 
a/components/camel-geocoder/src/main/java/org/apache/camel/component/geocoder/GeoCoderNominatimProducer.java
+++ 
b/components/camel-geocoder/src/main/java/org/apache/camel/component/geocoder/GeoCoderNominatimProducer.java
@@ -129,20 +129,33 @@ public class GeoCoderNominatimProducer extends 
DefaultProducer {
             exchange.getIn().setBody(place);
         }
 
+        if (place != null) {
+            place = place.trim();
+            // search answers an array, of one place as we ask for limit=1, 
and [] when nothing is found
+            if (place.startsWith("[") && place.endsWith("]")) {
+                place = place.substring(1, place.length() - 1).trim();
+            }
+        }
         if (place == null || place.isEmpty()) {
             exchange.getIn().setHeader(GeoCoderConstants.STATUS, 
GeocoderStatus.ZERO_RESULTS);
             return;
         }
-        exchange.getIn().setHeader(GeoCoderConstants.STATUS, 
GeocoderStatus.OK);
 
-        if (place.startsWith("[") && place.endsWith("]")) {
-            place = place.substring(1, place.length() - 1);
-        }
-
-        // additional details
         final Configuration conf = 
Configuration.defaultConfiguration().addOptions(Option.SUPPRESS_EXCEPTIONS);
         final DocumentContext doc = JsonPath.using(conf).parse(place);
 
+        Object error = doc.read("$['error']");
+        if (error instanceof String) {
+            // reverse answers {"error":"Unable to geocode"} when there is no 
place at the latitude and longitude
+            exchange.getIn().setHeader(GeoCoderConstants.STATUS, 
GeocoderStatus.ZERO_RESULTS);
+            return;
+        } else if (error != null) {
+            // an error answer such as {"error":{"code":400,"message":"..."}}
+            exchange.getIn().setHeader(GeoCoderConstants.STATUS, 
GeocoderStatus.ERROR);
+            return;
+        }
+        exchange.getIn().setHeader(GeoCoderConstants.STATUS, 
GeocoderStatus.OK);
+
         exchange.getIn().setHeader(GeoCoderConstants.ADDRESS, 
doc.read("$['display_name']"));
 
         // just grab the first element and its lat and lon
diff --git 
a/components/camel-geocoder/src/test/java/org/apache/camel/component/geocoder/GeoCoderNominatimAddressTest.java
 
b/components/camel-geocoder/src/test/java/org/apache/camel/component/geocoder/GeoCoderNominatimAddressTest.java
index 6294366c113a..4998bf71f8e6 100644
--- 
a/components/camel-geocoder/src/test/java/org/apache/camel/component/geocoder/GeoCoderNominatimAddressTest.java
+++ 
b/components/camel-geocoder/src/test/java/org/apache/camel/component/geocoder/GeoCoderNominatimAddressTest.java
@@ -18,19 +18,21 @@ package org.apache.camel.component.geocoder;
 
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
-import org.apache.camel.test.junit6.CamelTestSupport;
 import org.junit.jupiter.api.Test;
 
-public class GeoCoderNominatimAddressTest extends CamelTestSupport {
+public class GeoCoderNominatimAddressTest extends GeoCoderNominatimTestSupport 
{
 
     @Test
     public void testGeoCoder() throws Exception {
+        answerFromResource("search", "search-calle-marie-curie.json");
+
         MockEndpoint mock = getMockEndpoint("mock:result");
         mock.expectedMessageCount(1);
+        mock.expectedHeaderReceived(GeoCoderConstants.STATUS, 
GeocoderStatus.OK);
         mock.expectedHeaderReceived(GeoCoderConstants.COUNTRY_SHORT, "ES");
         mock.expectedHeaderReceived(GeoCoderConstants.REGION_NAME, 
"Andalucía");
+        mock.expectedHeaderReceived(GeoCoderConstants.POSTAL_CODE, "41092");
 
-        // the address header overrides the endpoint configuration
         template.sendBody("direct:start", "Test");
 
         MockEndpoint.assertIsSatisfied(context);
@@ -41,7 +43,8 @@ public class GeoCoderNominatimAddressTest extends 
CamelTestSupport {
         return new RouteBuilder() {
             public void configure() {
                 from("direct:start").to(
-                        "geocoder:address:calle marie curie, sevilla, 
sevilla?type=NOMINATIM&serverUrl=RAW(https://nominatim.openstreetmap.org)")
+                        "geocoder:address:calle marie curie, sevilla, 
sevilla?type=NOMINATIM&serverUrl=RAW(" + serverUrl()
+                                        + ")")
                         .to("log:result")
                         .log("Location ${header.CamelGeocoderAddress} is at 
lat/lng: ${header.CamelGeocoderLatlng}"
                              + " and in city ${header.CamelGeoCoderCity} in 
country ${header.CamelGeoCoderCountryLong}")
diff --git 
a/components/camel-geocoder/src/test/java/org/apache/camel/component/geocoder/GeoCoderNominatimTest.java
 
b/components/camel-geocoder/src/test/java/org/apache/camel/component/geocoder/GeoCoderNominatimTest.java
index 3a1790fa175d..dfc749809df8 100644
--- 
a/components/camel-geocoder/src/test/java/org/apache/camel/component/geocoder/GeoCoderNominatimTest.java
+++ 
b/components/camel-geocoder/src/test/java/org/apache/camel/component/geocoder/GeoCoderNominatimTest.java
@@ -18,19 +18,21 @@ package org.apache.camel.component.geocoder;
 
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
-import org.apache.camel.test.junit6.CamelTestSupport;
 import org.junit.jupiter.api.Test;
 
-public class GeoCoderNominatimTest extends CamelTestSupport {
+public class GeoCoderNominatimTest extends GeoCoderNominatimTestSupport {
 
     @Test
     public void testGeoCoder() throws Exception {
+        answerFromResource("reverse", "reverse-sevilla.json");
+
         MockEndpoint mock = getMockEndpoint("mock:result");
         mock.expectedMessageCount(1);
+        mock.expectedHeaderReceived(GeoCoderConstants.STATUS, 
GeocoderStatus.OK);
         mock.expectedHeaderReceived(GeoCoderConstants.COUNTRY_SHORT, "ES");
         mock.expectedHeaderReceived(GeoCoderConstants.CITY, "Sevilla");
+        mock.expectedHeaderReceived(GeoCoderConstants.LATLNG, "37.38618880, 
-5.99255350");
 
-        // the address header overrides the endpoint configuration
         template.sendBody("direct:start", "Test");
 
         MockEndpoint.assertIsSatisfied(context);
@@ -41,7 +43,7 @@ public class GeoCoderNominatimTest extends CamelTestSupport {
         return new RouteBuilder() {
             public void configure() {
                 from("direct:start").to(
-                        
"geocoder:latlng:37.38619,-5.99255?type=NOMINATIM&serverUrl=RAW(https://nominatim.openstreetmap.org)")
+                        
"geocoder:latlng:37.38619,-5.99255?type=NOMINATIM&serverUrl=RAW(" + serverUrl() 
+ ")")
                         .to("log:result")
                         .log("Location ${header.CamelGeocoderAddress} is at 
lat/lng: ${header.CamelGeocoderLatlng}"
                              + " and in city ${header.CamelGeoCoderCity} in 
country ${header.CamelGeoCoderCountryLong}")
diff --git 
a/components/camel-geocoder/src/test/java/org/apache/camel/component/geocoder/GeoCoderNominatimTestSupport.java
 
b/components/camel-geocoder/src/test/java/org/apache/camel/component/geocoder/GeoCoderNominatimTestSupport.java
new file mode 100644
index 000000000000..ed1d5c8c9235
--- /dev/null
+++ 
b/components/camel-geocoder/src/test/java/org/apache/camel/component/geocoder/GeoCoderNominatimTestSupport.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.geocoder;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.sun.net.httpserver.HttpServer;
+import org.apache.camel.test.junit6.CamelTestSupport;
+
+/**
+ * Runs the Nominatim tests against a local HTTP server that answers the 
recorded responses of
+ * nominatim.openstreetmap.org, so the build does not depend on the public 
service.
+ */
+public abstract class GeoCoderNominatimTestSupport extends CamelTestSupport {
+
+    private final Map<String, String> answers = new ConcurrentHashMap<>();
+    private HttpServer server;
+
+    @Override
+    protected void setupResources() throws Exception {
+        server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
+        server.createContext("/", exchange -> {
+            String answer = answers.get(exchange.getRequestURI().getPath());
+            byte[] bytes = answer != null ? 
answer.getBytes(StandardCharsets.UTF_8) : new byte[0];
+            exchange.getResponseHeaders().add("Content-Type", 
"application/json; charset=utf-8");
+            exchange.sendResponseHeaders(answer != null ? 200 : 404, 
bytes.length > 0 ? bytes.length : -1);
+            try (OutputStream body = exchange.getResponseBody()) {
+                body.write(bytes);
+            }
+        });
+        server.start();
+    }
+
+    @Override
+    protected void cleanupResources() throws Exception {
+        if (server != null) {
+            server.stop(0);
+        }
+    }
+
+    /**
+     * The URL of the local Nominatim server, for the serverUrl option.
+     */
+    protected String serverUrl() {
+        return "http://127.0.0.1:"; + server.getAddress().getPort();
+    }
+
+    /**
+     * Sets the answer of the given operation (search or reverse).
+     */
+    protected void answer(String operation, String json) {
+        answers.put("/" + operation, json);
+    }
+
+    /**
+     * Sets the answer of the given operation (search or reverse) from a 
recorded response in
+     * src/test/resources/nominatim.
+     */
+    protected void answerFromResource(String operation, String name) throws 
IOException {
+        try (InputStream is = getClass().getResourceAsStream("/nominatim/" + 
name)) {
+            answer(operation, new String(is.readAllBytes(), 
StandardCharsets.UTF_8));
+        }
+    }
+}
diff --git 
a/components/camel-geocoder/src/test/java/org/apache/camel/component/geocoder/GeoCoderNominatimZeroResultsTest.java
 
b/components/camel-geocoder/src/test/java/org/apache/camel/component/geocoder/GeoCoderNominatimZeroResultsTest.java
new file mode 100644
index 000000000000..97acc619fc30
--- /dev/null
+++ 
b/components/camel-geocoder/src/test/java/org/apache/camel/component/geocoder/GeoCoderNominatimZeroResultsTest.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.geocoder;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.jupiter.api.Test;
+
+public class GeoCoderNominatimZeroResultsTest extends 
GeoCoderNominatimTestSupport {
+
+    @Test
+    public void testAddressNotFound() throws Exception {
+        // search answers an empty array when it finds nothing
+        answer("search", "[]");
+
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedMessageCount(1);
+        mock.expectedBodiesReceived("[]");
+        mock.expectedHeaderReceived(GeoCoderConstants.STATUS, 
GeocoderStatus.ZERO_RESULTS);
+
+        template.sendBody("direct:address", "Test");
+
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Test
+    public void testLatLngNotFound() throws Exception {
+        // reverse answers an error text when there is no place at the 
latitude and longitude, such as at sea
+        answer("reverse", "{\"error\":\"Unable to geocode\"}");
+
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedMessageCount(1);
+        mock.expectedHeaderReceived(GeoCoderConstants.STATUS, 
GeocoderStatus.ZERO_RESULTS);
+
+        template.sendBody("direct:latlng", "Test");
+
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Test
+    public void testErrorAnswer() throws Exception {
+        answer("reverse", "{\"error\":{\"code\":400,\"message\":\"Parameter 
'lat' must be a number.\"}}");
+
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedMessageCount(1);
+        mock.expectedHeaderReceived(GeoCoderConstants.STATUS, 
GeocoderStatus.ERROR);
+
+        template.sendBody("direct:latlng", "Test");
+
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            public void configure() {
+                from("direct:address")
+                        
.to("geocoder:address:nowhere?type=NOMINATIM&serverUrl=RAW(" + serverUrl() + 
")")
+                        .to("mock:result");
+
+                from("direct:latlng")
+                        
.to("geocoder:latlng:0,-30?type=NOMINATIM&serverUrl=RAW(" + serverUrl() + ")")
+                        .to("mock:result");
+            }
+        };
+    }
+}
diff --git 
a/components/camel-geocoder/src/test/resources/nominatim/reverse-sevilla.json 
b/components/camel-geocoder/src/test/resources/nominatim/reverse-sevilla.json
new file mode 100644
index 000000000000..11a72c3d1c6a
--- /dev/null
+++ 
b/components/camel-geocoder/src/test/resources/nominatim/reverse-sevilla.json
@@ -0,0 +1 @@
+{"place_id":295378929,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. 
http://osm.org/copyright","osm_type":"node","osm_id":3814289959,"lat":"37.3861888","lon":"-5.9925535","category":"historic","type":"memorial","place_rank":30,"importance":0.23094323434513592,"addresstype":"historic","name":"Giraldillo","display_name":"Giraldillo,
 Puerta de Palos, Santa Cruz, Casco Antiguo, Sevilla, Andalucía, 41004, 
España","address":{"historic":"Giraldillo","road":"Puerta de Palos","neighbourh 
[...]
\ No newline at end of file
diff --git 
a/components/camel-geocoder/src/test/resources/nominatim/search-calle-marie-curie.json
 
b/components/camel-geocoder/src/test/resources/nominatim/search-calle-marie-curie.json
new file mode 100644
index 000000000000..b0d877fe7939
--- /dev/null
+++ 
b/components/camel-geocoder/src/test/resources/nominatim/search-calle-marie-curie.json
@@ -0,0 +1 @@
+[{"place_id":295982222,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. 
http://osm.org/copyright","osm_type":"way","osm_id":70971075,"lat":"37.4062844","lon":"-6.0059975","category":"highway","type":"primary","place_rank":26,"importance":0.05341013427939265,"addresstype":"primary","name":"Calle
 Marie Curie","display_name":"Calle Marie Curie, Triana, Sevilla, Andalucía, 
41092, España","address":{"road":"Calle Marie 
Curie","suburb":"Triana","city":"Sevilla","province":"Sevilla","ISO [...]
\ No newline at end of file
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 10f5a47c1b89..538140b96503 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -764,6 +764,13 @@ onto the configuration of the endpoint. Before, they were, 
so every later query
 `location` already did the geolocation when its producer was created, so its 
`CamelWeatherLocation` header was never
 used. The `lat` and `lon` options of an endpoint are still used instead of the 
location.
 
+=== camel-geocoder - Nominatim sets ZERO_RESULTS when it finds no place
+
+With `type=NOMINATIM`, an address that Nominatim does not find (it answers 
`[]`) now sets the `CamelGeoCoderStatus`
+header to `ZERO_RESULTS`. Before, the exchange failed with an 
`IllegalArgumentException`. A latitude and longitude with
+no place (Nominatim answers `{"error":"Unable to geocode"}`) now sets 
`ZERO_RESULTS` too, and another error answer of
+Nominatim sets `ERROR`. Before, both set `OK` with no other headers.
+
 === camel-gson, camel-jsonb - the unmarshal type header needs 
allowUnmarshallType
 
 The Gson and JSON-B data formats now use the `CamelGsonUnmarshalType` and 
`CamelJsonbUnmarshallType` headers to choose

Reply via email to