This commit is contained in:
Jay 2024-10-29 03:00:35 -07:00
commit 79bf8d3f6b
30 changed files with 115688 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
.venv/
node_modules/

0
.prettierignore Normal file
View file

11
.prettierrc Normal file
View 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
View file

@ -0,0 +1,3 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode"
}

11
app.py Normal file
View 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
View file

@ -0,0 +1,9 @@
{
"CORSRules": [
{
"AllowedOrigins": ["https://www.geolens.app"],
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "HEAD"]
}
]
}

6361
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

34
package.json Normal file
View 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
View 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
View 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
View 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
View file

1
src/ts/Main.ts Normal file
View file

@ -0,0 +1 @@
import "./Map";

38
src/ts/Map.ts Normal file
View 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);

View 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;
});
});
};

View 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
View 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
View 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

File diff suppressed because one or more lines are too long

2052
static/js/bundle.js Normal file

File diff suppressed because one or more lines are too long

View 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

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

63
tailwind.config.js Normal file
View 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
View file

0
templates/_header.html Normal file
View file

5
templates/index.html Normal file
View file

@ -0,0 +1,5 @@
{% extends 'layout.html' %} {% block content %}
<div id="map"></div>
{% endblock content %}

33
templates/layout.html Normal file
View 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
View 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
View 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",
},
};