├── .babelrc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── package.json ├── preview.gif ├── renovate.json ├── src ├── js │ ├── index.js │ └── vendor │ │ └── Detector.js ├── sass │ ├── _colors.sass │ ├── _geometry.sass │ ├── _typography.sass │ └── home.sass ├── templates │ └── index.html └── textures │ ├── agri-medium-autumn.jpg │ ├── agri-medium-dem.tif │ ├── agri-small-autumn.jpg │ ├── agri-small-dem.tif │ └── agri-small-winter.jpg ├── webpack.common.js ├── webpack.dev.js ├── webpack.prod.js ├── wipe-dependencies.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"], 3 | "plugins": [] 4 | } -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | *.d.ts 4 | *.config.js 5 | .json 6 | src/js/vendor -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@babel/eslint-parser", 4 | "parserOptions": { 5 | "ecmaVersion": 2020 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:prettier/recommended" 10 | ], 11 | "plugins": [ 12 | "prettier" 13 | ], 14 | "rules": { 15 | "prettier/prettier": [ 16 | "error", 17 | { 18 | "usePrettierrc": true 19 | } 20 | ] 21 | }, 22 | "env": { 23 | "browser": true, 24 | "node": true, 25 | "es6": true 26 | }, 27 | "globals": { 28 | "GeoTIFF": "readonly" 29 | } 30 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | .DS_Store 3 | 4 | # editor 5 | .vscode/ 6 | 7 | node_modules/ 8 | 9 | 10 | # webpack bundles 11 | dist/ 12 | build/ 13 | 14 | # unignore the favicon 15 | !/build/favicon.ico 16 | 17 | 18 | # logs 19 | *.log 20 | 21 | # linters 22 | .eslintrc.json 23 | 24 | # aux files 25 | src/textures/**.xml 26 | stats.json 27 | 28 | 29 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v16.14.2 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | package-lock.json 3 | node_modules 4 | dist 5 | build 6 | 7 | # Ignore all HTML files: 8 | *.json 9 | webpack.* 10 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "semi": true, 5 | "singleQuote": true, 6 | "printWidth": 100 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 jackaljack 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 | # threejs-dem-visualizer 2 | 3 | ![A GIF file showing a preview of the starter project](https://github.com/orabazu/threejs-dem-visualizer/blob/master/preview.gif "Mouth Ağrı, modeled from USGS's digital elevation model and satellite image data") 4 | 5 | ## Detailed explanation on Medium: 6 | 7 | https://medium.com/@zubazor/visualizing-a-mountain-using-three-js-landsat-and-srtm-26275c920e34 8 | 9 | ### Features : 10 | 11 | - DEM(Digital Elevation Model) and satellite image data is downloaded from [USGS](https://earthexplorer.usgs.gov/) 12 | - ASTER and LANDSAT satellite's data is used. 13 | - Geotiff files generated using QGIS [QGIS](https://qgis.org/tr/site/) 14 | 15 | ### Installation 16 | 17 | ``` 18 | git clone git@github.com:orabazu/threejs-dem-visualizer.git 19 | cd threejs-dem-visualizer 20 | 21 | yarn install 22 | ``` 23 | 24 | ### Usage 25 | 26 | Generate all js/css bundles 27 | 28 | ``` 29 | yarn build 30 | ``` 31 | 32 | Developement 33 | 34 | ``` 35 | yarn start 36 | ``` 37 | 38 | Go to `localhost:8080` to see your project live! 39 | 40 | ### Credits 41 | 42 | Skeleton of the code is built on https://github.com/jackdbd/threejs-es6-webpack-starter project. I added some css for loading since reading geotiff files take time. If you have problems on build better to follow original instructions. 43 | 44 | Demo of [jonathanlurie](https://github.com/jonathanlurie/ThreejsDEM) and [mapmasters] (http://blog.mastermaps.com/2013/10/terrain-building-with-threejs.html) were realy helpfull to understand the logic of DEM to three.js convertion 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "threejs-es6-webpack-starter-landsat", 3 | "version": "0.1.1", 4 | "description": "A starter project for Three.js with support for ES6 and Webpack 5", 5 | "scripts": { 6 | "build": "webpack --config webpack.prod.js --progress --env production", 7 | "start": "webpack serve --mode development --config ./webpack.dev.js --open", 8 | "ci": "yarn run test && yarn run coverage", 9 | "deploy": "npx gh-pages -d build", 10 | "format": "prettier --write '{,!(node_modules)/}**/*.{js,jsx}'", 11 | "nuke": "rimraf node_modules && rm yarn.lock", 12 | "predeploy": "yarn build", 13 | "lint": "eslint ./src --ext .js ", 14 | "lint:fix": "eslint ./src --ext .js --fix", 15 | "analyze-bundle": "npx webpack --json > stats.json && yarn webpack-bundle-analyzer stats.json" 16 | }, 17 | "repository": "https://github.com/orabazu/threejs-dem-visualizer", 18 | "author": "orabazu ", 19 | "license": "MIT", 20 | "dependencies": { 21 | "three": "^0.145.0" 22 | }, 23 | "devDependencies": { 24 | "@babel/core": "^7.19.6", 25 | "@babel/eslint-parser": "^7.19.1", 26 | "@babel/preset-env": "^7.19.4", 27 | "clean-webpack-plugin": "^4.0.0", 28 | "compression-webpack-plugin": "^10.0.0", 29 | "css-loader": "^6.7.1", 30 | "esbuild-loader": "^2.20.0", 31 | "eslint": "^8.25.0", 32 | "eslint-config-prettier": "^8.5.0", 33 | "eslint-plugin-prettier": "^4.2.1", 34 | "eslint-webpack-plugin": "^3.2.0", 35 | "file-loader": "^6.2.0", 36 | "gh-pages": "^4.0.0", 37 | "html-webpack-plugin": "^5.5.0", 38 | "image-webpack-loader": "^8.1.0", 39 | "mini-css-extract-plugin": "^2.6.1", 40 | "node-sass": "^7.0.3", 41 | "prettier": "^2.3.2", 42 | "rimraf": "^3.0.2", 43 | "sass-loader": "^13.1.0", 44 | "style-loader": "^3.3.1", 45 | "webpack": "^5.46.0", 46 | "webpack-bundle-analyzer": "^4.6.1", 47 | "webpack-cli": "^4.10.0", 48 | "webpack-dev-server": "^4.11.1", 49 | "webpack-glsl-loader": "^1.0.1", 50 | "webpack-merge": "^5.8.0" 51 | }, 52 | "keywords": [ 53 | "threejs", 54 | "three.js", 55 | "boilerplate", 56 | "webpack", 57 | "es6", 58 | "babel", 59 | "sass" 60 | ], 61 | "bugs": { 62 | "url": "https://github.com/orabazu/threejs-dem-visualizer/issues" 63 | }, 64 | "homepage": "https://github.com/orabazu/threejs-dem-visualizer" 65 | } 66 | -------------------------------------------------------------------------------- /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orabazu/threejs-dem-visualizer/bfd9f90a9eb6d1399fa558a1baa552c357d58332/preview.gif -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/js/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | Scene, 3 | PerspectiveCamera, 4 | DirectionalLight, 5 | WebGLRenderer, 6 | PlaneGeometry, 7 | GridHelper, 8 | AxesHelper, 9 | TextureLoader, 10 | MeshLambertMaterial, 11 | DoubleSide, 12 | Mesh, 13 | } from 'three'; // import min because three.js is not tree-shakable for now 14 | import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; 15 | import * as Detector from '../js/vendor/Detector'; 16 | // TODO: Major performance problems on reading big images 17 | // import terrain from '../textures/agri-medium-dem.tif'; 18 | // import mountainImage from '../textures/agri-medium-autumn.jpg'; 19 | 20 | import terrain from '../textures/agri-small-dem.tif'; 21 | import mountainImage from '../textures/agri-small-autumn.jpg'; 22 | 23 | require('../sass/home.sass'); 24 | 25 | class Application { 26 | constructor(opts = {}) { 27 | this.width = window.innerWidth; 28 | this.height = window.innerHeight; 29 | 30 | if (opts.container) { 31 | this.container = opts.container; 32 | } else { 33 | const div = Application.createContainer(); 34 | document.body.appendChild(div); 35 | this.container = div; 36 | } 37 | 38 | if (Detector.webgl) { 39 | this.init(); 40 | this.render(); 41 | } else { 42 | // TODO: style warning message 43 | console.log('WebGL NOT supported in your browser!'); 44 | const warning = Detector.getWebGLErrorMessage(); 45 | this.container.appendChild(warning); 46 | } 47 | } 48 | 49 | init() { 50 | this.scene = new Scene(); 51 | this.setupRenderer(); 52 | this.setupCamera(); 53 | this.setupControls(); 54 | this.setupLight(); 55 | this.setupTerrainModel(); 56 | this.setupHelpers(); 57 | 58 | window.addEventListener('resize', () => { 59 | const w = window.innerWidth; 60 | const h = window.innerHeight; 61 | this.renderer.setSize(w, h); 62 | this.camera.aspect = w / h; 63 | this.camera.updateProjectionMatrix(); 64 | }); 65 | } 66 | 67 | render() { 68 | this.controls.update(); 69 | this.renderer.render(this.scene, this.camera); 70 | // when render is invoked via requestAnimationFrame(this.render) there is 71 | // no 'this', so either we bind it explicitly or use an es6 arrow function. 72 | // requestAnimationFrame(this.render.bind(this)); 73 | requestAnimationFrame(() => this.render()); 74 | } 75 | 76 | static createContainer() { 77 | const div = document.createElement('div'); 78 | div.setAttribute('id', 'canvas-container'); 79 | div.setAttribute('class', 'container'); 80 | return div; 81 | } 82 | 83 | setupRenderer() { 84 | this.renderer = new WebGLRenderer({ antialias: true }); 85 | this.renderer.setClearColor(0xd3d3d3); // it's a dark gray 86 | this.renderer.setPixelRatio(window.devicePixelRatio || 1); 87 | this.renderer.setSize(this.width, this.height); 88 | this.renderer.shadowMap.enabled = true; 89 | this.container.appendChild(this.renderer.domElement); 90 | } 91 | 92 | setupCamera() { 93 | const fov = 75; 94 | const aspect = this.width / this.height; 95 | const near = 0.1; 96 | const far = 10000; 97 | this.camera = new PerspectiveCamera(fov, aspect, near, far); 98 | this.camera.position.set(1000, 1000, 1000); 99 | this.camera.lookAt(this.scene.position); 100 | } 101 | 102 | setupControls() { 103 | this.controls = new OrbitControls(this.camera, this.renderer.domElement); 104 | this.controls.enabled = true; 105 | this.controls.maxDistance = 1500; 106 | this.controls.minDistance = 0; 107 | this.controls.autoRotate = true; 108 | } 109 | 110 | setupLight() { 111 | this.light = new DirectionalLight(0xffffff); 112 | this.light.position.set(500, 1000, 250); 113 | this.scene.add(this.light); 114 | // this.scene.add(new AmbientLight(0xeeeeee)); 115 | } 116 | 117 | setupTerrainModel() { 118 | const readGeoTif = async () => { 119 | const rawTiff = await GeoTIFF.fromUrl(terrain); 120 | const tifImage = await rawTiff.getImage(); 121 | const image = { 122 | width: tifImage.getWidth(), 123 | height: tifImage.getHeight(), 124 | }; 125 | 126 | /* 127 | The third and fourth parameter are image segments and we are subtracting one from each, 128 | otherwise our 3D model goes crazy. 129 | https://github.com/mrdoob/three.js/blob/master/src/geometries/PlaneGeometry.js#L57 130 | */ 131 | const geometry = new PlaneGeometry( 132 | image.width, 133 | image.height, 134 | image.width - 1, 135 | image.height - 1 136 | ); 137 | const data = await tifImage.readRasters({ interleave: true }); 138 | console.time('parseGeom'); 139 | const arr1 = new Array(geometry.attributes.position.count); 140 | const arr = arr1.fill(1); 141 | arr.forEach((a, index) => { 142 | geometry.attributes.position.setZ(index, (data[index] / 10) * -1); 143 | }); 144 | console.timeEnd('parseGeom'); 145 | 146 | const texture = new TextureLoader().load(mountainImage); 147 | const material = new MeshLambertMaterial({ 148 | wireframe: false, 149 | side: DoubleSide, 150 | map: texture, 151 | }); 152 | 153 | const mountain = new Mesh(geometry, material); 154 | mountain.position.y = 0; 155 | mountain.rotation.x = Math.PI / 2; 156 | 157 | this.scene.add(mountain); 158 | 159 | const loader = document.getElementById('loader'); 160 | loader.style.opacity = '-1'; 161 | 162 | // After a proper animation on opacity, hide element to make canvas clickable again 163 | setTimeout(() => { 164 | loader.style.display = 'none'; 165 | }, 1500); 166 | }; 167 | 168 | readGeoTif(); 169 | } 170 | 171 | setupHelpers() { 172 | const gridHelper = new GridHelper(1000, 40); 173 | this.scene.add(gridHelper); 174 | console.log('The X axis is red. The Y axis is green. The Z axis is blue.'); 175 | const axesHelper = new AxesHelper(500); 176 | this.scene.add(axesHelper); 177 | } 178 | } 179 | 180 | (() => { 181 | const app = new Application({ 182 | container: document.getElementById('canvas-container'), 183 | }); 184 | console.log(app); 185 | })(); 186 | -------------------------------------------------------------------------------- /src/js/vendor/Detector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author alteredq / http://alteredqualia.com/ 3 | * @author mr.doob / http://mrdoob.com/ 4 | */ 5 | 6 | var Detector = { 7 | canvas: !!window.CanvasRenderingContext2D, 8 | webgl: (function() { 9 | try { 10 | var canvas = document.createElement("canvas"); 11 | return !!( 12 | window.WebGLRenderingContext && 13 | (canvas.getContext("webgl") || canvas.getContext("experimental-webgl")) 14 | ); 15 | } catch (e) { 16 | return false; 17 | } 18 | })(), 19 | workers: !!window.Worker, 20 | fileapi: window.File && window.FileReader && window.FileList && window.Blob, 21 | 22 | getWebGLErrorMessage: function() { 23 | var element = document.createElement("div"); 24 | element.id = "webgl-error-message"; 25 | element.style.fontFamily = "monospace"; 26 | element.style.fontSize = "13px"; 27 | element.style.fontWeight = "normal"; 28 | element.style.textAlign = "center"; 29 | element.style.background = "#fff"; 30 | element.style.color = "#000"; 31 | element.style.padding = "1.5em"; 32 | element.style.width = "400px"; 33 | element.style.margin = "5em auto 0"; 34 | 35 | if (!this.webgl) { 36 | element.innerHTML = window.WebGLRenderingContext 37 | ? [ 38 | 'Your graphics card does not seem to support WebGL.
', 39 | 'Find out how to get it here.' 40 | ].join("\n") 41 | : [ 42 | 'Your browser does not seem to support WebGL.
', 43 | 'Find out how to get it here.' 44 | ].join("\n"); 45 | } 46 | 47 | return element; 48 | }, 49 | 50 | addGetWebGLMessage: function(parameters) { 51 | var parent, id, element; 52 | 53 | parameters = parameters || {}; 54 | 55 | parent = 56 | parameters.parent !== undefined ? parameters.parent : document.body; 57 | id = parameters.id !== undefined ? parameters.id : "oldie"; 58 | 59 | element = Detector.getWebGLErrorMessage(); 60 | element.id = id; 61 | 62 | parent.appendChild(element); 63 | } 64 | }; 65 | 66 | // browserify support 67 | if (typeof module === "object") { 68 | module.exports = Detector; 69 | } 70 | -------------------------------------------------------------------------------- /src/sass/_colors.sass: -------------------------------------------------------------------------------- 1 | $description-color: #f00 2 | $header-color: #0f0 3 | -------------------------------------------------------------------------------- /src/sass/_geometry.sass: -------------------------------------------------------------------------------- 1 | $footer-height: 50px 2 | -------------------------------------------------------------------------------- /src/sass/_typography.sass: -------------------------------------------------------------------------------- 1 | // 1rem in most browsers is 16px, so 1.125rem is 18px 2 | $description-font-size: 1.125rem 3 | $header-font-size: 3rem 4 | $header-font-family: 'Raleway', 'Open Sans', sans-serif 5 | -------------------------------------------------------------------------------- /src/sass/home.sass: -------------------------------------------------------------------------------- 1 | @import 'colors' 2 | @import 'geometry' 3 | @import 'typography' 4 | 5 | 6 | body 7 | margin: 0 8 | 9 | h1 10 | color: $header-color 11 | font-family: $header-font-family 12 | font-size: $header-font-size 13 | 14 | .container 15 | height: 80% 16 | width: 80% 17 | 18 | canvas 19 | display: block 20 | 21 | #loader 22 | position: absolute 23 | width: 100vw 24 | height: 100vh 25 | background: linear-gradient(to right, #F44336, #3F51B5); 26 | opacity: 0.7; 27 | transition: all 1.5s; 28 | 29 | 30 | h1 31 | font-family: monospace 32 | text-align: center 33 | -------------------------------------------------------------------------------- /src/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Three.js DEM and Satellite Visualization 7 | 8 | 9 | 10 | 11 | 12 |
13 |

Generating 3D Model ...

14 |
15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/textures/agri-medium-autumn.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orabazu/threejs-dem-visualizer/bfd9f90a9eb6d1399fa558a1baa552c357d58332/src/textures/agri-medium-autumn.jpg -------------------------------------------------------------------------------- /src/textures/agri-medium-dem.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orabazu/threejs-dem-visualizer/bfd9f90a9eb6d1399fa558a1baa552c357d58332/src/textures/agri-medium-dem.tif -------------------------------------------------------------------------------- /src/textures/agri-small-autumn.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orabazu/threejs-dem-visualizer/bfd9f90a9eb6d1399fa558a1baa552c357d58332/src/textures/agri-small-autumn.jpg -------------------------------------------------------------------------------- /src/textures/agri-small-dem.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orabazu/threejs-dem-visualizer/bfd9f90a9eb6d1399fa558a1baa552c357d58332/src/textures/agri-small-dem.tif -------------------------------------------------------------------------------- /src/textures/agri-small-winter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orabazu/threejs-dem-visualizer/bfd9f90a9eb6d1399fa558a1baa552c357d58332/src/textures/agri-small-winter.jpg -------------------------------------------------------------------------------- /webpack.common.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 3 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 4 | const { CleanWebpackPlugin } = require("clean-webpack-plugin"); 5 | // const BundleAnalyzerPlugin = require("webpack-bundle-analyzer") 6 | 7 | module.exports = { 8 | target: "web", 9 | context: __dirname, 10 | entry: { 11 | home: ["/src/js/index.js", "/src/sass/home.sass"], 12 | }, 13 | output: { 14 | path: path.resolve(__dirname, "build"), 15 | filename: "[chunkhash].js", 16 | chunkFilename: "[id].bundle.js" 17 | }, 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.(js|jsx)$/, 22 | include: [path.resolve(__dirname, "js", "src")], 23 | exclude: [path.resolve(__dirname, "node_modules")], 24 | use: { 25 | loader: "esbuild-loader", 26 | } 27 | }, 28 | { 29 | test: /\.(css|sass|scss)$/, 30 | use: [ 31 | MiniCssExtractPlugin.loader, 32 | { 33 | loader: "css-loader", 34 | options: { 35 | // modules: true, 36 | // importLoaders allows to configure how many loaders before css-loader should be applied to @imported resources. 37 | // 0 => no loaders (default); 1 => postcss-loader; 2 => postcss-loader, sass-loader 38 | importLoaders: 2, 39 | } 40 | }, 41 | { 42 | loader: "sass-loader", 43 | } 44 | ] 45 | }, 46 | // rule for textures (images) 47 | { 48 | test: /\.(jpe?g|png|tif)$/i, 49 | include: path.resolve(__dirname, "src", "textures"), 50 | use: [ 51 | "file-loader", 52 | { 53 | loader: "image-webpack-loader", 54 | options: { 55 | disable: true, // webpack@2.x and newer 56 | mozjpeg: { 57 | progressive: true, 58 | }, 59 | optipng: { 60 | optimizationLevel: 7, 61 | }, 62 | gifsicle: { 63 | interlaced: false, 64 | }, 65 | pngquant: { 66 | quality: [0.65, 0.90], 67 | speed: 4 68 | }, 69 | } 70 | } 71 | ] 72 | } 73 | ] 74 | }, 75 | devServer: { 76 | host: "localhost", 77 | port: 8080, 78 | open: true, 79 | hot: true, 80 | historyApiFallback: true, 81 | client: { 82 | overlay: { 83 | errors: true, 84 | warnings: false 85 | } 86 | } 87 | }, 88 | 89 | plugins: [ 90 | // new BundleAnalyzerPlugin(), 91 | new CleanWebpackPlugin({ 92 | root: __dirname, 93 | exclude: ["favicon.ico"], 94 | verbose: true 95 | }), 96 | new MiniCssExtractPlugin({ 97 | filename: '[name].[hash].css', 98 | chunkFilename: '[id].[hash].css', 99 | }), 100 | new HtmlWebpackPlugin({ 101 | template: path.resolve(__dirname, "src", "templates", "index.html"), 102 | hash: true, 103 | filename: "index.html", 104 | chunks: ["commons", "home"] 105 | }), 106 | ], 107 | 108 | externals: { 109 | GeoTIFF: 'GeoTIFF', 110 | }, 111 | }; 112 | -------------------------------------------------------------------------------- /webpack.dev.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge'); 2 | const ESLintPlugin = require('eslint-webpack-plugin'); 3 | const common = require('./webpack.common.js'); 4 | 5 | module.exports = merge(common, { 6 | mode: 'development', 7 | devtool: 'eval-cheap-module-source-map', // TODO add multiple webpack conf 8 | performance: { 9 | hints: "warning" 10 | }, 11 | plugins: [new ESLintPlugin()], 12 | }); 13 | 14 | 15 | -------------------------------------------------------------------------------- /webpack.prod.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | 4 | module.exports = merge(common, { 5 | mode: 'production', 6 | }); -------------------------------------------------------------------------------- /wipe-dependencies.js: -------------------------------------------------------------------------------- 1 | // https://medium.com/@_jh3y/how-to-update-all-npm-packages-in-your-project-at-once-17a8981860ea 2 | var fs = require("fs"), 3 | wipeDependencies = function() { 4 | var file = fs.readFileSync("package.json"), 5 | content = JSON.parse(file); 6 | for (var devDep in content.devDependencies) { 7 | content.devDependencies[devDep] = "*"; 8 | } 9 | for (var dep in content.dependencies) { 10 | content.dependencies[dep] = "*"; 11 | } 12 | fs.writeFileSync("package.json", JSON.stringify(content)); 13 | }; 14 | if (require.main === module) { 15 | wipeDependencies(); 16 | } else { 17 | module.exports = wipeDependencies; 18 | } 19 | --------------------------------------------------------------------------------