Skip to content
This repository has been archived by the owner on May 4, 2024. It is now read-only.

Commit

Permalink
#conditions-gp | added import files for Road Continditions, comments …
Browse files Browse the repository at this point in the history
…to be added and dependencies fixed #3
  • Loading branch information
marcosantiagomuro committed Sep 9, 2023
1 parent 61dd64a commit e68aaa3
Show file tree
Hide file tree
Showing 24 changed files with 826 additions and 17,838 deletions.
17,836 changes: 0 additions & 17,836 deletions frontend/package-lock.json

This file was deleted.

2 changes: 1 addition & 1 deletion frontend/src/Components/Map/Hooks/useZoom.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect, useState } from "react"
import { useMapEvents } from "react-leaflet"


//zoom implementation on map events
const useZoom = () => {
const [zoom, setZoom] = useState<number>()

Expand Down
1 change: 1 addition & 0 deletions frontend/src/Components/Map/MapWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Zoom from './Zoom';
import '../../css/map.css'
import { MAP_OPTIONS } from './constants';

//container for map and its attributes
const MapWrapper = ( props : any ) => {

const { children } = props;
Expand Down
69 changes: 69 additions & 0 deletions frontend/src/Components/Map/Renderers/DistHotline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@

import { FC, useEffect, useMemo, useState } from 'react';
import { LeafletEvent, Polyline } from 'leaflet'
import { HotlineOptions, useCustomHotline } from 'react-leaflet-hotline';

import { useGraph } from '../../../context/GraphContext';

import { Condition, Node, WayId } from '../../../models/path';

import DistRenderer from '../../../assets/hotline/DistRenderer';
import { DistData } from '../../../assets/hotline/hotline';
import HoverHotPolyline from '../../../assets/hotline/HoverHotPolyline';
import { HotlineEventFn, HotlineEventHandlers } from 'react-leaflet-hotline/lib/types';
import useZoom from '../Hooks/useZoom';
import { useHoverContext } from "../../../context/GraphHoverContext";


const getLat = (n: Node) => n.lat;
const getLng = (n: Node) => n.lng;
const getVal = (n: Node) => n.way_dist;
const getWeight = (z: number | undefined) => z === undefined ? 0 : Math.max(z > 8 ? z - 6 : z - 5, 2)

interface IDistHotline {
way_ids: WayId[];
geometry: Node[][];
conditions: Condition[][];
options?: HotlineOptions,
eventHandlers?: HotlineEventHandlers;
}

const handler = (eventHandlers: HotlineEventHandlers | undefined, event: keyof HotlineEventHandlers, opacity: number) => {
return (e: LeafletEvent, i: number, p: Polyline<any, any>) => {
p.setStyle( { opacity } )
if ( eventHandlers && eventHandlers[event] !== undefined )
(eventHandlers[event] as HotlineEventFn)(e, i, p);
}
}

const DistHotline: FC<IDistHotline> = ( { way_ids, geometry, conditions, options, eventHandlers } ) => {

const { dotHover } = useHoverContext()
const zoom = useZoom()

const opts = useMemo( () => ({
...options, weight: getWeight(zoom)
}), [options, zoom] )

const handlers: HotlineEventHandlers = useMemo( () => ({
...eventHandlers,
mouseover: handler(eventHandlers, 'mouseover', 0.5),
mouseout: handler(eventHandlers, 'mouseout', 0),
}), [eventHandlers] )

const { hotline } = useCustomHotline<Node, DistData>(
DistRenderer, HoverHotPolyline,
{ data: geometry, getLat, getLng, getVal, options: opts, eventHandlers: handlers },
way_ids, conditions
);

useEffect( () => {
if ( hotline === undefined ) return;
(hotline as HoverHotPolyline<Node, DistData>).setHover(dotHover)
}, [dotHover])

return null;
}


export default DistHotline;
1 change: 1 addition & 0 deletions frontend/src/Components/Map/Zoom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { ZoomControl } from "react-leaflet"
import useZoom from "./Hooks/useZoom"

// implement zoom component on map
const Zoom = () => {

const zoom = useZoom()
Expand Down
34 changes: 34 additions & 0 deletions frontend/src/Components/Palette/PaletteEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { FC, MouseEvent, useState } from "react";
import { Gradient } from "react-gradient-hook";
import { CursorOptions } from "react-gradient-hook/lib/types";
import { useMap } from "react-leaflet";
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<IPaletteEditor> = ( { width, defaultPalette, cursorOptions, onChange } ) => {

const [show, setShow] = useState<boolean>(false)

const toggleAppear = () => setShow(prev => !prev)

if ( width === undefined || width === 0 ) return null;

return (
<div className={`palette-wrapper ${show ? 'palette-show' : ''}`} style={{width: `${width}px`}} >
<div className="palette-container">
<Gradient defaultColors={defaultPalette} cursorOptions={cursorOptions} onChange={onChange}/>
</div>
<div className='palette-hover' onClick={toggleAppear}>🎨</div>
</div>
)
}

export default PaletteEditor;
97 changes: 97 additions & 0 deletions frontend/src/Components/RoadConditions/ConditionsGraph.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { FC, useCallback, useEffect, useMemo, useRef } from "react";
import { ChartData, Chart, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, ActiveElement, ChartEvent, ChartOptions, ChartTypeRegistry, Plugin } from "chart.js";
import { Color, Palette } from "react-leaflet-hotline";
import { Line } from "react-chartjs-2";

import { ConditionType } from "../../models/graph";

Chart.register( CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend );

const options = ({name, min, max}: ConditionType): ChartOptions<'line'> => ({
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'top' as const,
labels: { color: 'white' },
},
},
scales: {
x: {
title: {
display: true,
text: 'distance (m)'
},
ticks: {
maxTicksLimit: 30,
stepSize: 200,
callback: (tick: string | number) => Math.round(parseFloat(tick.toString()))
}
},
y: {
title: {
display: true,
text: name
},
min: min,
max: max
}
}
});

interface Props {
type: ConditionType;
data: ChartData<"line", number[], number> | undefined
palette: Palette;
}

const ConditionsGraph: FC<Props> = ( { type, data, palette } ) => {

const ref = useRef<Chart<"line", number[], number>>(null)

const addPaletteChart = (palette: Palette) => (chart: Chart<keyof ChartTypeRegistry, number[], unknown>) => {
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;
}

useEffect( () => {
if (ref.current === null ) return;
const chart = ref.current;
addPaletteChart(palette)(chart)
chart.update()
}, [ref, data, palette])

// attach events to the graph options
const graphOptions: ChartOptions<'line'> = useMemo( () => ({
...options(type),
onClick: (event: ChartEvent, elts: ActiveElement[], chart: Chart<keyof ChartTypeRegistry, number[], unknown>) => {
if ( elts.length === 0 ) return;
const elt = elts[0] // doesnt work if multiple datasets
const pointIndex = elt.index
console.log(pointIndex, event, elts);
}
}), [] )

const plugins: Plugin<"line">[] = [ {
id: 'id',
} ]

return (
<div className="road-conditions-graph">
{ data && <Line
ref={ref}
data={data}
options={graphOptions}
plugins={plugins} />
}
</div>
)
}

export default ConditionsGraph;
1 change: 1 addition & 0 deletions frontend/src/Components/RoadConditions/ConditionsMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ interface Props {
setWayData: React.Dispatch<React.SetStateAction<ChartData<"line", number[], number> | undefined>>;
}

//part of Road Conditions component
const ConditionsMap: FC<Props> = ( { type, palette, setPalette, setWayData } ) => {

const { name, max, grid, samples } = type;
Expand Down
60 changes: 60 additions & 0 deletions frontend/src/Components/RoadConditions/Ways.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@

import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { TRGB } from 'react-gradient-hook/lib/types';
import { HotlineOptions } from 'react-leaflet-hotline';
import { HotlineEventHandlers } from 'react-leaflet-hotline/lib/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<IWays> = ( { palette, type, onClick } ) => {

const zoom = useZoom();
const { minY, maxY } = useGraph()

const [ways, setWays] = useState<WaysConditions>()

const options = useMemo<HotlineOptions>( () => ({
palette, min: minY, max: maxY
} ), [palette, minY, maxY] )

const handlers = useMemo<HotlineEventHandlers>( () => ({
click: (_, i) => {
if ( ways && onClick )
onClick(ways.way_ids[i], ways.way_lengths[i])
},
}), [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
? <DistHotline
way_ids={ways.way_ids}
geometry={ways.geometry}
conditions={ways.conditions}
options={options}
eventHandlers={handlers}/>
: null
}
</>
)
}

export default Ways;
67 changes: 67 additions & 0 deletions frontend/src/assets/graph/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@

import { Selection } from "d3"
import { FC } from "react"
import { Color } from "react-leaflet-hotline";
import { Bounds } from "../../models/path";

// SVG
export type SVG = d3.Selection<SVGGElement, unknown, null, undefined>
export type SVGLayer = d3.Selection<d3.BaseType, unknown, null, undefined>

// Axis
export interface IAxis {
svg: SVG;
axis: Axis | undefined,
width: number;
height: number;
zoom: number;
absolute?: boolean;
time?: boolean;
}
export type ReactAxis = FC<IAxis>;
export type Axis = d3.ScaleLinear<number, number, never>
export type GraphAxis = [Axis, Axis]

// Data format
export type GraphPoint = {
x: number,
y: number,
lat: number,
lng: number } // [number, number]

export type GraphData = GraphPoint[]

export interface Plot {
data: GraphData
bounds?: Bounds;
label: string;
}

// Events
export interface DotHover {
label: string;
point: GraphPoint
}

// Options
export interface PathOptions {
stroke?: string;
strokeWidth?: number;
}

export interface DotsOptions {
radius?: number;
opacity?: number;
fill?: string;
}

// Palette - Gradient
export type Gradient = Selection<SVGStopElement, Color, SVGLinearGradientElement, unknown>

// MinMax
export type MinMax = [number, number]
export type AddMinMaxFunc = (label: string, bounds: Required<Bounds>) => void
export type RemMinMaxFunc = (label: string) => void

// Callback
export type D3Callback = (event: any, d: GraphPoint) => void
Loading

0 comments on commit e68aaa3

Please sign in to comment.