Update README, added example and fixed issue with plantnet
This commit is contained in:
26
README.md
26
README.md
@@ -32,6 +32,32 @@ Scientists, (app) developers, project partners, people who are lookikng for lo
|
||||
## Documentation
|
||||
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
|
||||
Provided functionalities in R 1.0:
|
||||
User login (Apple/Google/Email)
|
||||
|
||||
36
src/app/app.constant.example.ts
Normal file
36
src/app/app.constant.example.ts
Normal 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.
|
||||
@@ -1,5 +1,13 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
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({
|
||||
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_PRIVATE_KEY = ENV.plantnetKey; // secret
|
||||
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';
|
||||
ORGAN_1 = 'flower';
|
||||
IMAGE_2 = '../data/image_2.jpeg';
|
||||
ORGAN_2 = 'leaf';
|
||||
uploadSuccess = false; // Add this line
|
||||
image = '';
|
||||
user = null;
|
||||
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() {
|
||||
|
||||
// now make a POST request to the API
|
||||
const xhr = new XMLHttpRequest();
|
||||
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 () {
|
||||
// do something to response
|
||||
console.log(this.responseText);
|
||||
// get location
|
||||
this.getLocation();
|
||||
|
||||
|
||||
// get image from local storage
|
||||
this.storage.get('image').then((image) => {
|
||||
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'
|
||||
};
|
||||
const params = {
|
||||
'include-related-images': 'false',
|
||||
'no-reject': 'false',
|
||||
'lang': 'en',
|
||||
'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);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
xhr.send(JSON.stringify({
|
||||
images: [
|
||||
{
|
||||
url: this.IMAGE_1,
|
||||
organ: this.ORGAN_1
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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);
|
||||
},
|
||||
{
|
||||
url: this.IMAGE_2,
|
||||
organ: this.ORGAN_2
|
||||
|
||||
(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);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ Take a picture
|
||||
|
||||
<ion-content [fullscreen]="false">
|
||||
<ion-card>
|
||||
Welcome to the Plantnet API
|
||||
<ion-card-header>Welcome to the Plantnet API</ion-card-header>
|
||||
</ion-card>
|
||||
|
||||
<div *ngIf="image">
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
/* 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 { ModalController } from '@ionic/angular';
|
||||
import { PreviewPage } from './preview/preview.page';
|
||||
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';
|
||||
|
||||
Parse.initialize(ENV.parseAppId, ENV.parseJSKey);
|
||||
(Parse as any).serverURL = ENV.parseServerUrl; // use your server url
|
||||
|
||||
let uploadSuccess = false;
|
||||
|
||||
@@ -33,37 +28,23 @@ export class PlantnetPage implements OnInit {
|
||||
constructor(
|
||||
private modal: ModalController,
|
||||
private translateService: TranslateService,
|
||||
private authService: AuthService,
|
||||
private afAuth: Auth,
|
||||
|
||||
private storage: Storage,
|
||||
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() {
|
||||
|
||||
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() {
|
||||
const modal = await this.modal.create({
|
||||
component: PreviewPage,
|
||||
@@ -71,48 +52,16 @@ export class PlantnetPage implements OnInit {
|
||||
animated: true,
|
||||
});
|
||||
|
||||
modal.onDidDismiss().then((data) => {
|
||||
modal.onDidDismiss().then(async (data) => {
|
||||
if (data !== null) {
|
||||
this.image = data.data;
|
||||
|
||||
this.uploadSuccess = true;
|
||||
// create parse class
|
||||
|
||||
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('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);
|
||||
}
|
||||
);
|
||||
// store image in local storage
|
||||
await this.storage.set('plantnet_image', this.image);
|
||||
} else {
|
||||
console.log('no data');
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export class PreviewPage implements OnInit {
|
||||
|
||||
launchCamera() {
|
||||
const cameraPreviewOptions: CameraPreviewOptions = {
|
||||
position: 'front', // front or rear
|
||||
position: 'rear', // front or rear
|
||||
parent: 'content', // the id on the ion-content
|
||||
className: '',
|
||||
width: window.screen.width, //width of the camera display
|
||||
|
||||
Reference in New Issue
Block a user