Route path report as map source and layer
Display a route path report as a map source and layer.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="https://maps-sdk.trimblemaps.com/v4/trimblemaps-4.2.7.css" />
<script src="https://maps-sdk.trimblemaps.com/v4/trimblemaps-4.2.7.js"></script>
<style>
body { margin: 0; padding: 0; }
html, body, #map { height: 100%; }
.controls {
position: absolute;
top: 10px;
left: 10px;
z-index: 1;
background: rgba(255, 255, 255, 0.95);
padding: 10px 14px;
border-radius: 6px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
}
.controls button {
font: 12px/20px system-ui, sans-serif;
background: #3386c0;
color: #fff;
padding: 8px 16px;
border: none;
cursor: pointer;
border-radius: 3px;
margin-right: 4px;
}
.controls button:hover { background: #4ea0da; }
.controls label {
margin-left: 12px;
font: 12px/20px system-ui, sans-serif;
cursor: pointer;
user-select: none;
color: #333;
}
.controls input[type="checkbox"] { margin-right: 4px; cursor: pointer; }
#status {
margin-left: 12px;
color: #333;
font: 12px/20px system-ui, sans-serif;
}
</style>
</head>
<body>
<div id="map"></div>
<div class="controls">
<button id="fetchRoute">Fetch route path & add to map</button>
<label><input type="checkbox" id="chkRoutePath" checked> Route path</label>
<label><input type="checkbox" id="chkStops" checked> Stops</label>
<span id="status"></span>
</div>
<script>
TrimbleMaps.setAPIKey('YOUR_API_KEY_HERE');
const map = new TrimbleMaps.Map({
container: 'map', // container id
style: TrimbleMaps.Common.Style.TRANSPORTATION, // hosted style id
center: [-118.31, 33.72],
zoom: 12
});
const ROUTE_SOURCE_ID = 'derived-route-path';
const ROUTE_LAYER_ID = 'derived-route-line';
const STOPS_SOURCE_ID = 'derived-route-stops';
const STOPS_LAYER_ID = 'derived-route-stops-layer';
let stopMarkers = [];
function setStatus(msg, isError) {
const el = document.getElementById('status');
if (el) {
el.textContent = msg;
el.style.color = isError ? '#c00' : '#333';
}
}
/**
* Normalize the Route path API response to a GeoJSON Feature.
* The derived route path API returns the response body as a string (JSON);
* it must be parsed first. The API returns GeoJSON Feature with
* geometry.type 'MultiLineString' or 'LineString'.
* @param {Object} response - callback (error, response) where response = { data }
* @returns {GeoJSON.Feature | null}
*/
function routePathResponseToGeoJSON(response) {
if (!response || response.data === undefined) return null;
let data = response.data;
// POST response is raw string – must parse (same as route.ts)
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (e) {
console.warn('Route path response is not valid JSON', e);
return null;
}
}
// Already a GeoJSON Feature (how derivedRoute/routePath returns it)
if (data && data.type === 'Feature' && data.geometry && data.geometry.coordinates) {
return {
type: 'Feature',
properties: data.properties || {},
geometry: data.geometry
};
}
// Alternate shapes: routePath array or plain coordinates array
let coordinates = null;
if (Array.isArray(data)) {
coordinates = data;
} else if (data && data.routePath && Array.isArray(data.routePath)) {
coordinates = data.routePath;
} else if (data && data.geometry && data.geometry.coordinates) {
const geom = data.geometry;
if (geom.type === 'LineString' || geom.type === 'MultiLineString') {
return {
type: 'Feature',
properties: {},
geometry: {type: geom.type, coordinates: geom.coordinates}
};
}
}
if (!coordinates || coordinates.length < 2) return null;
const normalized = coordinates.map((c) => {
if (Array.isArray(c)) return [Number(c[0]), Number(c[1])];
if (c && (c.lng !== undefined || c.lon !== undefined)) return [Number(c.lng ?? c.lon), Number(c.lat)];
return c;
});
return {
type: 'Feature',
properties: {},
geometry: {type: 'LineString', coordinates: normalized}
};
}
/**
* Add or update the route path as a GeoJSON source and line layer on the map.
* Call only after map has loaded. GeoJSON line layers support both
* LineString and MultiLineString.
* @param {Object} geojson - GeoJSON Feature with LineString or MultiLineString
*/
function addRoutePathToMap(geojson) {
if (!geojson || !geojson.geometry || !geojson.geometry.coordinates) return;
if (!map.isStyleLoaded()) {
console.warn('Map style not ready; add route path after map load.');
return;
}
const mapSource = map.getSource(ROUTE_SOURCE_ID);
if (mapSource) {
mapSource.setData(geojson);
} else {
map.addSource(ROUTE_SOURCE_ID, {
type: 'geojson',
data: geojson
});
map.addLayer({
id: ROUTE_LAYER_ID,
type: 'line',
source: ROUTE_SOURCE_ID,
layout: {
'line-join': 'round',
'line-cap': 'round'
},
paint: {
'line-color': '#3386c0',
'line-width': 6,
'line-opacity': 0.9
}
});
}
try {
const bounds = new TrimbleMaps.LngLatBounds();
const coords = geojson.geometry.type === 'LineString' ?
geojson.geometry.coordinates :
geojson.geometry.coordinates.flat(1);
coords.forEach((c) => { bounds.extend(c); });
map.fitBounds(bounds, {padding: 40, maxZoom: 14});
} catch (e) { /* ignore */ }
}
/**
* Get start and end coordinates from the route path GeoJSON (first and last point of the route).
* @param {Object} routeGeojson - GeoJSON Feature with LineString or MultiLineString
* @returns {Array<[number,number]>} [startCoord, endCoord] or empty if invalid
*/
function getStopsFromRoutePath(routeGeojson) {
if (!routeGeojson || !routeGeojson.geometry || !routeGeojson.geometry.coordinates) return [];
const coords = routeGeojson.geometry.coordinates;
let first, last;
if (routeGeojson.geometry.type === 'LineString') {
if (coords.length < 2) return [];
first = coords[0];
last = coords[coords.length - 1];
} else if (routeGeojson.geometry.type === 'MultiLineString') {
if (coords.length === 0) return [];
first = coords[0][0];
last = coords[coords.length - 1][coords[coords.length - 1].length - 1];
} else {
return [];
}
return [first, last];
}
function getStopCoords(stop) {
if (Array.isArray(stop)) return [Number(stop[0]), Number(stop[1])];
if (typeof stop.toArray === 'function') return stop.toArray();
return [Number(stop.lng ?? stop.lon), Number(stop.lat)];
}
function clearStopMarkers() {
stopMarkers.forEach((m) => { m.remove(); });
stopMarkers = [];
}
function addStopMarkers(stops, options) {
clearStopMarkers();
if (!stops || !Array.isArray(stops) || stops.length === 0) return;
const labels = (options && options.labels) ? options.labels : ['Start', 'End'];
stops.forEach((stop, i) => {
const coords = getStopCoords(stop);
const marker = new TrimbleMaps.Marker({color: '#3386c0'})
.setLngLat(coords)
.setPopup(new TrimbleMaps.Popup({offset: 25}).setText(labels[i] || `Stop ${i + 1}`))
.addTo(map);
stopMarkers.push(marker);
});
updateStopMarkersVisibility();
}
function updateStopMarkersVisibility() {
const show = document.getElementById('chkStops').checked;
stopMarkers.forEach((m) => {
const el = m.getElement();
if (el) el.style.display = show ? '' : 'none';
});
}
/**
* Add or update stops as a GeoJSON source and circle layer on the map.
* Also adds DOM Markers so stops are always visible regardless of style layers.
*/
function addStopsToMap(stops, options) {
if (!stops || !Array.isArray(stops) || stops.length === 0) return;
const features = stops.map((stop, i) => {
const coords = getStopCoords(stop);
const label = (options && options.labels && options.labels[i]) || (i === 0 ? 'Start' : (i === stops.length - 1 ? 'End' : `Stop ${i + 1}`));
return {
type: 'Feature',
properties: {stopType: i === 0 ? 'start' : (i === stops.length - 1 ? 'end' : 'stop'), label},
geometry: {type: 'Point', coordinates: coords}
};
});
const geojson = {type: 'FeatureCollection', features};
addStopMarkers(stops, options);
if (map.isStyleLoaded()) {
const mapSource = map.getSource(STOPS_SOURCE_ID);
if (mapSource) {
mapSource.setData(geojson);
} else {
map.addSource(STOPS_SOURCE_ID, {type: 'geojson', data: geojson});
const showStops = document.getElementById('chkStops').checked;
map.addLayer({
id: STOPS_LAYER_ID,
type: 'circle',
source: STOPS_SOURCE_ID,
minzoom: 0,
layout: {'visibility': showStops ? 'visible' : 'none'},
paint: {
'circle-radius': 10,
'circle-color': '#3386c0',
'circle-stroke-width': 2,
'circle-stroke-color': '#fff'
}
});
moveRouteAndStopsLayersToTop();
}
} else {
map.once('idle', () => {
if (map.getSource(STOPS_SOURCE_ID)) {
map.getSource(STOPS_SOURCE_ID).setData(geojson);
} else {
map.addSource(STOPS_SOURCE_ID, {type: 'geojson', data: geojson});
const showStops = document.getElementById('chkStops').checked;
map.addLayer({
id: STOPS_LAYER_ID,
type: 'circle',
source: STOPS_SOURCE_ID,
minzoom: 0,
layout: {'visibility': showStops ? 'visible' : 'none'},
paint: {
'circle-radius': 10,
'circle-color': '#3386c0',
'circle-stroke-width': 2,
'circle-stroke-color': '#fff'
}
});
moveRouteAndStopsLayersToTop();
}
});
}
updateLayerVisibility();
}
function moveRouteAndStopsLayersToTop() { // To keep the route path and stop circles on top of the base map.
try {
const layers = map.getStyle().layers;
if (!layers || !layers.length) return;
let topId = layers[layers.length - 1].id;
if (map.getLayer(ROUTE_LAYER_ID) && topId !== ROUTE_LAYER_ID) {
map.moveLayer(topId, ROUTE_LAYER_ID);
}
if (map.getLayer(STOPS_LAYER_ID)) {
topId = map.getStyle().layers[map.getStyle().layers.length - 1].id;
if (topId !== STOPS_LAYER_ID) map.moveLayer(topId, STOPS_LAYER_ID);
}
} catch (e) { /* ignore */ }
}
function updateLayerVisibility() {
const showRoute = document.getElementById('chkRoutePath').checked;
const showStops = document.getElementById('chkStops').checked;
if (map.getLayer(ROUTE_LAYER_ID)) {
map.setLayoutProperty(ROUTE_LAYER_ID, 'visibility', showRoute ? 'visible' : 'none');
}
if (map.getLayer(STOPS_LAYER_ID)) {
map.setLayoutProperty(STOPS_LAYER_ID, 'visibility', showStops ? 'visible' : 'none');
}
updateStopMarkersVisibility();
}
function fetchAndAddRoute() {
setStatus('Loading route path...');
const options = {
routePings: [
new TrimbleMaps.LngLat(-118.309434, 33.714154),
new TrimbleMaps.LngLat(-118.316631, 33.721061)
],
offRouteMiles: 0,
highwayOnly: false,
reportType: TrimbleMaps.Common.ReportType.MILEAGE,
region: TrimbleMaps.Common.Region.NA,
routingOptions: {
vehicleType: TrimbleMaps.Common.VehicleType.TRUCK,
routeType: TrimbleMaps.Common.RouteType.PRACTICAL,
routeOptimization: TrimbleMaps.Common.RouteOptimization.NONE,
tollDiscourage: false,
bordersOpen: true,
overrideRestrict: false,
highwayOnly: false,
hazMatTypes: [TrimbleMaps.Common.HazMatType.NONE],
distanceUnits: TrimbleMaps.Common.DistanceUnit.MILES,
trkUnits: TrimbleMaps.Common.TruckUnit.ENGLISH,
trkHeight: '13\u00276"',
trkLength: '48\u00270"',
trkWidth: '96"',
trkWeight: '80000',
trkAxles: 5,
useSites: false,
overrideClass: TrimbleMaps.Common.ClassOverride.NONE
},
extendedOptions: {
estimatedTimeOptions: {
eTAETDType: TrimbleMaps.Common.ETAETDType.DEPART,
dateOption: TrimbleMaps.Common.DateOption.SPECIFIC,
dateAndTime: {
calendarDate: '4/23/2014',
dayOfWeek: TrimbleMaps.Common.DayOfWeek.SUNDAY,
timeOfDay: '6:00 AM',
timeZone: TrimbleMaps.Common.TimeZone.LOCAL
}
},
useTraffic: true,
truckStyle: TrimbleMaps.Common.TruckConfig.NONE,
includeVehicleRestrictedCleanupPoints: false
},
callback (error, response) {
if (error) {
console.error('Route path request failed', error);
setStatus(`Error: ${error.message || error}`, true);
return;
}
const geojson = routePathResponseToGeoJSON(response);
if (geojson) {
addRoutePathToMap(geojson);
const stopsFromRoute = getStopsFromRoutePath(geojson);
addStopsToMap(stopsFromRoute.length ? stopsFromRoute : options.routePings, {labels: ['Start', 'End']});
updateLayerVisibility();
setTimeout(moveRouteAndStopsLayersToTop, 100);
setStatus('Route path and stops added to map.');
} else {
console.warn('Could not parse route path. Response:', response);
setStatus('No route path in response. Check console.', true);
}
}
};
TrimbleMaps.Routing.postDerivedRoutePath(options);
}
map.on('load', () => {
document.getElementById('fetchRoute').addEventListener('click', fetchAndAddRoute);
document.getElementById('chkRoutePath').addEventListener('change', updateLayerVisibility);
document.getElementById('chkStops').addEventListener('change', updateLayerVisibility);
});
</script>
</body>
</html>
Route path report as map source and layer
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="https://maps-sdk.trimblemaps.com/v4/trimblemaps-4.2.7.css" />
<script src="https://maps-sdk.trimblemaps.com/v4/trimblemaps-4.2.7.js"></script>
<style>
body { margin: 0; padding: 0; }
html, body, #map { height: 100%; }
.controls {
position: absolute;
top: 10px;
left: 10px;
z-index: 1;
background: rgba(255, 255, 255, 0.95);
padding: 10px 14px;
border-radius: 6px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
}
.controls button {
font: 12px/20px system-ui, sans-serif;
background: #3386c0;
color: #fff;
padding: 8px 16px;
border: none;
cursor: pointer;
border-radius: 3px;
margin-right: 4px;
}
.controls button:hover { background: #4ea0da; }
.controls label {
margin-left: 12px;
font: 12px/20px system-ui, sans-serif;
cursor: pointer;
user-select: none;
color: #333;
}
.controls input[type="checkbox"] { margin-right: 4px; cursor: pointer; }
#status {
margin-left: 12px;
color: #333;
font: 12px/20px system-ui, sans-serif;
}
</style>
</head>
<body>
<div id="map"></div>
<div class="controls">
<button id="fetchRoute">Fetch route path & add to map</button>
<label><input type="checkbox" id="chkRoutePath" checked> Route path</label>
<label><input type="checkbox" id="chkStops" checked> Stops</label>
<span id="status"></span>
</div>
<script>
TrimbleMaps.setAPIKey('YOUR_API_KEY_HERE');
const map = new TrimbleMaps.Map({
container: 'map', // container id
style: TrimbleMaps.Common.Style.TRANSPORTATION, // hosted style id
center: [-118.31, 33.72],
zoom: 12
});
const ROUTE_SOURCE_ID = 'derived-route-path';
const ROUTE_LAYER_ID = 'derived-route-line';
const STOPS_SOURCE_ID = 'derived-route-stops';
const STOPS_LAYER_ID = 'derived-route-stops-layer';
let stopMarkers = [];
function setStatus(msg, isError) {
const el = document.getElementById('status');
if (el) {
el.textContent = msg;
el.style.color = isError ? '#c00' : '#333';
}
}
/**
* Normalize the Route path API response to a GeoJSON Feature.
* The derived route path API returns the response body as a string (JSON);
* it must be parsed first. The API returns GeoJSON Feature with
* geometry.type 'MultiLineString' or 'LineString'.
* @param {Object} response - callback (error, response) where response = { data }
* @returns {GeoJSON.Feature | null}
*/
function routePathResponseToGeoJSON(response) {
if (!response || response.data === undefined) return null;
let data = response.data;
// POST response is raw string – must parse (same as route.ts)
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (e) {
console.warn('Route path response is not valid JSON', e);
return null;
}
}
// Already a GeoJSON Feature (how derivedRoute/routePath returns it)
if (data && data.type === 'Feature' && data.geometry && data.geometry.coordinates) {
return {
type: 'Feature',
properties: data.properties || {},
geometry: data.geometry
};
}
// Alternate shapes: routePath array or plain coordinates array
let coordinates = null;
if (Array.isArray(data)) {
coordinates = data;
} else if (data && data.routePath && Array.isArray(data.routePath)) {
coordinates = data.routePath;
} else if (data && data.geometry && data.geometry.coordinates) {
const geom = data.geometry;
if (geom.type === 'LineString' || geom.type === 'MultiLineString') {
return {
type: 'Feature',
properties: {},
geometry: {type: geom.type, coordinates: geom.coordinates}
};
}
}
if (!coordinates || coordinates.length < 2) return null;
const normalized = coordinates.map((c) => {
if (Array.isArray(c)) return [Number(c[0]), Number(c[1])];
if (c && (c.lng !== undefined || c.lon !== undefined)) return [Number(c.lng ?? c.lon), Number(c.lat)];
return c;
});
return {
type: 'Feature',
properties: {},
geometry: {type: 'LineString', coordinates: normalized}
};
}
/**
* Add or update the route path as a GeoJSON source and line layer on the map.
* Call only after map has loaded. GeoJSON line layers support both
* LineString and MultiLineString.
* @param {Object} geojson - GeoJSON Feature with LineString or MultiLineString
*/
function addRoutePathToMap(geojson) {
if (!geojson || !geojson.geometry || !geojson.geometry.coordinates) return;
if (!map.isStyleLoaded()) {
console.warn('Map style not ready; add route path after map load.');
return;
}
const mapSource = map.getSource(ROUTE_SOURCE_ID);
if (mapSource) {
mapSource.setData(geojson);
} else {
map.addSource(ROUTE_SOURCE_ID, {
type: 'geojson',
data: geojson
});
map.addLayer({
id: ROUTE_LAYER_ID,
type: 'line',
source: ROUTE_SOURCE_ID,
layout: {
'line-join': 'round',
'line-cap': 'round'
},
paint: {
'line-color': '#3386c0',
'line-width': 6,
'line-opacity': 0.9
}
});
}
try {
const bounds = new TrimbleMaps.LngLatBounds();
const coords = geojson.geometry.type === 'LineString' ?
geojson.geometry.coordinates :
geojson.geometry.coordinates.flat(1);
coords.forEach((c) => { bounds.extend(c); });
map.fitBounds(bounds, {padding: 40, maxZoom: 14});
} catch (e) { /* ignore */ }
}
/**
* Get start and end coordinates from the route path GeoJSON (first and last point of the route).
* @param {Object} routeGeojson - GeoJSON Feature with LineString or MultiLineString
* @returns {Array<[number,number]>} [startCoord, endCoord] or empty if invalid
*/
function getStopsFromRoutePath(routeGeojson) {
if (!routeGeojson || !routeGeojson.geometry || !routeGeojson.geometry.coordinates) return [];
const coords = routeGeojson.geometry.coordinates;
let first, last;
if (routeGeojson.geometry.type === 'LineString') {
if (coords.length < 2) return [];
first = coords[0];
last = coords[coords.length - 1];
} else if (routeGeojson.geometry.type === 'MultiLineString') {
if (coords.length === 0) return [];
first = coords[0][0];
last = coords[coords.length - 1][coords[coords.length - 1].length - 1];
} else {
return [];
}
return [first, last];
}
function getStopCoords(stop) {
if (Array.isArray(stop)) return [Number(stop[0]), Number(stop[1])];
if (typeof stop.toArray === 'function') return stop.toArray();
return [Number(stop.lng ?? stop.lon), Number(stop.lat)];
}
function clearStopMarkers() {
stopMarkers.forEach((m) => { m.remove(); });
stopMarkers = [];
}
function addStopMarkers(stops, options) {
clearStopMarkers();
if (!stops || !Array.isArray(stops) || stops.length === 0) return;
const labels = (options && options.labels) ? options.labels : ['Start', 'End'];
stops.forEach((stop, i) => {
const coords = getStopCoords(stop);
const marker = new TrimbleMaps.Marker({color: '#3386c0'})
.setLngLat(coords)
.setPopup(new TrimbleMaps.Popup({offset: 25}).setText(labels[i] || `Stop ${i + 1}`))
.addTo(map);
stopMarkers.push(marker);
});
updateStopMarkersVisibility();
}
function updateStopMarkersVisibility() {
const show = document.getElementById('chkStops').checked;
stopMarkers.forEach((m) => {
const el = m.getElement();
if (el) el.style.display = show ? '' : 'none';
});
}
/**
* Add or update stops as a GeoJSON source and circle layer on the map.
* Also adds DOM Markers so stops are always visible regardless of style layers.
*/
function addStopsToMap(stops, options) {
if (!stops || !Array.isArray(stops) || stops.length === 0) return;
const features = stops.map((stop, i) => {
const coords = getStopCoords(stop);
const label = (options && options.labels && options.labels[i]) || (i === 0 ? 'Start' : (i === stops.length - 1 ? 'End' : `Stop ${i + 1}`));
return {
type: 'Feature',
properties: {stopType: i === 0 ? 'start' : (i === stops.length - 1 ? 'end' : 'stop'), label},
geometry: {type: 'Point', coordinates: coords}
};
});
const geojson = {type: 'FeatureCollection', features};
addStopMarkers(stops, options);
if (map.isStyleLoaded()) {
const mapSource = map.getSource(STOPS_SOURCE_ID);
if (mapSource) {
mapSource.setData(geojson);
} else {
map.addSource(STOPS_SOURCE_ID, {type: 'geojson', data: geojson});
const showStops = document.getElementById('chkStops').checked;
map.addLayer({
id: STOPS_LAYER_ID,
type: 'circle',
source: STOPS_SOURCE_ID,
minzoom: 0,
layout: {'visibility': showStops ? 'visible' : 'none'},
paint: {
'circle-radius': 10,
'circle-color': '#3386c0',
'circle-stroke-width': 2,
'circle-stroke-color': '#fff'
}
});
moveRouteAndStopsLayersToTop();
}
} else {
map.once('idle', () => {
if (map.getSource(STOPS_SOURCE_ID)) {
map.getSource(STOPS_SOURCE_ID).setData(geojson);
} else {
map.addSource(STOPS_SOURCE_ID, {type: 'geojson', data: geojson});
const showStops = document.getElementById('chkStops').checked;
map.addLayer({
id: STOPS_LAYER_ID,
type: 'circle',
source: STOPS_SOURCE_ID,
minzoom: 0,
layout: {'visibility': showStops ? 'visible' : 'none'},
paint: {
'circle-radius': 10,
'circle-color': '#3386c0',
'circle-stroke-width': 2,
'circle-stroke-color': '#fff'
}
});
moveRouteAndStopsLayersToTop();
}
});
}
updateLayerVisibility();
}
function moveRouteAndStopsLayersToTop() { // To keep the route path and stop circles on top of the base map.
try {
const layers = map.getStyle().layers;
if (!layers || !layers.length) return;
let topId = layers[layers.length - 1].id;
if (map.getLayer(ROUTE_LAYER_ID) && topId !== ROUTE_LAYER_ID) {
map.moveLayer(topId, ROUTE_LAYER_ID);
}
if (map.getLayer(STOPS_LAYER_ID)) {
topId = map.getStyle().layers[map.getStyle().layers.length - 1].id;
if (topId !== STOPS_LAYER_ID) map.moveLayer(topId, STOPS_LAYER_ID);
}
} catch (e) { /* ignore */ }
}
function updateLayerVisibility() {
const showRoute = document.getElementById('chkRoutePath').checked;
const showStops = document.getElementById('chkStops').checked;
if (map.getLayer(ROUTE_LAYER_ID)) {
map.setLayoutProperty(ROUTE_LAYER_ID, 'visibility', showRoute ? 'visible' : 'none');
}
if (map.getLayer(STOPS_LAYER_ID)) {
map.setLayoutProperty(STOPS_LAYER_ID, 'visibility', showStops ? 'visible' : 'none');
}
updateStopMarkersVisibility();
}
function fetchAndAddRoute() {
setStatus('Loading route path...');
const options = {
routePings: [
new TrimbleMaps.LngLat(-118.309434, 33.714154),
new TrimbleMaps.LngLat(-118.316631, 33.721061)
],
offRouteMiles: 0,
highwayOnly: false,
reportType: TrimbleMaps.Common.ReportType.MILEAGE,
region: TrimbleMaps.Common.Region.NA,
routingOptions: {
vehicleType: TrimbleMaps.Common.VehicleType.TRUCK,
routeType: TrimbleMaps.Common.RouteType.PRACTICAL,
routeOptimization: TrimbleMaps.Common.RouteOptimization.NONE,
tollDiscourage: false,
bordersOpen: true,
overrideRestrict: false,
highwayOnly: false,
hazMatTypes: [TrimbleMaps.Common.HazMatType.NONE],
distanceUnits: TrimbleMaps.Common.DistanceUnit.MILES,
trkUnits: TrimbleMaps.Common.TruckUnit.ENGLISH,
trkHeight: '13\u00276"',
trkLength: '48\u00270"',
trkWidth: '96"',
trkWeight: '80000',
trkAxles: 5,
useSites: false,
overrideClass: TrimbleMaps.Common.ClassOverride.NONE
},
extendedOptions: {
estimatedTimeOptions: {
eTAETDType: TrimbleMaps.Common.ETAETDType.DEPART,
dateOption: TrimbleMaps.Common.DateOption.SPECIFIC,
dateAndTime: {
calendarDate: '4/23/2014',
dayOfWeek: TrimbleMaps.Common.DayOfWeek.SUNDAY,
timeOfDay: '6:00 AM',
timeZone: TrimbleMaps.Common.TimeZone.LOCAL
}
},
useTraffic: true,
truckStyle: TrimbleMaps.Common.TruckConfig.NONE,
includeVehicleRestrictedCleanupPoints: false
},
callback (error, response) {
if (error) {
console.error('Route path request failed', error);
setStatus(`Error: ${error.message || error}`, true);
return;
}
const geojson = routePathResponseToGeoJSON(response);
if (geojson) {
addRoutePathToMap(geojson);
const stopsFromRoute = getStopsFromRoutePath(geojson);
addStopsToMap(stopsFromRoute.length ? stopsFromRoute : options.routePings, {labels: ['Start', 'End']});
updateLayerVisibility();
setTimeout(moveRouteAndStopsLayersToTop, 100);
setStatus('Route path and stops added to map.');
} else {
console.warn('Could not parse route path. Response:', response);
setStatus('No route path in response. Check console.', true);
}
}
};
TrimbleMaps.Routing.postDerivedRoutePath(options);
}
map.on('load', () => {
document.getElementById('fetchRoute').addEventListener('click', fetchAndAddRoute);
document.getElementById('chkRoutePath').addEventListener('change', updateLayerVisibility);
document.getElementById('chkStops').addEventListener('change', updateLayerVisibility);
});
</script>
</body>
</html>