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