transitive-js
Version:
A tool for generating dynamic stylized transit maps that are easy to understand.
1 lines • 80.6 kB
JavaScript
"use strict";function t(t){return t&&"object"==typeof t&&"default"in t?t.default:t}Object.defineProperty(exports,"__esModule",{value:!0});var e=t(require("d3")),s=t(require("component-emitter")),r=require("lodash"),i=t(require("sphericalmercator")),n=t(require("priorityqueuejs")),o=t(require("svg.js")),h=t(require("rounded-rect")),a=t(require("measure-text"));function d(t,e,s,r){return Math.sqrt((s-t)*(s-t)+(r-e)*(r-e))}function l(t,e,s,r,i,n){const o=function(t,e,s,r,i,n){return(s-t)*(n-e)-(i-t)*(r-e)}(t,e,s,r,i,n);return 0===o?0:o/Math.abs(o)}function c(t,e,s,r,i,n,o,h){o=Math.abs(o)/o;let a=Math.PI/2-Math.abs(n)/2;const d=f(m({x:s-t,y:r-e},o*a)),l=t+i*d.x,c=e+i*d.y;let g={x:-(p=d).x,y:-p.y};var p;return a=Math.abs(n)*h*o,g=f(m(g,a)),{x:l+i*g.x,y:c+i*g.y}}function g(t,e){let s=Math.atan(e/t);return t<0&&s<=0?s+=Math.PI:t<0&&s>=0&&(s-=Math.PI),s}function p(t,e,s,r,i,n,o,h){const a=((n-e)*o-(i-t)*h)/(o*r-h*s),d=((n-e)*s-(i-t)*r)/(o*r-h*s);return{intersect:a>-1e-6&&d>-1e-6,u:a,v:d}}function u(t){return function(t,e,s=1e-6){return Math.abs(t-0)<s}(t.x)?t.y>0:t.x>0}function f(t){const e=Math.sqrt(t.x*t.x+t.y*t.y);return{x:t.x/e,y:t.y/e}}function m(t,e){return{x:t.x*Math.cos(e)-t.y*Math.sin(e),y:t.x*Math.sin(e)+t.y*Math.cos(e)}}const x=new i;function y(t){return t+(isFinite(t)?"px":"")}class E{constructor(t){this.parent=t,this.sortableType="LABEL"}getText(){return this.labelText||(this.labelText=this.initText()),this.labelText}initText(){return"function"==typeof this.parent.getName?this.parent.getName():null}render(t){throw new Error("method not defined by subclass!")}refresh(t){}setVisibility(t){this.svgGroup&&this.svgGroup.attr("display",t?"initial":"none")}getBBox(){return null}intersects(t){return null}intersectsBBox(t){const e=this.getBBox(this.orientation);return e.x<=t.x+t.width&&t.x<=e.x+e.width&&e.y<=t.y+t.height&&t.y<=e.y+e.height}isFocused(){return this.parent.isFocused()}getZIndex(){return 1e6}}class w extends E{constructor(t){super(t),this.labelAngle=0,this.labelPosition=1}initText(){return this.parent.getName()}render(t){const e=this.getText();if(!e||!this.labelAnchor)return;const s={x:this.labelAnchor.x,y:this.labelAnchor.y-this.textHeight/2},r={fill:"#000","font-family":t.styler.compute2("labels","font-family",this.parent),"font-size":y(this.fontSize),"text-anchor":this.labelPosition>0?"start":"end"};t.drawText(e,s,Object.assign({},r,{fill:"none",stroke:"#fff","stroke-opacity":.75,"stroke-width":3})),t.drawText(e,s,r)}setOrientation(t){this.orientation=t;const e=this.parent.getMarkerBBox();if(!e)return;let s,r;"E"===t?(s=e.x+e.width+5,r=e.y+e.height/2,this.labelPosition=1,this.labelAngle=0):"W"===t?(s=e.x-5,r=e.y+e.height/2,this.labelPosition=-1,this.labelAngle=0):"NE"===t?(s=e.x+e.width+5,r=e.y-5,this.labelPosition=1,this.labelAngle=-45):"SE"===t?(s=e.x+e.width+5,r=e.y+e.height+5,this.labelPosition=1,this.labelAngle=45):"NW"===t?(s=e.x-5,r=e.y-5,this.labelPosition=-1,this.labelAngle=45):"SW"===t?(s=e.x-5,r=e.y+e.height+5,this.labelPosition=-1,this.labelAngle=-45):"N"===t?(s=e.x+e.width/2,r=e.y-5,this.labelPosition=1,this.labelAngle=-90):"S"===t&&(s=e.x+e.width/2,r=e.y+e.height+5,this.labelPosition=-1,this.labelAngle=-90),this.labelAnchor={x:s,y:r}}getBBox(){if("E"===this.orientation)return{height:this.textHeight,width:this.textWidth,x:this.labelAnchor.x,y:this.labelAnchor.y-this.textHeight};if("W"===this.orientation)return{height:this.textHeight,width:this.textWidth,x:this.labelAnchor.x-this.textWidth,y:this.labelAnchor.y-this.textHeight};if("N"===this.orientation)return{height:this.textWidth,width:this.textHeight,x:this.labelAnchor.x-this.textHeight,y:this.labelAnchor.y-this.textWidth};if("S"===this.orientation)return{height:this.textWidth,width:this.textHeight,x:this.labelAnchor.x-this.textHeight,y:this.labelAnchor.y};const t=this.textWidth*Math.sqrt(2)/2;return"NE"===this.orientation?{height:t,width:t,x:this.labelAnchor.x,y:this.labelAnchor.y-t}:"SE"===this.orientation?{height:t,width:t,x:this.labelAnchor.x,y:this.labelAnchor.y}:"NW"===this.orientation?{height:t,width:t,x:this.labelAnchor.x-t,y:this.labelAnchor.y-t}:"SW"===this.orientation?{height:t,width:t,x:this.labelAnchor.x-t,y:this.labelAnchor.y}:void 0}intersects(t){return t instanceof E?this.intersectsBBox(t.getBBox()):!!(t.x&&t.y&&t.width&&t.height)&&this.intersectsBBox(t)}runFocusTransition(t,e){this.mainLabel&&(this.parent.isFocused()&&this.setVisibility(!0),this.mainLabel.transition().style("opacity",this.parent.isFocused()?1:0).call(e))}}class b{constructor(t){for(const e in t)this[e]=t[e];this.paths=[],this.renderData=[],this.label=new w(this),this.renderLabel=!0,this.focused=!0,this.sortableType="POINT",this.placeOffsets={x:0,y:0},this.zIndex=1e4}getId(){throw new Error("method not defined by subclass!")}getElementId(){return this.getType().toLowerCase()+"-"+this.getId()}getType(){throw new Error("method not defined by subclass!")}getName(){return`${this.getType()} point (ID=${this.getId()})`}getLat(){return 0}getLon(){return 0}containsSegmentEndPoint(){return!1}containsBoardPoint(){return!1}containsAlightPoint(){return!1}containsTransferPoint(){return!1}getPatterns(){return[]}render(t){}addRenderData(){}clearRenderData(){}containsFromPoint(){return!1}containsToPoint(){return!1}constructMergedMarker(t){const e=this.getRenderDataArray(),s=[],r=[];e.forEach((function(t){const e=t.y;s.push(t.x),r.push(e)}));const i=Math.min.apply(Math,s),n=Math.min.apply(Math,r),o=Math.max.apply(Math,s),h=Math.max.apply(Math,r),a=t.styler.compute(t.styler.stops_merged["marker-type"],t,{owner:this}),d=t.styler.compute(t.styler.stops_merged.r,t,{owner:this});let l,c,g;if("circle"===a&&d)l=c=2*d,g=d;else{const e=o-i,s=h-n,r=t.styler.compute(t.styler.stops_merged["marker-padding"],t,{owner:this})||0,d=t.styler.compute(t.styler[this.patternStylerKey].r,t,{owner:this});g=parseFloat(d)+r,"circle"===a?(l=c=Math.max(e,s)+2*g,g=l/2):(l=e+2*g,c=s+2*g,"rectangle"===a&&(g=0))}return{height:c,rx:g,ry:g,width:l,x:(i+o)/2-l/2,y:(n+h)/2-c/2}}initMarkerData(t){if(("STOP"===this.getType()||"MULTI"===this.getType())&&(this.mergedMarkerData=this.constructMergedMarker(t),this.placeOffsets={x:0,y:0},this.adjacentPlace)){const e=t.styler.compute(t.styler.places.r,t,{owner:this.adjacentPlace}),s=t.xScale.compute(this.adjacentPlace.worldX),i=t.yScale.compute(this.adjacentPlace.worldY),n=this.mergedMarkerData.width/2,o=this.mergedMarkerData.x+n-s,h=this.mergedMarkerData.y+n-i,a=Math.sqrt(o*o+h*h);if(e+n>a){const s=(e+n)/a;this.placeOffsets={x:o*s-o,y:h*s-h},this.mergedMarkerData.x+=this.placeOffsets.x,this.mergedMarkerData.y+=this.placeOffsets.y,r.forEach(this.graphVertex.incidentEdges(),e=>{r.forEach(e.renderSegments,e=>{e.refreshRenderData(t)})})}}}getMarkerBBox(){return this.markerBBox}setFocused(t){this.focused=t}isFocused(){return!0===this.focused}runFocusTransition(t,e){}setAllPatternsFocused(){}getZIndex(){return this.zIndex}getAverageCoord(){const t=this.getRenderDataArray();let e=0,s=0;return r.forEach(t,t=>{e+=t.x,s+=t.y}),{x:e/t.length,y:s/t.length}}hasRenderData(){const t=this.getRenderDataArray();return t&&t.length>0}makeDraggable(t){}toString(){return`${this.getType()} point: ${this.getId()} (${this.getName()})`}}class T extends b{constructor(t){if(super(t),t&&t.stop_lat&&t.stop_lon){const e=x.forward([t.stop_lon,t.stop_lat]);this.worldX=e[0],this.worldY=e[1]}this.patterns=[],this.patternRenderData={},this.patternFocused={},this.patternCount=0,this.patternStylerKey="stops_pattern",this.isSegmentEndPoint=!1}getId(){return this.stop_id}getType(){return"STOP"}getName(){return this.stop_name?this.stop_name:`Unnamed Stop (ID=${this.getId()})`}getLat(){return this.stop_lat}getLon(){return this.stop_lon}containsSegmentEndPoint(){return this.isSegmentEndPoint}containsBoardPoint(){return this.isBoardPoint}containsAlightPoint(){return this.isAlightPoint}containsTransferPoint(){return this.isTransferPoint}getPatterns(){return this.patterns}addPattern(t){-1===this.patterns.indexOf(t)&&this.patterns.push(t)}addRenderData(t){if("TRANSIT"===t.rEdge.getType()){const e={getZIndex:function(){return this.owner.graphVertex?this.owner.getZIndex():this.rEdge.getZIndex()+1},owner:this,sortableType:"POINT_STOP_PATTERN"};for(const s in t)e[s]=t[s];this.patternRenderData[t.rEdge.patternIds]=e,r.forEach(t.rEdge.patterns,t=>{this.addPattern(t)})}this.patternCount=Object.keys(this.patternRenderData).length}isPatternFocused(t){return!(t in this.patternFocused)||this.patternFocused[t]}setPatternFocused(t,e){this.patternFocused[t]=e}setAllPatternsFocused(t){for(const e in this.patternRenderData)this.patternFocused[e]=t}render(t){if(super.render(t),0===this.patternCount)return;this.initMarkerData(t);const e=t.styler;this.isSegmentEndPoint&&this.mergedMarkerData&&(t.drawRect({x:this.mergedMarkerData.x,y:this.mergedMarkerData.y},{fill:e.compute2("stops_merged","fill",this),height:this.mergedMarkerData.height,rx:this.mergedMarkerData.rx,ry:this.mergedMarkerData.ry,stroke:e.compute2("stops_merged","stroke",this),"stroke-width":e.compute2("stops_merged","stroke-width",this),width:this.mergedMarkerData.width}),this.markerBBox={height:this.mergedMarkerData.height,width:this.mergedMarkerData.width,x:this.mergedMarkerData.x,y:this.mergedMarkerData.y})}getRenderDataArray(){const t=[];for(const e in this.patternRenderData)t.push(this.patternRenderData[e]);return t}clearRenderData(){this.patternRenderData={},this.mergedMarkerData=null,this.placeOffsets={x:0,y:0}}}class P extends b{constructor(t){if(super(t),t&&t.place_lat&&t.place_lon){const e=x.forward([t.place_lon,t.place_lat]);this.worldX=e[0],this.worldY=e[1]}this.zIndex=1e5}getType(){return"PLACE"}getId(){return this.place_id}getName(){return this.place_name}getLat(){return this.place_lat}getLon(){return this.place_lon}containsSegmentEndPoint(){return!0}containsFromPoint(){return"from"===this.getId()}containsToPoint(){return"to"===this.getId()}addRenderData(t){this.renderData.push(t)}getRenderDataArray(){return this.renderData}clearRenderData(){this.renderData=[]}render(t){super.render(t);const e=t.styler;if(!this.renderData)return;if("none"===e.compute2("places","display",this))return;this.renderXY={x:t.xScale.compute(t.activeZoomFactors.useGeographicRendering?this.worldX:this.graphVertex.x),y:t.yScale.compute(t.activeZoomFactors.useGeographicRendering?this.worldY:this.graphVertex.y)};const s=e.compute2("places","r",this)||10;t.drawCircle(this.renderXY,{fill:e.compute2("places","fill",this)||"#fff",r:s,stroke:e.compute2("places","stroke",this)||"#000","stroke-width":e.compute2("places","stroke-width",this)||2}),this.markerBBox={height:2*s,width:2*s,x:this.renderXY.x-s,y:this.renderXY.y-s}}}class S{constructor(){this.points=[]}addPoint(t){-1===this.points.indexOf(t)&&this.points.push(t)}mergeVertices(t){const e=[];r.forEach(this.points,t=>{e.push(t.graphVertex)}),t.mergeVertices(e)}}class V extends b{constructor(t){super(),this.points=[],t&&r.forEach(t,t=>{this.addPoint(t)}),this.renderData=[],this.id="multi",this.toPoint=this.fromPoint=null,this.patternStylerKey="multipoints_pattern"}getId(){return this.id}getType(){return"MULTI"}getName(){if(this.fromPoint)return this.fromPoint.getName();if(this.toPoint)return this.toPoint.getName();let t=null;return r.forEach(this.points,e=>{"TURN"!==e.getType()&&(!t||e.getName().length<t.length)&&(t=e.getName())}),t}containsSegmentEndPoint(){for(let t=0;t<this.points.length;t++)if(this.points[t].containsSegmentEndPoint())return!0;return!1}containsBoardPoint(){for(let t=0;t<this.points.length;t++)if(this.points[t].containsBoardPoint())return!0;return!1}containsAlightPoint(){for(let t=0;t<this.points.length;t++)if(this.points[t].containsAlightPoint())return!0;return!1}containsTransferPoint(){for(let t=0;t<this.points.length;t++)if(this.points[t].containsTransferPoint())return!0;return!1}containsFromPoint(){return null!==this.fromPoint}containsToPoint(){return null!==this.toPoint}getPatterns(){const t=[];return r.forEach(this.points,e=>{e.patterns&&r.forEach(e.patterns,e=>{-1===t.indexOf(e)&&t.push(e)})}),t}addPoint(t){-1===this.points.indexOf(t)&&(this.points.push(t),this.id+="-"+t.getId(),t.containsFromPoint()&&(this.fromPoint=t),t.containsToPoint()&&(this.toPoint=t),this.calcWorldCoords())}calcWorldCoords(){let t=0,e=0;r.forEach(this.points,s=>{t+=s.worldX,e+=s.worldY}),this.worldX=t/this.points.length,this.worldY=e/this.points.length}addRenderData(t){0===t.offsetX&&0===t.offsetY||(this.hasOffsetPoints=!0),this.renderData.push(t)}clearRenderData(){this.hasOffsetPoints=!1,this.renderData=[]}render(t){if(super.render(t),!this.renderData)return;const e=this.renderData.map(t=>t.x),s=this.renderData.map(t=>t.y),r=Math.min(...e),i=Math.max(...e),n=Math.min(...s),o=r-6,h=n-6,a=i-r+12,d=Math.max(...s)-n+12;t.drawRect({x:o,y:h},{fill:"#fff",height:d,rx:6,ry:6,stroke:"#000","stroke-width":2,width:a}),this.markerBBox={height:d,width:a,x:o,y:h}}initMergedMarker(t){this.fromPoint||this.toPoint?this.mergedMarker=this.markerSvg.append("g").append("circle").datum({owner:this}).attr("class","transitive-multipoint-marker-merged"):(this.hasOffsetPoints||this.renderData.length>1)&&(this.mergedMarker=this.markerSvg.append("g").append("rect").datum({owner:this}).attr("class","transitive-multipoint-marker-merged"))}getRenderDataArray(){return this.renderData}setFocused(t){this.focused=t,r.forEach(this.points,e=>{e.setFocused(t)})}runFocusTransition(t,e){if(this.mergedMarker){const s=t.styler.compute(t.styler.multipoints_merged.stroke,t,{owner:this});this.mergedMarker.transition().style("stroke",s).call(e)}this.label&&this.label.runFocusTransition(t,e)}}class A{constructor(t){this.transitive=t,this.clusters=[],this.clusterLookup={};const s=[];r.forEach(Object.values(t.stops),t=>{t.used&&s.push(t)},this),r.forEach(Object.values(t.turnPoints),t=>{s.push(t)},this);const i=e.geom.voronoi().x((function(t){return t.worldX})).y((function(t){return t.worldY})).links(s);r.forEach(i,t=>{if(d(t.source.worldX,t.source.worldY,t.target.worldX,t.target.worldY)<100&&("TURN"!==t.source.getType()||"TURN"!==t.target.getType())){const e=t.source in this.clusterLookup,s=t.target in this.clusterLookup;if(e&&!s)this.addPointToCluster(t.target,this.clusterLookup[t.source]);else if(!e&&s)this.addPointToCluster(t.source,this.clusterLookup[t.target]);else if(!e&&!s){const e=new S;this.clusters.push(e),this.addPointToCluster(t.source,e),this.addPointToCluster(t.target,e)}}},this),this.vertexPoints=[],r.forEach(this.clusters,t=>{const e=new V(t.points);this.vertexPoints.push(e),r.forEach(t.points,t=>{t.multipoint=e})})}addPointToCluster(t,e){e.addPoint(t),this.clusterLookup[t]=e}clearMultiPoints(){r.forEach(this.clusters,t=>{r.forEach(t.points,t=>{t.multipoint=null})})}getVertexPoints(t){if(!t)return this.vertexPoints;const e=this.vertexPoints.concat();return r.forEach(t,t=>{t.multipoint||e.push(t)}),e}}let v=0;class I{constructor(t,e,s,r){this.id=v++,this.graphEdge=t,this.forward=e,this.type=s,this.points=[],this.clearOffsets(),this.focused=!0,this.sortableType="SEGMENT",this.useGeographicRendering=r}clearGraphData(){this.graphEdge=null,this.edgeFromOffset=0,this.edgeToOffset=0}addPattern(t){this.patterns||(this.patterns=[]),-1===this.patterns.indexOf(t)&&(this.patterns.push(t),this.patternIds=M(this.patterns))}addPathSegment(t){this.pathSegments||(this.pathSegments=[]),-1===this.pathSegments.indexOf(t)&&(this.pathSegments.push(t),this.pathSegmentIds=M(this.pathSegments))}getId(){return this.id}getType(){return this.type}setFromOffset(t){this.fromOffset=t}setToOffset(t){this.toOffset=t}clearOffsets(){this.fromOffset=0,this.toOffset=0}getAlignmentVector(t){return this.graphEdge.getFromAlignmentId()===t?this.graphEdge.fromVector:this.graphEdge.getToAlignmentId()===t?this.graphEdge.toVector:null}offsetAlignment(t,e){this.graphEdge.getFromAlignmentId()===t&&this.setFromOffset(u(this.graphEdge.fromVector)?e:-e),this.graphEdge.getToAlignmentId()===t&&this.setToOffset(u(this.graphEdge.toVector)?e:-e)}setFocused(t){this.focused=t}refreshRenderData(t){if(this.graphEdge.fromVertex.x===this.graphEdge.toVertex.x&&this.graphEdge.fromVertex.y===this.graphEdge.toVertex.y)return void(this.renderData=[]);this.lineWidth=this.computeLineWidth(t,!0);const e=this.fromOffset*this.lineWidth,s=this.toOffset*this.lineWidth;this.renderData=this.useGeographicRendering&&this.graphEdge.geomCoords?this.graphEdge.getGeometricCoords(e,s,t,this.forward):this.graphEdge.getRenderCoords(e,s,t,this.forward);const i=this.renderData[0],n=this.renderData[this.renderData.length-1];let o;this.graphEdge.fromVertex.isInternal||(o=this.forward?i:n,o&&this.graphEdge.fromVertex.point.addRenderData({rEdge:this,x:o.x,y:o.y})),o=this.forward?n:i,o&&this.graphEdge.toVertex.point.addRenderData({rEdge:this,x:o.x,y:o.y}),r.forEach(this.graphEdge.pointArray,(e,s)=>{if("TURN"===e.getType())return;const r=(s+1)/(this.graphEdge.pointArray.length+1),i=this.graphEdge.coordAlongEdge(this.forward?r:1-r,this.renderData,t);i&&e.addRenderData({rEdge:this,x:i.x,y:i.y})})}computeLineWidth(t,e){const s=t.styler;if(s&&t){const r=s.compute(s.segments.envelope,t,this);if(r&&e)return parseFloat(r.substring(0,r.length-2),10)-2;{const e=s.compute(s.segments["stroke-width"],t,this);return parseFloat(e.substring(0,e.length-2),10)-2}}}isFocused(){return!0===this.focused}getZIndex(){return 1e4}intersect(t){if(this.graphEdge.renderedEdges.length!==t.graphEdge.renderedEdges.length)return;const e=this.graphEdge.commonVertex(t.graphEdge);if(!e||e.point.isSegmentEndPoint)return;const s=e===this.graphEdge.fromVertex&&this.forward||e===this.graphEdge.toVertex&&!this.forward,r=e===t.graphEdge.fromVertex&&t.forward||e===t.graphEdge.toVertex&&!t.forward,i=s?this.renderData[0]:this.renderData[this.renderData.length-1],n=this.graphEdge.getVector(e),o=r?t.renderData[0]:t.renderData[t.renderData.length-1],h=t.graphEdge.getVector(e);if(!i||!o||!n||!h||i.x===o.x&&i.y===o.y)return;const a=function(t,e,s,r,i,n,o,h){const a=(t-s)*(n-h)-(e-r)*(i-o);return 0===a?{intersect:!1}:{intersect:!0,x:((t*r-e*s)*(i-o)-(t-s)*(i*h-n*o))/a,y:((t*r-e*s)*(n-h)-(e-r)*(i*h-n*o))/a}}(i.x,i.y,i.x+n.x,i.y-n.y,o.x,o.y,o.x+h.x,o.y-h.y);a.intersect&&(s?(this.renderData[0].x=a.x,this.renderData[0].y=a.y):(this.renderData[this.renderData.length-1].x=a.x,this.renderData[this.renderData.length-1].y=a.y),r?(t.renderData[0].x=a.x,t.renderData[0].y=a.y):(t.renderData[t.renderData.length-1].x=a.x,t.renderData[t.renderData.length-1].y=a.y),e.point.addRenderData({rEdge:this,x:a.x,y:a.y}))}findExtension(t){const e=t.incidentEdges(this.graphEdge),s=this.patternIds||this.pathSegmentIds;for(let t=0;t<e.length;t++){const r=e[t].renderedEdges;for(let t=0;t<r.length;t++){const e=r[t];if(s===(e.patternIds||e.pathSegmentIds))return e}}}toString(){return`RenderedEdge ${this.id} type=${this.type} on ${this.graphEdge.toString()} w/ patterns ${this.patternIds} fwd=${this.forward}`}}function M(t){const e=[];return r.forEach(t,t=>{e.push(t.getId())}),e.sort(),e.join(",")}let R=0;class k{constructor(t){this.id=R++,this.renderedEdges=[],this.pathSegment=t,t&&(this.type=t.type),this.focused=!0}getId(){return this.id}getType(){return this.type}addRenderedEdge(t){this.renderedEdges.push(t)}render(t){const e=t.styler;if(this.renderData=[],r.forEach(this.renderedEdges,t=>{this.renderData=this.renderData.concat(t.renderData)}),this.pathSegment.journeySegment&&this.pathSegment.journeySegment.arc){const t=this.renderData[0],e=this.renderData[this.renderData.length-1],s=m(f({x:e.x-t.x,y:e.y-t.y}),-Math.PI/2),r=d(t.x,t.y,e.x,e.y);this.renderData=[t,{arc:-45,ex:(e.x+t.x)/2+s.x*(r/4),ey:(e.y+t.y)/2+s.y*(r/4),radius:.75*r,x:e.x,y:e.y},e]}t.drawPath(this.renderData,{fill:"none",stroke:e.compute2("segments","stroke",this),"stroke-dasharray":e.compute2("segments","stroke-dasharray",this),"stroke-linecap":e.compute2("segments","stroke-linecap",this),"stroke-width":e.compute2("segments","stroke-width",this)})}setFocused(t){this.focused=t}isFocused(){return this.focused}runFocusTransition(t,e){const s=t.styler.compute(t.styler.segments.stroke,t,this);this.lineGraph.transition().style("stroke",s).call(e)}getZIndex(){return this.zIndex}computeLineWidth(t,e){const s=t.styler;if(s&&t){const r=s.compute(s.segments.envelope,t,this);if(r&&e)return parseFloat(r.substring(0,r.length-2),10)-2;{const e=s.compute(s.segments["stroke-width"],t,this);return parseFloat(e.substring(0,e.length-2),10)-2}}}compareTo(t){return"TRANSIT"===this.type&&"TRANSIT"!==t.type?-1:"TRANSIT"===t.type&&"TRANSIT"!==this.type?1:"TRANSIT"===this.type&&"TRANSIT"===t.type?this.mode&&t.mode&&this.mode!==t.mode?this.mode>t.mode:this.getId()<t.getId():void 0}getLabelTextArray(){const t=[];return r.forEach(this.patterns,e=>{const s=e.route.route_short_name;-1===t.indexOf(s)&&t.push(s)}),t}getLabelAnchors(t,e){const s=[];this.computeRenderLength(t);const r=Math.floor(this.renderLength/e),i=e/this.renderLength;for(let e=0;e<r;e++){const r=this.coordAlongRenderedPath(e%2==0?.5+e/2*i:.5-(e+1)/2*i,t);r&&s.push(r)}return s}coordAlongRenderedPath(t,e){const s=t*this.renderLength;let r=0;for(let t=0;t<this.renderedEdges.length;t++){const i=this.renderedEdges[t],n=i.graphEdge.getRenderLength(e);if(s<=r+n)return i.graphEdge.coordAlongEdge((s-r)/n,i.renderData,e);r+=n}}computeRenderLength(t){this.renderLength=0,r.forEach(this.renderedEdges,e=>{this.renderLength+=e.graphEdge.getRenderLength(t)})}toString(){return`RenderedSegment ${this.id} on ${this.pathSegment?this.pathSegment.toString():" (null segment)"}`}}let D=0;class L{constructor(t,e,s){this.id=D++,this.pointArray=t,this.fromVertex=e,this.toVertex=s,this.pathSegments=[],this.renderedEdges=[]}getId(){return this.id}getLength(){const t=this.toVertex.x-this.fromVertex.x,e=this.toVertex.y-this.fromVertex.y;return Math.sqrt(t*t+e*e)}getWorldLength(){return this.worldLength||this.calculateWorldLengthAndMidpoint(),this.worldLength}getWorldMidpoint(){return this.worldMidpoint||this.calculateWorldLengthAndMidpoint(),this.worldMidpoint}calculateWorldLengthAndMidpoint(){const t=[this.fromVertex.point].concat(this.pointArray,[this.toVertex.point]);this.worldLength=0;for(var e=0;e<t.length-1;e++)this.worldLength+=d(t[e].worldX,t[e].worldY,t[e+1].worldX,t[e+1].worldY);if(0===this.worldLength)this.worldMidpoint={x:this.fromVertex.point.worldX,y:this.fromVertex.point.worldY};else{let s=0;for(e=0;e<t.length-1;e++){const r=d(t[e].worldX,t[e].worldY,t[e+1].worldX,t[e+1].worldY);if((s+r)/this.worldLength>=.5){const i=(.5-s/this.worldLength)/(r/this.worldLength);this.worldMidpoint={x:t[e].worldX+i*(t[e+1].worldX-t[e].worldX),y:t[e].worldY+i*(t[e+1].worldY-t[e].worldY)},this.pointsBeforeMidpoint=e,this.pointsAfterMidpoint=this.pointArray.length-e;break}s+=r}}}isAxial(){return this.toVertex.x===this.fromVertex.x||this.toVertex.y===this.fromVertex.y}hasCurvature(){return null!==this.elbow}replaceVertex(t,e){t===this.fromVertex&&(this.fromVertex=e),t===this.toVertex&&(this.toVertex=e)}addPathSegment(t){this.pathSegments.push(t)}copyPathSegments(t){r.forEach(t.pathSegments,t=>{this.addPathSegment(t)})}getPathSegmentIds(t){const e=this.pathSegments.map(t=>t.id);return e.sort(),e.join(",")}addRenderedEdge(t){-1===this.renderedEdges.indexOf(t)&&this.renderedEdges.push(t)}calculateGeometry(t,e){this.angleConstraintR=(e=e||45)*Math.PI/180,this.fx=this.fromVertex.point.worldX,this.fy=this.fromVertex.point.worldY,this.tx=this.toVertex.point.worldX,this.ty=this.toVertex.point.worldY;const s=this.getWorldMidpoint(),r=g(s.x-this.fx,s.y-this.fy);this.constrainedFromAngle=Math.round(r/this.angleConstraintR)*this.angleConstraintR;const i=Math.abs(this.constrainedFromAngle-r);this.fvx=Math.cos(this.constrainedFromAngle),this.fvy=Math.sin(this.constrainedFromAngle);const n=g(s.x-this.tx,s.y-this.ty);this.constrainedToAngle=Math.round(n/this.angleConstraintR)*this.angleConstraintR;const o=Math.abs(this.constrainedToAngle-n);this.tvx=Math.cos(this.constrainedToAngle),this.tvy=Math.sin(this.constrainedToAngle);const h=f({x:this.toVertex.x-this.fromVertex.x,y:this.toVertex.y-this.fromVertex.y});if(!C(this.fvx,this.fvy,-this.tvx,-this.tvy,.01)||!C(this.fvx,this.fvy,h.x,h.y,.01)){const t=this.computeEndpointIntersection();t.intersect?this.elbow={x:this.fx+t.u*this.fvx,y:this.fy+t.u*this.fvy}:Math.abs(i-o)>.087?i<o?this.adjustToAngle():this.adjustFromAngle():this.pointsAfterMidpoint<this.pointsBeforeMidpoint?this.adjustToAngle():this.adjustFromAngle()}this.fromAngle=this.constrainedFromAngle,this.toAngle=this.constrainedToAngle,this.calculateVectors(),this.calculateAlignmentIds()}adjustToAngle(){const t=l(this.fx,this.fy,this.fx+this.fvx,this.fy+this.fvy,this.tx,this.ty)>0?this.angleConstraintR:-this.angleConstraintR;let e,s=0;for(;s++<100&&(this.constrainedToAngle+=t,this.tvx=Math.cos(this.constrainedToAngle),this.tvy=Math.sin(this.constrainedToAngle),e=this.computeEndpointIntersection(),!e.intersect););this.elbow={x:this.fx+e.u*this.fvx,y:this.fy+e.u*this.fvy}}adjustFromAngle(){const t=l(this.tx,this.ty,this.tx+this.tvx,this.ty+this.tvy,this.fx,this.fy)>0?this.angleConstraintR:-this.angleConstraintR;let e,s=0;for(;s++<100&&(this.constrainedFromAngle+=t,this.fvx=Math.cos(this.constrainedFromAngle),this.fvy=Math.sin(this.constrainedFromAngle),e=this.computeEndpointIntersection(),!e.intersect););this.elbow={x:this.fx+e.u*this.fvx,y:this.fy+e.u*this.fvy}}computeEndpointIntersection(){return p(this.fx,this.fy,this.fvx,this.fvy,this.tx,this.ty,this.tvx,this.tvy)}calculateVectors(t,e){this.fromVector={x:Math.cos(this.fromAngle),y:Math.sin(this.fromAngle)},this.fromleftVector={x:-this.fromVector.y,y:this.fromVector.x},this.fromRightVector={x:this.fromVector.y,y:-this.fromVector.x},this.toVector={x:Math.cos(this.toAngle+Math.PI),y:Math.sin(this.toAngle+Math.PI)},this.toleftVector={x:-this.toVector.y,y:this.toVector.x},this.toRightVector={x:this.toVector.y,y:-this.toVector.x}}calculateAlignmentId(t,e,s){let r=Math.round(180*s/Math.PI);return r>90&&(r-=180),r<=-90&&(r+=180),90===r?"90_x"+t:r+"_y"+Math.round(e-t*Math.tan(s))}calculateAlignmentIds(){this.fromAlignmentId=this.calculateAlignmentId(this.fromVertex.x,this.fromVertex.y,this.fromAngle),this.toAlignmentId=this.calculateAlignmentId(this.toVertex.x,this.toVertex.y,this.toAngle)}hasTransit(t){for(let t=0;t<this.pathSegments.length;t++)if("TRANSIT"===this.pathSegments[t].getType())return!0;return!1}getFromAlignmentId(){return this.fromAlignmentId}getToAlignmentId(){return this.toAlignmentId}getAlignmentRange(t){let e,s,r,i;if(t===this.fromAlignmentId)e=this.fromVertex,s=this.elbow||this.toVertex;else{if(t!==this.toAlignmentId)return null;e=this.toVertex,s=this.elbow||this.fromVertex}return"90"===t.substring(0,2)?(i=Math.min(e.y,s.y),r=Math.max(e.y,s.y)):(i=Math.min(e.x,s.x),r=Math.max(e.x,s.x)),{max:r,min:i}}align(t,e){if(this.aligned||!this.hasCurvature())return;const s=this.getVector(t);Math.abs(s.x)===Math.abs(e.x)&&Math.abs(s.y)===Math.abs(e.y)||(this.curveAngle=-this.curveAngle,this.calculateGeometry()),this.aligned=!0}getGeometricCoords(t,e,s,i){const n=[],o=i?this.geomCoords:this.geomCoords.concat().reverse();return r.forEach(o,(e,r)=>{let i,h,a,d=null,l=null;const c=s.xScale.compute(e[0]),g=s.yScale.compute(e[1]);if(r>0){const t=o[r-1],e=s.xScale.compute(t[0]),i=s.yScale.compute(t[1]);if(c===e&&g===i)return;l={x:c-e,y:g-i}}if(r<o.length-1){const t=o[r+1],e=s.xScale.compute(t[0]),i=s.yScale.compute(t[1]);if(e===c&&i===g)return;d={x:e-c,y:i-g}}d&&!l?(i=f({x:d.y,y:-d.x}),h=t*i.x,a=t*i.y):!d&&l?(i=f({x:l.y,y:-l.x}),h=t*i.x,a=t*i.y):(i=f({x:d.y,y:-d.x}),h=t*i.x,a=t*i.y),n.push({x:c+h,y:g+a})}),n}getRenderCoords(t,e,s,r){const i=0===t&&0===e;this.baseRenderCoords||i||this.calculateBaseRenderCoords(s);const n=t*this.fromRightVector.x,o=t*this.fromRightVector.y,h=e*this.toRightVector.x,a=e*this.toRightVector.y,c=this.fromVertex.getRenderX(s)+n,g=this.fromVertex.getRenderY(s)-o,u=this.fromVector.x,f=-this.fromVector.y,m=this.toVertex.getRenderX(s)+h,x=this.toVertex.getRenderY(s)-a,y=-this.toVector.x,E=this.toVector.y,w=[];w.push({x:r?c:m,y:r?g:x});let b,T,P,S,V=null;if(i&&!this.isStraight()||!i&&4===this.baseRenderCoords.length){const s=p(c,g,u,f,m,x,y,E);if(s.intersect){const i=s.u,n=c+u*i,o=g+f*i;this.ccw=l(c,g,n,o,m,x);const h=this.getElbowAngle();let a=(this.getBaseRadiusPx()-this.ccw*(t+e)/2)*Math.tan(h/2);const p=d(c,g,n,o),y=d(m,x,n,o);a=Math.min(Math.min(p,y),a),b=n-this.fromVector.x*a,T=o+this.fromVector.y*a,P=n+this.toVector.x*a,S=o-this.toVector.y*a;const E=function(t,e){return e/2/Math.sin(t/2)}(h,d(b,T,P,S)),A=h*(180/Math.PI)*(this.ccw<0?1:-1);r?(w.push({len:d(c,g,b,T),x:b,y:T}),w.push({arc:A,ex:n,ey:o,len:h*E,radius:E,x:P,y:S}),V=d(P,S,m,x)):(w.push({len:d(m,x,P,S),x:P,y:S}),w.push({arc:-A,len:h*E,radius:E,x:b,y:T}),V=d(b,T,c,g))}}return null===V&&(V=d(c,g,m,x)),w.push({len:V,x:r?m:c,y:r?x:g}),w}calculateBaseRenderCoords(t){this.baseRenderCoords=this.getRenderCoords(0,0,t,!0)}isStraight(){return Math.abs(this.fromVector.x-this.toVector.x)<1e-5&&Math.abs(this.fromVector.y-this.toVector.y)<1e-5}getBaseRadiusPx(){return 15}getElbowAngle(){const t=this.fromVector.x-this.toVector.x,e=this.fromVector.y-this.toVector.y,s=Math.sqrt(t*t+e*e)/2;return 2*Math.asin(s)}getRenderLength(t){if(this.baseRenderCoords||this.calculateBaseRenderCoords(t),!this.renderLength){this.renderLength=0;for(let t=1;t<this.baseRenderCoords.length;t++)this.renderLength+=this.baseRenderCoords[t].len}return this.renderLength}coordAlongEdge(t,e,s){if(this.baseRenderCoords||this.calculateBaseRenderCoords(s),e.length!==this.baseRenderCoords.length)return this.coordAlongOffsetEdge(t,e,s);const r=this.getRenderLength();return this.coordAlongRefEdge(t*r,e,this.baseRenderCoords)}coordAlongOffsetEdge(t,e){let s=0;for(let t=1;t<e.length;t++){const r=e[t-1],i=e[t];i.len||(i.len=d(r.x,r.y,i.x,i.y)),s+=i.len}return this.coordAlongRefEdge(t*s,e)}coordAlongRefEdge(t,e,s=e){let r=0;for(let i=1;i<s.length;i++){const n=s[i].len,{arc:o,radius:h,x:a,y:d}=e[i];if(t<r+n){const s=(t-r)/n;if(o){const t=Math.PI*o/180,[r,i,n]=e,a=l(r.x,r.y,i.x,i.y,n.x,n.y);return c(i.x,i.y,n.x,n.y,h,t,a,s)}return{x:e[i-1].x+(a-e[i-1].x)*s,y:e[i-1].y+(d-e[i-1].y)*s}}r+=n}}clearRenderData(){this.baseRenderCoords=null,this.renderLength=null}getVector(t){return t===this.fromVertex?this.fromVector:t===this.toVertex?this.toVector:void 0}oppositeVertex(t){return t===this.toVertex?this.fromVertex:t===this.fromVertex?this.toVertex:null}commonVertex(t){return this.fromVertex===t.fromVertex||this.fromVertex===t.toVertex?this.fromVertex:this.toVertex===t.fromVertex||this.toVertex===t.toVertex?this.toVertex:null}setPointLabelPosition(t,e){this.fromVertex.point!==e&&(this.fromVertex.point.labelPosition=t),this.toVertex.point!==e&&(this.toVertex.point.labelPosition=t),r.forEach(this.pointArray,s=>{s!==e&&(s.labelPosition=t)})}isNonTransitPath(){return 1===this.pathSegments.length&&"TRANSIT"!==this.pathSegments[0]&&1===this.pathSegments[0].path.segments.length}toString(){return"Edge "+this.getId()+" ("+this.fromVertex.toString()+" to "+this.toVertex.toString()+")"}}function C(t,e,s,r,i){return i=i||0,Math.abs(t-s)<i&&Math.abs(e-r)<i}class _{constructor(t,e,s){this.fromVertex=t,this.toVertex=e,this.type=s,this.edges=[],this.commonPoints=null,this.worldLength=0}addEdge(t){this.edges.push(t),t.edgeGroup=this,this.worldLength=Math.max(this.worldLength,t.getWorldLength()),this.commonPoints=null===this.commonPoints?t.pointArray.slice():this.commonPoints.concat(t.pointArray.filter(t=>-1!==this.commonPoints.indexOf(t)))}getWorldLength(){return this.worldLength}getInternalVertexPQ(){const t=[this.fromVertex.point].concat(this.commonPoints,[this.toVertex.point]),e=new n((function(t,e){return t.weight-e.weight}));for(let s=1;s<t.length-1;s++){const r=this.getInternalVertexWeight(t,s);e.enq({point:t[s],weight:r})}return e}getInternalVertexWeight(t,e){const s=t[e-1].worldX,r=t[e-1].worldY,i=t[e].worldX,n=t[e].worldY,o=t[e+1].worldX,h=t[e+1].worldY,a=d(s,r,i,n),l=d(i,n,o,h),c=function(t,e,s,r,i,n){const o=d(t,e,s,r),h=d(s,r,i,n),a=d(t,e,i,n);return Math.acos((h*h+o*o-a*a)/(2*h*o))}(s,r,i,n,o,h),g=this.getWorldLength();return a/g+l/g+Math.abs(Math.PI-c)/Math.PI}hasTransit(){for(let t=0;t<this.edges.length;t++)if(this.edges[t].hasTransit())return!0;return!1}isNonTransitPath(){return 1===this.edges.length&&this.edges[0].isNonTransitPath()}getTurnPoints(t){return t=t||.75*Math.PI,this.commonPoints.filter(e=>"TURN"===e.getType()&&Math.abs(e.turnAngle)<t)}}let B=0;class O{constructor(t,e,s){this.id=B++,this.point=t,this.point.graphVertex=this,this.x=this.origX=e,this.y=this.origY=s,this.edges=[]}getId(){return this.id}getRenderX(t){return t.xScale.compute(this.x)+this.point.placeOffsets.x}getRenderY(t){return t.yScale.compute(this.y)+this.point.placeOffsets.y}moveTo(t,e){this.x=t,this.y=e}incidentEdges(t){return this.edges.filter(e=>e!==t)}addEdge(t){-1===this.edges.indexOf(t)&&this.edges.push(t)}removeEdge(t){const e=this.edges.indexOf(t);-1!==e&&this.edges.splice(e,1)}toString(){return`Vertex ${this.getId()} (${this.point?this.point.toString():"no point assigned"})`}}const F=require("debug")("transitive:graph");class W{constructor(t,e){this.network=t,this.edges=[],this.vertices=[],this.edgeGroups={};for(const t in e)this.addVertex(e[t],e[t].worldX,e[t].worldY)}bounds(){let t=null,e=null,s=null,r=null;for(const i in this.vertices){const n=this.vertices[i];e=e?Math.min(e,n.x):n.x,t=t?Math.max(t,n.x):n.x,r=r?Math.min(r,n.y):n.y,s=s?Math.max(s,n.y):n.y}return[[e||-20037508.34,r||-20037508.34],[t||20037508.34,s||20037508.34]]}addVertex(t,e,s){if(void 0===e||void 0===s){const r=x.forward([t.getLon(),t.getLat()]);e=r[0],s=r[1]}const r=new O(t,e,s);return this.vertices.push(r),r}addEdge(t,e,s,r){if(-1===this.vertices.indexOf(e)||-1===this.vertices.indexOf(s))return void F("Error: Cannot add edge. Graph does not contain vertices.");const i=new L(t,e,s);this.edges.push(i),e.edges.push(i),s.edges.push(i);const n=this.network.transitive.options.groupEdges?this.getEdgeGroupKey(i,r):i.getId();return n in this.edgeGroups||(this.edgeGroups[n]=new _(i.fromVertex,i.toVertex,r)),this.edgeGroups[n].addEdge(i),i}removeEdge(t){const e=this.edges.indexOf(t);-1!==e&&this.edges.splice(e,1),r.forEach(t.pathSegments,e=>{e.removeEdge(t)}),t.fromVertex.removeEdge(t),t.toVertex.removeEdge(t)}getEdgeGroup(t){return this.edgeGroups[this.getEdgeGroupKey(t)]}getEdgeGroupKey(t,e){return t.fromVertex.getId()<t.toVertex.getId()?e+"_"+t.fromVertex.getId()+"_"+t.toVertex.getId():e+"_"+t.toVertex.getId()+"_"+t.fromVertex.getId()}mergeVertices(t){let e=0,s=0;const i={MULTI:[],PLACE:[],STOP:[],TURN:[]};if(r.forEach(t,t=>{t.point.getType()in i&&i[t.point.getType()].push(t)}),i.STOP.length>0&&i.PLACE.length>0||i.PLACE.length>1||i.MULTI.length>0)return;let n;if(1===i.PLACE.length&&i.TURN.length>0?n=i.PLACE[0].point:1===i.STOP.length&&i.TURN.length>0?n=i.STOP[0].point:i.STOP.length>1?(n=new V,r.forEach(i.STOP,t=>{n.addPoint(t.point)})):i.TURN.length>1&&(n=i.TURN[0].point),!n)return;const o=new O(n,0,0);r.forEach(t,i=>{e+=i.x,s+=i.y,r.forEach(i.edges.slice(),e=>{-1===t.indexOf(e.fromVertex)||-1===t.indexOf(e.toVertex)?(e.replaceVertex(i,o),o.addEdge(e)):this.removeEdge(e)});const n=this.vertices.indexOf(i);-1!==n&&this.vertices.splice(n,1)}),o.x=e/t.length,o.y=s/t.length,o.oldVertices=t,this.vertices.push(o)}sortVertices(){this.vertices.sort((t,e)=>t.point&&"PLACE"===t.point.getType()?-1:e.point&&"PLACE"===e.point.getType()?1:t.point&&"MULTI"===t.point.getType()?-1:e.point&&"MULTI"===e.point.getType()?1:t.point&&"STOP"===t.point.getType()?-1:e.point&&"STOP"===e.point.getType()?1:void 0)}getEquivalentEdge(t,e,s){for(let r=0;r<this.edges.length;r++){const i=this.edges[r];if(i.fromVertex===e&&i.toVertex===s&&t.length===i.pointArray.length&&Y(t,i.pointArray))return i;if(i.fromVertex===s&&i.toVertex===e&&t.length===i.pointArray.length&&Y(t.slice(0).reverse(),i.pointArray))return i}}splitEdgeAtInternalPoints(t,e){let s,i=[];const n=[];let o=t.fromVertex,h=[];r.forEach(t.pointArray,(r,a)=>{if(t.pointGeom&&a<t.pointGeom.length&&(h=h.concat(t.pointGeom[a])),-1!==e.indexOf(r)){const e=r.worldX,a=r.worldY,d=r.graphVertex||this.addVertex(r,e,a);d.isInternal=!0,s=this.addEdge(i,o,d,t.edgeGroup.type),s.isInternal=!0,s.copyPathSegments(t),n.push({fromVertex:o,graphEdge:s}),h.length>0&&(s.geomCoords=h),i=[],o=d,h=[]}else i.push(r)}),s=this.addEdge(i,o,t.toVertex,t.edgeGroup.type),s.isInternal=!0,s.copyPathSegments(t),t.pointGeom&&t.pointArray.length<t.pointGeom.length&&(h=h.concat(t.pointGeom[t.pointArray.length])),h.length>0&&(s.geomCoords=h),n.push({fromVertex:o,graphEdge:s}),r.forEach(t.pathSegments,e=>{const s=e.getEdgeIndex(t),i=e.edges[s].forward;let o=e.getEdgeIndex(t);r.forEach(i?n:n.reverse(),t=>{e.insertEdgeAt(o,t.graphEdge,i?t.fromVertex:t.toVertex),o++})}),this.removeEdge(t)}pruneVertices(){r.forEach(this.vertices,t=>{if(t.point.containsSegmentEndPoint())return;const e=[],s={};if(r.forEach(t.edges,r=>{const i=r.getPathSegmentIds();i in s||(s[i]=[]),s[i].push(r);const n=r.oppositeVertex(t);-1===e.indexOf(n)&&e.push(n)}),2===e.length)for(const t in s){const e=s[t];2===e.length&&this.mergeEdges(e[0],e[1])}})}mergeEdges(t,e){if(t.fromVertex===e.toVertex&&e.fromVertex===t.toVertex)return;if(t.fromVertex===e.toVertex)return void this.mergeEdges(e,t);if(t.toVertex!==e.fromVertex)return;const s=t.pointArray.concat(e.pointArray),i=this.addEdge(s,t.fromVertex,e.toVertex,t.edgeGroup.type);i.pathSegments=t.pathSegments,r.forEach(i.pathSegments,e=>{const s=e.getEdgeIndex(t);e.insertEdgeAt(s,i,i.fromVertex)}),t.geomCoords&&e.geomCoords&&(i.geomCoords=t.geomCoords.concat(e.geomCoords.length>0?e.geomCoords.slice(1):[])),F("merging:"),F(t),F(e),this.removeEdge(t),this.removeEdge(e)}snapToGrid(t){const e={};r.forEach(this.vertices,s=>{const r=Math.round(s.x/t)*t,i=Math.round(s.y/t)*t;s.x=r,s.y=i;const n=r+"_"+i;n in e?e[n].push(s):e[n]=[s]}),r.forEach(e,t=>{t.length>1&&this.mergeVertices(t)})}calculateGeometry(t,e){r.forEach(this.edges,s=>{s.calculateGeometry(t,e)})}resetCoordinates(){r.forEach(this.vertices,t=>{t.x=t.origX,t.y=t.origY})}recenter(){const t=[],s=[];r.forEach(this.vertices,e=>{t.push(e.x),s.push(e.y)});const i=e.median(t),n=e.median(s);r.forEach(this.vertices,t=>{t.x=t.x-i,t.y=t.y-n})}apply2DOffsets(){this.initComparisons();const t={},e=(e,s)=>{let r;const i=e.graphEdge.getAlignmentRange(s);if(s in t){const n=t[s];for(let t=0;t<n.length;t++)if(n[t].rangeOverlaps(i.min,i.max))return void n[t].addEdge(e,i.min,i.max);r=new G,r.addEdge(e,i.min,i.max),n.push(r)}else r=new G,r.addEdge(e,i.min,i.max),t[s]=[r]};r.forEach(this.edges,t=>{const s=t.getFromAlignmentId(),i=t.getToAlignmentId();r.forEach(t.renderedEdges,t=>{e(t,s),e(t,i)})});const s=(t,e)=>{const s=t.patternIds||t.pathSegmentIds,r=e.patternIds||e.pathSegmentIds,i=t.getAlignmentVector(this.currentAlignmentId),n=e.getAlignmentVector(this.currentAlignmentId),o=u(i)&&u(n)?1:-1,h=s+"_"+r;if(h in this.bundleComparisons)return o*this.bundleComparisons[h];const a=r+"_"+s;return a in this.bundleComparisons?o*this.bundleComparisons[a]:t.route&&e.route&&t.route.route_type!==e.route.route_type?t.route.route_type>e.route.route_type?1:-1:(t.forward&&e.forward?1:-1)*o*(s<r?-1:1)};r.forEach(Object.keys(t),e=>{r.forEach(t[e],t=>{if(t.items.length<=1)return;const i=1.2*(t.items.length-1);this.currentAlignmentId=e,t.items.sort(s),r.forEach(t.items,(t,s)=>{const n=-i/2+1.2*s;"TRANSIT"===t.getType()?r.forEach(t.patterns,t=>{t.offsetAlignment(e,n)}):t.offsetAlignment(e,n)})})})}initComparisons(){this.bundleComparisons={},r.forEach(this.vertices,t=>{const e=t.incidentEdges(),s={};r.forEach(e,e=>{const r=180*(e.fromVertex===t?e.fromAngle:e.toAngle)/Math.PI;r in s||(s[r]=[]),s[r]=s[r].concat(e.renderedEdges)}),r.forEach(s,e=>{if(!(e.length<2))for(let s=0;s<e.length-1;s++)for(let r=s+1;r<e.length;r++){const i=e[s],n=e[r];let o=i.graphEdge.oppositeVertex(t),h=n.graphEdge.oppositeVertex(t),a=l(o.x,o.y,t.x,t.y,h.x,h.y);if(0===a){const e=i.findExtension(o),s=n.findExtension(h);e&&(o=e.graphEdge.oppositeVertex(o)),s&&(h=s.graphEdge.oppositeVertex(h)),a=l(o.x,o.y,t.x,t.y,h.x,h.y)}a=N(i,n,t)*a,a>0&&this.storeComparison(i,n),a<0&&this.storeComparison(n,i)}})})}storeComparison(t,e){const s=t.patternIds||t.pathSegmentIds,r=e.patternIds||e.pathSegmentIds;F(`storing comparison: ${s} < ${r}`),this.bundleComparisons[`${s}_${r}`]=-1,this.bundleComparisons[`${r}_${s}`]=1}}class G{constructor(){this.items=[],this.min=Number.MAX_VALUE,this.max=-Number.MAX_VALUE}addEdge(t,e,s){-1===this.items.indexOf(t)&&this.items.push(t),this.min=Math.min(this.min,e),this.max=Math.max(this.max,s)}rangeOverlaps(t,e){return this.min<e&&t<this.max}}function N(t,e,s){return t.graphEdge.toVertex===s&&e.graphEdge.toVertex===s||t.graphEdge.toVertex===s&&e.graphEdge.fromVertex===s?-1:1}function Y(t,e){if(t.length!==e.length)return!1;for(const s in t)if(t[s]!==e[s])return!1;return!0}function j(t){let e=0,s=0,r=0;const i=t.length,n=[];for(;e<i;){let i=0,h=0;var o;do{h|=(31&(o=t.charCodeAt(e++)-63))<<i,i+=5}while(o>=32);s+=1&h?~(h>>1):h>>1,i=0,h=0;do{h|=(31&(o=t.charCodeAt(e++)-63))<<i,i+=5}while(o>=32);r+=1&h?~(h>>1):h>>1,n.push([1e-5*s,1e-5*r])}return n}class X extends b{constructor(t,e){if(super(t),this.name=`Turn @ ${t.lat}, ${t.lon}`,!this.worldX||!this.worldY){const e=x.forward([t.lon,t.lat]);this.worldX=e[0],this.worldY=e[1],this.isSegmentEndPoint=!1}this.id=e}getId(){return this.id}getType(){return"TURN"}getName(){return this.name}containsSegmentEndPoint(){return this.isSegmentEndPoint}}class z{constructor(t){this.renderedSegment=t,this.renderedEdges=[]}addEdge(t){this.renderedEdges.push(t),this.edgeIds=this.edgeIds?this.edgeIds+","+t.getId():t.getId()}getLabelTextArray(){const t=[];return r.forEach(this.renderedSegment.pathSegment.getPatterns(),e=>{const s=e.route.route_short_name;-1===t.indexOf(s)&&t.push(s)}),t}getLabelAnchors(t,e){const s=[],r=this.getRenderLength(t),i=Math.floor(r/e),n=e/r;for(let e=0;e<i;e++){const r=this.coordAlongRenderedPath(e%2==0?.5+e/2*n:.5-(e+1)/2*n,t);r&&s.push(r)}return s}coordAlongRenderedPath(t,e){const s=t*this.getRenderLength(e);let r=0;for(let t=0;t<this.renderedEdges.length;t++){const i=this.renderedEdges[t],n=i.graphEdge.getRenderLength(e);if(s<=r+n)return i.graphEdge.coordAlongEdge((s-r)/n,i.renderData,e);r+=n}}getRenderLength(t){return this.renderLength||(this.renderLength=0,r.forEach(this.renderedEdges,e=>{this.renderLength+=e.graphEdge.getRenderLength(t)})),this.renderLength}}class ${constructor(){this.patterns=[],this.fromIndexLookup={},this.toIndexLookup={}}addPattern(t,e,s){-1===this.patterns.indexOf(t)&&(this.patterns.push(t),this.fromIndexLookup[t.pattern_id]=e,this.toIndexLookup[t.pattern_id]=s)}getFromIndex(t){return this.fromIndexLookup[t.pattern_id]}getToIndex(t){return this.toIndexLookup[t.pattern_id]}}let H=0;class q{constructor(t,e){this.id=H++,this.type=t,this.path=e,this.points=[],this.edges=[],this.renderedSegments=[],this.patternGroup=new $}clearGraphData(){this.edges=[],this.points.forEach((function(t){t.graphVertex=null})),this.renderLength=null}getId(){return this.id}getType(){return this.type}addRenderedSegment(t){this.renderedSegments.push(t)}addEdge(t,e){this.edges.push({forward:e===t.fromVertex,graphEdge:t})}insertEdgeAt(t,e,s){this.edges.splice(t,0,{forward:s===e.fromVertex,graphEdge:e})}removeEdge(t){let e=null;for(let s=0;s<this.edges.length;s++)if(this.edges[s].graphEdge===t){e=s;break}null!==e&&this.edges.splice(e,1)}getEdgeIndex(t){for(let e=0;e<this.edges.length;e++)if(this.edges[e].graphEdge===t)return e;return-1}getGraphVertices(){const t=[];return this.edges.forEach((function(e,s){0===s&&t.push(e.graphEdge.fromVertex),t.push(e.graphEdge.toVertex)})),t}vertexArray(){let t=this.startVertex();const e=[t];return this.edges.forEach((function(s){t=s.graphEdge.oppositeVertex(t),e.push(t)})),e}startVertex(){if(this.points[0].multipoint)return this.points[0].multipoint.graphVertex;if(!this.edges||0===this.edges.length)return null;const t=this.edges[0].graphEdge;return this.edges[0].forward?t.fromVertex:t.toVertex}endVertex(){if(this.points[this.points.length-1].multipoint)return this.points[this.points.length-1].multipoint.graphVertex;if(!this.edges||0===this.edges.length)return null;const t=this.edges[this.edges.length-1].graphEdge;return this.edges[this.edges.length-1].forward?t.toVertex:t.fromVertex}addPattern(t,e,s){if(s-e+1>this.points.length){this.points=[];let r=null;for(let i=e;i<=s;i++){const e=t.stops[i];r!==e&&this.points.push(e),r=e}}this.patternGroup.addPattern(t,e,s)}getPattern(){return this.patternGroup.patterns[0]}getPatterns(){return this.patternGroup.patterns}getMode(){return this.patternGroup.patterns[0].route.route_type}toString(){const t=this.startVertex(),e=this.endVertex();return"PathSegment id="+this.id+" type="+this.type+" from "+(t?t.toString():"(unknown)")+" to "+(e?e.toString():"(unknown)")}getLabelEdgeGroups(){const t=[];return r.forEach(this.renderedSegments,e=>{if(!e.isFocused())return;let s=new z(e);r.forEach(e.renderedEdges,r=>{s.addEdge(r),r.graphEdge.toVertex.point.containsSegmentEndPoint()&&(t.push(s),s=new z(e))})}),t}}class U{constructor(t){this.parent=t,this.segments=[]}clearGraphData(t){this.segments.forEach((function(t){t.clearGraphData()}))}addSegment(t){this.segments.push(t),t.points.forEach((function(t){t.paths.push(this)}),this)}getRenderedSegments(){let t=[];return this.segments.forEach((function(e){t=t.concat(e.renderedSegments)})),t}getPointArray(){const t=[];for(let e=0;e<this.segments.length;e++){const s=this.segments[e];e>0&&s.points[0]===this.segments[e-1].points[this.segments[e-1].points.length-1]&&t.concat(s.points.slice(1))}return t}}class Z{constructor(t,e){this.network=e;for(const e in t)this[e]=t[e];this.path=new U(this),r.forEach(this.segments,t=>{const s=new q(t.type,this.path);if(s.journeySegment=t,"TRANSIT"===t.type)t.patterns?r.forEach(t.patterns,t=>{s.addPattern(e.patterns[t.pattern_id],t.from_stop_index,t.to_stop_index)}):t.pattern_id&&s.addPattern(e.patterns[t.pattern_id],t.from_stop_index,t.to_stop_index);else{const r=[];if("STOP"===t.from.type&&"STOP"===t.to.type&&t.from.stop_id===t.to.stop_id)return;if(s.points.push(K(t.from,e)),t.streetEdges&&t.streetEdges.length>0){let i=null;for(let n=0;n<t.streetEdges.length;n++){const o=t.streetEdges[n],h=e.streetEdges[o];if(h.id=o,r.push(h),n>=t.streetEdges.length-1)continue;i&&(h.fromTurnPoint=i);const a=h.length-1;if(h.latLons[0][0]===h.latLons[a][0]&&h.latLons[0][1]===h.latLons[a][1])continue;const d=Q({lat:h.latLons[a][0],lon:h.latLons[a][1],worldX:h.worldCoords[a][0],worldY:h.worldCoords[a][1]},e);s.points.push(d),i=h.toTurnPoint=d}s.streetEdges=r}s.points.push(K(t.to,e))}this.path.addSegment(s)})}getElementId(){return"journey-"+this.journey_id}}function K(t,e){return"PLACE"===t.type?e.places[t.place_id]:"STOP"===t.type?e.stops[t.stop_id]:void 0}function Q(t,e){const s=t.lat+"_"+t.lon;if(s in e.turnPoints)return e.turnPoints[s];const r=new X(t,s);return e.turnPoints[s]=r,r}class J{constructor(t,e){for(const e in t)"stops"!==e&&(this[e]=t[e]);this.stops=[],this.interStopGeometry=[],e&&r.forEach(t.stops,t=>{if(this.stops.push(e.stops[t.stop_id]),t.geometry){const e=j(t.geometry),s=[];r.forEach(e,t=>{s.push(x.forward([t[1],t[0]]))}),this.interStopGeometry.push(s)}}),this.renderedEdges=[]}getId(){return this.pattern_id}getElementId(){return"pattern-"+this.pattern_id}getName(){return this.pattern_name}addRenderedEdge(t){-1===this.renderedEdges.indexOf(t)&&this.renderedEdges.push(t)}offsetAlignment(t,e){r.forEach(this.renderedEdges,s=>{s.offsetAlignment(t,e)})}createPath(){const t=new U(this),e=new q("TRANSIT",t);return e.addPattern(this,0,this.stops.length-1),t.addSegment(e),t}}function tt(t,e){return e=e||2,(new Array(e).join("0")+t).slice(-e)}class et{constructor(t){for(const e in t)"patterns"!==e&&(this[e]=t[e]);this.patterns=[]}addPattern(t){this.patterns.push(t),t.route=this}getTextColor(){const t=this.getColor();if(t)return function(t,e=!0){0===t.indexOf("#")&&(t=t.slice(1)),3===t.length&&(t=t[0]+t[0]+t[1]+t[1]+t[2]+t[2]),6!==t.length&&console.warn("Invalid HEX color.",t);let s=parseInt(t.slice(0,2),16),r=parseInt(t.slice(2,4),16),i=parseInt(t.slice(4,6),16);return e?.299*s+.587*r+.114*i>186?"#000000":"#FFFFFF":(s=(255-s).toString(16),r=(255-r).toString(16),i=(255-i).toString(16),"#"+tt(s)+tt(r)+tt(i))}(t)}getColor(){if(this.route_color)return"#"===this.route_color.charAt(0)?this.route_color:"#"+this.route_color}}const st=require("debug")("transitive:network");class rt{constructor(t,e){this.transitive=t,this.routes={},this.stops={},this.patterns={},this.places={},this.journeys={},this.paths=[],this.baseVertexPoints=[],this.graph=new W(this,[]),e&&this.load(e)}load(t){st("loading",t),t||(t={}),this.data=t,this.baseVertexPoints=[],this.adjacentStops={},this.turnPoints={},this.streetEdges={},r.forEach(t.streetEdges,t=>{const e=j(t.geometry.points),s=[];r.forEach(e,t=>{s.push(x.forward([t[1],t[0]]))}),this.streetEdges[t.edge_id]={latLons:e,length:t.geometry.length,worldCoords:s}}),this.routes={},r.forEach(t.routes,t=>{this.routes[t.route_id]=new et(t)}),this.stops={},r.forEach(t.stops,t=>{this.stops[t.stop_id]=new T(t)}),this.patterns={},r.forEach(t.patterns,t=>{const e=new J(t,this);this.patterns[t.pattern_id]=e;const s=this.routes[t.route_id];s?(s.addPattern(e),e.route=s):st("Error: pattern "+t.pattern_id+" refers to route that was not found: "+t.route_id),e.render&&this.paths.push(e.createPath())}),this.places={},r.forEach(t.places,t=>{const e=this.places[t.place_id]=new P(t,this);this.addVertexPoint(e)}),this.journeys={},r.forEach(t.journeys,t=>{const e=new Z(t,this);this.journeys[t.journey_id]=e,this.paths.push(e.path)});for(let t=0;t<this.paths.length;t++){const e=this.paths[t];for(let t=0;t<e.segments.length;t++)this.processSegment(e.segments[t])}if(!t.journeys||0===t.journeys.length)for(const t in this.adjacentStops)this.adjacentStops[t].length>2&&this.addVertexPoint(this.stops[t]);const e={},s=function(t,s){t.getId()in e||(e[t.getId()]=[]),-1===e[t.getId()].indexOf(s)&&e[t.getId()].push(s)};r.forEach(Object.values(th