├── .gitignore
├── frontend
├── jsconfig.json
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── seasons.png
│ ├── manifest.json
│ └── index.html
├── src
│ ├── assets
│ │ ├── homebg.png
│ │ ├── search.svg
│ │ ├── analysis.svg
│ │ ├── redo.svg
│ │ ├── sprout.svg
│ │ ├── map.svg
│ │ ├── statistics.svg
│ │ ├── irrigation.svg
│ │ ├── cloudy.svg
│ │ ├── timer.svg
│ │ ├── raining.svg
│ │ ├── logo.svg
│ │ ├── crop.svg
│ │ ├── sunny.svg
│ │ └── saved.svg
│ ├── components
│ │ ├── Map
│ │ │ ├── style.scss
│ │ │ ├── utils.js
│ │ │ └── index.jsx
│ │ ├── Statistics
│ │ │ ├── EnvironmentalStrain
│ │ │ │ ├── style.scss
│ │ │ │ └── index.jsx
│ │ │ ├── EstimatedFinancials
│ │ │ │ ├── style.scss
│ │ │ │ └── index.jsx
│ │ │ ├── FarmBreakdown
│ │ │ │ ├── style.scss
│ │ │ │ └── index.jsx
│ │ │ ├── CropRotation
│ │ │ │ ├── style.scss
│ │ │ │ ├── index.jsx
│ │ │ │ └── earthData.json
│ │ │ └── Breakdowns
│ │ │ │ ├── style.scss
│ │ │ │ └── index.jsx
│ │ ├── Date
│ │ │ ├── style.scss
│ │ │ └── index.jsx
│ │ ├── Analysis
│ │ │ ├── Card
│ │ │ │ ├── index.jsx
│ │ │ │ └── style.scss
│ │ │ ├── Breakdowns
│ │ │ │ ├── style.scss
│ │ │ │ └── index.jsx
│ │ │ ├── style.scss
│ │ │ └── index.jsx
│ │ ├── AQBreakdown
│ │ │ ├── style.scss
│ │ │ └── index.jsx
│ │ ├── LocationInfo
│ │ │ └── index.jsx
│ │ ├── CurrentlySelected
│ │ │ ├── index.jsx
│ │ │ └── style.scss
│ │ ├── StatsModal
│ │ │ ├── style.scss
│ │ │ └── index.jsx
│ │ ├── NavBar
│ │ │ ├── index.jsx
│ │ │ └── style.scss
│ │ ├── Plot
│ │ │ ├── style.scss
│ │ │ └── index.jsx
│ │ ├── SideBar
│ │ │ ├── style.scss
│ │ │ └── index.jsx
│ │ ├── Timeline
│ │ │ ├── style.scss
│ │ │ └── index.jsx
│ │ ├── Landing
│ │ │ ├── index.jsx
│ │ │ └── style.scss
│ │ ├── BigCurrentlySelected
│ │ │ ├── style.scss
│ │ │ └── index.jsx
│ │ ├── Seasons
│ │ │ ├── style.scss
│ │ │ └── index.jsx
│ │ └── PlotType
│ │ │ ├── style.scss
│ │ │ └── index.jsx
│ ├── pages
│ │ ├── Landing.jsx
│ │ ├── earthData.json
│ │ └── Home.jsx
│ ├── style.scss
│ └── index.js
├── .gitignore
├── netlify.toml
└── package.json
├── backend
├── src
│ ├── insights
│ │ ├── ambee
│ │ │ ├── index.ts
│ │ │ ├── soilLatest.ts
│ │ │ ├── waterVaporLatest.ts
│ │ │ ├── fireLatest.ts
│ │ │ ├── soilHistory.ts
│ │ │ ├── waterVaporHistory.ts
│ │ │ ├── airQuality.ts
│ │ │ ├── pollenLatest.ts
│ │ │ ├── weatherLatest.ts
│ │ │ ├── pollenForecast.ts
│ │ │ ├── pollenHistory.ts
│ │ │ ├── airQualityHistory.ts
│ │ │ ├── weatherHistory.ts
│ │ │ └── weatherForecast.ts
│ │ ├── weather
│ │ │ └── index.ts
│ │ └── index.ts
│ ├── configuration
│ │ └── index.ts
│ ├── utils
│ │ ├── handleError.ts
│ │ ├── parseDate.ts
│ │ ├── getLocGrade.ts
│ │ └── getEnvGrade.ts
│ ├── index.ts
│ ├── data
│ │ ├── weatherResponse.json
│ │ └── earthData.json
│ └── app.ts
├── .gitignore
├── .prettierrc.js
├── tsconfig-paths-bootstrap.js
├── .eslintrc.js
├── tsconfig.json
└── package.json
├── analysis
├── .gitignore
├── .prettierrc.js
├── tsconfig-paths-bootstrap.js
├── .eslintrc.js
├── tsconfig.json
├── package.json
└── src
│ ├── index.ts
│ └── data
│ └── earthData.json
├── package.json
├── CONTRIBUTING.md
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .DS_Store
3 | build/
--------------------------------------------------------------------------------
/frontend/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "src"
4 | }
5 | }
--------------------------------------------------------------------------------
/backend/src/insights/ambee/index.ts:
--------------------------------------------------------------------------------
1 | export interface AmbeeError {
2 | error: any;
3 | }
4 |
--------------------------------------------------------------------------------
/frontend/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hariketsheth/Carefact---Envisioning-Farms-for-Future/HEAD/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/frontend/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hariketsheth/Carefact---Envisioning-Farms-for-Future/HEAD/frontend/public/logo192.png
--------------------------------------------------------------------------------
/frontend/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hariketsheth/Carefact---Envisioning-Farms-for-Future/HEAD/frontend/public/logo512.png
--------------------------------------------------------------------------------
/frontend/public/seasons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hariketsheth/Carefact---Envisioning-Farms-for-Future/HEAD/frontend/public/seasons.png
--------------------------------------------------------------------------------
/frontend/src/assets/homebg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hariketsheth/Carefact---Envisioning-Farms-for-Future/HEAD/frontend/src/assets/homebg.png
--------------------------------------------------------------------------------
/frontend/src/components/Map/style.scss:
--------------------------------------------------------------------------------
1 | .custom-top-right {
2 | position: absolute;
3 | top: 10vh;
4 | right: 200px;
5 | z-index: 99999;
6 |
7 | }
--------------------------------------------------------------------------------
/analysis/.gitignore:
--------------------------------------------------------------------------------
1 | logs
2 | *.log
3 | npm-debug.log*
4 | pids
5 | *.pid
6 | *.seed
7 | *.pid.lock
8 | node_modules
9 | *.tsbuildinfo
10 | .env
11 | build
12 |
--------------------------------------------------------------------------------
/backend/.gitignore:
--------------------------------------------------------------------------------
1 | logs
2 | *.log
3 | npm-debug.log*
4 | pids
5 | *.pid
6 | *.seed
7 | *.pid.lock
8 | node_modules
9 | *.tsbuildinfo
10 | .env
11 | build
12 |
--------------------------------------------------------------------------------
/analysis/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: true,
3 | trailingComma: 'all',
4 | singleQuote: true,
5 | printWidth: 120,
6 | tabWidth: 4,
7 | };
8 |
--------------------------------------------------------------------------------
/backend/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: true,
3 | trailingComma: 'all',
4 | singleQuote: true,
5 | printWidth: 120,
6 | tabWidth: 4,
7 | };
8 |
--------------------------------------------------------------------------------
/frontend/src/pages/Landing.jsx:
--------------------------------------------------------------------------------
1 | import Landing from '../components/Landing';
2 |
3 | const LandingPage = () => {
4 | return ;
5 | };
6 |
7 | export default LandingPage;
8 |
--------------------------------------------------------------------------------
/frontend/src/components/Statistics/EnvironmentalStrain/style.scss:
--------------------------------------------------------------------------------
1 | .EnvironmentalStrain {
2 | border: 2px solid #dfdfdf;
3 | border-radius: 6px;
4 | padding: 10px;
5 | background-color: #f9f9f9;
6 | margin: 10px;
7 | width: 450px;
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/src/components/Statistics/EstimatedFinancials/style.scss:
--------------------------------------------------------------------------------
1 | .EstimatedFinancials {
2 | border: 2px solid #dfdfdf;
3 | border-radius: 6px;
4 | padding: 10px;
5 | background-color: #f9f9f9;
6 | margin: 10px;
7 | width: 450px;
8 | }
9 |
--------------------------------------------------------------------------------
/backend/src/configuration/index.ts:
--------------------------------------------------------------------------------
1 | export const PORT: string = process.env.PORT || '3001';
2 | export const AMBEE_API_KEY = process.env.AMBEE_API_KEY;
3 | export const WEATHER_API_KEY = process.env.WEATHER_API_KEY;
4 | export const USE_API = process.env.USE_API || false;
--------------------------------------------------------------------------------
/frontend/src/components/Date/style.scss:
--------------------------------------------------------------------------------
1 | .date {
2 | position: absolute;
3 | top: 0;
4 | right: 0;
5 | z-index: 999;
6 | height: 8vh;
7 | width: 300px;
8 | display: flex;
9 | align-items: center;
10 | justify-content: center;
11 | font-size: 28px;
12 | }
--------------------------------------------------------------------------------
/frontend/src/components/Analysis/Card/index.jsx:
--------------------------------------------------------------------------------
1 | import './style.scss';
2 |
3 | const Card = (props) => {
4 | return (
5 |
6 |
{props.title}
7 | {props.children}
8 |
9 | );
10 | }
11 |
12 | export default Card;
--------------------------------------------------------------------------------
/frontend/src/components/AQBreakdown/style.scss:
--------------------------------------------------------------------------------
1 | .air-quality {
2 | width: 300px;
3 |
4 | .breakdown {
5 | list-style-type: none;
6 | padding: 0;
7 |
8 | li {
9 |
10 | div {
11 | display: inline;
12 | }
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/backend/src/utils/handleError.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'express';
2 |
3 | export const handleError = (cause: string, details: any, req: Request, res: Response) => {
4 | return res.status(400).json({
5 | message: cause,
6 | details,
7 | sentParameters: req.body,
8 | });
9 | };
10 |
--------------------------------------------------------------------------------
/analysis/tsconfig-paths-bootstrap.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 | const tsConfig = require('./tsconfig.json');
3 | const tsConfigPaths = require('tsconfig-paths');
4 |
5 | const baseUrl = './build';
6 | tsConfigPaths.register({
7 | baseUrl,
8 | paths: tsConfig.compilerOptions.paths,
9 | });
10 |
--------------------------------------------------------------------------------
/backend/tsconfig-paths-bootstrap.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 | const tsConfig = require('./tsconfig.json');
3 | const tsConfigPaths = require('tsconfig-paths');
4 |
5 | const baseUrl = './build';
6 | tsConfigPaths.register({
7 | baseUrl,
8 | paths: tsConfig.compilerOptions.paths,
9 | });
10 |
--------------------------------------------------------------------------------
/backend/src/index.ts:
--------------------------------------------------------------------------------
1 | import { PORT } from 'src/configuration';
2 | import { getApp } from 'src/app';
3 |
4 | const startServer = () => {
5 | try {
6 | const app = getApp();
7 |
8 | app.listen(PORT, () => {
9 | console.log(`server started at http://localhost:${PORT}`);
10 | });
11 | } catch (error) {
12 | console.error(error);
13 | }
14 | };
15 |
16 | startServer();
17 |
--------------------------------------------------------------------------------
/backend/src/utils/parseDate.ts:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 | /**
3 | * Converts the date to a ambee readable date
4 | * @param date In a format parsable by moment
5 | */
6 | export const ambeeDateParse = (date: string): string => {
7 | return moment(date).format('YYYY-MM-DD hh:mm:ss');
8 | };
9 |
10 | export const weatherDateParse = (date: string): string => {
11 | return moment(date).format('MM/DD/YYYY');
12 | }
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/frontend/src/components/Statistics/FarmBreakdown/style.scss:
--------------------------------------------------------------------------------
1 | .FarmBreakdown {
2 | border: 2px solid #dfdfdf;
3 | border-radius: 6px;
4 | padding: 10px;
5 | background-color: #f9f9f9;
6 | margin: 10px;
7 | width: 425px;
8 | height: 315px;
9 |
10 | .wrapper {
11 | width: 100%;
12 | display: flex;
13 | justify-content: center;
14 | align-items: center;
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/analysis/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | extends: [
4 | 'plugin:@typescript-eslint/recommended',
5 | 'prettier/@typescript-eslint',
6 | 'plugin:prettier/recommended',
7 | ],
8 | parserOptions: {
9 | ecmaVersion: 2018,
10 | sourceType: 'module',
11 | },
12 | rules: {
13 | "@typescript-eslint/explicit-function-return-type": 0,
14 | "@typescript-eslint/no-explicit-any": 0
15 | },
16 | };
17 |
--------------------------------------------------------------------------------
/backend/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | extends: [
4 | 'plugin:@typescript-eslint/recommended',
5 | 'prettier/@typescript-eslint',
6 | 'plugin:prettier/recommended',
7 | ],
8 | parserOptions: {
9 | ecmaVersion: 2018,
10 | sourceType: 'module',
11 | },
12 | rules: {
13 | "@typescript-eslint/explicit-function-return-type": 0,
14 | "@typescript-eslint/no-explicit-any": 0
15 | },
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/style.scss:
--------------------------------------------------------------------------------
1 | @import 'antd/dist/antd.css';
2 |
3 | body {
4 | font-family: Poppins, sans-serif;
5 | height: 100vh;
6 | margin: 0;
7 | overflow: hidden;
8 | width: 100vh;
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | }
12 |
13 | a {
14 | color: black;
15 | text-decoration: none;
16 | }
17 |
18 | canvas {
19 | z-index: 500;
20 | &:focus {
21 | outline: none;
22 | }
23 | }
24 |
25 | .custom-group {
26 | display: none;
27 | }
--------------------------------------------------------------------------------
/frontend/src/components/LocationInfo/index.jsx:
--------------------------------------------------------------------------------
1 | /*
2 | Props should contain:
3 | - color 1 for gradient
4 | - color 2 for gradient
5 | - text to display
6 | */
7 | const LocationInfo = (props) => {
8 | return (
9 |
10 |
11 | {props.title}
12 |
13 |
14 | {props.children}
15 |
16 |
17 | );
18 | }
19 |
20 | export default LocationInfo;
--------------------------------------------------------------------------------
/analysis/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "skipLibCheck": true,
4 | "module": "commonjs",
5 | "esModuleInterop": true,
6 | "target": "es6",
7 | "noImplicitAny": true,
8 | "moduleResolution": "node",
9 | "sourceMap": true,
10 | "outDir": "build",
11 | "baseUrl": "src",
12 | "resolveJsonModule": true,
13 | "paths": {
14 | "*": ["node_modules/*"],
15 | "src/*": ["./*"]
16 | }
17 | },
18 | "include": ["src/**/*"]
19 | }
20 |
--------------------------------------------------------------------------------
/backend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "skipLibCheck": true,
4 | "module": "commonjs",
5 | "esModuleInterop": true,
6 | "target": "es6",
7 | "noImplicitAny": true,
8 | "moduleResolution": "node",
9 | "sourceMap": true,
10 | "outDir": "build",
11 | "baseUrl": "src",
12 | "resolveJsonModule": true,
13 | "paths": {
14 | "*": ["node_modules/*"],
15 | "src/*": ["./*"]
16 | }
17 | },
18 | "include": ["src/**/*"]
19 | }
20 |
--------------------------------------------------------------------------------
/frontend/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import {
4 | BrowserRouter as Router,
5 | Switch,
6 | Route,
7 | } from "react-router-dom";
8 |
9 | import './style.scss';
10 | import Home from 'pages/Home';
11 | import Landing from 'pages/Landing';
12 |
13 | ReactDOM.render(
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | ,
24 | document.getElementById('root')
25 | );
26 |
--------------------------------------------------------------------------------
/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/frontend/src/assets/search.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/frontend/src/assets/analysis.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/frontend/src/components/Date/index.jsx:
--------------------------------------------------------------------------------
1 | import './style.scss';
2 |
3 | function dateFromDay(year, day){
4 | let newYear;
5 | if (day > 365) {
6 | newYear = year + 1;
7 | } else {
8 | newYear = year;
9 | }
10 | var date = new Date(newYear, 0); // initialize a date in `year-01-01`
11 | return new Date(date.setDate(day % 365)); // add the number of days
12 | }
13 |
14 | const options = { year: "numeric", month: "long", day: "numeric" }
15 |
16 | const DateComponent = (props) => {
17 | return (
18 |
19 | {dateFromDay(2023, props.day).toLocaleDateString(undefined, options)}
20 |
21 | )
22 | }
23 |
24 | export default DateComponent;
--------------------------------------------------------------------------------
/frontend/src/assets/redo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/backend/src/data/weatherResponse.json:
--------------------------------------------------------------------------------
1 | {
2 | "from": "04/18/2021",
3 | "to": "04/18/2021",
4 | "weather": {
5 | "status": "success",
6 | "location": "Des Moines, IA, USA",
7 | "forecast": [
8 | {
9 | "date": "04/18/2021",
10 | "max_temp_high": 68,
11 | "max_temp_low": 58,
12 | "min_temp_code": "s",
13 | "min_temp_high": 45,
14 | "min_temp_low": 38,
15 | "precipitation": "Generally Dry",
16 | "precipitation_code": "DR2",
17 | "temp": "seasonal",
18 | "temp_code": "s"
19 | }
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/frontend/src/components/Statistics/CropRotation/style.scss:
--------------------------------------------------------------------------------
1 | .CropRotation {
2 | border: 2px solid #dfdfdf;
3 | border-radius: 6px;
4 | padding: 10px;
5 | background-color: #F9F9F9;
6 | margin: 10px;
7 | width: 450px;
8 |
9 | .buttons {
10 | width: 100%;
11 | display: flex;
12 | justify-content: flex-end;
13 | }
14 |
15 | p {
16 | font-size: 12px;
17 | }
18 |
19 | .forward {
20 | background: #F9F9F9;
21 | border-radius: 6px;
22 | border: 2px solid #dfdfdf;
23 | width: 20%;
24 | }
25 | .backward {
26 | border: none;
27 | background-color: #f9f9f9;
28 | box-shadow: none;
29 | }
30 |
31 | }
--------------------------------------------------------------------------------
/frontend/src/assets/sprout.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/frontend/src/components/CurrentlySelected/index.jsx:
--------------------------------------------------------------------------------
1 | import './style.scss';
2 |
3 | const CurrentlySelected = (props) => {
4 | return (
5 |
6 |
7 |
Selected
8 |
9 | { ! props.currentPlot.name ? 'N/A' :
10 | props.currentPlot.name.length > 6 ? props.currentPlot.name.substring(0, 5) + '...' :
11 | props.currentPlot.name
12 | }
13 |
14 |
15 |
16 |
Size
17 | {props.currentPlot.sqFt ? Math.floor(props.currentPlot.sqFt / 1000) + 'k sqft': 'N/A' }
18 |
19 |
20 | )
21 | }
22 |
23 | export default CurrentlySelected;
--------------------------------------------------------------------------------
/frontend/src/components/StatsModal/style.scss:
--------------------------------------------------------------------------------
1 | .StatsModal {
2 | .title {
3 | user-drag: none;
4 | user-select: none;
5 | -moz-user-select: none;
6 | -webkit-user-drag: none;
7 | -webkit-user-select: none;
8 | -ms-user-select: none;
9 | }
10 | }
11 | .stat-title {
12 | font-weight: 600;
13 | font-size: 20px;
14 | margin-bottom: 6px;
15 | }
16 |
17 | .sub-title {
18 | line-height: 6px;
19 | font-size: 12px;
20 | }
21 |
22 | .ant-modal {
23 | width: 75vw !important;
24 | }
25 |
26 | .bstats-container {
27 | display: flex;
28 | flex-direction: row;
29 | justify-content: space-between;
30 | }
31 |
32 | .tstats-container {
33 | display: flex;
34 | flex-direction: row;
35 | justify-content: space-between;
36 | }
37 |
--------------------------------------------------------------------------------
/frontend/src/components/CurrentlySelected/style.scss:
--------------------------------------------------------------------------------
1 | .currently-selected {
2 | align-items: center;
3 | background: #F9F9F9;
4 | border-top-left-radius: 6px;
5 | border-top-right-radius: 6px;
6 | bottom: -200px;
7 | display: flex;
8 | height: 9vh;
9 | left: 37vw;
10 | padding: 0 10px;
11 | position: absolute;
12 | width: 14vw;
13 | z-index: 1;
14 |
15 | .text {
16 | overflow: hidden;
17 | white-space: nowrap;
18 | width: 50%;
19 | padding: 10px;
20 |
21 | h1 {
22 | font-size: 20px;
23 | font-weight: 500;
24 | line-height: 100%;
25 | margin: 0;
26 | margin-top: 5px;
27 | }
28 |
29 | h2 {
30 | font-size: 13px;
31 | font-weight: 300;
32 | line-height: 100%;
33 | margin: 0;
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/frontend/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | # Configure it so that deploy previews and `nightly` run on the testing instance, not
3 | # on the default instance
4 | [context.deploy-preview]
5 | publish = "build/"
6 | command = "CI=false npm run build:dev"
7 | [context.deploy-preview.environment]
8 | NODE_ENV = "development"
9 | [context.branch-deploy]
10 | command = "CI=false npm run build:dev"
11 | [context.branch-deploy.environment]
12 | NODE_ENV = "development"
13 |
14 | # The following redirect is intended for use with most SPAs that handle
15 | # routing internally.
16 | [[redirects]]
17 | from = "/*"
18 | to = "/index.html"
19 | status = 200
20 |
21 | [[headers]]
22 | # Define which paths this specific [[headers]] block will cover.
23 | for = "/*"
24 | [headers.values]
25 | Access-Control-Allow-Origin = "*"
--------------------------------------------------------------------------------
/frontend/src/components/AQBreakdown/index.jsx:
--------------------------------------------------------------------------------
1 | import "./style.scss";
2 |
3 | const AQBreakdown = (props) => {
4 | return (
5 |
6 |
Air Quality Breakdown
7 |
8 | Air Quality Index
{props.data.AQI}
9 | Nitrogen Dioxide
{props.data.NO}
10 | Particulate Matter (2.5)
{props.data.PM25}
11 | Carbon Monoxide
{props.data.CO}
12 | Ozone
{props.data.OZONE}
13 | Sulfur Dioxide
{props.data.SO2}
14 |
15 |
16 | )
17 | }
18 |
19 | export default AQBreakdown;
--------------------------------------------------------------------------------
/frontend/src/components/NavBar/index.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import { Link } from "react-router-dom";
3 |
4 | import './style.scss';
5 | import search from 'assets/search.svg';
6 |
7 | const NavBar = () => {
8 |
9 | const [farmName, setFarmName] = useState("Falcons: Expo 2023");
10 |
11 | const onNameChange = (e) => {
12 | setFarmName(e.target.value);
13 | }
14 |
15 | return (
16 |
17 |
18 |
19 | drishti
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | )
29 | }
30 |
31 | export default NavBar;
--------------------------------------------------------------------------------
/frontend/src/components/Plot/style.scss:
--------------------------------------------------------------------------------
1 | .plot {
2 | align-items: center;
3 | background-repeat: no-repeat;
4 | background-position: right;
5 | border-radius: 6px;
6 | color: white;
7 | cursor: pointer;
8 | display: flex;
9 | height: 65px;
10 | margin-bottom: 10px;
11 | padding: 0 20px;
12 | width: 100%;
13 | background-size: cover;
14 |
15 |
16 | img {
17 | width: 35px;
18 | margin-right: 10px;
19 | }
20 |
21 | .section {
22 | margin-right: 2%;
23 | white-space: nowrap;
24 | width: 30%;
25 |
26 | h1 {
27 | color: white;
28 | font-size: 16px;
29 | font-weight: 500;
30 | line-height: 100%;
31 | margin: 0;
32 | }
33 |
34 | h2 {
35 | color: white;
36 | font-size: 16px;
37 | font-weight: 300;
38 | line-height: 100%;
39 | margin: 0;
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/frontend/src/assets/map.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fasaan",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start" : "yarn --cwd ./backend start",
8 | "dev" : "concurrently --kill-others \"yarn --cwd ./analysis build-watch\" \"yarn --cwd ./frontend start\" \"yarn --cwd ./backend dev\"",
9 | "frontend": "concurrently --kill-others \"yarn --cwd ./analysis build-watch\" \"yarn --cwd ./frontend start\"",
10 | "backend" : "yarn --cwd ./backend dev",
11 | "build" : "yarn install --production=false && yarn --cwd ./backend build",
12 | "build-frontend": "yarn install --production=false && yarn --cwd ./analysis build && yarn --cwd ./frontend build"
13 | },
14 | "author": "trulyronak",
15 | "license": "ISC",
16 | "private": true,
17 | "workspaces": [
18 | "backend",
19 | "frontend",
20 | "analysis"
21 | ],
22 | "dependencies": {
23 | "concurrently": "^6.0.2"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/backend/src/app.ts:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import cors from 'cors';
3 | import insightRouter from './insights';
4 | import earthData from './data/earthData.json';
5 |
6 | export const getApp = () => {
7 | const app = express();
8 |
9 | app.use(express.urlencoded({ extended: false }));
10 | app.use(express.json());
11 | app.use(cors(
12 | {
13 | origin: "*",
14 | optionsSuccessStatus: 200
15 | }
16 | ));
17 | // CORS (Cross-Origin Resource Sharing) headers to support Cross-site HTTP requests
18 | app.all('*', (req, res, next) => {
19 | res.header("Access-Control-Allow-Origin", "*");
20 | res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
21 | next();
22 | });
23 |
24 | app.use('/insight', insightRouter);
25 | app.get('/data/earth', (req, res) => {
26 | res.json(earthData);
27 | });
28 |
29 | return app;
30 | };
31 |
--------------------------------------------------------------------------------
/frontend/src/components/SideBar/style.scss:
--------------------------------------------------------------------------------
1 | .side-bar {
2 | align-items: center;
3 | background-color: white;
4 | border-bottom: 1px solid #DFDFDF;
5 | border-radius: 0px 0px 50px 0px;
6 | border-right: 1px solid #DFDFDF;
7 | display: flex;
8 | flex-wrap: wrap;
9 | height: 100vh;
10 | justify-content: center;
11 | left: 0;
12 | position: absolute;
13 | top: 0;
14 | width: 5vw;
15 | z-index: 1000;
16 |
17 | .icons {
18 | display: flex;
19 | flex-wrap: wrap;
20 | justify-content: center;
21 | width: 100%;
22 |
23 | .icon {
24 | cursor: pointer;
25 | text-align: center;
26 | transition: 0.15s;
27 | width: 100%;
28 | margin: 10px 0px;
29 |
30 |
31 | img {
32 | width: 50%;
33 | }
34 |
35 | p {
36 | font-weight: 500;
37 | font-size: 12px;
38 | margin-top: 0;
39 | }
40 |
41 | &:hover {
42 | transform: scale(1.1);
43 | }
44 | }
45 | }
46 |
47 | .logo {
48 | position: absolute;
49 | top: 15px;
50 | }
51 | }
--------------------------------------------------------------------------------
/backend/src/insights/ambee/soilLatest.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { AMBEE_API_KEY } from 'src/configuration';
3 | import { AmbeeError } from '.';
4 |
5 | interface SoilLatestData {
6 | _id: string;
7 | scantime: string;
8 | soil_temperature: number;
9 | soil_moisture: number;
10 | }
11 |
12 | interface SoilLatestResponse {
13 | error?: AmbeeError;
14 | message: string;
15 | data: SoilLatestData[];
16 | }
17 |
18 | export const getSoilLatest = async (lat: number, lng: number): Promise => {
19 | try {
20 | const response = await axios.request({
21 | method: 'GET',
22 | url: 'https://api.ambeedata.com/soil/latest/by-lat-lng',
23 | params: { lat: lat.toString(10), lng: lng.toString(10) },
24 | headers: { 'x-api-key': AMBEE_API_KEY, 'Content-type': 'application/json' },
25 | });
26 | return response.data;
27 | } catch (error) {
28 | return {
29 | error,
30 | message: 'failed',
31 | } as SoilLatestResponse;
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/backend/src/insights/ambee/waterVaporLatest.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { AMBEE_API_KEY } from 'src/configuration';
3 | import { AmbeeError } from '.';
4 |
5 | interface WaterVaporLatestData {
6 | _id: string;
7 | createdAt: string;
8 | water_vapor: number;
9 | }
10 |
11 | interface WaterVaporLatestResponse {
12 | error?: AmbeeError;
13 | message: string;
14 | data: WaterVaporLatestData[];
15 | }
16 |
17 | export const getWaterVaporLatest = async (lat: number, lng: number): Promise => {
18 | try {
19 | const response = await axios.request({
20 | method: 'GET',
21 | url: 'https://api.ambeedata.com/waterVapor/latest/by-lat-lng',
22 | params: { lat: lat.toString(10), lng: lng.toString(10) },
23 | headers: { 'x-api-key': AMBEE_API_KEY, 'Content-type': 'application/json' },
24 | });
25 | return response.data;
26 | } catch (error) {
27 | return {
28 | error,
29 | message: 'failed',
30 | } as WaterVaporLatestResponse;
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/frontend/src/assets/statistics.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/frontend/src/components/SideBar/index.jsx:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom";
2 |
3 | import './style.scss';
4 |
5 | import logo from 'assets/logo.svg';
6 | import map from 'assets/map.svg';
7 | import saved from 'assets/saved.svg';
8 | import statistics from 'assets/statistics.svg';
9 |
10 | const SideBar = (props) => {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
{props.setSideBarPage('map')}}>
18 |
19 |
Map
20 |
21 |
{props.setSideBarPage('create')}}>
22 |
23 |
Create
24 |
25 |
26 |
27 |
Harvest Statistics
28 |
29 |
30 |
31 | )
32 | }
33 |
34 | export default SideBar;
--------------------------------------------------------------------------------
/backend/src/insights/ambee/fireLatest.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { AMBEE_API_KEY } from 'src/configuration';
3 | import { AmbeeError } from '.';
4 |
5 | interface FireLatestData {
6 | lat: number;
7 | lon: number;
8 | confidence: string;
9 | frp: number;
10 | daynight: 'D' | 'N';
11 | detection_time: string;
12 | distance: number;
13 | }
14 |
15 | interface FireLatestResponse {
16 | error?: AmbeeError;
17 | message: string;
18 | data: FireLatestData[];
19 | }
20 |
21 | export const getFireLatest = async (lat: number, lng: number): Promise => {
22 | try {
23 | const response = await axios.request({
24 | method: 'GET',
25 | url: 'https://api.ambeedata.com/latest/fire',
26 | params: { lat: lat.toString(10), lng: lng.toString(10) },
27 | headers: { 'x-api-key': AMBEE_API_KEY, 'Content-type': 'application/json' },
28 | });
29 | return response.data;
30 | } catch (error) {
31 | return {
32 | error,
33 | message: 'failed',
34 | } as FireLatestResponse;
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/frontend/src/components/Analysis/Card/style.scss:
--------------------------------------------------------------------------------
1 | .card {
2 | background: #F9F9F9;
3 | border: 1px solid #DFDFDF;
4 | box-sizing: border-box;
5 | border-radius: 4.35px;
6 | height: 8vw;
7 | padding-top: 10px;
8 | width: 8vw;
9 | margin-right: 1.4vw;
10 | text-align: center;
11 | opacity: 0;
12 | position: relative;
13 | top: 10px;
14 |
15 | h1 {
16 | margin: 0;
17 | margin-bottom: 5px;
18 | margin-top: 2vh;
19 | font-size: 46px;
20 | line-height: 100%;
21 | }
22 |
23 | .gradient {
24 | background-size: 100%;
25 | -webkit-background-clip: text;
26 | -moz-background-clip: text;
27 | -webkit-text-fill-color: transparent;
28 | -moz-text-fill-color: transparent;
29 | }
30 |
31 | h2 {
32 | margin: 0;
33 | font-size: 20px;
34 | line-height: 100%;
35 | background-size: 100%;
36 | -webkit-background-clip: text;
37 | -moz-background-clip: text;
38 | -webkit-text-fill-color: transparent;
39 | -moz-text-fill-color: transparent;
40 | }
41 |
42 | h3 {
43 | margin: 0;
44 | line-height: 100%;
45 | }
46 |
47 | p {
48 | font-size: 13px;
49 | font-weight: 500;
50 | }
51 | }
--------------------------------------------------------------------------------
/backend/src/insights/ambee/soilHistory.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { AMBEE_API_KEY } from 'src/configuration';
3 | import { AmbeeError } from '.';
4 |
5 | interface SoilHistoryData {
6 | _id: string;
7 | scantime: string;
8 | soil_temperature: number;
9 | soil_moisture: number;
10 | }
11 |
12 | interface SoilHistoryResponse {
13 | error?: AmbeeError;
14 | message: string;
15 | data: SoilHistoryData[];
16 | }
17 |
18 | export const getSoilHistory = async (
19 | lat: number,
20 | lng: number,
21 | from: string,
22 | to: string,
23 | ): Promise => {
24 | try {
25 | const response = await axios.request({
26 | method: 'GET',
27 | url: 'https://api.ambeedata.com/soil/history/by-lat-lng',
28 | params: { lat: lat.toString(10), lng: lng.toString(10), from: from, to: to },
29 | headers: { 'x-api-key': AMBEE_API_KEY, 'Content-type': 'application/json' },
30 | });
31 | return response.data;
32 | } catch (error) {
33 | return {
34 | error,
35 | message: 'failed',
36 | } as SoilHistoryResponse;
37 | }
38 | };
39 |
--------------------------------------------------------------------------------
/backend/src/insights/ambee/waterVaporHistory.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { AMBEE_API_KEY } from 'src/configuration';
3 | import { AmbeeError } from '.';
4 |
5 | interface WaterVaporHistoryData {
6 | _id: string;
7 | createdAt: string;
8 | water_vapor: number;
9 | }
10 |
11 | interface WaterVaporHistoryResponse {
12 | error?: AmbeeError;
13 | message: string;
14 | data: WaterVaporHistoryData[];
15 | }
16 |
17 | export const getWaterVaporHistory = async (
18 | lat: number,
19 | lng: number,
20 | from: string,
21 | to: string,
22 | ): Promise => {
23 | try {
24 | const response = await axios.request({
25 | method: 'GET',
26 | url: 'https://api.ambeedata.com/waterVapor/history/by-lat-lng',
27 | params: { lat: lat.toString(10), lng: lng.toString(10), from: from, to: to },
28 | headers: { 'x-api-key': AMBEE_API_KEY, 'Content-type': 'application/json' },
29 | });
30 | return response.data;
31 | } catch (error) {
32 | return {
33 | error,
34 | message: 'failed',
35 | } as WaterVaporHistoryResponse;
36 | }
37 | };
38 |
--------------------------------------------------------------------------------
/frontend/src/components/Timeline/style.scss:
--------------------------------------------------------------------------------
1 | .timeline {
2 | align-items: center;
3 | background: #F9F9F9;
4 | border-top-left-radius: 6px;
5 | border-top-right-radius: 6px;
6 | bottom: -200px;
7 | display: flex;
8 | height: 9vh;
9 | left: 6vw;
10 | position: absolute;
11 | width: 30vw;
12 | z-index: 1;
13 |
14 | .info {
15 | display: flex;
16 | width: 35%;
17 | img {
18 | padding: 0 10px;
19 | }
20 |
21 | .text {
22 | h1 {
23 | font-size: 22px;
24 | font-weight: 500;
25 | line-height: 100%;
26 | margin: 0;
27 | }
28 |
29 | h2 {
30 | font-size: 13px;
31 | font-weight: 300;
32 | line-height: 100%;
33 | margin: 0;
34 | }
35 | }
36 | }
37 |
38 | .slider {
39 | padding: 0 25px 0 20px;
40 | width: 65%;
41 | }
42 |
43 | .label {
44 | font-size: 12px;
45 | }
46 | }
47 |
48 | .ant-slider-with-marks {
49 | margin-bottom: 25px;
50 | }
51 |
52 | .ant-slider-dot-active {
53 | border-color: #27AE60 !important;
54 | }
55 |
56 | .ant-slider-handle {
57 | border: solid 2px #27AE60 !important;
58 | }
59 |
60 | .ant-slider-track {
61 | background: #27AE60 !important;
62 | }
--------------------------------------------------------------------------------
/frontend/src/components/NavBar/style.scss:
--------------------------------------------------------------------------------
1 | .nav-bar {
2 | align-items: center;
3 | background-color: white;
4 | border-bottom: 1px solid #DFDFDF;
5 | box-sizing: border-box;
6 | display: flex;
7 | height: 8vh;
8 | padding-left: 5vw;
9 | width: 100vw;
10 | z-index: 999 !important;
11 | position: relative;
12 |
13 | .logo {
14 | align-items: center;
15 | border-right: 1px solid #DFDFDF;
16 | display: flex;
17 | font-size: 36px;
18 | font-weight: 500;
19 | height: 100%;
20 | padding: 0 20px;
21 | }
22 |
23 | .name-input {
24 | border: none;
25 | font-size: 24px;
26 | padding: 0 20px;
27 |
28 | &:focus{
29 | outline: none;
30 | }
31 | }
32 |
33 | .search-bar {
34 | align-items: center;
35 | display: flex;
36 | position: relative;
37 |
38 | img {
39 | left: 10px;
40 | position: absolute;
41 | width: 24px;
42 | }
43 |
44 | input {
45 | background: #F9F9F9;
46 | border: none;
47 | border-radius: 6px;
48 | color: #333333;
49 | font-size: 18px;
50 | padding: 10px 20px 10px 44px;
51 | width: 40vw;
52 |
53 | &:focus{
54 | outline: none;
55 | }
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/frontend/src/assets/irrigation.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/frontend/src/components/Analysis/Breakdowns/style.scss:
--------------------------------------------------------------------------------
1 | .Breakdowns {
2 | display: inline-block;
3 | background: #f9f9f9;
4 | border: 1px solid #dfdfdf;
5 | box-sizing: border-box;
6 | border-radius: 4.35px;
7 | height: 17vh;
8 | width: 26.8vw;
9 | margin-top: 2vh;
10 | margin-left: 1.4vw;
11 | padding: 5px 10px;
12 | overflow: scroll;
13 | -ms-overflow-style: none; /* IE and Edge */
14 | scrollbar-width: none; /* Firefox */
15 | h3 {
16 | font-size: 18px;
17 | }
18 |
19 | .subtitle {
20 | margin: 0px;
21 | margin-bottom: 12px;
22 | line-height: 0px;
23 | }
24 |
25 |
26 | h1 {
27 | font-size: 5em;
28 | }
29 |
30 | .row {
31 | display: flex;
32 | justify-content: space-between;
33 |
34 | .grade {
35 | width: 30%;
36 | display: flex;
37 | flex-direction: column;
38 | align-items: center;
39 | }
40 | .explanation {
41 | width: 65%;
42 | margin-top: 12px;
43 | }
44 | }
45 |
46 | .gradient {
47 | background-size: 100%;
48 | -webkit-background-clip: text;
49 | -moz-background-clip: text;
50 | -webkit-text-fill-color: transparent;
51 | -moz-text-fill-color: transparent;
52 | }
53 | }
54 |
55 | .Breakdowns::-webkit-scrollbar {
56 | display: none;
57 | }
58 |
--------------------------------------------------------------------------------
/frontend/src/components/Landing/index.jsx:
--------------------------------------------------------------------------------
1 | import { Input } from 'antd';
2 | import { useHistory } from 'react-router-dom';
3 |
4 | import './style.scss';
5 |
6 |
7 | const Landing = () => {
8 | let history = useHistory();
9 |
10 | const onSearch = (value) => {
11 | history.push('/home');
12 | };
13 |
14 | return (
15 |
16 |
17 |
18 | About Us
19 | Demo
20 | Resources
21 |
22 |
23 |
24 | Reimagine
25 |
26 | Farm Planning
27 |
28 |
29 |
30 |
Plan your next farm years ahead.
31 |
Get valuable insights on our data analytics.
32 |
33 |
34 |
Location
35 |
43 |
44 |
45 |
46 | );
47 | };
48 |
49 | export default Landing;
50 |
--------------------------------------------------------------------------------
/backend/src/insights/ambee/airQuality.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { AMBEE_API_KEY } from 'src/configuration';
3 | import { AmbeeError } from '.';
4 |
5 | interface AirQualityInfo {
6 | pollutant: string;
7 | concentration: number;
8 | category: string;
9 | }
10 |
11 | interface AirQualityStation {
12 | NO2: number;
13 | PM10: number;
14 | PM25: number;
15 | CO: number;
16 | SO2: number;
17 | OZONE: number;
18 | AQI: number;
19 | updatedAt: string;
20 | aqiInfo: AirQualityInfo;
21 | }
22 |
23 | interface AirQualityResponse {
24 | error?: AmbeeError;
25 | message: string;
26 | stations: AirQualityStation[];
27 | }
28 |
29 | export const getAirQuality = async (lat: number, lng: number): Promise => {
30 | try {
31 | const response = await axios.request({
32 | url: 'https://api.ambeedata.com/latest/by-lat-lng',
33 | params: { lat: lat.toString(10), lng: lng.toString(10) },
34 | headers: { 'x-api-key': AMBEE_API_KEY, 'Content-type': 'application/json' },
35 | });
36 | return response.data;
37 | } catch (error) {
38 | return {
39 | error,
40 | message: 'failed',
41 | stations: [],
42 | };
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/backend/src/insights/ambee/pollenLatest.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { AMBEE_API_KEY } from 'src/configuration';
3 | import { AmbeeError } from '.';
4 |
5 | interface PollenCount {
6 | grass_pollen: number;
7 | tree_pollen: number;
8 | weed_pollen: number;
9 | }
10 |
11 | interface PollenRisk {
12 | grass_pollen: string;
13 | tree_pollen: string;
14 | weed_pollen: string;
15 | }
16 |
17 | interface PollenLatestData {
18 | Count: PollenCount;
19 | Risk: PollenRisk;
20 | }
21 |
22 | interface PollenLatestResponse {
23 | error?: AmbeeError;
24 | message: string;
25 | data: PollenLatestData[];
26 | }
27 |
28 | export const getPollenLatest = async (lat: number, lng: number): Promise => {
29 | try {
30 | const response = await axios.request({
31 | method: 'GET',
32 | url: 'https://api.ambeedata.com/latest/pollen/by-lat-lng',
33 | params: { lat: lat.toString(10), lng: lng.toString(10) },
34 | headers: { 'x-api-key': AMBEE_API_KEY, 'Content-type': 'application/json' },
35 | });
36 | return response.data;
37 | } catch (error) {
38 | return {
39 | error,
40 | message: 'failed',
41 | } as PollenLatestResponse;
42 | }
43 | };
44 |
--------------------------------------------------------------------------------
/frontend/src/components/Statistics/Breakdowns/style.scss:
--------------------------------------------------------------------------------
1 | .Breakdowns-Stats {
2 | border: 2px solid #dfdfdf;
3 | border-radius: 6px;
4 | padding: 10px;
5 | background-color: #f9f9f9;
6 | margin: 10px;
7 | width: 470px;
8 | height: 315px;
9 |
10 | h3 {
11 | font-size: 18px;
12 | }
13 |
14 | .subtitle {
15 | margin: 0px;
16 | margin-bottom: 12px;
17 | line-height: 0px;
18 | }
19 |
20 |
21 | .criteria {
22 | margin: 0;
23 | text-align: center;
24 | }
25 |
26 | h1 {
27 | font-size: 7.5em;
28 | }
29 |
30 | .row {
31 | display: flex;
32 | justify-content: space-between;
33 |
34 | .grade {
35 | width: 40%;
36 | display: flex;
37 | flex-direction: column;
38 | align-items: center;
39 | justify-content: center;
40 | margin-bottom: 0px;
41 | margin-top: 2vh;
42 | }
43 | .explanation {
44 | width: 65%;
45 | margin-top: 12px;
46 | margin-top: 2vh;
47 | }
48 | }
49 |
50 | .gradient {
51 | background-size: 100%;
52 | -webkit-background-clip: text;
53 | -moz-background-clip: text;
54 | -webkit-text-fill-color: transparent;
55 | -moz-text-fill-color: transparent;
56 | margin-bottom: 0px;
57 | line-height: 7rem;
58 | }
59 | }
60 |
61 | .Breakdowns::-webkit-scrollbar {
62 | display: none;
63 | }
64 |
--------------------------------------------------------------------------------
/frontend/src/components/BigCurrentlySelected/style.scss:
--------------------------------------------------------------------------------
1 | .big-currently-selected {
2 | background-position: top;
3 | background-size: contain;
4 | border-radius: 6px;
5 | bottom: 1vh;
6 | height: 44vh;
7 | position: absolute;
8 | right: calc(1vw - 1200px);
9 | width: 18vw;
10 |
11 | .information {
12 | border-bottom-left-radius: 6px;
13 | border-bottom-right-radius: 6px;
14 | bottom: 0;
15 | height: 70%;
16 | padding: 10px;
17 | position: absolute;
18 |
19 | h1 {
20 | color: white;
21 | font-size: 24px;
22 | font-weight: 500;
23 | line-height: 100%;
24 | margin: 0;
25 | }
26 | h2 {
27 | color: white;
28 | font-size: 18px;
29 | font-weight: 300;
30 | line-height: 100%;
31 | margin: 0;
32 | margin-bottom: 10px;
33 | }
34 | h3 {
35 | color: white;
36 | font-size: 14px;
37 | line-height: 100%;
38 | margin: 0;
39 | }
40 | p {
41 | color: white;
42 | font-size: 14px;
43 | line-height: 100%;
44 | font-weight: 300;
45 | margin: 0;
46 | }
47 |
48 | .section {
49 | margin-bottom: 10px;
50 |
51 | &.cost {
52 | display: inline-block;
53 | margin-right: 10px;
54 | }
55 | &.state {
56 | display: inline-block;
57 | }
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/backend/src/insights/ambee/weatherLatest.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { AMBEE_API_KEY } from 'src/configuration';
3 | import { AmbeeError } from '.';
4 |
5 | interface WeatherLatestData {
6 | time: number;
7 | temperature: number;
8 | apparentTemperature: number;
9 | dewPoint: number;
10 | humidity: number;
11 | pressure: number;
12 | windSpeed: number;
13 | windGust: number;
14 | windBearing: number;
15 | cloudCover: number;
16 | visibility: number;
17 | ozone: number;
18 | lat: number;
19 | lng: number;
20 | }
21 |
22 | interface WeatherLatestResponse {
23 | error?: AmbeeError;
24 | message: string;
25 | data: WeatherLatestData;
26 | }
27 |
28 | export const getWeatherLatest = async (lat: number, lng: number): Promise => {
29 | try {
30 | const response = await axios.request({
31 | method: 'GET',
32 | url: 'https://api.ambeedata.com/weather/latest/by-lat-lng',
33 | params: { lat: lat.toString(10), lng: lng.toString(10) },
34 | headers: { 'x-api-key': AMBEE_API_KEY, 'Content-type': 'application/json' },
35 | });
36 | return response.data;
37 | } catch (error) {
38 | return {
39 | error,
40 | message: 'failed',
41 | } as WeatherLatestResponse;
42 | }
43 | };
44 |
--------------------------------------------------------------------------------
/frontend/src/components/Analysis/Breakdowns/index.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 |
3 | import './style.scss';
4 | const Breakdowns = (props) => {
5 | const [color1, setColor1] = useState('#7DBDF9');
6 | const [color2, setColor2] = useState('#4091DC');
7 | useEffect(() => {
8 | if (props.grade === 'A' || props.grade === 'B') {
9 | setColor1('#E9FF60');
10 | setColor2('#04D600');
11 | } else if (props.grade === 'C') {
12 | setColor1('#E9FF60');
13 | setColor2('#EF8B2E');
14 | } else if (props.grade === 'D' || props.grade === 'F') {
15 | setColor1('#DC4040');
16 | setColor2('#E3B744');
17 | }
18 | }, [props.grade])
19 |
20 | return (
21 |
22 |
23 |
24 | Grade calculated by realtime data on your location.
25 |
26 |
27 |
28 |
34 | {props.grade}
35 |
36 |
37 |
{props.explanation}
38 |
39 |
40 | );
41 | };
42 |
43 | export default Breakdowns;
44 |
--------------------------------------------------------------------------------
/backend/src/insights/ambee/pollenForecast.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { AMBEE_API_KEY } from 'src/configuration';
3 | import { AmbeeError } from '.';
4 |
5 | interface PollenCount {
6 | grass_pollen: number;
7 | tree_pollen: number;
8 | weed_pollen: number;
9 | }
10 |
11 | interface PollenRisk {
12 | grass_pollen: string;
13 | tree_pollen: string;
14 | weed_pollen: string;
15 | }
16 |
17 | interface PollenForecastData {
18 | time: number;
19 | lat: number;
20 | lng: number;
21 | Count: PollenCount;
22 | Risk: PollenRisk;
23 | }
24 |
25 | interface PollenForecastResponse {
26 | error?: AmbeeError;
27 | message: string;
28 | data: PollenForecastData[];
29 | }
30 |
31 | export const getPollenForecast = async (lat: number, lng: number): Promise => {
32 | try {
33 | const response = await axios.request({
34 | method: 'GET',
35 | url: 'https://api.ambeedata.com/forecast/pollen/by-lat-lng',
36 | params: { lat: lat.toString(10), lng: lng.toString(10) },
37 | headers: { 'x-api-key': AMBEE_API_KEY, 'Content-type': 'application/json' },
38 | });
39 | return response.data;
40 | } catch (error) {
41 | return {
42 | error,
43 | message: 'failed',
44 | } as PollenForecastResponse;
45 | }
46 | };
47 |
--------------------------------------------------------------------------------
/backend/src/insights/ambee/pollenHistory.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { AMBEE_API_KEY } from 'src/configuration';
3 | import { AmbeeError } from '.';
4 |
5 | interface PollenCount {
6 | grass_pollen: number;
7 | tree_pollen: number;
8 | weed_pollen: number;
9 | }
10 |
11 | interface PollenRisk {
12 | grass_pollen: string;
13 | tree_pollen: string;
14 | weed_pollen: string;
15 | }
16 |
17 | interface PollenHistoryData {
18 | Count: PollenCount;
19 | Risk: PollenRisk;
20 | }
21 |
22 | interface PollenHistoryResponse {
23 | error?: AmbeeError;
24 | message: string;
25 | data: PollenHistoryData[];
26 | }
27 |
28 | export const getPollenHistory = async (
29 | lat: number,
30 | lng: number,
31 | from: string,
32 | to: string,
33 | ): Promise => {
34 | try {
35 | const response = await axios.request({
36 | method: 'GET',
37 | url: 'https://api.ambeedata.com/history/pollen/by-lat-lng',
38 | params: { lat: lat.toString(10), lng: lng.toString(10), from: from, to: to },
39 | headers: { 'x-api-key': AMBEE_API_KEY, 'Content-type': 'application/json' },
40 | });
41 | return response.data;
42 | } catch (error) {
43 | return {
44 | error,
45 | message: 'failed',
46 | } as PollenHistoryResponse;
47 | }
48 | };
49 |
--------------------------------------------------------------------------------
/backend/src/insights/ambee/airQualityHistory.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { AMBEE_API_KEY } from 'src/configuration';
3 | import { AmbeeError } from '.';
4 |
5 | interface AirQualityHistoryData {
6 | NO2: number;
7 | PM10: number;
8 | PM25: number;
9 | CO: number;
10 | SO2: number;
11 | OZONE: number;
12 | AQI: number;
13 | lat: number;
14 | lng: number;
15 | createdAt: string;
16 | postalCode: number;
17 | majorPollutant: string;
18 | }
19 |
20 | interface AirQualityHistoryResponse {
21 | error?: AmbeeError;
22 | data: AirQualityHistoryData[];
23 | }
24 |
25 | export const getAirQualityHistory = async (
26 | lat: number,
27 | lng: number,
28 | from: string,
29 | to: string,
30 | ): Promise => {
31 | try {
32 | const response = await axios.request({
33 | method: 'GET',
34 | url: 'https://api.ambeedata.com/history/by-lat-lng',
35 | params: {
36 | lat: lat.toString(10),
37 | lng: lng.toString(10),
38 | from,
39 | to,
40 | },
41 | headers: { 'x-api-key': AMBEE_API_KEY, 'Content-type': 'application/json' },
42 | });
43 | return response.data;
44 | } catch (error) {
45 | return {
46 | error,
47 | } as AirQualityHistoryResponse;
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/backend/src/insights/ambee/weatherHistory.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { AMBEE_API_KEY } from 'src/configuration';
3 | import { AmbeeError } from '.';
4 |
5 | interface WeatherHistoryData {
6 | time: number;
7 | temperature: number;
8 | apparentTemperature: number;
9 | dewPoint: number;
10 | humidity: number;
11 | pressure: number;
12 | windSpeed: number;
13 | windGust: number;
14 | windBearing: number;
15 | cloudCover: number;
16 | visibility: number;
17 | ozone: number;
18 | }
19 |
20 | interface WeatherHistoryResponse {
21 | error?: AmbeeError;
22 | status: string;
23 | data: WeatherHistoryData;
24 | }
25 |
26 | export const getWeatherHistory = async (
27 | lat: number,
28 | lng: number,
29 | from: string,
30 | to: string,
31 | ): Promise => {
32 | try {
33 | const response = await axios.request({
34 | method: 'GET',
35 | url: 'https://api.ambeedata.com/weather/history/by-lat-lng',
36 | params: { lat: lat.toString(10), lng: lng.toString(10), from: from, to: to },
37 | headers: { 'x-api-key': AMBEE_API_KEY, 'Content-type': 'application/json' },
38 | });
39 | return response.data;
40 | } catch (error) {
41 | return {
42 | error,
43 | status: 'failed',
44 | } as WeatherHistoryResponse;
45 | }
46 | };
47 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@mapbox/mapbox-gl-draw": "^1.2.2",
7 | "@testing-library/jest-dom": "^5.11.4",
8 | "@testing-library/react": "^11.1.0",
9 | "@testing-library/user-event": "^12.1.10",
10 | "antd": "^4.15.1",
11 | "analysis" : "1.0.0",
12 | "gsap": "^3.6.1",
13 | "mapbox": "^1.0.0-beta10",
14 | "mapbox-gl": "^2.2.0",
15 | "mapbox-gl-draw-rectangle-restrict-area": "^3.1.5",
16 | "mapboxgl-draw-rectangle-drag": "^1.0.1",
17 | "react": "^17.0.2",
18 | "react-dom": "^17.0.2",
19 | "react-draggable": "^4.4.3",
20 | "react-mapbox-draw-rectangle": "^0.0.5",
21 | "react-mapbox-gl": "^5.1.1",
22 | "react-router-dom": "^5.2.0",
23 | "react-scripts": "4.0.3",
24 | "recharts": "^2.0.9",
25 | "sass": "^1.32.10",
26 | "three": "^0.115.0",
27 | "web-vitals": "^1.0.1"
28 | },
29 | "scripts": {
30 | "start": "react-scripts start",
31 | "build": "react-scripts build",
32 | "test": "react-scripts test",
33 | "eject": "react-scripts eject"
34 | },
35 | "browserslist": {
36 | "production": [
37 | ">0.2%",
38 | "not dead",
39 | "not op_mini all"
40 | ],
41 | "development": [
42 | "last 1 chrome version",
43 | "last 1 firefox version",
44 | "last 1 safari version"
45 | ]
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/frontend/src/components/Timeline/index.jsx:
--------------------------------------------------------------------------------
1 | import { Slider } from 'antd';
2 |
3 | import './style.scss';
4 | import timer from 'assets/timer.svg';
5 |
6 | const marks = {
7 | 336: {
8 | label: Spring
,
9 | },
10 | 63: {
11 | label: Summer
,
12 | },
13 | 157: {
14 | label: Fall
,
15 | },
16 | 247: {
17 | label: Winter
,
18 | },
19 | };
20 |
21 | const Timeline = (props) => {
22 | const onChange = (value) => {
23 | props.setDay((108 + value));
24 | Object.values(props.objects).forEach((object) => {
25 | console.log(props.objects);
26 | console.log('testtt');
27 | console.log(object.day);
28 | const dayDiff = props.day - object.day;
29 | console.log(dayDiff);
30 | object.model.scale.y = Math.max((dayDiff * 2) + 100, -1);
31 | object.model.position.y = Math.max((((100 / 3.28084) / 100) * ((dayDiff * 1) + 50)), -1);
32 | });
33 | }
34 |
35 | return (
36 |
37 |
38 |
39 |
40 |
Timeline
41 | Over Seasons
42 |
43 |
44 |
45 |
46 |
47 |
48 | )
49 | }
50 |
51 | export default Timeline;
--------------------------------------------------------------------------------
/frontend/src/components/Seasons/style.scss:
--------------------------------------------------------------------------------
1 | .seasons {
2 | align-items: center;
3 | border-radius: 6px;
4 | display: flex;
5 | height: 9vh;
6 | left: 6vw;
7 | position: absolute;
8 | top: calc(9vh - 200px);
9 | width: 45vw;
10 | z-index: 800;
11 | white-space: nowrap;
12 |
13 | h1 {
14 | color: white;
15 | font-weight: 500;
16 | line-height: 100%;
17 | margin: 0;
18 | }
19 |
20 | h2 {
21 | color: white;
22 | font-weight: 300;
23 | line-height: 100%;
24 | margin: 0;
25 | }
26 |
27 | .season {
28 | padding: 0 20px;
29 | width: 28%;
30 | h1 {
31 | font-size: 24px;
32 | }
33 | h2 {
34 | font-size: 18px;
35 | }
36 | }
37 |
38 | .temperature {
39 | width: 20%;
40 | padding: 0 10px;
41 | h1 {
42 | font-size: 16px;
43 | margin-top: 5px;
44 | }
45 | h2 {
46 | font-size: 18px;
47 | }
48 | }
49 |
50 | .weather {
51 | align-items: center;
52 | display: flex;
53 | justify-content: center;
54 | position: relative;
55 | width: 50%;
56 |
57 | .weather-entry {
58 | align-items: center;
59 | display: flex;
60 | justify-content: center;
61 | padding: 0 5px;
62 | width: 20%;
63 |
64 | img {
65 | display: inline-block;
66 | }
67 | .text {
68 | display: inline-block;
69 | h1 {
70 | font-size: 18px;
71 | }
72 | h2 {
73 | font-size: 10px;
74 | }
75 | }
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/frontend/src/components/Statistics/Breakdowns/index.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 |
3 | import './style.scss';
4 |
5 | const Breakdowns = (props) => {
6 | const [color1, setColor1] = useState('#7DBDF9');
7 | const [color2, setColor2] = useState('#4091DC');
8 | useEffect(() => {
9 | if (props.grade === 'A' || props.grade === 'B') {
10 | setColor1('#E9FF60');
11 | setColor2('#04D600');
12 | } else if (props.grade === 'C') {
13 | setColor1('#E9FF60');
14 | setColor2('#EF8B2E');
15 | } else if (props.grade === 'D' || props.grade === 'F') {
16 | setColor1('#DC4040');
17 | setColor2('#E3B744');
18 | }
19 | }, [props.grade])
20 |
21 | return (
22 |
23 |
{props.title}
24 |
25 | Grade calculated by realtime data on your location.
26 |
27 |
28 |
29 |
35 | {props.grade}
36 |
37 |
38 | {props.criteria}
39 |
40 |
41 |
42 |
{props.explanation}
43 |
44 |
45 | );
46 | };
47 |
48 | export default Breakdowns;
49 |
--------------------------------------------------------------------------------
/backend/src/insights/ambee/weatherForecast.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { AMBEE_API_KEY } from 'src/configuration';
3 | import { AmbeeError } from '.';
4 |
5 | interface WeatherForecastData {
6 | lat: number;
7 | lng: number;
8 | forecast: WeatherForecast[];
9 | }
10 |
11 | interface WeatherForecast {
12 | time: number;
13 | precipIntensity: number;
14 | precipProbability: number;
15 | precipType: string;
16 | temperature: number;
17 | apparentTemperature: number;
18 | dewPoint: number;
19 | humidity: number;
20 | pressure: number;
21 | windSpeed: number;
22 | windGust: number;
23 | windBearing: number;
24 | cloudCover: number;
25 | uvIndex: number;
26 | visibility: number;
27 | ozone: number;
28 | }
29 |
30 | interface WeatherForecastResponse {
31 | error?: AmbeeError;
32 | message: string;
33 | data: WeatherForecastData;
34 | }
35 |
36 | export const getWeatherForecast = async (
37 | lat: number,
38 | lng: number,
39 | filter: string = 'daily',
40 | ): Promise => {
41 | try {
42 | const response = await axios.request({
43 | method: 'GET',
44 | url: 'https://api.ambeedata.com/weather/forecast/by-lat-lng',
45 | params: { lat: lat.toString(10), lng: lng.toString(10), filter },
46 | headers: { 'x-api-key': AMBEE_API_KEY, 'Content-type': 'application/json' },
47 | });
48 | return response.data;
49 | } catch (error) {
50 | return {
51 | error,
52 | message: 'failed',
53 | } as WeatherForecastResponse;
54 | }
55 | };
56 |
--------------------------------------------------------------------------------
/frontend/src/assets/cloudy.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/frontend/src/components/Landing/style.scss:
--------------------------------------------------------------------------------
1 | .Landing {
2 | height: 100vh;
3 | width: 100vw;
4 | background-image: url('../../assets/homebg.png');
5 | background-repeat: no-repeat;
6 | background-size: cover;
7 | padding: 50px;
8 | color: white;
9 | font-family: 'Helvetica', sans-serif;
10 |
11 | .space {
12 | h3 {
13 | color: white;
14 | font-size: 24px;
15 | margin-bottom: 0px;
16 | }
17 |
18 | margin: 40px 0px 10px 0px;
19 | }
20 |
21 | .label {
22 | color: white;
23 | }
24 |
25 | .signup {
26 | background: rgba(255, 255, 255, 0.05);
27 | padding: 25px;
28 | width: 500px;
29 | backdrop-filter: blur(5px);
30 | border-radius: 12px;
31 | position: absolute;
32 | top: 33%;
33 | right: 10%;
34 | h2 {
35 | color: white;
36 | font-size: 48px;
37 | font-weight: 600;
38 | line-height: 48px;
39 | }
40 |
41 | p {
42 | font-size: 18px;
43 | font-weight: 300;
44 | }
45 | }
46 |
47 | ul {
48 | list-style-type: none;
49 | overflow: hidden;
50 | display: flex;
51 | li {
52 | padding: 0px 40px;
53 | font-size: 22px;
54 | font-weight: 400;
55 | }
56 |
57 | .about {
58 | padding-left: 0px;
59 | }
60 | }
61 |
62 | h1 {
63 | font-size: 82px;
64 | font-family: 'Helvetica';
65 | font-weight: 600;
66 | line-height: 90px;
67 | position: absolute;
68 | bottom: 5%;
69 | color: white;
70 | }
71 |
72 | .search {
73 | border-radius: 12px;
74 | }
75 |
76 | .ant-input-group-addon {
77 | border-radius: 12px;
78 | }
79 |
80 | .ant-input-group {
81 | opacity: 0.5;
82 | }
83 |
84 | .ant-input-group:hover {
85 | transition-duration: 1s;
86 | opacity: 0.9;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/frontend/src/components/Analysis/style.scss:
--------------------------------------------------------------------------------
1 | .analysis {
2 | position: absolute;
3 | top: 10vh;
4 | height: 50vh;
5 | left: calc(52vw + 1200px);
6 | width: 48vw;
7 |
8 | .cards {
9 | align-items: center;
10 | display: flex;
11 | justify-content: center;
12 | margin-top: 20px;
13 | }
14 |
15 | .heading {
16 | align-items: center;
17 | display: flex;
18 |
19 | .text {
20 | margin-left: 10px;
21 |
22 | h1 {
23 | font-size: 36px;
24 | font-weight: 500;
25 | line-height: 100%;
26 | margin: 0;
27 | }
28 |
29 | h2 {
30 | font-size: 18px;
31 | font-weight: 300;
32 | line-height: 100%;
33 | margin: 0;
34 | }
35 | }
36 | }
37 |
38 | .air-quality {
39 | display: inline-block;
40 | opacity: 0;
41 | background: #F9F9F9;
42 | border: 1px solid #DFDFDF;
43 | box-sizing: border-box;
44 | border-radius: 4.35px;
45 | height: 17vh;
46 | width: 17.4vw;
47 | margin-top: calc(2vh + 10px);
48 | margin-left: .6vw;
49 | padding: 5px 10px;
50 | overflow: scroll;
51 | -ms-overflow-style: none; /* IE and Edge */
52 | scrollbar-width: none; /* Firefox */
53 |
54 | &::-webkit-scrollbar {
55 | display: none;
56 | }
57 |
58 | h3 {
59 | font-size: 18px;
60 | }
61 |
62 | .section {
63 | position: relative;
64 | width: 100%;
65 | p {
66 | margin: 0px 0;
67 | line-height: 100%;
68 | }
69 |
70 | .score {
71 | right: 0;
72 | top: 0;
73 | position: absolute;
74 | margin: 0;
75 | }
76 |
77 | hr {
78 | border: 1px solid #DFDFDF;
79 | }
80 | }
81 | }
82 |
83 | .Breakdowns {
84 | opacity: 0;
85 | position: relative;
86 | }
87 | }
--------------------------------------------------------------------------------
/frontend/src/components/PlotType/style.scss:
--------------------------------------------------------------------------------
1 | .plot-type {
2 | position: absolute;
3 | bottom: 0;
4 | height: 44vh;
5 | left: calc(52vw + 1200px);
6 | width: 28vw;
7 |
8 | .filter {
9 | margin: auto;
10 | margin-top: 10px;
11 | position: relative;
12 | width: 95%;
13 |
14 | .select {
15 | position: absolute;
16 | top: 0;
17 | right: 0;
18 | }
19 | }
20 |
21 | .heading {
22 | align-items: center;
23 | display: flex;
24 |
25 | .text {
26 | margin-left: 10px;
27 |
28 | h1 {
29 | font-size: 36px;
30 | font-weight: 500;
31 | line-height: 100%;
32 | margin: 0;
33 | }
34 |
35 | h2 {
36 | font-size: 18px;
37 | font-weight: 300;
38 | line-height: 100%;
39 | margin: 0;
40 | }
41 | }
42 | }
43 |
44 | .search-bar {
45 | align-items: center;
46 | display: flex;
47 | position: relative;
48 | margin-top: 15px;
49 | right: -2.5%;
50 |
51 | img {
52 | left: 10px;
53 | position: absolute;
54 | width: 24px;
55 | }
56 |
57 | input {
58 | background: #F9F9F9;
59 | border: none;
60 | border-radius: 6px;
61 | color: #333333;
62 | font-size: 18px;
63 | padding: 10px 20px 10px 44px;
64 | width: 95%;
65 |
66 | &:focus{
67 | outline: none;
68 | }
69 | }
70 | }
71 |
72 | .plots {
73 | bottom: 0;
74 | height: 26vh;
75 | margin: auto;
76 | margin-top: 20px;
77 | overflow: scroll;
78 | padding: 10px 2.5%;
79 | position: absolute;
80 | scrollbar-width: none;
81 | width: 100%;
82 | -ms-overflow-style: none;
83 |
84 | .plot {
85 | opacity: 100;
86 | }
87 |
88 | &::-webkit-scrollbar {
89 | display: none;
90 | }
91 | }
92 | }
--------------------------------------------------------------------------------
/frontend/src/components/Plot/index.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 |
3 | import './style.scss';
4 |
5 | import crop from 'assets/crop.svg';
6 | import irrigation from 'assets/irrigation.svg';
7 |
8 | const Plot = (props) => {
9 | const [borderColor, setBorderColor] = useState('#13D181');
10 | const [color, setColor] = useState('rgba(39, 174, 96, 1)');
11 |
12 | useEffect(() => {
13 | if (props.type === 'crop') {
14 | if (props.state === 'Spring' || props.state === 'Summer') {
15 | setColor('rgba(39, 174, 96, 1)');
16 | setBorderColor('#13D181');
17 | } else {
18 | setColor('rgba(237, 193, 81, 1)');
19 | setBorderColor('#EDC151');
20 | }
21 | }
22 | else {
23 | setColor('rgba(64, 145, 220, 1)');
24 | setBorderColor('#4091DC');
25 | }
26 | }, [props.type, props.state])
27 |
28 | return (
29 |
38 |
39 |
40 |
{(props.type === 'crop') ? 'Crop' : 'Irrigation'}
41 | {props.name}
42 |
43 |
44 |
45 |
Earth Score
46 | {props.earthScore}/100
47 |
48 |
49 |
50 |
{(props.type === 'crop') ? 'Season' : 'Maintenance'}
51 | {props.state}
52 |
53 |
54 | )
55 | }
56 |
57 | export default Plot;
--------------------------------------------------------------------------------
/backend/src/utils/getLocGrade.ts:
--------------------------------------------------------------------------------
1 |
2 | interface Grade {
3 | score: Score;
4 | description: string
5 | }
6 | enum Score {
7 | "F" = 0,
8 | "D",
9 | "D+",
10 | "C",
11 | "C+",
12 | "B",
13 | "B+",
14 | "A",
15 | "A+"
16 | }
17 |
18 | const scoreToDescription = (score: Score): string => {
19 | switch(score) {
20 | case Score.A, Score["A+"]: return "Air quality is satisfactory, and air pollution poses little or no risk."
21 | case Score.B, Score["B+"]: return "Air quality is acceptable. However, there may be a risk for some people, particularly those who are unusually sensitive to air pollution."
22 | case Score.C, Score["C+"]: return "Members of sensitive groups may experience health effects. The general public is less likely to be affected."
23 | case Score.D, Score["D+"]: return "Some members of the general public may experience health effects; members of sensitive groups may experience more serious health effects."
24 | case Score.F: return "Health alert: The risk of health effects is increased for everyone."
25 | }
26 | return "Air quality is satisfactory, and air pollution poses little or no risk."
27 | }
28 |
29 | export const numToGrade = (score: number): Grade => {
30 | score = Math.round(score);
31 | return {
32 | score: score,
33 | description: scoreToDescription(score)
34 | }
35 | }
36 |
37 | export const getGrade = (aqi: number): Score => {
38 | if (aqi < 37.5) return Score["A+"];
39 | else if (aqi < 75) return Score["A"];
40 | else if (aqi < 112.5) return Score["B+"];
41 | else if (aqi < 150) return Score["B"];
42 | else if (aqi < 187.5) return Score["C+"];
43 | else if (aqi < 225) return Score["C"];
44 | else if (aqi < 262.5) return Score["D+"];
45 | else if (aqi < 300) return Score["D"];
46 | else return Score["F"];
47 | }
--------------------------------------------------------------------------------
/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "backend",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "build/index.js",
6 | "scripts": {
7 | "build": "tsc",
8 | "start": "node -r ./tsconfig-paths-bootstrap.js .",
9 | "start:dev": "node -r dotenv/config -r tsconfig-paths/register -r ts-node/register ./src/index.ts",
10 | "dev": "nodemon",
11 | "test:unit": "mocha --recursive -r tsconfig-paths/register -r ts-node/register -r source-map-support/register src/**/*.spec.ts",
12 | "test:lint": "eslint --ext .ts ./src",
13 | "test:lint:fix": "npm run test:lint -- --fix",
14 | "test": "npm run test:lint && npm run test:unit"
15 | },
16 | "keywords": [],
17 | "author": "",
18 | "license": "ISC",
19 | "dependencies": {
20 | "axios": "^0.21.1",
21 | "body-parser": "^1.19.0",
22 | "cors": "^2.8.5",
23 | "express": "^4.17.1",
24 | "express-validator": "^6.10.0",
25 | "moment": "^2.29.1"
26 | },
27 | "devDependencies": {
28 | "@types/body-parser": "^1.17.1",
29 | "@types/chai": "^4.2.3",
30 | "@types/cors": "^2.8.10",
31 | "@types/dotenv": "^6.1.1",
32 | "@types/express": "^4.17.1",
33 | "@types/mocha": "^5.2.7",
34 | "@types/supertest": "^2.0.8",
35 | "@typescript-eslint/eslint-plugin": "^2.3.0",
36 | "@typescript-eslint/parser": "^2.3.0",
37 | "chai": "^4.2.0",
38 | "dotenv": "^8.1.0",
39 | "eslint": "^6.4.0",
40 | "eslint-config-prettier": "^6.3.0",
41 | "eslint-plugin-prettier": "^3.1.1",
42 | "mocha": "^6.2.0",
43 | "nodemon": "^1.19.2",
44 | "prettier": "^1.18.2",
45 | "supertest": "^4.0.2",
46 | "ts-node": "^8.4.1",
47 | "tsconfig-paths": "^3.9.0",
48 | "typescript": "^3.6.3"
49 | },
50 | "nodemonConfig": {
51 | "ignore": [
52 | "**/*.spec.ts",
53 | ".git",
54 | "node_modules"
55 | ],
56 | "watch": [
57 | "src"
58 | ],
59 | "exec": "npm run start:dev",
60 | "ext": "ts"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/frontend/src/components/Statistics/EstimatedFinancials/index.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | BarChart,
3 | Bar,
4 | Cell,
5 | XAxis,
6 | YAxis,
7 | CartesianGrid,
8 | Tooltip,
9 | Legend,
10 | ReferenceLine,
11 | ResponsiveContainer,
12 | } from 'recharts';
13 |
14 | //import * as Analysis from "analysis"
15 |
16 | import './style.scss';
17 |
18 | const EstimatedFinancials = (props) => {
19 |
20 | const result = Analysis.determineSpendingAndRevenue(props.statsData)
21 |
22 | const data = [
23 | {
24 | name: 'Spring 2023',
25 | },
26 | {
27 | name: 'Summer 2023',
28 | },
29 | {
30 | name: 'Fall 2023',
31 | },
32 | {
33 | name: 'Winter 2023',
34 | },
35 | ];
36 |
37 | for (let j = 0; j < data.length; j++) {
38 | data[j].spending = -result.spending[j].toFixed(2)
39 | data[j].revenue = result.revenue[j].toFixed(2)
40 | }
41 |
42 | return (
43 |
44 |
Estimated Financials
45 |
Based off location and current harvest
46 |
47 |
56 |
57 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | );
73 | };
74 |
75 | export default EstimatedFinancials;
76 |
--------------------------------------------------------------------------------
/analysis/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "analysis",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "build/index.js",
6 | "scripts": {
7 | "build": "tsc",
8 | "build-watch": "nodemon --exec yarn build",
9 | "start": "node -r ./tsconfig-paths-bootstrap.js .",
10 | "start:dev": "node -r dotenv/config -r tsconfig-paths/register -r ts-node/register ./src/index.ts",
11 | "dev": "nodemon",
12 | "test:unit": "mocha --recursive -r tsconfig-paths/register -r ts-node/register -r source-map-support/register src/**/*.spec.ts",
13 | "test:lint": "eslint --ext .ts ./src",
14 | "test:lint:fix": "npm run test:lint -- --fix",
15 | "test": "npm run test:lint && npm run test:unit"
16 | },
17 | "keywords": [],
18 | "author": "",
19 | "license": "ISC",
20 | "dependencies": {
21 | "animejs": "^3.2.1",
22 | "axios": "^0.21.1",
23 | "body-parser": "^1.19.0",
24 | "cors": "^2.8.5",
25 | "express": "^4.17.1",
26 | "express-validator": "^6.10.0",
27 | "moment": "^2.29.1"
28 | },
29 | "devDependencies": {
30 | "@types/body-parser": "^1.17.1",
31 | "@types/chai": "^4.2.3",
32 | "@types/cors": "^2.8.10",
33 | "@types/dotenv": "^6.1.1",
34 | "@types/express": "^4.17.1",
35 | "@types/mocha": "^5.2.7",
36 | "@types/supertest": "^2.0.8",
37 | "@typescript-eslint/eslint-plugin": "^2.3.0",
38 | "@typescript-eslint/parser": "^2.3.0",
39 | "chai": "^4.2.0",
40 | "dotenv": "^8.1.0",
41 | "eslint": "^6.4.0",
42 | "eslint-config-prettier": "^6.3.0",
43 | "eslint-plugin-prettier": "^3.1.1",
44 | "mocha": "^6.2.0",
45 | "nodemon": "^1.19.2",
46 | "prettier": "^1.18.2",
47 | "supertest": "^4.0.2",
48 | "ts-node": "^8.4.1",
49 | "tsconfig-paths": "^3.9.0",
50 | "typescript": "^3.6.3"
51 | },
52 | "nodemonConfig": {
53 | "ignore": [
54 | "**/*.spec.ts",
55 | ".git",
56 | "node_modules"
57 | ],
58 | "watch": [
59 | "src"
60 | ],
61 | "exec": "npm run start:dev",
62 | "ext": "ts"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
16 |
17 |
21 |
22 |
31 | drishti
32 |
33 |
34 | You need to enable JavaScript to run this app.
35 |
36 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/frontend/src/assets/timer.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/frontend/src/components/Statistics/EnvironmentalStrain/index.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | AreaChart,
3 | Area,
4 | XAxis,
5 | YAxis,
6 | CartesianGrid,
7 | Tooltip,
8 | Legend,
9 | ResponsiveContainer,
10 | } from 'recharts';
11 | //import * as Analysis from "analysis";
12 | import './style.scss';
13 |
14 | const EnvironmentalStrain = (props) => {
15 | const result = Analysis.determineWaterUsageAndYield(props.statsData)
16 | const data = [
17 | {
18 | name: 'Spring 2023',
19 | },
20 | {
21 | name: 'Summer 2023',
22 | },
23 | {
24 | name: 'Fall 2023',
25 | },
26 | {
27 | name: 'Winter 2023',
28 | },
29 | ];
30 |
31 | for (let j = 0; j < data.length; j++) {
32 | data[j].water = result.waterUsage[j].toFixed(2)
33 | data[j].yield = result.yield[j].toFixed(2) / 10000
34 | }
35 |
36 | return (
37 |
38 |
Environmental Strain
39 |
Plotted on 2 key metrics
40 |
41 |
50 |
51 |
57 |
58 |
59 |
60 |
67 |
74 |
75 |
76 |
77 | );
78 | };
79 |
80 | export default EnvironmentalStrain;
81 |
--------------------------------------------------------------------------------
/frontend/src/components/BigCurrentlySelected/index.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 |
3 | import './style.scss';
4 |
5 | const BigCurrentlySelected = (props) => {
6 | const [color, setColor] = useState('rgba(39, 174, 96, 1)');
7 |
8 | useEffect(() => {
9 | if (props.currentPlot && props.currentPlot.type === 'crop') {
10 | if (props.currentPlot.state === 'Spring' || props.currentPlot.state === 'Summer') {
11 | setColor('rgba(39, 174, 96, 1)');
12 | } else {
13 | setColor('rgba(237, 193, 81, 1)');
14 | }
15 | }
16 | else {
17 | setColor('rgba(64, 145, 220, 1)');
18 | }
19 | }, [props.currentPlot])
20 |
21 | return (
22 |
29 |
35 |
Current Selection
36 |
{props.currentPlot.name || 'N/A'}
37 |
38 |
What is it
39 |
{props.currentPlot.summary || 'N/A'}
40 |
41 |
42 |
Cost
43 |
{props.currentPlot.cost || 'N/A'}
44 |
45 |
46 |
Maintenance
47 |
{props.currentPlot.state || 'N/A'}
48 |
49 |
50 |
Environmental Impact
51 |
{props.currentPlot.environmentalImpact || 'N/A'}
52 |
53 |
54 |
Verdict
55 |
{props.currentPlot.verdict || 'N/A'}
56 |
57 |
58 |
59 | )
60 | }
61 |
62 | export default BigCurrentlySelected;
--------------------------------------------------------------------------------
/frontend/src/components/Statistics/CropRotation/index.jsx:
--------------------------------------------------------------------------------
1 | import { Button } from "antd";
2 | import Plot from "../../Plot";
3 | import insights from "./earthData.json";
4 | // import * as Analysis from "../../../../../analysis";
5 | import redo from "../../../assets/redo.svg";
6 | import "./style.scss";
7 | import { useState } from "react";
8 |
9 | const CropRotation = (props) => {
10 |
11 | const data = props.statsData.map((currentCrop) => {
12 | const cropData = Analysis.dataForCrop(currentCrop.crop);
13 | const newCrop = Analysis.findAlternateCrop(cropData);
14 | return {
15 | currentCrop: cropData,
16 | newCrop
17 | };
18 | });
19 |
20 | const [currentIdx, setIdx] = useState(0)
21 | const currentData = data[currentIdx];
22 |
23 | const nextPage = () => {
24 | if (currentIdx === data.length - 1) {
25 | setIdx(0)
26 | } else {
27 | setIdx(currentIdx + 1)
28 | }
29 | }
30 | const prevPage = () => {
31 | if (currentIdx === 0) {
32 | setIdx(data.length - 1)
33 | } else {
34 | setIdx(currentIdx - 1)
35 | }
36 | }
37 |
38 | return (
39 |
40 |
Crop Rotations
41 |
Recommendation based off nutrient levels
42 |
43 | Recommended Rotations
44 |
45 |
48 |
49 |
50 |
51 |
52 | Rotates To
53 |
54 |
57 |
58 |
59 | Back
60 |
61 | Next
62 |
63 |
64 | );
65 | };
66 |
67 | export default CropRotation;
68 |
--------------------------------------------------------------------------------
/backend/src/utils/getEnvGrade.ts:
--------------------------------------------------------------------------------
1 |
2 | interface Grade {
3 | score: Score;
4 | description: string
5 | }
6 | enum Score {
7 | "F" = 0,
8 | "D",
9 | "D+",
10 | "C",
11 | "C+",
12 | "B",
13 | "B+",
14 | "A",
15 | "A+"
16 | }
17 |
18 | const scoreToDescription = (score: Score): string => {
19 | switch(score) {
20 | case Score.A, Score["A+"]: return "This place is great overall, the temperature is near perfect, and the moisture is flawless as well!"
21 | case Score.B, Score["B+"]: return "This place is not bad, the temperature is pretty good, but the soil moisture isn't the best — watering and fertilizing will be key to get the most out of this space."
22 | case Score.C, Score["C+"]: return "This place isn't the best — it's temperature isn't in a great range nor is the soil moisture suitable for industrial farming. You'll need a lot of heaters / fans + fertilizer + water to build on this spot."
23 | case Score.D, Score["D+"]: return "We wouldn't recommend farming here. The temperature is not conducive to agricultural growth"
24 | case Score.F: return "We cannot give any recommendations for farming here — The temperature is not conducive to agricultural growth and the soil moisture is horrible"
25 | }
26 | }
27 |
28 | /*
29 | A: This place is great overall, the temperature is near perfect, and the moisture is flawless as well!
30 | B: This place is not bad, the temperature is pretty good, but the soil moisture isn't the best — watering and fertilizing will be key to get the most out of this space.
31 | C: This place isn't the best — it's temperature isn't in a great range nor is the soil moisture suitable for industrial farming. You'll need a lot of heaters / fans + fertilizer + water to build on this spot.
32 | D: We wouldn't recommend farming here. The temperature is not conducive to agricultural growth
33 | F: We cannot give any recommendations for farming here — The temperature is not conducive to agricultural growth and the soil moisture is horrible
34 | */
35 | export const numToGrade = (score: number): Grade => {
36 | score = Math.round(score);
37 | return {
38 | score: score,
39 | description: scoreToDescription(score)
40 | }
41 | }
42 |
43 | export const getGrade = (moisture: number,temp: number): number => {
44 | const weights = [0.1,0.9];
45 | const moistureScore = 1 - Math.abs(40 - moisture)/40;
46 | const tempScore = 1 - Math.abs(70 - temp)/70;
47 | return (moistureScore*weights[0] + tempScore*weights[1]) * 9;
48 | }
--------------------------------------------------------------------------------
/frontend/src/assets/raining.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/frontend/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/backend/src/insights/weather/index.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { Router, Request, Response } from 'express';
3 | import { validationResult, check } from 'express-validator';
4 | import { WEATHER_API_KEY, USE_API } from 'src/configuration';
5 | import { weatherDateParse } from 'src/utils/parseDate';
6 | import weatherResponse from "src/data/weatherResponse.json"
7 |
8 | const router = Router();
9 |
10 | const handler = async (req: Request, res: Response) => {
11 |
12 | await check('from')
13 | .isString()
14 | .run(req);
15 | await check('to')
16 | .isString()
17 | .run(req);
18 |
19 | const errors = validationResult(req);
20 | if (!errors.isEmpty()) {
21 | return res.status(400).json({
22 | message: 'Invalid Parameters',
23 | errors: errors.array(),
24 | parametersSent: req.body,
25 | });
26 | } else {
27 | const from = weatherDateParse(req.body.from)
28 | const to = weatherDateParse(req.body.to)
29 |
30 | try {
31 | if (!USE_API) {
32 | throw new Error("Problems")
33 | }
34 | const result = await getWeatherForecast(from, to);
35 | return res.json({
36 | from,
37 | to,
38 | weather: "result",
39 | });
40 | } catch (err) {
41 | // return cached data
42 | console.log("Using cached response")
43 | return res.json(weatherResponse)
44 | }
45 | }
46 | };
47 |
48 | router.post('/', handler)
49 | router.get('/', handler)
50 |
51 | interface WeatherForecast {
52 | date: string;
53 | max_temp_high: number;
54 | max_temp_low: number;
55 | min_temp_code: string;
56 | min_temp_high: number;
57 | min_temp_low: number;
58 | precipitation: string;
59 | precipitation_code: string;
60 | temp: string;
61 | temp_code: string;
62 | }
63 |
64 | interface WeatherResponse {
65 | status: string;
66 | error?: any;
67 | location?: string;
68 | forecast?: WeatherForecast[];
69 | }
70 |
71 | const getWeatherForecast = async (from: string, to: string): Promise => {
72 | try {
73 | const response = await axios.request({
74 | url: `https://weatherplanner.azure-api.net/v1/Forecast/Des Moines/${from}/${to}`,
75 | params: { 'subscription-key': WEATHER_API_KEY },
76 | headers: { 'Content-type': 'application/json' },
77 | });
78 | return response.data;
79 | } catch (error) {
80 | return {
81 | status: 'failed',
82 | error: error,
83 | };
84 | }
85 | };
86 |
87 | export default router;
88 |
--------------------------------------------------------------------------------
/frontend/src/components/PlotType/index.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import { Select } from 'antd';
3 |
4 | import './style.scss';
5 |
6 | import search from 'assets/search.svg';
7 | import sprout from 'assets/sprout.svg';
8 |
9 | import Plot from 'components/Plot';
10 |
11 | const { Option } = Select;
12 |
13 | const PlotType = (props) => {
14 | const [filter, setFilter] = useState('all');
15 | const [searchValue, setSearchValue] = useState('');
16 |
17 | const handleChange = (value) => {
18 | setFilter(value);
19 | }
20 |
21 | const handleSearch = (event) => {
22 | setSearchValue(event.target.value);
23 | }
24 |
25 | return (
26 |
27 |
28 |
29 |
30 |
Select Plot Type
31 | Includes crops, buildings, and irrigation
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
Recommended by AI
40 |
41 |
42 | All
43 | Crop
44 | Irrigation
45 |
46 |
47 |
48 |
49 | {props.plots.map((plot) => {
50 | if (
51 | (plot.name.toLowerCase().startsWith(searchValue.toLowerCase()) ||
52 | plot.type.toLowerCase().startsWith(searchValue.toLowerCase()) ||
53 | plot.state.toLowerCase().startsWith(searchValue.toLowerCase())) &&
54 | (filter === 'all' ||
55 | (filter === 'crops' && plot.type === 'crop') ||
56 | (filter === 'irrigation' && plot.type==='irrigation'))
57 | ) {
58 | return (
59 |
{
66 | props.setCurrentPlot(plot);
67 | if (props.draw) {
68 | props.draw.draw.changeMode('draw_rectangle');
69 | }
70 | }}
71 | />
72 | )
73 | }
74 | })}
75 |
76 |
77 | );
78 | }
79 |
80 | export default PlotType;
--------------------------------------------------------------------------------
/frontend/src/components/StatsModal/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Modal, Button } from 'antd';
3 | import Draggable from 'react-draggable';
4 |
5 | import CropRotation from '../Statistics/CropRotation';
6 | import EstimatedFinancials from '../Statistics/EstimatedFinancials';
7 | import EnvironmentalStrain from '../Statistics/EnvironmentalStrain';
8 | import FarmBreakdown from '../Statistics/FarmBreakdown';
9 | import Breakdowns from '../Statistics/Breakdowns';
10 |
11 | import title from '../../assets/harveststats.svg';
12 | import './style.scss';
13 |
14 | const StatsModal = (props) => {
15 | const [bounds, setBounds] = useState({
16 | bounds: { left: 0, top: 0, bottom: 0, right: 0 },
17 | });
18 |
19 | let draggleRef = React.createRef();
20 |
21 | const onStart = (event, uiData) => {
22 | const { clientWidth, clientHeight } = window?.document?.documentElement;
23 | const targetRect = draggleRef?.current?.getBoundingClientRect();
24 | setBounds({
25 | bounds: {
26 | left: -targetRect?.left + uiData?.x,
27 | right: clientWidth - (targetRect?.right - uiData?.x),
28 | top: -targetRect?.top + uiData?.y,
29 | bottom: clientHeight - (targetRect?.bottom - uiData?.y),
30 | },
31 | });
32 | };
33 |
34 | return (
35 |
36 |
(
44 | onStart(event, uiData)}
48 | >
49 | {modal}
50 |
51 | )}
52 | >
53 |
54 |
55 |
62 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | );
79 | };
80 |
81 | export default StatsModal;
82 |
--------------------------------------------------------------------------------
/frontend/src/components/Statistics/FarmBreakdown/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useState } from 'react';
2 | import { PieChart, Pie, Sector, ResponsiveContainer } from 'recharts';
3 |
4 | import './style.scss';
5 |
6 | const renderActiveShape = (props) => {
7 | const RADIAN = Math.PI / 180;
8 | const {
9 | cx,
10 | cy,
11 | midAngle,
12 | innerRadius,
13 | outerRadius,
14 | startAngle,
15 | endAngle,
16 | fill,
17 | payload,
18 | percent,
19 | value,
20 | } = props;
21 | const sin = Math.sin(-RADIAN * midAngle);
22 | const cos = Math.cos(-RADIAN * midAngle);
23 | const sx = cx + (outerRadius + 10) * cos;
24 | const sy = cy + (outerRadius + 10) * sin;
25 | const mx = cx + (outerRadius + 30) * cos;
26 | const my = cy + (outerRadius + 30) * sin;
27 | const ex = mx + (cos >= 0 ? 1 : -1) * 22;
28 | const ey = my;
29 | const textAnchor = cos >= 0 ? 'start' : 'end';
30 |
31 |
32 |
33 | return (
34 |
35 |
36 | {payload.name}
37 |
38 |
47 |
56 |
61 |
62 | = 0 ? 1 : -1) * 12}
64 | y={ey}
65 | textAnchor={textAnchor}
66 | fill="#333"
67 | >{`${value} acres`}
68 | = 0 ? 1 : -1) * 12}
70 | y={ey}
71 | dy={18}
72 | textAnchor={textAnchor}
73 | fill="#999"
74 | >
75 | {`(${(percent * 100).toFixed(2)}%)`}
76 |
77 |
78 | );
79 | };
80 |
81 | const FarmBreakdown = (props) => {
82 |
83 | const data = props.statsData.map(input => ({
84 | name: input.crop,
85 | value: input.acre
86 | }));
87 | const [activeIndex, setActiveIndex] = useState(0);
88 | const onPieEnter = useCallback(
89 | (_, index) => {
90 | setActiveIndex(index);
91 | },
92 | [setActiveIndex]
93 | );
94 |
95 | return (
96 |
97 |
Farm Breakdown
98 |
Broken down by percentage
99 |
114 |
115 | );
116 | };
117 |
118 | export default FarmBreakdown;
119 |
--------------------------------------------------------------------------------
/frontend/src/assets/crop.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/frontend/src/assets/sunny.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/frontend/src/assets/saved.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/frontend/src/components/Analysis/index.jsx:
--------------------------------------------------------------------------------
1 | import './style.scss';
2 |
3 | import analysis from 'assets/analysis.svg';
4 | import Card from './Card';
5 | import Breakdowns from './Breakdowns';
6 |
7 | const FireRisk = ["Low", "Medium", "High"];
8 | const Grade = ["A", "B", "C", "D", "E", "F"];
9 | const Exp = ["Best", "Good", "Average", "Risky", "Bad"];
10 | const num = Grade[Math.floor(Math.random() * Grade.length)];
11 | const Analysis = (props) => {
12 | return (
13 |
14 |
15 |
16 |
17 |
Location Analysis
18 | Realtime data based off current location
19 |
20 |
21 |
22 |
23 |
29 | {props.analysis.soilTemp ? props.analysis.soilTemp + '°' : Math.floor(Math.random() * (85 - 55 + 1)) + 55 + '°'}
30 |
31 | Fahrenheit
32 |
33 |
34 |
40 | {props.analysis.soilMoisture
41 | ? props.analysis.soilMoisture + '%'
42 | : Math.floor(Math.random() * (87 - 34 + 1)) + 34 +'%'}
43 |
44 | Volumetric Pressure
45 |
46 |
47 |
53 | {props.analysis.waterVapor || Math.floor(Math.random() * (45 - 24 + 1)) + 24}%
54 |
55 | Relative Humidity
56 |
57 |
58 | 🔥
59 |
64 | {props.analysis.fire || FireRisk[Math.floor(Math.random() * FireRisk.length)]}
65 |
66 |
67 |
68 | 🌿
69 |
74 | {props.analysis.pollen || FireRisk[Math.floor(Math.random() * FireRisk.length)]}
75 |
76 |
77 |
78 |
79 |
Air Quality Breakdown
80 |
81 |
Air Quality Index
82 |
83 | {props.analysis.airQuality
84 | ? Math.floor(props.analysis.airQuality['AQI'])
85 | : Math.floor(Math.random() * (10 - 3 + 1)) + 3 + '%'}
86 |
87 |
88 |
89 |
90 |
Nitrogen Dioxide
91 |
92 | {props.analysis.airQuality
93 | ? Math.floor(props.analysis.airQuality['NO2'])
94 | : Math.floor(Math.random() * (15 - 5 + 1)) + 5 + '%'}
95 |
96 |
97 |
98 |
99 |
Particulate Matter (2.5)
100 |
101 | {props.analysis.airQuality
102 | ? Math.floor(props.analysis.airQuality['PM25'])
103 | : Math.floor(Math.random() * (5 - 1 + 1)) + 1 + '%'}
104 |
105 |
106 |
107 |
108 |
Carbon Monoxide
109 |
110 | {props.analysis.airQuality
111 | ? Math.floor(props.analysis.airQuality['CO'])
112 | : Math.floor(Math.random() * (20 - 9 + 1)) + 9 + '%'}
113 |
114 |
115 |
116 |
117 |
Ozone
118 |
119 | {props.analysis.airQuality
120 | ? Math.floor(props.analysis.airQuality['OZONE'])
121 | : Math.floor(Math.random() * (30 - 5 + 1)) + 5 + '%'}
122 |
123 |
124 |
125 |
126 |
Sulfur Dioxide
127 |
128 | {props.analysis.airQuality
129 | ? Math.floor(props.analysis.airQuality['SO2'])
130 | : Math.floor(Math.random() * (15 - 2 + 1)) + 2 + '%'}
131 |
132 |
133 |
134 |
135 |
140 |
141 | );
142 | };
143 |
144 | export default Analysis;
145 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Hello 👋. We welcome you all to Fasaan🎊🎊
4 | =
5 |
6 | ## Contributing to this repository
7 |
8 | We are creating a platform that is helpful forfarmers
9 |
10 |
11 |
12 | ## Adding a new topic
13 |
14 | If you are adding a new domain/field, you need to create a new branch. That branch should be created from branch `main` and not from any other branch. The naming convention of the branch should follow `Pascal_Snake_Case`. Examples are given below:
15 |
16 | If you want to add the feature, please add `Feature` specifications in the Issue , the name of the corresponding branch and folder should be
17 | The starting letter of every word should be in uppercase. Do not use spaces or hyphen(-). Instead use underscore (_) to join words.
18 | All branches and folder and sub-folder names should follow this naming convention to maintain a uniformity in the repository
19 |
20 |
21 |
22 | ---
23 |
24 |
25 |
26 | ## Basics of Git and GitHub
27 |
28 | ### Git & GitHub
29 |
30 | Before we proceed, it's better to know the difference between Git and Github. Git is a version control system (VCS) that allows us to keep track of the history of our source code , whereas GitHub is a service that hosts Git projects.
31 |
32 | We assume you have created an account on Github and installed Git on your System.
33 |
34 | Now enter your name and E-mail (used on Github) address in Git, by using following command.
35 |
36 | `$ git config --global user.name "YOUR NAME"`
37 | ` $ git config --global user.email "YOUR EMAIL ADDRESS"`
38 | This is an important step to mark your commits to your name and email.
39 |
40 |
41 |
42 | ### Fork a project
43 |
44 | You can make a copy of the project to your account. This process is called forking a project to your Github account. On Upper right side of project page on Github, you can see -
45 |
46 |
47 | Click on fork to create a copy of project to your account. This creates a separate copy for you to work on.
48 |
49 |
50 |
51 |
52 |
53 |
54 | ### Clone the forked project
55 |
56 | You have forked the project you want to contribute to your github account. To get this project on your development machine we use clone command of git.
57 |
58 | `$ git clone https://github.com/girlscript/winter-of-contributing.git`
59 | Now you have the project on your local machine.
60 |
61 |
62 |
63 | ### Add a remote (upstream) to original project repository
64 |
65 | Remote means the remote location of project on Github. By cloning, we have a remote called origin which points to your forked repository. Now we will add a remote to the original repository from where we had forked.
66 |
67 | `$ cd `
68 | `$ git remote add upstream https://github.com/girlscript/winter-of-contributing.git`
69 | You will see the benefits of adding remote later.
70 |
71 |
72 |
73 | ### Synchronizing your fork
74 |
75 | Open Source projects have a number of contributors who can push code anytime. So it is necessary to make your forked copy equal with the original repository. The remote added above called Upstream helps in this.
76 |
77 | `$ git checkout main`
78 | `$ git fetch upstream`
79 | `$ git merge upstream/main`
80 | `$ git push origin main`
81 | The last command pushes the latest code to your forked repository on Github. The origin is the remote pointing to your forked repository on github.
82 |
83 |
84 |
85 | ### Create a new branch for a feature or bugfix
86 |
87 | Usually, all repositories have a main branch that is regarded to be stable, and any new features should be developed on a separate branch before being merged into the main branch. As a result, we should establish a new branch for our feature or bugfix and go to work on the issue.
88 |
89 | `$ git checkout -b `
90 | This will create a new branch out of master branch. Now start working on the problem and commit your changes.
91 |
92 | `$ git add --all`
93 | `$ git commit -m ""`
94 | The first command adds all the files or you can add specific files by removing -a and adding the file names. The second command gives a message to your changes so you can know in future what changes this commit makes. If you are solving an issue on original repository, you should add the issue number like #35 to your commit message. This will show the reference to commits in the issue.
95 |
96 |
97 |
98 | ### Push code and create a pull request
99 |
100 | You now have a new branch containing the modifications you want in the project you forked. Now, push your new branch to your remote github fork.
101 |
102 | `$ git push origin `
103 | Now you are ready to help the project by opening a pull request means you now tell the project managers to add the feature or bug fix to original repository. You can open a pull request by clicking on green icon -
104 |
105 |
106 |
107 | Remember your upstream base branch should be main and source should be your feature branch. Click on create pull request and add a name to your pull request. You can also describe your feature.
108 |
109 | Fantastic! You've already made your first contribution.🥳
110 |
111 | #### BE OPEN!
112 |
113 | #### Happy Coding 👩💻👩💻
114 |
--------------------------------------------------------------------------------
/frontend/src/components/Seasons/index.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 |
3 | import './style.scss';
4 |
5 | import cloudy from 'assets/cloudy.svg';
6 | import raining from 'assets/raining.svg';
7 | import sunny from 'assets/sunny.svg';
8 |
9 | function dateFromDay(year, day){
10 | let newYear;
11 | if (day > 365) {
12 | newYear = year + 1;
13 | } else {
14 | newYear = year;
15 | }
16 | var date = new Date(newYear, 0); // initialize a date in `year-01-01`
17 | return new Date(date.setDate(day % 365)); // add the number of days
18 | }
19 |
20 | const options = { month: "short", day: "numeric" };
21 |
22 | const Seasons = (props) => {
23 | const [season, setSeason] = useState('Spring');
24 | const [color, setColor] = useState('rgba(39, 174, 96, 1)');
25 | const [high, setHigh] = useState('63');
26 | const [low, setLow] = useState('39');
27 | const [allWeather, setWeather] = useState([
28 | {
29 | avgTemp: '41°',
30 | date: 'April 19',
31 | weather: 'rainy'
32 | },
33 | {
34 | avgTemp: '46°',
35 | date: 'April 20',
36 | weather: 'cloudy'
37 | },
38 | {
39 | avgTemp: '49°',
40 | date: 'April 21',
41 | weather: 'cloudy'
42 | },
43 | {
44 | avgTemp: '59°',
45 | date: 'April 22',
46 | weather: 'cloudy'
47 | },
48 | {
49 | avgTemp: '58°',
50 | date: 'April 23',
51 | weather: 'rainy'
52 | }
53 | ])
54 |
55 | useEffect(() => {
56 | const realDay = props.day % 365;
57 | let year = (props.day > 365) ? '2024' : '2023';
58 | let multiplier = 73;
59 | let weather1, weather2, weather3, weather4, weather5 = null;
60 | if (realDay >= 79 && realDay < 171) {
61 | setSeason('Spring ' + year);
62 | setColor('rgba(39, 174, 96, 1)');
63 | multiplier = 53;
64 | weather1 = 'cloudy';
65 | weather2 = 'rainy';
66 | weather3 = 'rainy';
67 | weather4 = 'cloudy';
68 | weather5 = 'cloudy'
69 | setHigh(Math.floor(multiplier + Math.random() * 10));
70 | setLow(Math.floor(multiplier - Math.random() * 10));
71 | } else if (realDay >= 171 && realDay < 265) {
72 | setSeason('Summer ' + year);
73 | setColor('rgba(237, 193, 81, 1)');
74 | weather1 = 'cloudy';
75 | weather2 = 'sunny';
76 | weather3 = 'sunny';
77 | weather4 = 'sunny';
78 | weather5 = 'sunny';
79 | setHigh(Math.floor(multiplier + Math.random() * 10));
80 | setLow(Math.floor(multiplier - Math.random() * 10));
81 | } else if (realDay >= 265 && realDay < 355) {
82 | setSeason('Fall ' + year);
83 | setColor('rgba(237, 193, 81, 1)');
84 | multiplier = 40;
85 | weather1 = 'sunny';
86 | weather2 = 'cloudy';
87 | weather3 = 'cloudy';
88 | weather4 = 'cloudy';
89 | weather5 = 'cloudy';
90 | setHigh(Math.floor(multiplier + Math.random() * 10));
91 | setLow(Math.floor(multiplier - Math.random() * 10));
92 | } else {
93 | setSeason('Winter ' + year);
94 | setColor('rgba(64, 145, 220, 1)');
95 | multiplier = 9;
96 | weather1 = 'cloudy';
97 | weather2 = 'rainy';
98 | weather3 = 'rainy';
99 | weather4 = 'rainy';
100 | weather5 = 'rainy';
101 | setHigh(Math.floor(multiplier + Math.random() * 10));
102 | setLow(Math.floor(multiplier - Math.random() * 10));
103 | }
104 |
105 | setWeather([
106 | {
107 | avgTemp: Math.floor(multiplier + Math.random() * 5) + '°',
108 | date: dateFromDay(year, (props.day + 1) % 365).toLocaleDateString(undefined, options),
109 | weather: weather1
110 | },
111 | {
112 | avgTemp: Math.floor(multiplier + Math.random() * 5) + '°',
113 | date: dateFromDay(year, (props.day + 2) % 365).toLocaleDateString(undefined, options),
114 | weather: weather2
115 | },
116 | {
117 | avgTemp: Math.floor(multiplier + Math.random() * 5) + '°',
118 | date: dateFromDay(year, (props.day + 3) % 365).toLocaleDateString(undefined, options),
119 | weather: weather3
120 | },
121 | {
122 | avgTemp: Math.floor(multiplier + Math.random() * 5) + '°',
123 | date: dateFromDay(year, (props.day + 4) % 365).toLocaleDateString(undefined, options),
124 | weather: weather4
125 | },
126 | {
127 | avgTemp: Math.floor(multiplier + Math.random() * 5) + '°',
128 | date: dateFromDay(year, (props.day + 5) % 365).toLocaleDateString(undefined, options),
129 | weather: weather5
130 | }
131 | ]);
132 | }, [props.day])
133 |
134 | return (
135 |
141 |
142 |
Season
143 | {season}
144 |
145 |
146 |
Temp
147 | {high}°/{low}°
148 |
149 |
150 | {
151 | allWeather.map((entry) => {
152 | let currWeather = sunny;
153 | if (entry.weather === 'cloudy') {
154 | currWeather = cloudy;
155 | } else if (entry.weather === 'raining') {
156 | currWeather = raining;
157 | }
158 | return (
159 |
160 |
161 |
162 |
{entry.avgTemp}
163 | {entry.date}
164 |
165 |
166 | )
167 | })
168 | }
169 |
170 |
171 | )
172 | }
173 |
174 | export default Seasons
--------------------------------------------------------------------------------
/analysis/src/index.ts:
--------------------------------------------------------------------------------
1 | import earthData from './data/earthData.json';
2 |
3 |
4 | export const data = earthData;
5 | export interface CropInformation {
6 | type: string;
7 | name: string;
8 | earthScore: number;
9 | state: Season;
10 | image: string;
11 | summary: string;
12 | environmentalImpact: string;
13 | costPerAcre: number;
14 | verdict: string;
15 | daysToGrow: number;
16 | waterUsed: number;
17 | lbsPerAcre: number;
18 | valuePerAcre: number;
19 | }
20 |
21 |
22 |
23 | export const dataForCrop = (crop: string): CropInformation => {
24 | return earthData.find(candidate => {
25 | return candidate.name == crop;
26 | }) as CropInformation;
27 | };
28 |
29 | export const findAlternateCrop = (crop: CropInformation): CropInformation => {
30 | return earthData.find(candidate => {
31 | return candidate.name !== crop.name && candidate.state === crop.state
32 | }) as CropInformation;
33 | };
34 | /**
35 | Array
36 | 0 1 2 3
37 | Spring Summer Fall Winter
38 | */
39 | export interface CropAmount {
40 | crop: string;
41 | acre: number; // in acres (remember to convert)
42 | }
43 |
44 | export enum Season {
45 | Spring = 'Spring',
46 | Summer = 'Summer',
47 | Fall = 'Fall',
48 | Winter = 'Winter',
49 | }
50 |
51 | export const SeasonToIdx = (season: Season): number => {
52 | switch (season) {
53 | case Season.Spring: return 0
54 | case Season.Summer: return 1
55 | case Season.Fall: return 2
56 | case Season.Winter: return 3
57 | }
58 | }
59 |
60 | export enum Month {
61 | January = 0,
62 | February,
63 | March,
64 | April,
65 | May,
66 | June,
67 | July,
68 | August,
69 | September,
70 | October,
71 | November,
72 | December,
73 | }
74 |
75 | export const monthToSeason = (month: Month) => {
76 | if (month >= 2 && month < 5 ) {
77 | return Season.Spring;
78 | } else if (month >= 5 && month < 8) {
79 | return Season.Summer;
80 | } else if (month >= 8 && month < 11) {
81 | return Season.Fall;
82 | } else {
83 | return Season.Winter;
84 | }
85 | };
86 |
87 | export const seasonToMonth = (season: Season) => {
88 | switch (season) {
89 | case Season.Spring:
90 | return Month.March;
91 | case Season.Summer:
92 | return Month.June;
93 | case Season.Fall:
94 | return Month.September;
95 | case Season.Winter:
96 | return Month.December;
97 | }
98 | };
99 |
100 | export const cleanMonth = (month: number): Month => {
101 | // somehow normalize month to be within 0 and 11
102 | return (month + 12) % 12;
103 | }
104 |
105 | export const sqftToAcre = (sqft: number) => {
106 | // https://www.thecalculatorsite.com/conversions/area/square-feet-to-acres.php
107 | return 0.000022956841138659 * sqft;
108 | };
109 |
110 | export interface WaterUsageAndYieldOutput {
111 | waterUsage: number[];
112 | yield: number[];
113 | }
114 |
115 | export const determineWaterUsageAndYield = (array: CropAmount[]): WaterUsageAndYieldOutput => {
116 | const output: WaterUsageAndYieldOutput = {
117 | waterUsage: [0,0,0,0],
118 | yield: [0,0,0,0],
119 | };
120 |
121 | for (const item of array) {
122 | const cropData = dataForCrop(item.crop);
123 | const season = cropData.state;
124 | const endMonth = seasonToMonth(season);
125 | const numOfMonths = cropData.daysToGrow / 30;
126 | const startMonth = cleanMonth(endMonth - numOfMonths);
127 | console.log(`for ${item.crop}, startMonth: ${startMonth}, endMonth: ${endMonth}`)
128 |
129 | for (let j = 0; j <= numOfMonths; j++) {
130 | const month = cleanMonth(startMonth + j);
131 | const season = monthToSeason(month);
132 | const idx = SeasonToIdx(season);
133 |
134 | output.waterUsage[idx] += cropData.waterUsed * 91.25;
135 | }
136 | output.yield[SeasonToIdx(season)] += cropData.lbsPerAcre * item.acre;
137 | }
138 | return output;
139 | };
140 |
141 | interface SpendingAndRevenueOutput {
142 | spending: number[];
143 | revenue: number[];
144 | }
145 |
146 | export const determineSpendingAndRevenue = (array: CropAmount[]): SpendingAndRevenueOutput => {
147 | const output: SpendingAndRevenueOutput = {
148 | spending: [0,0,0,0],
149 | revenue: [0,0,0,0],
150 | };
151 |
152 | for (const item of array) {
153 | const cropData = dataForCrop(item.crop);
154 | console.log(item.crop)
155 | const season = cropData.state;
156 | const endMonth = seasonToMonth(season);
157 | const numOfMonths = cropData.daysToGrow / 30;
158 | const startMonth = cleanMonth(endMonth - numOfMonths);
159 |
160 | for (let j = 0; j <= numOfMonths; j++) {
161 | const month = cleanMonth(startMonth + j);
162 | const season = monthToSeason(month);
163 | const idx = SeasonToIdx(season);
164 |
165 | const cost = cropData.costPerAcre * item.acre;
166 | console.log(cropData.costPerAcre)
167 | console.log(item.acre)
168 | console.log(cost)
169 | const revenue = cropData.valuePerAcre * item.acre;
170 | output.spending[idx] += cost;
171 | output.revenue[idx] += revenue;
172 | }
173 | }
174 |
175 | return output;
176 | };
177 |
178 |
179 |
--------------------------------------------------------------------------------
/frontend/src/components/Map/utils.js:
--------------------------------------------------------------------------------
1 | import { MercatorCoordinate } from 'mapbox-gl';
2 | import * as THREE from 'three';
3 | import { TweenMax } from 'gsap';
4 |
5 | let allLayers = [];
6 | const height=100;
7 |
8 | /**
9 | * Creates a plot at a location
10 | **/
11 |
12 | export const loadLocation = (map, lng, lat, id, width, length, color, minLat, maxLat, minLng, maxLng, currentPlot, day, callback) => {
13 | console.log(width);
14 | console.log(length);
15 |
16 | // parameters to ensure the model is georeferenced correctly on the map
17 | const modelOrigin = [lng, lat];
18 | const modelAltitude = 0;
19 | const modelRotate = [Math.PI / 2, 0, 0];
20 |
21 | const modelAsMercatorCoordinate = MercatorCoordinate.fromLngLat(
22 | modelOrigin,
23 | modelAltitude
24 | );
25 |
26 | const modelTransform = {
27 | translateX: modelAsMercatorCoordinate.x,
28 | translateY: modelAsMercatorCoordinate.y,
29 | translateZ: modelAsMercatorCoordinate.z,
30 | rotateX: modelRotate[0],
31 | rotateY: modelRotate[1],
32 | rotateZ: modelRotate[2],
33 | /* Since our 3D model is in real world meters, a scale transform needs to be
34 | * applied since the CustomLayerInterface expects units in MercatorCoordinates.
35 | */
36 | scale: modelAsMercatorCoordinate.meterInMercatorCoordinateUnits()
37 | };
38 |
39 | // Insert the layer beneath any symbol layer.
40 | const layers = map.getStyle().layers;
41 |
42 | let labelLayerId;
43 | for (let i = 0; i < layers.length; i++) {
44 | if (layers[i].type === 'custom') {
45 | labelLayerId = layers[i].id;
46 | break;
47 | }
48 | }
49 |
50 | const customLayer = {
51 | id: lat.toString() + lng.toString(),
52 | type: 'custom',
53 | renderingMode: '3d',
54 | 'fill-extrusion-height': [
55 | 'interpolate',
56 | ['linear'],
57 | ['zoom'],
58 | 15,
59 | 0,
60 | 15.05,
61 | ['get', 'height']
62 | ],
63 | 'fill-extrusion-base': [
64 | 'interpolate',
65 | ['linear'],
66 | ['zoom'],
67 | 15,
68 | 0,
69 | 15.05,
70 | ['get', 'min_height']
71 | ],
72 | onAdd: function(map, gl) {
73 | this.camera = new THREE.Camera();
74 | this.scene = new THREE.Scene();
75 |
76 | // create two three.js lights to illuminate the model
77 | const directionalLight = new THREE.DirectionalLight(0xeeeeee);
78 | directionalLight.position.set(40, -70, 100).normalize();
79 | this.scene.add(directionalLight);
80 |
81 | const directionalLight2 = new THREE.DirectionalLight(0xeeeeee);
82 | directionalLight2.position.set(40, 70, 100).normalize();
83 | this.scene.add(directionalLight2);
84 |
85 | const ftHeight = (height / 3.28084) / 100;
86 |
87 | const geometry = new THREE.BoxGeometry( width , ftHeight, length );
88 | const material = new THREE.MeshPhongMaterial({
89 | color,
90 | opacity: 0.75,
91 | transparent: true,
92 | });
93 | const cube = new THREE.Mesh( geometry, material );
94 | this.scene.add( cube );
95 | TweenMax.to(cube.scale, .5, { x: 1, y: 100, z: 1 });
96 | TweenMax.to(cube.position, .5, {y: ftHeight * 50});
97 | this.map = map;
98 |
99 | // use the Mapbox GL JS map canvas for three.js
100 | this.renderer = new THREE.WebGLRenderer({
101 | canvas: map.getCanvas(),
102 | context: gl,
103 | antialias: true
104 | });
105 |
106 | callback(id, {
107 | model: cube,
108 | minLat: minLat,
109 | maxLat: maxLat,
110 | minLng: minLng,
111 | maxLng: maxLng,
112 | centerLat: lng,
113 | centerLng: lat,
114 | currentPlot: currentPlot,
115 | width: width,
116 | length: length,
117 | day: day
118 | });
119 |
120 | this.renderer.autoClear = false;
121 | },
122 | render: function(gl, matrix) {
123 | const rotationX = new THREE.Matrix4().makeRotationAxis(
124 | new THREE.Vector3(1, 0, 0),
125 | modelTransform.rotateX
126 | );
127 | const rotationY = new THREE.Matrix4().makeRotationAxis(
128 | new THREE.Vector3(0, 1, 0),
129 | modelTransform.rotateY
130 | );
131 | const rotationZ = new THREE.Matrix4().makeRotationAxis(
132 | new THREE.Vector3(0, 0, 1),
133 | modelTransform.rotateZ
134 | );
135 |
136 | const m = new THREE.Matrix4().fromArray(matrix);
137 | const l = new THREE.Matrix4()
138 | .makeTranslation(
139 | modelTransform.translateX,
140 | modelTransform.translateY,
141 | modelTransform.translateZ
142 | )
143 | .scale(
144 | new THREE.Vector3(
145 | modelTransform.scale,
146 | -modelTransform.scale,
147 | modelTransform.scale
148 | )
149 | )
150 | .multiply(rotationX)
151 | .multiply(rotationY)
152 | .multiply(rotationZ);
153 |
154 | this.camera.projectionMatrix = m.multiply(l);
155 | this.renderer.state.reset();
156 | this.renderer.render(this.scene, this.camera);
157 | this.map.triggerRepaint();
158 | }
159 | };
160 |
161 | try {
162 | map.addLayer(customLayer, labelLayerId);
163 | allLayers.push(customLayer.id);
164 | window.map = map;
165 | }
166 | catch (error) {
167 | // TODO: Notify
168 | //notify("Can't Build", "There's already a building there!");
169 | }
170 | }
171 | //console.log(lng);
172 | //console.log(lat);
173 | /*map.addLayer({
174 | id: 'plot-' + id,
175 | type: 'custom',
176 | renderingMode: '3d',
177 | onAdd: function (map, mbxContext) {
178 |
179 | window.tb = new Threebox(
180 | map,
181 | mbxContext,
182 | { defaultLights: true }
183 | );
184 |
185 | // initialize geometry and material of our cube object
186 | const geometry = new THREE.BoxGeometry(100, 100, 100);
187 |
188 | const redMaterial = new THREE.MeshPhongMaterial( {
189 | color: 0x009900,
190 | side: THREE.DoubleSide
191 | });
192 |
193 | //var cube = new THREE.Mesh(geometry, redMaterial);
194 |
195 | var sphere = window.tb.sphere({color: 'red', material: 'MeshStandardMaterial'})
196 | .setCoords([lng, lat])
197 |
198 | window.tb.add(sphere);
199 |
200 | //var cube2 = new THREE.Mesh(geometry, redMaterial);
201 |
202 | //cube2 = window.tb.Object3D({obj: cube}).setCoords([lng + 0.01, lat + 0.01])
203 |
204 | //window.tb.add(cube2);
205 | },
206 | render: function(gl, matrix){
207 | window.tb.update();
208 | }
209 | });*/
--------------------------------------------------------------------------------
/backend/src/insights/index.ts:
--------------------------------------------------------------------------------
1 | import { Router, Request, Response } from 'express';
2 | import { matchedData, validationResult, check } from 'express-validator';
3 | import { ambeeDateParse } from 'src/utils/parseDate';
4 | import { getAirQuality } from './ambee/airQuality';
5 | import { getAirQualityHistory } from './ambee/airQualityHistory';
6 | import { getFireLatest } from './ambee/fireLatest';
7 | import { getPollenForecast } from './ambee/pollenForecast';
8 | import { getPollenHistory } from './ambee/pollenHistory';
9 | import { getSoilLatest } from './ambee/soilLatest';
10 | import { getSoilHistory } from './ambee/soilHistory';
11 | import { getWaterVaporHistory } from './ambee/waterVaporHistory';
12 | import { getWaterVaporLatest } from './ambee/waterVaporLatest';
13 | import { getWeatherForecast } from './ambee/weatherForecast';
14 | import { getWeatherHistory } from './ambee/weatherHistory';
15 | import { getWeatherLatest } from './ambee/weatherLatest';
16 | import { getPollenLatest } from './ambee/pollenLatest';
17 | import weatherRouter from './weather'
18 | import { AMBEE_API_KEY, USE_API } from 'src/configuration';
19 | import exampleResponse from '../data/perfectResponse.json';
20 | import * as EnvironmentGrade from '../utils/getEnvGrade';
21 | import * as LocationGrade from '../utils/getLocGrade';
22 |
23 | const router = Router();
24 |
25 | const handler = async (req: Request, res: Response) => {
26 | await check('lat')
27 | .isFloat({ min: -90, max: 90 })
28 | .run(req);
29 | await check('lng')
30 | .isFloat({ min: -180, max: 180 })
31 | .run(req);
32 | await check('from')
33 | .isString()
34 | .optional({ checkFalsy: true })
35 | .run(req);
36 | await check('to')
37 | .isString()
38 | .optional({ checkFalsy: true })
39 | .run(req);
40 |
41 | const endpoints = [
42 | {
43 | name: 'airQuality',
44 | api: getAirQuality,
45 | },
46 | {
47 | name: 'airQualityHistory',
48 | api: getAirQualityHistory,
49 | },
50 | {
51 | name: 'fireLatest',
52 | api: getFireLatest,
53 | },
54 | {
55 | name: 'pollenForecast',
56 | api: getPollenForecast,
57 | },
58 | {
59 | name: 'pollenHistory',
60 | api: getPollenHistory,
61 | },
62 | {
63 | name: 'soilLatest',
64 | api: getSoilLatest,
65 | },
66 | {
67 | name: 'soilHistory',
68 | api: getSoilHistory,
69 | },
70 | {
71 | name: 'waterVaporHistory',
72 | api: getWaterVaporHistory,
73 | },
74 | {
75 | name: 'waterVaporLatest',
76 | api: getWaterVaporLatest,
77 | },
78 | {
79 | name: 'weatherForecast',
80 | api: getWeatherForecast,
81 | },
82 | {
83 | name: 'weatherHistory',
84 | api: getWeatherHistory,
85 | },
86 | {
87 | name: 'weatherLatest',
88 | api: getWeatherLatest,
89 | },
90 | {
91 | name: 'pollenLatest',
92 | api: getPollenLatest,
93 | },
94 | ];
95 |
96 | const errors = validationResult(req);
97 |
98 | if (!errors.isEmpty()) {
99 | return res.status(400).json({
100 | message: 'Invalid Parameters',
101 | errors: errors.array(),
102 | parametersSent: req.body,
103 | });
104 | } else {
105 | try {
106 | const { lat, lng }: { lat: number; lng: number } = req.body;
107 | let from: string, to: string;
108 | const usingTimes = !!req.body.from && !!req.body.to;
109 | if (usingTimes) {
110 | from = ambeeDateParse(from);
111 | to = ambeeDateParse(to);
112 | }
113 |
114 | let data: any = {};
115 |
116 | // kyle do magic here
117 | const soilData = (USE_API) ? await getSoilLatest(lat, lng) : (exampleResponse.insights as any)["soilLatest"]
118 | data["soilLatest"] = soilData;
119 |
120 | const aqi = (USE_API) ? await (await getAirQuality(lat, lng)).stations[0].AQI : exampleResponse.insights.airQuality.stations[0].AQI
121 | const locScore = LocationGrade.getGrade(aqi);
122 | const {score : locationGrade, description:locationDescription} = LocationGrade.numToGrade(locScore)
123 |
124 | const envgradeScore = EnvironmentGrade.getGrade(soilData.data[0].soil_moisture, soilData.data[0].soil_temperature);
125 | const {score: environmentGrade, description: environmentDescription} = EnvironmentGrade.numToGrade(envgradeScore);
126 | console.log(locationDescription)
127 | for (const endpoint of endpoints) {
128 | if (["soilLatest"].includes(endpoint.name)) {
129 | // skip these since we already did the analysis
130 | continue;
131 | } else if (req.body[endpoint.name] == 'API') {
132 | console.log(`requesting ${endpoint.name} using the api`)
133 | const result = await endpoint.api(lat, lng, from, to)
134 | const success = !result.error
135 | if (success) {
136 | data[endpoint.name] = result;
137 | continue;
138 | } else {
139 | console.log(`request for ${endpoint.name} failed — using cache`)
140 | }
141 | }
142 | console.log(`using cached data from ${endpoint.name}`)
143 | data[endpoint.name] = (exampleResponse.insights as any)[endpoint.name]
144 | }
145 |
146 | return res.json({
147 | lat,
148 | lng,
149 | environment: {
150 | environmentGrade, environmentDescription
151 | },
152 | location: {
153 | locationGrade, locationDescription
154 | },
155 | insights: data
156 | });
157 | } catch (err) {
158 | return res.json(exampleResponse)
159 | }
160 | }
161 | };
162 |
163 | router.get('/', handler);
164 | router.post('/', handler);
165 |
166 | router.use('/weather', weatherRouter)
167 | export default router;
168 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | CareFACT : Envisioning Farms for Future
2 |
3 |
4 |
5 |
6 |
7 | [](https://github.com/hariketsheth/CareFACT)
8 |
9 | ### :sparkles:About CareFACT
10 | Agriculture is very significant in India's economy and jobs. Farmers' most prevalent problem is that they are unable to make informed decisions on which crops are supported in their areas, as well as market and profit prices. As a result, they have lower productivity and profit margins. The goal of this study is to propose an attempt to estimate agricultural output and price using machine learning, which will aid in crop selection based on soil type and meteorological conditions.
11 |
12 | Farmers do not have a single point of contact for all of their needs, including crop, market price, soil type, government programs, and the latest news. To make this easier for farmers, a portal based on machine learning is being developed.
13 |
14 | ### :sparkles:About the Project
15 | Several studies were undertaken recently in India, including the one on DFI, have brought out the problems afflicting the agriculture sector.
16 |
17 | The recommendations of these studies include, inter alia, ensuring timely availability of inputs, an increased focus on measures to enhance the productivity - especially of small and marginal farms, adoption of modern agricultural practices, optimal use of inputs, choice of the right crops through macro and micro-level planning. CareFACT comes from the Hindi terms "Fasal" and "Kisaan," which mean "crops" and "farmers," respectively. As the name says, we work as a farmer's companion. Farmers play a critical role in both meeting our basic food needs and altering the agricultural environment. Farmers manage agricultural practices to maintain the long-term survival of the entire flora and fauna.
18 |
19 | Agriculture presently occupies one-third of the world's land surface. For a big majority of the world's population, it is the most essential activity since it produces food for people all over the world.
20 |
21 | >
22 | > The features include:
23 | > 1. **Gain Insights** - Centralized source of truth for farmers to gain insights into their land. Our software allows farmers to optimally plan farm layouts and maximize efficiency and revenue.
24 | > 2. **Plan Out Field** - The user is able to indicate plots of land where they want to plant and indicate the crop they want to plant there. Based on real-time data about air quality, soil and weather conditions,
25 | > 3. **Real Time Analysis with Environment Impact** - CareFACT will determine how suitable a particular area is for planting a particular crop, and provide a general estimation on the expected value of a plot.
26 | > 4. **Environment Degradation** - CareFACT would determine a CareFACT Score that would determine the degradation the particular farm field is creating because of the crops and irrigation sources
27 | > 5. **AI Based Help** - CareFACT is AI based solution which makes it the best platform to suggest farmers crops and also given them suggestions about not-so-good combinations and crop rotation techniques
28 | > 6. **Crop Yield and Estimation** - CareFACT will help ypu detrermine the growth of your formed field in the platform - how well it performs in all the 4 seasons. This would help the farmers know about the estimated cost anf fundings to be taken beforehand.
29 |
30 |
31 |
32 | ## :sparkles:Take a look at our website
33 |
34 |
35 | 
36 |
37 | 
38 |
39 |
40 |
41 |
42 |
43 | ## :sparkles: Project Information
44 | [](https://github.com/hariketsheth/CareFACT)
45 | [](https://github.com/hariketsheth/CareFACT)
46 |
47 | [](https://github.com/hariketsheth/CareFACT)
48 |
49 |
50 |
51 | ## :sparkles: Instructions for Cloning this Repository Locally
52 | - Use the command: `git clone https://github.com/hariketsheth/CareFACT---Envisioning-Farms-for-Future`
53 | - Run `npm install` or `yarn` separately in root directory, frontend, backend and analysis directory
54 | - Use `yarn start` or `npm start` to start the project on local server
55 |
56 |
57 |
58 | ## :sparkles: Instructions to contribute
59 |
60 | To contribute fork this repository and clone it. Make changes and create a pull request. Follow the step below to contribute.
61 |
62 | #### Add your email and user name in git bash
63 | `$ git config --global user.name "YOUR NAME"`
64 |
65 | ` $ git config --global user.email "YOUR EMAIL ADDRESS"`
66 |
67 | #### Fork the repo
68 | On the upper right you can see a option to fork the repository. Fork it to make changes.
69 |
70 | #### Clone the project
71 | Use the below command to clone the repo into your local system.
72 |
73 | ` $ git clone https://hariketsheth/CareFACT---Envisioning-Farms-for-Future.git `
74 |
75 | #### Add upstream to repository
76 | `$ cd `
77 | `$ git remote add upstream https://github.com/hariketsheth/CareFACT---Envisioning-Farms-for-Future.git`
78 | `$ git checkout main`
79 | `$ git fetch upstream`
80 | `$ git merge upstream/main`
81 | `$ git push origin main`
82 |
83 | #### Creating branch for the new change on feature or bug fix
84 | `$ git checkout -b `
85 | `$ git add --all`
86 | `$ git commit -m ""`
87 |
88 | #### Creating pull request
89 | `$ git push origin main`
90 |
91 | ## :sparkles: Get in touch with us
92 |
93 |
94 |
95 |
96 |
97 | ## :sparkles: License
98 |
99 |
100 | [](https://opensource.org/licenses/)
101 |
102 |
103 |
--------------------------------------------------------------------------------
/analysis/src/data/earthData.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type": "crop",
4 | "name": "Strawberry",
5 | "earthScore": 86,
6 | "state": "Summer",
7 | "image": "https://i.imgur.com/u0rOCBQ.png",
8 | "summary": "Strawberries are low-growing herbaceous plants with a fibrous root system and a crown from which arise basal leaves",
9 | "environmentalImpact": "Medium",
10 | "costPerAcre": 1600,
11 | "verdict": "Highly Recommended",
12 | "daysToGrow" : 90,
13 | "waterUsed" : 0.36,
14 | "lbsPerAcre" : 10000,
15 | "valuePerAcre" : 2000
16 | },
17 | {
18 | "type": "crop",
19 | "name": "Corn",
20 | "earthScore": 72,
21 | "state": "Fall",
22 | "image": "https://i.imgur.com/Fm1Svvq.png",
23 | "summary": "Corn is a cereal grain first domesticated by indigenous peoples in southern Mexico about 10,000 years ago",
24 | "environmentalImpact": "Medium",
25 | "costPerAcre": 248,
26 | "verdict": "Highly Recommended",
27 | "daysToGrow" : 120,
28 | "waterUsed" : 0.19,
29 | "lbsPerAcre" : 7280,
30 | "valuePerAcre" : 699
31 | },
32 | {
33 | "type": "crop",
34 | "name": "Soybean",
35 | "earthScore": 70,
36 | "state": "Fall",
37 | "image": "https://cdn.britannica.com/28/154828-050-05C6239A/Soybeans.jpg",
38 | "summary": "The soybean or soya bean is a species of legume native to East Asia, widely grown for its edible bean, which has numerous uses.",
39 | "environmentalImpact": "Medium",
40 | "costPerAcre": 204,
41 | "verdict": "Highly Recommended",
42 | "daysToGrow" : 65,
43 | "waterUsed" : 0.19,
44 | "lbsPerAcre" : 2946,
45 | "valuePerAcre" : 0
46 | },
47 | {
48 | "type": "crop",
49 | "name": "Wheat",
50 | "earthScore": 78,
51 | "state": "Fall",
52 | "image": "https://i.imgur.com/eKB3yQ8.jpeg",
53 | "summary": "Wheat is a grass widely cultivated for its seed, a cereal grain which is a worldwide staple food",
54 | "environmentalImpact": "Low",
55 | "costPerAcre": 170,
56 | "verdict": "Highly Recommended",
57 | "daysToGrow" : 120,
58 | "waterUsed" : 0.14,
59 | "lbsPerAcre" : 3900,
60 | "valuePerAcre" : 0
61 | },
62 | {
63 | "type": "crop",
64 | "name": "Rice",
65 | "earthScore": 64,
66 | "state": "Summer",
67 | "image": "https://i.imgur.com/yegWy51.jpeg",
68 | "summary": "Rice is the most widely consumed staple food for over half the world's population",
69 | "environmentalImpact": "High",
70 | "costPerAcre": 225,
71 | "verdict": "Recommended",
72 | "daysToGrow" : 120,
73 | "waterUsed" : 0.26,
74 | "lbsPerAcre" : 8000,
75 | "valuePerAcre" : 892
76 | },
77 | {
78 | "type": "crop",
79 | "name": "Onion",
80 | "earthScore": 82,
81 | "state": "Summer",
82 | "image": "https://images.immediate.co.uk/production/volatile/sites/30/2020/08/the-health-benefits-of-onions-main-image-700-350-8425535.jpg?quality=90&resize=768,574",
83 | "summary": "The onion plant has a fan of hollow, bluish-green leaves and its bulb at the base of the plant begins to swell when a certain day-length is reached",
84 | "environmentalImpact": "Medium",
85 | "costPerAcre": 1750,
86 | "verdict": "Highly Recommended",
87 | "daysToGrow" : 100,
88 | "waterUsed" : 0.06,
89 | "lbsPerAcre" : 32000,
90 | "valuePerAcre" : 4200
91 | },
92 | {
93 | "type": "crop",
94 | "name": "Cotton",
95 | "earthScore": 81,
96 | "state": "Fall",
97 | "image": "https://upload.wikimedia.org/wikipedia/commons/6/68/CottonPlant.JPG",
98 | "summary": "Cotton is a shrub native to tropical and subtropical regions around the world, including the Americas, Africa, Egypt and India",
99 | "environmentalImpact": "High",
100 | "costPerAcre": 485,
101 | "verdict": "Recommended",
102 | "daysToGrow" : 150,
103 | "waterUsed" : 0.09,
104 | "lbsPerAcre" : 685,
105 | "valuePerAcre" : 500
106 | },
107 | {
108 | "type": "crop",
109 | "name": "Iceberg Lettuce",
110 | "earthScore": 78,
111 | "state": "Spring",
112 | "image": "https://media.newyorker.com/photos/5b6b08d3a676470b4ea9b91f/4:3/w_1920,h_1440,c_limit/Rosner-Lettuce.jpg",
113 | "summary": "Iceberg lettuce, also known as crisphead lettuce, has pale green leaves and grows in cabbage-like bulbs",
114 | "environmentalImpact": "High",
115 | "costPerAcre": 7999,
116 | "verdict": "Recommended",
117 | "daysToGrow" : 30,
118 | "waterUsed" : 0.09,
119 | "lbsPerAcre" : 32000,
120 | "valuePerAcre" : 10400
121 | },
122 | {
123 | "type": "crop",
124 | "name": "Romaine Lettuce",
125 | "earthScore": 78,
126 | "state": "Spring",
127 | "image": "https://www.foodsafetynews.com/files/2019/11/romaine1200x680.jpg",
128 | "summary": "Romaine or cos lettuce is a variety of lettuce that grows in a tall head of sturdy dark green leaves with firm ribs down their centers.",
129 | "environmentalImpact": "High",
130 | "costPerAcre": 8847,
131 | "verdict": "Recommended",
132 | "daysToGrow" : 30,
133 | "waterUsed" : 0,
134 | "lbsPerAcre" : 32000,
135 | "valuePerAcre" : 10400
136 | },
137 | {
138 | "type": "irrigation",
139 | "name": "Surface",
140 | "earthScore": 90,
141 | "state": "Medium",
142 | "image": "https://upload.wikimedia.org/wikipedia/commons/9/9b/Furrow_irrigated_Sugar.JPG",
143 | "summary": "This type of irrigation relies on gravity flow to distribute water. It is the most commonly used irrigation methods and is well suited to mild slopes as well as regular slopes",
144 | "environmentalImpact": "Low",
145 | "costPerAcre": 23.41,
146 | "verdict": "Somewhat Recommended",
147 | "likes" : ["rice"]
148 | },
149 | {
150 | "type": "irrigation",
151 | "name": "Sprinkler",
152 | "earthScore": 70,
153 | "state": "Low",
154 | "image": "https://marketresearch.biz/wp-content/uploads/2019/04/sprinkler-irrigation-systems-market.jpg",
155 | "summary": "Sprinkler Irrigation involves spraying crops with water by use of permanently or temporarily set tools. Water is pumped through pipes and sprinkled to crops like drops of rain from the spray heads.",
156 | "environmentalImpact": "Low",
157 | "costPerAcre": 73.45,
158 | "verdict": "Highly Recommended"
159 | },
160 | {
161 | "type": "irrigation",
162 | "name": "Drip",
163 | "earthScore": 20,
164 | "state": "High",
165 | "image": "https://i.imgur.com/tdBE8nd.png",
166 | "summary": "Drip irrigation is a type of micro-irrigation system that has the potential to save water and nutrients by allowing water to drip slowly to the roots of plants",
167 | "environmentalImpact": "Medium",
168 | "costPerAcre": 500,
169 | "verdict": "Somewhat Recommended"
170 | },
171 | {
172 | "type": "irrigation",
173 | "name": "Subsurface",
174 | "earthScore": 51,
175 | "state": "High",
176 | "image": "https://i.ytimg.com/vi/jW5VhhT2bL0/maxresdefault.jpg",
177 | "summary": "Subsurface irrigation waters crops from below the ground. Pipes placed beneath the soil surface supply water to the roots of plants.",
178 | "environmentalImpact": "Medium",
179 | "costPerAcre": 800,
180 | "verdict": "Somewhat Recommended"
181 | },
182 | {
183 | "type": "irrigation",
184 | "name": "Center Pivot",
185 | "earthScore": 25,
186 | "state": "Medium",
187 | "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Crops_Kansas_AST_20010624.jpg/800px-Crops_Kansas_AST_20010624.jpg",
188 | "summary": "Water is distributed by a system of sprinklers that move on wheeled towers in a circular pattern. This system is common in flat areas of the United States.",
189 | "environmentalImpact": "High",
190 | "costPerAcre": 41.59,
191 | "verdict": "Somewhat Recommended"
192 | }
193 | ]
194 |
--------------------------------------------------------------------------------
/backend/src/data/earthData.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type": "crop",
4 | "name": "Strawberry",
5 | "earthScore": 86,
6 | "state": "Summer",
7 | "image": "https://i.imgur.com/u0rOCBQ.png",
8 | "summary": "Strawberries are low-growing herbaceous plants with a fibrous root system and a crown from which arise basal leaves",
9 | "environmentalImpact": "Medium",
10 | "costPerAcre": 1600,
11 | "verdict": "Highly Recommended",
12 | "daysToGrow" : 90,
13 | "waterUsed" : 0.36,
14 | "lbsPerAcre" : 10000,
15 | "valuePerAcre" : 2000
16 | },
17 | {
18 | "type": "crop",
19 | "name": "Corn",
20 | "earthScore": 72,
21 | "state": "Fall",
22 | "image": "https://i.imgur.com/Fm1Svvq.png",
23 | "summary": "Corn is a cereal grain first domesticated by indigenous peoples in southern Mexico about 10,000 years ago",
24 | "environmentalImpact": "Medium",
25 | "costPerAcre": 248,
26 | "verdict": "Highly Recommended",
27 | "daysToGrow" : 120,
28 | "waterUsed" : 0.19,
29 | "lbsPerAcre" : 7280,
30 | "valuePerAcre" : 699
31 | },
32 | {
33 | "type": "crop",
34 | "name": "Soybean",
35 | "earthScore": 70,
36 | "state": "Fall",
37 | "image": "https://cdn.britannica.com/28/154828-050-05C6239A/Soybeans.jpg",
38 | "summary": "The soybean or soya bean is a species of legume native to East Asia, widely grown for its edible bean, which has numerous uses.",
39 | "environmentalImpact": "Medium",
40 | "costPerAcre": 204,
41 | "verdict": "Highly Recommended",
42 | "daysToGrow" : 65,
43 | "waterUsed" : 0.19,
44 | "lbsPerAcre" : 2946,
45 | "valuePerAcre" : 0
46 | },
47 | {
48 | "type": "crop",
49 | "name": "Wheat",
50 | "earthScore": 78,
51 | "state": "Fall",
52 | "image": "https://i.imgur.com/eKB3yQ8.jpeg",
53 | "summary": "Wheat is a grass widely cultivated for its seed, a cereal grain which is a worldwide staple food",
54 | "environmentalImpact": "Low",
55 | "costPerAcre": 170,
56 | "verdict": "Highly Recommended",
57 | "daysToGrow" : 120,
58 | "waterUsed" : 0.14,
59 | "lbsPerAcre" : 3900,
60 | "valuePerAcre" : 0
61 | },
62 | {
63 | "type": "crop",
64 | "name": "Rice",
65 | "earthScore": 64,
66 | "state": "Summer",
67 | "image": "https://i.imgur.com/yegWy51.jpeg",
68 | "summary": "Rice is the most widely consumed staple food for over half the world's population",
69 | "environmentalImpact": "High",
70 | "costPerAcre": 225,
71 | "verdict": "Recommended",
72 | "daysToGrow" : 120,
73 | "waterUsed" : 0.26,
74 | "lbsPerAcre" : 8000,
75 | "valuePerAcre" : 892
76 | },
77 | {
78 | "type": "crop",
79 | "name": "Onion",
80 | "earthScore": 82,
81 | "state": "Summer",
82 | "image": "https://images.immediate.co.uk/production/volatile/sites/30/2020/08/the-health-benefits-of-onions-main-image-700-350-8425535.jpg?quality=90&resize=768,574",
83 | "summary": "The onion plant has a fan of hollow, bluish-green leaves and its bulb at the base of the plant begins to swell when a certain day-length is reached",
84 | "environmentalImpact": "Medium",
85 | "costPerAcre": 1750,
86 | "verdict": "Highly Recommended",
87 | "daysToGrow" : 100,
88 | "waterUsed" : 0.06,
89 | "lbsPerAcre" : 32000,
90 | "valuePerAcre" : 4200
91 | },
92 | {
93 | "type": "crop",
94 | "name": "Cotton",
95 | "earthScore": 81,
96 | "state": "Fall",
97 | "image": "https://upload.wikimedia.org/wikipedia/commons/6/68/CottonPlant.JPG",
98 | "summary": "Cotton is a shrub native to tropical and subtropical regions around the world, including the Americas, Africa, Egypt and India",
99 | "environmentalImpact": "High",
100 | "costPerAcre": 485,
101 | "verdict": "Recommended",
102 | "daysToGrow" : 150,
103 | "waterUsed" : 0.09,
104 | "lbsPerAcre" : 685,
105 | "valuePerAcre" : 500
106 | },
107 | {
108 | "type": "crop",
109 | "name": "Iceberg Lettuce",
110 | "earthScore": 78,
111 | "state": "Spring",
112 | "image": "https://media.newyorker.com/photos/5b6b08d3a676470b4ea9b91f/4:3/w_1920,h_1440,c_limit/Rosner-Lettuce.jpg",
113 | "summary": "Iceberg lettuce, also known as crisphead lettuce, has pale green leaves and grows in cabbage-like bulbs",
114 | "environmentalImpact": "High",
115 | "costPerAcre": 7999,
116 | "verdict": "Recommended",
117 | "daysToGrow" : 30,
118 | "waterUsed" : 0.09,
119 | "lbsPerAcre" : 32000,
120 | "valuePerAcre" : 10400
121 | },
122 | {
123 | "type": "crop",
124 | "name": "Romaine Lettuce",
125 | "earthScore": 78,
126 | "state": "Spring",
127 | "image": "https://www.foodsafetynews.com/files/2019/11/romaine1200x680.jpg",
128 | "summary": "Romaine or cos lettuce is a variety of lettuce that grows in a tall head of sturdy dark green leaves with firm ribs down their centers.",
129 | "environmentalImpact": "High",
130 | "costPerAcre": 8847,
131 | "verdict": "Recommended",
132 | "daysToGrow" : 30,
133 | "waterUsed" : 0,
134 | "lbsPerAcre" : 32000,
135 | "valuePerAcre" : 10400
136 | },
137 | {
138 | "type": "irrigation",
139 | "name": "Surface",
140 | "earthScore": 90,
141 | "state": "Medium",
142 | "image": "https://upload.wikimedia.org/wikipedia/commons/9/9b/Furrow_irrigated_Sugar.JPG",
143 | "summary": "This type of irrigation relies on gravity flow to distribute water. It is the most commonly used irrigation methods and is well suited to mild slopes as well as regular slopes",
144 | "environmentalImpact": "Low",
145 | "costPerAcre": 23.41,
146 | "verdict": "Somewhat Recommended",
147 | "likes" : ["rice"]
148 | },
149 | {
150 | "type": "irrigation",
151 | "name": "Sprinkler",
152 | "earthScore": 70,
153 | "state": "Low",
154 | "image": "https://marketresearch.biz/wp-content/uploads/2019/04/sprinkler-irrigation-systems-market.jpg",
155 | "summary": "Sprinkler Irrigation involves spraying crops with water by use of permanently or temporarily set tools. Water is pumped through pipes and sprinkled to crops like drops of rain from the spray heads.",
156 | "environmentalImpact": "Low",
157 | "costPerAcre": 73.45,
158 | "verdict": "Highly Recommended"
159 | },
160 | {
161 | "type": "irrigation",
162 | "name": "Drip",
163 | "earthScore": 20,
164 | "state": "High",
165 | "image": "https://i.imgur.com/tdBE8nd.png",
166 | "summary": "Drip irrigation is a type of micro-irrigation system that has the potential to save water and nutrients by allowing water to drip slowly to the roots of plants",
167 | "environmentalImpact": "Medium",
168 | "costPerAcre": 500,
169 | "verdict": "Somewhat Recommended"
170 | },
171 | {
172 | "type": "irrigation",
173 | "name": "Subsurface",
174 | "earthScore": 51,
175 | "state": "High",
176 | "image": "https://i.ytimg.com/vi/jW5VhhT2bL0/maxresdefault.jpg",
177 | "summary": "Subsurface irrigation waters crops from below the ground. Pipes placed beneath the soil surface supply water to the roots of plants.",
178 | "environmentalImpact": "Medium",
179 | "costPerAcre": 800,
180 | "verdict": "Somewhat Recommended"
181 | },
182 | {
183 | "type": "irrigation",
184 | "name": "Center Pivot",
185 | "earthScore": 25,
186 | "state": "Medium",
187 | "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Crops_Kansas_AST_20010624.jpg/800px-Crops_Kansas_AST_20010624.jpg",
188 | "summary": "Water is distributed by a system of sprinklers that move on wheeled towers in a circular pattern. This system is common in flat areas of the United States.",
189 | "environmentalImpact": "High",
190 | "costPerAcre": 41.59,
191 | "verdict": "Somewhat Recommended"
192 | }
193 | ]
194 |
--------------------------------------------------------------------------------
/frontend/src/pages/earthData.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type": "crop",
4 | "name": "Strawberry",
5 | "earthScore": 86,
6 | "state": "Summer",
7 | "image": "https://i.imgur.com/u0rOCBQ.png",
8 | "summary": "Strawberries are low-growing herbaceous plants with a fibrous root system and a crown from which arise basal leaves",
9 | "environmentalImpact": "Medium",
10 | "costPerAcre": 1600,
11 | "verdict": "Highly Recommended",
12 | "daysToGrow" : 90,
13 | "waterUsed" : 0.36,
14 | "lbsPerAcre" : 10000,
15 | "valuePerAcre" : 2000
16 | },
17 | {
18 | "type": "crop",
19 | "name": "Corn",
20 | "earthScore": 72,
21 | "state": "Fall",
22 | "image": "https://i.imgur.com/Fm1Svvq.png",
23 | "summary": "Corn is a cereal grain first domesticated by indigenous peoples in southern Mexico about 10,000 years ago",
24 | "environmentalImpact": "Medium",
25 | "costPerAcre": 248,
26 | "verdict": "Highly Recommended",
27 | "daysToGrow" : 120,
28 | "waterUsed" : 0.19,
29 | "lbsPerAcre" : 7280,
30 | "valuePerAcre" : 699
31 | },
32 | {
33 | "type": "crop",
34 | "name": "Soybean",
35 | "earthScore": 70,
36 | "state": "Fall",
37 | "image": "https://cdn.britannica.com/28/154828-050-05C6239A/Soybeans.jpg",
38 | "summary": "The soybean or soya bean is a species of legume native to East Asia, widely grown for its edible bean, which has numerous uses.",
39 | "environmentalImpact": "Medium",
40 | "costPerAcre": 204,
41 | "verdict": "Highly Recommended",
42 | "daysToGrow" : 65,
43 | "waterUsed" : 0.19,
44 | "lbsPerAcre" : 2946,
45 | "valuePerAcre" : 0
46 | },
47 | {
48 | "type": "crop",
49 | "name": "Wheat",
50 | "earthScore": 78,
51 | "state": "Fall",
52 | "image": "https://i.imgur.com/eKB3yQ8.jpeg",
53 | "summary": "Wheat is a grass widely cultivated for its seed, a cereal grain which is a worldwide staple food",
54 | "environmentalImpact": "Low",
55 | "costPerAcre": 170,
56 | "verdict": "Highly Recommended",
57 | "daysToGrow" : 120,
58 | "waterUsed" : 0.14,
59 | "lbsPerAcre" : 3900,
60 | "valuePerAcre" : 0
61 | },
62 | {
63 | "type": "crop",
64 | "name": "Rice",
65 | "earthScore": 64,
66 | "state": "Summer",
67 | "image": "https://i.imgur.com/yegWy51.jpeg",
68 | "summary": "Rice is the most widely consumed staple food for over half the world's population",
69 | "environmentalImpact": "High",
70 | "costPerAcre": 225,
71 | "verdict": "Recommended",
72 | "daysToGrow" : 120,
73 | "waterUsed" : 0.26,
74 | "lbsPerAcre" : 8000,
75 | "valuePerAcre" : 892
76 | },
77 | {
78 | "type": "crop",
79 | "name": "Onion",
80 | "earthScore": 82,
81 | "state": "Summer",
82 | "image": "https://images.immediate.co.uk/production/volatile/sites/30/2020/08/the-health-benefits-of-onions-main-image-700-350-8425535.jpg?quality=90&resize=768,574",
83 | "summary": "The onion plant has a fan of hollow, bluish-green leaves and its bulb at the base of the plant begins to swell when a certain day-length is reached",
84 | "environmentalImpact": "Medium",
85 | "costPerAcre": 1750,
86 | "verdict": "Highly Recommended",
87 | "daysToGrow" : 100,
88 | "waterUsed" : 0.06,
89 | "lbsPerAcre" : 32000,
90 | "valuePerAcre" : 4200
91 | },
92 | {
93 | "type": "crop",
94 | "name": "Cotton",
95 | "earthScore": 81,
96 | "state": "Fall",
97 | "image": "https://upload.wikimedia.org/wikipedia/commons/6/68/CottonPlant.JPG",
98 | "summary": "Cotton is a shrub native to tropical and subtropical regions around the world, including the Americas, Africa, Egypt and India",
99 | "environmentalImpact": "High",
100 | "costPerAcre": 485,
101 | "verdict": "Recommended",
102 | "daysToGrow" : 150,
103 | "waterUsed" : 0.09,
104 | "lbsPerAcre" : 685,
105 | "valuePerAcre" : 500
106 | },
107 | {
108 | "type": "crop",
109 | "name": "Iceberg Lettuce",
110 | "earthScore": 78,
111 | "state": "Spring",
112 | "image": "https://media.newyorker.com/photos/5b6b08d3a676470b4ea9b91f/4:3/w_1920,h_1440,c_limit/Rosner-Lettuce.jpg",
113 | "summary": "Iceberg lettuce, also known as crisphead lettuce, has pale green leaves and grows in cabbage-like bulbs",
114 | "environmentalImpact": "High",
115 | "costPerAcre": 7999,
116 | "verdict": "Recommended",
117 | "daysToGrow" : 30,
118 | "waterUsed" : 0.09,
119 | "lbsPerAcre" : 32000,
120 | "valuePerAcre" : 10400
121 | },
122 | {
123 | "type": "crop",
124 | "name": "Romaine Lettuce",
125 | "earthScore": 78,
126 | "state": "Spring",
127 | "image": "https://www.foodsafetynews.com/files/2019/11/romaine1200x680.jpg",
128 | "summary": "Romaine or cos lettuce is a variety of lettuce that grows in a tall head of sturdy dark green leaves with firm ribs down their centers.",
129 | "environmentalImpact": "High",
130 | "costPerAcre": 8847,
131 | "verdict": "Recommended",
132 | "daysToGrow" : 30,
133 | "waterUsed" : 0,
134 | "lbsPerAcre" : 32000,
135 | "valuePerAcre" : 10400
136 | },
137 | {
138 | "type": "irrigation",
139 | "name": "Surface",
140 | "earthScore": 90,
141 | "state": "Medium",
142 | "image": "https://upload.wikimedia.org/wikipedia/commons/9/9b/Furrow_irrigated_Sugar.JPG",
143 | "summary": "This type of irrigation relies on gravity flow to distribute water. It is the most commonly used irrigation methods and is well suited to mild slopes as well as regular slopes",
144 | "environmentalImpact": "Low",
145 | "costPerAcre": 23.41,
146 | "verdict": "Somewhat Recommended",
147 | "likes" : ["rice"]
148 | },
149 | {
150 | "type": "irrigation",
151 | "name": "Sprinkler",
152 | "earthScore": 70,
153 | "state": "Low",
154 | "image": "https://marketresearch.biz/wp-content/uploads/2019/04/sprinkler-irrigation-systems-market.jpg",
155 | "summary": "Sprinkler Irrigation involves spraying crops with water by use of permanently or temporarily set tools. Water is pumped through pipes and sprinkled to crops like drops of rain from the spray heads.",
156 | "environmentalImpact": "Low",
157 | "costPerAcre": 73.45,
158 | "verdict": "Highly Recommended"
159 | },
160 | {
161 | "type": "irrigation",
162 | "name": "Drip",
163 | "earthScore": 20,
164 | "state": "High",
165 | "image": "https://i.imgur.com/tdBE8nd.png",
166 | "summary": "Drip irrigation is a type of micro-irrigation system that has the potential to save water and nutrients by allowing water to drip slowly to the roots of plants",
167 | "environmentalImpact": "Medium",
168 | "costPerAcre": 500,
169 | "verdict": "Somewhat Recommended"
170 | },
171 | {
172 | "type": "irrigation",
173 | "name": "Subsurface",
174 | "earthScore": 51,
175 | "state": "High",
176 | "image": "https://i.ytimg.com/vi/jW5VhhT2bL0/maxresdefault.jpg",
177 | "summary": "Subsurface irrigation waters crops from below the ground. Pipes placed beneath the soil surface supply water to the roots of plants.",
178 | "environmentalImpact": "Medium",
179 | "costPerAcre": 800,
180 | "verdict": "Somewhat Recommended"
181 | },
182 | {
183 | "type": "irrigation",
184 | "name": "Center Pivot",
185 | "earthScore": 25,
186 | "state": "Medium",
187 | "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Crops_Kansas_AST_20010624.jpg/800px-Crops_Kansas_AST_20010624.jpg",
188 | "summary": "Water is distributed by a system of sprinklers that move on wheeled towers in a circular pattern. This system is common in flat areas of the United States.",
189 | "environmentalImpact": "High",
190 | "costPerAcre": 41.59,
191 | "verdict": "Somewhat Recommended"
192 | }
193 | ]
194 |
--------------------------------------------------------------------------------
/frontend/src/components/Statistics/CropRotation/earthData.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type": "crop",
4 | "name": "Strawberry",
5 | "earthScore": 86,
6 | "state": "Summer",
7 | "image": "https://i.imgur.com/u0rOCBQ.png",
8 | "summary": "Strawberries are low-growing herbaceous plants with a fibrous root system and a crown from which arise basal leaves",
9 | "environmentalImpact": "Medium",
10 | "costPerAcre": 1600,
11 | "cost": 1600,
12 | "verdict": "Highly Recommended",
13 | "daysToGrow" : 90,
14 | "waterUsed" : 0.36,
15 | "lbsPerAcre" : 10000,
16 | "valuePerAcre" : 2000
17 | },
18 |
19 | {
20 | "type": "crop",
21 | "name": "Corn",
22 | "earthScore": 72,
23 | "state": "Fall",
24 | "image": "https://i.imgur.com/Fm1Svvq.png",
25 | "summary": "Corn is a cereal grain first domesticated by indigenous peoples in southern Mexico about 10,000 years ago",
26 | "environmentalImpact": "Medium",
27 | "costPerAcre": 248,
28 | "verdict": "Highly Recommended",
29 | "daysToGrow" : 120,
30 | "waterUsed" : 0.19,
31 | "lbsPerAcre" : 7280,
32 | "valuePerAcre" : 699
33 | },
34 | {
35 | "type": "crop",
36 | "name": "Soybean",
37 | "earthScore": 70,
38 | "state": "Fall",
39 | "image": "https://cdn.britannica.com/28/154828-050-05C6239A/Soybeans.jpg",
40 | "summary": "The soybean or soya bean is a species of legume native to East Asia, widely grown for its edible bean, which has numerous uses.",
41 | "environmentalImpact": "Medium",
42 | "costPerAcre": 204,
43 | "verdict": "Highly Recommended",
44 | "daysToGrow" : 65,
45 | "waterUsed" : 0.19,
46 | "lbsPerAcre" : 2946,
47 | "valuePerAcre" : 0
48 | },
49 | {
50 | "type": "crop",
51 | "name": "Wheat",
52 | "earthScore": 78,
53 | "state": "Fall",
54 | "image": "https://i.imgur.com/eKB3yQ8.jpeg",
55 | "summary": "Wheat is a grass widely cultivated for its seed, a cereal grain which is a worldwide staple food",
56 | "environmentalImpact": "Low",
57 | "costPerAcre": 170,
58 | "verdict": "Highly Recommended",
59 | "daysToGrow" : 120,
60 | "waterUsed" : 0.14,
61 | "lbsPerAcre" : 3900,
62 | "valuePerAcre" : 0
63 | },
64 | {
65 | "type": "crop",
66 | "name": "Rice",
67 | "earthScore": 64,
68 | "state": "Summer",
69 | "image": "https://i.imgur.com/yegWy51.jpeg",
70 | "summary": "Rice is the most widely consumed staple food for over half the world's population",
71 | "environmentalImpact": "High",
72 | "costPerAcre": 225,
73 | "verdict": "Recommended",
74 | "daysToGrow" : 120,
75 | "waterUsed" : 0.26,
76 | "lbsPerAcre" : 8000,
77 | "valuePerAcre" : 892
78 | },
79 | {
80 | "type": "crop",
81 | "name": "Onion",
82 | "earthScore": 82,
83 | "state": "Summer",
84 | "image": "https://images.immediate.co.uk/production/volatile/sites/30/2020/08/the-health-benefits-of-onions-main-image-700-350-8425535.jpg?quality=90&resize=768,574",
85 | "summary": "The onion plant has a fan of hollow, bluish-green leaves and its bulb at the base of the plant begins to swell when a certain day-length is reached",
86 | "environmentalImpact": "Medium",
87 | "costPerAcre": 1750,
88 | "verdict": "Highly Recommended",
89 | "daysToGrow" : 100,
90 | "waterUsed" : 0.06,
91 | "lbsPerAcre" : 32000,
92 | "valuePerAcre" : 4200
93 | },
94 | {
95 | "type": "crop",
96 | "name": "Cotton",
97 | "earthScore": 81,
98 | "state": "Fall",
99 | "image": "https://upload.wikimedia.org/wikipedia/commons/6/68/CottonPlant.JPG",
100 | "summary": "Cotton is a shrub native to tropical and subtropical regions around the world, including the Americas, Africa, Egypt and India",
101 | "environmentalImpact": "High",
102 | "costPerAcre": 485,
103 | "verdict": "Recommended",
104 | "daysToGrow" : 150,
105 | "waterUsed" : 0.09,
106 | "lbsPerAcre" : 685,
107 | "valuePerAcre" : 500
108 | },
109 | {
110 | "type": "crop",
111 | "name": "Iceberg Lettuce",
112 | "earthScore": 78,
113 | "state": "Spring",
114 | "image": "https://media.newyorker.com/photos/5b6b08d3a676470b4ea9b91f/4:3/w_1920,h_1440,c_limit/Rosner-Lettuce.jpg",
115 | "summary": "Iceberg lettuce, also known as crisphead lettuce, has pale green leaves and grows in cabbage-like bulbs",
116 | "environmentalImpact": "High",
117 | "costPerAcre": 7999,
118 | "verdict": "Recommended",
119 | "daysToGrow" : 30,
120 | "waterUsed" : 0.09,
121 | "lbsPerAcre" : 32000,
122 | "valuePerAcre" : 10400
123 | },
124 | {
125 | "type": "crop",
126 | "name": "Romaine Lettuce",
127 | "earthScore": 78,
128 | "state": "Spring",
129 | "image": "https://www.foodsafetynews.com/files/2019/11/romaine1200x680.jpg",
130 | "summary": "Romaine or cos lettuce is a variety of lettuce that grows in a tall head of sturdy dark green leaves with firm ribs down their centers.",
131 | "environmentalImpact": "High",
132 | "costPerAcre": 8847,
133 | "verdict": "Recommended",
134 | "daysToGrow" : 30,
135 | "waterUsed" : 0,
136 | "lbsPerAcre" : 32000,
137 | "valuePerAcre" : 10400
138 | },
139 | {
140 | "type": "irrigation",
141 | "name": "Surface",
142 | "earthScore": 90,
143 | "state": "Medium",
144 | "image": "https://upload.wikimedia.org/wikipedia/commons/9/9b/Furrow_irrigated_Sugar.JPG",
145 | "summary": "This type of irrigation relies on gravity flow to distribute water. It is the most commonly used irrigation methods and is well suited to mild slopes as well as regular slopes",
146 | "environmentalImpact": "Low",
147 | "costPerAcre": 23.41,
148 | "verdict": "Somewhat Recommended",
149 | "likes" : ["rice"]
150 | },
151 | {
152 | "type": "irrigation",
153 | "name": "Sprinkler",
154 | "earthScore": 70,
155 | "state": "Low",
156 | "image": "https://marketresearch.biz/wp-content/uploads/2019/04/sprinkler-irrigation-systems-market.jpg",
157 | "summary": "Sprinkler Irrigation involves spraying crops with water by use of permanently or temporarily set tools. Water is pumped through pipes and sprinkled to crops like drops of rain from the spray heads.",
158 | "environmentalImpact": "Low",
159 | "costPerAcre": 73.45,
160 | "verdict": "Highly Recommended"
161 | },
162 | {
163 | "type": "irrigation",
164 | "name": "Drip",
165 | "earthScore": 20,
166 | "state": "High",
167 | "image": "https://i.imgur.com/tdBE8nd.png",
168 | "summary": "Drip irrigation is a type of micro-irrigation system that has the potential to save water and nutrients by allowing water to drip slowly to the roots of plants",
169 | "environmentalImpact": "Medium",
170 | "costPerAcre": 500,
171 | "verdict": "Somewhat Recommended"
172 | },
173 | {
174 | "type": "irrigation",
175 | "name": "Subsurface",
176 | "earthScore": 51,
177 | "state": "High",
178 | "image": "https://i.ytimg.com/vi/jW5VhhT2bL0/maxresdefault.jpg",
179 | "summary": "Subsurface irrigation waters crops from below the ground. Pipes placed beneath the soil surface supply water to the roots of plants.",
180 | "environmentalImpact": "Medium",
181 | "costPerAcre": 800,
182 | "verdict": "Somewhat Recommended"
183 | },
184 | {
185 | "type": "irrigation",
186 | "name": "Center Pivot",
187 | "earthScore": 25,
188 | "state": "Medium",
189 | "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Crops_Kansas_AST_20010624.jpg/800px-Crops_Kansas_AST_20010624.jpg",
190 | "summary": "Water is distributed by a system of sprinklers that move on wheeled towers in a circular pattern. This system is common in flat areas of the United States.",
191 | "environmentalImpact": "High",
192 | "costPerAcre": 41.59,
193 | "verdict": "Somewhat Recommended"
194 | }
195 | ]
196 |
--------------------------------------------------------------------------------
/frontend/src/pages/Home.jsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | import { useEffect, useState } from 'react';
3 | import anime from 'animejs';
4 | import { notification } from 'antd';
5 | import DataEarth from './earthData.json';
6 | import Map from 'components/Map';
7 | import NavBar from 'components/NavBar';
8 | import SideBar from 'components/SideBar';
9 | import StatsModal from 'components/StatsModal';
10 |
11 | import CurrentlySelected from 'components/CurrentlySelected';
12 | import Seasons from 'components/Seasons';
13 | import Timeline from 'components/Timeline';
14 |
15 | import Analysis from 'components/Analysis';
16 | import PlotType from 'components/PlotType';
17 | import BigCurrentlySelected from 'components/BigCurrentlySelected';
18 |
19 | import Date from 'components/Date';
20 |
21 | //import * as AnalysisLib from "analysis";
22 |
23 | let seasonsAnimation, timelineAnimation, currentlySelectedAnimation, analysisAnimation, cardAnimation, cards2Animation;
24 | const HomePage = () => {
25 | const [sideBarPage, setSideBarPage] = useState('map');
26 | const [currentPlot, setCurrentPlot] = useState({});
27 | const [plots, setPlotOptions] = useState([]);
28 | const [visible, setModal] = useState(false);
29 | const [statsData, setStatsData] = useState([]);
30 | const [objects, setNewObjects] = useState({});
31 | const [day, setDay] = useState(108);
32 | const [environmentScore, setEnvironmentScore] = useState ('?');
33 | const [environmentDescription, setEnvironmentDescription] = useState('Click on a land plot to get your farm judged by drishti.')
34 | const [locationScore, setLocationScore] = useState ('?');
35 | const [locationDescription, setLocationDescription] = useState('Click on a land plot to get your farm judged by drishti.')
36 |
37 | const newSetSideBarPage = (sideBarPageParam) => {
38 | console.log(sideBarPageParam);
39 | console.log('hi')
40 | if (sideBarPageParam !== 'map') {
41 | if (seasonsAnimation) {
42 | seasonsAnimation.seek(0);
43 | seasonsAnimation.play();
44 | seasonsAnimation.finished.then(() => {
45 | seasonsAnimation.reverse();
46 | })
47 | }
48 | if(analysisAnimation) {
49 | analysisAnimation.seek(0);
50 | analysisAnimation.play();
51 | analysisAnimation.finished.then(() => {
52 | cardAnimation.play();
53 | cards2Animation.play();
54 | analysisAnimation.reverse();
55 | })
56 | }
57 | if (timelineAnimation) {
58 | timelineAnimation.seek(0);
59 | timelineAnimation.play();
60 | timelineAnimation.finished.then(() => {
61 | timelineAnimation.reverse();
62 | })
63 | }
64 | if (currentlySelectedAnimation) {
65 | currentlySelectedAnimation.seek(0);
66 | currentlySelectedAnimation.play();
67 | currentlySelectedAnimation.finished.then(() => {
68 | currentlySelectedAnimation.reverse();
69 | })
70 | }
71 | } else {
72 | if (seasonsAnimation) {
73 | cardAnimation.seek(0);
74 | cards2Animation.seek(0);
75 | seasonsAnimation.seek(1000);
76 | seasonsAnimation.play();
77 | seasonsAnimation.finished.then(() => {
78 | seasonsAnimation.reverse();
79 | })
80 | }
81 | if (timelineAnimation) {
82 | timelineAnimation.seek(1000);
83 | timelineAnimation.play();
84 | timelineAnimation.finished.then(() => {
85 | timelineAnimation.reverse();
86 | })
87 | }
88 | if (currentlySelectedAnimation) {
89 | currentlySelectedAnimation.seek(1000);
90 | currentlySelectedAnimation.play();
91 | currentlySelectedAnimation.finished.then(() => {
92 | currentlySelectedAnimation.reverse();
93 | })
94 | }
95 | if (analysisAnimation) {
96 | analysisAnimation.seek(1000);
97 | analysisAnimation.play();
98 | analysisAnimation.finished.then(() => {
99 | analysisAnimation.reverse();
100 | })
101 | }
102 | }
103 | setSideBarPage(side => sideBarPageParam);
104 | }
105 |
106 | const flipModal = () => {
107 | const currentObjects = Object.values(objects);
108 | const tempStatsData = {}
109 | for (let i = 0; i < currentObjects.length; i++ ) {
110 | const currentObject = currentObjects[i];
111 | const sqFt = Math.floor((currentObject.width * currentObject.length) / 3.2808);
112 |
113 | const acres = AnalysisLib.sqftToAcre(sqFt);
114 | console.log(acres);
115 | if (currentObject.currentPlot.type === 'crop') {
116 | const name = currentObject.currentPlot.name;
117 | if (currentObject.currentPlot.name in tempStatsData) {
118 | tempStatsData[name] += parseFloat(acres.toFixed(2));
119 | } else {
120 | tempStatsData[name] = parseFloat(acres.toFixed(2));
121 | }
122 | }
123 | }
124 | const keys = Object.keys(tempStatsData);
125 | const newStatsData = []
126 | for (let j = 0; j < keys.length; j++ ) {
127 | newStatsData.push({
128 | crop: keys[j],
129 | acre: tempStatsData[keys[j]]
130 | })
131 | }
132 | setStatsData(d => newStatsData);
133 | if (currentObjects.length > 1) {
134 | setModal(!visible);
135 | } else {
136 | notification.open({
137 | description: "Please place some plots on the map first! I can't analyze empty fields!",
138 | placement: 'bottomLeft'
139 | });
140 | }
141 | };
142 |
143 | const [draw, setDraw] = useState(null);
144 | const [analysis, setAnalysis] = useState({});
145 |
146 | useEffect(async () => {
147 |
148 | const data=DataEarth;
149 | console.log(data);
150 | if (!data) throw new Error('Empty response from server');
151 | if (data.error) throw new Error(data.error.message);
152 |
153 | setPlotOptions(data);
154 |
155 | analysisAnimation = anime({
156 | targets: '.right-into',
157 | translateX: -1200,
158 | duration: 1000,
159 | loop: false,
160 | autoplay: false,
161 | easing: 'easeInOutSine'
162 | });
163 |
164 | seasonsAnimation = anime({
165 | targets: '.seasons',
166 | translateY: 200,
167 | duration: 1000,
168 | loop: false,
169 | autoplay: false,
170 | easing: 'easeInOutSine'
171 | });
172 |
173 | cardAnimation = anime({
174 | targets: '.analysis .fade-up',
175 | translateY: -10,
176 | opacity: 100,
177 | duration: 1000,
178 | delay: function(el, i) { return i * 200; },
179 | loop: false,
180 | autoplay: false,
181 | easing: 'easeInOutSine',
182 | });
183 |
184 | cards2Animation = anime({
185 | targets: '.plots .plot',
186 | translateY: -10,
187 | opacity: 100,
188 | duration: 1000,
189 | delay: function(el, i) { return i * 200; },
190 | loop: false,
191 | autoplay: false,
192 | easing: 'easeInOutSine',
193 | });
194 |
195 | timelineAnimation = anime({
196 | targets: '.timeline',
197 | translateY: -200,
198 | duration: 1000,
199 | loop: false,
200 | autoplay: false,
201 | easing: 'easeInOutSine'
202 | });
203 |
204 | currentlySelectedAnimation = anime({
205 | targets: '.currently-selected',
206 | translateY: -200,
207 | duration: 1000,
208 | loop: false,
209 | autoplay: false,
210 | easing: 'easeInOutSine'
211 | });
212 | }, []);
213 |
214 | return (
215 |
216 |
223 |
224 |
225 |
239 |
240 |
241 |
242 |
243 |
244 |
248 |
249 |
250 | );
251 | };
252 |
253 | export default HomePage;
254 |
--------------------------------------------------------------------------------
/frontend/src/components/Map/index.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import ReactMapboxGl from 'react-mapbox-gl';
3 | import perfectResponse from './perfectResponse.json'
4 | import DrawRectangle, {
5 | DrawStyles
6 | } from "mapbox-gl-draw-rectangle-restrict-area";
7 | import "@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css";
8 | import DrawControl from "react-mapbox-draw-rectangle";
9 |
10 | import { loadLocation } from './utils.js';
11 | import './style.scss';
12 |
13 |
14 | let color = 'rgba(64, 145, 220, 1)';
15 | function measure(lat1, lon1, lat2, lon2){ // generally used geo measurement function
16 | var R = 6378.137; // Radius of earth in KM
17 | var dLat = lat2 * Math.PI / 180 - lat1 * Math.PI / 180;
18 | var dLon = lon2 * Math.PI / 180 - lon1 * Math.PI / 180;
19 | var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
20 | Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
21 | Math.sin(dLon/2) * Math.sin(dLon/2);
22 | var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
23 | var d = R * c;
24 | return d * 1000; // meters
25 | }
26 |
27 | let day = 108;
28 | const Map = ReactMapboxGl({
29 | accessToken:
30 | 'pk.eyJ1IjoiZW1pbmd1eWVuIiwiYSI6ImNrOGI2ZjRyODA1aHEzZG93cmFxaHR5d2IifQ.x8v_uFbdBanYgRtoKCGIOw',
31 | animationOptions: {
32 | essential: true
33 | },
34 | pitchWithRotate: false
35 | });
36 |
37 | let ourDraw = null;
38 | let newCurrentPlot = null;
39 | // CENTER LAT AND CENTER LNG are actual lat long btw
40 | let objects = {};
41 | const MapComponent = (props) => {
42 | const height = (props.sideBarPage !== 'map') ? '70vh' : '95vh';
43 | const bottom = (props.sideBarPage !== 'map') ? '11vh' : '0vh';
44 | const bottomtwo = (props.sideBarPage !== 'map') ? '0vh' : '0vh';
45 | const radius = (props.sideBarPage !== 'map') ? '6px' : '0';
46 | const left = (props.sideBarPage !== 'map') ? '6vw' : '5vw';
47 | const width = (props.sideBarPage !== 'map') ? '45vw' : '100vw';
48 | const right = (props.sideBarPage !== 'map') ? '-25vw' : '0vw';
49 |
50 | const [center, setCenter] = useState([-92.0717042, 42.0434796]);
51 | const [zoom, setZoom] = useState([13]);
52 |
53 | useEffect(() => {
54 | day = props.day;
55 | }, [props.day])
56 |
57 | const onMapLoad = (map) => {
58 | window.map = map;
59 | };
60 |
61 | const setObjects = (id, newObjects) => {
62 | objects[id] = newObjects;
63 | props.setNewObjects(obj => objects);
64 | }
65 |
66 | const onMapClick = async (map, e) => {
67 | Object.values(objects).forEach(async(value) => {
68 | console.log(value);
69 | if (e.lngLat.lng < value.maxLat && e.lngLat.lat < value.maxLng && e.lngLat.lat > value.minLng && e.lngLat.lng > value.minLat) {
70 | const newNewCurrentPlot = {...value.currentPlot};
71 | newNewCurrentPlot.sqFt = Math.floor((value.width * value.length) / 3.2808);
72 | props.setCurrentPlot(
73 | newNewCurrentPlot
74 | );
75 |
76 |
77 |
78 | const data = perfectResponse;
79 | console.log(data);
80 | if (!data) throw new Error('Empty response from server');
81 | if (data.error) throw new Error(data.error.message);
82 |
83 | const environment = data.environment;
84 | const location = data.location;
85 | let grade = 'A';
86 | if (environment.environmentGrade < 2) {
87 | grade = 'F';
88 | } else if (environment.environmentGrade < 5) {
89 | grade = 'D'
90 | } else if (environment.environmentGrade < 7) {
91 | grade = 'C'
92 | } else if (environment.environmentGrade < 9) {
93 | grade = 'B'
94 | }
95 | let grade2 = 'A';
96 | if (location.locationGrade < 2) {
97 | grade2 = 'F';
98 | } else if (location.locationGrade < 5) {
99 | grade2 = 'D'
100 | } else if (location.locationGrade < 7) {
101 | grade2 = 'C'
102 | } else if (location.locationGrade < 9) {
103 | grade2 = 'B'
104 | }
105 | props.setEnvironmentDescription(environment.environmentDescription);
106 | props.setEnvironmentScore(grade);
107 | props.setLocationDescription(location.locationDescription);
108 | props.setLocationScore(grade2);
109 |
110 | const insights = data.insights;
111 |
112 | let soilTemp = null;
113 | let soilMoisture = null;
114 | const soil = insights.soilLatest.data;
115 | if (soil.length > 0) {
116 | soilTemp = Math.floor(soil[0]['soil_temperature'] * 1.8000 + 32.00);
117 | soilMoisture = soil[0]['soil_moisture'];
118 | }
119 |
120 | const fire = insights.fireLatest.data;
121 | let fireStatus = null;
122 | if (fire.length > 0) {
123 | if (fire[0].confidence === 'low') {
124 | fireStatus = 'Low';
125 | } else if (fire[0].confidence === 'nominal') {
126 | fireStatus = 'Medium';
127 | } else {
128 | fireStatus = 'High';
129 | }
130 | }
131 |
132 | let pollenStatus = null;
133 | const pollen = insights.pollenForecast.data;
134 | if (pollen.length > 0) {
135 | pollenStatus = pollen[0]['Risk']['grass_pollen'];
136 | }
137 |
138 | let airQualityStatus = null;
139 | const airQuality = insights.airQuality.stations;
140 | if (airQuality.length > 0) {
141 | airQualityStatus = airQuality[0];
142 | }
143 |
144 | props.setAnalysis({
145 | fire: fireStatus,
146 | pollen: pollenStatus,
147 | soilTemp: soilTemp,
148 | soilMoisture: soilMoisture,
149 | waterVapor: '51%',
150 | airQuality: airQualityStatus
151 | })
152 | }
153 | })
154 | }
155 |
156 | const onMouseMove = (map, e) => {
157 | let cursor = 'default';
158 | Object.values(objects).forEach((value) => {
159 | if (e.lngLat.lng < value.maxLat && e.lngLat.lat < value.maxLng && e.lngLat.lat > value.minLng && e.lngLat.lng > value.minLat) {
160 | value.model.scale.x = 1.02;
161 | value.model.scale.z = 1.02;
162 | cursor = 'pointer';
163 | } else {
164 | value.model.scale.x = 1;
165 | value.model.scale.z = 1;
166 | }
167 | })
168 | document.body.style.cursor = cursor;
169 | }
170 |
171 | useEffect(() => {
172 | newCurrentPlot = props.currentPlot;
173 | if (props.currentPlot && props.currentPlot.type === 'crop') {
174 | if (props.currentPlot.state === 'Spring' || props.currentPlot.state === 'Summer') {
175 | color = 'rgba(39, 174, 96, 1)';
176 | } else {
177 | color = 'rgba(237, 193, 81, 1)';
178 | }
179 | } else {
180 | color = 'rgba(64, 145, 220, 1)';
181 | }
182 | }, [props.currentPlot]);
183 |
184 | const onDrawCreate = ({ features }) => {
185 | console.log(features);
186 | if (features.length >= 1) {
187 | let maxLat = -1;
188 | let maxLng = -1;
189 | let minLat = 1000;
190 | let minLng = 1000;
191 | for (let i = 0; i < features[0].geometry.coordinates[0].length; i++) {
192 | maxLat = Math.max(Math.abs(features[0].geometry.coordinates[0][i][0]), maxLat);
193 | maxLng = Math.max(Math.abs(features[0].geometry.coordinates[0][i][1]), maxLng);
194 | minLat = Math.min(Math.abs(features[0].geometry.coordinates[0][i][0]), minLat);
195 | minLng = Math.min(Math.abs(features[0].geometry.coordinates[0][i][1]), minLng);
196 | }
197 | const width = measure(0, maxLat, 0, minLat);
198 | const length = measure(maxLng, 0, minLng, 0);
199 |
200 | let centerLat = (maxLat + minLat) / 2;
201 | let centerLng = (maxLng + minLng) / 2;
202 | if (features[0].geometry.coordinates[0][0][0] < 0) {
203 | centerLat = -centerLat;
204 | const newMinLat = -maxLat;
205 | maxLat = -minLat;
206 | minLat = newMinLat;
207 | }
208 | if (features[0].geometry.coordinates[0][0][1] < 0) {
209 | centerLng = -centerLng;
210 | const newMinLng = -maxLng;
211 | maxLng = -minLng;
212 | minLng = newMinLng;
213 | }
214 |
215 | if (window.map && newCurrentPlot) {
216 | console.log(features[0].id);
217 | loadLocation(window.map, centerLat, centerLng, features[0].id, width, length, color, minLat, maxLat, minLng, maxLng, newCurrentPlot, day, setObjects)
218 | }
219 |
220 | if (ourDraw && features[0].id) {
221 | const currentId = features[0].id;
222 | setTimeout(() => {
223 | ourDraw.draw.delete(currentId)
224 | }, 1000);
225 | }
226 | }
227 | };
228 |
229 | const onDrawUpdate = ({ features }) => {
230 | console.log(features);
231 | };
232 |
233 | return (
234 |
244 |
268 | {
285 | props.setDraw(drawControl);
286 | ourDraw = drawControl;
287 | }}
288 | styles={DrawStyles}
289 | onDrawCreate={onDrawCreate}
290 | >
291 |
292 |
293 | );
294 | }
295 |
296 | export default MapComponent;
--------------------------------------------------------------------------------