Public
Edited
Dec 5, 2022
Importers
Insert cell
Insert cell
Insert cell
Insert cell
// basic rendering with default options
renderRouteMap(luckyduck_shop_ride__168__goldenrod)
Insert cell
// render a different basemap, adjust the overlay symbology styles
renderRouteMap(marin_headlands_loop, {
mapBasemap: "arcgis-streets-night",
symbolColor: "yellow",
symbolWidth: 1.5
})
Insert cell
// hide the map UI and disable the map UX
renderRouteMap(bike_to_wherever_day_alviso_to_san_jose, {
viewHideUI: true,
viewDisableUX: true
})
Insert cell
// render the overlay with no basemap and instead a flat background color
renderRouteMap(nitro_trail_10k, {
mapBasemap: "none",
mapBackgroundColor: "hsl(45, 95%, 80%)",
viewHideUI: true
})
Insert cell
renderRouteMap(sf_tour_with_twin_peaks_24m_1500_ft, {
mapBasemap: "arcgis-dark-gray"
})
Insert cell
renderRouteMap(silk_road_mountain_race_2022, {
mapBasemap: "arcgis-terrain"
})
Insert cell
renderRouteMap(gravel_tuesday_canonical_route, {
mapBasemap: "arcgis-newspaper",
symbolColor: "#000"
})
Insert cell
renderRouteMap(nitro_trail_10k, {
mapBasemap: "arcgis-terrain",
symbolColor: "red",
symbolLineJoin: "round",
symbolLineDash: "dash"
})
Insert cell
Insert cell
renderRouteMap = function* (xmlDoc, options = {}) {
let geojsonUrl;

if (xmlDoc instanceof XMLDocument) {
geojsonUrl = convertGpx(xmlDoc);
} else {
return "Requires XMLDocument object as first argument.";
}

if (!geojsonUrl) {
return "Invalid GPX file";
}

const {
mapWidth,
mapHeight,
mapBasemap,
mapBackgroundColor,
symbolColor,
symbolWidth,
symbolLineCap,
symbolLineDash,
symbolLineJoin,
viewPadding,
viewCenter,
viewZoom,
viewHideUI,
viewDisableUX
} = {
...defaultOptions,
...options
};

const id = `viewDiv-${DOM.uid().id}`;
const container = htl.html`<div id=${id} />
<style>
#${id} {
height: ${mapHeight};
width: ${mapWidth};
background-color: ${mapBackgroundColor};
}
</style>
`;

yield container;

const map = new ArcGIS.Map({
basemap: mapBasemap === "none" ? undefined : mapBasemap
});

const geojsonRenderer = new ArcGIS.SimpleRenderer({
symbol: new ArcGIS.SimpleLineSymbol({
width: symbolWidth,
color: symbolColor,
cap: symbolLineCap,
style: symbolLineDash,
join: symbolLineJoin
})
});

const geojsonLayer = new ArcGIS.GeoJSONLayer({
url: geojsonUrl,
renderer: geojsonRenderer
});

const mapView = new ArcGIS.MapView({
map,
center: viewCenter,
...(!viewHideUI && { zoom: viewZoom }),
padding: {
top: viewPadding,
right: viewPadding,
left: viewPadding,
bottom: viewPadding
},
container,
navigation: {
mouseWheelZoomEnabled: false,
browserTouchPanEnabled: false
},
...(viewHideUI && { ui: { components: [] } })
});

map.add(geojsonLayer);

geojsonLayer
.when(() => {
return geojsonLayer.queryExtent();
})
.then((response) => {
mapView.goTo(response.extent);
});

if (viewDisableUX) {
// 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();
});
}

return container;

// clean up when cell re-runs
invalidation.then(() => mapView.destroy());
}
Insert cell
defaultOptions = ({
mapWidth: `${width}px`,
mapHeight: "500px",
mapBasemap: "arcgis-topographic",
mapBackgroundColor: "#333",
symbolWidth: 2,
symbolColor: "#fb12ff",
symbolLineCap: "round",
symbolLineJoin: "round",
symbolLineDash: "solid",
viewPadding: 16,
viewCenter: [-122.27, 37.8],
viewZoom: 13,
viewHideUI: false,
viewDisableUX: false
})
Insert cell
convertGpx = function (xmlDocument) {
let dataUrl = "";
try {
const geojson = toGeoJson.gpx(xmlDocument);
const blob = new Blob([JSON.stringify(geojson)], {
type: "application/json"
});
dataUrl = URL.createObjectURL(blob);
} catch {}

return dataUrl;
}
Insert cell
Insert cell
luckyduck_shop_ride__168__goldenrod = FileAttachment(
"Luckyduck_Shop_Ride__168__Goldenrod_(copy).gpx"
).xml()
Insert cell
marin_headlands_loop = FileAttachment("Marin_Headlands_Loop.gpx").xml()
Insert cell
bike_to_wherever_day_alviso_to_san_jose = FileAttachment(
"Bike_to_Wherever_Day_-_Alviso_to_San_Jose.gpx"
).xml()
Insert cell
ferrieresSoulorAubisqueLaruns = FileAttachment("Ferrières-Soulor-Aubisque-Laruns.gpx").xml()
Insert cell
sf_tour_with_twin_peaks_24m_1500_ft = FileAttachment("SF_tour_with_Twin_Peaks_(24m_1500_ft).gpx").xml()
Insert cell
silk_road_mountain_race_2022 = FileAttachment("Silk_Road_Mountain_Race_2022.gpx").xml()
Insert cell
gravel_tuesday_canonical_route = FileAttachment("Gravel_Tuesday_Canonical_Route.gpx").xml()
Insert cell
nitro_trail_10k = FileAttachment("Nitro_Trail_10K.gpx").xml()
Insert cell
Insert cell
JS_API_VERSION = 4.25
Insert cell
JS_API_BASE_URL = `https://js.arcgis.com/${JS_API_VERSION}/@arcgis/core`
Insert cell
// NOTE: This key will only work for this notebook, please acquire your own at: https://developers.arcgis.com/api-keys
JS_API_KEY = "AAPKeaa067e241884f1f8ea72a5593cb8821aRuJV_aPxvhDXViKpdrzEOnB8RKhvKVPogv2Ry0d5AlAMEqq8S66zKkwHWHFBZKg"
Insert cell
getImport = async (path, ext = ".js") =>
await (
await import(`${JS_API_BASE_URL}${path}${ext}`)
).default
Insert cell
ArcGIS = {
const imports = await Promise.all(
[
"/config",
"/layers/GeoJSONLayer",
"/Map",
"/views/MapView",
"/renderers/SimpleRenderer",
"/symbols/SimpleLineSymbol"
].map((d) => getImport(d))
);

const importNames = [
"Config",
"GeoJSONLayer",
"Map",
"MapView",
"SimpleRenderer",
"SimpleLineSymbol"
];

const ArcGIS = Object.fromEntries(imports.map((d, i) => [importNames[i], d]));

const { Config } = ArcGIS;
Config.apiKey = JS_API_KEY;

if (!Config._style) {
const href = await require.resolve(
`${JS_API_BASE_URL}/assets/esri/themes/light/main.css`
);
document.head.appendChild(
(Config._style = html`<link href=${href} rel=stylesheet>`)
);
}

return ArcGIS;
}
Insert cell
toGeoJson = require("@tmcw/togeojson")
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