3

I am trying to select database table features within a certain distance from a leafleat generated geosjon using an sql view in geoserver. I first used a GET function and a user here suggested POST. I am getting this error:

GeoJSON.js:221 
        
       Uncaught Error: Invalid GeoJSON object.
    at De (GeoJSON.js:221:9)
    at i.addData (GeoJSON.js:117:15)
    at i.initialize (GeoJSON.js:92:9)
    at new i (Class.js:22:20)
    at Object.Ke (GeoJSON.js:439:9)
    at Object.success (app.js:289:29)
    at i (jquery-3.2.0.min.js:2:28017)
    at Object.fireWith [as resolveWith] (jquery-3.2.0.min.js:2:28783)
    at A (jquery-3.2.0.min.js:4:14017)
    at XMLHttpRequest.<anonymous> (jquery-3.2.0.min.js:4:16305)

Edit: I updated the function that generates the path to create a geojson object from the l.geojson class then stringified it:

function getRoute_Nature(callback) {
    var url = `${geoserverUrl}/wfs?service=WFS&version=1.0.0&request=GetFeature&typeName=pgrouting:nature_path&outputformat=application/json&viewparams=source:${source};target:${target};`;
  
    $.getJSON(url, function(data) {
      pathLayerNature = L.geoJSON(data, {
        style: {
            color: '#4CAF50',
            weight: 7,
            opacity: 1
        }
      }).addTo(map);

    natureGeoJSON = pathLayerNature.toGeoJSON();
    natureGjsonString = JSON.stringify(natureGeoJSON);

    console.log("geojson: " + natureGeoJSON);
    console.log("string: " + natureGjsonString);

    if (callback) {
      callback();
    }
    });
}

The SQL view looks like this, not sure if my issue is in the SQL but looks like it should be working, path as a parameter, it will be the "stringified" geojson object:

SELECT 
  ST_AsGeoJSON(ST_Collect(way)) AS geometry 
FROM (
  SELECT way FROM planet_osm_polygon 
  WHERE leisure = 'park' AND ST_DWithin(way::geography, ST_GeomFromGeoJSON('%path%')::geography, 50)
) AS nature_features;

and the function I have right now using POST request is below, I am suspecting I have sonmething wrong here?:

function getNatureFeatures(path) {
// Create a new AJAX request to get the data from the server
$.ajax({
  type: "POST",
  url: "http://127.0.0.1:8080/geoserver/wfs",
  data: {
    service: "WFS",
    version: "1.0.0",
    request: "GetFeature",
    typeName: "nature_poi",
    outputFormat: "application/json",
    cql_filter: "INCLUDE",
    viewparams: "path:" + path
  },
  success: function(data) {
    // Create a new geoJSON layer with the data returned from the server
    console.log(data)
    var natureLayer = L.geoJson(data, {
      onEachFeature: function(feature, layer) {
        layer.bindPopup(feature.properties.name);
      }
    });
    // Add the layer to the map
    natureLayer.addTo(map);        
  },
  error: function(xhr, status, error) {
    console.log("Error: " + error);
  }
});

}

when I run the code, the console.log(data) returns an xml document error saying: (the number 25 changes depending on how big the path/input geojson is)

<ServiceExceptionReport xmlns="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2.0" xsi:schemaLocation="http://www.opengis.net/ogc http://schemas.opengis.net/wfs/1.0.0/OGC-exception.xsd">
<ServiceException code="org.geoserver.wfs.kvp.GetFeatureKvpRequestReader"> 1 feature types requested, but found 25 view params specified. </ServiceException>
</ServiceExceptionReport> 

This is how I use all the functions, after a button is clicked:

$('.nature').on('click', function() {
  clearAllInfo();
  clearAllLayers();
  getRoute_Nature(function() {
    calculatePathDetails(pathLayerNature, "natureInfo");
    getNatureFeatures(natureGjsonString);
  });

I get an error saying "Uncaught Error: Invalid GeoJSON object.", I printed the stingified geojson from the getRoute_Nature function and it works fine, returns a valid geojson.

An example of an input geojson path would look like this (this is an example of natureGjsonString):

{"type":"FeatureCollection","features":[{"type":"Feature","id":"nature_path.fid-29856ddd_186d1675796_-678e","geometry":{"type":"MultiLineString","coordinates":[[[-75.691719,45.418201],[-75.691289,45.418379]]]},"geometry_name":"geom","properties":{"seq":3,"id":110550,"name":"Cooper Street","distance":39.00298642840114}},{"type":"Feature","id":"nature_path.fid-29856ddd_186d1675796_-678d","geometry":{"type":"MultiLineString","coordinates":[[[-75.691289,45.418379],[-75.691269,45.418387]]]},"geometry_name":"geom","properties":{"seq":2,"id":112670,"name":"Cooper Street","distance":1.856382386068106}},{"type":"Feature","id":"nature_path.fid-29856ddd_186d1675796_-678c","geometry":{"type":"MultiLineString","coordinates":[[[-75.691269,45.418387],[-75.691301,45.418424]]]},"geometry_name":"geom","properties":{"seq":1,"id":368750,"name":null,"distance":4.825816521280108}}]}
7
  • 1
    Use a POST, not a GET, with the parameters wrapped inside the DATA block.
    – Vince
    Mar 8 at 2:48
  • @SonofaBeach I just modified the post, thanks
    – Ayogo
    Mar 8 at 3:06
  • @Vince Do you have an example of how to use POST? This is my first time working with this and I just followed examples and trying to make them work for my case.
    – Ayogo
    Mar 8 at 3:33
  • 1
    A google on "How to use POST from JavaScript" should suffice. Your Question is about the SQL, but that's not the problem.
    – Vince
    Mar 8 at 4:57
  • 1
    You are sending in a FeatureCollection; however ST_GeomFromGeoJSON accepts only a single geometry - both logically, as well as structurally: it works on isolated GeoJSON Geometry objects only. So a) your view/query expects a single geometric parameter only, and b) the GeoJSON syntax error is thrown by ST_GeomFromGeoJSON because it can't parse FeatureCollections.
    – geozelot
    Mar 13 at 12:47

2 Answers 2

2
+100

You seem to have a logical misconception in your approach, leading to the syntax issue:

  • you are sending a FeatureCollection to the server to eventually get passed into the query - however, ST_GeomFromGeoJSON parses GeoJSON geometry objects only (e.g. path.features[i].geometry), thus throwing Invalid GeoJSON object

  • but you also send multiple features - ST_GeomFromGeoJSON (and the whole approach in general, for that matter) is not capable of parsing more than one GeoJSON geometry object


With this in mind, you have three more obvious options directly on top of your approach above:

  • isolate the one feature you actually want to lookup candidates for, e.g.:
    $.ajax({
    type: "POST",
    url: "http://127.0.0.1:8080/geoserver/wfs",
    data: {
      service: "WFS",
      version: "1.0.0",
      request: "GetFeature",
      typeName: "nature_poi",
      outputFormat: "application/json",
      cql_filter: "INCLUDE",
      viewparams: "path:" + path.features[0].geometry  -- e.g. use the first feature of the selected
    },
    ...
    
  • loop over all features in the selection and pull them in sequence, e.g.:
    for (const feature in path.features) {
      $.ajax({
        type: "POST",
        url: "http://127.0.0.1:8080/geoserver/wfs",
        data: {
          service: "WFS",
          version: "1.0.0",
          request: "GetFeature",
          typeName: "nature_poi",
          outputFormat: "application/json",
          cql_filter: "INCLUDE",
          viewparams: "path:" + feature.geometry
      },
      success: function(sequential_results) {
        -- do something with the sequential results, like appending to a 'FeatureCollection'
      },
      ...
    }
    
    var natureLayer = L.geoJson(<combined_results>, {  -- pass in a merged GeoJSON object here
      onEachFeature: function(feature, layer) {
        layer.bindPopup(feature.properties.name);
      }
    });
    
    natureLayer.addTo(map);  
    
  • merge all selected feature geometries into a single Multi geometry, then send it in as one single GeoJSON geometry member as in option one - ST_GeomFromGeoJSON is perfectly able to parse and process Multi geometries, or even a GeometryCollection
0

NB: This answer was for an earlier edition of the question which was using GET rather than POST. Therefore this answer is no longer relevant, due to edits to the question.

OPTION 1

Use an HTTP POST request instead of an HTTP GET request. A POST request does not require the parameters to be included in the URL and therefore does not have the 2048 character limit of URLs.

POST requests are a little more complicated to generate, and if you need help with this, it may be suitable to ask an entirely new question about how to generate a POST request for Geoserver (or search for other answers here, or Google).

OPTION 2

Encode your JSON for URL compatibility before including it in a URL. Eg,

var pathString = encodeURIComponent(JSON.stringify(pathGeoJSON));

See: https://stackoverflow.com/questions/6807180/how-to-escape-a-json-string-to-have-it-in-a-url

This will only work if the resulting URL (after encoding, etc) does not exceed 2048 characters, which means it will not work for complex GeoJSON features.

Also, for manually testing your URLs, you can use an interactive online JSON URL encoder, such as: https://onlinejsontools.com/url-encode-json

3
  • Thanks, just edited my post with the new function and an example input geojson
    – Ayogo
    Mar 8 at 22:27
  • OK, that makes this answer irrelevant, now that you're using POST instead of GET. I'll update the answer to explain this. Mar 8 at 22:31
  • I just updated my post to reflect the recent changed I made
    – Ayogo
    Mar 11 at 17:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.