Skip to content

Commit

Permalink
Initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
jkieboom committed Apr 5, 2022
1 parent c1957cc commit 2feef5d
Show file tree
Hide file tree
Showing 90 changed files with 8,303 additions and 4 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.DS_Store
node_modules/**/*
dist/**/*
reveal.js
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "reveal.js"]
path = reveal.js
url = https://github.com/Esri/reveal.js
3 changes: 3 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"printWidth": 100
}
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ This repository contains the 3D Visualization presentation and accompanying code

## Presentation

[Live presentation: 3D Visualization](https://esridevsummit.github.io/DS2022-3D-Visualization/index.html)
[Live presentation: 3D Visualization](https://esridevsummit.github.io/DS2022-3D-Visualization/)

Arno Fiva, Esri R&D Center Zürich
Lisa Staehli, Esri R&D Center Zürich
Jesse van den Kieboom, Esri R&D Center Zürich
- Arno Fiva, Esri R&D Center Zürich
- Lisa Staehli, Esri R&D Center Zürich
- Jesse van den Kieboom, Esri R&D Center Zürich

## Local setup

Expand Down
19 changes: 19 additions & 0 deletions common/scenes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const DENVER_PARCELS = "3e1228d3c6e2442c890843a638bb9228";
export const ESRI_OFFICE_BSL = "94379f34cee14049a9273ab91ac1743f";
export const HIKING_TRAILS = "8037d7b07bd2402d83faba39a60c76af";
export const MANHATTAN = "9a542f6755274436985617a462ffdf44";
export const MUNICH = "cfddaf892c4b477bbc42a662acc4e88a";
export const SAMEDAN_AIRPORT = "3ef242366dc24c2d98e3f8a0f253ae12";
export const SAN_FRANCISCO = "c7adc0ed4c124c72ad7bc79bddada094";
export const SAN_FRANCISCO_EDITABLE = "2e38f798f3124f2dae84217839cc43dc";
export const SHADOW_CAST = "8d7de23cd45f4b3fa4932c57272bacf1";
export const WORLD_CAPITALS = "c025297bbf004abc863773858add4058";
export const HELSINKI = "a350d4d2b71a40a6a95031d25da9535f";
export const HELSINKI_BUILDING_NAME = "Helsinki_buildings";
export const HELSINKI_FIELDS = {
addressField: "address",
usageField: "usage",
yearField: "yearCompleted",
solarAreaField: "solarAreaSuitableM2",
solarPotentialField: "solarElectricitGenPotYearlyKWh",
};
331 changes: 331 additions & 0 deletions common/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,331 @@
import Camera from "@arcgis/core/Camera";
import { when } from "@arcgis/core/core/reactiveUtils";
import Point from "@arcgis/core/geometry/Point";
import Graphic from "@arcgis/core/Graphic";
import IdentityManager from "@arcgis/core/identity/IdentityManager";
import OAuthInfo from "@arcgis/core/identity/OAuthInfo";
import GraphicsLayer from "@arcgis/core/layers/GraphicsLayer";
import SceneLayer from "@arcgis/core/layers/SceneLayer";
import ClassBreaksRenderer from "@arcgis/core/renderers/ClassBreaksRenderer";
import StatisticDefinition from "@arcgis/core/rest/support/StatisticDefinition";
import SceneView from "@arcgis/core/views/SceneView";
import WebScene from "@arcgis/core/WebScene";
import SketchViewModel from "@arcgis/core/widgets/Sketch/SketchViewModel";
import "@esri/calcite-components";
import "@esri/calcite-components/dist/components/calcite-alert";
import "@esri/calcite-components/dist/components/calcite-loader";
import { HELSINKI_FIELDS } from "./scenes";

const Reveal = (parent as any).Reveal as RevealStatic | null;

export function initView(itemId?: string, camera?: Camera) {
const container = document.getElementById("viewDiv") as HTMLDivElement;

const viewProps: __esri.SceneViewProperties = {
container,
map: itemId ? new WebScene({ portalItem: { id: itemId } }) : new WebScene({ basemap: "topo" }),
qualityProfile: "medium",
popup: { defaultPopupTemplateEnabled: false },
};

const view = new SceneView(camera ? { ...viewProps, camera } : viewProps);

(view as any).pixelRatio = 1.5;

showSpinnerUntilLoaded(view);

return view;
}

function showSpinnerUntilLoaded(view: SceneView): void {
// const container = view.container;
// container.classList.add("loading");
// const loader = container.appendChild(document.createElement("calcite-loader"));
// loader.type = "indeterminate";
// loader.active = true;
// when(
// () => !view.groundView.updating,
// () => {
// container.classList.remove("loading");
// loader.remove();
// },
// { once: true }
// );
}

export function onInit(title: string, cb: () => void) {
if (getCurrentSlide()?.getAttribute("data-slideId") === title) {
cb();
}
}

export function onFragment(id: string, cb: () => void) {
if (!Reveal) {
return;
}

const run = () => {
if (getCurrentFragmentId() === id) {
cb();
}
};

Reveal.addEventListener("fragmenthidden", run);
Reveal.addEventListener("fragmentshown", run);
run();
}

export function onPlayClick(name: string, cb: () => void): void {
getCurrentSlide()
?.querySelector(`[data-fragment-id="${name}"] > .play`)
?.addEventListener("click", cb);
}

export function getCurrentSlide(): HTMLElement | null {
return Reveal?.getCurrentSlide() as HTMLElement;
}

export function getCurrentFragment(): HTMLElement | null {
return getCurrentSlide()?.querySelector(".current-fragment") ?? null;
}

function getCurrentFragmentId(): string | null {
return getCurrentFragment()?.getAttribute("data-fragment-id") ?? null;
}

export function setHeader(header: string, selector: string = ".header"): void {
const headerElement = getCurrentSlide()?.querySelector(selector);
if (!headerElement) {
return;
}

headerElement.textContent = header;
}

export function showAlert(msg: string): void {
const alertElement = document.createElement("calcite-alert");
alertElement.active = true;

const msgElement = alertElement.appendChild(document.createElement("div"));
msgElement.slot = "message";
msgElement.textContent = msg;

document.body.appendChild(alertElement);
}

export function addOAuthSupport(): void {
IdentityManager.registerOAuthInfos([
new OAuthInfo({
appId: "pZzd4uJ0gZddupQh",
popup: true,
popupCallbackUrl: `${import.meta.env.BASE_URL}oauth-callback-api.html`,
}),
]);

(window as any).setOAuthResponseHash = (responseHash: string) => {
IdentityManager.setOAuthResponseHash(responseHash);
};
}

export function throwIfAborted(signal: AbortSignal): void {
if (signal.aborted) {
throw new Error("AbortError");
}
}

export function throwIfNotAbortError(e: any): void {
if (e?.message !== "AbortError") {
throw e;
}
}

export async function getLayerFromView(layerName: string, view: SceneView): Promise<SceneLayer> {
await (view.map as any).load();
return view.map.layers.find((l) => l.title === layerName) as SceneLayer;
}

const yearClasses = [
{
minYear: 1500,
maxYear: 1899,
color: "#bd0026",
label: "<1900",
},
{
minYear: 1900,
maxYear: 1924,
color: "#f03b20",
label: "1900 - 1924",
},
{
minYear: 1925,
maxYear: 1949,
color: "#fd8d3c",
label: "1925 - 1949",
},
{
minYear: 1950,
maxYear: 1974,
color: "#feb24c",
label: "1951 - 1974",
},
{
minYear: 1975,
maxYear: 1999,
color: "#fed976",
label: "1975 - 1999",
},
{
minYear: 2000,
maxYear: 2020,
color: "#ffffb2",
label: "2000 - 2020",
},
];

export function applyYearRenderer(layer: SceneLayer) {
const classBreakInfos = yearClasses.map((year) => {
return {
minValue: year.minYear,
maxValue: year.maxYear,
symbol: {
type: "mesh-3d",
symbolLayers: [
{
type: "fill",
material: {
color: year.color,
colorMixMode: "replace",
},
edges: {
type: "solid",
color: [50, 50, 50, 0.5],
},
},
],
},
};
});

layer.renderer = new ClassBreaksRenderer({
field: HELSINKI_FIELDS.yearField,
defaultSymbol: {
type: "mesh-3d",
symbolLayers: [
{
type: "fill",
material: {
color: "white",
colorMixMode: "replace",
},
edges: {
type: "solid",
color: [50, 50, 50, 0.5],
},
},
],
} as any,
classBreakInfos: classBreakInfos as any,
});
}

export function applySolarAreaRenderer(layer: SceneLayer) {
layer.renderer = {
type: "simple",
symbol: {
type: "mesh-3d",
symbolLayers: [
{
type: "fill",
material: { color: "white", colorMixMode: "replace" },
edges: {
type: "solid",
color: [50, 50, 50, 0.5],
},
},
],
},
visualVariables: [
{
type: "color",
field: HELSINKI_FIELDS.solarAreaField,
legendOptions: {
title: "Solar-Suitable Area (m<sup>2</sup>)",
},
stops: [
{ value: 200, color: "#406f8a", label: "< 100 m<sup>2</sup>" },
{ value: 1000, color: "#ffe23b", label: "> 1000 m<sup>2</sup>" },
],
binSize: 200,
},
],
} as any;
}

export const point = new Point({
spatialReference: { latestWkid: 3857, wkid: 102100 } as any,
x: 2775690.9496367406,
y: 8434900.987816326,
z: 50,
});

export function addEditablePoint(view: SceneView, onUpdate: (pointGraphic: Graphic) => void) {
const graphicsLayer = new GraphicsLayer({
// elevationInfo: { mode: "on-the-ground" },
title: "Sketch GraphicsLayer",
});
const markerSymbol = {
type: "simple-marker", // autocasts as new SimpleMarkerSymbol()
color: [226, 119, 40],
outline: {
// autocasts as new SimpleLineSymbol()
color: [255, 255, 255],
width: 2,
},
};
const pointGraphic = new Graphic({
geometry: point,
symbol: markerSymbol,
});
graphicsLayer.add(pointGraphic);

let sketchVM = new SketchViewModel({
layer: graphicsLayer,
view: view,
defaultCreateOptions: {
hasZ: true, // default value
},
defaultUpdateOptions: {
enableZ: true, // default value
},
});

view.on("click", async (e) => {
const hitTestResult = await view.hitTest(e);
if (hitTestResult.results.find((r) => r.graphic === pointGraphic)) {
sketchVM.update(pointGraphic);
}
});

sketchVM.on("update", () => onUpdate(pointGraphic));

return { graphicsLayer, pointGraphic };
}

// setup statistic definition
export const sumLabel = "sum_area";
export const statDefinition: StatisticDefinition[] = [
new StatisticDefinition({
onStatisticField: HELSINKI_FIELDS.solarAreaField,
outStatisticFieldName: sumLabel,
statisticType: "sum",
}),
];

export function addSumArea(view: SceneView) {
const sumContainer = document.getElementById("sumContainer") as HTMLDivElement;
sumContainer.style.display = "block";
sumContainer.style.backgroundColor = "white";
sumContainer.style.padding = "5px";
view.ui.add("sumContainer", "top-right");
}
Loading

0 comments on commit 2feef5d

Please sign in to comment.