Commit 06d5045c authored by Samarakoon S.M.A.D IT20233808's avatar Samarakoon S.M.A.D IT20233808

Merge branch 'feature/IT20233808/soilBasedCropRecommendation' into 'master'

pest detection component added

See merge request !15
parents 511a4013 ce348831
......@@ -15,7 +15,12 @@ import AgriExpertSolution from './components/agriExpertComponent/AgriExpertSolut
import NutrientSlider from './components/agriExpertComponent/NutrientSelect';
import CustomCrop from './components/recomendationComponentsBySoil/CustomCrop';
import CustomCropSelect from './components/recomendationComponentsBySoil/CustomCropSelect';
import PestDetectOptions from './components/Pest_management/PestDetectOptions';
import uploadPhotoDetect from './components/Pest_management/uploadPhotoDetect';
import DetectByVideo from './components/Pest_management/DetectByVideo'
import AgriExpert from './components/Pest_management/AgriExpert'
import PestControlInfo from './components/Pest_management/PestControlInfo';
import GetInstructions from './components/Pest_management/GetInstructionsl';
export default function App() {
const Stack = createNativeStackNavigator();
......@@ -36,6 +41,12 @@ export default function App() {
<Stack.Screen name='Agri Expert' component={AgriExpertSolution} />
<Stack.Screen name='Test Custom Crop' component={CustomCrop} />
<Stack.Screen name='Custom Crop Select' component={CustomCropSelect} />
<Stack.Screen name="PestDetectOptions" component={PestDetectOptions}/>
<Stack.Screen name="UploadImage" component={uploadPhotoDetect}/>
<Stack.Screen name="CaptureImage" component={DetectByVideo}/>
<Stack.Screen name="AgriExpert" component={AgriExpert}/>
<Stack.Screen name="PestInfo" component={PestControlInfo}/>
<Stack.Screen name="GetInstructions" component={GetInstructions}/>
</Stack.Navigator>
</NavigationContainer>
......
import React from 'react';
import { ViroARScene, ViroARImageMarker, ViroText } from 'react-viro';
const ARScene = () => {
return (
<ViroARScene>
<ViroARImageMarker target="assets\220px-Weisse-Fliege.jpg">
<ViroText
text="Recognized Image!"
position={[0, 0, -1]}
style={{ fontSize: 0.2, color: '#FFFF00' }}
/>
</ViroARImageMarker>
</ViroARScene>
);
};
export default ARScene;
import React, { useState } from "react";
import { View, Text, TextInput, Button, StyleSheet, Image } from "react-native";
import RNPickerSelect from "react-native-picker-select";
export default function AgriExpert() {
const [method, setMethod] = React.useState("");
const [name, setName] = React.useState("");
const [selectedWeather, setSelectedWeather] = useState([]);
const [selectedDuration, setSelectedDuration] = useState([]);
const [selectedDamage, setSelectedDamage] = useState([]);
const handleWeather = (itemValue) => {
setSelectedWeather(itemValue);
};
const handleDamage = (itemValue) => {
setSelectedDamage(itemValue);
};
const handleDuration = (itemValue) => {
setSelectedDuration(itemValue);
};
const handleSubmit = async () => {
try {
const requestBody = {
method,
name,
weather: selectedWeather.split(',').map((w) => w.trim()),
duration: selectedDuration.split(',').map((d) => d.trim()),
damage: selectedDamage.split(',').map((d) => d.trim()),
};
const apiResponse = await fetch('http://192.168.152.59:5000/add_data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
}).then(response => response.json)
.then((data)=>{
console.log("Successfuly added")
})
} catch (error) {
console.log('Error:', error);
}
}
return (
<View style={styles.container}>
<Image
source={require("../../assets/backgroundSoilData.jpg")}
style={styles.backgroundImage}
/>
<View style={styles.buttonContainer}>
<Text style={styles.title}>Add Methods</Text>
<Text style={styles.label}>Pest name: </Text>
<TextInput
style={styles.input}
onChangeText={setName}
value={name}
/>
<Text style={styles.label}>Control method: </Text>
<TextInput
style={styles.input}
onChangeText={setMethod}
value={method}
/>
<Text style={styles.label}>Weather: </Text>
<RNPickerSelect
onValueChange={handleWeather}
items={[
{ label: "Sunny", value: "sunny" },
{ label: "Rainy", value: "rainy" },
{ label: "Foggy", value: "foggy" },
]}
placeholder={{
label: "Select options...",
value: null,
}}
value={selectedWeather}
multiple
/>
<Text style={styles.label}>Duration: </Text>
<RNPickerSelect
onValueChange={handleDuration}
items={[
{ label: "Short", value: "short" },
{ label: "Mid", value: "mid" },
{ label: "Long", value: "long" },
]}
placeholder={{
label: "Select options...",
value: null,
}}
value={selectedDuration}
multiple
/>
<Text style={styles.label}>Damage: </Text>
<RNPickerSelect
onValueChange={handleDamage}
items={[
{ label: "Low", value: "low" },
{ label: "Average", value: "average" },
{ label: "High", value: "high" },
]}
placeholder={{
label: "Select options...",
value: null,
}}
value={selectedDamage}
multiple
/>
<Button title="Submit" onPress={handleSubmit} />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
scrollContainer: {
flexGrow: 1,
justifyContent: "center",
alignItems: "center",
},
backgroundImage: {
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%",
},
title: {
fontSize: 24,
fontWeight: "bold",
marginBottom: 50,
},
label: {
fontSize: 18,
fontWeight: "bold",
marginBottom: 0,
textAlign: "left", // Align the text to the left
},
input: {
height: 40,
width: 250,
margin: 12,
borderWidth: 1,
padding: 10,
},
buttonContainer: {
backgroundColor: "rgba(255, 255, 255, 0.8)",
borderRadius: 10,
padding: 20,
justifyContent: "space-between",
alignItems: "center",
width: "80%",
maxWidth: 400,
},
});
import React, { useEffect, useRef, useState } from "react";
import {
View,
Button,
StyleSheet,
Text,
Image,
ActivityIndicator,
} from "react-native";
import { Camera } from "expo-camera";
import firebase from "firebase/compat/app";
import "firebase/compat/storage";
import pestData from './PestData.json';
const firebaseConfig = {
// Add your Firebase configuration here
apiKey: "AIzaSyBX0highSIXvOLxH9Z20CQRyXrGYZj-GfY",
authDomain: "pest-detection-d63bd.firebaseapp.com",
projectId: "pest-detection-d63bd",
storageBucket: "pest-detection-d63bd.appspot.com",
messagingSenderId: "1096191342865",
appId: "1:1096191342865:web:4b2c237166dd4b2d4e34b0",
measurementId: "G-2ZH7JPF0DE",
};
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
function CameraComponent() {
const cameraRef = useRef(null);
const [icon, setIcon] = useState("");
const [detections, setDetections] = useState([]);
const [selectedImage, setSelectedImage] = useState(null);
const [captured, setCaptured] = useState(false);
const [upload, setUpload] = useState(false);
const [pestName, setPestName] = useState("");
const [pestImage, setPestImage] = useState("");
const [pestMethods, setPestMethods] = useState([]);
const [details, setDetails] = useState(false);
useEffect(() => {
(async () => {
const { status } = await Camera.requestCameraPermissionsAsync();
if (status !== "granted") {
console.error("Camera permission denied");
}
})();
}, []);
const captureImage = async () => {
if (cameraRef.current) {
try {
const photo = await cameraRef.current.takePictureAsync();
setSelectedImage(photo.uri);
setCaptured(true);
} catch (error) {
console.error("Error capturing photo:", error);
}
}
};
const uploadImage = async () => {
if (selectedImage) {
const response = await fetch(selectedImage);
const blob = await response.blob();
const storageRef = firebase.storage().ref();
const imageRef = storageRef.child("images/" + new Date().getTime());
try {
setUpload(true)
await imageRef.put(blob);
const downloadURL = await imageRef.getDownloadURL();
console.log("Firebase Image URL:", downloadURL);
const apiResponse = await fetch("http://192.168.152.59:5000/link", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ image_url: downloadURL }),
});
if (apiResponse.ok) {
const data = await apiResponse.json();
setIcon(`data:image/png;base64,${data.annotated_image}`);
setUpload(false)
setDetections(data.detections);
} else {
console.log("API Error:", apiResponse.status);
}
} catch (error) {
console.log("Error:", error);
}
}
};
function getPestData(pestName) {
if (pestName === 'Mealy Bug') {
setPestName(pestData.pests[0].name);
setPestImage(pestData.pests[0].image);
setPestMethods(pestData.pests[0].methods);
} else if (pestName === 'whitefly') {
setPestName(pestData.pests[1].name);
setPestImage(pestData.pests[1].image);
setPestMethods(pestData.pests[1].methods);
} else if (pestName === 'Ash weevil') {
setPestName(pestData.pests[2].name);
setPestImage(pestData.pests[2].image);
setPestMethods(pestData.pests[2].methods);
} else if (pestName === 'Fruit fly') {
setPestName(pestData.pests[3].name);
setPestImage(pestData.pests[3].image);
setPestMethods(pestData.pests[3].methods);
} else if (pestName === 'Uroleucon compositae') {
setPestName(pestData.pests[4].name);
setPestImage(pestData.pests[4].image);
setPestMethods(pestData.pests[4].methods);
}
setDetails(true);
}
return (
<View style={styles.container}>
<Image source={require('../../assets/backgroundSoilData.jpg')} style={styles.backgroundImage} />
{!details && !icon && !captured && (
<Camera style={styles.camera} ref={cameraRef}>
<Text>Itentified pests and confidence</Text>
</Camera>
)}
{!details && captured && icon == "" && (
<Image
style={{ width: 400, height: 300 }}
source={{ uri: selectedImage }}
/>
)}
{icon && !details &&(
<View style={styles.resultContainer}>
<Image style={styles.resultImage} source={{ uri: icon }} />
<Text style={styles.resultText}>Identified pests and confidence</Text>
{detections.map((item, index) => (
<View key={index} style={styles.detectionItem}>
<Text style={styles.detectionText}>
{index + 1}. {item.class_name} (confidence: {item.confidence})
</Text>
<Button title="View More" onPress={() => getPestData(item.class_name)} />
</View>
))}
<Text style={styles.resultText}>No of objects detected: {detections.length}</Text>
</View>
)}
{!captured && !details && (
<Button
style={styles.button}
title="Capture Photo"
onPress={captureImage}
/>
)}
{captured && !icon && !details && (
<Button
style={styles.button}
title="Upload image"
onPress={uploadImage}
></Button>
)}
{upload && !details && (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#007AFF" />
<Text style={styles.loadingText}>Analyzing...</Text>
</View>
)}
{details && (
<View style={styles.detailsContainer}>
<Text style={styles.title}>Pest Details</Text>
<Text style={styles.label}>Name:</Text>
<Text style={styles.text}>{pestName}</Text>
<Image source={{ uri: pestImage }} style={styles.image} />
<Text style={styles.label}>Control Methods:</Text>
<View style={styles.methodList}>
{pestMethods.map((method, index) => (
<View key={index} style={styles.methodItem}>
<Text style={styles.bulletPoint}></Text>
<Text style={styles.methodText}>{method}</Text>
</View>
))}
</View>
<Button title="Go Back" onPress={() => setDetails(false)} />
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
camera: {
width: 400,
height: 600,
},
backgroundImage: {
flex: 1,
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%",
opacity: 0.8,
},
buttonContainer: {
backgroundColor: "rgba(255, 255, 255, 0.8)",
borderRadius: 10,
padding: 20,
justifyContent: "space-between",
alignItems: "center",
width: "80%",
maxWidth: 600,
},
button: {
backgroundColor: "#3498db",
borderRadius: 5,
paddingVertical: 10,
paddingHorizontal: 20,
marginBottom: 20,
width: 250,
alignItems: "center",
marginVertical: 20,
},
buttonText: {
color: "black",
fontSize: 16,
fontWeight: "bold",
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
scrollContainer: {
flexGrow: 1,
justifyContent: 'center',
alignItems: 'center',
},
backgroundImage: {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 16,
},
image: {
width: 300,
height: 300,
marginVertical: 10,
},
resultContainer: {
width: '100%',
maxWidth: 500,
padding: 0,
borderRadius: 10,
backgroundColor: 'rgba(255, 255, 255, 0.8)',
margin: 0,
alignItems: 'center',
},
resultImage: {
width: 400,
height: 300,
},
resultText: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 8,
},
detectionItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 8,
},
detectionText: {
flex: 1,
fontSize: 16,
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
loadingText: {
fontSize: 18,
marginTop: 10,
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
marginVertical: 10,
},
button: {
backgroundColor: '#3498db',
borderRadius: 5,
padding: 10,
width: 150,
alignItems: 'center',
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
detailsContainer: {
width: '100%',
maxWidth: 400,
padding: 0,
borderRadius: 10,
backgroundColor: 'rgba(255, 255, 255, 0.8)',
margin: 0,
alignItems: 'center',
},
label: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 8,
},
text: {
fontSize: 16,
marginBottom: 8,
},
methodList: {
width: '100%',
},
methodItem: {
flexDirection: 'row',
alignItems: 'flex-start',
marginBottom: 8,
},
bulletPoint: {
fontSize: 16,
fontWeight: 'bold',
marginRight: 8,
},
methodText: {
fontSize: 16,
},
});
export default CameraComponent;
import React, { useState } from "react";
import { View, Text, TextInput, Button, StyleSheet, Image } from "react-native";
import RNPickerSelect from "react-native-picker-select";
import pestData from './PestData.json';
export default function GetInstructions() {
const [recomdations, setRecomdations] = React.useState([]);
const [value, setValue] = React.useState(false);
const [name, setName] = React.useState("");
const [selectedWeather, setSelectedWeather] = useState([]);
const [selectedDuration, setSelectedDuration] = useState([]);
const [selectedDamage, setSelectedDamage] = useState([]);
const [pestImage, setPestImage] = useState("");
const [pestName, setPestName] = useState("");
const handleWeather = (itemValue) => {
setSelectedWeather(itemValue);
};
const handleDamage = (itemValue) => {
setSelectedDamage(itemValue);
};
const handleDuration = (itemValue) => {
setSelectedDuration(itemValue);
};
function getPestData(pestName) {
if (pestName === 'mealybug') {
setPestName(pestData.pests[0].name);
setPestImage(pestData.pests[0].image);
} else if (pestName === 'whitefly') {
setPestName(pestData.pests[1].name);
setPestImage(pestData.pests[1].image);
} else if (pestName === 'Ash weevil') {
setPestName(pestData.pests[2].name);
setPestImage(pestData.pests[2].image);
} else if (pestName === 'Fruit fly') {
setPestName(pestData.pests[3].name);
setPestImage(pestData.pests[3].image);
} else if (pestName === 'Uroleucon compositae') {
setPestName(pestData.pests[4].name);
setPestImage(pestData.pests[4].image);
}
}
const handleSubmit = async () => {
try {
const requestBody = {
"name": name,
"weather":selectedWeather,
"duration":selectedDuration,
"damage":selectedDamage,
};
const apiResponse = await fetch('http://192.168.152.59:5000/recommendations', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
})
console.log(requestBody)
if (apiResponse.ok) {
getPestData(name)
const responseData = await apiResponse.json(); // Parse response as JSON
console.log('Response Data:', responseData["Recommended Methods"]);
setRecomdations(responseData["Recommended Methods"])
setValue(true)
} else {
console.error('API request failed:', apiResponse.status);
}
} catch (error) {
console.log('Error:', error);
}
}
return (
<View style={styles.container}>
<Image
source={require("../../assets/backgroundSoilData.jpg")}
style={styles.backgroundImage}
/>
{!value && (
<View style={styles.buttonContainer}>
<Text style={styles.title}>Get Instructions</Text>
<Text style={styles.label}>Pest name: </Text>
<TextInput
style={styles.input}
onChangeText={setName}
value={name}
/>
<Text style={styles.label}>Weather: </Text>
<RNPickerSelect
onValueChange={handleWeather}
items={[
{ label: "Sunny", value: "sunny" },
{ label: "Rainy", value: "rainy" },
{ label: "Foggy", value: "foggy" },
]}
placeholder={{
label: "Select options...",
value: null,
}}
value={selectedWeather}
multiple
/>
<Text style={styles.label}>Duration: </Text>
<RNPickerSelect
onValueChange={handleDuration}
items={[
{ label: "Short", value: "short" },
{ label: "Mid", value: "mid" },
{ label: "Long", value: "long" },
]}
placeholder={{
label: "Select options...",
value: null,
}}
value={selectedDuration}
multiple
/>
<Text style={styles.label}>Damage: </Text>
<RNPickerSelect
onValueChange={handleDamage}
items={[
{ label: "Low", value: "low" },
{ label: "Average", value: "average" },
{ label: "High", value: "high" },
]}
placeholder={{
label: "Select options...",
value: null,
}}
value={selectedDamage}
multiple
/>
<Button title="Submit" onPress={handleSubmit} />
</View>
)}
{value && (
<View style={styles.detailsContainer}>
<Text style={styles.title}>Pest Instructions</Text>
<Text style={styles.label}>Name:</Text>
<Text style={styles.text}>{pestName}</Text>
<Image source={{ uri: pestImage }} style={styles.image} />
<Text style={styles.label}>Control Methods:</Text>
<View style={styles.methodList}>
{recomdations.map((method, index) => (
<View key={index} style={styles.methodItem}>
<Text style={styles.bulletPoint}></Text>
<Text style={styles.methodText}>{method}</Text>
</View>
))}
</View>
<Button title="Go Back" onPress={() => setValue(false)} />
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
scrollContainer: {
flexGrow: 1,
justifyContent: "center",
alignItems: "center",
},
backgroundImage: {
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%",
},
title: {
fontSize: 24,
fontWeight: "bold",
marginBottom: 50,
},
label: {
fontSize: 18,
fontWeight: "bold",
marginBottom: 0,
textAlign: "left",
},
input: {
height: 40,
width: 250,
margin: 12,
borderWidth: 1,
padding: 10,
},
buttonContainer: {
backgroundColor: "rgba(255, 255, 255, 0.8)",
borderRadius: 10,
padding: 20,
justifyContent: "space-between",
alignItems: "center",
width: "80%",
maxWidth: 400, // Align the text to the left
},
methodList: {
width: '100%',
},
methodItem: {
flexDirection: 'row',
alignItems: 'flex-start',
marginBottom: 8,
},
bulletPoint: {
fontSize: 16,
fontWeight: 'bold',
marginRight: 8,
},
methodText: {
fontSize: 16,
},
image: {
width: 300,
height: 300,
marginVertical: 10,
},
detailsContainer: {
width: '100%',
maxWidth: 400,
padding: 0,
borderRadius: 10,
backgroundColor: 'rgba(255, 255, 255, 0.8)',
margin: 0,
alignItems: 'center',
},
});
import React, { useState } from 'react';
import { View, Text,Image, ScrollView, StyleSheet } from 'react-native';
import pestData from './PestData.json'
function PestControlInfo({ pestName }) {
const [pestInfo, setPestInfo] = useState(null);
const pest = pestData.pests.find((p) => p.name.toLowerCase() === pestName.toLowerCase());
setPestInfo(pest);
console.log(pest)
return (
<ScrollView>
<View style={styles.container}>
{/* <View>
<Text style={styles.heading}>Name: {pestInfo.name}</Text>
<Image style={styles.image} source={{ uri: pestInfo.image }} />
<Text style={styles.heading}>Control Methods:</Text>
<ul>
{pestInfo.methods.map((method, index) => (
<Text key={index} style={styles.method}>
- {method}
</Text>
))}
</ul>
</View> */}
</View>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
padding: 20,
},
input: {
borderWidth: 1,
borderColor: '#ccc',
padding: 10,
width: '80%',
marginBottom: 10,
},
heading: {
fontSize: 18,
fontWeight: 'bold',
marginTop: 10,
},
image: {
width: 200,
height: 150,
resizeMode: 'cover',
marginVertical: 10,
},
method: {
marginLeft: 20,
marginBottom: 5,
},
});
export default PestControlInfo;
{
"pests": [
{
"name": "mealybug",
"image": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e1/Mealybugs_on_flower_stem%2C_Yogyakarta%2C_2014-10-31.jpg/220px-Mealybugs_on_flower_stem%2C_Yogyakarta%2C_2014-10-31.jpg",
"methods": ["Apple Cider Vinegar", "Diatomaceous Earth", "Pruning", "Soap and Water", "Beneficial Insects", "Essential Oils, Neem Oil"]
},
{
"name": "whitefly",
"image": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5b/Weisse-Fliege.jpg/220px-Weisse-Fliege.jpg",
"methods": ["insecticidal soap", "Spray Neem oil", "Use yellow sticky whitefly traps for whitefly control","Introduce natural predators or beneficial bugs such as female beetles", "tiny parasitic wasps"]
},
{
"name": "Ash weevil",
"image": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a3/Eastern_Pine_Weevil_%28Pissodes_nemorensis%29.jpg/330px-Eastern_Pine_Weevil_%28Pissodes_nemorensis%29.jpg",
"methods": ["Collect and destroy adults", "Apply Neem cake @ 500 kg/ha at the time of last ploughing", "In endemic areas, apply carbofuran 3 G @15 kg/ha on 15 days after planting","Spray carbaryl 50 WP @3g + wettable sulphur 2g/litre"]
},
{
"name": "Fruit fly",
"image": "data:image/jpeg;base64,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",
"methods": ["Appropriate selection of plants that are not susceptible to fruit fly attack.", "Control of regrowth of removed host plants and fruit trees that may occur.", "Selection of plants or trees that are suited to your location.","Proper care and maintenance of new plants in your garden."]
},
{
"name": "Uroleucon compositae",
"image": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIAKAAxwMBIgACEQEDEQH/xAAcAAABBQEBAQAAAAAAAAAAAAAAAQMEBQYHAgj/xABIEAABAwMCAwQGBQgGCwEAAAABAAIDBAUREiEGMUETUWGBBxQicZGhIzJSscEVJEJicoKSojM0Q1PR8CVUVWNzdJOVstPhFv/EABsBAAICAwEAAAAAAAAAAAAAAAABAgMEBQYH/8QAIREAAgICAgIDAQAAAAAAAAAAAAECEQMhBDESQSIyQhP/2gAMAwEAAhEDEQA/ANwE9GMkJkKREN15ZBXJI6GXRLhG4U+AKHCNwp8AXU8DHs1+Zj4SpG78l65DJ5d66GKMFghQam9Wmk/rV0oYf+JUsb+KgScZ8MRfXv1v8pwfuVlMiXqFm3cfcJtODfaX+b/BJD6QOEZn6WcQUOc49qTTn4p0wNKlUakrqOuY2SiqoKiNwy18MgeD8FJ8kCBCQnCzt14o01j7bw/RPu9yYQJGRu0wwE/3snIfsjJ8EIDRqpruI7RQT+rTV8T6rcClh+lmO2caG5PyVazh663Qh/Et4cY/9QtoMEI8HOzrf8QPAq8tlqt9op/V7XRU9JF9mGMNz7+9SGQm3S5Vf9QskrW7fSV8wgaR3hoDneRATuniCTft7VAfCGWXHnqYrTCECKn1a/8A+1Lb/wBuf/7l71XyF3tRW6rb3skfA74EPHzCs0IArvyo+Mn1q3VsIG5c1glb/ISfknqW5UNUQynqonSH+zLtL/4Tv8lLXmaKOoZ2dRGyVh5tkaHA+RQB6R0yoooYmf1d0kB6dm/b+E7JQKyM7mGob0JzG4feD8kASUJuKUv2dE+MgfpY+8FCAMq0KTEFG9rSdABd0BOF7ipXy71E73fqR+w0fifj5LzPjw8pG/yMfkudJTzdgXmSpxkQQtL3nyHIeJwFIhqLrUt/N6KGlB2D6t+ojx0M5+7UF7o4IoGaII2xt7mgBWMXJdbwa7RrszITbdWTAeu3ad/MFtNGIG/i7+ZKbBa3j84pG1P/ADLnS/8AkSrHp4Kvqr3QU8nYtl7efl2UAMj8+IC3HlXZi030Sqeho6YYpqSnhHdHE1v3BSAT0yPcqOqlv9fFptscFtDv7aqb2r2jwYCBn3lZK+Q+kex1fr9sqqe+0ojb2tM6EMc4g7lrQeeMcj5KcakKSo6UHH7R+KbmhinbpqIY5W90jQ4fNY7g70i2riOUUM4dbbs06X0dTtl3UNJxk+Gx8FtRjopbI9HPvSTwpRR2aa/2alZR3a3M7WOWmaWFzR9YHTjp/nBKsPRxxU2+2OnirpC26QwsM7ZAGmQHIDx3g4V3xTWigs00sjMxOxFITGXhrXbEkDwPzXz7LUMtpZSS0YnqIoy+U1sT8jlpxgg7j5YwqsmRxqlZk4MP9Yu2bz0i8X112vVv4Y4XqHBlZI6CpnY0gPJdpLWu+y0aiSF0ywWejsNpp7Zbo9FPA3AGN3Hq4+JXIvR/NSDiOirtIMLw2KCORhAZK/DXaDyyAd/Artud1LHPzVleaH8n4oEIVbeb9aLFE2S8XGmpNWdIlkAc/wBzeZ8grUUlkhZC3+knhu4SvZST1UjGOLXTCjkMY8S4DAB6ZWlguNDUYENVCS76rS/Dj5FDdDpkpCPEckIECEIQAIQhAAhCEwMo1SoRyUZiamtEU8cjIKiqpDL9d1PJgn45wfEbrzfhRTls3uRkusvVutrmMqagdvJ/RwRgySv9zG5JT0FXdaxv5rQiljP1ZKs+1/AOXmfJebLZ6C0xdnb6VkOrdzxu956lzjuT4lXUYwF1nEivz0a/I67K42VtT7Vzq56vr2erRGP3Rz8yp9JSU9JH2dLAyFvLDAAnkoWzjBJmM5tghZTinj21cPSS05jqa6rhLe3gpIy4wscM6nHkOm2eoVPTemThKZ7WSzVdOT/eQHA9+FakQZfcV8E2jiUtmqYzDXMwWVcOzxjv+0Pn3EL1bpblZIo6a5uNTA3DWzjcjzPPz38T0srPf7RfIy+0XKlrA0AuEUgLm55am8xy6hPXWrjobfUVMzO0bGwns8ga/wBXfvUJRvdk4zaXi+jB+ma6OZwxBDR1EQjqJgJeri0csDnnPMeB7lzjiG21F3prVVUpllY6VtO9zcSDJ5YPhjke/op1dabpxHK+uqu1gpZJi6SCOQHsXsaQA0Pdz5jPhyVVWTwcO07IIBcDKKhk7pDNG0OezlpaGHYdzj37b5VTkpyTT2ZkI+EGi9gqa6zcZWKGJsrTE9sGiZ3tDWcEAZHP3b9Mldxq6yCjjL6iVrG92dz7gvnVthbBIy409VVs0zNexkjA5zXZz9ZpAccdy6X6M7m68xyU94Yamrp8fnL36mvP2SOjhv8ADpySxSUV4xdsjng5fJrSJ18uXFV8/NOFYG0dO46X183Mfs9Phk+5Q7L6JLPFUGv4jqKi9XB51PdO8iPPu5nzJ5cl0UAAAN2b0AQsqKa7dmJKaekqGKCipLdSspaCnjpoGfVjibpaPgvM1vo5mlstLC4E7+wN1JQpNJ9kU2uis/IsEZzR1FVSn/dSZb8HZHwwvIbe6bk6mr27AavoX+O4yPkrVKo+C9D837KWXiOkonabxFPbh/fTszD/ANQZaO/chWtPPDUwtmppo5onjLZInhzXDwI5pwgFpbjY7Ed4VHUcLUHrRq7ZJPaqlxzI+hcGCX9thBafeRnbu2MkhN2XiFGoaCmoWkU7CC76z3uLnOPeSeZUlMQIQhAGWZzUyLkocfNTIui844S2bzITYBupjQosA3Clhdhw18TW5nsVQLrWup2Mp6XDq2c4ibzx3uPgE9X1sdBTunly7GzGN+s932R4qNaKKVrnV9ePz2YYI6RN6NH+f8TnN+kUxXtjtrtdPb6Z0bWNdJK4vnkIyZHHmSkrLHaa6IxVlso5mHmHwNP4KehWrSIN2Yat9FXDctR6zbfXLTUDOHUM5aPgc4HgMKpu10qrBc2WupvDqqOGESONQwuEucgNIGT0z9YBdMnf2cTnnkBk7Z2XLeO7nHw/eWX71WCpirmNhidPET2bmZOoDYHUDsfDbKqyq1rsvwS3T6Mc0cU3XiD1mxtp6WWQNZ2VNWxnSAMB5aXau/fTnbGFsbzwdVVfC9xFwqmVdyZSt7NtPGD9I06icAZJccDyHenuCqybi54utwpKan9SnAoZYY3RF8ekh7eZJaCW9cZW600sZNRL2Mcmgl78gHGBnJ8vklXuicpNOkcruPCNxqKSeaQBtQezdSVzatrYGkuy441Z9wxjf4XFFfLfwy2khju1HVlrWtlawjtZCCBsM/tHPVXHGFLJJR+uTQGtpKCI1LaOFxYJHgt0uON3YBeccvZzzWIoOIoeMZ6KzPpYXVTqgPFSRoLYw8OLNgc7ZG56d6TjVJE4vyTbO3scHDIXpQaKrjfVy0zdQewAkFpGR0I7xz+CnLJi7RhSVMEIQmIVIlQmAiEIQAIQhAAhCECMtFzU6JQY+anQ8l51weze5SdAnppo6aF807wyNjS5zj0ATMB3AVK154lu5DTqs1ulw49KqpadwO9jNvAu2/RXYcNfA1mT7FhRQyV1Uy41bXMawn1aF2+gfaPifkrYJN1zzibiTi6tvVVZ+CKCllbSvZFUV0rh9C8jJGHEDbI3wfis6ESmTtm+qqumo4XTVc8UETRlz5XhoA95WQuXpT4To5fV6evfcKg7NiooXSaj+1jB8iVTUvonNze2r40vtbdKlxDnRxvLIx4D/wCYW4snDVksMQjtFsp6YD9JrcuPiXHJJ81ZSREprde79fHCZlrfb6TTqYJsCV3dz5bZ6dRuq+r4Tpb1emVnEMM74I4miOJ0/said2kAk92cED3rX17m00xqGxue97dOGczjOAM7d6agqmPe3to5aeQktayQ41bnlvgn2c9+PeqGvl2XqWtIbpaSClpILeGQhjG6Y2Mj7NuGnoOmNk9GKEVL3MdE6Y5L3ZBO2Ac93RLSyxvxG6aN8wadRY3GcHBON+uy9dpD9PGdQEQzK3szvnfu9rYHllSogiOI6FrC/FOBGdRc0glpIyTkcsg581kavgxls4jhu/D876WUza5aVkJMcreRBPeS48yBg7YxlbOGopHURqIXBsODrdp04wOoxsvE0rYJtNTI7s5nBrCGH2ScADI7yeZwn6JJ0yLar3HUVUVJUwupqwgO7Nzg4HvAI7t1oVg+J7E3iWqhZTx1Nvr6ZzjFX506Htxp5H2mnPPwKr7bx/cuGqyOz+kSkdA8+zDdImaoph3ux193wCliutkcqj6OmITdPPDVU7J6WVk0MjdTJIzlrh3gpxWFQJUiVMBEIQgBUJEIAAhCECMtHzU2I7KDHzU2HovOeC6kb3KLJS18/bdjcRA1zSxrWQglmeuSefyU602+C1W2moKRpbFTsDW5OSe8k9SeZRBzypYXZcSVwNXl+wqpuHooo6y8vibpMtY5z/F3LPyCuFVcPhubkWYwa6XOPfn8Vl/pEF0y3QkSq1FYxWRtkiLXgFpGCD1BVOy40MkAe2cyR5OnTqJOBkgEbjb7/FXsoLo3AHBI2Wer55RVGBsLy0NbI1wkDAfa5d/Poq8mtlmPeifTzucY9YL+2LnRuZGQA3mM55HGO7O+yJpZIoZHGGWRsbebQD2ueYaM5z78KJR1ZqnSuZJ9FktZpLcAgkEg8/I93xlyy1TDphoy6BjW6cyDU/n3nIxgc+9CdobVM9skkc1rzA5rtWHDbLRvh2x5be/fkmvWGwud2s5OdRa1zQMY93PH49Us4mMD5G+xL2ThjWBvjbfcDBPPBVddK+Siw7XDIxjw0tL2sOcb5cds+GyGxpWyVw9JBUvmmpmz6HOD+0m1jXqbnLQ7cAZxjbBB2Vhd7TQXq3y0F0pWVFNKMOY/7weYPiN0trD/AFZr5AQ9zQXAnOCd8Z+CmK2CpFU3bOO1FFf/AET1nrdtkluXCT5MzUzt30+Tz8PeNj1Gd10/h6/W7iO2x3C01AmgfsejmO6tcOhCsJmxvhe2YNMZadQcMgjrlZ/gajkpaGvldhsVTXSywRAACNmwA29x/wAlStWKtWaNCEJiBCEIAEIQgAQhCAMqxTIFBYpkJXmvDdTN9kLCDmpo5KBCdwpzDkLseDK4mrzLZ7G5VXw+D6rO7bS+pkc3HkrLlv3bqs4ZH+h4j9p8h/ncth+kUr6stQhCFaQF3XF/Sl65e/SFZuHrXPLC+OPVLLC4gsDyC4kjuDV2j37Bct9GdJ+X+K+J+L5/aEtS6lonYxhgPMfuhg+KkCGuFIOKbPfK+iuj5K6CNoNJJLNo1tBcNQI59Mg5xzC3cta6Nwb2Jex36QlZ7HxP3KBxPZ23Ckqt5DUGMshOrSYXfqOG4ztnfoFyipbxBMTTS1N0ZJEdLGNLg4nx3wT4rFnPwZmY4LKuzsNVWNdb5HSRMMrW6mMlILdYGR3dfBccu/CHEdx4buN8uT533BkjZ44tZw+MZ1O0/a2BAxsAPcOj2fh+pko6M3C53BssDcTMZLpEmTkhx556ZBzzWutlFLT04bU1D6iTLvpJGtBIJOxAGORwrcdvbKslRTiim9G3ETOJuEqOt1A1Mf0NSBzErefxGHea1C5Zaox6P/SW+2f0dj4g9um39mKfP1fDfb95vjjqfyVxQ77Id4qPVbbPLnDtOlvvO34py304pKGCAZ9hgG/f1+eVCu35xX0FDgEOkM0mfssH4nbzVqox+1kn9UgQhCmQFSIQgBUJEqABIlSIAyTVJgJworVIhK8wwOsh0E0WMBU+I7BVsJU+Err+BM1udHubaGQ5x7J+5QOGc/kGi9oHMec+8lWEwzE8bHLSN/cq7heQScP0Lgc4i0E+LSWn5hbZfYxvwy1QgIVxWZz0iXoWHg65VodiV0XYwjqXv2H4nyTnAVl//P8ACNtt72hszYg+cd0jvad8M48lnb1p4v8ASDSWQNLrfYCKytJ2a+c/0bPHGT/MuhJjfVEetpzUxFrZXRv6PbzC4+30e3O4Orq+6XJ9LU073A6p3b6RnUSPqgjB2zz5Dku0LmHpYqH0V1tjhK+OlqInirbrw2VkckexHg2R4z3HwVc4rstwt34o1XA1nrbXZKRl0qJpKjshqjlk7TQck8+/Bx5BaVB+s7PUlCtSSKZSt2Zvj7hwcS8Py00OllfCRNRyn9CVu436A8knAPEp4ksYfUsMVypHer10LhgskHX3HmtKsTe7JXWniuHiWwdkyOdpZdoXu0tlaBkSD9YYTetjW9Ggt4NTerhVnOmPTTx79AMu+Z+GFbKu4fgMFqh15MkhMryTndxz8gQPJWKUVSFJ2xUJEqkRESoQgAQhCABCEIGY5qfjPJRmFPsK8sTp2dHNFhCVOgPJVsLlNhdhdLwcvRgZok4d6ouEXCCO52tzjroK+Vu/2JPpWfKTHkrtpyFQXl0lmvdNe2AmhmaKW4AAksGfo5f3XEg+Ds8gukg/IwHZpELyOWy9KxMgeGxRMe6RkTGvfjW4NALscsnqvaEKYhVl+OrC+9U1CYI9U1PVMcMfZccO8uRPuWoQhpPTCMnF2gHvPnuhCFIQKpv8gkjpqAfXrJwzHXQPacfIBWyzdpcb1xFU3cOzQ0OuipN8iR+R2snxGgfsu70nvRKOtmkAAADRgDohCFIiKhCECBCEIAEIQgAQhCAMUwp5hUeNPNK8skjp5ImQuU2JyronbqZE5bPhZTDyRLKJ226aqqM1VVTuldmnhLnmI8nv5NJHUDc+/B6BJE5SWnYLquNktGuyRpjoSrwCvSzYspYqEIVpEVCEJiBCEKSAoOJKyrllislpDm1lYPpanSdNLD+k/PLV0aO/fkFcUFHT2+igo6ONsdPAwMjY3kAEz6s/8tGqa0CM0wjeeriH5b8Mu/iU1A2CUJEJiFQkQgQqEiEAKhIhACoSIQB//9k=",
"methods": ["safflower aphid including early sowing, intercropping with sorghum, coriander and wheat, and judicious use of insecticides including thiamethoxam, acetamipirid and imidacloprid"]
}
]
}
import {React,useState} from 'react';
import { View, TouchableOpacity, Text, StyleSheet, Image } from 'react-native';
const PestDetectOptions = ({navigation}) => {
return (
<View style={styles.container}>
<Image source={require('../../assets/backgroundSoilData.jpg')} style={styles.backgroundImage} />
<View style={styles.buttonContainer}>
<TouchableOpacity style={styles.button} onPress={() => {navigation.navigate("UploadImage")}}>
<Text style={styles.buttonText}>Upload Image</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={() => {navigation.navigate("CaptureImage")}}>
<Text style={styles.buttonText}>Capture Image</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={() => {navigation.navigate("GetInstructions")}}>
<Text style={styles.buttonText}>Get Instructions</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={() => {navigation.navigate("AgriExpert")}}>
<Text style={styles.buttonText}>AgriExpert</Text>
</TouchableOpacity>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
backgroundImage: {
flex: 1,
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
opacity: 0.8,
},
buttonContainer: {
backgroundColor: 'rgba(255, 255, 255, 0.8)',
borderRadius: 10,
padding: 20,
justifyContent: 'space-between',
alignItems: 'center',
width: '80%',
maxWidth: 400,
},
button: {
backgroundColor: '#3498db',
borderRadius: 5,
paddingVertical: 10,
paddingHorizontal: 20,
marginBottom: 10,
width: 250,
alignItems: 'center',
marginVertical: 20,
},
buttonText: {
color: '#fff',
fontSize: 16,
fontWeight: 'bold',
},
});
export default PestDetectOptions;
import React, { useState } from 'react';
import { View, TouchableOpacity, Image, Text, StyleSheet, ActivityIndicator, Button, ScrollView } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import firebase from 'firebase/compat/app';
import 'firebase/compat/storage';
import pestData from './PestData.json';
const firebaseConfig = {
apiKey: "AIzaSyBX0highSIXvOLxH9Z20CQRyXrGYZj-GfY",
authDomain: "pest-detection-d63bd.firebaseapp.com",
projectId: "pest-detection-d63bd",
storageBucket: "pest-detection-d63bd.appspot.com",
messagingSenderId: "1096191342865",
appId: "1:1096191342865:web:4b2c237166dd4b2d4e34b0",
measurementId: "G-2ZH7JPF0DE"
};
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
function UploadPhotoDetect() {
const [selectedImage, setSelectedImage] = useState(null);
const [icon, setIcon] = useState("");
const [detections, setDetections] = useState([]);
const [upload, setUpload] = useState(false);
const [details, setDetails] = useState(false);
const [pestName, setPestName] = useState("");
const [pestImage, setPestImage] = useState("");
const [pestMethods, setPestMethods] = useState([]);
const [count, setCount] = useState(0);
const pickImage = async () => {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== 'granted') {
console.log('Permission denied');
return;
}
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!result.canceled) {
setSelectedImage(result.assets[0].uri);
}
};
const uploadImage = async () => {
if (selectedImage) {
const response = await fetch(selectedImage);
const blob = await response.blob();
const storageRef = firebase.storage().ref();
const imageRef = storageRef.child('images/' + new Date().getTime());
try {
setUpload(true);
await imageRef.put(blob);
const downloadURL = await imageRef.getDownloadURL();
console.log('Firebase Image URL:', downloadURL);
const apiResponse = await fetch('http://192.168.152.59:5000/link', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ image_url: downloadURL }),
});
if (apiResponse.ok) {
const data = await apiResponse.json();
setIcon(`data:image/png;base64,${data.annotated_image}`);
setUpload(false);
setDetections(data.detections);
} else {
console.log('API Error:', apiResponse.status);
}
} catch (error) {
console.log('Error:', error);
}
}
}
function getPestData(pestName) {
if (pestName === 'Mealy Bug') {
setPestName(pestData.pests[0].name);
setPestImage(pestData.pests[0].image);
setPestMethods(pestData.pests[0].methods);
} else if (pestName === 'whitefly') {
setPestName(pestData.pests[1].name);
setPestImage(pestData.pests[1].image);
setPestMethods(pestData.pests[1].methods);
} else if (pestName === 'Ash weevil') {
setPestName(pestData.pests[2].name);
setPestImage(pestData.pests[2].image);
setPestMethods(pestData.pests[2].methods);
} else if (pestName === 'Fruit fly') {
setPestName(pestData.pests[3].name);
setPestImage(pestData.pests[3].image);
setPestMethods(pestData.pests[3].methods);
} else if (pestName === 'Uroleucon compositae') {
setPestName(pestData.pests[4].name);
setPestImage(pestData.pests[4].image);
setPestMethods(pestData.pests[4].methods);
}
setDetails(true);
}
return (
<View style={styles.container}>
<Image source={require('../../assets/backgroundSoilData.jpg')} style={styles.backgroundImage} />
<ScrollView contentContainerStyle={styles.scrollContainer}>
<Text style={styles.title}>Pest Detection</Text>
{selectedImage && !icon && (
<Image source={{ uri: selectedImage }} style={styles.image} />
)}
{icon && !details && (
<View style={styles.resultContainer}>
<Image style={styles.resultImage} source={{ uri: icon }} />
<Text style={styles.resultText}>Identified pests and confidence</Text>
{detections.map((item, index) => (
<View key={index} style={styles.detectionItem}>
<Text style={styles.detectionText}>
{index + 1}. {item.class_name} (confidence: {item.confidence})
</Text>
<Button title="View More" onPress={() => getPestData(item.class_name)} />
</View>
))}
<Text style={styles.resultText}>No of objects detected: {detections.length}</Text>
</View>
)}
{upload && (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#007AFF" />
<Text style={styles.loadingText}>Analyzing...</Text>
</View>
)}
{!icon && !upload && (
<View style={styles.buttonContainer}>
<TouchableOpacity style={styles.button} title="Pick an image" onPress={pickImage}>
<Text style={styles.buttonText}>Pick a photo</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} title="Upload image" onPress={uploadImage}>
<Text style={styles.buttonText}>Upload photo</Text>
</TouchableOpacity>
</View>
)}
{details && (
<View style={styles.detailsContainer}>
<Text style={styles.title}>Pest Details</Text>
<Text style={styles.label}>Name:</Text>
<Text style={styles.text}>{pestName}</Text>
<Image source={{ uri: pestImage }} style={styles.image} />
<Text style={styles.label}>Control Methods:</Text>
<View style={styles.methodList}>
{pestMethods.map((method, index) => (
<View key={index} style={styles.methodItem}>
<Text style={styles.bulletPoint}></Text>
<Text style={styles.methodText}>{method}</Text>
</View>
))}
</View>
<Button title="Go Back" onPress={() => setDetails(false)} />
</View>
)}
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
scrollContainer: {
flexGrow: 1,
justifyContent: 'center',
alignItems: 'center',
},
backgroundImage: {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 16,
},
image: {
width: 300,
height: 300,
marginVertical: 10,
},
resultContainer: {
width: '100%',
maxWidth: 500,
padding: 0,
borderRadius: 10,
backgroundColor: 'rgba(255, 255, 255, 0.8)',
margin: 0,
alignItems: 'center',
},
resultImage: {
width: 400,
height: 300,
},
resultText: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 8,
},
detectionItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 8,
},
detectionText: {
flex: 1,
fontSize: 16,
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
loadingText: {
fontSize: 18,
marginTop: 10,
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
marginVertical: 10,
},
button: {
backgroundColor: '#3498db',
borderRadius: 5,
padding: 10,
width: 150,
alignItems: 'center',
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
detailsContainer: {
width: '100%',
maxWidth: 400,
padding: 0,
borderRadius: 10,
backgroundColor: 'rgba(255, 255, 255, 0.8)',
margin: 0,
alignItems: 'center',
},
label: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 8,
},
text: {
fontSize: 16,
marginBottom: 8,
},
methodList: {
width: '100%',
},
methodItem: {
flexDirection: 'row',
alignItems: 'flex-start',
marginBottom: 8,
},
bulletPoint: {
fontSize: 16,
fontWeight: 'bold',
marginRight: 8,
},
methodText: {
fontSize: 16,
},
});
export default UploadPhotoDetect;
......@@ -6,7 +6,7 @@ const HomePage = ({ navigation }) => {
const handleOptionSelect = (option) => {
if (option === 'pestDisease') {
navigation.navigate('PestDiseasePage');
navigation.navigate('PestDetectOptions');
} else if (option === 'soilData') {
navigation.navigate('Soil Data Options');
} else if (option === 'weatherData') {
......@@ -82,4 +82,4 @@ const styles = StyleSheet.create({
},
});
export default HomePage;
export default HomePage;
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -25,7 +25,11 @@
"react-native-screens": "~3.20.0",
"react-native-snap-carousel": "^3.9.1",
"react-native-svg": "^13.9.0",
"react-native-web": "~0.18.11"
"react-native-web": "~0.18.11",
"expo-camera": "~13.4.2",
"expo-image-picker": "~14.3.2",
"react-native-multiple-select": "^0.5.12",
"react-native-picker-select": "^8.1.0"
},
"devDependencies": {
"@babel/core": "^7.20.0"
......
......@@ -2888,6 +2888,11 @@
prompts "^2.4.0"
semver "^6.3.0"
"@react-native-picker/picker@^1.8.3":
version "1.16.8"
resolved "https://registry.yarnpkg.com/@react-native-picker/picker/-/picker-1.16.8.tgz#2126ca54d4a5a3e9ea5e3f39ad1e6643f8e4b3d4"
integrity sha512-pacdQDX6V6EmjF+HoiIh6u++qx4mTK0WnhgUHRc01B+Qt5eoeUwseBqmqfTSXTx/aHDEd6PiIw7UGvKgFoqgFQ==
"@react-native-picker/picker@^2.5.1":
version "2.5.1"
resolved "https://registry.yarnpkg.com/@react-native-picker/picker/-/picker-2.5.1.tgz#dfa13d5b97bfbedf1f7e7c608181a82f1d58b351"
......@@ -5613,6 +5618,13 @@ expo-asset@~8.9.1:
path-browserify "^1.0.0"
url-parse "^1.5.9"
expo-camera@~13.4.2:
version "13.4.4"
resolved "https://registry.yarnpkg.com/expo-camera/-/expo-camera-13.4.4.tgz#e01ead31a3150398d37e94c307f6937480680690"
integrity sha512-7k54APbpSulUDR2CrD5SrmKjCdfdg4tqKRpbBOKc2J2MIBHhunExU77435JDYSejHRY5bfRHZsEp3yKwR862uw==
dependencies:
invariant "^2.2.4"
expo-constants@~14.2.0, expo-constants@~14.2.1:
version "14.2.1"
resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-14.2.1.tgz#b5b6b8079d2082c31ccf2cbc7cf97a0e83c229c3"
......@@ -5635,6 +5647,18 @@ expo-font@~11.1.1:
dependencies:
fontfaceobserver "^2.1.0"
expo-image-loader@~4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/expo-image-loader/-/expo-image-loader-4.3.0.tgz#d897b4d4f1838faa8291c36f00b99c2b36a6ac5c"
integrity sha512-2kqJIO+oYM8J3GbvTUHLqTSpt1dLpOn/X0eB4U4RTuzz/faj8l/TyQELsMBLlGAkweNUuG9LqznbaBz+WuSFEw==
expo-image-picker@~14.3.2:
version "14.3.2"
resolved "https://registry.yarnpkg.com/expo-image-picker/-/expo-image-picker-14.3.2.tgz#5ae2f36fe518a10c8fe82e5ff2ee735c14817cfc"
integrity sha512-xr/YeQMIYheXecWP033F2SPwpBlBR5xVCx7YSfSCTH8Y9pw7Z886agqKGbS9QBVGlzJ5qecJktZ6ASSzeslDVg==
dependencies:
expo-image-loader "~4.3.0"
expo-keep-awake@~12.0.1:
version "12.0.1"
resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-12.0.1.tgz#19c5ab55391394ded3f6c262b0707c7140658a11"
......@@ -8898,7 +8922,7 @@ prompts@^2.3.2, prompts@^2.4.0:
kleur "^3.0.3"
sisteransi "^1.0.5"
prop-types@*, prop-types@^15.6.1:
prop-types@*, prop-types@^15.6.1, prop-types@^15.7.2:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
......@@ -9103,6 +9127,21 @@ react-native-gradle-plugin@^0.71.18:
resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.18.tgz#20ef199bc85be32e45bb6cc069ec2e7dcb1a74a6"
integrity sha512-7F6bD7B8Xsn3JllxcwHhFcsl9aHIig47+3eN4IHFNqfLhZr++3ElDrcqfMzugM+niWbaMi7bJ0kAkAL8eCpdWg==
react-native-multiple-select@^0.5.12:
version "0.5.12"
resolved "https://registry.yarnpkg.com/react-native-multiple-select/-/react-native-multiple-select-0.5.12.tgz#be9204f49bc1bb734c40422a89acc173959bcd70"
integrity sha512-lFw0u798/2qHr4TwDdxMtReRtsNOCC2SWPzWHRGKE4XcBiUll0hHhke7iqQg4xJdfo46C/h69f1ZXphDOjZY3A==
dependencies:
prop-types "^15.7.2"
react-native-picker-select@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/react-native-picker-select/-/react-native-picker-select-8.1.0.tgz#667a5442f783f4bcfd3f65880c6926155fd2c39c"
integrity sha512-iLsLv2OEWpXnQMDYJS6du5Cl1HTHy887n60Yp5OOiMny0TDB9w5CfxTUYWtpsvJJrUa/Yrv+1NMQiJy7IA4ETw==
dependencies:
"@react-native-picker/picker" "^1.8.3"
lodash.isequal "^4.5.0"
react-native-safe-area-context@4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-4.5.0.tgz#9208313236e8f49e1920ac1e2a2c975f03aed284"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment