Select to view content in your preferred language

Create Line Geom from Coords

1380
7
Jump to solution
10-29-2022 10:49 PM
SimonGIS
Regular Contributor
  • Waze Traffic endpoint provides a set of coordinates for constructing a polyline to show the slowdown traffic data.  This is shown under the nested JSON under "line":

 

"jams": [
{
"country": "AS",
"city": "Deception Bay",
"level": 5,
"line": [
{
"x": 153.029946,
"y": -27.193773
},
{
"x": 153.029894,
"y": -27.194082
}
],
"speedKMH": 0,
"length": 35,
"turnType": "NONE",
"type": "NONE",
"uuid": 74115178,
"speed": 0,
"segments": [
{}
],
"blockingAlertUuid": "d390204c-634f-4a2f-832e-54bbb14131e6",
"roadType": 20,
"delay": -1,
"pubMillis": 1667099170082
},

 

  • Using an HTTP Poller, we can derive the schema as shown below:

SimonGIS_0-1667108642264.png

  • This gives me a flattened Line field that contains the coordinates in a list like so:
  • [{"x":152.516454,"y":-27.51705},{"x":152.50828,"y":-27.515946}]
  • I then specify to this Line field to be the single geometry field, the geom is line, and format is coordinates

SimonGIS_1-1667108832795.png

 

However, due to how the coordinates are currently formatted, I get the following error in the logs:

WARN:  Failed to parse geometry field 'line'. Please make sure your geometry field string is valid. 

 

What would the optimal way to parse this JSON feed so that I can ingest it and persist the lines into a spatiotemporal feature layer?

Tags (3)
0 Kudos
1 Solution

Accepted Solutions
PeterNasuti
Esri Contributor

@brudo Thanks much for your reply and these ideas, I was just discussing with a coworker the use of a for loop workflow to generate the path array and what you proposed should work as expected. 

@SimonGIS As an FYI, these are alternate solutions that would better scale to variable length polylines when compared to the solution I provided that only accommodated a two-point polyline.

Coming back to edit, the following Arcade structure worked in test:

var path = [];
var i = 0;
var lineArray = FromJSON($feature.line)

for (var linePoint in lineArray) {
    var xVal = lineArray[i]["x"];
    var yVal = lineArray[i]["y"];
    path[i] = [xVal, yVal]
    i++
}

return Polyline({
  "hasM": false,
  "hasZ": false,
  "paths": [path],
  "spatialReference": {"wkid": 4326}
})

View solution in original post

0 Kudos
7 Replies
PeterNasuti
Esri Contributor

@SimonGIS Thanks for reaching out with the question! I have an answer/solution below, but it depends on a few elements. Overall, the answer is that Velocity "single geometry field" supports EsriJSON, GeoJSON, WKT, and Coordinates (point). This Waze format is not any of those, therefore you would need to use an Arcade expression to build the geometry.

https://doc.arcgis.com/en/iot/ingest/define-location-properties.htm

Additional considerations:

  • This solution only takes the first two points from your "line" array object in your JSON to build a line. If there were 3+ points, the extras past the first two points would be ignored
  • This solution expects two valid points from the "line" array object in your JSON. If there was a null, or only a first point, this could fail to calculate the geometry for the given feature
    • This could be enhanced using the Arcade "IIf" function and other logic to handle such a case of nulls, or not two points being present, or of more than 2 points

 

var lineArray = FromJSON($feature.line);
var x1 = lineArray[0]["x"];
var y1 = lineArray[0]["y"];
var x2 = lineArray[1]["x"];
var y2 = lineArray[1]["y"];

return Polyline({
  "hasM": false,
  "hasZ": false,
  "paths": [
    [
      [x1,y1],
      [x2,y2]
    ]
  ],
  "spatialReference": {"wkid": 4326}
})

 

As you can see above, we use the "FromJSON" function to convert the JSON string to a proper object, we parse the desired elements out, and then we send that into a "Polyline" function using the EsriJSON format. This is configured in the Calculate Fields or Map Fields tool for the geometry configuration.

Arcade1_Replace.pngArcade2.png

Arcade3.png

brudo
by
Occasional Contributor

If the line can contain more vertices, here are a few options:

  • You could manipulate the string before passing it to FromJSON. This takes advantage of the way that Velocity flattens the value, with no whitespace, and that the coordinates are already in the required order...

 

var path = Replace(Replace(Replace(Replace($feature.line,
  '{', '['), '}', ']'), '"x":',''), '"y":','')

return Polyline({
  "hasM": false,
  "hasZ": false,
  "paths": [FromJSON(path)],
  "spatialReference": {"wkid": 4326}
})

​

 

  • You could use a for loop to construct the path for the paths array. This would be more versatile for other data formats, or where the order of the coordinates is not guaranteed. It would look something like this (untested)...

 

var path = []
var i = 0
for (var c in FromJSON($feature.line)) {
  path[i] = [c.x, c.y]
  i++
}

return Polyline({
  "hasM": false,
  "hasZ": false,
  "paths": [path],
  "spatialReference": {"wkid": 4326}
})​

 

PeterNasuti
Esri Contributor

@brudo Thanks much for your reply and these ideas, I was just discussing with a coworker the use of a for loop workflow to generate the path array and what you proposed should work as expected. 

@SimonGIS As an FYI, these are alternate solutions that would better scale to variable length polylines when compared to the solution I provided that only accommodated a two-point polyline.

Coming back to edit, the following Arcade structure worked in test:

var path = [];
var i = 0;
var lineArray = FromJSON($feature.line)

for (var linePoint in lineArray) {
    var xVal = lineArray[i]["x"];
    var yVal = lineArray[i]["y"];
    path[i] = [xVal, yVal]
    i++
}

return Polyline({
  "hasM": false,
  "hasZ": false,
  "paths": [path],
  "spatialReference": {"wkid": 4326}
})
0 Kudos
SimonGIS
Regular Contributor

I have lines!  Thanks @brudo and @PeterNasuti 

I ended up using Peter's arcade snippet.  Working well so far.  No performance issues from my side.  

Thanks again.

SimonGIS_0-1667432708410.png

 

0 Kudos
chunguangWayneZhang
Emerging Contributor

Hi Peter,

I tried to do the same thing with the ARCADE from REAL TIME analytics to calculate the GEOMETRY field. Somehow, when you click the $feature.line, it shows as "pacific" instead of "X,Y" string arrays like your screen shot displayed on the right content. The feeds come in OK and I use Location as NONE.  Do you think I miss something? Thanks featureLine.JPG

0 Kudos
SimonGIS
Regular Contributor

Hey @chunguangWayneZhang - I missed that out, and I had same issue.  Peter saved the day:

Pacific is the Arcade “sample value” for a string field, which line is a string field

To resolve this, in the Arcade editor on the right click the Globals tab, click the arrow at the right of the $feature element, find $feature.line, click the edit pencil, and paste the valid JSON of the line element (not wrapped in quotations).


Then, testing the Arcade expression will evaluate against that sample:

For example, try this test string to use in place of Pacific under $feature.line

[{"x":153.029946,"y":-27.193773},{"x":154.029894,"y":-28.194082},{"x":155.029894,"y":-29.194082}]

chunguangWayneZhang
Emerging Contributor

Hi  @SimonGIS

PREVIEW
Your explanation helps to clear the puzzle. I thought PACIFIC is a sort of string that is parsed as an error. Never thought you can use generic JSON strings for testing. Thanks so much
 
 
0 Kudos