Public
Edited
Dec 19, 2022
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
map = {
const container = htl.html`
<div class="wrapper">
<div class="bg-image"></div>
<div class="map-container"></div>
</div>
<style>
.wrapper {
position: relative;
width: ${width}px;
height: ${Math.max(settings.mapHeight, settings.imageHeight)}px;
}
.map-container {
position: ${BG_IMG_URL ? "absolute" : "relative"};
width: ${MAP_WIDTH};
height: ${MAP_HEIGHT};
${BG_IMG_URL ? mapPositionProps[settings.mapPosition] : ""}
}
.bg-image {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: ${settings.imageWidth}px;
height: ${settings.imageHeight}px;
min-height: 100%;
background-image: url("${BG_IMG_URL}");
background-size: cover;
background-repeat: no-repeat;
}
.wrapper .route-stats-container {
position: absolute;
left: 0;
bottom: 0;
color: ${settings.statsFontColor};
}
</style>
`;

yield container;

const map = new ArcGIS.Map({
basemap: DEFAULT_SETTINGS.basemap,
ground: "world-elevation"
});

const mapView = new ArcGIS.MapView({
map,
center: [-122.27, 37.8],
padding: {
top: 16,
right: 16,
left: 16,
bottom: 16
},
zoom: 13,
container: container.querySelector(".map-container"),
navigation: {
mouseWheelZoomEnabled: false,
browserTouchPanEnabled: false
},
ui: { components: [] }
});

// see: https://developers.arcgis.com/javascript/latest/sample-code/view-disable-navigation/
mapView.on("key-down", (event) => {
const prohibitedKeys = ["+", "-", "Shift", "_", "="];
const keyPressed = event.key;
if (prohibitedKeys.indexOf(keyPressed) !== -1) {
event.stopPropagation();
}
});

mapView.on("double-click", (event) => {
event.stopPropagation();
});

mapView.on("double-click", ["Control"], (event) => {
event.stopPropagation();
});

mapView.on("drag", (event) => {
event.stopPropagation();
});

mapView.on("drag", ["Shift"], (event) => {
event.stopPropagation();
});

mapView.on("drag", ["Shift", "Control"], (event) => {
event.stopPropagation();
});

const polyline = new ArcGIS.Polyline({
paths: coordinates
});

const polylineSymbol = {
type: "simple-line",
width: settings.routeStrokeWidth,
color: settings.routeColor,
cap: settings.routeCapStyle,
style: settings.routeDashStyle,
join: settings.routeJoinStyle
};

const polylineGraphic = new ArcGIS.Graphic({
geometry: polyline,
symbol: polylineSymbol
});

mapView.graphics.add(polylineGraphic);
mapView.goTo(polylineGraphic.geometry.extent);

const elevationProfile = new ArcGIS.ElevationProfileViewModel({
view: mapView,
input: polylineGraphic,
unit: "imperial",
profiles: [
{
type: "input",
color: "#f57e42",
title: "Line elevation"
}
]
});

const handlerStats = ArcGIS.reactiveUtils.watch(
() => elevationProfile.profiles.items?.[0]?.statistics,
(statistics) => {
let statsContainer = container.querySelector(".route-stats-container");
if (statsContainer) {
statsContainer.remove();
}
container.querySelector(".wrapper").append(statisticsView(statistics));
}
);

return Object.assign(container, { map, mapView, polyline, polylineGraphic });

invalidation.then(() => {
mapView.destroy();
polylineGraphic.destroy();
elevationProfile.destroy();
handlerStats.remove();
});
}
Insert cell
{
if (!map || !map.map || !map.mapView) return;

// doesn't really work, because when "settings" changes the whole map cell re-excutes I think
map.map.basemap = settings.basemapOption;
}
Insert cell
Insert cell
Insert cell
Insert cell
// renders the UI for the route statistics
statisticsView = (elevStatsObj) => {
if (!elevStatsObj) return;

const { maxDistance, maxElevation } = elevStatsObj;

const stats = [
{
name: "Distance",
value: maxDistance,
unit: "mi"
},
{
name: "Elevation Gain",
value: maxElevation,
unit: "ft"
}
];

// NOTE: not all GPX data contains timestamps for track_points,
// for example routes created with planning tools like RideWithGPS.com
if (durationInSeconds) {
const value = getPaceInMPH(maxDistance, durationInSeconds);
stats.push({
name: "Pace",
value,
unit: "mph"
});
}

const container = html`<div class="route-stats-container">
<dl id="route-stats"></dl>
<style>
.route-stats-container {
background-color: rgba(255, 255, 255, 0.85);
padding: 0 1rem;
}
#route-stats {
display: flex;
gap: 2em;
}
#route-stats dt,
#route-stats dd {
margin: 0;
padding: 0;
}
#route-stats dd {
padding-top: 0.33em;
font-weight: bold;
}
</style>
</div>`;

const renderStat = (name, value, unit) =>
htl.html`<div class="stat">
<dt>${name}:</dt>
<dd>${value.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})} ${unit}</dd>
</div>`;

stats.forEach(({ name, value, unit }) => {
container
.querySelector("#route-stats")
.append(renderStat(name, value, unit));
});

return container;
}
Insert cell
getPaceInMPH = (distanceInMiles, timeInSeconds) => {
const oneHourInSeconds = 60 * 60;
const durationHours = timeInSeconds / oneHourInSeconds;
const mph = distanceInMiles / durationHours;
return mph;
}
Insert cell
coordinates = data.features[0].geometry.coordinates
Insert cell
durationInSeconds = {
if (!times || !Array.isArray(times) || times.length < 2) return;

const start = new Date(times.at(0));
const end = new Date(times.at(-1));
const diff = end.getTime() - start.getTime();
return diff / 1000;
}
Insert cell
times = dataCoordinateProps?.times
Insert cell
dataCoordinateProps = data?.features?.[0]?.properties?.coordinateProperties
Insert cell
data = toGeoJson.gpx(settings.selectedXmlDoc.value)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
DEFAULT_SETTINGS = ({
basemap: "arcgis-topographic"
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more