Transit Service - Advanced
Using the Transit Service to get the direction by public transport between an origin and a destination.
Example
Transit Service - Advanced
let map: woosmap.map.Map;
let transitService: woosmap.map.TransitService;
let transitRequest: woosmap.map.transit.TransitRouteRequest;
let markersArray: woosmap.map.Marker[] = [];
let originContainer: HTMLElement;
let destinationContainer: HTMLElement;
let transitRenderer: woosmap.map.TransitRenderer;
function createMarker(
position: woosmap.map.LatLngLiteral | woosmap.map.LatLng,
label: string,
url: string,
): woosmap.map.Marker {
return new woosmap.map.Marker({
map,
position,
draggable: true,
icon: {
url,
labelOrigin: new woosmap.map.Point(13, 15),
scaledSize: {
height: 38,
width: 26,
},
},
label: {
text: label,
color: "white",
},
});
}
function clearMarkers(): void {
for (const marker of markersArray) {
marker.setMap(null);
}
markersArray = [];
}
function displayTransitMarkers(): void {
clearMarkers();
const originMarker = createMarker(
transitRequest.origin,
"O",
"https://images.woosmap.com/marker-blue.svg",
);
originMarker.addListener("dragend", () => {
transitRequest.origin = originMarker.getPosition();
setLatLngToContainer(originContainer, originMarker.getPosition().toJSON());
calculateTransit();
});
const destinationMarker = createMarker(
transitRequest.destination,
"D",
"https://images.woosmap.com/marker-red.svg",
);
destinationMarker.addListener("dragend", () => {
transitRequest.destination = destinationMarker.getPosition();
setLatLngToContainer(
destinationContainer,
destinationMarker.getPosition().toJSON(),
);
calculateTransit();
});
markersArray.push(originMarker);
markersArray.push(destinationMarker);
}
function createDefaultRequest(): woosmap.map.transit.TransitRouteRequest {
const origin = { lat: 51.6511, lng: -0.1615 };
const destination = { lat: 51.5146, lng: -0.0212 };
setLatLngToContainer(originContainer, origin);
setLatLngToContainer(destinationContainer, destination);
return {
origin,
destination,
};
}
function setLatLngToContainer(
element: HTMLElement,
location: woosmap.map.LatLngLiteral,
): void {
element.innerHTML = `<li><span>lat: ${location.lat.toFixed(4)}, lng: ${location.lng.toFixed(4)}<span></li>`;
}
function createRoutesTable(response: woosmap.map.transit.TransitRouteResponse) {
const directionTripElements = response.routes.map(
(route: woosmap.map.transit.TransitRoute, index: number) => {
const distanceTotal = route.legs.reduce(
(total, leg) => total + leg.distance,
0,
);
const directionTrip = document.createElement("div");
directionTrip.className = "directionTrip";
if (index === 0) {
directionTrip.classList.add("directionTrip__selected");
}
const startName =
route.legs.find(
(leg: woosmap.map.transit.TransitLeg) =>
leg.start_location && leg.start_location.name !== null,
)?.start_location.name || "Walking";
const travelModeIconSrc =
startName === "Walking"
? "https://images.woosmap.com/directions/walk_black.png"
: "https://images.woosmap.com/directions/transit_black.png";
directionTrip.innerHTML = `
<img class="directionTrip__travelModeIcon" src="${travelModeIconSrc}">
<div class="directionTrip__description">
<div class="directionTrip__numbers">
<div class="directionTrip__duration">${formatTime(route.duration)}</div>
<div class="directionTrip__distance">${formatDistance(distanceTotal)}</div>
</div>
<div class="directionTrip__title">through ${startName}</div>
<div class="directionTrip__summary">${formatTime(route.duration)}</div>
<div class="directionTrip__detailsMsg"></div>
</div>
`;
directionTrip.addEventListener("click", () => {
selectCorrectRoute(index);
transitRenderer.setRouteIndex(index);
});
return directionTrip;
},
);
function formatDistance(meters: number): string {
if (meters < 1000) {
return `${meters} m`;
} else {
return `${(meters / 1000).toFixed(2)} km`;
}
}
function formatTime(seconds: number): string {
const minutes = Math.round(seconds / 60);
if (minutes < 60) {
return `${minutes}m`;
} else {
const hours = Math.floor(minutes / 60);
const remainingMinutes = minutes % 60;
return `${hours}h${remainingMinutes}m`;
}
}
function selectCorrectRoute(index: number) {
document
.querySelectorAll(".directionTrip__selected")
.forEach((selectedElement) => {
selectedElement.classList.remove("directionTrip__selected");
});
directionTripElements[index].classList.add("directionTrip__selected");
}
const tableContainer = document.querySelector(
".tableContainer",
) as HTMLElement;
tableContainer.innerHTML = "";
directionTripElements.forEach((element) =>
tableContainer.appendChild(element),
);
tableContainer.style.display = "block";
}
function displayOrHideError(error: string) {
const infoMsgElement = document.getElementById("infoMessage") as HTMLElement;
if (error === "") {
infoMsgElement.innerText = "Drag markers to update route";
} else {
infoMsgElement.innerHTML = error;
infoMsgElement.style.display = "block";
const tableContainer = document.querySelector(
".tableContainer",
) as HTMLElement;
tableContainer.innerHTML = "";
tableContainer.style.display = "none";
}
}
function toggleProgress() {
(document.querySelector(".linear-progress") as HTMLElement).classList.toggle(
"hide",
);
}
function displayTransitRoute(routes: woosmap.map.transit.TransitRoute[]) {
transitRenderer.setRoutes(routes);
}
function calculateTransit(): void {
toggleProgress();
transitService
.route(transitRequest)
.then(handleResponse)
.catch((error) => {
console.error("Error calculating transit route:", error);
displayOrHideError(error);
toggleProgress();
});
}
function handleResponse(response: woosmap.map.transit.TransitRouteResponse) {
displayTransitRoute(response.routes);
displayTransitMarkers();
createRoutesTable(response);
displayOrHideError("");
toggleProgress();
}
function isValidDate(date: Date): boolean {
return date.getTime && typeof date.getTime === "function";
}
function updateTime(timeType: "departureTime" | "arrivalTime"): void {
const timeElement = document.getElementById(
`${timeType}-time`,
) as HTMLInputElement;
const datetimeRadioButton = document.getElementById(
"datetime",
) as HTMLInputElement;
const otherTimeType =
timeType === "departureTime" ? "arrivalTime" : "departureTime";
const otherTimeElement = document.getElementById(
`${otherTimeType}-time`,
) as HTMLInputElement;
if (!timeElement || !otherTimeElement) {
return;
}
timeElement.min = new Date().toISOString().slice(0, 16);
timeElement.disabled = true;
document.querySelectorAll(`input[name="${timeType}"]`).forEach((el) => {
el.addEventListener("change", () => {
const selectedOption = (el as HTMLInputElement).value;
switch (selectedOption) {
case "empty":
delete transitRequest[timeType];
timeElement.disabled = true;
break;
case "now":
case "datetime":
delete transitRequest[otherTimeType];
(
document.querySelector(
`input[name="${otherTimeType}"][value="empty"]`,
) as HTMLInputElement
).checked = true;
otherTimeElement.value = "";
otherTimeElement.disabled = true;
if (timeElement.value) {
const newDate = new Date(timeElement.value);
transitRequest[timeType] = isValidDate(newDate)
? newDate.getTime().toString()
: undefined;
} else {
transitRequest[timeType] = undefined;
}
timeElement.disabled = false;
break;
}
calculateTransit();
});
});
timeElement.addEventListener("change", () => {
if (datetimeRadioButton) {
datetimeRadioButton.checked = true;
}
const newDate = new Date(timeElement.value);
transitRequest[timeType] = isValidDate(newDate)
? newDate.getTime().toString()
: undefined;
calculateTransit();
});
}
function registerAddButton(
selector: string,
element: HTMLElement,
loc: woosmap.map.LatLngLiteral[] | woosmap.map.LatLngLiteral,
): void {
const button = document.querySelector(selector) as HTMLElement;
button.addEventListener("click", () => {
if (button.classList.contains("addLocation__selected")) {
button.classList.remove("addLocation__selected");
document.getElementById("map")?.classList.remove("cursor-crosshair");
woosmap.map.event.clearListeners(map, "click");
return;
}
button.classList.add("addLocation__selected");
document.getElementById("map")?.classList.add("cursor-crosshair");
woosmap.map.event.addListenerOnce(map, "click", (e) => {
document.getElementById("map")?.classList.remove("cursor-crosshair");
button.classList.remove("addLocation__selected");
const location = e.latlng;
if (element === originContainer) {
transitRequest.origin = location;
}
if (element === destinationContainer) {
transitRequest.destination = location;
}
setLatLngToContainer(element, location);
calculateTransit();
});
});
}
const includedModes: string[] = [];
const excludedModes: string[] = [];
const modes = {
train: [
"highSpeedTrain",
"intercityTrain",
"interRegionalTrain",
"regionalTrain",
"cityTrain",
"subway",
"lightRail",
"monorail",
"inclined",
],
bus: ["bus", "privateBus", "busRapid"],
ferry: ["ferry"],
aerial: ["aerial", "flight", "spaceship"],
};
function updateTransitOptions() {
const checkboxes = document.querySelectorAll('.modesChk[type="checkbox"]');
const includeRadio = document.querySelector(
"#includeModes",
) as HTMLInputElement;
const excludeRadio = document.querySelector(
"#excludeModes",
) as HTMLInputElement;
function updateMode() {
includedModes.length = 0;
excludedModes.length = 0;
Object.keys(modes).forEach((mode) => {
const checkbox = document.querySelector(
`#mode${mode.charAt(0).toUpperCase() + mode.slice(1)}`,
) as HTMLInputElement;
if (checkbox.checked) {
modes[mode].forEach((subMode) => {
if (includeRadio.checked) {
includedModes.push(subMode);
} else if (excludeRadio.checked) {
excludedModes.push("-" + subMode);
}
});
}
});
if (includedModes.length === 0 && excludedModes.length === 0) {
delete transitRequest.modes;
} else {
transitRequest.modes = includeRadio.checked
? includedModes
: excludedModes;
}
calculateTransit();
}
checkboxes.forEach((checkbox) => {
checkbox.addEventListener("change", updateMode);
});
includeRadio.addEventListener("change", updateMode);
excludeRadio.addEventListener("change", updateMode);
}
function initUI(): void {
updateTime("departureTime");
updateTime("arrivalTime");
updateTransitOptions();
registerAddButton(
".addLocation__destinations",
destinationContainer,
transitRequest.destination as woosmap.map.LatLngLiteral,
);
registerAddButton(
".addLocation__origins",
originContainer,
transitRequest.origin as woosmap.map.LatLngLiteral,
);
}
function initMap(): void {
map = new woosmap.map.Map(document.getElementById("map") as HTMLElement, {
center: { lat: 51.5074, lng: -0.1478 },
zoom: 10,
});
transitService = new woosmap.map.TransitService();
transitRenderer = new woosmap.map.TransitRenderer({});
transitRenderer.setMap(map);
originContainer = document.getElementById("origin") as HTMLElement;
destinationContainer = document.getElementById("destination") as HTMLElement;
transitRequest = createDefaultRequest();
initUI();
calculateTransit();
}
declare global {
interface Window {
initMap: () => void;
}
}
window.initMap = initMap;
let map;
let transitService;
let transitRequest;
let markersArray = [];
let originContainer;
let destinationContainer;
let transitRenderer;
function createMarker(position, label, url) {
return new woosmap.map.Marker({
map,
position,
draggable: true,
icon: {
url,
labelOrigin: new woosmap.map.Point(13, 15),
scaledSize: {
height: 38,
width: 26,
},
},
label: {
text: label,
color: "white",
},
});
}
function clearMarkers() {
for (const marker of markersArray) {
marker.setMap(null);
}
markersArray = [];
}
function displayTransitMarkers() {
clearMarkers();
const originMarker = createMarker(
transitRequest.origin,
"O",
"https://images.woosmap.com/marker-blue.svg",
);
originMarker.addListener("dragend", () => {
transitRequest.origin = originMarker.getPosition();
setLatLngToContainer(originContainer, originMarker.getPosition().toJSON());
calculateTransit();
});
const destinationMarker = createMarker(
transitRequest.destination,
"D",
"https://images.woosmap.com/marker-red.svg",
);
destinationMarker.addListener("dragend", () => {
transitRequest.destination = destinationMarker.getPosition();
setLatLngToContainer(
destinationContainer,
destinationMarker.getPosition().toJSON(),
);
calculateTransit();
});
markersArray.push(originMarker);
markersArray.push(destinationMarker);
}
function createDefaultRequest() {
const origin = { lat: 51.6511, lng: -0.1615 };
const destination = { lat: 51.5146, lng: -0.0212 };
setLatLngToContainer(originContainer, origin);
setLatLngToContainer(destinationContainer, destination);
return {
origin,
destination,
};
}
function setLatLngToContainer(element, location) {
element.innerHTML = `<li><span>lat: ${location.lat.toFixed(4)}, lng: ${location.lng.toFixed(4)}<span></li>`;
}
function createRoutesTable(response) {
const directionTripElements = response.routes.map((route, index) => {
const distanceTotal = route.legs.reduce(
(total, leg) => total + leg.distance,
0,
);
const directionTrip = document.createElement("div");
directionTrip.className = "directionTrip";
if (index === 0) {
directionTrip.classList.add("directionTrip__selected");
}
const startName =
route.legs.find(
(leg) => leg.start_location && leg.start_location.name !== null,
)?.start_location.name || "Walking";
const travelModeIconSrc =
startName === "Walking"
? "https://images.woosmap.com/directions/walk_black.png"
: "https://images.woosmap.com/directions/transit_black.png";
directionTrip.innerHTML = `
<img class="directionTrip__travelModeIcon" src="${travelModeIconSrc}">
<div class="directionTrip__description">
<div class="directionTrip__numbers">
<div class="directionTrip__duration">${formatTime(route.duration)}</div>
<div class="directionTrip__distance">${formatDistance(distanceTotal)}</div>
</div>
<div class="directionTrip__title">through ${startName}</div>
<div class="directionTrip__summary">${formatTime(route.duration)}</div>
<div class="directionTrip__detailsMsg"></div>
</div>
`;
directionTrip.addEventListener("click", () => {
selectCorrectRoute(index);
transitRenderer.setRouteIndex(index);
});
return directionTrip;
});
function formatDistance(meters) {
if (meters < 1000) {
return `${meters} m`;
} else {
return `${(meters / 1000).toFixed(2)} km`;
}
}
function formatTime(seconds) {
const minutes = Math.round(seconds / 60);
if (minutes < 60) {
return `${minutes}m`;
} else {
const hours = Math.floor(minutes / 60);
const remainingMinutes = minutes % 60;
return `${hours}h${remainingMinutes}m`;
}
}
function selectCorrectRoute(index) {
document
.querySelectorAll(".directionTrip__selected")
.forEach((selectedElement) => {
selectedElement.classList.remove("directionTrip__selected");
});
directionTripElements[index].classList.add("directionTrip__selected");
}
const tableContainer = document.querySelector(".tableContainer");
tableContainer.innerHTML = "";
directionTripElements.forEach((element) =>
tableContainer.appendChild(element),
);
tableContainer.style.display = "block";
}
function displayOrHideError(error) {
const infoMsgElement = document.getElementById("infoMessage");
if (error === "") {
infoMsgElement.innerText = "Drag markers to update route";
} else {
infoMsgElement.innerHTML = error;
infoMsgElement.style.display = "block";
const tableContainer = document.querySelector(".tableContainer");
tableContainer.innerHTML = "";
tableContainer.style.display = "none";
}
}
function toggleProgress() {
document.querySelector(".linear-progress").classList.toggle("hide");
}
function displayTransitRoute(routes) {
transitRenderer.setRoutes(routes);
}
function calculateTransit() {
toggleProgress();
transitService
.route(transitRequest)
.then(handleResponse)
.catch((error) => {
console.error("Error calculating transit route:", error);
displayOrHideError(error);
toggleProgress();
});
}
function handleResponse(response) {
displayTransitRoute(response.routes);
displayTransitMarkers();
createRoutesTable(response);
displayOrHideError("");
toggleProgress();
}
function isValidDate(date) {
return date.getTime && typeof date.getTime === "function";
}
function updateTime(timeType) {
const timeElement = document.getElementById(`${timeType}-time`);
const datetimeRadioButton = document.getElementById("datetime");
const otherTimeType =
timeType === "departureTime" ? "arrivalTime" : "departureTime";
const otherTimeElement = document.getElementById(`${otherTimeType}-time`);
if (!timeElement || !otherTimeElement) {
return;
}
timeElement.min = new Date().toISOString().slice(0, 16);
timeElement.disabled = true;
document.querySelectorAll(`input[name="${timeType}"]`).forEach((el) => {
el.addEventListener("change", () => {
const selectedOption = el.value;
switch (selectedOption) {
case "empty":
delete transitRequest[timeType];
timeElement.disabled = true;
break;
case "now":
case "datetime":
delete transitRequest[otherTimeType];
document.querySelector(
`input[name="${otherTimeType}"][value="empty"]`,
).checked = true;
otherTimeElement.value = "";
otherTimeElement.disabled = true;
if (timeElement.value) {
const newDate = new Date(timeElement.value);
transitRequest[timeType] = isValidDate(newDate)
? newDate.getTime().toString()
: undefined;
} else {
transitRequest[timeType] = undefined;
}
timeElement.disabled = false;
break;
}
calculateTransit();
});
});
timeElement.addEventListener("change", () => {
if (datetimeRadioButton) {
datetimeRadioButton.checked = true;
}
const newDate = new Date(timeElement.value);
transitRequest[timeType] = isValidDate(newDate)
? newDate.getTime().toString()
: undefined;
calculateTransit();
});
}
function registerAddButton(selector, element, loc) {
const button = document.querySelector(selector);
button.addEventListener("click", () => {
if (button.classList.contains("addLocation__selected")) {
button.classList.remove("addLocation__selected");
document.getElementById("map")?.classList.remove("cursor-crosshair");
woosmap.map.event.clearListeners(map, "click");
return;
}
button.classList.add("addLocation__selected");
document.getElementById("map")?.classList.add("cursor-crosshair");
woosmap.map.event.addListenerOnce(map, "click", (e) => {
document.getElementById("map")?.classList.remove("cursor-crosshair");
button.classList.remove("addLocation__selected");
const location = e.latlng;
if (element === originContainer) {
transitRequest.origin = location;
}
if (element === destinationContainer) {
transitRequest.destination = location;
}
setLatLngToContainer(element, location);
calculateTransit();
});
});
}
const includedModes = [];
const excludedModes = [];
const modes = {
train: [
"highSpeedTrain",
"intercityTrain",
"interRegionalTrain",
"regionalTrain",
"cityTrain",
"subway",
"lightRail",
"monorail",
"inclined",
],
bus: ["bus", "privateBus", "busRapid"],
ferry: ["ferry"],
aerial: ["aerial", "flight", "spaceship"],
};
function updateTransitOptions() {
const checkboxes = document.querySelectorAll('.modesChk[type="checkbox"]');
const includeRadio = document.querySelector("#includeModes");
const excludeRadio = document.querySelector("#excludeModes");
function updateMode() {
includedModes.length = 0;
excludedModes.length = 0;
Object.keys(modes).forEach((mode) => {
const checkbox = document.querySelector(
`#mode${mode.charAt(0).toUpperCase() + mode.slice(1)}`,
);
if (checkbox.checked) {
modes[mode].forEach((subMode) => {
if (includeRadio.checked) {
includedModes.push(subMode);
} else if (excludeRadio.checked) {
excludedModes.push("-" + subMode);
}
});
}
});
if (includedModes.length === 0 && excludedModes.length === 0) {
delete transitRequest.modes;
} else {
transitRequest.modes = includeRadio.checked
? includedModes
: excludedModes;
}
calculateTransit();
}
checkboxes.forEach((checkbox) => {
checkbox.addEventListener("change", updateMode);
});
includeRadio.addEventListener("change", updateMode);
excludeRadio.addEventListener("change", updateMode);
}
function initUI() {
updateTime("departureTime");
updateTime("arrivalTime");
updateTransitOptions();
registerAddButton(
".addLocation__destinations",
destinationContainer,
transitRequest.destination,
);
registerAddButton(
".addLocation__origins",
originContainer,
transitRequest.origin,
);
}
function initMap() {
map = new woosmap.map.Map(document.getElementById("map"), {
center: { lat: 51.5074, lng: -0.1478 },
zoom: 10,
});
transitService = new woosmap.map.TransitService();
transitRenderer = new woosmap.map.TransitRenderer({});
transitRenderer.setMap(map);
originContainer = document.getElementById("origin");
destinationContainer = document.getElementById("destination");
transitRequest = createDefaultRequest();
initUI();
calculateTransit();
}
window.initMap = initMap;
html,
body {
height: 100%;
margin: 0;
padding: 0;
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
}
#container {
height: 100%;
display: flex;
}
#sidebar {
flex-basis: 12rem;
flex-grow: 1;
max-width: 30rem;
height: 100%;
box-sizing: border-box;
overflow: auto;
}
#map {
flex-basis: 70vw;
flex-grow: 5;
height: 100%;
}
#sidebar {
flex-basis: 18rem;
box-shadow: 0 -2px 4px 0 rgba(0, 0, 0, 0.12);
z-index: 1;
}
#mapContainer {
display: flex;
flex-direction: column;
flex-basis: 70vw;
flex-grow: 5;
position: relative;
}
.tableContainer {
max-height: 35%;
overflow-y: auto;
font-size: 13px;
}
.directionTrip {
cursor: pointer;
color: rgba(0, 0, 0, 0.5411764706);
border-bottom: 1px solid #e6e6e6;
flex: none;
padding: 1rem;
position: relative;
}
.directionTrip__selected :before {
content: "";
position: absolute;
top: 0;
left: 0;
border-left: 5px solid #3d5afe;
height: 100%;
}
.directionTrip__selected .directionTrip__detailsMsg {
display: block;
}
.directionTrip__detailsMsg {
display: none;
}
.directionTrip__travelModeIcon {
position: absolute;
top: 18px;
width: 24px;
height: 24px;
opacity: 0.6;
}
.directionTrip__description {
padding-left: 48px;
}
.directionTrip__numbers {
float: right;
text-align: right;
padding-left: 10px;
}
.directionTrip__duration, .directionTrip__title {
color: rgba(0, 0, 0, 0.87);
font-size: 15px;
vertical-align: top;
}
.directionTrip__duration {
color: #188038;
}
.directionTrip__distance, .directionTrip__summary {
line-height: 16px;
padding: 4px 0;
font-size: 13px;
}
.directionTrip__detailsMsg {
font-size: 13px;
font-weight: 500;
text-transform: uppercase;
color: #3d5afe;
}
.directionTrip__empty {
padding: 1rem;
}
.directionTrip__error {
margin-top: 1rem;
font-size: 0.9rem;
}
.directionTrip__error:before {
content: "";
display: inline-block;
background-size: cover;
width: 17px;
height: 15px;
padding-right: 5px;
overflow: hidden;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='48' width='48'%3E%3Cpath d='M2 42 24 4l22 38Zm22.2-5.85q.65 0 1.075-.425.425-.425.425-1.075 0-.65-.425-1.075-.425-.425-1.075-.425-.65 0-1.075.425Q22.7 34 22.7 34.65q0 .65.425 1.075.425.425 1.075.425Zm-1.5-5.55h3V19.4h-3Z' fill= '%23F3d5afe' /%3E%3C/svg%3E");
}
#innerWrapper {
display: flex;
flex-direction: column;
flex-grow: 1;
overflow: hidden;
overflow-y: auto;
padding: 0 10px 40px;
}
input,
select {
font-family: inherit;
font-size: 100%;
box-sizing: border-box;
}
.transitOptions {
padding: 0;
margin: 0;
list-style: none;
height: 100%;
background: #fff;
display: flex;
font-size: 13px;
}
.transitOptions__list {
width: 100%;
height: 100%;
}
.transitOptions__header {
font-weight: 600;
line-height: 24px;
display: flex;
}
.transitOptions__input {
height: 24px;
display: flex;
align-items: baseline;
}
.transitOptions__content {
padding: 10px 0;
}
.transitOptions__content:first-child {
padding-top: 0;
}
.addLocation {
font-size: 0.75em;
display: flex;
color: #222;
cursor: pointer;
}
.addLocation:hover {
text-decoration: underline;
}
.addLocation div {
margin-left: 5px;
}
.addLocation__selected {
color: #3d5afe;
}
.addLocation__selected svg path {
fill: #3d5afe;
}
.sectionHeader {
background: #f1f1f1;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #eeeeee;
margin: 20px -10px 5px;
padding: 5px 10px;
color: #222;
}
.sectionHeader span {
font-size: 0.85em;
font-weight: 600;
}
.sectionHeader:first-child {
margin-top: 0;
}
.customCounter {
margin: 0;
padding: 0;
list-style-type: none;
width: 100%;
}
.customCounter li {
counter-increment: step-counter;
line-height: 14px;
height: 22px;
display: flex;
align-items: center;
}
.customCounter li span {
flex-grow: 1;
}
.customCounter li::before {
margin-right: 5px;
font-size: 80%;
color: #fff;
background-position: bottom;
font-weight: 600;
width: 20px;
height: 20px;
text-align: center;
background-size: contain;
background-repeat: no-repeat;
}
.customCounter__destination li::before {
background-image: url(https://images.woosmap.com/marker-red.svg);
content: "D";
}
.customCounter__origin li::before {
background-image: url(https://images.woosmap.com/marker-blue.svg);
content: "O";
}
.clear-searchButton {
display: block;
height: 18px;
width: 22px;
background: none;
border: none;
vertical-align: middle;
pointer-events: all;
cursor: pointer;
}
.clear-searchButton .clear-icon {
color: inherit;
flex-shrink: 0;
height: 16px;
width: 16px;
}
#map.cursor-crosshair .mapboxgl-canvas-container {
cursor: crosshair !important;
}
#infoMessage {
font-size: 12px;
max-width: 200px;
position: absolute;
top: 0;
background-color: #fff;
border-radius: 3px;
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1);
margin: 10px;
padding: 5px;
overflow: hidden;
z-index: 1;
}
.linear-progress {
position: absolute;
width: 100%;
z-index: 2;
bottom: 0;
height: 4px;
border-radius: 3px;
overflow: hidden;
}
.progress {
height: 100%;
animation: progress 1.5s infinite;
background: linear-gradient(to right, #3D5AFE, #3D5AFE);
transform: translateX(-100%);
}
@keyframes progress {
0% {
transform: translateX(-100%);
}
50% {
transform: translateX(0);
}
100% {
transform: translateX(100%);
}
}
.linear-progress.hide {
display: none;
}
<html>
<head>
<title>Transit Service - Advanced</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="./style.css" />
<script type="module" src="./index.js"></script>
</head>
<body>
<div id="container">
<div id="sidebar">
<div id="innerWrapper">
<div class="sectionHeader">
<span>Origin</span>
<div
aria-label="Set Origin"
class="addLocation addLocation__origins"
>
<svg
width="10"
height="14"
viewBox="0 0 10 14"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4.21278471,13.2173279 C4.21344203,13.4210003 4.29852035,13.6152829 4.447752,13.7538931 C4.59698368,13.8925033 4.79700821,13.9630315 5.00017514,13.9486763 C5.20260653,13.9603467 5.40103075,13.8888849 5.54953735,13.7508253 C5.69804403,13.6127657 5.78376442,13.4200717 5.786865,13.2173279 C5.786865,11.8485015 6.73642703,10.7280113 7.74168082,9.54202199 C8.85201342,8.23203971 9.9998241,6.87722382 9.9998241,5.04184757 C10.0148585,3.24561164 9.06519921,1.57935055 7.51207057,0.676864524 C5.958942,-0.225621508 4.04105803,-0.225621508 2.48792942,0.676864524 C0.93480081,1.57935055 -0.0148585204,3.24561164 0.000175901018,5.04184757 C0.000175901018,6.87722382 1.14833685,8.23203971 2.25901969,9.54202199 C3.26322269,10.7280113 4.21278471,11.8485015 4.21278471,13.2173279 Z"
fill="#787878"
/>
</svg>
<div>Set origin</div>
</div>
</div>
<div class="transitOptions">
<ol id="origin" class="customCounter customCounter__origin"></ol>
</div>
<div class="sectionHeader">
<span>Destination</span>
<div
aria-label="Set Destination"
class="addLocation addLocation__destinations"
>
<svg
width="10"
height="14"
viewBox="0 0 10 14"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4.21278471,13.2173279 C4.21344203,13.4210003 4.29852035,13.6152829 4.447752,13.7538931 C4.59698368,13.8925033 4.79700821,13.9630315 5.00017514,13.9486763 C5.20260653,13.9603467 5.40103075,13.8888849 5.54953735,13.7508253 C5.69804403,13.6127657 5.78376442,13.4200717 5.786865,13.2173279 C5.786865,11.8485015 6.73642703,10.7280113 7.74168082,9.54202199 C8.85201342,8.23203971 9.9998241,6.87722382 9.9998241,5.04184757 C10.0148585,3.24561164 9.06519921,1.57935055 7.51207057,0.676864524 C5.958942,-0.225621508 4.04105803,-0.225621508 2.48792942,0.676864524 C0.93480081,1.57935055 -0.0148585204,3.24561164 0.000175901018,5.04184757 C0.000175901018,6.87722382 1.14833685,8.23203971 2.25901969,9.54202199 C3.26322269,10.7280113 4.21278471,11.8485015 4.21278471,13.2173279 Z"
fill="#787878"
/>
</svg>
<div>Set destination</div>
</div>
</div>
<div class="transitOptions">
<ol
id="destination"
class="customCounter customCounter__destination"
></ol>
</div>
<div class="sectionHeader"><span>Options</span></div>
<div class="transitOptions">
<div class="transitOptions__list">
<div class="transitOptions__content">
<span class="transitOptions__header">Departure time</span>
<div class="transitOptions__input">
<input
id="empty"
aria-label="Empty"
name="departureTime"
type="radio"
value="empty"
checked=""
/>
<label for="empty">Not Define (now)</label>
</div>
<div class="transitOptions__input">
<input
id="datetime"
aria-label="Datetime"
name="departureTime"
type="radio"
value="datetime"
/>
<input
id="departureTime-time"
name="departure-time"
type="datetime-local"
/>
</div>
</div>
</div>
</div>
<div class="transitOptions">
<div class="transitOptions__list">
<div class="transitOptions__content">
<span class="transitOptions__header">Arrival time</span>
<div class="transitOptions__input">
<input
id="empty"
aria-label="Empty"
name="arrivalTime"
type="radio"
value="empty"
checked=""
/>
<label for="empty">Not Define</label>
</div>
<div class="transitOptions__input">
<input
id="datetime"
aria-label="Datetime"
name="arrivalTime"
type="radio"
value="datetime"
/>
<input
id="arrivalTime-time"
name="arrival-time"
type="datetime-local"
/>
</div>
</div>
</div>
</div>
<div class="transitOptions">
<div class="transitOptions__list">
<div class="transitOptions__content">
<span class="transitOptions__header">Specify modes</span>
<div class="transitOptions__input">
<input
id="includeModes"
aria-label="include"
name="modeOption"
type="radio"
value="include"
checked=""
/>
<label for="empty">Include only</label>
</div>
<div class="transitOptions__input">
<input
id="excludeModes"
aria-label="exclude"
name="modeOption"
type="radio"
value="exclude"
/>
<label for="empty">Exclude</label>
</div>
<div class="transitOptions__input">
<input
class="modesChk"
id="modeTrain"
aria-label="Mode Train"
name="train"
type="checkbox"
/>
<label for="modeTrain">Train</label>
</div>
<div class="transitOptions__input">
<input
class="modesChk"
id="modeBus"
aria-label="Mode Bus"
name="bus"
type="checkbox"
/>
<label for="modeBus">Bus</label>
</div>
<div class="transitOptions__input">
<input
class="modesChk"
id="modeFerry"
aria-label="ferry"
name="modeFerry"
type="checkbox"
/>
<label for="modeFerry">Ferry</label>
</div>
<div class="transitOptions__input">
<input
class="modesChk"
id="modeAerial"
aria-label="aerial"
name="modeAerial"
type="checkbox"
/>
<label for="modeAerial">Aerial</label>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="mapContainer">
<div class="linear-progress hide">
<div class="progress"></div>
</div>
<div id="map"></div>
<div id="infoMessage">Drag markers to update route</div>
<div class="tableContainer"></div>
</div>
</div>
<script
src="https://sdk.woosmap.com/map/map.js?key=woos-48c80350-88aa-333e-835a-07f4b658a9a4&callback=initMap"
defer
></script>
</body>
</html>
Running the Sample Locally
Before you can run this sample on your local machine, you need to have Git and Node.js installed. If they’re not already installed, follow these instructions to get them set up.
Once you have Git and Node.js installed, you can run the sample by following these steps:
-
Clone the repository and navigate to the directory of the sample.
-
Install the necessary dependencies.
-
Start running the sample.
Here are the commands you can use in your terminal to do this:
git clone -b sample/transit-advanced https://github.com/woosmap/js-samples.git
cd js-samples
npm i
npm start
You can experiment with other samples by switching to any branch that starts with the pattern sample/SAMPLE_NAME
.
git checkout sample/SAMPLE_NAME
npm i
npm start
Was this article helpful?
Have more questions? Submit a request