kobotoolbox / enketo-express

We've moved! Please use the new repository 🠊 https://github.com/enketo/enketo-express
Apache License 2.0
102 stars 90 forks source link

Error when calculating an area with repeated elements #508

Closed overnin closed 6 years ago

overnin commented 8 years ago

One node of my form is performing an area calculation from a maximum of 3 different geotraces (repeats). All is working well on the android app, however when the submission data is edited by enketo, it throws the following error.

screen shot 2016-05-17 at 20 57 04

Any idea on what is causing this issue, or any work around in the xform to avoid this error?

MartijnR commented 8 years ago

Thanks for this. I'm not sure if it's a bug. Check https://opendatakit.github.io/odk-xform-spec/. The area() function is described as follows:

area(node-set ns | geoshape gs)

Returns the calculated area in m2 of either a nodeset of geopoints or a geoshape value (not a combination of both) on Earth. It takes into account the circumference of the Earth around the Equator but does not take altitude into account.

Are you using the function in a supported manner?

In particular, you cannot use a geotrace (has to be a geoshape).

MartijnR commented 8 years ago

Actually, when looking at the actual implementation, geotrace should work as well. The best next step would be to create an as-simple-as-possible form that demonstrates the issue. It would be great if you have time to do that, because I don't have myself at the moment.

I'd be happy to look at that simple reproducible case and see if there is a bug.

overnin commented 8 years ago

Looking at the issue again, the area() might not be the problem at all. The issue is rather a simple javascript reference to an non existing array index. If this is true it would be nice if enketo express would have a more progressive way of failing especially for the calculated field which don't need to be edited. At the moment the form is not displayed at all due to this error. Whereas the form should still be displayed but the field could simply be deactivated.

For the function area(), it is working well when using a single field and in our case we are just concatenating multiple of those trace. This is an example of a 3 point polygon (first and last points are the same) data.

65.967298 -18.530265 12 1.0; 65.967924 -18.528913 10 1; 65.968561 -18.531209 13 1; 65.967298 -18.530265 12 1.0

MartijnR commented 8 years ago

I don't understand the

non-existing array index

Can you show how does that occur, with a test form?

Generally, it's very much deliberate to fail a form hard if an XPath expression is invalid, but not clear if that's the case here.

overnin commented 8 years ago

non-existing array index maybe a confusion on my side, it's because the area computation is supposed to add a maximum of 3 areas. It depends on how many area the farmer is cultivating cocoa on. The farmer might only grow cocoa on one parcel, therefor if the calculation is trying to access a non existing index in the repeat keys one could end up with a "undefine index". However the above bug couldn't occur as the formula always test first the number of parcels before accessing the specific index.

This is the original calculate formula in the excel file.

area(indexed-repeat(${cocoamapping_boundaries}, ${repeat_cocoaboundaries}, 1)) + if(int(${cocoa_plotparts}) >= 2, area(indexed-repeat(${cocoamapping_boundaries}, ${repeat_cocoaboundaries}, 2)), 0) + if(int(${cocoa_plotparts}) >= 3, area(indexed-repeat(${cocoamapping_boundaries}, ${repeat_cocoaboundaries}, 3)), 0)

The then pyform convert it in the following formula:

area(/model/instance[1]/Ghana-FarmSeal-Mapping-3.0/grp_parcelcocoa/grp_cocoamapping/repeat_cocoaboundaries[position() = 1]/cocoamapping_boundaries) + if(int( /model/instance[1]/Ghana-FarmSeal-Mapping-3.0/grp_parcelcocoa/grp_cocoamapping/cocoa_plotparts ) >= 2, area(/model/instance[1]/Ghana-FarmSeal-Mapping-3.0/grp_parcelcocoa/grp_cocoamapping/repeat_cocoaboundaries[position() = 2]/cocoamapping_boundaries), 0) + if(int( /model/instance[1]/Ghana-FarmSeal-Mapping-3.0/grp_parcelcocoa/grp_cocoamapping/cocoa_plotparts ) >= 3, area(/model/instance[1]/Ghana-FarmSeal-Mapping-3.0/grp_parcelcocoa/grp_cocoamapping/repeat_cocoaboundaries[position() = 3]/cocoamapping_boundaries), 0)

If it helps i can build and share a simple form illustrating the bug with only 2 parcels.

gaudetb commented 6 years ago

Hi, I realize this is an old thread, but I've had problems integrating the area calculation for a deployment on KoBotoolbox. The form gets validated on ODK validate, but the area calculation is not recognized when I try to upload in KoBo. Any clues? Help much appreciated, Thanks - Gen

Error message: ODK Validate Errors: XForm Parse Warning: Warning: 1 Unrecognized attributes found in Element [input] and will be ignored: [query] Location: Problem found at nodeset: /html/body/group[@ref=${identification}.a]/input With element Problem found at nodeset: /html/body/group[@ref=${identification}.a]/input With element org.javarosa.core.log.WrappedException: Error evaluating field 'shape_area': The problem was located in calculate expression for ${identification}.a/shape_area XPath evaluation: cannot handle function 'area' => org.javarosa.xpath.XPathUnhandledException[The problem was located in calculate expression for ${identification}.a/shape_area XPath evaluation: cannot handle function 'area'] org.javarosa.core.log.WrappedException: Error evaluating field 'shape_area': The problem was located in calculate expression for ${identification}.a/shape_area XPath evaluation: cannot handle function 'area' => org.javarosa.xpath.XPathUnhandledException[The problem was located in calculate expression for ${identification}.a/shape_area XPath evaluation: cannot handle function 'area'] >> Something broke the parser. See above for a hint. Result: Invalid

MartijnR commented 6 years ago

That's weird. Looks like the KoBoCAT server is using a really ancient version of ODK Validate (the built-in one).

Are you using a server that is managed by KoBo?

PS this could be interesting to check as well: https://validate.enketo.org (a test page for developers for XForms (not XLSForms)).

gaudetb commented 6 years ago

Thanks for sharing. Funny, the form does not get validated on the test page for both Kobo and ODK... However, no problem when I use http://opendatakit.org/xiframe/ (although I have not tried to collect data).

I'm deploying on the project legacy of Kobo because of select_one_external which doesn't get recognised if I deploy on the main project page. However, I've tested it on the main page and I get the same error...

The calculation is written area(${var}), where var is the variable name of the geoshape data. Is that correct?

MartijnR commented 6 years ago

The calculation is written area(${var}), where var is the variable name of the geoshape data. Is that correct?

Yes, that's correct. The spec is here: https://opendatakit.github.io/xforms-spec/#fn:area

Since it works on opendatakit.org/xiframe (which is using the latest Enketo Express - a service that I'm running), and is therefore not related to Enketo, it's best to pursue the issue with KoBo support as I saw you have done already.

I'll close this issue. If there is an issue running a form in Enketo with area() (not with uploading a form to KoBo), feel free to re-open it and send the form.

imaginasion commented 6 years ago

Hello all, I see Martin and Overnin have been working on something I am currently working on. Cultivated farm area data capture. Please can you share the steps to include Area in hectares to the KoboCollect form. Also the Google SDK is not working on my Android, but Openstreet is. Any suggestions?

MartijnR commented 6 years ago

To get the Google maps layers to work, you need an API key from Google: https://github.com/kobotoolbox/enketo-express/tree/master/config#google

To gather an area in hectares, you could e.g. collect geopoints on the border inside a repeat and then use "calculate" question with area(${your_geopoint_question}) (and divide by 10,000, and round()). You could also collect a geoshape right away and use area() on that value.

Hope that helps!