1
0
Fork 0
mirror of https://github.com/Findus23/vienna-cycling-quality.git synced 2024-09-09 04:13:48 +02:00

many custom changes

This commit is contained in:
Lukas Winkler 2024-03-24 15:59:55 +01:00
parent fef94fc6f4
commit 95c6c93ca7
Signed by: lukas
GPG key ID: 54DE4D798D244853
17 changed files with 2654 additions and 968 deletions

3331
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,8 @@
"@headlessui/react": "^1.7.18",
"@nanostores/react": "^0.7.2",
"@nanostores/router": "^0.13.0",
"@turf/turf": "^6.5.0",
"@turf/turf": "^7.0.0-alpha.114",
"immutable": "^5.0.0-beta.5",
"maplibre-gl": "^4.1.1",
"pmtiles": "^3.0.5",
"react": "^18.2.0",
@ -22,14 +23,19 @@
"tailwind-merge": "^2.2.2"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.10",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^10.4.19",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"postcss": "^8.4.38",
"tailwindcss": "^3.4.1",
"typescript": "^5.2.2",
"vite": "^5.2.0"
}

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

2
publish.sh Executable file
View file

@ -0,0 +1,2 @@
#!/bin/bash
rsync -rvzP ./dist/ lukas@lw1.at:/var/www/vienna-cycling-quality/ --fuzzy --delete-after -v

View file

@ -1,23 +1,23 @@
import * as turf from '@turf/turf'
import type {LngLatBoundsLike} from 'react-map-gl/maplibre'
import {CqiMap} from "./page_map/CqiMap.tsx";
import "./base.css"
function App() {
const berlinInnenstadtBbox = [16.18278, 48.11833, 16.58, 48.32306] satisfies ReturnType<typeof turf.bbox>
const maxBounds = turf.bbox(
turf.buffer(turf.bboxPolygon(berlinInnenstadtBbox), 250, {
units: 'meters',
}),
) as LngLatBoundsLike
const viennabbox = [16.18278, 48.11833, 16.58, 48.32306] as LngLatBoundsLike
// const maxBounds = bbox(
// buffer(bboxPolygon(berlinInnenstadtBbox), 250, {
// units: 'meters',
// }),
// ) as LngLatBoundsLike
// console.log(maxBounds)
const minZoom = 10
return (
<>
<CqiMap maxBounds={maxBounds} minZoom={minZoom}/>
<CqiMap maxBounds={viennabbox} minZoom={minZoom}/>
<div className="absolute bottom-3 left-3 rounded bg-white/80 px-1 py-0.5 text-xs">
Datenstand: 02.03.2024
Datenstand: 24.03.2024
</div>
</>

View file

@ -3,7 +3,7 @@ import maplibregl from 'maplibre-gl'
import 'maplibre-gl/dist/maplibre-gl.css'
import * as pmtiles from 'pmtiles'
import { useEffect, useState } from 'react'
import { Map, type ViewStateChangeEvent } from 'react-map-gl/maplibre'
import {Layer, Map, Source, type ViewStateChangeEvent} from 'react-map-gl/maplibre'
import {
$clickedMapData,
$mapLoaded,
@ -14,6 +14,7 @@ import {
type SearchParamBaseMap,
} from './store'
import { roundPositionForURL } from './utils/roundNumber'
import * as Immutable from "immutable";
type Props = {
initialViewState: MapSearchParam
@ -56,6 +57,8 @@ export const BaseMap = ({ initialViewState, interactiveLayerIds, boxZoom, childr
const latLngZoom = paramMapParse(params.map)
const rasterAttribution='Data: © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>, <a href="http://viewfinderpanoramas.org/">SRTM</a>, <a href="https://portal.opentopography.org/datasetMetadata?otCollectionID=OT.032021.4326.2">NASADEM</a>, <a href="https://worldcover2021.esa.int">ESA WorldCover</a>; Maps © <a href="https://www.tracestrack.com/">Tracestrack</a>'
return (
<Map
initialViewState={{
@ -65,7 +68,7 @@ export const BaseMap = ({ initialViewState, interactiveLayerIds, boxZoom, childr
longitude: latLngZoom.longitude || initialViewState.longitude,
}}
// Style: https://cloud.maptiler.com/maps/dataviz/
mapStyle="https://api.maptiler.com/maps/dataviz/style.json?key=0opClOQz7xpg46NzNSOo"
// mapStyle="https://api.maptiler.com/maps/dataviz/style.json?key=0opClOQz7xpg46NzNSOo"
style={{ width: '100%', height: '100%' }}
boxZoom={boxZoom || true}
// hash
@ -80,6 +83,16 @@ export const BaseMap = ({ initialViewState, interactiveLayerIds, boxZoom, childr
onMouseLeave={() => setCursorStyle('grab')}
onClick={(event) => $clickedMapData.set(event.features)}
>
<Source
type="raster"
tiles={['https://maps.lw1.at/tiles/1.0.0/tracestack/webmercator_hq/{z}/{x}/{y}.png']}
tileSize={512}
attribution={rasterAttribution}
>
<Layer type="raster"></Layer>
</Source>
{children}
</Map>
)

57
src/base.css Normal file
View file

@ -0,0 +1,57 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
/* https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
https://lukechannings.com/blog/2021-06-09-does-safari-15-fix-the-vh-bug/ */
.computed-h-screen {
height: 100vh;
height: calc((var(--vh, 1vh) * 100) - env(safe-area-inset-bottom));
}
.img-thumbnail {
@apply p-0.5;
@apply border border-gray-200;
@apply rounded-sm;
}
.notice {
@apply mb-3 p-4;
@apply bg-gray-200;
@apply rounded;
@apply leading-normal;
}
/* Overwrite defaults, which we cannot to inline since markdow makes that hard. */
.prose .notice p {
@apply m-0;
}
.prose .notice > h2:first-child {
@apply mt-0;
}
.article-headline-spacing h1:not(:first-child) {
@apply mt-10;
}
.article-headline-spacing h1 {
@apply mb-3;
}
}
h1 {
font-size: 2rem;
line-height: 2.6666rem;
}
h1, p {
margin-bottom: 1rem;
}
#overlay a {
@apply text-emerald-600 hover:text-emerald-800 visited:text-emerald-600
}

View file

@ -1,68 +0,0 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

View file

@ -1,7 +1,6 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>

View file

@ -8,6 +8,7 @@ import { validAnzeigeValues, type SearchParamsCqiMap } from './storeCqi'
import { interactiveLayerIdsByGroup } from './layers/layers'
import { Overlay } from './Overlay'
import {BaseMap} from "../BaseMap/BaseMap.tsx";
import {MapInfo} from "./MapInfo.tsx";
type Props = {
maxBounds: MapSearchParam['maxBounds']
@ -40,6 +41,7 @@ export const CqiMap = ({ maxBounds, minZoom, maxZoom }: Props) => {
<MapSourceCqi />
<NavigationControl showCompass={false} position="top-right" />
<MapInspector />
<MapInfo />
<Overlay />
</BaseMap>
)

56
src/page_map/MapInfo.tsx Normal file
View file

@ -0,0 +1,56 @@
import {useState} from "react";
export const MapInfo = () => {
// const clickedMapData = useStore($clickedMapData)
//
// if (!clickedMapData || !clickedMapData.length) return null
const [displayed, setDisplayed] = useState(true);
return (
<div id="overlay" style={{
top: 0,
bottom: 0,
width: "100%",
position: "absolute",
display: displayed ? "flex" : "none",
justifyContent: "center",
// pointerEvents: "none",
alignItems: "center",
zIndex: 2000
}} onClick={() => setDisplayed(false)}>
<section
onClick={(e) => {
e.stopPropagation();
}}
className="text-base z-50 overflow-y-auto rounded-lg bg-white border border-emerald-200 p-4 shadow-xl sm:inset-x-auto sm:inset-y-10 sm:right-5 sm:w-1/2">
<h1 className="text-center">Cycling Quality Index<br/>Wien</h1>
<p>
Ein Radverkehrs-Qualitätsindex für Wien basierend auf
<a href="https://www.openstreetmap.org/" target="_blank"> OSM</a>-Daten.
</p>
<p>Der Code für die Analyse und Visualisierung stammt vom
<a href="https://www.osm-verkehrswende.org/cqi/" target="_blank"> www.osm-verkehrswende.org</a>-Team
und wurde von
mir nur
<a href="https://github.com/Findus23/vienna-cycling-quality" target="_blank"> für Wien adaptiert</a>.
Weitere Informationen zur Methodik findet man auch auf der
<a href="https://www.osm-verkehrswende.org/cqi/" target="_blank"> osm-verkehrswende-Webseite </a>
oder im <a
href="https://www.osm-verkehrswende.org/cqi/posts/2024-01-01-cqi-fossgis-2024/"> FOSSGIS-Vortrag </a>
von Alex Seidel.
</p>
<p>
Alle Ergebnisse sind nur so gut wie die Rohdaten und fehlende Attribute (z.B. über Fahrbahnbreiten)
können die Ergebnisse verfälschen.
<a href="https://www.osm-verkehrswende.org/cqi/improve-data/"> Hier </a>
gibt es Informationen, wie man zu den Daten beitragen kann.
</p>
<p>
Über mich: <a href="https://lw1.at">lw1.at</a>
</p>
<p>
Impressum: <a href="https://lw1.at/de/impressum/">lw1.at/de/impressum</a>
</p>
</section>
</div>
)
}

View file

@ -93,6 +93,10 @@ export const MapInspector = () => {
})}
</tbody>
</table>
<details>
<summary className="text-lg">Rohdaten</summary>
<pre>{JSON.stringify(feature.properties, null, 2)}</pre>
</details>
</div>
)
})}

View file

@ -5,27 +5,26 @@ import { Layer, Source } from 'react-map-gl/maplibre'
import { wrapFilterWithAll } from '../BaseMap/utils/wrapFilterWithAll'
import { layerByGroups, layersSelected } from './layers/layers'
import { $focus, type SearchParamsCqiMap } from './storeCqi'
import {useMap} from "react-map-gl";
import {default as dataTiles} from "../assets/data.pmtiles";
export const MapSourceCqi = () => {
const params = useStore($searchParams) as SearchParamsCqiMap
const focus = useStore($focus)
const mapData = useStore($clickedMapData)
const mapDataIds = mapData?.map((feature) => feature.properties?.id) ?? []
// Debugging:
console.log(mapDataIds)
const map = useMap()
console.log(map.current?.getStyle())
// // Debugging:
// console.log(mapDataIds)
// const map = useMap()
// console.log(map.current?.getStyle())
const focusFilter = focus ? ['match', ['get', focus.key], focus.values, true, false] : null
const pmtilesUrl = "pmtiles://" + dataTiles
return (
<Source
id="cqi"
type="vector"
// url="pmtiles://https://atlas-tiles.s3.eu-central-1.amazonaws.com/cycling_quality_index.pmtiles"
url="pmtiles://http://localhost:5174/src/assets/out.pmtiles"
url={pmtilesUrl}
attribution="© OpenStreetMap"
>
{layersSelected.map((layer) => {

6
src/vite-env.d.ts vendored
View file

@ -1 +1,7 @@
/// <reference types="vite/client" />
// images
declare module '*.pmtiles' {
const src: string
export default src
}

28
tailwind.config.js Normal file
View file

@ -0,0 +1,28 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}',
'./public/javascript/*.{js,ts}',
],
theme: {
extend: {
zIndex: {
1000: '1000',
},
},
},
corePlugins: {
// Removes the opacity var() from Styles in order to get dev tools show the color.
// https://twitter.com/adamwathan/status/1529596984235118595
// https://tailwindcss.com/docs/configuration#core-plugins
backdropOpacity: false, // The backdrop-opacity utilities like backdrop-opacity-50
backgroundOpacity: false, // The background-color opacity utilities like bg-opacity-25
borderOpacity: false, // The border-color opacity utilities like border-opacity-25
divideOpacity: false, // The divide-opacity utilities like divide-opacity-50
placeholderOpacity: false, // The placeholder color opacity utilities like placeholder-opacity-25
ringOpacity: false, // The ring-opacity utilities like ring-opacity-50
textOpacity: false, // The text-opacity utilities like text-opacity-50},
// 'opacity': true, // The opacity utilities like opacity-50
},
plugins: [require('@tailwindcss/typography'), require('@tailwindcss/forms')],
}

View file

@ -4,4 +4,5 @@ import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
assetsInclude:["**/*.pmtiles"]
})