Initial Commit
This commit is contained in:
commit
b499bd0890
9 changed files with 347 additions and 0 deletions
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
8
.idea/CheitMaps.iml
generated
Normal file
8
.idea/CheitMaps.iml
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
|
@ -0,0 +1,6 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.11" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11" project-jdk-type="Python SDK" />
|
||||
</project>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/CheitMaps.iml" filepath="$PROJECT_DIR$/.idea/CheitMaps.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
198
main.py
Normal file
198
main.py
Normal file
|
@ -0,0 +1,198 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# backend/main.py
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
import uvicorn
|
||||
from pathlib import Path
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# Mount static folder to serve images, PDFs, JS if needed
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
# Set up template rendering
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
# Sample map data
|
||||
|
||||
MapsFolder = Path('./static/maps')
|
||||
|
||||
maps = [
|
||||
{
|
||||
"id": 2,
|
||||
"name": "DaisyRevedins",
|
||||
"latitude": 44.480829598869185,
|
||||
"longitude": 11.352026314262476,
|
||||
'stem': 'San Mamolo-JoNotte',
|
||||
"description": "From the grass to the church while visiting the Hospital"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "CastelWeak",
|
||||
"latitude": 44.49959787027563,
|
||||
"longitude": 11.27473782212433,
|
||||
'stem': 'Casteldebole_2024_05_15',
|
||||
"description": "Totally Flat"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "Golinelli",
|
||||
"latitude": 44.50847170695366,
|
||||
"longitude": 11.308195638402232,
|
||||
"stem": "Norwegian Sprint",
|
||||
"description": "Jo Noche"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "SwordVille",
|
||||
"latitude": 44.49119622583157,
|
||||
"longitude": 11.308122673834362,
|
||||
"stem": "A3-Pellegrino",
|
||||
"description": "San Pellegrino night"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Small Heaven",
|
||||
"latitude": 44.375066657656234,
|
||||
"longitude": 11.247854195489277,
|
||||
"stem": "Piccolo Paradiso_small",
|
||||
"description": "Fucked Dogs & Owners"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": "Marzab8",
|
||||
"latitude": 44.340876557519195,
|
||||
"longitude": 11.208385117204514,
|
||||
"stem": "Marzabotto-O-Training",
|
||||
"description": "Marzabotto Greto Fiume"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "Bazzano",
|
||||
"latitude": 44.506688577284855,
|
||||
"longitude": 11.084856756615434,
|
||||
'stem': 'Pro Mel',
|
||||
"description": "Bazzano Pro Mel"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"name": "Battindarno",
|
||||
'coord': (44.495505920730935, 11.288849376526782),
|
||||
'stem': 'JoNotte.2022.1',
|
||||
"description": "Battindarno 2022-1"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"name": "Daisy Gardens",
|
||||
"latitude": 44.480829598869185,
|
||||
"longitude": 11.3525,
|
||||
'stem': 'Revedinmargherita',
|
||||
"description": "Giardini e Revedin"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"name": "Pontecchio",
|
||||
"coord": (44.431200464913914, 11.269902531675038),
|
||||
'stem': 'Pontecchio_night bo 2020',
|
||||
"description": "Occhio al custode di villa Grifone"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"name": "Sasso Marconi",
|
||||
"coord": (44.398978299870166, 11.25642430653767),
|
||||
'stem': 'night_bo_13_02_20',
|
||||
"description": "Cà de Testi e Sasso"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"name": "Croce e Funivia",
|
||||
"coord": (44.48620778951313, 11.283029683468458),
|
||||
'stem': '1802',
|
||||
"description": "Da Casalatch alla Funivia"
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"name": "CastelWeakRiverBank",
|
||||
"coord": (44.50400435370321, 11.283098136577783),
|
||||
'stem': '11 febbraio',
|
||||
"description": "Casteldebole e lungo fiume"
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"name": "ZanardiPark",
|
||||
"coord": (44.48890188652948, 11.28842972160338),
|
||||
'stem': 'ParcoZanardi',
|
||||
"description": "Parco Zanardi, Casalatch"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"name": "The Monster",
|
||||
"coord": (44.47603075768931, 11.262382357503611),
|
||||
'stem': 'EremoELITE',
|
||||
"description": "Eremo di Tizzano / Mostro"
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"name": "Villa Angeletti",
|
||||
"coord": (44.510693796692124, 11.333109723632031),
|
||||
'stem': 'Night BO Villa Angeletti-Night 1',
|
||||
"description": "Villa Angeletti 2020"
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"name": "Navile",
|
||||
"coord": (44.536246247660856, 11.354504683188482),
|
||||
'stem': 'NightBO_Navile_20022020',
|
||||
"description": "Navile, Nightbo 2020"
|
||||
},
|
||||
|
||||
]
|
||||
|
||||
for m in maps:
|
||||
if 'stem' in m:
|
||||
base = m['stem']
|
||||
m['pdf'] = f'/static/maps/{base}.pdf'
|
||||
for suffix in ('.jpg', '.png'):
|
||||
fn = MapsFolder / f'{base}_small{suffix}'
|
||||
if fn.is_file():
|
||||
m['thumbnail'] = f'/static/maps/{fn.name}'
|
||||
fn = MapsFolder / f'{base}_big{suffix}'
|
||||
if fn.is_file():
|
||||
m['full_size'] = f'/static/maps/{fn.name}'
|
||||
|
||||
if 'coord' in m:
|
||||
lat, long = m.get('coord')
|
||||
m['latitude'] = lat
|
||||
m['longitude'] = long
|
||||
|
||||
|
||||
# Serve the main map page
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
async def index(request: Request):
|
||||
return templates.TemplateResponse("index.html", {"request": request})
|
||||
|
||||
|
||||
# JSON endpoint to get map data
|
||||
@app.get("/api/maps")
|
||||
async def get_maps():
|
||||
return maps
|
||||
|
||||
|
||||
# Detailed map page
|
||||
@app.get("/map/{map_id}", response_class=HTMLResponse)
|
||||
async def map_detail(request: Request, map_id: int):
|
||||
map_data = next((m for m in maps if m["id"] == map_id), None)
|
||||
if not map_data:
|
||||
return HTMLResponse(content="Map not found", status_code=404)
|
||||
return templates.TemplateResponse("map_detail.html", {"request": request, "map": map_data})
|
||||
|
||||
|
||||
def main():
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
uvicorn.run(app, host="127.0.0.1", port=8000)
|
82
templates/index.html
Normal file
82
templates/index.html
Normal file
|
@ -0,0 +1,82 @@
|
|||
<!-- backend/templates/index.html -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Orienteering Map Viewer</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
|
||||
<style>
|
||||
#map { height: 100vh; margin: 0; }
|
||||
.popup-img { width: 250px; cursor: pointer; }
|
||||
body, html {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#map {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
#title-overlay {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
padding: 10px 20px;
|
||||
border-radius: 12px;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.2);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="map"></div>
|
||||
<div id="title-overlay">Super Cheit Orienteering Maps<img src="/static/face.jog" style="width: 50px;" />
|
||||
</div>
|
||||
<div id="map"></div>
|
||||
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
|
||||
<script>
|
||||
const map = L.map('map'); // Initialize without setting center and zoom yet
|
||||
const flag = L.icon({ iconUrl: '/static/O_Flag_.svg', iconSize: [64, 80] , iconAnchor: [32, 79]})
|
||||
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© OpenStreetMap contributors'
|
||||
}).addTo(map);
|
||||
|
||||
fetch("/api/maps")
|
||||
.then(res => res.json())
|
||||
.then(maps => {
|
||||
const bounds = [];
|
||||
|
||||
maps.forEach(m => {
|
||||
const latlng = [m.latitude, m.longitude];
|
||||
bounds.push(latlng);
|
||||
|
||||
const marker = L.marker(latlng, {icon: flag}).addTo(map);
|
||||
const popup = `
|
||||
<strong>${m.name}</strong><br>
|
||||
${m.description}<br>
|
||||
<img class="popup-img" src="${m.thumbnail}" onclick="window.location='/map/${m.id}'" />
|
||||
`;
|
||||
marker.bindPopup(popup);
|
||||
});
|
||||
|
||||
if (bounds.length > 0) {
|
||||
map.fitBounds(bounds, { padding: [50, 50] });
|
||||
} else {
|
||||
// Fallback if no maps
|
||||
map.setView([45, 9], 5); // Default view
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
29
templates/map_detail.html
Normal file
29
templates/map_detail.html
Normal file
|
@ -0,0 +1,29 @@
|
|||
<!-- backend/templates/map_detail.html -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ map.name }}</title>
|
||||
<style>
|
||||
body, html {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ map.name }}</h1>
|
||||
<p>{{ map.description }}</p>
|
||||
<br>
|
||||
<a href="{{ map.pdf }}" download>Download PDF</a>
|
||||
<br>
|
||||
<br>
|
||||
<a href="https://www.google.com/maps?q={{ map.latitude }},{{ map.longitude }} "target="_blank">Parking Lot coordinates</a>
|
||||
<br><br>
|
||||
<a href="/">← Back to Map</a>
|
||||
<br>
|
||||
<br>
|
||||
<img src="{{ map.full_size }}" style="width: 100vw;" />
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Reference in a new issue