diff --git a/backend/src/app.controller.spec.ts b/backend/src/app.controller.spec.ts index f739a12a..41d1d33b 100644 --- a/backend/src/app.controller.spec.ts +++ b/backend/src/app.controller.spec.ts @@ -4,7 +4,7 @@ import { AppService } from './app.service'; import { KnexModule } from 'nestjs-knex'; import { ConfigModule, ConfigService } from '@nestjs/config'; -import { DB_LIRAMAP_CONFIG, POSTGIS_DB_CONFIG } from './database'; +import { DB_LIRAMAP_CONFIG } from './database'; const database = (config: any, name: string) => { return KnexModule.forRootAsync( @@ -22,7 +22,6 @@ describe('AppController', () => { const app: TestingModule = await Test.createTestingModule({ imports: [ ConfigModule.forRoot(), - database(POSTGIS_DB_CONFIG, 'postgis'), database(DB_LIRAMAP_CONFIG, 'lira-map'), ], controllers: [AppController], diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 35685b09..1cee77bf 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -8,7 +8,7 @@ import { AppService } from './app.service'; import { RCController } from './conditions/rc.controller'; import { RCService } from './conditions/rc.service'; -import { DB_LIRAMAP_CONFIG, POSTGIS_DB_CONFIG } from './database'; +import { DB_LIRAMAP_CONFIG } from './database'; const database = (config: any, name: string) => { return KnexModule.forRootAsync( @@ -20,11 +20,7 @@ const database = (config: any, name: string) => { }; @Module({ - imports: [ - ConfigModule.forRoot(), - database(POSTGIS_DB_CONFIG, 'postgis'), - database(DB_LIRAMAP_CONFIG, 'lira-map'), - ], + imports: [ConfigModule.forRoot(), database(DB_LIRAMAP_CONFIG, 'lira-map')], controllers: [AppController, RCController], providers: [AppService, ConfigService, RCService], }) diff --git a/backend/src/conditions/rc.controller.spec.ts b/backend/src/conditions/rc.controller.spec.ts index f01de004..8ae3f789 100644 --- a/backend/src/conditions/rc.controller.spec.ts +++ b/backend/src/conditions/rc.controller.spec.ts @@ -5,7 +5,7 @@ import { RCService } from './rc.service'; import { KnexModule } from 'nestjs-knex'; import { ConfigModule, ConfigService } from '@nestjs/config'; -import { DB_LIRAMAP_CONFIG, POSTGIS_DB_CONFIG } from '../database'; +import { DB_LIRAMAP_CONFIG } from '../database'; const database = (config: any, name: string) => { return KnexModule.forRootAsync( @@ -23,7 +23,6 @@ describe('Road Condition Controller', () => { const app: TestingModule = await Test.createTestingModule({ imports: [ ConfigModule.forRoot(), - database(POSTGIS_DB_CONFIG, 'postgis'), database(DB_LIRAMAP_CONFIG, 'lira-map'), ], controllers: [RCController], diff --git a/backend/src/conditions/rc.controller.ts b/backend/src/conditions/rc.controller.ts index e5d5da73..58cd4419 100644 --- a/backend/src/conditions/rc.controller.ts +++ b/backend/src/conditions/rc.controller.ts @@ -1,26 +1,16 @@ import { Controller, Get, Query } from '@nestjs/common'; -import { Condition, WaysConditions } from 'src/models'; +import { Condition } from 'src/models'; import { RCService } from './rc.service'; @Controller('conditions') export class RCController { constructor(private readonly service: RCService) {} - @Get('ways') - getWaysConditions( - @Query() query: { type: string; zoom: string }, - ): Promise { - const { type, zoom } = query; - return this.service.getWaysConditions(type, zoom); - } - @Get('way') - getWayConditions( - @Query() query: { wayId: string; type: string }, - ): Promise { - const { wayId, type } = query; - return this.service.getWayRoadConditions(wayId, type); + getWayConditions(@Query() query: { dbId: string }): Promise { + const { dbId } = query; + return this.service.getWayRoadConditions(dbId); } @Get('') diff --git a/backend/src/conditions/rc.service.ts b/backend/src/conditions/rc.service.ts index 76470506..fc65595e 100644 --- a/backend/src/conditions/rc.service.ts +++ b/backend/src/conditions/rc.service.ts @@ -1,94 +1,35 @@ import { Injectable } from '@nestjs/common'; import { InjectConnection, Knex } from 'nestjs-knex'; -import { Condition, LatLngDist, WayId, WaysConditions } from 'src/models'; -import groupBy from '../util'; -import { RoadConditions, Ways, ZoomConditions, Conditions2 } from '../tables'; +import { Condition } from 'src/models'; +import { Conditions2, Conditions } from '../tables'; import knexPostgis = require('knex-postgis'); @Injectable() export class RCService { - constructor( - @InjectConnection('postgis') private readonly knex: Knex, - @InjectConnection('lira-map') private readonly knex_liramap: Knex, - ) {} + constructor(@InjectConnection('lira-map') private readonly liramap: Knex) {} - async getWays( - wayIds: string[], - ): Promise<[{ [key: WayId]: LatLngDist[] }, { [key: WayId]: number }]> { - const ways = await Ways(this.knex) - .select( - 'id as way_id', - this.knex.raw( - "ST_AsGeoJSON((ST_DumpPoints(geom)).geom)::json->'coordinates' as pos", - ), - this.knex.raw( - 'ST_LineLocatePoint(geom, (ST_DumpPoints(geom)).geom) as way_dist', - ), - this.knex.raw('ST_Length(geom::geography) as length'), - ) - .whereIn('id', wayIds) - .orderBy(this.knex.raw('id::integer') as any); - - return [ - groupBy(ways, 'way_id', (cur: any) => ({ - lat: cur.pos[1], - lng: cur.pos[0], - way_dist: cur.way_dist, - })), - ways.reduce((acc, cur) => { - acc[cur.way_id] = cur.length; - return acc; - }, {}), - ]; - } - - async getWayRoadConditions( - way_id: string, - type: string, - ): Promise { - return RoadConditions(this.knex) - .select('way_id', 'way_dist', 'value') - .where({ type: type, way_id: way_id }) - .orderBy('way_dist'); - } - - async getZoomConditions( - type: string, - zoom: string, - ): Promise<{ [key: WayId]: Condition[] }> { - const res = await ZoomConditions(this.knex) - .select('way_id', 'way_dist', 'value') - .where({ type: type, zoom: parseInt(zoom, 10) }) - .orderBy('way_dist'); - return groupBy(res, 'way_id', ({ way_dist, value }) => ({ - way_dist, - value, - })); - } - - async getWaysConditions(type: string, zoom: string): Promise { - const zoomConditions = await this.getZoomConditions(type, zoom); - const wayIds = Object.keys(zoomConditions); - const [ways, way_lengths] = await this.getWays(wayIds); - - return wayIds.reduce( - (acc, way_id) => { - { - acc.way_ids.push(way_id); - acc.way_lengths.push(way_lengths[way_id]); - acc.geometry.push(ways[way_id]); - acc.conditions.push(zoomConditions[way_id]); - } - return acc; - }, - { - way_ids: [], - way_lengths: [], - geometry: [], - conditions: [], - } as WaysConditions, + async getWayRoadConditions(dbId: string): Promise { + return ( + Conditions(this.liramap) + .select( + 'cond1.value as KPI', + 'cond2.value as DI', + 'cond1.distance01 as way_dist', + ) + .join( + 'condition_coverages as cond2', + 'cond1.distance01', + '=', + 'cond2.distance01', + ) + .where('cond1.type', 'KPI') + .where('cond2.type', 'DI') + .where(this.liramap.raw('cond1.fk_way_id = cond2.fk_way_id')) + // .where('cond1.distance01', '<>', 0) + .where('cond1.fk_way_id', dbId) + .orderBy('cond1.distance01') ); } @@ -102,7 +43,7 @@ export class RCService { valid_after: string, computed_after: string, ) { - const db = this.knex_liramap; + const db = this.liramap; const st = knexPostgis(db); let res; diff --git a/backend/src/database.ts b/backend/src/database.ts index 73717c68..cc63cc18 100644 --- a/backend/src/database.ts +++ b/backend/src/database.ts @@ -2,8 +2,6 @@ import * as dotenv from 'dotenv'; dotenv.config(); const { - DB_USER_POSTGIS, - DB_PWD_POSTGIS, DB_LIRAMAP_HOST, DB_LIRAMAP_PORT, DB_LIRAMAP_NAME, @@ -41,18 +39,6 @@ const BASE_CONFIG = { }, }; -export const POSTGIS_DB_CONFIG = { - ...BASE_CONFIG, - connection: { - host: 'liradb.postgres.database.azure.com', - port: 5432, - user: DB_USER_POSTGIS, - password: DB_PWD_POSTGIS, - database: 'postgis', - ssl: true, - }, -}; - export const DB_LIRAMAP_CONFIG = { ...BASE_CONFIG, connection: { diff --git a/backend/src/tables.ts b/backend/src/tables.ts index 59894e68..3b80b4b5 100644 --- a/backend/src/tables.ts +++ b/backend/src/tables.ts @@ -33,6 +33,7 @@ export const ZoomConditions = (k: Knex) => // ekki@dtu.dk: This is for PostGIS in the LiRAMap (LiraVis) database export interface Condition_Coverage { id: string; + fk_way_id: string; type: string; value: number; section_geom: Geometry; @@ -40,7 +41,7 @@ export interface Condition_Coverage { } export const Conditions = (k: Knex) => - k.from('condition_coverages'); + k.from('condition_coverages as cond1'); export const Conditions2 = (k: Knex) => k.from('coverage_values'); diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index a1033df4..28098f14 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -19,6 +19,7 @@ module.exports = { }, ignorePatterns: ['.eslintrc.js'], rules: { + '@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/interface-name-prefix': 'off', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c8482d35..a39ab4ca 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -21,6 +21,7 @@ "react-router-dom": "^6.16.0", "react-scripts": "^5.0.1", "react-slider": "^2.0.6", + "react-split": "^2.0.14", "typescript": "^4.9.5", "web-vitals": "^3.4.0" }, @@ -22521,6 +22522,18 @@ "react": "^16 || ^17 || ^18" } }, + "node_modules/react-split": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/react-split/-/react-split-2.0.14.tgz", + "integrity": "sha512-bKWydgMgaKTg/2JGQnaJPg51T6dmumTWZppFgEbbY0Fbme0F5TuatAScCLaqommbGQQf/ZT1zaejuPDriscISA==", + "dependencies": { + "prop-types": "^15.5.7", + "split.js": "^1.6.0" + }, + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-styleguidist": { "version": "13.1.1", "resolved": "https://registry.npmjs.org/react-styleguidist/-/react-styleguidist-13.1.1.tgz", @@ -23794,6 +23807,11 @@ "wbuf": "^1.7.3" } }, + "node_modules/split.js": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/split.js/-/split.js-1.6.5.tgz", + "integrity": "sha512-mPTnGCiS/RiuTNsVhCm9De9cCAUsrNFFviRbADdKiiV+Kk8HKp/0fWu7Kr8pi3/yBmsqLFHuXGT9UUZ+CNLwFw==" + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index 22c88ac1..5564d2e3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,6 +16,7 @@ "react-router-dom": "^6.16.0", "react-scripts": "^5.0.1", "react-slider": "^2.0.6", + "react-split": "^2.0.14", "typescript": "^4.9.5", "web-vitals": "^3.4.0" }, diff --git a/frontend/src/Components/Map/ForceMapUpdate.tsx b/frontend/src/Components/Map/ForceMapUpdate.tsx new file mode 100644 index 00000000..b59a5bef --- /dev/null +++ b/frontend/src/Components/Map/ForceMapUpdate.tsx @@ -0,0 +1,32 @@ +import { LatLng } from '../../models/models'; +import { useMap } from 'react-leaflet'; +import React, { useEffect } from 'react'; + +interface Props { + /** A modification of this value is used to trigger an update of the map */ + triggerUpdate: number; + /** The coordinates of the map center*/ + position?: LatLng; +} + +/** + * This component is used to force an update of the map. It is used in conjunction with the useMap hook. + */ +const ForceMapUpdate: React.FC = ({ triggerUpdate, position }) => { + const map = useMap(); + + useEffect(() => { + console.log('hello'); + map.invalidateSize(); + }, [triggerUpdate]); + + useEffect(() => { + if (position) { + map.flyTo(position); + } + }, [position]); + + return null; +}; + +export default ForceMapUpdate; diff --git a/frontend/src/Components/Palette/PaletteEditor.tsx b/frontend/src/Components/Palette/PaletteEditor.tsx deleted file mode 100644 index f127c3a0..00000000 --- a/frontend/src/Components/Palette/PaletteEditor.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { FC, useState } from 'react'; -import { Gradient } from 'react-gradient-hook'; -import { CursorOptions } from 'react-gradient-hook/lib/types'; -import { Palette } from 'react-leaflet-hotline'; - -import '../../css/palette.css'; - -interface IPaletteEditor { - width: number | undefined; - defaultPalette?: Palette; - cursorOptions?: CursorOptions; - onChange?: (palette: Palette) => void; -} - -const PaletteEditor: FC = ({ - width, - defaultPalette, - cursorOptions, - onChange, -}) => { - const [show, setShow] = useState(false); - - const toggleAppear = () => setShow((prev) => !prev); - - if (width === undefined || width === 0) return null; - - return ( -
-
- -
-
- 🎨 -
-
- ); -}; - -export default PaletteEditor; diff --git a/frontend/src/Components/RoadConditions/ConditionsMap.tsx b/frontend/src/Components/RoadConditions/ConditionsMap.tsx deleted file mode 100644 index 0cd8fc1f..00000000 --- a/frontend/src/Components/RoadConditions/ConditionsMap.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { FC, useCallback, useRef } from 'react'; -import { ChartData } from 'chart.js'; -import { Palette } from 'react-leaflet-hotline'; - -import MapWrapper from '../Map/MapWrapper'; -import { RENDERER_PALETTE } from '../Map/constants'; -import PaletteEditor from '../Palette/PaletteEditor'; -import Ways from './Ways'; - -import useSize from '../../hooks/useSize'; - -import { ConditionType } from '../../models/graph'; -import { Condition } from '../../models/path'; - -import { getConditions } from '../../queries/conditions'; - -interface Props { - type: ConditionType; - palette: Palette; - setPalette: React.Dispatch>; - setWayData: React.Dispatch< - React.SetStateAction | undefined> - >; -} - -//part of Road Conditions component -const ConditionsMap: FC = ({ - type, - palette, - setPalette, - setWayData, -}) => { - const { name, max, grid, samples } = type; - - const ref = useRef(null); - const [width] = useSize(ref); - - const onClick = useCallback((way_id: string, way_length: number) => { - console.log('onclick called'); - - getConditions(way_id, name, (wc: Condition[]) => { - console.log('getConditions called'); - setWayData({ - labels: wc.map((p) => p.way_dist * way_length), - datasets: [ - { - type: 'line' as const, - label: way_id, - borderColor: 'rgb(255, 99, 132)', - borderWidth: 2, - fill: false, - tension: 0.1, - data: wc.map((p) => p.value), - }, - ], - }); - }); - }, []); - - return ( -
- - - - - -
- ); -}; - -export default ConditionsMap; diff --git a/frontend/src/Components/RoadConditions/Ways.tsx b/frontend/src/Components/RoadConditions/Ways.tsx index 1053cb5f..6fd030f9 100644 --- a/frontend/src/Components/RoadConditions/Ways.tsx +++ b/frontend/src/Components/RoadConditions/Ways.tsx @@ -1,32 +1,29 @@ -import { FC, useEffect, useMemo, useState } from 'react'; -import { TRGB } from 'react-gradient-hook/lib/types'; +import { FC, useMemo, useState } from 'react'; + import { HotlineOptions } from 'react-leaflet-hotline'; import { HotlineEventHandlers } from 'react-leaflet-hotline/dist/types/types'; import { useGraph } from '../../context/GraphContext'; import { WaysConditions } from '../../models/path'; -import { getWaysConditions } from '../../queries/conditions'; -import useZoom from '../Map/Hooks/useZoom'; import DistHotline from '../Map/Renderers/DistHotline'; interface IWays { - palette: TRGB[]; type: string; onClick?: (way_id: string, way_length: number) => void; } -const Ways: FC = ({ palette, type, onClick }) => { - const zoom = useZoom(); +const Ways: FC = ({ type, onClick }) => { + console.log(type); + const { minY, maxY } = useGraph(); - const [ways, setWays] = useState(); + const [ways, _] = useState(); const options = useMemo( () => ({ - palette, min: minY, max: maxY, }), - [palette, minY, maxY], + [minY, maxY], ); const handlers = useMemo( @@ -38,15 +35,6 @@ const Ways: FC = ({ palette, type, onClick }) => { [ways], ); - useEffect(() => { - if (zoom === undefined) return; - const z = Math.max(0, zoom - 12); - getWaysConditions(type, z, (data: WaysConditions) => { - console.log(data); - setWays(data); - }); - }, [zoom]); - return ( <> {ways ? ( diff --git a/frontend/src/Components/RoadConditions/ConditionsGraph.tsx b/frontend/src/Components/RoadDetails/ConditionsGraph.tsx similarity index 53% rename from frontend/src/Components/RoadConditions/ConditionsGraph.tsx rename to frontend/src/Components/RoadDetails/ConditionsGraph.tsx index 0a0d07b6..0f15f27f 100644 --- a/frontend/src/Components/RoadConditions/ConditionsGraph.tsx +++ b/frontend/src/Components/RoadDetails/ConditionsGraph.tsx @@ -15,10 +15,7 @@ import { Title, Tooltip, } from 'chart.js'; -import { Color, Palette } from 'react-leaflet-hotline'; -import { Line } from 'react-chartjs-2'; - -import { ConditionType } from '../../models/graph'; +import { Scatter } from 'react-chartjs-2'; Chart.register( CategoryScale, @@ -30,7 +27,7 @@ Chart.register( Legend, ); -const options = ({ name, min, max }: ConditionType): ChartOptions<'line'> => ({ +const options = (): ChartOptions<'scatter'> => ({ responsive: true, maintainAspectRatio: false, plugins: { @@ -46,61 +43,50 @@ const options = ({ name, min, max }: ConditionType): ChartOptions<'line'> => ({ text: 'distance (m)', }, ticks: { - maxTicksLimit: 30, - stepSize: 200, + stepSize: 10, callback: (tick: string | number) => Math.round(parseFloat(tick.toString())), }, }, - y: { + //TODO will be a variable passed from parent + KPI: { + type: 'linear', + position: 'left', + display: 'auto', + title: { + display: true, + text: 'KPI', + }, + }, + DI: { + type: 'linear', + position: 'right', + display: 'auto', title: { display: true, - text: name, + text: 'DI', }, - min: min, - max: max, }, }, }); interface Props { - type: ConditionType; - data: ChartData<'line', number[], number> | undefined; - palette: Palette; + data: ChartData<'scatter', number[], number> | undefined; } -const ConditionsGraph: FC = ({ type, data, palette }) => { - const ref = useRef>(null); - - const addPaletteChart = - (palette: Palette) => - (chart: Chart) => { - const dataset = chart.data.datasets[0]; - const gradient = chart.ctx.createLinearGradient( - 0, - chart.chartArea.bottom, - 0, - 0, - ); - console.log(...palette); - palette.forEach((c: Color) => { - gradient.addColorStop(c.t, `rgb(${c.r}, ${c.g}, ${c.b})`); - }); - dataset.borderColor = gradient; - dataset.backgroundColor = gradient; - }; +const ConditionsGraph: FC = ({ data }) => { + const ref = useRef>(null); useEffect(() => { if (ref.current === null) return; const chart = ref.current; - addPaletteChart(palette)(chart); chart.update(); - }, [ref, data, palette]); + }, [ref, data]); // attach events to the graph options - const graphOptions: ChartOptions<'line'> = useMemo( + const graphOptions: ChartOptions<'scatter'> = useMemo( () => ({ - ...options(type), + ...options(), onClick: ( event: ChartEvent, elts: ActiveElement[], @@ -115,7 +101,7 @@ const ConditionsGraph: FC = ({ type, data, palette }) => { [], ); - const plugins: Plugin<'line'>[] = [ + const plugins: Plugin<'scatter'>[] = [ { id: 'id', }, @@ -124,7 +110,12 @@ const ConditionsGraph: FC = ({ type, data, palette }) => { return (
{data && ( - + )}
); diff --git a/frontend/src/Components/RoadDetails/MapArea.tsx b/frontend/src/Components/RoadDetails/MapArea.tsx index 0d435662..4fb54da0 100644 --- a/frontend/src/Components/RoadDetails/MapArea.tsx +++ b/frontend/src/Components/RoadDetails/MapArea.tsx @@ -1,15 +1,21 @@ import React from 'react'; import ImageGallery from './ImageGallery'; import MapWrapper from '../Map/MapWrapper'; +import ForceMapUpdate from '../Map/ForceMapUpdate'; +interface Props { + triggerUpdate: number; +} /** * The map area op the road details(road inspect) page */ -const MapArea: React.FC = () => { +const MapArea: React.FC = ({ triggerUpdate }) => { return (
- + + +
{/* Use the imageGallery component */} diff --git a/frontend/src/Components/RoadDetails/RoadImage.tsx b/frontend/src/Components/RoadDetails/RoadImage.tsx index 640320b1..46ad113f 100644 --- a/frontend/src/Components/RoadDetails/RoadImage.tsx +++ b/frontend/src/Components/RoadDetails/RoadImage.tsx @@ -3,7 +3,6 @@ import React from 'react'; const maproadStyles = { margin: 0, padding: 20, - color: '#333', }; const road_area =

Road surface image

; diff --git a/frontend/src/css/map.css b/frontend/src/css/map.css index 906d98f0..31ee3482 100644 --- a/frontend/src/css/map.css +++ b/frontend/src/css/map.css @@ -8,8 +8,7 @@ } .map-zoom { - position: absolute; - right: 55px; + /*position: absolute;*/ z-index: calc(var(--navbar-z-index) - 2); color: var(--background); font-family: 'Noto Sans JP', sans-serif; diff --git a/frontend/src/css/road_conditions.css b/frontend/src/css/road_conditions.css deleted file mode 100644 index 731f8df8..00000000 --- a/frontend/src/css/road_conditions.css +++ /dev/null @@ -1,10 +0,0 @@ -.road-conditions-wrapper { - height: calc(100vh - var(--navbar-height)); - display: grid; - grid-template-rows: auto min(40vh, 400px); -} - -.road-conditions-graph { - position: relative; - width: 100vw; -} diff --git a/frontend/src/css/road_details.css b/frontend/src/css/road_details.css index 43eab5fa..cd96b9f9 100644 --- a/frontend/src/css/road_details.css +++ b/frontend/src/css/road_details.css @@ -13,7 +13,7 @@ .road-image, .map-area { /* background-color: #eee; Example color for the middle area */ - height: 60vh; /* Occupying half of the page vertically */ + height: 100%; /* Occupying half of the page vertically */ display: flex; flex-direction: column; justify-content: flex-start; @@ -81,6 +81,14 @@ transition: 0.4s; } +.road-conditions-graph { + position: relative; + width: 100vw; + display: flex; + justify-content: center; + align-items: center; +} + /* Style to change the background color when switched on */ input:checked + .slider { background-color: #4caf50; diff --git a/frontend/src/css/split.css b/frontend/src/css/split.css new file mode 100644 index 00000000..28382eab --- /dev/null +++ b/frontend/src/css/split.css @@ -0,0 +1,14 @@ +.split { + height: calc(100vh - var(--navbar-height)); +} + +.gutter { + background-color: #eee; + background-repeat: no-repeat; + background-position: 50%; +} + +.gutter.gutter-vertical { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII='); + cursor: row-resize; +} diff --git a/frontend/src/models/graph.ts b/frontend/src/models/graph.ts deleted file mode 100644 index aa1f509d..00000000 --- a/frontend/src/models/graph.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface ConditionType { - name: string; - min: number; - max: number; - grid: boolean; - samples?: number; -} diff --git a/frontend/src/models/path.ts b/frontend/src/models/path.ts index 7da6d71b..7bd8db88 100644 --- a/frontend/src/models/path.ts +++ b/frontend/src/models/path.ts @@ -59,10 +59,18 @@ export interface ValueLatLng extends LatLng { } export interface Condition { + type: string; way_dist: number; value: number; } +export interface ConditionKPIDI { + fk_way_id: string; + KPI: number; + DI: number; + way_dist: number; +} + export type WayId = string; export interface WaysConditions { diff --git a/frontend/src/pages/RoadConditions.tsx b/frontend/src/pages/RoadConditions.tsx deleted file mode 100644 index b852fb3d..00000000 --- a/frontend/src/pages/RoadConditions.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { useState } from 'react'; -import { Palette } from 'react-leaflet-hotline'; -import { ChartData } from 'chart.js'; - -import ConditionsMap from '../Components/RoadConditions/ConditionsMap'; -import ConditionsGraph from '../Components/RoadConditions/ConditionsGraph'; - -import { ConditionType } from '../models/graph'; - -import { GraphProvider } from '../context/GraphContext'; - -import '../css/road_conditions.css'; - -//this is to visualise the Road Conditions (GP) map -const RoadConditions = () => { - const [palette, setPalette] = useState([]); - const [wayData, setWayData] = useState>(); - - const type: ConditionType = { - name: 'IRI', - min: 0, - max: 10, - grid: true, - samples: 40, - }; - - return ( - -
- - -
-
- ); -}; - -export default RoadConditions; diff --git a/frontend/src/pages/RoadDetails.tsx b/frontend/src/pages/RoadDetails.tsx index b6cf86ba..81c19c31 100644 --- a/frontend/src/pages/RoadDetails.tsx +++ b/frontend/src/pages/RoadDetails.tsx @@ -1,19 +1,77 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import '../css/road_details.css'; // Import the CSS file import MapArea from '../Components/RoadDetails/MapArea'; import RoadImage from '../Components/RoadDetails/RoadImage'; import TopBar from '../Components/RoadDetails/TopBar'; +import Split from 'react-split'; +import ConditionsGraph from '../Components/RoadDetails/ConditionsGraph'; +import { ChartData } from 'chart.js'; +import { getConditionsWay } from '../queries/conditions'; +import '../css/split.css'; const RoadDetails = () => { const [showMapImageMode, setShowRoadImageMode] = useState(false); + const [wayData, setWayData] = + useState>(); + const [triggerUpdate, setTriggerUpdate] = useState(0); + + useEffect(() => { + getConditionsWay('0cba0666-d75e-45bd-9da6-62ef0fe9544c', (wc) => { + console.log(wc); + setWayData({ + labels: wc.map((p) => p.way_dist * 100), + datasets: [ + { + //@ts-ignore + type: 'line' as const, + label: 'KPI', + borderColor: 'rgb(255, 99, 132)', + borderWidth: 2, + fill: false, + tension: 0.2, + data: wc.map((p) => p.KPI), + yAxisID: 'KPI', + }, + { + //@ts-ignore + type: 'line' as const, + label: 'DI', + borderColor: 'rgb(120, 245, 23)', + borderWidth: 2, + fill: false, + tension: 0.2, + data: wc.map((p) => p.DI), + yAxisID: 'DI', + }, + ], + }); + }); + }, []); return (
- {showMapImageMode ? : } - {/* TODO: putting plots here */} -
{/* Content for the plot area */}
+ { + setTriggerUpdate((prev) => prev + 1); + }} + > +
+ {showMapImageMode ? ( + + ) : ( + + )} +
+ +
+ ,
); }; diff --git a/frontend/src/queries/conditions.ts b/frontend/src/queries/conditions.ts index 78b2414a..ede01aad 100644 --- a/frontend/src/queries/conditions.ts +++ b/frontend/src/queries/conditions.ts @@ -1,5 +1,5 @@ import { MapBounds } from '../models/map'; -import { Condition, WaysConditions } from '../models/path'; +import { ConditionKPIDI, WaysConditions } from '../models/path'; import { asyncPost, post } from './fetch'; export const getWaysConditions = ( @@ -10,12 +10,11 @@ export const getWaysConditions = ( post('/conditions/ways', { type, zoom }, setWays); }; -export const getConditions = ( - wayId: string, - type: string, - setConditions: (data: Condition[]) => void, +export const getConditionsWay = ( + dbId: string, + setConditions: (data: ConditionKPIDI[]) => void, ) => { - post('/conditions/way', { wayId, type }, setConditions); + post('/conditions/way', { dbId }, setConditions); }; export const getBoundedWaysConditions = async (