Move courses rendering to separate file
This commit is contained in:
		
							parent
							
								
									20f04408fc
								
							
						
					
					
						commit
						aeef4b5ae9
					
				|  | @ -0,0 +1,139 @@ | |||
| import { getVectorContext } from "ol/render"; | ||||
| import Point from "ol/geom/Point"; | ||||
| import { fromExtent } from 'ol/geom/Polygon'; | ||||
| import { Style, Icon } from "ol/style"; | ||||
| import { sizes, cacheStyle, makeBorderColor, makeCourseColor } from "./common"; | ||||
| import network from "../../tam/network.json"; | ||||
| 
 | ||||
| const courseStyle = cacheStyle( | ||||
|     lineColor => { | ||||
|         const icon = window.document.createElement("canvas"); | ||||
| 
 | ||||
|         const shapeSize = sizes.courseSize; | ||||
|         const iconSize = sizes.courseSize + sizes.courseOuterBorder; | ||||
| 
 | ||||
|         icon.width = iconSize; | ||||
|         icon.height = iconSize; | ||||
| 
 | ||||
|         const cx = icon.width / 2; | ||||
|         const cy = icon.height / 2; | ||||
| 
 | ||||
|         const iconCtx = icon.getContext("2d"); | ||||
| 
 | ||||
|         for (const [color, size] of [ | ||||
|             [makeBorderColor(lineColor), sizes.courseOuterBorder], | ||||
|             [lineColor, sizes.courseBorder], | ||||
|             [makeCourseColor(lineColor), sizes.courseInnerBorder] | ||||
|         ]) { | ||||
|             iconCtx.fillStyle = color; | ||||
|             iconCtx.strokeStyle = color; | ||||
|             iconCtx.lineWidth = size; | ||||
|             iconCtx.lineJoin = "round"; | ||||
|             iconCtx.miterLimit = 200000; | ||||
| 
 | ||||
|             iconCtx.beginPath(); | ||||
|             iconCtx.moveTo(cx - 0.5 * shapeSize, cy - 0.3 * shapeSize); | ||||
|             iconCtx.lineTo(cx + 0.5 * shapeSize, cy); | ||||
|             iconCtx.lineTo(cx - 0.5 * shapeSize, cy + 0.3 * shapeSize); | ||||
|             iconCtx.closePath(); | ||||
|             iconCtx.stroke(); | ||||
|             iconCtx.fill(); | ||||
|         } | ||||
| 
 | ||||
|         return new Style({ | ||||
|             image: new Icon({ | ||||
|                 img: icon, | ||||
|                 imgSize: [icon.width, icon.height] | ||||
|             }) | ||||
|         }); | ||||
|     }, | ||||
| ); | ||||
| 
 | ||||
| export const setupCoursesAnimation = ( | ||||
|     map, coursesSimulation, stopsLayer, onClick | ||||
| ) => { | ||||
|     const view = map.getView(); | ||||
| 
 | ||||
|     // Draw courses directly on the map
 | ||||
|     map.on("postcompose", ev => { | ||||
|         coursesSimulation.update(); | ||||
| 
 | ||||
|         // The normal way to access a layer’s vector context is through the
 | ||||
|         // `postrender` event of that layer. However, `postrender` is not
 | ||||
|         // triggered when no feature of the layer is inside the current
 | ||||
|         // bounding box, but we want to draw vehicles in between stops even
 | ||||
|         // if no stop is visible. This hack listens to the global `postcompose`
 | ||||
|         // event, which is always triggered at every frame, and reconstructs
 | ||||
|         // the stops layer’s vector context from internal variables
 | ||||
|         /* eslint-disable no-underscore-dangle */ | ||||
|         if (stopsLayer.renderer_) { | ||||
|             ev.context = stopsLayer.renderer_.context; | ||||
|             ev.inversePixelTransform = | ||||
|                 stopsLayer.renderer_.inversePixelTransform; | ||||
|             /* eslint-enable no-underscore-dangle */ | ||||
| 
 | ||||
|             const ctx = getVectorContext(ev); | ||||
|             const bbox = fromExtent(map.getView().calculateExtent()); | ||||
|             bbox.scale(1.05); | ||||
| 
 | ||||
|             for (const course of Object.values(coursesSimulation.courses)) { | ||||
|                 if (!bbox.intersectsCoordinate(course.position)) { | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 const point = new Point(course.position); | ||||
|                 const color = network.lines[course.line].color; | ||||
|                 const style = courseStyle(color); | ||||
| 
 | ||||
|                 style.getImage().setRotation( | ||||
|                     view.getRotation() + | ||||
|                     course.angle | ||||
|                 ); | ||||
| 
 | ||||
|                 ctx.setStyle(style); | ||||
|                 ctx.drawGeometry(point); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         map.render(); | ||||
|     }); | ||||
| 
 | ||||
|     map.on("singleclick", ev => { | ||||
|         const mousePixel = map.getPixelFromCoordinate(ev.coordinate); | ||||
|         const maxDistance = sizes.courseSize + sizes.courseInnerBorder; | ||||
|         const clicked = []; | ||||
| 
 | ||||
|         for (const course of Object.values(coursesSimulation.courses)) { | ||||
|             const coursePixel = map.getPixelFromCoordinate(course.position); | ||||
|             const dx = mousePixel[0] - coursePixel[0]; | ||||
|             const dy = mousePixel[1] - coursePixel[1]; | ||||
|             const distance = dx * dx + dy * dy; | ||||
| 
 | ||||
|             if (distance <= maxDistance * maxDistance) { | ||||
|                 clicked.push(course.id); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Sort selected courses in increasing departing/arrival time
 | ||||
|         clicked.sort((id1, id2) => { | ||||
|             const course1 = coursesSimulation.courses[id1]; | ||||
|             const course2 = coursesSimulation.courses[id2]; | ||||
| 
 | ||||
|             const time1 = ( | ||||
|                 course1.state === "moving" | ||||
|                 ? course1.arrivalTime | ||||
|                 : course1.departureTime | ||||
|             ); | ||||
| 
 | ||||
|             const time2 = ( | ||||
|                 course2.state === "moving" | ||||
|                 ? course2.arrivalTime | ||||
|                 : course2.departureTime | ||||
|             ); | ||||
| 
 | ||||
|             return time1 - time2; | ||||
|         }); | ||||
| 
 | ||||
|         onClick(clicked); | ||||
|     }); | ||||
| }; | ||||
|  | @ -1,59 +1,10 @@ | |||
| import "ol/ol.css"; | ||||
| 
 | ||||
| import { Map, View } from "ol"; | ||||
| import { getVectorContext } from "ol/render"; | ||||
| import Point from "ol/geom/Point"; | ||||
| import { fromExtent } from 'ol/geom/Polygon'; | ||||
| import * as proj from "ol/proj"; | ||||
| import { Style, Icon } from "ol/style"; | ||||
| import * as tilesLayers from "./tiles"; | ||||
| import * as networkLayers from "./network"; | ||||
| import { sizes, cacheStyle, makeBorderColor, makeCourseColor } from "./common"; | ||||
| import network from "../../tam/network.json"; | ||||
| 
 | ||||
| const courseStyle = cacheStyle( | ||||
|     lineColor => { | ||||
|         const icon = window.document.createElement("canvas"); | ||||
| 
 | ||||
|         const shapeSize = sizes.courseSize; | ||||
|         const iconSize = sizes.courseSize + sizes.courseOuterBorder; | ||||
| 
 | ||||
|         icon.width = iconSize; | ||||
|         icon.height = iconSize; | ||||
| 
 | ||||
|         const cx = icon.width / 2; | ||||
|         const cy = icon.height / 2; | ||||
| 
 | ||||
|         const iconCtx = icon.getContext("2d"); | ||||
| 
 | ||||
|         for (const [color, size] of [ | ||||
|             [makeBorderColor(lineColor), sizes.courseOuterBorder], | ||||
|             [lineColor, sizes.courseBorder], | ||||
|             [makeCourseColor(lineColor), sizes.courseInnerBorder] | ||||
|         ]) { | ||||
|             iconCtx.fillStyle = color; | ||||
|             iconCtx.strokeStyle = color; | ||||
|             iconCtx.lineWidth = size; | ||||
|             iconCtx.lineJoin = "round"; | ||||
|             iconCtx.miterLimit = 200000; | ||||
| 
 | ||||
|             iconCtx.beginPath(); | ||||
|             iconCtx.moveTo(cx - 0.5 * shapeSize, cy - 0.3 * shapeSize); | ||||
|             iconCtx.lineTo(cx + 0.5 * shapeSize, cy); | ||||
|             iconCtx.lineTo(cx - 0.5 * shapeSize, cy + 0.3 * shapeSize); | ||||
|             iconCtx.closePath(); | ||||
|             iconCtx.stroke(); | ||||
|             iconCtx.fill(); | ||||
|         } | ||||
| 
 | ||||
|         return new Style({ | ||||
|             image: new Icon({ | ||||
|                 img: icon, | ||||
|                 imgSize: [icon.width, icon.height] | ||||
|             }) | ||||
|         }); | ||||
|     }, | ||||
| ); | ||||
| import { getLayers as getTilesLayers } from "./tiles"; | ||||
| import { getLayers as getNetworkLayers } from "./network"; | ||||
| import { setupCoursesAnimation } from "./courses"; | ||||
| 
 | ||||
| export const create = (target, coursesSimulation, onClick) => { | ||||
|     const view = new View({ | ||||
|  | @ -63,101 +14,18 @@ export const create = (target, coursesSimulation, onClick) => { | |||
|         constrainResolution: true | ||||
|     }); | ||||
| 
 | ||||
|     const tilesLayers = getTilesLayers(); | ||||
|     const networkLayers = getNetworkLayers(); | ||||
|     const stopsLayer = networkLayers[2]; | ||||
| 
 | ||||
|     const map = new Map({ | ||||
|         target, | ||||
|         layers: [ | ||||
|             ...tilesLayers.getLayers(), | ||||
|             ...networkLayers.getLayers() | ||||
|         ], | ||||
|         layers: [...tilesLayers, ...networkLayers], | ||||
|         view | ||||
|     }); | ||||
| 
 | ||||
|     const stopsLayer = map.getLayers().item(3); | ||||
| 
 | ||||
|     // Draw courses directly on the map
 | ||||
|     map.on("postcompose", ev => { | ||||
|         coursesSimulation.update(); | ||||
| 
 | ||||
|         // The normal way to access a layer’s vector context is through the
 | ||||
|         // `postrender` event of that layer. However, `postrender` is not
 | ||||
|         // triggered when no feature of the layer is inside the current
 | ||||
|         // bounding box, but we want to draw vehicles in between stops even
 | ||||
|         // if no stop is visible. This hack listens to the global `postcompose`
 | ||||
|         // event, which is always triggered at every frame, and reconstructs
 | ||||
|         // the stops layer’s vector context from internal variables
 | ||||
|         /* eslint-disable no-underscore-dangle */ | ||||
|         if (stopsLayer.renderer_) { | ||||
|             ev.context = stopsLayer.renderer_.context; | ||||
|             ev.inversePixelTransform = | ||||
|                 stopsLayer.renderer_.inversePixelTransform; | ||||
|             /* eslint-enable no-underscore-dangle */ | ||||
| 
 | ||||
|             const ctx = getVectorContext(ev); | ||||
|             const bbox = fromExtent(map.getView().calculateExtent()); | ||||
|             bbox.scale(1.05); | ||||
| 
 | ||||
|             for (const course of Object.values(coursesSimulation.courses)) { | ||||
|                 if (!bbox.intersectsCoordinate(course.position)) { | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 const point = new Point(course.position); | ||||
|                 const color = network.lines[course.line].color; | ||||
|                 const style = courseStyle(color); | ||||
| 
 | ||||
|                 style.getImage().setRotation( | ||||
|                     view.getRotation() + | ||||
|                     course.angle | ||||
|                 ); | ||||
| 
 | ||||
|                 ctx.setStyle(style); | ||||
|                 ctx.drawGeometry(point); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         map.render(); | ||||
|     }); | ||||
| 
 | ||||
|     setupCoursesAnimation(map, coursesSimulation, stopsLayer, onClick); | ||||
|     map.render(); | ||||
| 
 | ||||
|     map.on("singleclick", ev => { | ||||
|         const mousePixel = map.getPixelFromCoordinate(ev.coordinate); | ||||
|         const maxDistance = sizes.courseSize + sizes.courseInnerBorder; | ||||
|         const clicked = []; | ||||
| 
 | ||||
|         for (const course of Object.values(coursesSimulation.courses)) { | ||||
|             const coursePixel = map.getPixelFromCoordinate(course.position); | ||||
|             const dx = mousePixel[0] - coursePixel[0]; | ||||
|             const dy = mousePixel[1] - coursePixel[1]; | ||||
|             const distance = dx * dx + dy * dy; | ||||
| 
 | ||||
|             if (distance <= maxDistance * maxDistance) { | ||||
|                 clicked.push(course.id); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Sort selected courses in increasing departing/arrival time
 | ||||
|         clicked.sort((id1, id2) => { | ||||
|             const course1 = coursesSimulation.courses[id1]; | ||||
|             const course2 = coursesSimulation.courses[id2]; | ||||
| 
 | ||||
|             const time1 = ( | ||||
|                 course1.state === "moving" | ||||
|                 ? course1.arrivalTime | ||||
|                 : course1.departureTime | ||||
|             ); | ||||
| 
 | ||||
|             const time2 = ( | ||||
|                 course2.state === "moving" | ||||
|                 ? course2.arrivalTime | ||||
|                 : course2.departureTime | ||||
|             ); | ||||
| 
 | ||||
|             return time1 - time2; | ||||
|         }); | ||||
| 
 | ||||
|         onClick(clicked); | ||||
|     }); | ||||
| 
 | ||||
|     return map; | ||||
| }; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue