├── .firebaserc ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── firebase.json ├── iceland-vegetation.jpg ├── package.json ├── postcss.config.js ├── scripts ├── filter-iceland.js └── make-vectors.sh ├── src ├── @types │ └── geotiff │ │ └── index.d.ts ├── assets │ ├── atlas │ │ ├── ndvi-anomaly.atlas │ │ ├── ndvi-anomaly.atlas.json │ │ ├── ndvi.atlas │ │ └── ndvi.atlas.json │ └── img │ │ ├── fa-bars.svg │ │ ├── fa-search-minus.svg │ │ ├── fa-search-plus.svg │ │ ├── fa-times.svg │ │ ├── favicon.png │ │ ├── iceland_ndvi.png │ │ ├── ndvi-high.svg │ │ ├── ndvi-low.svg │ │ ├── ndvi-medium.svg │ │ ├── select-arrow.svg │ │ ├── vegetation.svg │ │ └── vplogo.svg ├── js │ ├── components │ │ ├── App.tsx │ │ ├── Container.tsx │ │ ├── Footer.tsx │ │ ├── Header.tsx │ │ ├── Heading.tsx │ │ ├── Incompatible.tsx │ │ ├── InfoOverlay.tsx │ │ ├── Loading.tsx │ │ ├── Logo.tsx │ │ ├── ModeSelect.tsx │ │ ├── MouseElement.tsx │ │ ├── RightNav.tsx │ │ ├── SingleView.tsx │ │ ├── SingleViewContainer.tsx │ │ ├── SizedElement.tsx │ │ ├── TouchElement.tsx │ │ ├── Zoom.tsx │ │ ├── icons │ │ │ ├── Bars.tsx │ │ │ ├── Comment.tsx │ │ │ ├── FileText.tsx │ │ │ ├── GitHub.tsx │ │ │ └── Times.tsx │ │ └── timeSeries │ │ │ ├── Brush.tsx │ │ │ ├── Chart.tsx │ │ │ ├── Legend.tsx │ │ │ ├── NDVI.tsx │ │ │ ├── NDVIAnomaly.tsx │ │ │ ├── NDVIAnomalySorted.tsx │ │ │ ├── NDVISorted.tsx │ │ │ ├── Series.tsx │ │ │ ├── SeriesSorted.tsx │ │ │ ├── XAxis.tsx │ │ │ ├── XAxisSorted.tsx │ │ │ ├── YAxisNDVI.tsx │ │ │ └── YAxisNDVIAnomaly.tsx │ ├── constants.ts │ ├── gl │ │ ├── BoxSelectView.ts │ │ ├── GLManager.ts │ │ ├── GLTest.ts │ │ ├── OutlineView.ts │ │ ├── RasterHeightGather.ts │ │ ├── RasterMask.ts │ │ ├── RasterView.ts │ │ ├── RasterWidthGather.ts │ │ ├── VectorView.ts │ │ └── shaders │ │ │ ├── functions │ │ │ ├── atlasSample.glsl.ts │ │ │ ├── atlasUV.glsl.ts │ │ │ ├── colorScale.glsl.ts │ │ │ ├── geoByteScale.glsl.ts │ │ │ ├── isPointInBBox.glsl.ts │ │ │ ├── lngLatToMercator.glsl.ts │ │ │ ├── lngLatToSinusoidal.glsl.ts │ │ │ ├── luma.glsl.ts │ │ │ ├── mercatorToLngLat.glsl.ts │ │ │ ├── ndviScale.glsl.ts │ │ │ ├── pointInBBox.glsl.ts │ │ │ └── sinusoidalToLngLat.glsl.ts │ │ │ ├── gatherFragHeight.glsl.ts │ │ │ ├── gatherFragWidth.glsl.ts │ │ │ ├── gatherVert.glsl.ts │ │ │ ├── landFrag.glsl.ts │ │ │ ├── landVert.glsl.ts │ │ │ ├── maskFrag.glsl.ts │ │ │ ├── maskVert.glsl.ts │ │ │ ├── outlineFrag.glsl.ts │ │ │ ├── outlineVert.glsl.ts │ │ │ ├── viewFrag.glsl.ts │ │ │ └── viewVert.glsl.ts │ ├── index.tsx │ ├── models │ │ ├── Atlas.ts │ │ ├── BoundingBox.ts │ │ ├── Camera.ts │ │ ├── Point.ts │ │ ├── RootStore.ts │ │ └── VectorLayer.ts │ ├── scales.ts │ └── utils.ts └── scss │ └── styles.scss ├── tsconfig.json ├── tslint.json ├── webpack.common.js ├── webpack.dev.js ├── webpack.prod.js ├── webpack.stage.js └── yarn.lock /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "stage": "iceland-stage" 4 | } 5 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.psd filter=lfs diff=lfs merge=lfs -text 2 | *.tif filter=lfs diff=lfs merge=lfs -text 3 | *.tiff filter=lfs diff=lfs merge=lfs -text 4 | *.atlas filter=lfs diff=lfs merge=lfs -text 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Numerous always-ignore extensions 2 | *.diff 3 | *.err 4 | *.orig 5 | *.log 6 | *.rej 7 | *.swo 8 | *.swp 9 | *.vi 10 | *~ 11 | *.sass-cache 12 | 13 | # OS or Editor folders 14 | .DS_Store 15 | .cache 16 | .project 17 | .settings 18 | .tmproj 19 | nbproject 20 | Thumbs.db 21 | .exrc 22 | 23 | # NPM packages folder 24 | node_modules/ 25 | 26 | # Brunch output folder 27 | public/ 28 | 29 | # Temporary data folder 30 | tmpdata/ 31 | 32 | # Generated files 33 | src/assets/geojson/ 34 | dist/ 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 VisualPerspective, LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 | # NDVI Viewer 6 | 7 | Allows the analysis of NDVI over time for a region using regl, WebGL, and React. ([Live Demo](http://iceland.visualperspective.io/)) 8 | 9 | *__Note:__ this is alpha software and requires more documentation and tests before being generally useful.* 10 | 11 | ### Running the project 12 | To run the project, you'll need: 13 | * Node 9+ 14 | * a recent version of Yarn 15 | * git-lfs 16 | * ogr2ogr 17 | 18 | Then: 19 | * Clone this repo, and run `yarn` to install dependencies 20 | * Run `bash scripts/make-vectors.sh` to download and process geojson for Iceland 21 | * Run `yarn start` to start a development server 22 | * The app should be available at `http://localhost:8080` (initial page load will take a while for compilation) 23 | 24 | ### Data format 25 | The main data file, `src/assets/atlas/ndvi.atlas`, was produced using [geotiff-atlas](https://github.com/VisualPerspective/geotiff-atlas). Refer to the README of that repository for more information. 26 | 27 | ### Focus on Iceland data set 28 | For now, this viewer is focused on Iceland, to make it work with another region some changes and enhancements would need to be made: 29 | * Change various constants in `constants.ts` 30 | * Download a set of geotiffs for an area, and process them using https://github.com/VisualPerspective/geotiff-atlas 31 | * Make other tweaks/changes to make the code more general 32 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "dist", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /iceland-vegetation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VisualPerspective/ndvi-viewer/aad996d893f21ca00c3012bc0515af1e5b3fab54/iceland-vegetation.jpg -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iceland-ndvi", 3 | "version": "0.0.1", 4 | "main": "index.js", 5 | "license": "Commercial", 6 | "private": true, 7 | "dependencies": { 8 | "@types/earcut": "^2.1.0", 9 | "@types/lodash": "^4.14.110", 10 | "@types/node": "^12.7.11", 11 | "@types/react": "^16.4.4", 12 | "@types/react-dom": "^16.0.6", 13 | "@types/react-helmet": "^5.0.6", 14 | "autoprefixer": "^8.6.4", 15 | "axios": "^0.18.0", 16 | "babel-runtime": "^6.26.0", 17 | "copy-webpack-plugin": "^4.5.2", 18 | "css-loader": "^0.28.11", 19 | "d3": "^5.5.0", 20 | "d3-scale-chromatic": "^1.3.0", 21 | "earcut": "^2.1.3", 22 | "es6-promise": "^4.2.4", 23 | "file-loader": "^1.1.11", 24 | "geotiff": "1.0.0-beta.3", 25 | "gl-matrix": "^2.6.1", 26 | "glsl-colormap": "^1.0.1", 27 | "html-webpack-plugin": "^3.2.0", 28 | "lodash": "^4.17.10", 29 | "mini-css-extract-plugin": "^0.4.0", 30 | "mobx": "^4.3.1", 31 | "mobx-react": "^5.2.3", 32 | "mobx-react-devtools": "^5.0.1", 33 | "ndjson-cli": "^0.3.1", 34 | "node-sass": "^4.9.0", 35 | "postcss-loader": "^2.1.5", 36 | "range-slider-sass": "^2.0.0", 37 | "react": "^16.4.1", 38 | "react-dom": "^16.4.1", 39 | "react-helmet": "5.2.0", 40 | "reflect-metadata": "^0.1.12", 41 | "regl": "^1.3.7", 42 | "sass-loader": "^7.0.3", 43 | "shader-loader": "^1.3.1", 44 | "ts-loader": "^6.2.0", 45 | "tslint": "^5.10.0", 46 | "tslint-loader": "^3.6.0", 47 | "typescript": "^3.6.3", 48 | "viewport-mercator-project": "5.1.0", 49 | "webpack": "^4.12.2", 50 | "webpack-cli": "^3.0.8", 51 | "webpack-merge": "^4.1.3", 52 | "webpack-serve": "^1.0.4", 53 | "worker-loader": "^2.0.0" 54 | }, 55 | "scripts": { 56 | "start": "webpack-serve --config webpack.dev.js --host 0.0.0.0 --static ./src/assets/", 57 | "build": "webpack --config webpack.prod.js", 58 | "build-stage": "webpack --config webpack.stage.js", 59 | "deploy": "yarn build && firebase deploy", 60 | "deploy-stage": "yarn build-stage && firebase deploy" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('autoprefixer') 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /scripts/filter-iceland.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash') 2 | var countries = require('../tmpdata/vectors.json') 3 | var iceland = _.find(countries.features, (feature) => ( 4 | feature.properties.ADM0_A3 === 'ISL' 5 | )) 6 | 7 | console.log(JSON.stringify(iceland)) 8 | -------------------------------------------------------------------------------- /scripts/make-vectors.sh: -------------------------------------------------------------------------------- 1 | mkdir -p tmpdata 2 | cd tmpdata 3 | 4 | # countries 5 | if [ ! -f countries.zip ]; then 6 | curl "http://naciscdn.org/naturalearth/10m/cultural/ne_10m_admin_0_countries.zip" -o countries.zip 7 | fi 8 | unzip -o countries.zip 9 | 10 | mkdir -p ../src/assets/geojson/ 11 | 12 | ogr2ogr -f GeoJSON -t_srs crs:84 vectors.json ne_10m_admin_0_countries.shp 13 | 14 | node ../scripts/filter-iceland.js > ../src/assets/geojson/vectors.json 15 | -------------------------------------------------------------------------------- /src/@types/geotiff/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'geotiff' { 2 | export const fromArrayBuffer: (response: ArrayBuffer) => object 3 | export const Pool: any 4 | } 5 | -------------------------------------------------------------------------------- /src/assets/atlas/ndvi-anomaly.atlas: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:76826b07a94864e2225621a360efea72ed46f33bc7a9d1bce09bf466dc489fd8 3 | size 53015040 4 | -------------------------------------------------------------------------------- /src/assets/atlas/ndvi-anomaly.atlas.json: -------------------------------------------------------------------------------- 1 | { 2 | "approximateDownloadSize": 20800000, 3 | "format": "float32", 4 | "rasterWidth": 590, 5 | "rasterHeight": 416, 6 | "numRasters": 204, 7 | "rastersWide": 6, 8 | "rastersHigh": 9, 9 | "channels": 4, 10 | "boundingBox": [ 11 | -1153751, 12 | 7029082, 13 | -607255, 14 | 7414791 15 | ] 16 | } -------------------------------------------------------------------------------- /src/assets/atlas/ndvi.atlas: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:771d5893a396719741ec4430a9c8ddc129982602de05c21679954410cc060d8b 3 | size 53015040 4 | -------------------------------------------------------------------------------- /src/assets/atlas/ndvi.atlas.json: -------------------------------------------------------------------------------- 1 | { 2 | "approximateDownloadSize": 20800000, 3 | "format": "float32", 4 | "rasterWidth": 590, 5 | "rasterHeight": 416, 6 | "numRasters": 204, 7 | "rastersWide": 6, 8 | "rastersHigh": 9, 9 | "channels": 4, 10 | "boundingBox": [ 11 | -1153751, 12 | 7029082, 13 | -607255, 14 | 7414791 15 | ] 16 | } -------------------------------------------------------------------------------- /src/assets/img/fa-bars.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/img/fa-search-minus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/img/fa-search-plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/img/fa-times.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VisualPerspective/ndvi-viewer/aad996d893f21ca00c3012bc0515af1e5b3fab54/src/assets/img/favicon.png -------------------------------------------------------------------------------- /src/assets/img/iceland_ndvi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VisualPerspective/ndvi-viewer/aad996d893f21ca00c3012bc0515af1e5b3fab54/src/assets/img/iceland_ndvi.png -------------------------------------------------------------------------------- /src/assets/img/ndvi-high.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/img/ndvi-low.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/img/ndvi-medium.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/img/select-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/img/vegetation.svg: -------------------------------------------------------------------------------- 1 | Asset 1 -------------------------------------------------------------------------------- /src/assets/img/vplogo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/js/components/App.tsx: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata' 2 | import * as React from 'react' 3 | import Container from '@app/components/Container' 4 | import RootStore from '@app/models/RootStore' 5 | import GLTest from '@app/gl/GLTest' 6 | import { Provider } from 'mobx-react' 7 | 8 | const rootStore: RootStore = new RootStore() 9 | 10 | // Check for needed JS and GL features 11 | try { 12 | // tslint:disable-next-line 13 | new GLTest() 14 | 15 | if (Array.prototype.find === undefined) { 16 | rootStore.compatible = false 17 | } 18 | } catch (e) { 19 | rootStore.compatible = false 20 | } 21 | 22 | if (rootStore.compatible) { 23 | rootStore.initialize() 24 | } 25 | 26 | const App = () => ( 27 | 28 | 29 | 30 | ) 31 | 32 | export default App 33 | -------------------------------------------------------------------------------- /src/js/components/Container.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Helmet } from 'react-helmet' 3 | import { inject, observer } from 'mobx-react' 4 | import { strings } from '@app/constants' 5 | import InfoOverlay from '@app/components/InfoOverlay' 6 | import Header from '@app/components/Header' 7 | import Footer from '@app/components/Footer' 8 | import SingleViewContainer from '@app/components/SingleViewContainer' 9 | import Loading from '@app/components/Loading' 10 | import Incompatible from '@app/components/Incompatible' 11 | import RootStore from '@app/models/RootStore' 12 | 13 | const Container: React.SFC<{ rootStore?: RootStore }> = (props) => ( 14 | <> 15 | 16 | 17 | 18 | 19 | 20 | 27 | 28 | { 29 | props.rootStore.compatible ? ( 30 | props.rootStore.initialized ? ( 31 | <> 32 |

33 | 34 |