Compare commits

...

18 Commits

Author SHA1 Message Date
dancingCycle 5606ea0764 chore: adjust url for API call 2024-02-08 11:11:13 +01:00
dancingCycle a309c98ec9 chore: adjust url for API call 2024-02-08 10:14:47 +01:00
dancingCycle 0cee35631d chore: adjust url for API call 2024-02-07 22:48:57 +01:00
dancingCycle e90be52f4c chore: adjust url for API call 2024-02-07 22:43:33 +01:00
dancingCycle b23af870a1 chore: adjust url for API call 2024-02-07 22:01:37 +01:00
dancingCycle a828b309be feat: add entity TripUpdate counters to UI 2024-01-29 14:25:02 +01:00
dancingCycle b3025d9d79 feat: add entity TripUpdate counters to UI 2024-01-29 14:09:42 +01:00
dancingCycle 648d1afe66 chore: update minimum node version to 18.17.0 2024-01-26 14:04:06 +01:00
dancingCycle 134dcbcd8b chore: bump version to v0.4.0 2023-07-27 14:29:12 +02:00
dancingCycle 72815f7cbd adjust page Contact 2023-07-27 14:15:40 +02:00
dancingCycle 9ff992b39a add page TripUpdates and VehiclePositions 2023-07-27 12:34:42 +02:00
dancingCycle c5cd44e632 feat: apply function readPbf(buffer) 2023-07-26 14:36:12 +02:00
dancingCycle d67e967c7a feat: add function readPbf(buffer) 2023-07-25 19:17:16 +02:00
dancingCycle 5ed587f0ce chore: adjust url for API call 2023-07-25 18:19:45 +02:00
dancingCycle ac91c2b741 chore: adjust url for API call 2023-07-25 18:17:42 +02:00
dancingCycle 6d94dc0732 chore: adjust url for API call 2023-07-25 18:15:01 +02:00
dancingCycle 82b92eac86 feat: clear Home page 2023-07-25 18:04:06 +02:00
dancingCycle b5e4f567a3 feat: adjust home page 2023-02-15 15:38:11 +01:00
20 changed files with 900 additions and 198 deletions

View File

@ -5,10 +5,10 @@ import { BrowserRouter, Route, Routes } from 'react-router-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
import Contact from './pages/contact';
import Home from './pages/home';
import Table from './pages/table-page';
import Map from './pages/map-page';
import NavBar from './components/nav-bar';
import Table from './pages/table-page';
import TripUpdates from './pages/trip-updates';
export default function App() {
return (
@ -20,9 +20,9 @@ export default function App() {
<NavBar />
<Routes>
<Route path="/" element={<Map />} />
<Route path="/table" element={<Table />} />
<Route path="/map" element={<Map />} />
<Route path="/contact" element={<Contact />} />
<Route path="/map" element={<Map />} />
<Route path="/trip-updates" element={<TripUpdates />} />
</Routes>
</BrowserRouter>
);

32
app/components/input.js Normal file
View File

@ -0,0 +1,32 @@
import React from 'react';
import PropTypes from 'prop-types';
import Form from 'react-bootstrap/Form';
/*controlled component: input form value controlled by React*/
const InputSearch = ({id, name, onChange, placeholder, title, type, value}) => {
return (
<>
<Form.Control
aria-label={title}
className={name}
id={id}
name={name}
onChange={onChange}
placeholder={placeholder}
title={title}
type={type}
value={value}
/>
</>
);
};
export default InputSearch;
InputSearch.propTypes = {
id: PropTypes.string,
value: PropTypes.string,
name: PropTypes.string,
placeholder: PropTypes.string,
title: PropTypes.string,
type: PropTypes.string,
onChange: PropTypes.func
};

View File

@ -5,18 +5,17 @@ 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="/">GTFS Realtime Display</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="mr-auto">
<LinkContainer to="/table">
<Nav.Link>Table</Nav.Link>
<LinkContainer to="/trip-updates">
<Nav.Link>TripUpdates</Nav.Link>
</LinkContainer>
</Nav>
<Nav className="mr-auto">
<LinkContainer to="/map">
<Nav.Link>Map</Nav.Link>
<Nav.Link>VehiclePositions</Nav.Link>
</LinkContainer>
</Nav>
<Nav className="mr-auto">

36
app/components/select.js Normal file
View File

@ -0,0 +1,36 @@
import React from 'react';
import PropTypes from 'prop-types';
import Form from 'react-bootstrap/Form';
/*controlled component: select controlled by React*/
const Select = ({defaultValue, id, name, onChange, options, title}) => {
if (options) {
return (
<Form.Select
aria-label="select table entries per page"
className={name}
defaultValue={defaultValue}
name={name}
id={id}
onChange={onChange}
title={title}
>
{options.map((item, index) => (
<option key={index} value={item}>
{item}
</option>
))}
</Form.Select>
);
} else {
return <p>Select options unavailable.</p>;
}
};
export default Select;
Select.propTypes = {
id: PropTypes.string,
name: PropTypes.string,
defaultValue: PropTypes.number,
onChange: PropTypes.func,
options: PropTypes.array,
title: PropTypes.string
};

View File

@ -0,0 +1,63 @@
import React, { useEffect, useState } from 'react';
import Button from 'react-bootstrap/Button';
import Stack from 'react-bootstrap/Stack';
import PropTypes from 'prop-types';
import Select from './select';
import {selectOptions} from '../utils/select-options';
import TripUpdatesTable from './trip-updates-table';
import Input from './input';
import config from '../config';
export default function TripUpdatesPage(){
/*store and initialise data in function component state*/
const [oset, setOset] = useState(1);
const [limit, setLimit] = useState(parseInt(selectOptions[0],10));
const [searchField, setSearchField] = useState('');
const handleClickPrev = () => {
setOset((oset) => (oset > 1 ? --oset : oset));
};
const handleClickNext = () => {
setOset((oset) => ++oset);
};
const handleChangeLimit = (event) => {
setLimit((limit) => parseInt(event.target.value,10));
};
const handleSearch = (e) => {
setSearchField((searchField)=>e.target.value);
};
return (
<>
<Stack direction="horizontal" gap={1} className="m-1">
<Button variant="secondary" onClick={handleClickPrev}>
prev
</Button>
<Button variant="secondary" onClick={handleClickNext}>
next
</Button>
<Select
defaultValue={selectOptions[0]}
id="tablePageLimit"
name="tablePageLimit"
onChange={handleChangeLimit}
options={selectOptions}
/>
<Input
id="tablePageSearch"
name="tablePageSearch"
onChange={handleSearch}
placeholder="Search table globally"
type="search"
title="Enter search value"
value={searchField}
/>
</Stack>
<TripUpdatesTable
isFetched={false}
oset={oset}
limit={limit}
filter={searchField}
/>
</>
);
};

View File

@ -0,0 +1,36 @@
import React from 'react';
import PropTypes from 'prop-types';
import TripUpdatesEntry from './trip-updates-table-entry';
export default function TripUpdatesTableEntries ({aryData}) {
if (aryData.length > 0) {
//iterate over array
return aryData.map((item, index) => {
if (item.trip) {
//console.log('TripUpdatesTableEntries: trip available');
const trip = item.trip;
return (
<TripUpdatesEntry
tripId={typeof trip.trip_id !== 'undefined' ? trip.trip_id : null}
routeId={typeof trip.route_id !== 'undefined' ? trip.route_id : null}
directionId={typeof trip.direction_id !== 'undefined' ? trip.direction_id : null}
startTime={typeof trip.start_time !== 'undefined' ? trip.start_time : null}
startDate={typeof trip.start_date !== 'undefined' ? trip.start_date : null}
timestamp={typeof item.timestamp !== 'undefined' ? item.timestamp : null}
delay={typeof item.delay !== 'undefined' ? item.delay : null}
key={index}
/>
);
} else {
console.log('ERROR: TripUpdatesTableEntries: REQUIRED trip NOT available');
}
});
}else{
//data is empty
return null;
}
}
TripUpdatesTableEntries.propTypes = {
aryData: PropTypes.array
};

View File

@ -0,0 +1,37 @@
import React from 'react';
import PropTypes from 'prop-types';
/*destructure props object*/
const TripUpdatesTableEntry = ({
tripId,
routeId,
directionId,
startTime,
startDate,
timestamp,
delay
}) => {
return (
<tr>
<td>{tripId}</td>
<td>{routeId}</td>
<td>{directionId}</td>
<td>{startTime}</td>
<td>{startDate}</td>
<td>{timestamp}</td>
<td>{delay}</td>
</tr>
);
};
TripUpdatesTableEntry.propTypes = {
tripId: PropTypes.string,
routeId: PropTypes.string,
directionId: PropTypes.number,
startTime: PropTypes.string,
startDate: PropTypes.string,
timestamp: PropTypes.number,
delay: PropTypes.number
};
export default TripUpdatesTableEntry;

View File

@ -0,0 +1,17 @@
import React from 'react';
const TripUpdatesTableHead = () => {
return (
<tr>
<th>Trip:trip_id</th>
<th>Trip:route_id</th>
<th>Trip:direction_id</th>
<th>Trip:start_time</th>
<th>Trip:start_date</th>
<th>timestamp</th>
<th>delay</th>
</tr>
);
};
export default TripUpdatesTableHead;

View File

@ -0,0 +1,140 @@
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import PropTypes from 'prop-types';
import Table from 'react-bootstrap/Table';
import Alert from 'react-bootstrap/Alert';
import Badge from 'react-bootstrap/Badge';
import {readPbf} from '../utils/gtfs-rt-utils';
import TripUpdatesTableHead from './trip-updates-table-head';
import TripUpdatesTableEntries from './trip-updates-table-entries';
import config from '../config';
import {filterData} from '../utils/filter-data';
/*the simplest way to define a component is to write a JavaScript function*/
/*destructure props object*/
export default function TripUpdatesTable ({isFetched, oset, limit, filter}) {
//////console.log('TripUpdatesTable: oset: ' + oset);
////////console.log('TripUpdatesTable: limit: ' + limit);
const [ary, setAry] = useState([]);
const [aryFiltered, setAryFiltered] = useState([]);
const [fetchCompleted, setFetchCompleted] = useState(isFetched);
const [entityCount, setEntityCount] = useState(0);
const [entityTripUpdateCount, setEntityTripUpdateCount] = useState(0);
/*fetch ary in a JavaScript function*/
const fetch = async () => {
try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
//
const optns = { responseType: 'arraybuffer' };
const address = `${config.API}gtfs-rt-file`;
const res = await axios.get(address, optns);
if(res.data){
//////console.log('fetch() res available');
const rry = readPbf(res.data);
const rryLngth= rry.length;
//console.log('fetch() rryLngh: ' + rryLngth);
setEntityCount((entityCount) => rryLngth);
const rryETU = new Array();
rry.forEach(entity => {
const entityTripUpdate = entity.trip_update;
if (entityTripUpdate) {
rryETU.push(entityTripUpdate);
}
});
const rryETULngth= rryETU.length;
//console.log('fetch() rryETULength: ' + rryETULngth);
setEntityTripUpdateCount((entityTripUpdateCount) => rryETULngth);
const rryOstLmt = new Array();
let j = 0
for(let i = (oset - 1) * limit; i < rryETULngth ; i++){
if(j < limit){
rryOstLmt.push(rryETU[i]);
j++;
}
}
//console.log('fetch() rryOstLmt.length: ' + rryOstLmt.length);
setAry((ary) => rryOstLmt);
let data=filterData(rryOstLmt,'trip_updates',filter);
setAryFiltered((aryFiltered) => data);
}else{
console.error('fetch() res NOT available');
}
} catch (err) {
console.error('err.message: ' + err.message);
setAry((ary) => []);
setAryFiltered((aryFiltered) => []);
}
};
useEffect(()=>{
setAryFiltered((aryFiltered)=>{
let filtered=filterData(ary,'trip_updates',filter);
return filtered;
});
},[filter]);
useEffect(() => {
/*effect goes here*/
fetch();
setFetchCompleted((fetchCompleted)=>true);
//console.log('entityCount: ' + entityCount);
//console.log('entityTripUpdateCount: ' + entityTripUpdateCount);
/*use an empty dependency array to ensure the hook is running only once*/
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/
}, [oset,limit]);
if(fetchCompleted && aryFiltered.length > -1){
/*return a React element*/
return (
<>
<Badge bg="secondary">
abs entity count: {entityCount}&nbsp;
</Badge>
&nbsp;
<Badge bg="secondary">
abs TripUpdate count: {entityTripUpdateCount}&nbsp;
</Badge>
&nbsp;
<Badge bg="secondary">
page trip count: {ary.length}&nbsp;
</Badge>
&nbsp;
<Badge bg="secondary">
filtered trip count: {aryFiltered.length}
</Badge>
&nbsp;
<Table
striped
bordered
hover
size="sm"
variant="dark"
responsive
>
<thead>
<TripUpdatesTableHead />
</thead>
<tbody>
<TripUpdatesTableEntries aryData={aryFiltered} />
</tbody>
</Table>
</>
);
}else{
return (
<Alert variant={'secondary'} onClose={() => setShow(false)} dismissible>
<Badge bg="secondary">TripUpdate</Badge> entities loading...
</Alert>
);
}
}
TripUpdatesTable.propTypes = {
isFetched: PropTypes.bool,
offset: PropTypes.number,
limit: PropTypes.number,
filter: PropTypes.string
};

3
app/config.js Normal file
View File

@ -0,0 +1,3 @@
export default {
API: 'https://v1api.delfi.dp.gtfsr.swingbe.de/'
};

View File

@ -4,32 +4,36 @@ 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.
For questions about this website please do not hesitate to reach out to dialog
(at) swingbe (dot) de.
</p>
<p>
Source code has been made public on{' '}
Source code is controlled and provided using{' '}
<a
href="https://github.com/Software-Ingenieur-Begerad/gtfs-rt-display"
href="https://git.wtf-eg.de/dancesWithCycles/gtfs-rt-display"
target="_blank"
>
GitHub
</a>.
Git
</a>
.
</p>
<h2>Imprint</h2>
<address>
<strong>Software Ingenieur Begerad</strong>
<br />
Lammer Heide 87
<br />
38116 Braunschweig
<br />
Deutschland
<br />
</address>
<h2>Other</h2>
<p>
<a
href="https://www.swingbe.de/imprint"
target="_blank"
>
Imprint
</a>
</p>
<p>
<a
href="https://www.swingbe.de/privacy-policy"
target="_blank"
>
Privacy Policy
</a>
</p>
<p>
Version: {VERSION}
</p>

View File

@ -1,8 +1,10 @@
import React from 'react';
import React, {useState} from 'react';
import axios from 'axios';
export default function Home() {
return (
<>
<h1>Home</h1>
<p>Home</p>
</>
);
}

View File

@ -1,40 +1,53 @@
import React, {useEffect,useState} from 'react';
import axios from 'axios';
import Alert from 'react-bootstrap/Alert';
import Badge from 'react-bootstrap/Badge';
import Map from '../components/map/map';
import parseMessages from '../utils/gtfs-rt-utils';
import {parseMessages, readPbf} from '../utils/gtfs-rt-utils';
import config from '../config';
export default function MapPage() {
/*storage*/
const [vehPos, setVehPos] = useState([]);
const getData= async ()=>{
//console.log('getData() start...');
////console.log('getData() start...');
try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
//TODO Make fields available via configuration!
let url = 'https://api.entur.io/realtime/v1/gtfs-rt/vehicle-positions';
const res = await axios.get(url,
const address = `${config.API}gtfs-rt`;
const res = await axios.get(address,
{
responseType: 'arraybuffer'
//responseType: 'blob'
});
if(res.data){
//TODO remove debugging
//console.log('getData() res available');
/*parse messages*/
const messages = parseMessages(res.data);
//console.log('getData() messages.length: '+messages.length);
//console.log('getRry() res available');
const rry = readPbf(res.data);
const rryLngth= rry.length;
//console.log('getRry() rryLngh: ' + rryLngth);
const rryEVP = new Array();
let countEntityVehiclePositions = 0;
rry.forEach(entity => {
const entityVehiclePosition = entity.vehicle;
if (entityVehiclePosition) {
rryEVP.push(entityVehiclePosition);
countEntityVehiclePositions++;
}
});
//console.log('getRry() countEntityVehiclePositions: ' + countEntityVehiclePositions);
/*set state*/
setVehPos(messages);
setVehPos((messages) => rryEVP);
}else{
console.error('getData() res NOT available');
console.error('getRry() res NOT available');
}
} catch (err) {
console.error('err.message: ' + err.message);
}
//console.log('getData() done.');
////console.log('getData() done.');
};
useEffect(()=>{
@ -51,9 +64,19 @@ export default function MapPage() {
clearInterval(intervalCall);
};
},[]);
if (vehPos.length < 0) {
return (
<>
<Map messages={vehPos}/>
</>
);
} else {
return (
<Alert variant={'secondary'} onClose={() => setShow(false)} dismissible>
<Badge bg="secondary">VehiclePostion</Badge> entities loading...
</Alert>
);
}
}

View File

@ -1,8 +1,9 @@
import React, {useEffect,useState} from 'react';
import axios from 'axios';
import parseMessages from '../utils/gtfs-rt-utils';
import {parseMessages, readPbf} from '../utils/gtfs-rt-utils';
import Table from '../components/table/table';
export default function TablePage() {
const [vehPos, setVehPos] = useState([]);
const getData= async ()=>{
@ -10,13 +11,16 @@ export default function TablePage() {
try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
//TODO Make fields available via configuration!
let url = 'https://api.entur.io/realtime/v1/gtfs-rt/vehicle-positions';
let url = 'https://v1api.connect.dp.gtfsr.swingbe.de/gtfs-rt-file'
const res = await axios.get(url,
{
responseType: 'arraybuffer'
});
if(res.data){
//console.log('getData() res available');
/*parse messages*/
readPbf(res.data);
const messages = parseMessages(res.data);
//console.log('getData() messages.length: '+messages.length);
setVehPos(messages);

View File

@ -0,0 +1,9 @@
import React from 'react';
import TripUpdatesPage from '../components/trip-updates-page';
export default function TripUpdates() {
return (
<>
<TripUpdatesPage />
</>
);
};

216
app/utils/filter-data.js Normal file
View File

@ -0,0 +1,216 @@
function filterData(data, name,filter){
if(data.length>0){
//console.log('filterData() data.length: '+data.length);
//console.log('filterData() name: '+name);
//console.log('filterData() filter:'+filter);
switch(name){
case 'fare_zones_history':
//TODO implement
console.log('filterData() //TODO implement fare_zones_history');
return data;
break;
case 'tdb_fare_zones':
return data.filter((item, index) => {
return (
item.id.toLowerCase().includes(filter.toLowerCase()) ||
(item.external!==null && item.external.toLowerCase().includes(filter.toLowerCase())) ||
(item.internal!==null && item.internal.toLowerCase().includes(filter.toLowerCase())) ||
(item.name!==null && item.name.toLowerCase().includes(filter.toLowerCase())) ||
(item.short_name!==null && item.short_name.toLowerCase().includes(filter.toLowerCase())) ||
(item.type!==null && item.type.toLowerCase().includes(filter.toLowerCase())) ||
(item.valid_from!==null && item.valid_from.toLowerCase().includes(filter.toLowerCase())) ||
(item.valid_until!==null && item.valid_until.toLowerCase().includes(filter.toLowerCase()))
);
});
break;
case 'lct_msg':
return data.filter((item, index) => {
return (
item.bs_id.toLowerCase().includes(filter.toLowerCase()) ||
(item.vc_trip!==null && item.vc_trip.toLowerCase().includes(filter.toLowerCase())) ||
(item.vc_route!==null && item.vc_route.toLowerCase().includes(filter.toLowerCase())) ||
(item.vc_tenant!==null && item.vc_tenant.toLowerCase().includes(filter.toLowerCase())) ||
(item.vc_date!==null && item.vc_date.toLowerCase().includes(filter.toLowerCase())) ||
(item.vc_time!==null && item.vc_time.toLowerCase().includes(filter.toLowerCase())) ||
(item.vc_lon!==null && item.vc_lon.toLowerCase().includes(filter.toLowerCase())) ||
(item.vc_lat!==null && item.vc_lat.toLowerCase().includes(filter.toLowerCase()))
);
});
break;
case 'localization':
return data.filter((item, index) => {
return (
item.localization_id.toLowerCase().includes(filter.toLowerCase()) ||
(item.name!==null && item.name.toLowerCase().includes(filter.toLowerCase())) ||
(item.lang_de!==null && item.lang_de.toLowerCase().includes(filter.toLowerCase())) ||
(item.lang_en!==null && item.lang_en.toLowerCase().includes(filter.toLowerCase())) ||
(item.version_id!==null && item.version_id.toLowerCase().includes(filter.toLowerCase()))
);
});
break;
case 'relations':
return data.filter((item, index) => {
return (
(item.dtype!==null && item.dtype.toLowerCase().includes(filter.toLowerCase())) ||
(item.id!==null && item.id.toLowerCase().includes(filter.toLowerCase())) ||
(item.active!=null && item.active.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.direct_purchase!==null && item.direct_purchase.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.disabled!==null && item.disabled.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.info!==null && item.info.toLowerCase().includes(filter.toLowerCase())) ||
(item.last_modified!==null && item.last_modified.toLowerCase().includes(filter.toLowerCase())) ||
(item.price_level!==null && item.price_level.toLowerCase().includes(filter.toLowerCase())) ||
(item.start_zone!==null && item.start_zone.toLowerCase().includes(filter.toLowerCase())) ||
(item.target_zone!==null && item.target_zone.toLowerCase().includes(filter.toLowerCase())) ||
(item.valid_from!==null && item.valid_from.toLowerCase().includes(filter.toLowerCase())) ||
(item.valid_until!==null && item.valid_until.toLowerCase().includes(filter.toLowerCase())) ||
(item.via_name!==null && item.via_name.toLowerCase().includes(filter.toLowerCase())) ||
(item.via_fare_zone!==null && item.via_fare_zone.toLowerCase().includes(filter.toLowerCase())) ||
(item.zones!==null && item.zones.toLowerCase().includes(filter.toLowerCase())) ||
(item.matching_via_id!==null && item.matching_via_id.toLowerCase().includes(filter.toLowerCase())) ||
(item.variant!==null && item.variant.toLowerCase().includes(filter.toLowerCase())) ||
(item.created_user_id!==null && item.created_user_id.toLowerCase().includes(filter.toLowerCase())) ||
(item.last_modified_user_id!==null && item.last_modified_user_id.toLowerCase().includes(filter.toLowerCase())) ||
(item.comment!== null && item.comment.toLowerCase().includes(filter.toLowerCase())) ||
(item.reverse_direction_id!== null && item.reverse_direction_id.toLowerCase().includes(filter.toLowerCase())) ||
(item.via_id!==null && item.via_id.toLowerCase().includes(filter.toLowerCase())) ||
(item.all_transit_zones!=null && item.all_transit_zones.toString().toLowerCase().includes(filter.toLowerCase()))
);
});
break;
case 'price_levels':
return data.filter((item, index) => {
return (
item.price_level_id.toString().toLowerCase().includes(filter.toLowerCase()) ||
(item.short_name!==null && item.short_name.toLowerCase().includes(filter.toLowerCase())) ||
(item.name!==null && item.name.toLowerCase().includes(filter.toLowerCase()))
);
});
break;
case 'price':
return data.filter((item, index) => {
return (
item.price_id.toString().toLowerCase().includes(filter.toLowerCase()) ||
(item.product_id!==null && item.product_id.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.price_level_id!==null && item.price_level_id.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.id_ticket!==null && item.id_ticket.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.price!==null && item.price.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.duration!==null && item.duration.toLowerCase().includes(filter.toLowerCase())) ||
(item.priority!==null && item.priority.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.localization_id4add_info!==null && item.localization_id4add_info.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.localization_id4ticket_descr!==null && item.localization_id4ticket_descr.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.localization_id4sale_text1!==null && item.localization_id4sale_text1.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.localization_id4sale_text2!==null && item.localization_id4sale_text2.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.localization_id4ticket_note1!==null && item.localization_id4ticket_note1.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.localization_id4ticket_note2!==null && item.localization_id4ticket_note2.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.localization_id4note_lang!==null && item.localization_id4note_lang.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.filter_code!==null && item.filter_code.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.version_id!==null && item.version_id.toString().toLowerCase().includes(filter.toLowerCase()))
);
});
break;
case 'product':
return data.filter((item, index) => {
return (
item.product_id.toString().toLowerCase().includes(filter.toLowerCase()) ||
(item.id_prod!==null && item.id_prod.toLowerCase().includes(filter.toLowerCase())) ||
(item.ext_prod_localization_id!==null && item.ext_prod_localization_id.toLowerCase().includes(filter.toLowerCase())) ||
(item.info_prod_localization_id!==null && item.info_prod_localization_id.toLowerCase().includes(filter.toLowerCase()))
);
});
break;
case 'sales_parameter':
return data.filter((item, index) => {
return (
item.sales_parameter_id.toString().includes(filter.toLowerCase()) ||
(item.product_id!==null && item.product_id.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.cnt_presale_days!==null && item.cnt_presale_days.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.app_prsnt_after_val!==null && item.app_prsnt_after_val.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.bday!==null && item.bday.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.num_add_names!==null && item.num_add_names.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.val_type!==null && item.val_type.toLowerCase().includes(filter.toLowerCase())) ||
(item.val_days!==null && item.val_days.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.val_beg_m_f!==null && item.val_beg_m_f.toLowerCase().includes(filter.toLowerCase())) ||
(item.val_beg_s_s!==null && item.val_beg_s_s.toLowerCase().includes(filter.toLowerCase())) ||
(item.val_end!==null && item.val_end.toLowerCase().includes(filter.toLowerCase())) ||
(item.version_id!==null && item.version_id.toString().toLowerCase().includes(filter.toLowerCase()))
);
});
break;
case 'tdb_stops':
return data.filter((item, index) => {
return (
item.id.toLowerCase().includes(filter.toLowerCase()) ||
(item.active!==null && item.active.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.lon!==null && item.lon.toLowerCase().includes(filter.toLowerCase())) ||
(item.location!==null && item.location.toLowerCase().includes(filter.toLowerCase())) ||
(item.lat!==null && item.lat.toLowerCase().includes(filter.toLowerCase())) ||
(item.stop_long_name!==null && item.stop_long_name.toLowerCase().includes(filter.toLowerCase())) ||
(item.stop_name!==null && item.stop_name.toLowerCase().includes(filter.toLowerCase())) ||
(item.stop_name_extern!==null && item.stop_name_extern.toLowerCase().includes(filter.toLowerCase())) ||
(item.fare_zone_1!==null && item.fare_zone_1.toLowerCase().includes(filter.toLowerCase())) ||
(item.fare_zone_2!==null && item.fare_zone_2.toLowerCase().includes(filter.toLowerCase())) ||
(item.fare_zone_3!==null && item.fare_zone_3.toLowerCase().includes(filter.toLowerCase())) ||
(item.fare_zone_4!==null && item.fare_zone_4.toLowerCase().includes(filter.toLowerCase())) ||
(item.valid_from!==null && item.valid_from.toLowerCase().includes(filter.toLowerCase())) ||
(item.valid_until!==null && item.valid_until.toLowerCase().includes(filter.toLowerCase())) ||
(item.last_modified!==null && item.last_modified.toLowerCase().includes(filter.toLowerCase()))
);
});
break;
case 'ticket-selection':
return data.filter((item,index)=>{
return (
item.price_id.toString().toLowerCase().includes(filter.toLowerCase()) ||
(item.ext_prod_de!==null && item.ext_prod_de.toLowerCase().includes(filter.toLowerCase())) ||
(item.id_ticket!==null && item.id_ticket.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.price!==null && item.price.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.name!==null && item.name.toLowerCase().includes(filter.toLowerCase())) ||
(item.short_name!==null && item.short_name.toLowerCase().includes(filter.toLowerCase())) ||
(item.priority!==null && item.priority.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.duration!==null && item.duration.toLowerCase().includes(filter.toLowerCase())) ||
(item.val_beg_m_f!==null && item.val_beg_m_f.toLowerCase().includes(filter.toLowerCase())) ||
(item.val_beg_s_s!==null && item.val_beg_s_s.toLowerCase().includes(filter.toLowerCase())) ||
(item.val_end!==null && item.val_end.toLowerCase().includes(filter.toLowerCase()))
);
});
break;
case 'trip_updates':
return data.filter((item, index) => {
return (
(item.trip.trip_id !== null && item.trip.trip_id.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.trip.route_id !== null && item.trip.route_id.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.trip.direction_id !== null && item.trip.direction_id.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.trip.start_time !== null && item.trip.start_time.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.trip.start_date !== null && item.trip.start_date.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.timestamp !== null && item.timestamp.toString().toLowerCase().includes(filter.toLowerCase())) ||
(item.delay !== null && item.delay.toString().toLowerCase().includes(filter.toLowerCase()))
);
});
case 'tokens':
return data;
break;
case 'users':
console.log('filterData() //TODO implement users');
return data;
break;
case 'versions':
return data.filter((item, index) => {
return (
item.version_id.toString().toLowerCase().includes(filter.toLowerCase()) ||
(item.name!==null && item.name.toLowerCase().includes(filter.toLowerCase())) ||
(item.valid_from!==null && item.valid_from.toLowerCase().includes(filter.toLowerCase())) ||
(item.valid_until!==null && item.valid_until.toLowerCase().includes(filter.toLowerCase())) ||
(item.descr!==null && item.descr.toLowerCase().includes(filter.toLowerCase()))
);
});
break;
default:
console.error(`filterData() name: ${name} unknown`);
return data;
}
}
return data;
};
module.exports = {
filterData
};

View File

@ -1,41 +1,58 @@
import Pbf from 'pbf';
import { FeedMessage } from './gtfs-rt.js';
import charIntoString from './string';
//import charIntoString from './string';
export default function parseMessages(buffer){
//console.log('parseMessages() start...');
/**
* parse buffer into array of entity objects
*/
export function parseMessages(buffer){
////console.log('parseMessages() start...');
const messages = [];
const pbf = new Pbf(buffer);
const feed = FeedMessage.read(pbf);
//console.log('parseMessages() feed:'+JSON.stringify(feed));
////console.log('parseMessages() feed:'+JSON.stringify(feed));
let countEntityAlert = 0;
let countEntityShape = 0;
let countEntityTripUpdate = 0;
let countEntityVehicle = 0;
feed.entity.forEach(entity => {
/*Data about the realtime position of a vehicle.*/
const vehiclePos = entity.vehicle;
if (vehiclePos) {
//console.log('getVehPos() vehiclePos available');
const entityAlert = entity.alert;
const entityShape = entity.shape;
const entityTripUpdate = entity.trip_update;
const entityVehicle = entity.vehicle;
if (entityAlert) {
countEntityAlert++;
} else if (entityShape) {
countEntityShape++;
} else if (entityTripUpdate) {
countEntityTripUpdate++;
}else if (entityVehicle) {
countEntityVehicle++;
////console.log('parseMessage() entityVehicle available');
/*The Trip that this vehicle is serving.*/
const trip=vehiclePos.trip;
const trip=entityVehicle.trip;
/*Additional information on the vehicle that is serving this trip.*/
const vehicle=vehiclePos.vehicle;
const vehicle=entityVehicle.vehicle;
/*Current position of this vehicle.*/
const position=vehiclePos.position;
const position=entityVehicle.position;
/*Moment at which the vehicle's position was measured. In POSIX time (i.e., number of seconds since January 1st 1970 00:00:00 UTC).*/
const vehPosTimestamp=vehiclePos.timestamp;
const vehPosTimestamp=entityVehicle.timestamp;
//remove tailing dot
//match a dot when it is followed by a whitespace or the end of the string
/*TODO Is this precaution required?*/
//TODO Handle error! Placing a decimal point at a fixed place does not work in general!
//let posLat=position.latitude;
//console.log(`getVehPos() posLat:${posLat}`);
////console.log(`parseMessage() posLat:${posLat}`);
//let latFormed = position.latitude === undefined ? -360 : position.latitude.toString().replace(/\.+$/, "");
//console.log(`getVehPos() latFormed:${latFormed}`);
////console.log(`parseMessage() latFormed:${latFormed}`);
//latFormed=charIntoString(latFormed,latFormed.length - 7,'.');
//console.log(`getVehPos() latFormed:${latFormed}`);
////console.log(`parseMessage() latFormed:${latFormed}`);
//let lonFormed = position.longitude === undefined ? -720 : position.longitude.toString().replace(/\.+$/, "");
//lonFormed=charIntoString(lonFormed,lonFormed.length - 7,'.');
//console.log(`getVehPos() lonFormed:${lonFormed}`);
////console.log(`parseMessage() lonFormed:${lonFormed}`);
const now= new Date();
const message={
/*Version of the feed specification. The current version is 2.0.*/
@ -61,9 +78,27 @@ export default function parseMessages(buffer){
};
messages.push(message);
} else {
console.error('getVehPos() vehiclePos NOT available');
console.error('ERROR: parseMessage() entity NOT known');
}
});
//console.log('parseMessages() done.');
//console.log('parseMessages() countEntityAlert: ' + countEntityAlert);
//console.log('parseMessages() countEntityShape: ' + countEntityShape);
//console.log('parseMessages() countEntityTripUpdate: ' + countEntityTripUpdate);
//console.log('parseMessages() countEntityVehicle: ' + countEntityVehicle);
////console.log('parseMessages() done.');
return messages;
};
/**
* read pbf file
* @return array of GTFS RT entities
*/
export function readPbf(buffer){
//console.log('readPbfs() start...');
const messages = [];
const pbf = new Pbf(buffer);
const feed = FeedMessage.read(pbf);
//console.log('readPbfs() length: ' + feed.entity.length);
//console.log('readPbfs() done.');
return feed.entity;
};

View File

@ -0,0 +1,4 @@
const selectOptions = [5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 25000, 50000, 100000];
module.exports = {
selectOptions
};

290
package-lock.json generated
View File

@ -1,22 +1,22 @@
{
"name": "gtfs-rt-display",
"version": "0.3.0",
"version": "0.5.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "gtfs-rt-display",
"version": "0.3.0",
"version": "0.5.0",
"license": "GPL-3.0-or-later",
"dependencies": {
"axios": "0.27.2",
"bootstrap": "5.2.1",
"leaflet": "1.8.0",
"pbf": "^3.2.1",
"axios": "1.4.0",
"bootstrap": "5.3.1",
"leaflet": "1.9.4",
"pbf": "3.2.1",
"react": "18.2.0",
"react-bootstrap": "2.5.0",
"react-bootstrap": "2.8.0",
"react-dom": "18.2.0",
"react-leaflet": "4.0.2",
"react-leaflet": "4.2.1",
"react-router-bootstrap": "0.26.2"
},
"devDependencies": {
@ -34,7 +34,7 @@
"webpack-merge": "5.8.0"
},
"engines": {
"node": ">=10"
"node": "<=18.17.0"
}
},
"node_modules/@ampproject/remapping": {
@ -1721,11 +1721,11 @@
}
},
"node_modules/@babel/runtime": {
"version": "7.19.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.0.tgz",
"integrity": "sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==",
"version": "7.22.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz",
"integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==",
"dependencies": {
"regenerator-runtime": "^0.13.4"
"regenerator-runtime": "^0.13.11"
},
"engines": {
"node": ">=6.9.0"
@ -1877,31 +1877,31 @@
"dev": true
},
"node_modules/@popperjs/core": {
"version": "2.11.6",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@react-aria/ssr": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.3.0.tgz",
"integrity": "sha512-yNqUDuOVZIUGP81R87BJVi/ZUZp/nYOBXbPsRe7oltJOfErQZD+UezMpw4vM2KRz18cURffvmC8tJ6JTeyDtaQ==",
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.7.0.tgz",
"integrity": "sha512-bfufjg4ESE5giN+Fxj1XIzS5f/YIhqcGc+Ve+vUUKU8xZ8t/Xtjlv8F3kjqDBQdk//n3mluFY7xG1wQVB9rMLQ==",
"dependencies": {
"@babel/runtime": "^7.6.2"
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
}
},
"node_modules/@react-leaflet/core": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.0.1.tgz",
"integrity": "sha512-XGmx01DovDt0IWsW4tqeuSYifpY19aUn9NYCqTBI3KNtjbCjj0pfiWa7krNsnJ6l2oQbv4Nt0/BabLbIvT4ocA==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz",
"integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==",
"peerDependencies": {
"leaflet": "^1.8.0",
"leaflet": "^1.9.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
@ -1916,29 +1916,29 @@
}
},
"node_modules/@restart/hooks": {
"version": "0.4.7",
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.7.tgz",
"integrity": "sha512-ZbjlEHcG+FQtpDPHd7i4FzNNvJf2enAwZfJbpM8CW7BhmOAbsHpZe3tsHwfQUrBuyrxWqPYp2x5UMnilWcY22A==",
"version": "0.4.11",
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.11.tgz",
"integrity": "sha512-Ft/ncTULZN6ldGHiF/k5qt72O8JyRMOeg0tApvCni8LkoiEahO+z3TNxfXIVGy890YtWVDvJAl662dVJSJXvMw==",
"dependencies": {
"dequal": "^2.0.2"
"dequal": "^2.0.3"
},
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/@restart/ui": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.4.0.tgz",
"integrity": "sha512-5dDj5uDzUgK1iijWPRg6AnxjkHM04XhTQDJirM1h/8tIc7KyLtF9YyjcCpNEn259hPMXswpkfXKNgiag0skPFg==",
"version": "1.6.6",
"resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.6.6.tgz",
"integrity": "sha512-eC3puKuWE1SRYbojWHXnvCNHGgf3uzHCb6JOhnF4OXPibOIPEkR1sqDSkL643ydigxwh+ruCa1CmYHlzk7ikKA==",
"dependencies": {
"@babel/runtime": "^7.18.3",
"@popperjs/core": "^2.11.5",
"@react-aria/ssr": "^3.2.0",
"@restart/hooks": "^0.4.7",
"@babel/runtime": "^7.21.0",
"@popperjs/core": "^2.11.6",
"@react-aria/ssr": "^3.5.0",
"@restart/hooks": "^0.4.9",
"@types/warning": "^3.0.0",
"dequal": "^2.0.2",
"dequal": "^2.0.3",
"dom-helpers": "^5.2.0",
"uncontrollable": "^7.2.1",
"uncontrollable": "^8.0.1",
"warning": "^4.0.3"
},
"peerDependencies": {
@ -1946,6 +1946,22 @@
"react-dom": ">=16.14.0"
}
},
"node_modules/@restart/ui/node_modules/uncontrollable": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.2.tgz",
"integrity": "sha512-/GDx+K1STGtpgTsj5Dj3J51YaKxZDblbCQHTH1zHLuoBEWodj6MjtRVv3TUijj1JYLRLSFsFzN8NV4M3QV4d9w==",
"peerDependencies": {
"react": ">=16.14.0"
}
},
"node_modules/@swc/helpers": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz",
"integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==",
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@types/body-parser": {
"version": "1.19.2",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
@ -2504,12 +2520,13 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
"integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
"dependencies": {
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/babel-loader": {
@ -2679,9 +2696,9 @@
"dev": true
},
"node_modules/bootstrap": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.1.tgz",
"integrity": "sha512-UQi3v2NpVPEi1n35dmRRzBJFlgvWHYwyem6yHhuT6afYF+sziEt46McRbT//kVXZ7b1YUYEVGdXEH74Nx3xzGA==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.1.tgz",
"integrity": "sha512-jzwza3Yagduci2x0rr9MeFSORjcHpt0lRZukZPZQJT1Dth5qzV7XcgGqYzi39KGAVYR8QEDVoO0ubFKOxzMG+g==",
"funding": [
{
"type": "github",
@ -2693,7 +2710,7 @@
}
],
"peerDependencies": {
"@popperjs/core": "^2.11.6"
"@popperjs/core": "^2.11.8"
}
},
"node_modules/brace-expansion": {
@ -4441,9 +4458,9 @@
}
},
"node_modules/leaflet": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.8.0.tgz",
"integrity": "sha512-gwhMjFCQiYs3x/Sf+d49f10ERXaEFCPr+nVTryhAW8DWbMGqJqt9G4XuIaHmFW08zYvhgdzqXGr8AlW8v8dQkA=="
"version": "1.9.4",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA=="
},
"node_modules/loader-runner": {
"version": "4.3.0",
@ -5194,6 +5211,11 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
@ -5272,20 +5294,20 @@
}
},
"node_modules/react-bootstrap": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.5.0.tgz",
"integrity": "sha512-j/aLR+okzbYk61TM3eDOU1NqOqnUdwyVrF+ojoCRUxPdzc2R0xXvqyRsjSoyRoCo7n82Fs/LWjPCin/QJNdwvA==",
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.8.0.tgz",
"integrity": "sha512-e/aNtxl0Z2ozrIaR82jr6Zz7ss9GSoaXpQaxmvtDUsTZIq/XalkduR/ZXP6vbQHz2T4syvjA+4FbtwELxxmpww==",
"dependencies": {
"@babel/runtime": "^7.17.2",
"@restart/hooks": "^0.4.6",
"@restart/ui": "^1.3.1",
"@types/react-transition-group": "^4.4.4",
"classnames": "^2.3.1",
"@babel/runtime": "^7.21.0",
"@restart/hooks": "^0.4.9",
"@restart/ui": "^1.6.3",
"@types/react-transition-group": "^4.4.5",
"classnames": "^2.3.2",
"dom-helpers": "^5.2.1",
"invariant": "^2.2.4",
"prop-types": "^15.8.1",
"prop-types-extra": "^1.1.0",
"react-transition-group": "^4.4.2",
"react-transition-group": "^4.4.5",
"uncontrollable": "^7.2.1",
"warning": "^4.0.3"
},
@ -5318,14 +5340,14 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/react-leaflet": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.0.2.tgz",
"integrity": "sha512-pDyIf50Ek/LZ0p4Qxw2D9LMOx+LytQs2R+MY3mhTUPGzubrpP4QAgbCv2qc0L9y0qVRgx7dHavWuPFkc/WCJCw==",
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz",
"integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==",
"dependencies": {
"@react-leaflet/core": "^2.0.1"
"@react-leaflet/core": "^2.1.0"
},
"peerDependencies": {
"leaflet": "^1.8.0",
"leaflet": "^1.9.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
@ -5450,9 +5472,9 @@
}
},
"node_modules/regenerator-runtime": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
"node_modules/regenerator-transform": {
"version": "0.15.0",
@ -6181,8 +6203,7 @@
"node_modules/tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
"dev": true
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"node_modules/type-is": {
"version": "1.6.18",
@ -7912,11 +7933,11 @@
}
},
"@babel/runtime": {
"version": "7.19.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.0.tgz",
"integrity": "sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==",
"version": "7.22.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz",
"integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==",
"requires": {
"regenerator-runtime": "^0.13.4"
"regenerator-runtime": "^0.13.11"
}
},
"@babel/template": {
@ -8038,22 +8059,22 @@
"dev": true
},
"@popperjs/core": {
"version": "2.11.6",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw=="
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
},
"@react-aria/ssr": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.3.0.tgz",
"integrity": "sha512-yNqUDuOVZIUGP81R87BJVi/ZUZp/nYOBXbPsRe7oltJOfErQZD+UezMpw4vM2KRz18cURffvmC8tJ6JTeyDtaQ==",
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.7.0.tgz",
"integrity": "sha512-bfufjg4ESE5giN+Fxj1XIzS5f/YIhqcGc+Ve+vUUKU8xZ8t/Xtjlv8F3kjqDBQdk//n3mluFY7xG1wQVB9rMLQ==",
"requires": {
"@babel/runtime": "^7.6.2"
"@swc/helpers": "^0.5.0"
}
},
"@react-leaflet/core": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.0.1.tgz",
"integrity": "sha512-XGmx01DovDt0IWsW4tqeuSYifpY19aUn9NYCqTBI3KNtjbCjj0pfiWa7krNsnJ6l2oQbv4Nt0/BabLbIvT4ocA==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz",
"integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==",
"requires": {}
},
"@remix-run/router": {
@ -8063,27 +8084,43 @@
"peer": true
},
"@restart/hooks": {
"version": "0.4.7",
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.7.tgz",
"integrity": "sha512-ZbjlEHcG+FQtpDPHd7i4FzNNvJf2enAwZfJbpM8CW7BhmOAbsHpZe3tsHwfQUrBuyrxWqPYp2x5UMnilWcY22A==",
"version": "0.4.11",
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.11.tgz",
"integrity": "sha512-Ft/ncTULZN6ldGHiF/k5qt72O8JyRMOeg0tApvCni8LkoiEahO+z3TNxfXIVGy890YtWVDvJAl662dVJSJXvMw==",
"requires": {
"dequal": "^2.0.2"
"dequal": "^2.0.3"
}
},
"@restart/ui": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.4.0.tgz",
"integrity": "sha512-5dDj5uDzUgK1iijWPRg6AnxjkHM04XhTQDJirM1h/8tIc7KyLtF9YyjcCpNEn259hPMXswpkfXKNgiag0skPFg==",
"version": "1.6.6",
"resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.6.6.tgz",
"integrity": "sha512-eC3puKuWE1SRYbojWHXnvCNHGgf3uzHCb6JOhnF4OXPibOIPEkR1sqDSkL643ydigxwh+ruCa1CmYHlzk7ikKA==",
"requires": {
"@babel/runtime": "^7.18.3",
"@popperjs/core": "^2.11.5",
"@react-aria/ssr": "^3.2.0",
"@restart/hooks": "^0.4.7",
"@babel/runtime": "^7.21.0",
"@popperjs/core": "^2.11.6",
"@react-aria/ssr": "^3.5.0",
"@restart/hooks": "^0.4.9",
"@types/warning": "^3.0.0",
"dequal": "^2.0.2",
"dequal": "^2.0.3",
"dom-helpers": "^5.2.0",
"uncontrollable": "^7.2.1",
"uncontrollable": "^8.0.1",
"warning": "^4.0.3"
},
"dependencies": {
"uncontrollable": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.2.tgz",
"integrity": "sha512-/GDx+K1STGtpgTsj5Dj3J51YaKxZDblbCQHTH1zHLuoBEWodj6MjtRVv3TUijj1JYLRLSFsFzN8NV4M3QV4d9w==",
"requires": {}
}
}
},
"@swc/helpers": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz",
"integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==",
"requires": {
"tslib": "^2.4.0"
}
},
"@types/body-parser": {
@ -8589,12 +8626,13 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"axios": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
"integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
"requires": {
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"babel-loader": {
@ -8732,9 +8770,9 @@
"dev": true
},
"bootstrap": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.1.tgz",
"integrity": "sha512-UQi3v2NpVPEi1n35dmRRzBJFlgvWHYwyem6yHhuT6afYF+sziEt46McRbT//kVXZ7b1YUYEVGdXEH74Nx3xzGA==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.1.tgz",
"integrity": "sha512-jzwza3Yagduci2x0rr9MeFSORjcHpt0lRZukZPZQJT1Dth5qzV7XcgGqYzi39KGAVYR8QEDVoO0ubFKOxzMG+g==",
"requires": {}
},
"brace-expansion": {
@ -10026,9 +10064,9 @@
"dev": true
},
"leaflet": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.8.0.tgz",
"integrity": "sha512-gwhMjFCQiYs3x/Sf+d49f10ERXaEFCPr+nVTryhAW8DWbMGqJqt9G4XuIaHmFW08zYvhgdzqXGr8AlW8v8dQkA=="
"version": "1.9.4",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA=="
},
"loader-runner": {
"version": "4.3.0",
@ -10586,6 +10624,11 @@
}
}
},
"proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
@ -10645,20 +10688,20 @@
}
},
"react-bootstrap": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.5.0.tgz",
"integrity": "sha512-j/aLR+okzbYk61TM3eDOU1NqOqnUdwyVrF+ojoCRUxPdzc2R0xXvqyRsjSoyRoCo7n82Fs/LWjPCin/QJNdwvA==",
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.8.0.tgz",
"integrity": "sha512-e/aNtxl0Z2ozrIaR82jr6Zz7ss9GSoaXpQaxmvtDUsTZIq/XalkduR/ZXP6vbQHz2T4syvjA+4FbtwELxxmpww==",
"requires": {
"@babel/runtime": "^7.17.2",
"@restart/hooks": "^0.4.6",
"@restart/ui": "^1.3.1",
"@types/react-transition-group": "^4.4.4",
"classnames": "^2.3.1",
"@babel/runtime": "^7.21.0",
"@restart/hooks": "^0.4.9",
"@restart/ui": "^1.6.3",
"@types/react-transition-group": "^4.4.5",
"classnames": "^2.3.2",
"dom-helpers": "^5.2.1",
"invariant": "^2.2.4",
"prop-types": "^15.8.1",
"prop-types-extra": "^1.1.0",
"react-transition-group": "^4.4.2",
"react-transition-group": "^4.4.5",
"uncontrollable": "^7.2.1",
"warning": "^4.0.3"
}
@ -10678,11 +10721,11 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"react-leaflet": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.0.2.tgz",
"integrity": "sha512-pDyIf50Ek/LZ0p4Qxw2D9LMOx+LytQs2R+MY3mhTUPGzubrpP4QAgbCv2qc0L9y0qVRgx7dHavWuPFkc/WCJCw==",
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz",
"integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==",
"requires": {
"@react-leaflet/core": "^2.0.1"
"@react-leaflet/core": "^2.1.0"
}
},
"react-lifecycles-compat": {
@ -10772,9 +10815,9 @@
}
},
"regenerator-runtime": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
"regenerator-transform": {
"version": "0.15.0",
@ -11320,8 +11363,7 @@
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
"dev": true
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"type-is": {
"version": "1.6.18",

View File

@ -2,7 +2,7 @@
"private": true,
"name": "gtfs-rt-display",
"description": "display data from GTFS Realtime feeds",
"version": "0.3.0",
"version": "0.5.0",
"main": "index.js",
"keywords": [
"public",
@ -13,12 +13,12 @@
"display"
],
"author": "Software Ingenieur Begerad <dialog@SwIngBe.de>",
"homepage": "https://github.com/Software-Ingenieur-Begerad/gtfs-rt-display/tree/main",
"repository": "https://github.com/Software-Ingenieur-Begerad/gtfs-rt-display",
"bugs": "https://github.com/Software-Ingenieur-Begerad/gtfs-rt-display/issues",
"homepage": "https://www.swingbe.de/activity/gtfs-rt-display/",
"repository": "https://git.wtf-eg.de/dancesWithCycles/gtfs-rt-display",
"bugs": "https://git.wtf-eg.de/dancesWithCycles/gtfs-rt-display/issues",
"license": "GPL-3.0-or-later",
"engines": {
"node": ">=10"
"node": "<=18.17.0"
},
"scripts": {
"start": "webpack serve --config config/webpack.dev.js",
@ -39,14 +39,14 @@
"webpack-merge": "5.8.0"
},
"dependencies": {
"axios": "0.27.2",
"bootstrap": "5.2.1",
"leaflet": "1.8.0",
"pbf": "^3.2.1",
"axios": "1.4.0",
"bootstrap": "5.3.1",
"leaflet": "1.9.4",
"pbf": "3.2.1",
"react": "18.2.0",
"react-bootstrap": "2.5.0",
"react-bootstrap": "2.8.0",
"react-dom": "18.2.0",
"react-leaflet": "4.0.2",
"react-leaflet": "4.2.1",
"react-router-bootstrap": "0.26.2"
}
}