ODK geoshape/geotrace/geopoint to WKT

Hello Mathieu,
I trust you are well. (France played well yesterday!)
Attached is the most recent version of our generic Land Use Survey in ODK format. It currently automatically calculates the surface area of the Geoshape recorded in the form.
Q: Could you advise on the ODK code we should use to add an automated calculation for the 'perimeter' of the same geoshape?
C1.Land_Use_Survey_Generic_v7.xlsx (48.8 KB)

Merci Mathieu,

1 Like

See distance() function:

Returns the distance, in meters, of either:

    a nodeset of geopoints
    *the perimeter of a geoshape*
    the length of a geotrace value

It takes into account the circumference of the Earth around the Equator and does not take altitude into account.
1 Like

Thanks @davidbaines for sharing your form.
@Xiphware was faster :slight_smile:

Thanks so much. It worked perfectly.
So sorry for the late reply.

1 Like

Hello Mathieu,
Trust you are well.
I recently posted another question on the ODK forum - thought you might be interested:

Hi David,

I am fine thank you ! Hope you too.
I will spend some time tomorrow during a train trip on ODK (Central french translation to complete) I will try to understand the origin of the problem.
Could you provide some raw string data for both working and not working polygons ? I will see if we could automatically clean it or at least use it.

Thanks to @Xiphware explanations here :

I had a test to handle cases when geo strings contains '; ' as separator instead of the only semicolon.
This problem occurs sometimes and @davidbaines faced it here :

In such cases, point can be considered as a space-separated list of values with one more space and one more column index.
geoshape2wkt.xls (8 KB)

1 Like

What I'd like to do is be able to collect 1 to many geotraces or geoshapes, and wrap those up as MULTILINESTRING / MULTIPOLYGON outputs (and then at the end combine all point/trace/shape to a GEOMETRYCOLLECTION)

I started with @mathieubossaert's example, adding a repeat to allow multiple traces, but when I try to calculate the string inside the trace repeat, it uses all line vertices from every line, (so if line 1 = A to B to B and line 2 = line D to E, then it returns line 1 = A to B to C to D to E and line 2 = A to B to C to D to E)

I think the solution lies in how to refer to / constrain particular repeat elements (i.e. only take child repeat coordinates if their parent repeat instance = this instance) unless there's a simpler approach to the problem altogether?

Combining points to MULTIPOINT was simpler, and combining the MULTIPOINT with a LINESTRING etc to a GEOMETRYCOLLECTION is also simple.

This is the example, but incorrect form:
multiline_example.xlsx (20.3 KB)

Form output for 4 points and 2x 2 point lines becomes 4x points and 2x identical 4 point lines:

GEOMETRYCOLLECTION(MULTIPOINT Z ((-65.442744 9.232249 0),(-79.483427 -7.111795 0),(-34.553242 -7.340675 0),(-68.952915 -54.489187 0)), MULTILINESTRING Z((-16.300355 14.317615 0,48.261695 8.809082 0,6.866771 35.038992 0,22.322431 -34.152727 0),(-16.300355 14.317615 0,48.261695 8.809082 0,6.866771 35.038992 0,22.322431 -34.152727 0)))

Hi @ahblake,
I would like to well understand your need ? Note sure to be able to achieve it...

You want to merge the objects from each repeat into one single "MULTIxxx" ?
And at the end of the form get a "GEOMETRYCOLLECTION" with the MULTIxxx ?

Yes, what I'd like to do is have repeats for each geotype (point/line/shape) so the enumerator can add as many as required (eg place a point for all corrosion hole anomalies, trace a line along each affected pipe section, draw a shape around all affected areas).

Then bundle each type into a MULTIxxx, and bundle those into a collection, such that the parent submission has all the geo elements wrapped up in a single string. This means I can handle having any number of each geotype finding and won't have to pull the child repeat elements to get the details (unless they also have additional fields associated with them) as they're in GEOMETRYCOLLECTION in the parent form.

The part I'm stuck on is how to create the MULTILINESTRING and MULTIPOLYGON by referring to the particular repeat instances. I have already done the MULTIPOINT and GEOMETRYCOLLECTION steps.

From the above example, the correct output for the MULTILINESTRING would be:

MULTILINESTRING Z((-16.300355 14.317615 0,48.261695 8.809082 0) ,( 6.866771 35.038992 0,22.322431 -34.152727 0))

Hi @mathieubossaert
geoshape2wkt.xlsx (10.1 KB)
,
I am also looking for a solution. if you see the attached form. the output I am receiving from two repeated geoshape form is as Multipolygon((A B, C D, E F, A B, I J, K L, M N, I J)).
where A,B,C etc are longitude and latitude.
problem: I cannot group the output of a single repeated loop.

What I want:- Multipolygon(((A B, C D, E F, A B)), ((I J, K L, M N, I J))).
your help would be great for me.

For the moment I have quite the same result as yours (a geom union instead of a collect).
I will take a look at it when I'll get a bit of time.

1 Like

I forgot to welcome and encourage you onboard and to introduce yourself here @Satinder

Please take some time to do it :slight_smile:

I achieved to do what we discuss last few days with @ahblake .
3 repeat-groups :

  • one for point,
  • one fore line
  • and one for polygon.
    Every geometries of each loop are merged into :
  • one MULTIPOINT,
  • one MULTILINESTRING
  • and one MULTIPOLYGON.

And in the end those geometries (max 3) are collected into a single GEOMETRYCOLLECTION

Disclaimer, this in not really beautiful, I duplicate what worked for polygon for both lines and points. Unnecessary steps remain :-).

geopoints_lines_and_polygons_to_wkt_geometrycollection.xlsx (13.7 KB)

3 Likes

Thanks @mathieubossaert! This is the key that unlocked what I was trying to achieve: selected-at(${geo_polygon},${index_polygon}, I was reading the docs and knew this was the direction I needed to go in but hadn't worked it out.

The output is exactly what I was chasing, the ability to construct a collection of any number of different geo elements.

2 Likes

In case this trips anyone else in future:

In the repeat_count field in the example file @mathieubossaert has attached, the count is eg ${numpoints_polygon}. This will not throw an error in Enketo, and will function in Collect, but it will throw an error message on validation in Collect without pointing anywhere specific and preventing save/submit.

Sorry, form save failed! java.lang.Double cannot be cast to java.lang.Integer

Changing the repeat_count to eg int(${numpoints_polygon}) will fix this in Collect

Two posts tipped me off to this, one & two

(An old post points to the docs for a FAQ on how to get error logs, but the link is no more and redirects to docs)

Issue filed at https://github.com/getodk/javarosa/issues/686

@all,

the solution given in above thread works absolutely fine for smaller polygons i.e. number of points less than 40-50 in a single polygon.
APP freezes when it exceed 100 points which is above 1 acre in area.
we are mapping farms having different shapes (not necessarily square or rectangular) to capture that irregular shape we are collecting points at 3 seconds interval.
Any alternative solution for this or better to write a function at server side to convert from ODK shape to WKT?

That is likely your best bet for polygons of that size.

There is a $wkt attribute that can be used when requesting a JSON representation of the data. We currently only document it in the API docs: https://odkcentral.docs.apiary.io/#reference/odata-endpoints/odata-form-service/data-document

This will give you a WKT snippet associated with every field of a geo type:

{
  "value": [
    {
      "point": "POINT (-4.535363 9.869207 0)",
      "shape": "POLYGON ((173.4375 -44.51889095578011 0,143.203125 -6.381852166115692 0,120.10044642857142 14.993303789037626 0,112.60044642857144 0.2678561671694837 0,173.4375 -44.51889095578011 0))"
    }
  ], ...
}

value represents an array of submissions. Submissions are key value pairs with nesting for groups and references for repeats.

You probably would need to do another post-processing step but depending on your needs, this could be a good starting point. You can access it with pyodk with client.submissions.get_table(form_id=<form_id>, wkt=True) (see get_table example here) and with ruODK using geo_wkt.

You can also access it directly with:
https://<server>/v1/projects/<projectID>/forms/<form-id>.svc/Submissions?$wkt=true

If I want to get a response in-browser I usually copy the URL from the "Analyze via OData" button on the Submissions page and add /Submissions?$wkt=true. Unfortunately, there's one more step which is that your browser needs to request a JSON response. I use this browser extension to set the Accept header to application/json. We generally expect users to get this representation programmatically but it can be helpful to see in-browser when testing.

1 Like

Apologies, I think I have missed something here, but the returned value for polygons etc is in WKT format when queried via OData & Powerquery with the default svc URL, and displays in Central as WKT also but without SRID=4326;. Is this different with pyodk & other methods?

eg for an ODK polygon which would be (and is this format in the CSV)
-38 147 0.0 0.0;-39 147 0.0 0.0;-39 148 0.0 0.0;-39 147 0.0 0.0;-38 147 0.0 0.0
is returned as
SRID=4326;POLYGON ((147 -38 0, 147 -39 0, 148 -39 0, 147 -39 0, 147 -38 0))

:grimacing: No, it's just confusing. The default format returned as part of the OData JSON is geoJSON because that's what the OData spec defines. Powerquery transforms it to WKT. I hadn't thought about this but you might be able to use Excel/PowerBI connected to OData as part of your data pipeline depending on what you need to do next with WKT.

The Central frontend uses the OData JSON request with $wkt.