Compare commits
12 Commits
main
...
jsonOsmQue
Author | SHA1 | Date |
---|---|---|
dancingCycle | 8dbc45f396 | |
dancingCycle | feb536cf8c | |
dancingCycle | c0a9a5b363 | |
dancingCycle | 5f861ca362 | |
dancingCycle | 2bbd9ef8d8 | |
dancingCycle | a4f8699e1b | |
dancingCycle | 30e7be1e52 | |
dancingCycle | 30377d7c38 | |
dancingCycle | a6fd35f7ae | |
dancingCycle | 2e16654312 | |
dancingCycle | 082f112015 | |
dancingCycle | b8879d5571 |
|
@ -1,4 +1,7 @@
|
|||
# ---> Node
|
||||
# Others
|
||||
build*
|
||||
*.tar.gz
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
@ -6,7 +9,6 @@ npm-debug.log*
|
|||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
@ -43,8 +45,8 @@ build/Release
|
|||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
@ -55,9 +57,6 @@ web_modules/
|
|||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
|
@ -73,20 +72,15 @@ web_modules/
|
|||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
|
@ -94,20 +88,13 @@ dist
|
|||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
|
@ -119,14 +106,3 @@ dist
|
|||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
|
|
27
app/app.jsx
27
app/app.jsx
|
@ -1,27 +0,0 @@
|
|||
import React from 'react';
|
||||
import { BrowserRouter, Route, Routes } from 'react-router-dom';
|
||||
|
||||
/*some stylesheet is required to use react-bootstrip components*/
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
|
||||
import Contact from './pages/contact';
|
||||
import Home from './pages/home';
|
||||
import Map from './pages/map-page';
|
||||
import NavBar from './components/nav-bar';
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
//BrowserRouter is the router implementation for HTML5 browsers
|
||||
//Link enables Routes on an anchor tag
|
||||
//Switch returns only the first matching route rather than all
|
||||
//Route is the conditionally shown component //based on matching a path to a URL
|
||||
<BrowserRouter>
|
||||
<NavBar />
|
||||
<Routes>
|
||||
<Route path="/" element={<Map />} />
|
||||
<Route path="/map" element={<Map />} />
|
||||
<Route path="/contact" element={<Contact />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
import getIconUrl from './icon-url';
|
||||
|
||||
/*return icon object*/
|
||||
export default function getIcon(ptByIfleet){
|
||||
if(ptByIfleet===undefined || ptByIfleet===null){
|
||||
//console.log('getIcon(): ptByIfleet NOT available')
|
||||
return null;
|
||||
}
|
||||
else{
|
||||
//console.log('getIcon(): ptByIfleet available')
|
||||
const icon = new L.Icon({
|
||||
/*path to icon graphic*/
|
||||
iconUrl: getIconUrl(ptByIfleet.route_type),
|
||||
/*path to graphic used for high resolution monitors*/
|
||||
iconRetinaUrl: getIconUrl(ptByIfleet.route_type),
|
||||
popupAnchor: [-0, -0],
|
||||
/*size of the icon in width and hight*/
|
||||
iconSize: [32,32],
|
||||
/*determine how the popup is positions relative to the actual point on the map*/
|
||||
popupAnchor:[0,-10],
|
||||
/*determine how the image is positions relative to the actual point on the map*/
|
||||
iconAnchor: null,
|
||||
/*path to shadow graphic*/
|
||||
shadowUrl: null,
|
||||
/*size of the shadow in width and hight*/
|
||||
shadowSize: null,
|
||||
/*determine how the mage is positions relative to the actual point on the map*/
|
||||
shadowAnchor: null,
|
||||
className: 'marker-msg'
|
||||
});
|
||||
//console.log('getIcon(): icon available')
|
||||
return icon;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import bfly from '../../assets/Logo_SIB_electricindigo.svg';
|
||||
import bus from '../../assets/bus.svg';
|
||||
import train from '../../assets/train.svg';
|
||||
import tram from '../../assets/tram.svg';
|
||||
|
||||
/* return URL that fits GTFS route_type*/
|
||||
export default function getIconUrl(routeTypeEnum){
|
||||
//console.log('getIconUrl() routeTypeEnum: '+routeTypeEnum);
|
||||
return routeTypeEnum===0?tram:
|
||||
routeTypeEnum===1?bfly:
|
||||
routeTypeEnum===2?train:
|
||||
routeTypeEnum===3?bus:
|
||||
bfly;
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
/*set up the height of*/
|
||||
.leaflet-container {
|
||||
height: 85vh;
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {MapContainer,TileLayer} from 'react-leaflet';
|
||||
/*JS module import (vs cdn or style link)*/
|
||||
import 'leaflet/dist/leaflet.css'
|
||||
import './map.css';
|
||||
|
||||
import MarkerElem from './marker-elem';
|
||||
|
||||
export default function Map({elements}) {
|
||||
/*lat and lon of Braunschweig,DE*/
|
||||
const position = [52.26594, 10.52673]
|
||||
//TODO make this switch available via configuration!
|
||||
const hasGtfs = false;
|
||||
return (
|
||||
<>
|
||||
<MapContainer
|
||||
center={position}
|
||||
zoom={10}
|
||||
minZoom={2}
|
||||
scrollWheelZoom={true}
|
||||
>
|
||||
<TileLayer
|
||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
/>
|
||||
{
|
||||
elements.map(function(value,key) {
|
||||
//console.log(`key: ${key}, tripId: ${value.tripId}`);
|
||||
return <MarkerElem key={value.id} index={value.id} element={value}/>;
|
||||
})
|
||||
}
|
||||
</MapContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Map.propTypes = {
|
||||
elements: PropTypes.array
|
||||
};
|
|
@ -1,42 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Marker} from 'react-leaflet';
|
||||
|
||||
import PopupElem from './popup-elem';
|
||||
import getIcon from './icon';
|
||||
|
||||
export default function MarkerElemPlus({element}){
|
||||
if(element===undefined || element===null){
|
||||
console.error('element undefined or null');
|
||||
return null;
|
||||
}else{
|
||||
const markerIcon=getIcon();
|
||||
if(markerIcon===null){
|
||||
//TODO Handle issue!
|
||||
console.error('ERROR: icon null');
|
||||
return null;
|
||||
}else if(element.lat===undefined||element.lat===null){
|
||||
//TODO Handle issue!
|
||||
console.error('ERROR: element lat undefined or null');
|
||||
return null;
|
||||
}else if(element.lon===undefined||element.lon===null){
|
||||
//TODO Handle issue!
|
||||
console.error('ERROR: element lon undefined or null');
|
||||
return null;
|
||||
}else{
|
||||
return(
|
||||
<>
|
||||
<Marker
|
||||
position={[element.lat,element.lon]}
|
||||
icon={markerIcon}
|
||||
>
|
||||
<PopupElem element={element} />
|
||||
</Marker>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
MarkerElemPlus.propTypes = {
|
||||
element: PropTypes.object
|
||||
};
|
|
@ -1,22 +0,0 @@
|
|||
import React, {useEffect,useState} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import MarkerElemPlus from './marker-elem-plus';
|
||||
|
||||
export default function MarkerElem({ element }){
|
||||
if(element===undefined || element===null){
|
||||
console.error('element undefined or null');
|
||||
return null;
|
||||
}else{
|
||||
return(
|
||||
<>
|
||||
<MarkerElemPlus
|
||||
element={element}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
MarkerElem.propTypes = {
|
||||
element: PropTypes.object
|
||||
};
|
|
@ -1,19 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Popup} from 'react-leaflet';
|
||||
|
||||
export default function PopupElem({element}){
|
||||
return (
|
||||
<>
|
||||
<Popup>
|
||||
id: {element.id} <br/>
|
||||
name: {element.name} <br/>
|
||||
lat: {element.lat} <br/>
|
||||
lon: {element.lon}
|
||||
</Popup>
|
||||
</>
|
||||
);
|
||||
};
|
||||
PopupElem.propTypes = {
|
||||
element: PropTypes.object
|
||||
};
|
|
@ -1,27 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Navbar, Nav } from 'react-bootstrap';
|
||||
import { LinkContainer } from 'react-router-bootstrap';
|
||||
|
||||
function NavigationBar () {
|
||||
return (
|
||||
<Navbar collapseOnSelect fixed="top" bg="dark" expand="xxl" variant="dark">
|
||||
//TODO make brand available through configuration
|
||||
<Navbar.Brand href="/">GOMPASS</Navbar.Brand>
|
||||
<Navbar.Toggle aria-controls="basic-navbar-nav" />
|
||||
<Navbar.Collapse id="basic-navbar-nav">
|
||||
<Nav className="mr-auto">
|
||||
<LinkContainer to="/map">
|
||||
<Nav.Link>Second Hand</Nav.Link>
|
||||
</LinkContainer>
|
||||
</Nav>
|
||||
<Nav className="mr-auto">
|
||||
<LinkContainer to="/contact">
|
||||
<Nav.Link>Contact</Nav.Link>
|
||||
</LinkContainer>
|
||||
</Nav>
|
||||
</Navbar.Collapse>
|
||||
</Navbar>
|
||||
);
|
||||
}
|
||||
|
||||
export default NavigationBar;
|
|
@ -1,34 +0,0 @@
|
|||
import React from 'react';
|
||||
import packageInfo from '../../package.json'
|
||||
const VERSION = packageInfo.version;
|
||||
const Contact = () => {
|
||||
return (
|
||||
<>
|
||||
<h2>About this website</h2>
|
||||
|
||||
<p>
|
||||
For questions about this website please do not hesitate to reach out to dialog (at) swingbe (dot) de.
|
||||
</p>
|
||||
<h2>Imprint</h2>
|
||||
<address>
|
||||
<strong>
|
||||
Stefan Begerad, Software Ingenieur Begerad
|
||||
</strong>
|
||||
<br />
|
||||
c/o WTF Kooperative eG, Hinterhaus, 3. OG
|
||||
<br />
|
||||
Forsmannstr. 14b
|
||||
<br />
|
||||
22303 Hamburg
|
||||
<br />
|
||||
Deutschland
|
||||
<br />
|
||||
</address>
|
||||
<h2>Other</h2>
|
||||
<p>
|
||||
Version: {VERSION}
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default Contact;
|
|
@ -1,8 +0,0 @@
|
|||
import React from 'react';
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<h1>Home</h1>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
import React, {useEffect,useState} from 'react';
|
||||
import axios from 'axios';
|
||||
import qs from 'qs';
|
||||
|
||||
import Map from '../components/map/map';
|
||||
|
||||
const data = qs.stringify(
|
||||
{
|
||||
'data': '[bbox:51.990800124058914,10.045623779296875,52.552976197007524,11.01104736328125][out:json];(node["shop"="second_hand"];way["shop"="second_hand"];relation["shop"="second_hand"];);out center;'
|
||||
}
|
||||
);
|
||||
|
||||
//TODO Make fields available via configuration!
|
||||
const interval=3600000;
|
||||
const opInstance='https://overpass-api.de/api/interpreter';
|
||||
const opInstanceZ='https://z.overpass-api.de/api/interpreter';
|
||||
const config = {
|
||||
method: 'post',
|
||||
maxBodyLength: Infinity,
|
||||
url: opInstanceZ,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data : data
|
||||
};
|
||||
|
||||
function parseOsmElem(response){
|
||||
let elemAry=[];
|
||||
if(response.data){
|
||||
let elements=response.data.elements;
|
||||
let no=elements.length;
|
||||
//console.log('no: '+no);
|
||||
for(let i = 0; i < no; i++) {
|
||||
let elemObj={};
|
||||
elemObj.id=elements[i].id;
|
||||
elemObj.name=elements[i].tags.name;
|
||||
if(elements[i].tags.name===undefined){
|
||||
//TODO Handle issue!
|
||||
console.error('ERROR: OSM element does NOT include name tag');
|
||||
}else if(elements[i].type==='node'){
|
||||
elemObj.lat=elements[i].lat;
|
||||
elemObj.lon=elements[i].lon;
|
||||
}else if(elements[i].type==='way'){
|
||||
elemObj.lat=elements[i].center.lat;
|
||||
elemObj.lon=elements[i].center.lon;
|
||||
}else{
|
||||
console.error('ERROR: OSM element NOT supported');
|
||||
}
|
||||
//console.log('elem name: '+elemObj.name);
|
||||
//console.log('elem lat: '+elemObj.lat);
|
||||
//console.log('elem lon: '+elemObj.lon);
|
||||
elemAry.push(elemObj);
|
||||
//console.error('elemAry no: '+elemAry.length);
|
||||
}
|
||||
}else{
|
||||
console.error('ERROR: response NOT available');
|
||||
}
|
||||
return elemAry;
|
||||
};
|
||||
|
||||
export default function MapPage() {
|
||||
/*storage*/
|
||||
const [ary, setAry] = useState([]);
|
||||
const getData= async ()=>{
|
||||
//console.log('getData() start...');
|
||||
try {
|
||||
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
|
||||
const res = await axios(config);
|
||||
let elemAry=parseOsmElem(res);
|
||||
setAry((ary) => elemAry);
|
||||
} catch (err) {
|
||||
console.error('ERROR: err.message: ' + err.message);
|
||||
}
|
||||
//console.log('getData() done.');
|
||||
};
|
||||
|
||||
useEffect(()=>{
|
||||
/*do not wait the interval when component loads the first time*/
|
||||
getData();
|
||||
|
||||
/*refresh data periodically*/
|
||||
const intervalCall=setInterval(()=>{
|
||||
getData();
|
||||
}, interval);
|
||||
/*TODO adjust interval, make it available via config file*/
|
||||
return ()=>{
|
||||
/*clean up*/
|
||||
clearInterval(intervalCall);
|
||||
};
|
||||
},[]);
|
||||
return (
|
||||
<>
|
||||
<Map elements={ary}/>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -4,7 +4,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|||
const path = require('path');
|
||||
module.exports = {
|
||||
//bundle *.js from this entry point
|
||||
entry: path.resolve(__dirname, '../app/index.jsx'),
|
||||
entry: path.resolve(__dirname, '../src/index.jsx'),
|
||||
//create output file to be linked to index.html
|
||||
output: {
|
||||
filename: '[name].bundle.js',
|
||||
|
@ -18,7 +18,7 @@ module.exports = {
|
|||
//test all *.jsx (e.g. React.js) using babel-loader
|
||||
test: /\.(js|jsx)$/,
|
||||
exclude: /node_modules/,
|
||||
include: path.resolve(__dirname, '../app'),
|
||||
include: path.resolve(__dirname, '../src'),
|
||||
use: ['babel-loader'],
|
||||
},
|
||||
{
|
||||
|
@ -38,6 +38,14 @@ module.exports = {
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif)$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
|
|
|
@ -9,6 +9,6 @@ module.exports = merge(common, {
|
|||
//enable strong source mapping
|
||||
devtool: 'inline-source-map',
|
||||
devServer: {
|
||||
static: path.resolve(__dirname, '../build'),
|
||||
static: path.resolve(__dirname, '../dist'),
|
||||
},
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load Diff
48
package.json
48
package.json
|
@ -1,46 +1,40 @@
|
|||
{
|
||||
"private": true,
|
||||
"name": "gompass",
|
||||
"description": "gompass",
|
||||
"version": "0.1.0",
|
||||
"description": "Green Compass",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
"public",
|
||||
"compass",
|
||||
"gompass"
|
||||
"react",
|
||||
"webpack"
|
||||
],
|
||||
"author": "Software Ingenieur Begerad <dialog@SwIngBe.de>",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "webpack serve --config config/webpack.dev.js",
|
||||
"build": "webpack --config config/webpack.prod.js"
|
||||
"build": "webpack --config config/webpack.prod.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.19.1",
|
||||
"@babel/preset-env": "7.19.1",
|
||||
"@babel/preset-react": "7.18.6",
|
||||
"babel-loader": "8.2.5",
|
||||
"css-loader": "6.7.1",
|
||||
"html-webpack-plugin": "5.5.0",
|
||||
"style-loader": "3.3.1",
|
||||
"@babel/core": "7.22.10",
|
||||
"@babel/preset-env": "7.22.10",
|
||||
"@babel/preset-react": "7.22.5",
|
||||
"babel-loader": "9.1.3",
|
||||
"css-loader": "6.7.3",
|
||||
"file-loader": "6.2.0",
|
||||
"html-webpack-plugin": "5.5.3",
|
||||
"style-loader": "3.3.2",
|
||||
"svg-url-loader": "8.0.0",
|
||||
"webpack": "5.74.0",
|
||||
"webpack-cli": "4.10.0",
|
||||
"webpack-dev-server": "4.11.0",
|
||||
"webpack-merge": "5.8.0"
|
||||
"webpack": "5.88.2",
|
||||
"webpack-cli": "5.1.4",
|
||||
"webpack-dev-server": "4.15.1",
|
||||
"webpack-merge": "5.9.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "0.27.2",
|
||||
"bootstrap": "5.2.1",
|
||||
"leaflet": "1.8.0",
|
||||
"qs": "^6.11.0",
|
||||
"leaflet": "1.9.4",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.2.0",
|
||||
"react-bootstrap": "2.5.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-leaflet": "4.0.2",
|
||||
"react-router-bootstrap": "0.26.2"
|
||||
"react-router-dom": "6.16.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,18 +2,6 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!-- load resources for bootstrap -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
|
||||
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
|
||||
crossorigin="anonymous">
|
||||
<!-- avoid content hiding behind react-bootstrap navbar -->
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding-top: 100px;
|
||||
}
|
||||
</style>
|
||||
<title>Gompass</title>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# init
|
||||
|
||||
```
|
||||
mkdir ~/git && cd ~/git
|
||||
git clone -b main <repository>
|
||||
cd <repository>
|
||||
npm i
|
||||
```
|
||||
|
||||
# dev
|
||||
|
||||
```
|
||||
npm run start
|
||||
```
|
||||
|
||||
# prod
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
@ -0,0 +1,67 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { get } from '../utils/request';
|
||||
//TODO adjust config usage
|
||||
import config from '../utils/config';
|
||||
import Map from './map';
|
||||
import Table from './table';
|
||||
|
||||
/*destructure props object*/
|
||||
export default function FetchOsm ({address, title}) {
|
||||
|
||||
const [array, setArray] = useState([]);
|
||||
|
||||
/*fetch array in a JavaScript function*/
|
||||
const fetch = async () => {
|
||||
if(config && address){
|
||||
try {
|
||||
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
|
||||
//console.log("Fetch:fetch(): address: " + address);
|
||||
const res = await get(address);
|
||||
//console.log("Fetch:fetch(): res.length: "+res.elements.length);
|
||||
setArray((array) => res.elements);
|
||||
} catch (err) {
|
||||
console.error('err.message: ' + err.message);
|
||||
setArray((array) => []);
|
||||
}
|
||||
}else{
|
||||
//console.log("Fetch:fetch(): address: " + address);
|
||||
console.error('Fetch:fetch(): config or address NOT available');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
/*effect goes here*/
|
||||
fetch();
|
||||
/*use an empty dependency array to ensure the hook is running only once*/
|
||||
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/
|
||||
}, []);
|
||||
|
||||
|
||||
if ( array !== undefined && array !== null && array.length > 0 ) {
|
||||
/*return a React element*/
|
||||
return (
|
||||
<>
|
||||
<Map
|
||||
array={array}
|
||||
title={title}
|
||||
/>
|
||||
<Table
|
||||
array={array}
|
||||
title={title}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}else{
|
||||
return (
|
||||
<>
|
||||
<p>Loading...</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
FetchOsm.propTypes = {
|
||||
address: PropTypes.string,
|
||||
title: PropTypes.string
|
||||
};
|
|
@ -0,0 +1,31 @@
|
|||
import React from 'react'
|
||||
import { Link} from 'react-router-dom';
|
||||
|
||||
export default function Header(){
|
||||
|
||||
return (
|
||||
<>
|
||||
<Link
|
||||
to='/'
|
||||
>
|
||||
<button>
|
||||
Home
|
||||
</button>
|
||||
</Link>
|
||||
<Link
|
||||
to='https://www.swingbe.de/imprint/'
|
||||
>
|
||||
<button>
|
||||
Imprint
|
||||
</button>
|
||||
</Link>
|
||||
<Link
|
||||
to='https://www.swingbe.de/privacy-policy/'
|
||||
>
|
||||
<button>
|
||||
Privacy Policy
|
||||
</button>
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,9 +1,8 @@
|
|||
import L from "leaflet";
|
||||
|
||||
import bfly from '../../assets/Logo_SIB_electricindigo.svg';
|
||||
|
||||
/*return icon object*/
|
||||
export default function getIcon(){
|
||||
//console.log('getIcon(): ptByIfleet available')
|
||||
const icon = new L.Icon({
|
||||
export const icon = new L.icon({
|
||||
/*path to icon graphic*/
|
||||
iconUrl: bfly,
|
||||
/*path to graphic used for high resolution monitors*/
|
||||
|
@ -23,6 +22,3 @@ export default function getIcon(){
|
|||
shadowAnchor: null,
|
||||
className: 'marker-msg'
|
||||
});
|
||||
//console.log('getIcon(): icon available')
|
||||
return icon;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import L from "leaflet";
|
||||
import iconUrl from "leaflet/dist/images/marker-icon.png";
|
||||
export const icon = L.icon({
|
||||
iconUrl,
|
||||
});
|
|
@ -0,0 +1,71 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import MapObject from './map-object';
|
||||
|
||||
import { get } from '../utils/request';
|
||||
|
||||
// import JSON
|
||||
import jsonOsmQuery from '../osm-query.json';
|
||||
|
||||
export default function JsonOsmQuery(){
|
||||
|
||||
const rryOsmQuery = jsonOsmQuery.queries;
|
||||
console.log('rry.lngth: ' + rryOsmQuery.length);
|
||||
|
||||
const [mp, setMp] = useState(null);
|
||||
|
||||
/*fetch array in a JavaScript function*/
|
||||
const fetch = async () => {
|
||||
try {
|
||||
const mpRsp = new Map()
|
||||
console.log('mpRsp.lngth: ' + mpRsp.size);
|
||||
|
||||
|
||||
for ( let i = 0; i < rryOsmQuery.length; i++ ) {
|
||||
console.log('i: ' + i );
|
||||
const query = rryOsmQuery[i];
|
||||
|
||||
console.log('query title: ' + query.title);
|
||||
|
||||
const address = query.query;
|
||||
console.log("Fetch:fetch(): address: " + address);
|
||||
|
||||
const res = await get(address);
|
||||
console.log("Fetch:fetch(): res.length: "+res.elements.length);
|
||||
mpRsp.set(query.title, res.elements)
|
||||
}
|
||||
|
||||
setMp((mp) => mpRsp);
|
||||
} catch (err) {
|
||||
console.error('err.message: ' + err.message);
|
||||
setMp((array) => null);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
/*effect goes here*/
|
||||
fetch();
|
||||
/*use an empty dependency array to ensure the hook is running only once*/
|
||||
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/
|
||||
}, []);
|
||||
|
||||
|
||||
if ( mp !== undefined && mp !== null && mp.size > 0 ) {
|
||||
/*return a React element*/
|
||||
return (
|
||||
<>
|
||||
<p>JsonOsmQuery</p>
|
||||
<MapObject
|
||||
object={mp}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}else{
|
||||
return (
|
||||
<>
|
||||
<p>JsonOsmQuery</p>
|
||||
<p>Loading...</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,103 @@
|
|||
import React from 'react'
|
||||
import { Link} from 'react-router-dom';
|
||||
|
||||
export default function List(){
|
||||
|
||||
return (
|
||||
<>
|
||||
<ul>
|
||||
<li>
|
||||
<Link to={'/bike-ride'} >
|
||||
<button>
|
||||
Bike and ride (B+R) stations
|
||||
</button>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={'/organic-bakery'} >
|
||||
<button>
|
||||
Organic bakeries
|
||||
</button>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={'/organic-only-shop'} >
|
||||
<button>
|
||||
Organic only shops
|
||||
</button>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={'/organic-shop'} >
|
||||
<button>
|
||||
Organic shops
|
||||
</button>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={'/park-ride'} >
|
||||
<button>
|
||||
Park and ride (P+R) stations
|
||||
</button>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={'/public-bookcase'} >
|
||||
<button>
|
||||
Public bookcases
|
||||
</button>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={'/second-hand'} >
|
||||
<button>
|
||||
Second hand shops
|
||||
</button>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={'/taxi'} >
|
||||
<button>
|
||||
Taxi stations
|
||||
</button>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={'/ticket-machine'} >
|
||||
<button>
|
||||
Ticket machines
|
||||
</button>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={'/ticket-office'} >
|
||||
<button>
|
||||
Ticket offices
|
||||
</button>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={'/vegan'} >
|
||||
<button>
|
||||
Vegan only restaurants
|
||||
</button>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={'/vegetarian'} >
|
||||
<button>
|
||||
Vegetarian only restaurants
|
||||
</button>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={'/zero-waste'} >
|
||||
<button>
|
||||
Zero waste shops
|
||||
</button>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,133 @@
|
|||
import L from 'leaflet'
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import icon from "leaflet/dist/images/marker-icon.png";
|
||||
import iconShadow from "leaflet/dist/images/marker-shadow.png";
|
||||
|
||||
let DefaultIcon = L.icon({
|
||||
iconUrl: icon,
|
||||
shadowUrl: iconShadow,
|
||||
});
|
||||
|
||||
export default function MapArray( { array } ) {
|
||||
console.log('map-array:MapArray: array.lngth: ' + array.length);
|
||||
|
||||
/*center map at Node: Umweltzentrum Braunschweig (3650315388)*/
|
||||
const mapCenter = [52.2671189, 10.5221905];
|
||||
|
||||
const mapRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
//base layer
|
||||
const baseLayerOsm = L.tileLayer(
|
||||
"http://{s}.tile.osm.org/{z}/{x}/{y}.png",
|
||||
{
|
||||
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
|
||||
maxZoom: 18,
|
||||
scrollWheelZoom: false,
|
||||
}
|
||||
);
|
||||
|
||||
//marker array
|
||||
const rryMrkr = array.map( function( value, key ) {
|
||||
|
||||
//console.log('map-array:MapArray:map() value: ' +JSON.stringify(value));
|
||||
|
||||
//get entity properties
|
||||
const id = value.id;
|
||||
const type = value.type;
|
||||
const name = value.tags.name;
|
||||
let lat, lon = null;
|
||||
if ( type === "node"){
|
||||
lat = value.lat;
|
||||
lon = value.lon;
|
||||
} else if ( type === "way" || type === "relation" ) {
|
||||
lat = value.center.lat;
|
||||
lon = value.center.lon;
|
||||
} else {
|
||||
console.error("map-entities: OSM type (" + type + ") NOT known");
|
||||
}
|
||||
|
||||
//verify entity properties
|
||||
if(lat !== undefined && lat !== null && lon !== undefined && lon !== null ){
|
||||
//TODO
|
||||
/*
|
||||
var marker = L.marker([lat, lon]).addTo(map);
|
||||
const popupText ='name: <b>' + name + '</b><br />id: ' + id + '<br />type: ' + type;
|
||||
marker.bindPopup(popupText);
|
||||
|
||||
*/
|
||||
L.Marker.prototype.options.icon = DefaultIcon;
|
||||
const marker = L.marker(
|
||||
[lat, lon],
|
||||
{alt: 'marker name: ' + name}
|
||||
);
|
||||
const popupText ='name: <b>' + name + '</b><br />lat: ' + lat + '<br />lon: ' + lon;
|
||||
//console.log('popupText: ' + popupText);
|
||||
marker.bindPopup(popupText);
|
||||
return marker
|
||||
} else {
|
||||
console.error('map-entities: lat or lon undefined or null');
|
||||
}
|
||||
|
||||
});
|
||||
console.log('rryMrkr: ' + rryMrkr.length);
|
||||
|
||||
//layer group
|
||||
const overlayMrkr = L.layerGroup(rryMrkr);
|
||||
|
||||
//map
|
||||
const map = L.map(
|
||||
mapRef.current,
|
||||
{attributionControl: true,
|
||||
layers : [baseLayerOsm, overlayMrkr]}
|
||||
).setView(
|
||||
mapCenter,
|
||||
10
|
||||
);
|
||||
|
||||
//layout control
|
||||
var baseMaps = {
|
||||
"OpenStreetMap": baseLayerOsm
|
||||
};
|
||||
var overlayMaps = {
|
||||
'Markers' : overlayMrkr
|
||||
};
|
||||
const layerControl = L.control.layers(baseMaps, overlayMaps).addTo(map);
|
||||
|
||||
// unmount map function
|
||||
//You should unmount the function in react.js to remove the existing map.
|
||||
return () => map.remove();
|
||||
}, []);
|
||||
|
||||
if ( array !== undefined && array !== null && array.length > 0 ) {
|
||||
return (
|
||||
<>
|
||||
<p>MapArray</p>
|
||||
<div
|
||||
style={{padding: 0, margin: 0, width: "75%", height: "23vh",}}
|
||||
ref={el => mapRef.current = el}>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}else{
|
||||
//TODO Why does mapRef have to be present?
|
||||
return (
|
||||
<>
|
||||
<p>MapArray loading...</p>
|
||||
<div
|
||||
ref={el => mapRef.current = el}>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
};
|
||||
//https://github.com/azaharyan/react-leaflet-example
|
||||
|
||||
MapArray.propTypes = {
|
||||
array: PropTypes.array
|
||||
};
|
|
@ -0,0 +1,54 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { get } from '../utils/request';
|
||||
//TODO adjust config usage
|
||||
import config from '../utils/config';
|
||||
|
||||
import Map from './map-array';
|
||||
|
||||
export default function MapLGroup(){
|
||||
|
||||
const [array, setArray] = useState([]);
|
||||
|
||||
/*fetch array in a JavaScript function*/
|
||||
const fetch = async () => {
|
||||
try {
|
||||
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
|
||||
const address = config.ADDRESS + 'nwr[shop=second_hand](area)->.shops;nwr[second_hand=yes](area)->.secondHands;(.shops;.secondHands;);' + config.OUT;
|
||||
console.log("Fetch:fetch(): address: " + address);
|
||||
const res = await get(address);
|
||||
console.log("Fetch:fetch(): res.length: "+res.elements.length);
|
||||
setArray((array) => res.elements);
|
||||
} catch (err) {
|
||||
console.error('err.message: ' + err.message);
|
||||
setArray((array) => []);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
/*effect goes here*/
|
||||
fetch();
|
||||
/*use an empty dependency array to ensure the hook is running only once*/
|
||||
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/
|
||||
}, []);
|
||||
|
||||
|
||||
if ( array !== undefined && array !== null && array.length > 0 ) {
|
||||
/*return a React element*/
|
||||
return (
|
||||
<>
|
||||
<p>MapLGroup</p>
|
||||
<Map
|
||||
array={array}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}else{
|
||||
return (
|
||||
<>
|
||||
<p>MapLGroup</p>
|
||||
<p>Loading...</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
import L from 'leaflet'
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import icon from "leaflet/dist/images/marker-icon.png";
|
||||
import iconShadow from "leaflet/dist/images/marker-shadow.png";
|
||||
|
||||
let DefaultIcon = L.icon({
|
||||
iconUrl: icon,
|
||||
shadowUrl: iconShadow,
|
||||
});
|
||||
|
||||
export default function MapObject( { object } ) {
|
||||
console.log('map-object:MapObject: object.size: ' + object.size);
|
||||
|
||||
/*center map at Node: Umweltzentrum Braunschweig (3650315388)*/
|
||||
const mapCenter = [52.2671189, 10.5221905];
|
||||
|
||||
const mapRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
//base layer
|
||||
const baseLayerOsm = L.tileLayer(
|
||||
"http://{s}.tile.osm.org/{z}/{x}/{y}.png",
|
||||
{
|
||||
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
|
||||
maxZoom: 18,
|
||||
scrollWheelZoom: false,
|
||||
}
|
||||
);
|
||||
|
||||
//map
|
||||
const map = L.map(
|
||||
mapRef.current,
|
||||
{attributionControl: true,
|
||||
layers : [baseLayerOsm]}
|
||||
).setView(
|
||||
mapCenter,
|
||||
10
|
||||
);
|
||||
|
||||
const rryTitleKeys = new Array();
|
||||
const rryLayerValues = new Array();
|
||||
//iterate over osm rsp objects
|
||||
for (const [title, elements] of object) {
|
||||
console.log(`The value for key ${title} is ${elements}`);
|
||||
|
||||
//add title
|
||||
rryTitleKeys.push(title);
|
||||
|
||||
//add layer
|
||||
//marker array
|
||||
const rryMrkr = elements.map( function( value, key ) {
|
||||
|
||||
//console.log('map-object:MapObject:map() value: ' +JSON.stringify(value));
|
||||
|
||||
//get entity properties
|
||||
const id = value.id;
|
||||
const type = value.type;
|
||||
const name = value.tags.name;
|
||||
let lat, lon = null;
|
||||
if ( type === "node"){
|
||||
lat = value.lat;
|
||||
lon = value.lon;
|
||||
} else if ( type === "way" || type === "relation" ) {
|
||||
lat = value.center.lat;
|
||||
lon = value.center.lon;
|
||||
} else {
|
||||
console.error("map-entities: OSM type (" + type + ") NOT known");
|
||||
}
|
||||
|
||||
//verify entity properties
|
||||
if(lat !== undefined && lat !== null && lon !== undefined && lon !== null ){
|
||||
//TODO
|
||||
/*
|
||||
var marker = L.marker([lat, lon]).addTo(map);
|
||||
const popupText ='name: <b>' + name + '</b><br />id: ' + id + '<br />type: ' + type;
|
||||
marker.bindPopup(popupText);
|
||||
|
||||
*/
|
||||
L.Marker.prototype.options.icon = DefaultIcon;
|
||||
const marker = L.marker(
|
||||
[lat, lon],
|
||||
{alt: 'marker name: ' + name}
|
||||
);
|
||||
const popupText ='name: <b>' + name + '</b><br />lat: ' + lat + '<br />lon: ' + lon;
|
||||
//console.log('popupText: ' + popupText);
|
||||
marker.bindPopup(popupText);
|
||||
return marker
|
||||
} else {
|
||||
console.error('map-entities: lat or lon undefined or null');
|
||||
}
|
||||
|
||||
});
|
||||
console.log('rryMrkr: ' + rryMrkr.length);
|
||||
|
||||
//layer group
|
||||
const overlayMrkr = L.layerGroup(rryMrkr);
|
||||
rryLayerValues.push(overlayMrkr);
|
||||
}
|
||||
console.log('rryLayerValues.lngth: ' + rryLayerValues.length);
|
||||
console.log('rryTitleKeys.lngth: ' + rryTitleKeys.length);
|
||||
|
||||
const overlayMaps = new Object();
|
||||
for (let i = 0; i < rryTitleKeys.length; i++) {
|
||||
const ttl = rryTitleKeys[i];
|
||||
const lyr = rryLayerValues[i];
|
||||
console.log('i: ' + i + ', ttl: ' + ttl);
|
||||
overlayMaps[ttl] = lyr;
|
||||
}
|
||||
//ERROR console.log('overlayMaps: ' + JSON.stringify(overlayMaps));
|
||||
|
||||
//layout control
|
||||
const baseMaps = {
|
||||
"OpenStreetMap": baseLayerOsm
|
||||
};
|
||||
const layerControl = L.control.layers(baseMaps, overlayMaps).addTo(map);
|
||||
|
||||
// unmount map function
|
||||
//You should unmount the function in react.js to remove the existing map.
|
||||
return () => map.remove();
|
||||
}, []);
|
||||
|
||||
if ( object !== undefined && object !== null && object.size > 0 ) {
|
||||
return (
|
||||
<>
|
||||
<p>MapObject</p>
|
||||
<div
|
||||
style={{padding: 0, margin: 0, width: "75%", height: "23vh",}}
|
||||
ref={el => mapRef.current = el}>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}else{
|
||||
//TODO Why does mapRef have to be present?
|
||||
return (
|
||||
<>
|
||||
<p>MapObject loading...</p>
|
||||
<div
|
||||
ref={el => mapRef.current = el}>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
};
|
||||
//https://github.com/azaharyan/react-leaflet-example
|
||||
|
||||
MapObject.propTypes = {
|
||||
array: PropTypes.array
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import EntitiesMap from './map/map-entities';
|
||||
|
||||
/*destructure props object*/
|
||||
export default function Map ({array, title}){
|
||||
if ( array !== undefined && array !== null && array.length > 0 ) {
|
||||
/*return a React element*/
|
||||
return (
|
||||
<>
|
||||
<p>Map of {array.length} {title}</p>
|
||||
<EntitiesMap entities={array} />
|
||||
</>
|
||||
);
|
||||
}else{
|
||||
return (
|
||||
<>
|
||||
<p>Loading...</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Map.propTypes = {
|
||||
array: PropTypes.array,
|
||||
title: PropTypes.string
|
||||
};
|
|
@ -0,0 +1,99 @@
|
|||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import PropTypes from 'prop-types';
|
||||
import L from 'leaflet'
|
||||
|
||||
import "leaflet/dist/leaflet.css";
|
||||
|
||||
import { icon } from "../icons/icon-bfly";
|
||||
|
||||
//maxBounds={[[-60, -180], [85, 180]]}
|
||||
const corner1 = L.latLng(-60, -180),
|
||||
corner2 = L.latLng(85, 180),
|
||||
bounds = L.latLngBounds(corner1, corner2);
|
||||
|
||||
export default function MapEntities( { entities } ) {
|
||||
//console.log('map-entities:MapEntities: entities.lngth: ' + entities.length);
|
||||
|
||||
/*center map at Node: Umweltzentrum Braunschweig (3650315388)*/
|
||||
const mapCenter = [52.2671189, 10.5221905];
|
||||
|
||||
const mapRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const map = L.map( mapRef.current, {attributionControl: true})
|
||||
.setView( mapCenter, 12).
|
||||
setMaxBounds( bounds );
|
||||
|
||||
L.tileLayer(
|
||||
"http://{s}.tile.osm.org/{z}/{x}/{y}.png",
|
||||
{
|
||||
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
|
||||
minZoom: 4,
|
||||
maxZoom: 18,
|
||||
scrollWheelZoom: false,
|
||||
}).addTo(map);
|
||||
|
||||
//TODO: Why is 'scrollWheelZoom: false,' above not working?
|
||||
//TODO: Why the following line instead?
|
||||
map.scrollWheelZoom.disable()
|
||||
|
||||
entities.map( function( value, key ) {
|
||||
|
||||
//console.log('map-entities:MapEntities:map() value: ' +JSON.stringify(value));
|
||||
|
||||
L.Marker.prototype.options.icon = icon;
|
||||
|
||||
//get entity properties
|
||||
const id = value.id;
|
||||
const type = value.type;
|
||||
const name = value.tags.name;
|
||||
let lat, lon = null;
|
||||
if ( type === "node"){
|
||||
lat = value.lat;
|
||||
lon = value.lon;
|
||||
} else if ( type === "way" || type === "relation" ) {
|
||||
lat = value.center.lat;
|
||||
lon = value.center.lon;
|
||||
} else {
|
||||
console.error("map-entities: OSM type (" + type + ") NOT known");
|
||||
}
|
||||
|
||||
//verify entity properties
|
||||
if(lat !== undefined && lat !== null && lon !== undefined && lon !== null ){
|
||||
var marker = L.marker([lat, lon]).addTo(map);
|
||||
const popupText ='name: <b>' + name + '</b><br />id: ' + id + '<br />type: ' + type;
|
||||
marker.bindPopup(popupText);
|
||||
} else {
|
||||
console.error('map-entities: lat or lon undefined or null');
|
||||
}
|
||||
});
|
||||
|
||||
// unmount map function
|
||||
//You should unmount the function in react.js to remove the existing map.
|
||||
return () => map.remove();
|
||||
}, []);
|
||||
|
||||
if ( entities !== undefined && entities !== null && entities.length > 0 ) {
|
||||
return (
|
||||
<div
|
||||
style={{padding: 0, margin: 0, height: "75vh",}}
|
||||
ref={el => mapRef.current = el}>
|
||||
</div>
|
||||
);
|
||||
}else{
|
||||
//TODO Why does mapRef have to be present?
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
ref={el => mapRef.current = el}>
|
||||
</div>
|
||||
<p>MapEntities loading...</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
MapEntities.propTypes = {
|
||||
entities: PropTypes.array
|
||||
};
|
|
@ -0,0 +1,42 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
function TableEntries ({array}) {
|
||||
if ( array !== undefined && array !== null && array.length > 0 ) {
|
||||
//iterate over array
|
||||
return array.map((item, index) => {
|
||||
if( item.type === "node"){
|
||||
return (
|
||||
<tr
|
||||
key={item.id}
|
||||
>
|
||||
<td>{item.id}</td>
|
||||
<td>{item.tags.name}</td>
|
||||
<td>{item.lat}</td>
|
||||
<td>{item.lon}</td>
|
||||
</tr>
|
||||
);
|
||||
} else if( item.type === "way" || item.type === "relation") {
|
||||
return (
|
||||
<tr
|
||||
key={item.id}
|
||||
>
|
||||
<td>{item.id}</td>
|
||||
<td>{item.tags.name}</td>
|
||||
<td>{item.center.lat}</td>
|
||||
<td>{item.center.lon}</td>
|
||||
</tr>
|
||||
);
|
||||
} else {
|
||||
console.error("table-entities: OSM type NOT known");
|
||||
}
|
||||
});
|
||||
}else{
|
||||
//data is empty
|
||||
return null;
|
||||
}
|
||||
}
|
||||
TableEntries.propTypes = {
|
||||
array: PropTypes.array
|
||||
};
|
||||
export default TableEntries;
|
|
@ -0,0 +1,40 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import TableEntries from './table-entries';
|
||||
|
||||
/*destructure props object*/
|
||||
export default function Table ({array, title}){
|
||||
if ( array !== undefined && array !== null && array.length > 0 ) {
|
||||
/*return a React element*/
|
||||
return (
|
||||
<>
|
||||
<p>Table of {array.length} {title}</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>id</th>
|
||||
<th>name</th>
|
||||
<th>lon</th>
|
||||
<th>lat</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<TableEntries array={array} />
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
);
|
||||
}else{
|
||||
return (
|
||||
<>
|
||||
<h1>Table of {title}</h1>
|
||||
<p>Table loading...</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
Table.propTypes = {
|
||||
array: PropTypes.array,
|
||||
title: PropTypes.string
|
||||
};
|
|
@ -1,14 +1,21 @@
|
|||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import App from './app';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import Main from './main';
|
||||
|
||||
//TODO remove debugging
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.log('development mode');
|
||||
}
|
||||
const container = document.getElementById('root');
|
||||
const root = createRoot(container);
|
||||
|
||||
//since react 18
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
//create root container
|
||||
const root = createRoot(document.getElementById("root"));
|
||||
//render root main
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App tab="home" />
|
||||
<Main />
|
||||
</React.StrictMode>
|
||||
);
|
|
@ -0,0 +1,68 @@
|
|||
import React from 'react';
|
||||
import { BrowserRouter as Router, Link, Route, Routes } from 'react-router-dom';
|
||||
|
||||
import EntityOsm from './pages/entity-osm';
|
||||
import Header from './components/header';
|
||||
import Home from './pages/home';
|
||||
|
||||
import packageInfo from '../package.json'
|
||||
|
||||
import config from './utils/config';
|
||||
|
||||
const VERSION = packageInfo.version;
|
||||
const area = '(area);out body center qt;';
|
||||
|
||||
export default function App(){
|
||||
return (
|
||||
<Router>
|
||||
<Header />
|
||||
<h1>Gompass</h1>
|
||||
<Routes>
|
||||
<Route path='/' element={<Home/>}/>
|
||||
<Route path='/bike-ride' element={<EntityOsm
|
||||
address={config.ADDRESS + 'nwr[amenity=bicycle_parking][bike_ride]["bike_ride"!="no"]' + config.AREA }
|
||||
title='B+R Stations'/>}/>
|
||||
<Route path='/organic-bakery' element={<EntityOsm
|
||||
address={ config.ADDRESS + 'nwr[shop=bakery][organic=yes]' + config.AREA }
|
||||
title='Orgnic Bakeries'/>}/>
|
||||
<Route path='/organic-only-shop' element={<EntityOsm
|
||||
address={ config.ADDRESS + 'nwr[shop][organic=only]' + config.AREA }
|
||||
title='Orgnic Only Shops'/>}/>
|
||||
<Route path='/organic-shop' element={<EntityOsm
|
||||
address={ config.ADDRESS + 'nwr[shop][organic=yes]' + config.AREA }
|
||||
title='Orgnic Shops'/>}/>
|
||||
<Route path='/park-ride' element={<EntityOsm
|
||||
address={ config.ADDRESS + 'nwr["park_ride"!="no"]["park_ride"]' + config.AREA }
|
||||
title='P+R Stations'/>}/>
|
||||
<Route path='/public-bookcase' element={<EntityOsm
|
||||
address={ config.ADDRESS + 'nwr[amenity=public_bookcase]' + config.AREA }
|
||||
title='Public Bookcases'/>}/>
|
||||
<Route path='/second-hand' element={<EntityOsm
|
||||
address={ config.ADDRESS + 'nwr[shop=second_hand](area)->.shops;nwr[second_hand=yes](area)->.secondHands;(.shops;.secondHands;);' + config.OUT }
|
||||
title='Second Hand Shops'/>}/>
|
||||
<Route path='/taxi' element={<EntityOsm
|
||||
address={ config.ADDRESS + 'nwr[amenity=taxi]' + config.AREA }
|
||||
title='Taxi Stations'/>}/>
|
||||
<Route path='/ticket-machine' element={<EntityOsm
|
||||
address={ config.ADDRESS + 'nwr[amenity=vending_machine][vending=public_transport_tickets]' + config.AREA }
|
||||
title='Ticket Machines'/>}/>
|
||||
<Route path='/ticket-office' element={<EntityOsm
|
||||
address={ config.ADDRESS + 'nwr[shop=ticket]["tickets:public_transport"!=no]' + config.AREA }
|
||||
title='Ticket Offices'/>}/>
|
||||
<Route path='/vegan' element={<EntityOsm
|
||||
address={ config.ADDRESS + 'nwr[amenity=restaurant]["diet:vegan"=only]' + config.AREA }
|
||||
title='Vegan Only Restaurants'/>}/>
|
||||
<Route path='/vegetarian' element={<EntityOsm
|
||||
address={ config.ADDRESS + 'nwr[amenity=restaurant]["diet:vegetarian"=only]' + config.AREA }
|
||||
title='Vegetarian Only Restaurants'/>}/>
|
||||
<Route path='/zero-waste' element={<EntityOsm
|
||||
address={ config.ADDRESS + 'nwr[bulk_purchase=yes](area)->.bp;nwr[zero_waste=yes](area)->.zw;(.bp;.zw;);' + config.OUT }
|
||||
title='Zero Waste Shops'/>}/>
|
||||
</Routes>
|
||||
|
||||
<p>
|
||||
Version: {VERSION}
|
||||
</p>
|
||||
</Router>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"queries": [
|
||||
{
|
||||
"title": "Second hand shops",
|
||||
"query": "https://overpass-api.de/api/interpreter?data=[out:json][timeout:60];relation(62531);map_to_area;nwr[shop=second_hand](area)->.shops;nwr[second_hand=yes](area)->.secondHands;(.shops;.secondHands;);out body center qt;"
|
||||
},
|
||||
{
|
||||
"title": "Organic bakeries",
|
||||
"query": "https://overpass-api.de/api/interpreter?data=[out:json][timeout:60];relation(62531);map_to_area;nwr[shop=bakery][organic=yes](area);out body center qt;"
|
||||
},
|
||||
{
|
||||
"title": "Organic only shops",
|
||||
"query": "https://overpass-api.de/api/interpreter?data=[out:json][timeout:60];relation(62531);map_to_area;nwr[shop][organic=only](area);out body center qt;"
|
||||
},
|
||||
{
|
||||
"title": "Public bookcases",
|
||||
"query": "https://overpass-api.de/api/interpreter?data=[out:json][timeout:60];relation(62531);map_to_area;nwr[amenity=public_bookcase](area);out body center qt;"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import FetchOsm from '../components/fetch-osm';
|
||||
|
||||
export default function EntityOsm({address, title}) {
|
||||
return(
|
||||
<>
|
||||
<FetchOsm
|
||||
address={address}
|
||||
title={title}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
EntityOsm.propTypes = {
|
||||
address: PropTypes.string,
|
||||
title: PropTypes.string
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react'
|
||||
import { Link} from 'react-router-dom';
|
||||
|
||||
import List from '../components/list';
|
||||
import MapLGroup from '../components/map-lgroup';
|
||||
import JsonOsmQuery from '../components/json-osm-query';
|
||||
|
||||
export default function Home(){
|
||||
|
||||
return (
|
||||
<>
|
||||
<List />
|
||||
<MapLGroup />
|
||||
<JsonOsmQuery />
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export default {
|
||||
API: 'https://v1rvb.api.swingbe.de/',
|
||||
ADDRESS: 'https://overpass-api.de/api/interpreter?data=[out:json][timeout:60];relation(62531);map_to_area;',
|
||||
AREA: '(area);out body center qt;',
|
||||
OUT: 'out body center qt;',
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* get Wikidata content as string
|
||||
* @param unique q Wikidata content identifier
|
||||
* @return Wikidata content as string
|
||||
*/
|
||||
export async function getWikidata(q) {
|
||||
//console.log('request:getWikidata() q: ' + q);
|
||||
const address = 'https://www.wikidata.org/w/rest.php/wikibase/v0/entities/items/' + q;
|
||||
//console.log('request:getWikidata() address: ' + address)
|
||||
const objct = await get(address);
|
||||
////console.log('request:getWikidata() objct: ' + JSON.stringify(objct));
|
||||
//const strng = JSON.stringify(objct);
|
||||
////console.log('request:getWikidata() strng: ' + strng);
|
||||
//return strng;
|
||||
return objct
|
||||
};
|
||||
|
||||
/**
|
||||
* http get request
|
||||
*
|
||||
* @param pth path
|
||||
* @return response as JSON data
|
||||
*/
|
||||
export async function get(pth) {
|
||||
//console.log('request:get() pth: ' + pth);
|
||||
const data = await fetch(pth, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
const objct = await data.json();
|
||||
return objct;
|
||||
};
|
Loading…
Reference in New Issue