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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more