Upload
This commit is contained in:
commit
79bf8d3f6b
30 changed files with 115688 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
.venv/
|
||||||
|
node_modules/
|
||||||
0
.prettierignore
Normal file
0
.prettierignore
Normal file
11
.prettierrc
Normal file
11
.prettierrc
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"prettier.ignorePath": ".prettierignore",
|
||||||
|
"prettier.singleQuote": true,
|
||||||
|
"prettier.semi": false,
|
||||||
|
"prettier.trailingComma": "none",
|
||||||
|
"prettier.printWidth": 80,
|
||||||
|
"prettier.proseWrap": "preserve"
|
||||||
|
}
|
||||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
}
|
||||||
11
app.py
Normal file
11
app.py
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
from flask import Flask, render_template, send_from_directory
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
@app.route('/index')
|
||||||
|
def index():
|
||||||
|
return render_template('index.html', title='Home')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(debug=True)
|
||||||
9
cors.json
Normal file
9
cors.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"CORSRules": [
|
||||||
|
{
|
||||||
|
"AllowedOrigins": ["https://www.geolens.app"],
|
||||||
|
"AllowedHeaders": ["*"],
|
||||||
|
"AllowedMethods": ["GET", "HEAD"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
6361
package-lock.json
generated
Normal file
6361
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
34
package.json
Normal file
34
package.json
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bootstrap": "*",
|
||||||
|
"@types/jquery": "*",
|
||||||
|
"@types/node": "*",
|
||||||
|
"concurrently": "^7.2.1",
|
||||||
|
"sass": "^1.71.1",
|
||||||
|
"tailwindcss": "^3.4.1",
|
||||||
|
"ts-loader": "^9.3.0",
|
||||||
|
"typescript": "^4.7.2",
|
||||||
|
"webpack": "^5.72.1",
|
||||||
|
"webpack-cli": "^4.9.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bootstrap": "^5.3.3",
|
||||||
|
"flowbite": "^2.3.0",
|
||||||
|
"jquery": "^3.7.1",
|
||||||
|
"maplibre-gl": "^4.7.1",
|
||||||
|
"pmtiles": "^3.2.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"develop": "concurrently 'npm:develop:*'",
|
||||||
|
"develop:server": ". .venv/bin/activate && python app.py",
|
||||||
|
"develop:sass": "npm run build:sass -- --watch",
|
||||||
|
"develop:twcss": "npm run build:twcss -- --watch",
|
||||||
|
"develop:ts": "webpack --mode development --watch",
|
||||||
|
"build": "concurrently 'npm:build:*'",
|
||||||
|
"build:server": "",
|
||||||
|
"build:sass": "sass --style compressed src/scss/app.scss:static/css/app.css",
|
||||||
|
"build:twcss": "npx tailwindcss -i ./src/css/tailwind.css -o ./static/css/tailwind.css --minify",
|
||||||
|
"build:ts": "webpack --mode production",
|
||||||
|
"test": "echo 'test not implemented'"
|
||||||
|
}
|
||||||
|
}
|
||||||
9
requirements.txt
Normal file
9
requirements.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
blinker==1.8.2
|
||||||
|
click==8.1.7
|
||||||
|
Flask==3.0.3
|
||||||
|
importlib_metadata==8.5.0
|
||||||
|
itsdangerous==2.2.0
|
||||||
|
Jinja2==3.1.4
|
||||||
|
MarkupSafe==3.0.2
|
||||||
|
Werkzeug==3.0.6
|
||||||
|
zipp==3.20.2
|
||||||
4
src/css/tailwind.css
Normal file
4
src/css/tailwind.css
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
/** npx tailwindcss -i ./src/css/tailwind.css -o ./static/css/tailwind.css --minify **/
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
10
src/scss/app.scss
Normal file
10
src/scss/app.scss
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
@import "../css/tailwind.css";
|
||||||
|
@import "index";
|
||||||
|
|
||||||
|
#map {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
0
src/scss/index.scss
Normal file
0
src/scss/index.scss
Normal file
1
src/ts/Main.ts
Normal file
1
src/ts/Main.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
import "./Map";
|
||||||
38
src/ts/Map.ts
Normal file
38
src/ts/Map.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import * as pmtiles from "pmtiles";
|
||||||
|
import * as maplibregl from "maplibre-gl";
|
||||||
|
import { layerSpecification } from "./modules/MapLayerSpecificationModule";
|
||||||
|
import { addCountryBorder } from "./modules/AddCountryBorderModule";
|
||||||
|
|
||||||
|
// read https://maplibre.org/maplibre-gl-js/docs/examples/pmtiles/
|
||||||
|
// add pmtiles protocol
|
||||||
|
let protocol = new pmtiles.Protocol();
|
||||||
|
maplibregl.addProtocol("pmtiles", protocol.tile);
|
||||||
|
|
||||||
|
// create map tiles
|
||||||
|
const map = new maplibregl.Map({
|
||||||
|
container: "map",
|
||||||
|
zoom: 1,
|
||||||
|
minZoom: 2,
|
||||||
|
maxZoom: 6,
|
||||||
|
center: [0, 0],
|
||||||
|
style: {
|
||||||
|
version: 8,
|
||||||
|
sources: {
|
||||||
|
tiles: {
|
||||||
|
type: "vector",
|
||||||
|
// read https://maplibre.org/maplibre-style-spec/sources/#vector
|
||||||
|
url: "pmtiles://https://nbg1.your-objectstorage.com/maps/map.pmtiles",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
//
|
||||||
|
layers: layerSpecification,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// debug
|
||||||
|
//map.showTileBoundaries = true;
|
||||||
|
|
||||||
|
// add zoom and rotation controls
|
||||||
|
map.addControl(new maplibregl.NavigationControl());
|
||||||
|
|
||||||
|
addCountryBorder(map);
|
||||||
79
src/ts/modules/AddCountryBorderModule.ts
Normal file
79
src/ts/modules/AddCountryBorderModule.ts
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
let hoveredStateId = null;
|
||||||
|
|
||||||
|
export const addCountryBorder = (map: maplibregl.Map) => {
|
||||||
|
map.on("load", () => {
|
||||||
|
map.addSource("states", {
|
||||||
|
type: "geojson",
|
||||||
|
data: "/static/src/earth.geojson",
|
||||||
|
promoteId: "iso_n3", // use this field as the unique identifier for each country
|
||||||
|
});
|
||||||
|
|
||||||
|
// The feature-state dependent fill-opacity expression will render the hover effect
|
||||||
|
// when a feature's hover state is set to true.
|
||||||
|
map.addLayer({
|
||||||
|
id: "state-fills",
|
||||||
|
type: "fill",
|
||||||
|
source: "states",
|
||||||
|
layout: {},
|
||||||
|
paint: {
|
||||||
|
"fill-color": "#627BC1",
|
||||||
|
"fill-opacity": [
|
||||||
|
"case",
|
||||||
|
["boolean", ["feature-state", "hover"], false],
|
||||||
|
0.2,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
map.addLayer({
|
||||||
|
id: "state-borders",
|
||||||
|
type: "line",
|
||||||
|
source: "states",
|
||||||
|
layout: {
|
||||||
|
"line-join": "round",
|
||||||
|
"line-cap": "round",
|
||||||
|
},
|
||||||
|
paint: {
|
||||||
|
"line-color": "#4b4b4b", // Border color
|
||||||
|
"line-width": [
|
||||||
|
"case",
|
||||||
|
["boolean", ["feature-state", "hover"], false], // Check if hovered
|
||||||
|
1, // Thicker border on hover
|
||||||
|
0.5, // Regular border width
|
||||||
|
],
|
||||||
|
"line-opacity": 1, // Full opacity for borders
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// When the user moves their mouse over the state-fill layer, we'll update the
|
||||||
|
// feature state for the feature under the mouse.
|
||||||
|
map.on("mousemove", "state-fills", (e) => {
|
||||||
|
if (e.features.length > 0) {
|
||||||
|
if (hoveredStateId) {
|
||||||
|
map.setFeatureState(
|
||||||
|
{ source: "states", id: hoveredStateId },
|
||||||
|
{ hover: false }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
hoveredStateId = e.features[0].id;
|
||||||
|
map.setFeatureState(
|
||||||
|
{ source: "states", id: hoveredStateId },
|
||||||
|
{ hover: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// When the mouse leaves the state-fill layer, update the feature state of the
|
||||||
|
// previously hovered feature.
|
||||||
|
map.on("mouseleave", "state-fills", () => {
|
||||||
|
if (hoveredStateId) {
|
||||||
|
map.setFeatureState(
|
||||||
|
{ source: "states", id: hoveredStateId },
|
||||||
|
{ hover: false }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
hoveredStateId = null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
41
src/ts/modules/MapLayerSpecificationModule.ts
Normal file
41
src/ts/modules/MapLayerSpecificationModule.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { LayerSpecification } from "maplibre-gl";
|
||||||
|
|
||||||
|
export const layerSpecification: LayerSpecification[] = [
|
||||||
|
{
|
||||||
|
id: "forest",
|
||||||
|
source: "tiles",
|
||||||
|
"source-layer": "forest",
|
||||||
|
type: "fill",
|
||||||
|
paint: {
|
||||||
|
"fill-color": "#90ee90",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "water",
|
||||||
|
source: "tiles",
|
||||||
|
"source-layer": "water",
|
||||||
|
filter: ["==", ["geometry-type"], "Polygon"],
|
||||||
|
type: "fill",
|
||||||
|
paint: {
|
||||||
|
"fill-color": "#add8e6",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "buildings",
|
||||||
|
source: "tiles",
|
||||||
|
"source-layer": "buildings",
|
||||||
|
type: "fill",
|
||||||
|
paint: {
|
||||||
|
"fill-color": "#ffffc5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "roads",
|
||||||
|
source: "tiles",
|
||||||
|
"source-layer": "roads",
|
||||||
|
type: "line",
|
||||||
|
paint: {
|
||||||
|
"line-color": "#d3d3d3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
1
static/css/app.css
Normal file
1
static/css/app.css
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
@import"../css/tailwind.css";#map{position:absolute;top:0;left:0;width:100%;height:100%}/*# sourceMappingURL=app.css.map */
|
||||||
1
static/css/app.css.map
Normal file
1
static/css/app.css.map
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"sourceRoot":"","sources":["../../src/scss/app.scss"],"names":[],"mappings":"AAAQ,6BAGR,KACE,kBACA,MACA,OACA,WACA","file":"app.css"}
|
||||||
2
static/css/tailwind.css
Normal file
2
static/css/tailwind.css
Normal file
File diff suppressed because one or more lines are too long
2052
static/js/bundle.js
Normal file
2052
static/js/bundle.js
Normal file
File diff suppressed because one or more lines are too long
4
static/js/bundle.js.LICENSE.txt
Normal file
4
static/js/bundle.js.LICENSE.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
/**
|
||||||
|
* MapLibre GL JS
|
||||||
|
* @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v4.7.1/LICENSE.txt
|
||||||
|
*/
|
||||||
106877
static/src/earth.geojson
Normal file
106877
static/src/earth.geojson
Normal file
File diff suppressed because it is too large
Load diff
2
static/vendor/flowbite/flowbite.min.js
vendored
Normal file
2
static/vendor/flowbite/flowbite.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
63
tailwind.config.js
Normal file
63
tailwind.config.js
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: ["./templates/**/*.html", "./static/src/**/*.{js,ts,jsx,tsx}"],
|
||||||
|
darkMode: "class",
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
primary: {
|
||||||
|
50: "#eff6ff",
|
||||||
|
100: "#dbeafe",
|
||||||
|
200: "#bfdbfe",
|
||||||
|
300: "#93c5fd",
|
||||||
|
400: "#60a5fa",
|
||||||
|
500: "#3b82f6",
|
||||||
|
600: "#2563eb",
|
||||||
|
700: "#1d4ed8",
|
||||||
|
800: "#1e40af",
|
||||||
|
900: "#1e3a8a",
|
||||||
|
950: "#172554",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
body: [
|
||||||
|
"'Source Sans 3'",
|
||||||
|
"Inter",
|
||||||
|
"ui-sans-serif",
|
||||||
|
"system-ui",
|
||||||
|
"-apple-system",
|
||||||
|
"system-ui",
|
||||||
|
"Segoe UI",
|
||||||
|
"Roboto",
|
||||||
|
"Helvetica Neue",
|
||||||
|
"Arial",
|
||||||
|
"Noto Sans",
|
||||||
|
"sans-serif",
|
||||||
|
"Apple Color Emoji",
|
||||||
|
"Segoe UI Emoji",
|
||||||
|
"Segoe UI Symbol",
|
||||||
|
"Noto Color Emoji",
|
||||||
|
],
|
||||||
|
sans: [
|
||||||
|
"'Source Sans 3'",
|
||||||
|
"Inter",
|
||||||
|
"ui-sans-serif",
|
||||||
|
"system-ui",
|
||||||
|
"-apple-system",
|
||||||
|
"system-ui",
|
||||||
|
"Segoe UI",
|
||||||
|
"Roboto",
|
||||||
|
"Helvetica Neue",
|
||||||
|
"Arial",
|
||||||
|
"Noto Sans",
|
||||||
|
"sans-serif",
|
||||||
|
"Apple Color Emoji",
|
||||||
|
"Segoe UI Emoji",
|
||||||
|
"Segoe UI Symbol",
|
||||||
|
"Noto Color Emoji",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [require("flowbite/plugin")],
|
||||||
|
};
|
||||||
0
templates/_footer.html
Normal file
0
templates/_footer.html
Normal file
0
templates/_header.html
Normal file
0
templates/_header.html
Normal file
5
templates/index.html
Normal file
5
templates/index.html
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{% extends 'layout.html' %} {% block content %}
|
||||||
|
|
||||||
|
<div id="map"></div>
|
||||||
|
|
||||||
|
{% endblock content %}
|
||||||
33
templates/layout.html
Normal file
33
templates/layout.html
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
{% if title %}
|
||||||
|
<title>{{ title }} - Geolens</title>
|
||||||
|
{% else %}
|
||||||
|
<title>Geolens</title>
|
||||||
|
{% endif %}
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="{{ url_for('static', filename='css/app.css') }}"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://unpkg.com/maplibre-gl@4.7.0/dist/maplibre-gl.css"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
<body class="antialiased bg-white dark:bg-gray-900">
|
||||||
|
{% include '_header.html' %}
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div>{% block content %} {% endblock content %}</div>
|
||||||
|
<div>{% include '_footer.html' %}</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script src="{{ url_for('static', filename='vendor/flowbite/flowbite.min.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/bundle.js') }}"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
13
tsconfig.json
Normal file
13
tsconfig.json
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./static",
|
||||||
|
"removeComments": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"module": "es6",
|
||||||
|
"target": "es5",
|
||||||
|
"allowJs": true,
|
||||||
|
"moduleResolution": "node"
|
||||||
|
},
|
||||||
|
"exclude": ["test.ts", "node_modules"]
|
||||||
|
}
|
||||||
22
webpack.config.js
Normal file
22
webpack.config.js
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: "./src/ts/Main.ts",
|
||||||
|
devtool: "inline-source-map",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: "ts-loader",
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: [".js", ".ts", ".tsx"],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, "./"),
|
||||||
|
filename: "static/js/bundle.js",
|
||||||
|
},
|
||||||
|
};
|
||||||
Loading…
Reference in a new issue