Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
609 lines (519 sloc) 15.7 KB
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name=”twitter:card” content=”summary_large_image”>
<meta name=”twitter:site” content=”@ceddc>
<meta name=”twitter:creator” content=”@ceddc>
<meta name=”twitter:title” content=”Evolution de l'année de construction des bâtiments de Paris entre 1600 et 2017”>
<meta name=”twitter:description” content=”Evolution de l'année de construction des bâtiments de Paris entre 1600 et 2017 avec l'API ArcGIS API for JavaScript”>
<meta name=”twitter:image” content=”https://ceddc.github.io/BitsofStuff/ArcGIS-Javascript-45-Paris-Buildings/newcard.png“>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<title>Evolution de l'année de construction des bâtiments de Paris entre 1600 et 2017</title>
<link rel="stylesheet" href="https://js.arcgis.com/4.5/esri/themes/dark-blue/main.css">
<script>
var dojoConfig = {
has: {
// Enable webgl for feature layer in MapView
"esri-featurelayer-webgl": 1
}
};
</script>
<!-- Global Site Tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-92318045-2"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-92318045-2');
</script>
<script src="https://js.arcgis.com/4.5/"></script>
<style>
html,
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
#applicationDiv {
position: absolute;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
#viewDiv {
flex: 1 1 auto;
order: 1;
}
#titleDiv {
font-weight: 400;
font-style: normal;
font-size: 1.2019rem;
padding: 10px;
}
#sliderContainer {
flex: 0 0 80px;
order: 2;
display: flex;
flex-flow: row;
padding: 0 12px;
}
#sliderValue {
flex: 0 0 100px;
order: 1;
display: flex;
justify-content: center;
flex-direction: column;
text-align: center;
font-size: 300%;
}
#sliderInnerContainer {
flex: 1 1 auto;
order: 2;
display: flex;
flex-direction: column;
justify-content: center;
padding: 0 20px;
}
#sliderLabels {
flex: 1 1 auto;
order: 1;
display: flex;
justify-content: space-between;
margin-top: 20px;
}
#rangeWrapper {
flex: 1 1 auto;
order: 2;
position: relative;
padding: 0 20px;
}
#slider {
width: 100%;
}
/**
* Play/Stop toggle button
*/
#playButton {
flex: 0 0 100px;
order: 3;
margin: 20px 0;
}
.toggle-button {
display: flex;
}
.toggle-button.toggled .toggle-button-icon {
color: #CC1B1B;
}
.toggle-button .toggle-button-icon {
color: #1BCC1B;
}
.toggle-button> :nth-child(2) {
display: none;
}
.toggle-button.toggled> :nth-child(1) {
display: none;
}
.toggle-button.toggled> :nth-child(2) {
display: block;
}
/**
* Hover tooltip
*/
.tooltip {
position: absolute;
pointer-events: none;
transition: opacity 200ms;
}
.tooltip>div {
margin: 0 auto;
padding: 12px;
border-radius: 4px;
box-shadow: 0px 0px 4px rgba(255, 255, 255, 0.75);
transform: translate3d(-50%, -125%, 0);
}
</style>
<script>
require([
"esri/Map",
"esri/layers/FeatureLayer",
"esri/views/MapView",
"esri/widgets/Legend",
"esri/widgets/Home",
"dojo/domReady!"
], function(
Map,
FeatureLayer,
MapView,
Legend, Home
) {
//--------------------------------------------------------------------------
//
// Setup Map and View
//
//--------------------------------------------------------------------------
var layer = new FeatureLayer({
portalItem: {
id: "d50dd448c80e46cdbcee8adcd9945a0f"
},
definitionExpression: "AN_CONST > 0",
title: "Bâtiment avec année de construction connue",
minScale: 144447.638572,
//racourcis
copyright: "<a href=\"http://opendata.apur.org\">Plateforme Open Data - Apur</a> | <a href=\"https://github.com/ceddc/BitsofStuff/tree/master/ArcGIS-Javascript-45-Paris-Buildings\">About</a> this dataviz"
});
var map = new Map({
basemap: {
portalItem: {
id: "4f2e99ba65e34bb8af49733d9778fb8e"
}
},
layers: [layer]
});
view = new MapView({
map: map,
container: "viewDiv",
center: [2.349014, 48.864716],
zoom: 12,
constraints: {
snapToZoom: false,
minScale: 144447.638572
},
// This ensures that when going fullscreen
// The top left corner of the view extent
// stays aligned with the top left corner
// of the view's container
resizeAlign: "top-left"
});
//--------------------------------------------------------------------------
//
// Setup UI
//
//--------------------------------------------------------------------------
var applicationDiv = document.getElementById("applicationDiv");
var fullScreenButton = document.getElementById("fullScreenButton");
var slider = document.getElementById("slider");
var sliderValue = document.getElementById("sliderValue");
var playButton = document.getElementById("playButton");
var titleDiv = document.getElementById("titleDiv");
var animation = null;
// When user drags the slider:
// - stops the animation
// - set the visualized year to the slider one.
slider.addEventListener("input", function() {
stopAnimation();
setYear(parseInt(slider.value));
});
// Toggle animation on/off when user
// clicks on the play button
playButton.addEventListener("click", function() {
if (playButton.classList.contains("toggled")) {
stopAnimation();
}
else {
startAnimation();
}
});
view.ui.empty("top-left");
view.ui.add(titleDiv, "top-left");
view.ui.add(new Home({
view: view
}), "top-left");
view.ui.add(new Legend({
view: view
}), "bottom-left");
// When the layerview is available, setup hovering interactivity
view.whenLayerView(layer).then(setupHoverTooltip);
view.whenLayerView(layer).then(layer.fetchAttributionData())
// Starts the application by visualizing year 1984
setYear(1789);
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* Sets the current visualized construction year.
*/
function setYear(value) {
sliderValue.innerHTML = Math.floor(value);
slider.value = Math.floor(value);
layer.renderer = createRenderer(value);
}
/**
* Returns a renderer with a color visual variable driven by the input
* year. The selected year will always render buildings built in that year
* with a light blue color. Buildings built 20+ years before the indicated
* year are visualized with a pink color. Buildings built within that
* 20-year time frame are assigned a color interpolated between blue and pink.
*/
function createRenderer(year) {
var opacityStops = [
{
opacity: 1,
value: year
},
{
opacity: 0,
value: year + 1
}];
return {
type: "simple",
symbol: {
type: "simple-fill",
color: "rgb(0, 0, 0)",
outline: null
},
visualVariables: [{
type: "opacity",
field: "AN_CONST",
stops: opacityStops,
legendOptions: {
showLegend: false
}
}, {
type: "color",
field: "AN_CONST",
legendOptions: {
title: "Bâtiments construits:"
},
stops: [{
value: year,
color: "#0ff",
label: "en " + Math.floor(year)
}, {
value: year - 10,
color: "#f0f",
label: "en " + (Math.floor(year) - 20)
}, {
value: year - 50,
color: "#404",
label: "avant " + (Math.floor(year) - 50)
}]
}]
};
}
/**
* Sets up a moving tooltip that displays
* the construction year of the hovered building.
*/
function setupHoverTooltip(layerview) {
var promise;
var highlight;
var tooltip = createTooltip();
view.on("pointer-move", function(event) {
if (promise) {
promise.cancel();
}
promise = view.hitTest(event.x, event.y)
.then(hit => {
promise = null;
// remove current highlighted feature
if (highlight) {
highlight.remove();
highlight = null;
}
// highlight the hovered feature
// or hide the tooltip
if (hit.results.length) {
var graphic = hit.results[0].graphic;
var screenPoint = hit.screenPoint;
highlight = layerview.highlight(graphic);
tooltip.show(screenPoint, "Bâtiment construit en " + graphic.getAttribute(
"AN_CONST"));
}
else {
tooltip.hide();
}
});
});
}
/**
* Starts the animation that cycle
* through the construction years.
*/
function startAnimation() {
stopAnimation();
animation = animate(parseFloat(slider.value));
playButton.classList.add("toggled");
}
/**
* Stops the animations
*/
function stopAnimation() {
if (!animation) {
return;
}
animation.remove();
animation = null;
playButton.classList.remove("toggled");
}
/**
* Animates the color visual variable continously
*/
function animate(startValue) {
var animating = true;
var value = startValue;
var frame = function(timestamp) {
if (!animating) {
return;
}
value += 0.5;
if (value > 2017) {
value = 1600;
}
setYear(value);
// Update at 30fps
setTimeout(function() {
requestAnimationFrame(frame);
}, 1000 / 30);
}
frame();
return {
remove: function() {
animating = false;
}
};
}
/**
* Fullscreen API to easily move the application fullscreen
*/
var fullscreenAPI = null;
var fullScreenFnNames = ["requestFullscreen",
"webkitRequestFullscreen", "mozRequestFullScreen",
"msRequestFullscreen"
];
var fullscreenElementNames = ["fullscreenElement",
"webkitFullscreenElement", "mozFullScreenElement",
"msFullscreenElement"
];
var exitFullscreenFnNames = ["exitFullscreen", "webkitExitFullscreen",
"mozCancelFullScreen", "msExitFullscreen"
];
var fullScreenEventNames = ["fullscreenchange",
"webkitfullscreenchange", "mozfullscreenchange",
"MSFullscreenChange"
];
fullScreenFnNames.some(function(fnName, index) {
if (!view.container[fnName]) {
fullscreenAPI = {
supported: false
};
return false;
}
fullscreenAPI = {
supported: true,
eventName: fullScreenEventNames[index],
isFullscreen: function() {
return document[fullscreenElementNames[index]];
},
toggle: function() {
if (!document[fullscreenElementNames[index]]) {
applicationDiv[fnName]();
} else {
document[exitFullscreenFnNames[index]]();
}
}
}
return true;
});
if (!fullscreenAPI.supported) {
fullScreenButton.parentElement.removeChild(fullScreenButton);
}
else {
fullScreenButton.addEventListener("click", fullscreenAPI.toggle)
document.addEventListener(fullscreenAPI.eventName, function() {
if (fullscreenAPI.isFullscreen()) {
fullScreenButton.firstChild.className =
"esri-icon-zoom-in-fixed";
}
else {
fullScreenButton.firstChild.className =
"esri-icon-zoom-out-fixed";
}
});
view.ui.add(fullScreenButton, "top-right");
}
/**
* Creates a tooltip to display a the construction year of a building.
*/
function createTooltip() {
var tooltip = document.createElement("div");
var style = tooltip.style;
tooltip.setAttribute("role", "tooltip");
tooltip.classList.add("tooltip");
var textElement = document.createElement("div");
textElement.classList.add("esri-widget");
tooltip.appendChild(textElement);
view.container.appendChild(tooltip);
var x = 0;
var y = 0;
var targetX = 0;
var targetY = 0;
var visible = false;
// move the tooltip progressively
function move() {
x += (targetX - x) * 0.1;
y += (targetY - y) * 0.1;
if (Math.abs(targetX - x) < 1 && Math.abs(targetY - y) < 1) {
x = targetX;
y = targetY;
}
else {
requestAnimationFrame(move);
}
style.transform = "translate3d(" + Math.round(x) + "px," + Math.round(
y - view.height) + "px, 0)";
}
return {
show: function(point, text) {
if (!visible) {
x = point.x;
y = point.y;
}
targetX = point.x;
targetY = point.y;
style.opacity = 1;
visible = true;
textElement.innerHTML = text;
move();
},
hide: function() {
style.opacity = 0;
visible = false;
}
}
}
});
</script>
</head>
<body>
<div id="applicationDiv">
<div id="viewDiv">
<div id="titleDiv" class="esri-widget">Evolution de l'année de construction des bâtiments de Paris entre 1600 et 2017</div>
<div id="fullScreenButton" class="esri-widget esri-widget-button">
<span class="esri-icon-zoom-out-fixed" aria-label="fullscreen icon"></span>
</div>
</div>
<div id="sliderContainer" class="esri-widget">
<span id="sliderValue"></span>
<div id="sliderInnerContainer">
<div id="sliderLabels">
<span>1600</span>
<span>2017</span>
</div>
<div id="rangeWrapper">
<input id="slider" type="range" min="1600" max="2017" step="1" value="1789" />
</div>
</div>
<div id="playButton" class="esri-widget esri-widget-button toggle-button">
<div><span class="toggle-button-icon esri-icon-play" aria-label="play icon"></span> Play</div>
<div><span class="toggle-button-icon esri-icon-pause" aria-label="pause icon"></span> Pause</div>
</div>
</div>
</div>
</body>
</html>