Skip to content

Commit d3c65dc

Browse files
authored
Merge pull request #861 from Plant-for-the-Planet-org/hotfix/validation
Hotfix/validation
2 parents 495db85 + d3de20f commit d3c65dc

File tree

11 files changed

+91
-71
lines changed

11 files changed

+91
-71
lines changed

app.json

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"expo": {
33
"name": "TreeMapper",
44
"slug": "treemapper",
5-
"version": "2.0.1",
5+
"version": "2.0.3",
66
"orientation": "portrait",
77
"icon": "./assets/icon.png",
88
"userInterfaceStyle": "light",
@@ -16,6 +16,14 @@
1616
],
1717
"plugins": [
1818
"@maplibre/maplibre-react-native",
19+
[
20+
"expo-build-properties",
21+
{
22+
"ios": {
23+
"deploymentTarget": "15.5"
24+
}
25+
}
26+
],
1927
[
2028
"expo-location",
2129
{
@@ -49,7 +57,8 @@
4957
}
5058
],
5159
"expo-localization",
52-
"expo-secure-store"
60+
"expo-secure-store",
61+
"expo-build-properties"
5362
],
5463
"ios": {
5564
"supportsTablet": true,
@@ -88,4 +97,4 @@
8897
]
8998
}
9099
}
91-
}
100+
}

assets/adaptive-icon.png

96.3 KB
Loading

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"d3-shape": "^3.2.0",
3939
"expo": "~51.0.21",
4040
"expo-application": "~5.9.1",
41+
"expo-build-properties": "~0.12.5",
4142
"expo-camera": "~15.0.14",
4243
"expo-clipboard": "~6.0.3",
4344
"expo-constants": "~16.0.2",
@@ -96,7 +97,7 @@
9697
"react-native-web": "~0.19.10",
9798
"react-native-webview": "13.8.6",
9899
"react-native-youtube-iframe": "^2.3.0",
99-
"react-native-zip-archive": "^6.1.0",
100+
"react-native-zip-archive": "7.0.1",
100101
"react-redux": "^9.1.0",
101102
"realm": "^12.11.1",
102103
"redux-persist": "^6.0.0",
@@ -131,5 +132,6 @@
131132
"@babel/core": "^7.20.2",
132133
"babel-loader": "8.3.0"
133134
},
134-
"private": true
135+
"private": true,
136+
"packageManager": "[email protected]+sha512.ff4579ab459bb25aa7c0ff75b62acebe576f6084b36aa842971cf250a5d8c6cd3bc9420b22ce63c7f93a0857bc6ef29291db39c3e7a23aab5adfd5a4dd6c5d71"
135137
}

src/components/home/ProjectModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ const ProjectModal = (props: Props) => {
181181
)
182182
try {
183183
const parsedGeometry = JSON.parse(currentSiteData[0].geometry)
184-
const newCoords = getRandomPointInPolygon(parsedGeometry.coordinates[0], 1)
184+
const newCoords = getRandomPointInPolygon(parsedGeometry.coordinates[0])
185185
const { geoJSON } = makeInterventionGeoJson('Point', [newCoords], 'sd')
186186
const bounds = bbox(geoJSON)
187187
dispatch(updateMapBounds({ bounds: bounds, key: 'DISPLAY_MAP' }))

src/components/intervention/InterventionHeaderList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,6 @@ const styles = StyleSheet.create({
9292
borderColor: Colors.GRAY_LIGHT,
9393
borderRadius: 20,
9494
borderWidth: 0.5,
95-
marginRight:'1%'
95+
marginRight:10
9696
}
9797
})

src/screens/AddMeasurementView.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ const AddMeasurement = () => {
6666
const handleHeightChange = (text: string) => {
6767
setHeightErrorMessage('');
6868
const regex = /^(?!0*(\.0+)?$)(\d+(\.\d+)?|\.\d+)$/;
69-
const isValid = regex.test(text)
69+
const finalText = text.replace(/,/g, '.');
70+
const isValid = regex.test(finalText)
7071
// Ensure there is at most one decimal point
7172
if (isValid) {
7273
setHeight(text);
@@ -84,7 +85,8 @@ const AddMeasurement = () => {
8485
const handleDiameterChange = (text: string) => {
8586
setWidthErrorMessage('');
8687
const regex = /^(?!0*(\.0+)?$)(\d+(\.\d+)?|\.\d+)$/;
87-
const isValid = regex.test(text)
88+
const finalText = text.replace(/,/g, '.');
89+
const isValid = regex.test(finalText)
8890
if (isValid) {
8991
setWidth(text);
9092
} else {
@@ -97,7 +99,9 @@ const AddMeasurement = () => {
9799

98100

99101
const onSubmit = () => {
100-
const validationObject = measurementValidation(height, width, isNonISUCountry);
102+
const updatedHeight = height.replace(/,/g, '.');
103+
const updatedWidth = width.replace(/,/g, '.');
104+
const validationObject = measurementValidation(updatedHeight, updatedWidth, isNonISUCountry);
101105
const { diameterErrorMessage, heightErrorMessage, isRatioCorrect } = validationObject;
102106
setHeightErrorMessage(heightErrorMessage)
103107
setWidthErrorMessage(diameterErrorMessage)
@@ -141,6 +145,8 @@ const AddMeasurement = () => {
141145
}
142146

143147
const submitDetails = async () => {
148+
const updatedHeight = height.replace(/,/g, '.');
149+
const updatedWidth = width.replace(/,/g, '.');
144150
const { lat, long, accuracy } = getUserLocation()
145151
const treeDetails: SampleTree = {
146152
tree_id: id,
@@ -156,11 +162,11 @@ const AddMeasurement = () => {
156162
cdn_image_url: '',
157163
specie_name: SampleTreeData.current_species.scientificName,
158164
specie_diameter: getConvertedDiameter(
159-
width,
165+
updatedWidth,
160166
isNonISUCountry
161167
),
162168
specie_height: getConvertedHeight(
163-
height,
169+
updatedHeight,
164170
isNonISUCountry
165171
),
166172
tag_id: tagId,

src/screens/InterventionFormView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ const InterventionFormView = () => {
273273
el => el.id === registerForm.site_id,
274274
)
275275
const parsedGeometry = JSON.parse(currentSiteData[0].geometry)
276-
const newCoords = getRandomPointInPolygon(parsedGeometry.coordinates[0], 1)
276+
const newCoords = getRandomPointInPolygon(parsedGeometry.coordinates[0])
277277
return [newCoords]
278278
}
279279

src/screens/ReviewTreeDetails.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,12 @@ const ReviewTreeDetails = () => {
162162
let hasError = false;
163163

164164
const handleHeightValidation = () => {
165+
const updatedHeight = openEditModal.value.replace(/,/g, '.');
165166
const regex = /^(?!0*(\.0+)?$)(\d+(\.\d+)?|\.\d+)$/;
166-
const isValid = regex.test(openEditModal.value)
167+
const isValid = regex.test(updatedHeight)
167168
if (isValid) {
168169
const validationObject = measurementValidation(
169-
openEditModal.value,
170+
updatedHeight,
170171
treeDetails.specie_diameter,
171172
isNonISUCountry,
172173
);
@@ -179,7 +180,7 @@ const ReviewTreeDetails = () => {
179180
hasError = true
180181
} else {
181182
finalDetails.specie_height = getConvertedHeight(
182-
Number(openEditModal.value),
183+
Number(updatedHeight),
183184
isNonISUCountry
184185
)
185186
}
@@ -192,12 +193,13 @@ const ReviewTreeDetails = () => {
192193
};
193194

194195
const handleDiameterValidation = () => {
196+
const updatedWidth = openEditModal.value.replace(/,/g, '.');
195197
const regex = /^(?!0*(\.0+)?$)(\d+(\.\d+)?|\.\d+)$/;
196-
const isValid = regex.test(openEditModal.value)
198+
const isValid = regex.test(updatedWidth)
197199
if (isValid) {
198200
const validationObject = measurementValidation(
199201
treeDetails.specie_height,
200-
openEditModal.value,
202+
updatedWidth,
201203
isNonISUCountry,
202204
);
203205
setInputErrorMessage(validationObject.diameterErrorMessage);
@@ -209,7 +211,7 @@ const ReviewTreeDetails = () => {
209211
hasError = true
210212
} else {
211213
finalDetails.specie_diameter = getConvertedDiameter(
212-
Number(openEditModal.value),
214+
Number(updatedWidth),
213215
isNonISUCountry
214216
)
215217
}

src/screens/TreeRemeasurementView.tsx

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ const TreeRemeasurementView = () => {
134134

135135
const handleSkip = async () => {
136136
setShowSkipModal(false)
137+
const updatedHeight = height.replace(/,/g, '.');
138+
const updatedWidth = width.replace(/,/g, '.');
137139
const { lat, long } = getUserLocation()
138140
const isWithin20m = isWithinRange(lat, long, treeDetails.latitude, treeDetails.longitude)
139141
const param: History = {
@@ -143,11 +145,11 @@ const TreeRemeasurementView = () => {
143145
imageUrl: imageUri,
144146
cdnImageUrl: '',
145147
diameter: isAlive ? getConvertedDiameter(
146-
width,
148+
updatedWidth,
147149
isNonISUCountry,
148150
) : treeDetails.specie_diameter,
149151
height: isAlive ? getConvertedHeight(
150-
height,
152+
updatedHeight,
151153
isNonISUCountry,
152154
) : treeDetails.specie_height,
153155
appMetadata: '',
@@ -193,11 +195,12 @@ const TreeRemeasurementView = () => {
193195
const handleHeightChange = (text: string) => {
194196
setHeightErrorMessage('');
195197
const regex = /^(?!0*(\.0+)?$)(\d+(\.\d+)?|\.\d+)$/;
196-
const isValid = regex.test(text)
198+
const updatedHeight = text.replace(/,/g, '.');
199+
const isValid = regex.test(updatedHeight)
197200
// Ensure there is at most one decimal point
198201
if (isValid) {
199202
setHeight(text);
200-
const convertedHeight = height ? getConvertedHeight(text, isNonISUCountry) : 0;
203+
const convertedHeight = height ? getConvertedHeight(updatedHeight, isNonISUCountry) : 0;
201204
if (convertedHeight < DBHInMeter) {
202205
setDiameterLabel(i18next.t('label.measurement_basal_diameter'));
203206
} else {
@@ -211,9 +214,10 @@ const TreeRemeasurementView = () => {
211214
const handleDiameterChange = (text: string) => {
212215
setWidthErrorMessage('');
213216
const regex = /^(?!0*(\.0+)?$)(\d+(\.\d+)?|\.\d+)$/;
214-
const isValid = regex.test(text)
217+
const updatedWidth = text.replace(/,/g, '.');
218+
const isValid = regex.test(updatedWidth)
215219
if (isValid) {
216-
setWidth(text);
220+
setWidth(updatedWidth);
217221
} else {
218222
setWidthErrorMessage('Please provide the correct diameter.')
219223
}
@@ -273,8 +277,11 @@ const TreeRemeasurementView = () => {
273277
}
274278

275279
const submitHandler = async (gpsValidated?: boolean) => {
276-
const finalHeight = height || treeDetails.specie_height
277-
const finalWidth = width || treeDetails.specie_diameter
280+
const updatedHeight = height.replace(/,/g, '.');
281+
const updatedWidth = width.replace(/,/g, '.');
282+
283+
const finalHeight = updatedHeight || treeDetails.specie_height
284+
const finalWidth = updatedWidth || treeDetails.specie_diameter
278285
const { lat, long } = getUserLocation()
279286
const isWithin20m = isWithinRange(lat, long, treeDetails.latitude, treeDetails.longitude)
280287

Lines changed: 35 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
import seedrandom from 'seedrandom';
2-
3-
41
/**
52
* Generates a random point coordinate within a given polygon.
63
*
74
* @param {Array} polygon - An array of coordinate arrays representing the polygon.
85
* @param {number} index - An index to differentiate points for polygons with similar coordinates.
96
* @returns {Array} The random point coordinate [longitude, latitude].
107
*/
11-
export const getRandomPointInPolygon = (polygon, index) => {
8+
export const getRandomPointInPolygon = (polygon) => {
129
// Calculate the bounding box of the polygon
1310
const bounds = polygon.reduce(
1411
(prev, curr) => {
@@ -23,45 +20,39 @@ export const getRandomPointInPolygon = (polygon, index) => {
2320
{ minLon: Infinity, maxLon: -Infinity, minLat: Infinity, maxLat: -Infinity }
2421
);
2522

26-
// Create a seed based on the polygon's coordinates and the index
27-
const seed = JSON.stringify(polygon) + index;
28-
const rng = seedrandom(seed);
29-
30-
// Generate a random point within the bounding box
31-
let randomPoint;
32-
let isInPolygon = false;
33-
while (!isInPolygon) {
34-
const lon = bounds.minLon + rng() * (bounds.maxLon - bounds.minLon);
35-
const lat = bounds.minLat + rng() * (bounds.maxLat - bounds.minLat);
36-
randomPoint = [lon, lat];
37-
38-
// Check if the random point is within the polygon
39-
isInPolygon = isPointInPolygon(randomPoint, polygon);
23+
// Helper function to check if a point is inside the polygon
24+
const isPointInPolygon = (point, polygon) => {
25+
let inside = false;
26+
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
27+
const xi = polygon[i][0], yi = polygon[i][1];
28+
const xj = polygon[j][0], yj = polygon[j][1];
29+
30+
const intersect = ((yi > point[1]) !== (yj > point[1]))
31+
&& (point[0] < (xj - xi) * (point[1] - yi) / (yj - yi) + xi);
32+
if (intersect) inside = !inside;
33+
}
34+
return inside;
35+
};
36+
37+
// Generate random points until we find one inside the polygon
38+
let attempts = 0;
39+
const maxAttempts = 1000; // Prevent infinite loop
40+
41+
while (attempts < maxAttempts) {
42+
const randomLon = bounds.minLon + Math.random() * (bounds.maxLon - bounds.minLon);
43+
const randomLat = bounds.minLat + Math.random() * (bounds.maxLat - bounds.minLat);
44+
45+
if (isPointInPolygon([randomLon, randomLat], polygon)) {
46+
return [randomLon, randomLat, 0]; // Adding 0 for elevation to match input format
47+
}
48+
49+
attempts++;
4050
}
41-
42-
return randomPoint;
51+
52+
// If we couldn't find a point after max attempts, return center of bounding box
53+
return [
54+
(bounds.minLon + bounds.maxLon) / 2,
55+
(bounds.minLat + bounds.maxLat) / 2,
56+
0
57+
];
4358
};
44-
45-
/**
46-
* Checks if a point is within a given polygon.
47-
*
48-
* @param {Array} point - The point coordinate [longitude, latitude].
49-
* @param {Array} polygon - An array of coordinate arrays representing the polygon.
50-
* @returns {boolean} True if the point is within the polygon, false otherwise.
51-
*/
52-
function isPointInPolygon(point, polygon) {
53-
const [x, y] = point;
54-
let isInside = false;
55-
56-
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
57-
const [x1, y1] = polygon[i];
58-
const [x2, y2] = polygon[j];
59-
60-
const intersect =
61-
y1 > y !== y2 > y && x < ((x2 - x1) * (y - y1)) / (y2 - y1) + x1;
62-
63-
if (intersect) isInside = !isInside;
64-
}
65-
66-
return isInside;
67-
}

0 commit comments

Comments
 (0)