Update README, added example and fixed issue with plantnet

This commit is contained in:
Norbert Schmidt
2023-02-22 07:20:16 +01:00
parent 0d99d94642
commit b1c26baea0
6 changed files with 227 additions and 95 deletions

View File

@@ -32,6 +32,32 @@ Scientists, (app) developers, project partners, people who are lookikng for lo
## Documentation ## Documentation
More in depth documentation is available on ZENODO: https://zenodo.org/record/7615472#.Y-JidC8w2gQ More in depth documentation is available on ZENODO: https://zenodo.org/record/7615472#.Y-JidC8w2gQ
## Installation and setup
### Prerequisites
- Node.js
- Ionic / capactior
- Git
- Xcode (for iOS)
- Android Studio (for Android)
A Parse server (for example the one provided by Pocket Science, DIY or Back4App) is required for the app to work.
A Firebase account for login and push notifications is required.
### Installation
Rename the provided .app. constant example to app.copnstnt and fill in the required fields.
1. Clone the repository
2. Install the dependencies
npm install
3. Run the app
## Generic ## Generic
Provided functionalities in R 1.0: Provided functionalities in R 1.0:
User login (Apple/Google/Email) User login (Apple/Google/Email)

View File

@@ -0,0 +1,36 @@
export const ENV = {
production: false,
parseAppId: 'PARSE APP HERE',
parseServerUrl: 'PARSE URL HERE',
parseJSKey: 'parseJSKey here',
fileKey: 'parsefileKey here',
plantnetKey: 'plantnetKey here'
}
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
firebase: {
projectId: 'mobisapp-prod',
appId: 'firebase app id',
storageBucket: 'xx-prod.appspot.com',
//locationId: 'europe-west',
apiKey: 'xxx-no',
authDomain: 'xxxm',
messagingSenderId: 'xxx',
},
production: true
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

View File

@@ -1,5 +1,13 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import {ENV} from '../../../app.constant'; import {ENV} from '../../../app.constant';
import { TranslateService } from '@ngx-translate/core';
import { AuthService } from '../../../services/auth.service';
import { Auth, user } from '@angular/fire/auth';
import { Storage } from '@ionic/storage-angular';
import { Router } from '@angular/router';
import { Geolocation } from '@capacitor/geolocation';
import { Http } from '@capacitor-community/http';
@Component({ @Component({
selector: 'app-classify', selector: 'app-classify',
@@ -11,46 +19,159 @@ export class ClassifyPage implements OnInit {
API_URL = 'https://my-api.plantnet.org/v2/identify/' + this.PROJECT + '?api-key='; API_URL = 'https://my-api.plantnet.org/v2/identify/' + this.PROJECT + '?api-key=';
API_PRIVATE_KEY = ENV.plantnetKey; // secret API_PRIVATE_KEY = ENV.plantnetKey; // secret
API_SIMSEARCH_OPTION = '&include-related-images=true'; // optional: get most similar images API_SIMSEARCH_OPTION = '&include-related-images=true'; // optional: get most similar images
API_LANG = '&lang=fr'; // default: en API_LANG = '&lang=en'; // default: en
IMAGE_1 = '../data/image_1.jpeg'; uploadSuccess = false; // Add this line
ORGAN_1 = 'flower'; image = '';
IMAGE_2 = '../data/image_2.jpeg'; user = null;
ORGAN_2 = 'leaf'; language = '';
latitude: number;
longitude: number;
altitude: number;
constructor(
constructor() { } private storage: Storage,
private authService: AuthService,
private afAuth: Auth,
private translateService: TranslateService,
private router: Router
) { user(this.afAuth).subscribe((response) => {
//fill the user to verify if someone is logged in
this.user = response;
console.log(this.user.uid);
}); }
ngOnInit() { ngOnInit() {
// now make a POST request to the API // get location
const xhr = new XMLHttpRequest(); this.getLocation();
xhr.open('POST', this.API_URL + this.API_PRIVATE_KEY + this.API_SIMSEARCH_OPTION + this.API_LANG, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function () { // get image from local storage
// do something to response this.storage.get('image').then((image) => {
console.log(this.responseText); console.log(image);
this.image = image;
});
// add rose.jpg from assets directory for testing
const imageUri = 'assets/rose.jpg';
const imageType = 'image/jpeg';
const imageName = 'rose.jpg';
fetch(imageUri)
.then(response => response.blob())
.then(blob => {
const formData = new FormData();
formData.append('images', new File([blob], imageName, { type: imageType }));
formData.append('organs', 'auto');
const url = 'https://my-api.plantnet.org/v2/identify/all';
const headers = {
'accept': 'application/json',
'Content-Type': 'multipart/form-data'
}; };
xhr.send(JSON.stringify({ const params = {
images: [ 'include-related-images': 'false',
{ 'no-reject': 'false',
url: this.IMAGE_1, 'lang': 'en',
organ: this.ORGAN_1 'api-key': '2b10bmIKkNNcBL6D4jwq3il4rO'
};
Http.request({
method: 'POST',
url: url,
headers: headers,
params: params,
data: formData
}).then(response => {
console.log(response.data);
}).catch(error => {
console.error(error);
});
})
.catch(error => {
console.error(error);
});
};
savetoParse() {
const plantnet_data_store = Parse.Object.extend('plantnet_data');
// create new instance of parse class
const plantnet_data = new plantnet_data_store();
// set value for parse clas
const file = new Parse.File('plantnet_image.jpg', { base64: this.image });
file.save().then(
(file) => {
console.log(file);
}, },
{ (error) => {
url: this.IMAGE_2, console.log(error);
organ: this.ORGAN_2
} }
] );
})); // add location and clasiification
plantnet_data.set('user_uid', this.user.uid);
plantnet_data.set('name', this.user.displayName);
plantnet_data.set('email', this.user.email);
plantnet_data.set('image', file);
plantnet_data.set('latitude', this.latitude);
plantnet_data.set('longitude', this.longitude);
plantnet_data.set('altitude', this.altitude);
plantnet_data.save().then(
(result: any) => {
console.log(result);
},
(error: any) => {
console.log(error);
}
);
}
async getLocation() {
const position = await Geolocation.getCurrentPosition({
enableHighAccuracy: true,
});
this.latitude = position.coords.latitude;
console.log(position.coords.latitude);
this.longitude = position.coords.longitude;
this.altitude = position.coords.altitude;
return position.coords;
} }
} }

View File

@@ -11,7 +11,7 @@ Take a picture
<ion-content [fullscreen]="false"> <ion-content [fullscreen]="false">
<ion-card> <ion-card>
Welcome to the Plantnet API <ion-card-header>Welcome to the Plantnet API</ion-card-header>
</ion-card> </ion-card>
<div *ngIf="image"> <div *ngIf="image">

View File

@@ -1,18 +1,13 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import * as Parse from 'parse';
import { ENV } from '../../app.constant';
import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import { ModalController } from '@ionic/angular'; import { ModalController } from '@ionic/angular';
import { PreviewPage } from './preview/preview.page'; import { PreviewPage } from './preview/preview.page';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { AuthService } from '../../services/auth.service';
import { Auth, user } from '@angular/fire/auth';
import { Storage } from '@ionic/storage-angular'; import { Storage } from '@ionic/storage-angular';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Geolocation } from '@capacitor/geolocation';
Parse.initialize(ENV.parseAppId, ENV.parseJSKey);
(Parse as any).serverURL = ENV.parseServerUrl; // use your server url
let uploadSuccess = false; let uploadSuccess = false;
@@ -33,37 +28,23 @@ export class PlantnetPage implements OnInit {
constructor( constructor(
private modal: ModalController, private modal: ModalController,
private translateService: TranslateService, private translateService: TranslateService,
private authService: AuthService,
private afAuth: Auth,
private storage: Storage, private storage: Storage,
private router: Router private router: Router
) { ) {
user(this.afAuth).subscribe((response) => {
//fill the user to verify if someone is logged in
this.user = response;
console.log(this.user.uid);
});
} }
ngOnInit() { ngOnInit() {
this.getLocation();
} }
async getLocation() {
const position = await Geolocation.getCurrentPosition({
enableHighAccuracy: true,
});
this.latitude = position.coords.latitude;
console.log(position.coords.latitude);
this.longitude = position.coords.longitude;
this.altitude = position.coords.altitude;
return position.coords;
}
async openCamera() { async openCamera() {
const modal = await this.modal.create({ const modal = await this.modal.create({
component: PreviewPage, component: PreviewPage,
@@ -71,48 +52,16 @@ export class PlantnetPage implements OnInit {
animated: true, animated: true,
}); });
modal.onDidDismiss().then((data) => { modal.onDidDismiss().then(async (data) => {
if (data !== null) { if (data !== null) {
this.image = data.data; this.image = data.data;
this.uploadSuccess = true; this.uploadSuccess = true;
// create parse class // create parse class
const plantnet_data_store = Parse.Object.extend('plantnet_data');
// create new instance of parse class // store image in local storage
await this.storage.set('plantnet_image', this.image);
const plantnet_data = new plantnet_data_store();
// set value for parse clas
const file = new Parse.File('image.jpg', { base64: this.image });
file.save().then(
(file) => {
console.log(file);
},
(error) => {
console.log(error);
}
);
// add location and clasiification
plantnet_data.set('user_uid', this.user.uid);
plantnet_data.set('name', this.user.displayName);
plantnet_data.set('email', this.user.email);
plantnet_data.set('image', file);
plantnet_data.set('latitude', this.latitude);
plantnet_data.set('longitude', this.longitude);
plantnet_data.set('altitude', this.altitude);
plantnet_data.save().then(
(result: any) => {
console.log(result);
},
(error: any) => {
console.log(error);
}
);
} else { } else {
console.log('no data'); console.log('no data');
} }

View File

@@ -21,7 +21,7 @@ export class PreviewPage implements OnInit {
launchCamera() { launchCamera() {
const cameraPreviewOptions: CameraPreviewOptions = { const cameraPreviewOptions: CameraPreviewOptions = {
position: 'front', // front or rear position: 'rear', // front or rear
parent: 'content', // the id on the ion-content parent: 'content', // the id on the ion-content
className: '', className: '',
width: window.screen.width, //width of the camera display width: window.screen.width, //width of the camera display