@@ -0,0 +1 @@
|
||||
# speckleCon2024_GIS_workshop
|
||||
@@ -1,58 +1,58 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>SpeckleCon2024 GIS demo</title>
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css"/>
|
||||
<script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet.js"></script>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="mapContainer" style="min-height: 80vh;"></div>
|
||||
|
||||
<script>
|
||||
|
||||
// Add a map object
|
||||
var map = L.map('mapContainer').setView([ 45 , -75 ], 5);
|
||||
|
||||
// Add basemap layer
|
||||
map.addLayer(new L.TileLayer(
|
||||
'https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 22,
|
||||
attribution: '© <a href="https://openstreetmap.org/copyright">OpenStreetMap contributors</a> © Data: <a href="https://speckle.systems/">Speckle Systems</a>'
|
||||
}
|
||||
));
|
||||
|
||||
(async () => {
|
||||
var speckleUrl = 'https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/344f803f81/models/5582ab673e&datatype=projectcomments'
|
||||
const speckle_data = await fetch(speckleUrl, {
|
||||
headers: {
|
||||
'Accept': 'application/geo+json'
|
||||
}
|
||||
}).then(response => response.json());
|
||||
console.log(speckle_data)
|
||||
|
||||
speckle_layer = L.geoJSON(speckle_data, {
|
||||
filter: (feature) => {
|
||||
return feature.displayProperties["object_type"] == "comment"
|
||||
},
|
||||
pointToLayer: (feature, latlng) => {
|
||||
return new L.marker(latlng)
|
||||
},
|
||||
onEachFeature: function (feature, layer) {
|
||||
var html = '<span><td><p>' + feature['properties']['text_html'] + '</p></td> </span>';
|
||||
layer.bindPopup(html);
|
||||
}
|
||||
});
|
||||
speckle_layer.addTo(map);
|
||||
map.fitBounds(speckle_layer.getBounds())
|
||||
})();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>SpeckleCon2024 GIS demo</title>
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css"/>
|
||||
<script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet.js"></script>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="mapContainer" style="min-height: 80vh;"></div>
|
||||
|
||||
<script>
|
||||
|
||||
// Add a map object
|
||||
var map = L.map('mapContainer').setView([ 45 , -75 ], 5);
|
||||
|
||||
// Add basemap layer
|
||||
map.addLayer(new L.TileLayer(
|
||||
'https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 22,
|
||||
attribution: '© <a href="https://openstreetmap.org/copyright">OpenStreetMap contributors</a> © Data: <a href="https://speckle.systems/">Speckle Systems</a>'
|
||||
}
|
||||
));
|
||||
|
||||
(async () => {
|
||||
var speckleUrl = 'https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/344f803f81/models/5582ab673e&datatype=projectcomments'
|
||||
const speckle_data = await fetch(speckleUrl, {
|
||||
headers: {
|
||||
'Accept': 'application/geo+json'
|
||||
}
|
||||
}).then(response => response.json());
|
||||
console.log(speckle_data)
|
||||
|
||||
speckle_layer = L.geoJSON(speckle_data, {
|
||||
filter: (feature) => {
|
||||
return feature.displayProperties["object_type"] == "comment"
|
||||
},
|
||||
pointToLayer: (feature, latlng) => {
|
||||
return new L.marker(latlng)
|
||||
},
|
||||
onEachFeature: function (feature, layer) {
|
||||
var html = '<span><td><p>' + feature['properties']['text_html'] + '</p></td> </span>';
|
||||
layer.bindPopup(html);
|
||||
}
|
||||
});
|
||||
speckle_layer.addTo(map);
|
||||
map.fitBounds(speckle_layer.getBounds())
|
||||
})();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,25 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>SpeckleCon2024 GIS demo</title>
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
(async () => {
|
||||
// define Speckle Models' URLs
|
||||
var speckle_url = 'https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/b973c777fe/models/12e10a7220&datatype=lines&preserveattributes=true';
|
||||
|
||||
// fetch Speckle data
|
||||
const speckle_data = await fetch(speckle_url, { headers: {'Accept': 'application/geo+json'}}).then(response => response.json());
|
||||
|
||||
// print received data to console
|
||||
console.log(speckle_data);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>SpeckleCon2024 GIS demo</title>
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
(async () => {
|
||||
// define Speckle Models' URLs
|
||||
var speckle_url = 'https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/b973c777fe/models/12e10a7220&datatype=lines&preserveattributes=true';
|
||||
|
||||
// fetch Speckle data
|
||||
const speckle_data = await fetch(speckle_url, { headers: {'Accept': 'application/geo+json'}}).then(response => response.json());
|
||||
|
||||
// print received data to console
|
||||
console.log(speckle_data);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+31
-31
@@ -1,32 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>SpeckleCon2024 GIS demo</title>
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css"/>
|
||||
<script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet.js"></script>
|
||||
|
||||
<body>
|
||||
<div id="mapContainer" style="min-height: 80vh;"></div>
|
||||
|
||||
<script>
|
||||
|
||||
// Add a map object
|
||||
var map = L.map('mapContainer').setView([ 45 , -75 ], 5);
|
||||
|
||||
// Add basemap layer
|
||||
map.addLayer(new L.TileLayer(
|
||||
'https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 22,
|
||||
attribution: '© <a href="https://openstreetmap.org/copyright">OpenStreetMap contributors</a> © Data: <a href="https://speckle.systems/">Speckle Systems</a>'
|
||||
}
|
||||
));
|
||||
|
||||
</script>
|
||||
</body>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>SpeckleCon2024 GIS demo</title>
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css"/>
|
||||
<script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet.js"></script>
|
||||
|
||||
<body>
|
||||
<div id="mapContainer" style="min-height: 80vh;"></div>
|
||||
|
||||
<script>
|
||||
|
||||
// Add a map object
|
||||
var map = L.map('mapContainer').setView([ 45 , -75 ], 5);
|
||||
|
||||
// Add basemap layer
|
||||
map.addLayer(new L.TileLayer(
|
||||
'https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 22,
|
||||
attribution: '© <a href="https://openstreetmap.org/copyright">OpenStreetMap contributors</a> © Data: <a href="https://speckle.systems/">Speckle Systems</a>'
|
||||
}
|
||||
));
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,33 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>SpeckleCon2024 GIS demo</title>
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
(async () => {
|
||||
// provide your API key to HERE API
|
||||
const TRAFFIC_API_KEY = "41_ea6vX0sH8ec-wbRIEzUKt9tCUbW4rXDUeObNKoUc" // disposable key, replace with yours
|
||||
|
||||
// specify the traffic query extent
|
||||
center_x = 26.716031460406192
|
||||
center_y = 58.37359442585144
|
||||
buffer = 0.002 // degrees, approx 200 meters
|
||||
range_x = range_y = 2 * buffer
|
||||
|
||||
var bbox = `${center_x-range_x/2},${center_y-range_y/2},${center_x+range_x/2},${center_y+range_y/2}`
|
||||
|
||||
var traffic_url = `https://data.traffic.hereapi.com/v7/flow?locationReferencing=shape&in=bbox:${bbox}&apiKey=${TRAFFIC_API_KEY}`
|
||||
var traffic_data = await fetch(traffic_url).then(response => response.json());
|
||||
results = traffic_data.results
|
||||
|
||||
console.log(results)
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>SpeckleCon2024 GIS demo</title>
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
(async () => {
|
||||
// provide your API key to HERE API
|
||||
const TRAFFIC_API_KEY = "41_ea6vX0sH8ec-wbRIEzUKt9tCUbW4rXDUeObNKoUc" // disposable key, replace with yours
|
||||
|
||||
// specify the traffic query extent
|
||||
center_x = 26.716031460406192
|
||||
center_y = 58.37359442585144
|
||||
buffer = 0.002 // degrees, approx 200 meters
|
||||
range_x = range_y = 2 * buffer
|
||||
|
||||
var bbox = `${center_x-range_x/2},${center_y-range_y/2},${center_x+range_x/2},${center_y+range_y/2}`
|
||||
|
||||
var traffic_url = `https://data.traffic.hereapi.com/v7/flow?locationReferencing=shape&in=bbox:${bbox}&apiKey=${TRAFFIC_API_KEY}`
|
||||
var traffic_data = await fetch(traffic_url).then(response => response.json());
|
||||
results = traffic_data.results
|
||||
|
||||
console.log(results)
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,66 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>SpeckleCon2024 GIS demo</title>
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
|
||||
<body>
|
||||
|
||||
<div id="map" class="container"></div>
|
||||
<script>
|
||||
|
||||
function createTileLayer()
|
||||
{
|
||||
var tileLayer = new deck.TileLayer({
|
||||
id: 'TileLayer',
|
||||
data: 'https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png',
|
||||
attribution: '© <a href="http://osm.org">OpenStreetMap</a> contributors, © <a href="https://carto.com/">CARTO</a>',
|
||||
maxZoom: 19,
|
||||
minZoom: 0,
|
||||
|
||||
renderSubLayers: props => {
|
||||
const {boundingBox} = props.tile;
|
||||
|
||||
return new deck.BitmapLayer(props, {
|
||||
data: null,
|
||||
image: props.data,
|
||||
bounds: [boundingBox[0][0], boundingBox[0][1], boundingBox[1][0], boundingBox[1][1]]
|
||||
});
|
||||
}
|
||||
})
|
||||
return tileLayer;
|
||||
}
|
||||
|
||||
function createDeck(){
|
||||
|
||||
var state = {
|
||||
longitude: 26.716031460406192,
|
||||
latitude: 58.37359442585144,
|
||||
zoom: 20,
|
||||
maxZoom: 22,
|
||||
minZoom: 12,
|
||||
pitch: 60,
|
||||
bearing: 1.469387755102039
|
||||
}
|
||||
|
||||
var newDeckgl = new deck.DeckGL({
|
||||
container: 'map',
|
||||
initialViewState: state,
|
||||
controller: true,
|
||||
layers: []
|
||||
});
|
||||
|
||||
return newDeckgl;
|
||||
}
|
||||
|
||||
const deckgl = createDeck()
|
||||
var tile_layer = createTileLayer()
|
||||
deckgl.setProps({layers: [tile_layer]})
|
||||
|
||||
</script>
|
||||
</body>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>SpeckleCon2024 GIS demo</title>
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
|
||||
<body>
|
||||
|
||||
<div id="map" class="container"></div>
|
||||
<script>
|
||||
|
||||
function createTileLayer()
|
||||
{
|
||||
var tileLayer = new deck.TileLayer({
|
||||
id: 'TileLayer',
|
||||
data: 'https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png',
|
||||
attribution: '© <a href="http://osm.org">OpenStreetMap</a> contributors, © <a href="https://carto.com/">CARTO</a>',
|
||||
maxZoom: 19,
|
||||
minZoom: 0,
|
||||
|
||||
renderSubLayers: props => {
|
||||
const {boundingBox} = props.tile;
|
||||
|
||||
return new deck.BitmapLayer(props, {
|
||||
data: null,
|
||||
image: props.data,
|
||||
bounds: [boundingBox[0][0], boundingBox[0][1], boundingBox[1][0], boundingBox[1][1]]
|
||||
});
|
||||
}
|
||||
})
|
||||
return tileLayer;
|
||||
}
|
||||
|
||||
function createDeck(){
|
||||
|
||||
var state = {
|
||||
longitude: 26.716031460406192,
|
||||
latitude: 58.37359442585144,
|
||||
zoom: 20,
|
||||
maxZoom: 22,
|
||||
minZoom: 12,
|
||||
pitch: 60,
|
||||
bearing: 1.469387755102039
|
||||
}
|
||||
|
||||
var newDeckgl = new deck.DeckGL({
|
||||
container: 'map',
|
||||
initialViewState: state,
|
||||
controller: true,
|
||||
layers: []
|
||||
});
|
||||
|
||||
return newDeckgl;
|
||||
}
|
||||
|
||||
const deckgl = createDeck()
|
||||
var tile_layer = createTileLayer()
|
||||
deckgl.setProps({layers: [tile_layer]})
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+220
-220
@@ -1,221 +1,221 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>SpeckleCon2024 GIS demo</title>
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
|
||||
<body>
|
||||
|
||||
<div id="map" class="container"></div>
|
||||
<script>
|
||||
|
||||
function rgbaToArgbList(color) {
|
||||
col = color.replace('rgba(','').replace(')','').split(',',4);
|
||||
return [ parseInt(col[0]), parseInt(col[1]), parseInt(col[2]), parseInt(col[3]) ];
|
||||
}
|
||||
|
||||
function createTileLayer()
|
||||
{
|
||||
var tileLayer = new deck.TileLayer({
|
||||
id: 'TileLayer',
|
||||
data: 'https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png',
|
||||
attribution: '© <a href="http://osm.org">OpenStreetMap</a> contributors, © <a href="https://carto.com/">CARTO</a>',
|
||||
maxZoom: 19,
|
||||
minZoom: 0,
|
||||
|
||||
renderSubLayers: props => {
|
||||
const {boundingBox} = props.tile;
|
||||
|
||||
return new deck.BitmapLayer(props, {
|
||||
data: null,
|
||||
image: props.data,
|
||||
bounds: [boundingBox[0][0], boundingBox[0][1], boundingBox[1][0], boundingBox[1][1]]
|
||||
});
|
||||
}
|
||||
})
|
||||
return tileLayer;
|
||||
}
|
||||
|
||||
function createDeckWithExtent(extent){
|
||||
|
||||
var state= {
|
||||
longitude: extent[0] + (extent[2]-extent[0])/2,
|
||||
latitude: extent[1] + (extent[3]-extent[1])/2,
|
||||
zoom: 20,
|
||||
maxZoom: 22,
|
||||
minZoom: 12,
|
||||
pitch: 60,
|
||||
bearing: 1.469387755102039
|
||||
}
|
||||
|
||||
var newDeckgl = new deck.DeckGL({
|
||||
container: 'map',
|
||||
initialViewState: state,
|
||||
controller: true,
|
||||
layers: []
|
||||
});
|
||||
|
||||
return newDeckgl;
|
||||
}
|
||||
|
||||
function fixVerticalPolygons(speckle_data){
|
||||
var speckle_features = []
|
||||
for (let i = 0; i < speckle_data.features.length; i++) {
|
||||
|
||||
feature = speckle_data.features[i];
|
||||
coords = feature.geometry.coordinates;
|
||||
|
||||
if (!(feature.geometry.type.includes("Polygon"))){
|
||||
speckle_features.push({"id": speckle_features.length, "type":"Feature",
|
||||
"geometry": {"type": feature.geometry.type, "coordinates":feature.geometry.coordinates},
|
||||
"properties": feature.properties,
|
||||
"displayProperties": feature.displayProperties });
|
||||
}
|
||||
else {
|
||||
polygon_all_parts = []
|
||||
|
||||
// iterate through Polygon Parts
|
||||
for (let p = 0; p < coords.length; p++) {
|
||||
|
||||
// check orientation of each PolygonPart, if vertical - shift points slightly
|
||||
polygon_part = [];
|
||||
inner = false;
|
||||
|
||||
for (let c = 0; c < coords[p].length; c++) {
|
||||
polygon_part_loop = [];
|
||||
if (c>0){
|
||||
inner = true;
|
||||
}
|
||||
|
||||
sum_orientation = 0;
|
||||
polygon_pts = coords[p][c]; // usually 3 for Mesh faces
|
||||
for (let k = 0; k < polygon_pts.length; k++){
|
||||
index = k + 1
|
||||
if (k == polygon_pts.length - 1){index = 0}
|
||||
pt = polygon_pts[k]
|
||||
pt2 = polygon_pts[index]
|
||||
sum_orientation += (pt2[0] - pt[0]) * (pt2[1] + pt[1])
|
||||
};
|
||||
|
||||
createdPolygon = false;
|
||||
|
||||
if (-0.000000001 < sum_orientation && sum_orientation <0.000000001){
|
||||
|
||||
coords[p][c][0][0] += 0.0000001;
|
||||
coords[p][c][0][1] += 0.0000001;
|
||||
|
||||
coords[p][c][1][0] -= 0.0000001;
|
||||
coords[p][c][1][1] -= 0.0000001;
|
||||
|
||||
coords[p][c][2][0] += 0.0000001;
|
||||
coords[p][c][2][1] += 0.0000001;
|
||||
|
||||
if(polygon_pts.length==3) {
|
||||
createdPolygon = true;
|
||||
|
||||
multipolygon_coords = coords[p][c];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
|
||||
}
|
||||
else if (polygon_pts.length==4) {
|
||||
createdPolygon = true;
|
||||
|
||||
multipolygon_coords = coords[p][c].slice(0,3);
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
|
||||
/////////
|
||||
multipolygon_coords = [coords[p][c][2], coords[p][c][3], coords[p][c][0]];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
};
|
||||
|
||||
};
|
||||
if (createdPolygon == false){ // if non-vertical, or vertical with more than 4 vertices
|
||||
|
||||
multipolygon_coords = coords[p][c];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
if (inner == false){
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
}
|
||||
else{
|
||||
polygon_all_parts[polygon_all_parts.length-1].push(polygon_part_loop);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
new_polygon = {"id": speckle_features.length, "type":"Feature",
|
||||
"geometry": {"type": "MultiPolygon", "coordinates":polygon_all_parts},
|
||||
"properties": speckle_data.features[i].properties,
|
||||
"displayProperties": speckle_data.features[i].displayProperties };
|
||||
|
||||
new_polygon.displayProperties.lineWidth = 0.05
|
||||
speckle_features.push(new_polygon);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
speckle_data.features = speckle_features
|
||||
}
|
||||
|
||||
function createSpeckleLayer(speckle_data) {
|
||||
|
||||
// Create layer
|
||||
speckle_layer = new deck.GeoJsonLayer({
|
||||
id: `speckle_data`,
|
||||
data: speckle_data,
|
||||
|
||||
// Styles
|
||||
filled: true,
|
||||
opacity: 0.8,
|
||||
getFillColor: f => rgbaToArgbList(f.displayProperties.color),
|
||||
getLineWidth: f => f.displayProperties.lineWidth,
|
||||
getPointRadius: f => f.displayProperties.radius / 2,
|
||||
})
|
||||
|
||||
return speckle_layer;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
// define Speckle Models' URLs
|
||||
var speckle_url = 'https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/b973c777fe/models/49aff22443&limit=1000000&datatype=polygons&lon=26.7155164&lat=58.3739412&preserveattributes=true';
|
||||
|
||||
// fetch Speckle data
|
||||
const speckle_data = await fetch(speckle_url, { headers: {'Accept': 'application/geo+json'}}).then(response => response.json());
|
||||
|
||||
// print received data to console
|
||||
console.log(speckle_data);
|
||||
|
||||
// fix rendering of vertical polygons
|
||||
fixVerticalPolygons(speckle_data)
|
||||
|
||||
// get spatial extent of Speckle data
|
||||
var extent = speckle_data["extent"]
|
||||
|
||||
// Create deck.gl map
|
||||
const deckgl = createDeckWithExtent(extent)
|
||||
var tile_layer = createTileLayer()
|
||||
var speckle_layer = createSpeckleLayer(speckle_data)
|
||||
deckgl.setProps({layers: [tile_layer, speckle_layer]})
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>SpeckleCon2024 GIS demo</title>
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
|
||||
<body>
|
||||
|
||||
<div id="map" class="container"></div>
|
||||
<script>
|
||||
|
||||
function rgbaToArgbList(color) {
|
||||
col = color.replace('rgba(','').replace(')','').split(',',4);
|
||||
return [ parseInt(col[0]), parseInt(col[1]), parseInt(col[2]), parseInt(col[3]) ];
|
||||
}
|
||||
|
||||
function createTileLayer()
|
||||
{
|
||||
var tileLayer = new deck.TileLayer({
|
||||
id: 'TileLayer',
|
||||
data: 'https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png',
|
||||
attribution: '© <a href="http://osm.org">OpenStreetMap</a> contributors, © <a href="https://carto.com/">CARTO</a>',
|
||||
maxZoom: 19,
|
||||
minZoom: 0,
|
||||
|
||||
renderSubLayers: props => {
|
||||
const {boundingBox} = props.tile;
|
||||
|
||||
return new deck.BitmapLayer(props, {
|
||||
data: null,
|
||||
image: props.data,
|
||||
bounds: [boundingBox[0][0], boundingBox[0][1], boundingBox[1][0], boundingBox[1][1]]
|
||||
});
|
||||
}
|
||||
})
|
||||
return tileLayer;
|
||||
}
|
||||
|
||||
function createDeckWithExtent(extent){
|
||||
|
||||
var state= {
|
||||
longitude: extent[0] + (extent[2]-extent[0])/2,
|
||||
latitude: extent[1] + (extent[3]-extent[1])/2,
|
||||
zoom: 20,
|
||||
maxZoom: 22,
|
||||
minZoom: 12,
|
||||
pitch: 60,
|
||||
bearing: 1.469387755102039
|
||||
}
|
||||
|
||||
var newDeckgl = new deck.DeckGL({
|
||||
container: 'map',
|
||||
initialViewState: state,
|
||||
controller: true,
|
||||
layers: []
|
||||
});
|
||||
|
||||
return newDeckgl;
|
||||
}
|
||||
|
||||
function fixVerticalPolygons(speckle_data){
|
||||
var speckle_features = []
|
||||
for (let i = 0; i < speckle_data.features.length; i++) {
|
||||
|
||||
feature = speckle_data.features[i];
|
||||
coords = feature.geometry.coordinates;
|
||||
|
||||
if (!(feature.geometry.type.includes("Polygon"))){
|
||||
speckle_features.push({"id": speckle_features.length, "type":"Feature",
|
||||
"geometry": {"type": feature.geometry.type, "coordinates":feature.geometry.coordinates},
|
||||
"properties": feature.properties,
|
||||
"displayProperties": feature.displayProperties });
|
||||
}
|
||||
else {
|
||||
polygon_all_parts = []
|
||||
|
||||
// iterate through Polygon Parts
|
||||
for (let p = 0; p < coords.length; p++) {
|
||||
|
||||
// check orientation of each PolygonPart, if vertical - shift points slightly
|
||||
polygon_part = [];
|
||||
inner = false;
|
||||
|
||||
for (let c = 0; c < coords[p].length; c++) {
|
||||
polygon_part_loop = [];
|
||||
if (c>0){
|
||||
inner = true;
|
||||
}
|
||||
|
||||
sum_orientation = 0;
|
||||
polygon_pts = coords[p][c]; // usually 3 for Mesh faces
|
||||
for (let k = 0; k < polygon_pts.length; k++){
|
||||
index = k + 1
|
||||
if (k == polygon_pts.length - 1){index = 0}
|
||||
pt = polygon_pts[k]
|
||||
pt2 = polygon_pts[index]
|
||||
sum_orientation += (pt2[0] - pt[0]) * (pt2[1] + pt[1])
|
||||
};
|
||||
|
||||
createdPolygon = false;
|
||||
|
||||
if (-0.000000001 < sum_orientation && sum_orientation <0.000000001){
|
||||
|
||||
coords[p][c][0][0] += 0.0000001;
|
||||
coords[p][c][0][1] += 0.0000001;
|
||||
|
||||
coords[p][c][1][0] -= 0.0000001;
|
||||
coords[p][c][1][1] -= 0.0000001;
|
||||
|
||||
coords[p][c][2][0] += 0.0000001;
|
||||
coords[p][c][2][1] += 0.0000001;
|
||||
|
||||
if(polygon_pts.length==3) {
|
||||
createdPolygon = true;
|
||||
|
||||
multipolygon_coords = coords[p][c];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
|
||||
}
|
||||
else if (polygon_pts.length==4) {
|
||||
createdPolygon = true;
|
||||
|
||||
multipolygon_coords = coords[p][c].slice(0,3);
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
|
||||
/////////
|
||||
multipolygon_coords = [coords[p][c][2], coords[p][c][3], coords[p][c][0]];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
};
|
||||
|
||||
};
|
||||
if (createdPolygon == false){ // if non-vertical, or vertical with more than 4 vertices
|
||||
|
||||
multipolygon_coords = coords[p][c];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
if (inner == false){
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
}
|
||||
else{
|
||||
polygon_all_parts[polygon_all_parts.length-1].push(polygon_part_loop);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
new_polygon = {"id": speckle_features.length, "type":"Feature",
|
||||
"geometry": {"type": "MultiPolygon", "coordinates":polygon_all_parts},
|
||||
"properties": speckle_data.features[i].properties,
|
||||
"displayProperties": speckle_data.features[i].displayProperties };
|
||||
|
||||
new_polygon.displayProperties.lineWidth = 0.05
|
||||
speckle_features.push(new_polygon);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
speckle_data.features = speckle_features
|
||||
}
|
||||
|
||||
function createSpeckleLayer(speckle_data) {
|
||||
|
||||
// Create layer
|
||||
speckle_layer = new deck.GeoJsonLayer({
|
||||
id: `speckle_data`,
|
||||
data: speckle_data,
|
||||
|
||||
// Styles
|
||||
filled: true,
|
||||
opacity: 0.8,
|
||||
getFillColor: f => rgbaToArgbList(f.displayProperties.color),
|
||||
getLineWidth: f => f.displayProperties.lineWidth,
|
||||
getPointRadius: f => f.displayProperties.radius / 2,
|
||||
})
|
||||
|
||||
return speckle_layer;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
// define Speckle Models' URLs
|
||||
var speckle_url = 'https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/b973c777fe/models/49aff22443&limit=1000000&datatype=polygons&lon=26.7155164&lat=58.3739412&preserveattributes=true';
|
||||
|
||||
// fetch Speckle data
|
||||
const speckle_data = await fetch(speckle_url, { headers: {'Accept': 'application/geo+json'}}).then(response => response.json());
|
||||
|
||||
// print received data to console
|
||||
console.log(speckle_data);
|
||||
|
||||
// fix rendering of vertical polygons
|
||||
fixVerticalPolygons(speckle_data)
|
||||
|
||||
// get spatial extent of Speckle data
|
||||
var extent = speckle_data["extent"]
|
||||
|
||||
// Create deck.gl map
|
||||
const deckgl = createDeckWithExtent(extent)
|
||||
var tile_layer = createTileLayer()
|
||||
var speckle_layer = createSpeckleLayer(speckle_data)
|
||||
deckgl.setProps({layers: [tile_layer, speckle_layer]})
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,321 +1,321 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>SpeckleCon2024 GIS demo</title>
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
|
||||
<body>
|
||||
|
||||
<div id="map" class="container"></div>
|
||||
<script>
|
||||
|
||||
function rgbaToArgbList(color) {
|
||||
col = color.replace('rgba(','').replace(')','').split(',',4);
|
||||
return [ parseInt(col[0]), parseInt(col[1]), parseInt(col[2]), parseInt(col[3]) ];
|
||||
}
|
||||
|
||||
function createTileLayer()
|
||||
{
|
||||
var tileLayer = new deck.TileLayer({
|
||||
id: 'TileLayer',
|
||||
data: 'https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png',
|
||||
attribution: '© <a href="http://osm.org">OpenStreetMap</a> contributors, © <a href="https://carto.com/">CARTO</a>',
|
||||
maxZoom: 19,
|
||||
minZoom: 0,
|
||||
|
||||
renderSubLayers: props => {
|
||||
const {boundingBox} = props.tile;
|
||||
|
||||
return new deck.BitmapLayer(props, {
|
||||
data: null,
|
||||
image: props.data,
|
||||
bounds: [boundingBox[0][0], boundingBox[0][1], boundingBox[1][0], boundingBox[1][1]]
|
||||
});
|
||||
}
|
||||
})
|
||||
return tileLayer;
|
||||
}
|
||||
|
||||
function createDeckWithExtent(extent){
|
||||
|
||||
var state= {
|
||||
longitude: extent[0] + (extent[2]-extent[0])/2,
|
||||
latitude: extent[1] + (extent[3]-extent[1])/2,
|
||||
zoom: 18,
|
||||
maxZoom: 22,
|
||||
minZoom: 12,
|
||||
pitch: 60,
|
||||
bearing: 1.469387755102039
|
||||
}
|
||||
|
||||
var newDeckgl = new deck.DeckGL({
|
||||
container: 'map',
|
||||
initialViewState: state,
|
||||
controller: true,
|
||||
layers: []
|
||||
});
|
||||
|
||||
return newDeckgl;
|
||||
}
|
||||
|
||||
function fixVerticalPolygons(speckle_data){
|
||||
var speckle_features = []
|
||||
for (let i = 0; i < speckle_data.features.length; i++) {
|
||||
|
||||
feature = speckle_data.features[i];
|
||||
coords = feature.geometry.coordinates;
|
||||
|
||||
if (!(feature.geometry.type.includes("Polygon"))){
|
||||
speckle_features.push({"id": speckle_features.length, "type":"Feature",
|
||||
"geometry": {"type": feature.geometry.type, "coordinates":feature.geometry.coordinates},
|
||||
"properties": feature.properties,
|
||||
"displayProperties": feature.displayProperties });
|
||||
}
|
||||
else {
|
||||
polygon_all_parts = []
|
||||
|
||||
// iterate through Polygon Parts
|
||||
for (let p = 0; p < coords.length; p++) {
|
||||
|
||||
// check orientation of each PolygonPart, if vertical - shift points slightly
|
||||
polygon_part = [];
|
||||
inner = false;
|
||||
|
||||
for (let c = 0; c < coords[p].length; c++) {
|
||||
polygon_part_loop = [];
|
||||
if (c>0){
|
||||
inner = true;
|
||||
}
|
||||
|
||||
sum_orientation = 0;
|
||||
polygon_pts = coords[p][c]; // usually 3 for Mesh faces
|
||||
for (let k = 0; k < polygon_pts.length; k++){
|
||||
index = k + 1
|
||||
if (k == polygon_pts.length - 1){index = 0}
|
||||
pt = polygon_pts[k]
|
||||
pt2 = polygon_pts[index]
|
||||
sum_orientation += (pt2[0] - pt[0]) * (pt2[1] + pt[1])
|
||||
};
|
||||
|
||||
createdPolygon = false;
|
||||
|
||||
if (-0.000000001 < sum_orientation && sum_orientation <0.000000001){
|
||||
|
||||
coords[p][c][0][0] += 0.0000001;
|
||||
coords[p][c][0][1] += 0.0000001;
|
||||
|
||||
coords[p][c][1][0] -= 0.0000001;
|
||||
coords[p][c][1][1] -= 0.0000001;
|
||||
|
||||
coords[p][c][2][0] += 0.0000001;
|
||||
coords[p][c][2][1] += 0.0000001;
|
||||
|
||||
if(polygon_pts.length==3) {
|
||||
createdPolygon = true;
|
||||
|
||||
multipolygon_coords = coords[p][c];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
|
||||
}
|
||||
else if (polygon_pts.length==4) {
|
||||
createdPolygon = true;
|
||||
|
||||
multipolygon_coords = coords[p][c].slice(0,3);
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
|
||||
/////////
|
||||
multipolygon_coords = [coords[p][c][2], coords[p][c][3], coords[p][c][0]];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
};
|
||||
|
||||
};
|
||||
if (createdPolygon == false){ // if non-vertical, or vertical with more than 4 vertices
|
||||
|
||||
multipolygon_coords = coords[p][c];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
if (inner == false){
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
}
|
||||
else{
|
||||
polygon_all_parts[polygon_all_parts.length-1].push(polygon_part_loop);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
new_polygon = {"id": speckle_features.length, "type":"Feature",
|
||||
"geometry": {"type": "MultiPolygon", "coordinates":polygon_all_parts},
|
||||
"properties": speckle_data.features[i].properties,
|
||||
"displayProperties": speckle_data.features[i].displayProperties };
|
||||
|
||||
new_polygon.displayProperties.lineWidth = 0.05
|
||||
speckle_features.push(new_polygon);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
speckle_data.features = speckle_features
|
||||
}
|
||||
|
||||
function createSpeckleLayer(speckle_data) {
|
||||
|
||||
// Create layer
|
||||
speckle_layer = new deck.GeoJsonLayer({
|
||||
id: `speckle_data`,
|
||||
data: speckle_data,
|
||||
|
||||
// Styles
|
||||
filled: true,
|
||||
opacity: 0.8,
|
||||
getFillColor: f => rgbaToArgbList(f.displayProperties.color),
|
||||
getLineWidth: f => f.displayProperties.lineWidth,
|
||||
getPointRadius: f => f.displayProperties.radius / 2,
|
||||
})
|
||||
|
||||
return speckle_layer;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
// define Speckle Models' URLs
|
||||
var speckle_url = 'https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/b973c777fe/models/49aff22443&limit=1000000&datatype=polygons&lon=26.7155164&lat=58.3739412&preserveattributes=true';
|
||||
|
||||
// fetch Speckle data
|
||||
const speckle_data = await fetch(speckle_url, { headers: {'Accept': 'application/geo+json'}}).then(response => response.json());
|
||||
|
||||
// print received data to console
|
||||
console.log(speckle_data);
|
||||
|
||||
// fix rendering of vertical polygons
|
||||
fixVerticalPolygons(speckle_data)
|
||||
|
||||
// get spatial extent of Speckle data
|
||||
var extent = speckle_data["extent"]
|
||||
|
||||
// Create deck.gl map
|
||||
const deckgl = createDeckWithExtent(extent)
|
||||
var tile_layer = createTileLayer()
|
||||
var speckle_layer = createSpeckleLayer(speckle_data)
|
||||
|
||||
all_layers = [tile_layer, speckle_layer]
|
||||
deckgl.setProps({layers: all_layers})
|
||||
|
||||
////////// add and animate traffic
|
||||
// provide your API key to HERE API
|
||||
const TRAFFIC_API_KEY = "41_ea6vX0sH8ec-wbRIEzUKt9tCUbW4rXDUeObNKoUc" // disposable key, replace with yours
|
||||
autoUpdateTrafficLayer(extent, all_layers)
|
||||
|
||||
async function getFeaturesFromTraffic(traffic_url)
|
||||
{
|
||||
traffic_features = []
|
||||
try{
|
||||
const data = await fetch(traffic_url).then(response => response.json());
|
||||
results = data.results
|
||||
|
||||
for (segm in results){
|
||||
var speed_factor = results[segm].currentFlow.speed / results[segm].currentFlow.freeFlow
|
||||
|
||||
links = results[segm].location.shape.links
|
||||
pts_coords = []
|
||||
for (link_ind in links){
|
||||
pts = links[link_ind].points
|
||||
for (pt_ind in pts){
|
||||
pt = pts[pt_ind]
|
||||
pts_coords.push([pt.lng, pt.lat, 0])
|
||||
}
|
||||
}
|
||||
lineColor = getTrafficColor(speed_factor);
|
||||
|
||||
new_line = {"type":"Feature",
|
||||
"geometry": {"type": "LineString", "coordinates": pts_coords},
|
||||
"displayProperties": {"color": lineColor } };
|
||||
traffic_features.push(new_line);
|
||||
}
|
||||
}
|
||||
catch {}
|
||||
return traffic_features
|
||||
}
|
||||
|
||||
function getTrafficColor(speed_factor){
|
||||
if (speed_factor>1){
|
||||
speed_factor=1
|
||||
}
|
||||
|
||||
speed_factor = (speed_factor - 0.5)*2 // remap for bigger visual difference
|
||||
if (speed_factor<0){
|
||||
speed_factor=0.1
|
||||
}
|
||||
|
||||
color = 'rgba(100,100,100,255)'
|
||||
green = Math.pow( (speed_factor ),0.5) *240
|
||||
blue = 0
|
||||
red = Math.pow( ((1 - speed_factor) ),0.5) *240
|
||||
|
||||
color = `rgba(${Math.round(red)},${Math.round(green)},${Math.round(blue)},255)`
|
||||
return color
|
||||
}
|
||||
|
||||
async function addTrafficLayer(extent, all_layers){
|
||||
console.log("__update traffic")
|
||||
|
||||
// specify the traffic query extend a bit larger than the model extent
|
||||
buffer = 0.004 // degrees, approx 400 meters
|
||||
range_x = extent[2] - extent[0] + 2* buffer
|
||||
range_y = extent[3] - extent[1] + 2* buffer
|
||||
center_x = extent[0] + (extent[2] - extent[0])/2
|
||||
center_y = extent[1] + (extent[3] - extent[1])/2
|
||||
|
||||
var bbox = `${center_x-range_x/2},${center_y-range_y/2},${center_x+range_x/2},${center_y+range_y/2}`
|
||||
var traffic_url = `https://data.traffic.hereapi.com/v7/flow?locationReferencing=shape&in=bbox:${bbox}&apiKey=${TRAFFIC_API_KEY}`
|
||||
|
||||
var traffic_features = await getFeaturesFromTraffic(traffic_url);
|
||||
traffic_layer = new deck.GeoJsonLayer({
|
||||
id: 'traffic_layer',
|
||||
data: traffic_features,
|
||||
getLineWidth: f => 6,
|
||||
getLineColor: f => rgbaToArgbList(f.displayProperties.color),
|
||||
})
|
||||
all_layers.splice(2, 1); // remove old traffic layer
|
||||
all_layers.push(traffic_layer);
|
||||
}
|
||||
|
||||
async function delay( ms, state = null ) {
|
||||
return new Promise( ( resolve, reject ) => {
|
||||
window.setTimeout( () => resolve( state ), ms );
|
||||
} );
|
||||
}
|
||||
|
||||
async function autoUpdateTrafficLayer(extent, all_layers) {
|
||||
try {
|
||||
while( true ) {
|
||||
await addTrafficLayer(extent, all_layers);
|
||||
deckgl.setProps({layers: all_layers});
|
||||
await delay( 60000 );
|
||||
}
|
||||
}
|
||||
catch( err ) {
|
||||
console.error( err );
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>SpeckleCon2024 GIS demo</title>
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
|
||||
<body>
|
||||
|
||||
<div id="map" class="container"></div>
|
||||
<script>
|
||||
|
||||
function rgbaToArgbList(color) {
|
||||
col = color.replace('rgba(','').replace(')','').split(',',4);
|
||||
return [ parseInt(col[0]), parseInt(col[1]), parseInt(col[2]), parseInt(col[3]) ];
|
||||
}
|
||||
|
||||
function createTileLayer()
|
||||
{
|
||||
var tileLayer = new deck.TileLayer({
|
||||
id: 'TileLayer',
|
||||
data: 'https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png',
|
||||
attribution: '© <a href="http://osm.org">OpenStreetMap</a> contributors, © <a href="https://carto.com/">CARTO</a>',
|
||||
maxZoom: 19,
|
||||
minZoom: 0,
|
||||
|
||||
renderSubLayers: props => {
|
||||
const {boundingBox} = props.tile;
|
||||
|
||||
return new deck.BitmapLayer(props, {
|
||||
data: null,
|
||||
image: props.data,
|
||||
bounds: [boundingBox[0][0], boundingBox[0][1], boundingBox[1][0], boundingBox[1][1]]
|
||||
});
|
||||
}
|
||||
})
|
||||
return tileLayer;
|
||||
}
|
||||
|
||||
function createDeckWithExtent(extent){
|
||||
|
||||
var state= {
|
||||
longitude: extent[0] + (extent[2]-extent[0])/2,
|
||||
latitude: extent[1] + (extent[3]-extent[1])/2,
|
||||
zoom: 18,
|
||||
maxZoom: 22,
|
||||
minZoom: 12,
|
||||
pitch: 60,
|
||||
bearing: 1.469387755102039
|
||||
}
|
||||
|
||||
var newDeckgl = new deck.DeckGL({
|
||||
container: 'map',
|
||||
initialViewState: state,
|
||||
controller: true,
|
||||
layers: []
|
||||
});
|
||||
|
||||
return newDeckgl;
|
||||
}
|
||||
|
||||
function fixVerticalPolygons(speckle_data){
|
||||
var speckle_features = []
|
||||
for (let i = 0; i < speckle_data.features.length; i++) {
|
||||
|
||||
feature = speckle_data.features[i];
|
||||
coords = feature.geometry.coordinates;
|
||||
|
||||
if (!(feature.geometry.type.includes("Polygon"))){
|
||||
speckle_features.push({"id": speckle_features.length, "type":"Feature",
|
||||
"geometry": {"type": feature.geometry.type, "coordinates":feature.geometry.coordinates},
|
||||
"properties": feature.properties,
|
||||
"displayProperties": feature.displayProperties });
|
||||
}
|
||||
else {
|
||||
polygon_all_parts = []
|
||||
|
||||
// iterate through Polygon Parts
|
||||
for (let p = 0; p < coords.length; p++) {
|
||||
|
||||
// check orientation of each PolygonPart, if vertical - shift points slightly
|
||||
polygon_part = [];
|
||||
inner = false;
|
||||
|
||||
for (let c = 0; c < coords[p].length; c++) {
|
||||
polygon_part_loop = [];
|
||||
if (c>0){
|
||||
inner = true;
|
||||
}
|
||||
|
||||
sum_orientation = 0;
|
||||
polygon_pts = coords[p][c]; // usually 3 for Mesh faces
|
||||
for (let k = 0; k < polygon_pts.length; k++){
|
||||
index = k + 1
|
||||
if (k == polygon_pts.length - 1){index = 0}
|
||||
pt = polygon_pts[k]
|
||||
pt2 = polygon_pts[index]
|
||||
sum_orientation += (pt2[0] - pt[0]) * (pt2[1] + pt[1])
|
||||
};
|
||||
|
||||
createdPolygon = false;
|
||||
|
||||
if (-0.000000001 < sum_orientation && sum_orientation <0.000000001){
|
||||
|
||||
coords[p][c][0][0] += 0.0000001;
|
||||
coords[p][c][0][1] += 0.0000001;
|
||||
|
||||
coords[p][c][1][0] -= 0.0000001;
|
||||
coords[p][c][1][1] -= 0.0000001;
|
||||
|
||||
coords[p][c][2][0] += 0.0000001;
|
||||
coords[p][c][2][1] += 0.0000001;
|
||||
|
||||
if(polygon_pts.length==3) {
|
||||
createdPolygon = true;
|
||||
|
||||
multipolygon_coords = coords[p][c];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
|
||||
}
|
||||
else if (polygon_pts.length==4) {
|
||||
createdPolygon = true;
|
||||
|
||||
multipolygon_coords = coords[p][c].slice(0,3);
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
|
||||
/////////
|
||||
multipolygon_coords = [coords[p][c][2], coords[p][c][3], coords[p][c][0]];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
};
|
||||
|
||||
};
|
||||
if (createdPolygon == false){ // if non-vertical, or vertical with more than 4 vertices
|
||||
|
||||
multipolygon_coords = coords[p][c];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
if (inner == false){
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
}
|
||||
else{
|
||||
polygon_all_parts[polygon_all_parts.length-1].push(polygon_part_loop);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
new_polygon = {"id": speckle_features.length, "type":"Feature",
|
||||
"geometry": {"type": "MultiPolygon", "coordinates":polygon_all_parts},
|
||||
"properties": speckle_data.features[i].properties,
|
||||
"displayProperties": speckle_data.features[i].displayProperties };
|
||||
|
||||
new_polygon.displayProperties.lineWidth = 0.05
|
||||
speckle_features.push(new_polygon);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
speckle_data.features = speckle_features
|
||||
}
|
||||
|
||||
function createSpeckleLayer(speckle_data) {
|
||||
|
||||
// Create layer
|
||||
speckle_layer = new deck.GeoJsonLayer({
|
||||
id: `speckle_data`,
|
||||
data: speckle_data,
|
||||
|
||||
// Styles
|
||||
filled: true,
|
||||
opacity: 0.8,
|
||||
getFillColor: f => rgbaToArgbList(f.displayProperties.color),
|
||||
getLineWidth: f => f.displayProperties.lineWidth,
|
||||
getPointRadius: f => f.displayProperties.radius / 2,
|
||||
})
|
||||
|
||||
return speckle_layer;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
// define Speckle Models' URLs
|
||||
var speckle_url = 'https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/b973c777fe/models/49aff22443&limit=1000000&datatype=polygons&lon=26.7155164&lat=58.3739412&preserveattributes=true';
|
||||
|
||||
// fetch Speckle data
|
||||
const speckle_data = await fetch(speckle_url, { headers: {'Accept': 'application/geo+json'}}).then(response => response.json());
|
||||
|
||||
// print received data to console
|
||||
console.log(speckle_data);
|
||||
|
||||
// fix rendering of vertical polygons
|
||||
fixVerticalPolygons(speckle_data)
|
||||
|
||||
// get spatial extent of Speckle data
|
||||
var extent = speckle_data["extent"]
|
||||
|
||||
// Create deck.gl map
|
||||
const deckgl = createDeckWithExtent(extent)
|
||||
var tile_layer = createTileLayer()
|
||||
var speckle_layer = createSpeckleLayer(speckle_data)
|
||||
|
||||
all_layers = [tile_layer, speckle_layer]
|
||||
deckgl.setProps({layers: all_layers})
|
||||
|
||||
////////// add and animate traffic
|
||||
// provide your API key to HERE API
|
||||
const TRAFFIC_API_KEY = "41_ea6vX0sH8ec-wbRIEzUKt9tCUbW4rXDUeObNKoUc" // disposable key, replace with yours
|
||||
autoUpdateTrafficLayer(extent, all_layers)
|
||||
|
||||
async function getFeaturesFromTraffic(traffic_url)
|
||||
{
|
||||
traffic_features = []
|
||||
try{
|
||||
const data = await fetch(traffic_url).then(response => response.json());
|
||||
results = data.results
|
||||
|
||||
for (segm in results){
|
||||
var speed_factor = results[segm].currentFlow.speed / results[segm].currentFlow.freeFlow
|
||||
|
||||
links = results[segm].location.shape.links
|
||||
pts_coords = []
|
||||
for (link_ind in links){
|
||||
pts = links[link_ind].points
|
||||
for (pt_ind in pts){
|
||||
pt = pts[pt_ind]
|
||||
pts_coords.push([pt.lng, pt.lat, 0])
|
||||
}
|
||||
}
|
||||
lineColor = getTrafficColor(speed_factor);
|
||||
|
||||
new_line = {"type":"Feature",
|
||||
"geometry": {"type": "LineString", "coordinates": pts_coords},
|
||||
"displayProperties": {"color": lineColor } };
|
||||
traffic_features.push(new_line);
|
||||
}
|
||||
}
|
||||
catch {}
|
||||
return traffic_features
|
||||
}
|
||||
|
||||
function getTrafficColor(speed_factor){
|
||||
if (speed_factor>1){
|
||||
speed_factor=1
|
||||
}
|
||||
|
||||
speed_factor = (speed_factor - 0.5)*2 // remap for bigger visual difference
|
||||
if (speed_factor<0){
|
||||
speed_factor=0.1
|
||||
}
|
||||
|
||||
color = 'rgba(100,100,100,255)'
|
||||
green = Math.pow( (speed_factor ),0.5) *240
|
||||
blue = 0
|
||||
red = Math.pow( ((1 - speed_factor) ),0.5) *240
|
||||
|
||||
color = `rgba(${Math.round(red)},${Math.round(green)},${Math.round(blue)},255)`
|
||||
return color
|
||||
}
|
||||
|
||||
async function addTrafficLayer(extent, all_layers){
|
||||
console.log("__update traffic")
|
||||
|
||||
// specify the traffic query extend a bit larger than the model extent
|
||||
buffer = 0.004 // degrees, approx 400 meters
|
||||
range_x = extent[2] - extent[0] + 2* buffer
|
||||
range_y = extent[3] - extent[1] + 2* buffer
|
||||
center_x = extent[0] + (extent[2] - extent[0])/2
|
||||
center_y = extent[1] + (extent[3] - extent[1])/2
|
||||
|
||||
var bbox = `${center_x-range_x/2},${center_y-range_y/2},${center_x+range_x/2},${center_y+range_y/2}`
|
||||
var traffic_url = `https://data.traffic.hereapi.com/v7/flow?locationReferencing=shape&in=bbox:${bbox}&apiKey=${TRAFFIC_API_KEY}`
|
||||
|
||||
var traffic_features = await getFeaturesFromTraffic(traffic_url);
|
||||
traffic_layer = new deck.GeoJsonLayer({
|
||||
id: 'traffic_layer',
|
||||
data: traffic_features,
|
||||
getLineWidth: f => 6,
|
||||
getLineColor: f => rgbaToArgbList(f.displayProperties.color),
|
||||
})
|
||||
all_layers.splice(2, 1); // remove old traffic layer
|
||||
all_layers.push(traffic_layer);
|
||||
}
|
||||
|
||||
async function delay( ms, state = null ) {
|
||||
return new Promise( ( resolve, reject ) => {
|
||||
window.setTimeout( () => resolve( state ), ms );
|
||||
} );
|
||||
}
|
||||
|
||||
async function autoUpdateTrafficLayer(extent, all_layers) {
|
||||
try {
|
||||
while( true ) {
|
||||
await addTrafficLayer(extent, all_layers);
|
||||
deckgl.setProps({layers: all_layers});
|
||||
await delay( 60000 );
|
||||
}
|
||||
}
|
||||
catch( err ) {
|
||||
console.error( err );
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
@@ -0,0 +1,10 @@
|
||||
# BILT 2025
|
||||
## Getting started:
|
||||
|
||||
1. Install QGIS desktop, version from 3.34.0 to 3.40.5: https://qgis.org/download/
|
||||
2. Create Speckle account at https://app.speckle.systems/
|
||||
3. Install the latest version of Speckle QGIS v3 connector (Windows only): https://releases.speckle.systems/v3
|
||||
|
||||
## Presentation and handouts
|
||||
|
||||
All materials are here: https://drive.google.com/drive/folders/163zk4vPxXp6ZYaWszvPvEdFv61VxCbFW?usp=drive_link
|
||||
@@ -0,0 +1,222 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>SpeckleCon2024 GIS demo</title>
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
|
||||
<body>
|
||||
|
||||
<div id="map" class="container"></div>
|
||||
<script>
|
||||
|
||||
function rgbaToArgbList(color) {
|
||||
col = color.replace('rgba(','').replace(')','').split(',',4);
|
||||
return [ parseInt(col[0]), parseInt(col[1]), parseInt(col[2]), parseInt(col[3]) ];
|
||||
}
|
||||
|
||||
function createTileLayer()
|
||||
{
|
||||
var tileLayer = new deck.TileLayer({
|
||||
id: 'TileLayer',
|
||||
data: 'https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png',
|
||||
attribution: '© <a href="http://osm.org">OpenStreetMap</a> contributors, © <a href="https://carto.com/">CARTO</a>',
|
||||
maxZoom: 19,
|
||||
minZoom: 0,
|
||||
|
||||
renderSubLayers: props => {
|
||||
const {boundingBox} = props.tile;
|
||||
|
||||
return new deck.BitmapLayer(props, {
|
||||
data: null,
|
||||
image: props.data,
|
||||
bounds: [boundingBox[0][0], boundingBox[0][1], boundingBox[1][0], boundingBox[1][1]]
|
||||
});
|
||||
}
|
||||
})
|
||||
return tileLayer;
|
||||
}
|
||||
|
||||
function createDeckWithExtent(extent){
|
||||
|
||||
var state= {
|
||||
longitude: extent[0] + (extent[2]-extent[0])/2,
|
||||
latitude: extent[1] + (extent[3]-extent[1])/2,
|
||||
zoom: 18,
|
||||
maxZoom: 22,
|
||||
minZoom: 12,
|
||||
pitch: 60,
|
||||
bearing: 1.469387755102039
|
||||
}
|
||||
|
||||
var newDeckgl = new deck.DeckGL({
|
||||
container: 'map',
|
||||
initialViewState: state,
|
||||
controller: true,
|
||||
layers: []
|
||||
});
|
||||
|
||||
return newDeckgl;
|
||||
}
|
||||
|
||||
function fixVerticalPolygons(speckle_data){
|
||||
var speckle_features = []
|
||||
for (let i = 0; i < speckle_data.features.length; i++) {
|
||||
|
||||
feature = speckle_data.features[i];
|
||||
coords = feature.geometry.coordinates;
|
||||
|
||||
if (!(feature.geometry.type.includes("Polygon"))){
|
||||
speckle_features.push({"id": speckle_features.length, "type":"Feature",
|
||||
"geometry": {"type": feature.geometry.type, "coordinates":feature.geometry.coordinates},
|
||||
"properties": feature.properties,
|
||||
"displayProperties": feature.displayProperties });
|
||||
}
|
||||
else {
|
||||
polygon_all_parts = []
|
||||
|
||||
// iterate through Polygon Parts
|
||||
for (let p = 0; p < coords.length; p++) {
|
||||
|
||||
// check orientation of each PolygonPart, if vertical - shift points slightly
|
||||
polygon_part = [];
|
||||
inner = false;
|
||||
|
||||
for (let c = 0; c < coords[p].length; c++) {
|
||||
polygon_part_loop = [];
|
||||
if (c>0){
|
||||
inner = true;
|
||||
}
|
||||
|
||||
sum_orientation = 0;
|
||||
polygon_pts = coords[p][c]; // usually 3 for Mesh faces
|
||||
for (let k = 0; k < polygon_pts.length; k++){
|
||||
index = k + 1
|
||||
if (k == polygon_pts.length - 1){index = 0}
|
||||
pt = polygon_pts[k]
|
||||
pt2 = polygon_pts[index]
|
||||
sum_orientation += (pt2[0] - pt[0]) * (pt2[1] + pt[1])
|
||||
};
|
||||
|
||||
createdPolygon = false;
|
||||
|
||||
if (-0.000000001 < sum_orientation && sum_orientation <0.000000001){
|
||||
|
||||
coords[p][c][0][0] += 0.0000001;
|
||||
coords[p][c][0][1] += 0.0000001;
|
||||
|
||||
coords[p][c][1][0] -= 0.0000001;
|
||||
coords[p][c][1][1] -= 0.0000001;
|
||||
|
||||
coords[p][c][2][0] += 0.0000001;
|
||||
coords[p][c][2][1] += 0.0000001;
|
||||
|
||||
if(polygon_pts.length==3) {
|
||||
createdPolygon = true;
|
||||
|
||||
multipolygon_coords = coords[p][c];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
|
||||
}
|
||||
else if (polygon_pts.length==4) {
|
||||
createdPolygon = true;
|
||||
|
||||
multipolygon_coords = coords[p][c].slice(0,3);
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
|
||||
/////////
|
||||
multipolygon_coords = [coords[p][c][2], coords[p][c][3], coords[p][c][0]];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
};
|
||||
|
||||
};
|
||||
if (createdPolygon == false){ // if non-vertical, or vertical with more than 4 vertices
|
||||
|
||||
multipolygon_coords = coords[p][c];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
if (inner == false){
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
}
|
||||
else{
|
||||
polygon_all_parts[polygon_all_parts.length-1].push(polygon_part_loop);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
new_polygon = {"id": speckle_features.length, "type":"Feature",
|
||||
"geometry": {"type": "MultiPolygon", "coordinates":polygon_all_parts},
|
||||
"properties": speckle_data.features[i].properties,
|
||||
"displayProperties": speckle_data.features[i].displayProperties };
|
||||
|
||||
new_polygon.displayProperties.lineWidth = 0.03
|
||||
speckle_features.push(new_polygon);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
speckle_data.features = speckle_features
|
||||
}
|
||||
|
||||
function createSpeckleLayer(speckle_data) {
|
||||
|
||||
// Create layer
|
||||
speckle_layer = new deck.GeoJsonLayer({
|
||||
id: `speckle_data`,
|
||||
data: speckle_data,
|
||||
|
||||
// Styles
|
||||
filled: true,
|
||||
opacity: 0.8,
|
||||
getFillColor: f => rgbaToArgbList(f.displayProperties.color),
|
||||
getLineWidth: f => f.displayProperties.lineWidth,
|
||||
getPointRadius: f => f.displayProperties.radius / 2,
|
||||
})
|
||||
|
||||
return speckle_layer;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
// define Speckle Models' URLs
|
||||
var cad_data = 'https://app.speckle.systems/projects/0c353a2132/models/d9639e9a47';
|
||||
var speckle_url = `https://geo.speckle.systems/speckle/?speckleUrl=${cad_data}&limit=1000000&datatype=polygons&lon=26.7155164&lat=58.3739412&preserveattributes=true`;
|
||||
|
||||
// fetch Speckle data
|
||||
const speckle_data = await fetch(speckle_url, { headers: {'Accept': 'application/geo+json'}}).then(response => response.json());
|
||||
|
||||
// print received data to console
|
||||
console.log(speckle_data);
|
||||
|
||||
// fix rendering of vertical polygons
|
||||
fixVerticalPolygons(speckle_data)
|
||||
|
||||
// get spatial extent of Speckle data
|
||||
var extent = speckle_data["extent"]
|
||||
|
||||
// Create deck.gl map
|
||||
const deckgl = createDeckWithExtent(extent)
|
||||
var tile_layer = createTileLayer()
|
||||
var speckle_layer = createSpeckleLayer(speckle_data)
|
||||
deckgl.setProps({layers: [tile_layer, speckle_layer]})
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,302 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>SpeckleCon2024 GIS demo</title>
|
||||
<style type="text/css">
|
||||
html,body{margin:0px;height:100%;width:100%}
|
||||
.container{width:100%;height:100%}
|
||||
</style>
|
||||
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
|
||||
<body>
|
||||
|
||||
<div id="map" class="container"></div>
|
||||
<script>
|
||||
|
||||
function rgbaToArgbList(color) {
|
||||
col = color.replace('rgba(','').replace(')','').split(',',4);
|
||||
return [ parseInt(col[0]), parseInt(col[1]), parseInt(col[2]), parseInt(col[3]) ];
|
||||
}
|
||||
|
||||
function createTileLayer()
|
||||
{
|
||||
var tileLayer = new deck.TileLayer({
|
||||
id: 'TileLayer',
|
||||
data: 'https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png',
|
||||
attribution: '© <a href="http://osm.org">OpenStreetMap</a> contributors, © <a href="https://carto.com/">CARTO</a>',
|
||||
maxZoom: 19,
|
||||
minZoom: 0,
|
||||
|
||||
renderSubLayers: props => {
|
||||
const {boundingBox} = props.tile;
|
||||
|
||||
return new deck.BitmapLayer(props, {
|
||||
data: null,
|
||||
image: props.data,
|
||||
bounds: [boundingBox[0][0], boundingBox[0][1], boundingBox[1][0], boundingBox[1][1]]
|
||||
});
|
||||
}
|
||||
})
|
||||
return tileLayer;
|
||||
}
|
||||
|
||||
function createDeckWithExtent(extent){
|
||||
|
||||
var state= {
|
||||
longitude: extent[0] + (extent[2]-extent[0])/2,
|
||||
latitude: extent[1] + (extent[3]-extent[1])/2,
|
||||
zoom: 18,
|
||||
maxZoom: 22,
|
||||
minZoom: 12,
|
||||
pitch: 60,
|
||||
bearing: 1.469387755102039
|
||||
}
|
||||
|
||||
var newDeckgl = new deck.DeckGL({
|
||||
container: 'map',
|
||||
initialViewState: state,
|
||||
controller: true,
|
||||
layers: []
|
||||
});
|
||||
|
||||
return newDeckgl;
|
||||
}
|
||||
|
||||
function fixVerticalPolygons(speckle_data){
|
||||
var speckle_features = []
|
||||
for (let i = 0; i < speckle_data.features.length; i++) {
|
||||
|
||||
feature = speckle_data.features[i];
|
||||
coords = feature.geometry.coordinates;
|
||||
|
||||
if (!(feature.geometry.type.includes("Polygon"))){
|
||||
speckle_features.push({"id": speckle_features.length, "type":"Feature",
|
||||
"geometry": {"type": feature.geometry.type, "coordinates":feature.geometry.coordinates},
|
||||
"properties": feature.properties,
|
||||
"displayProperties": feature.displayProperties });
|
||||
}
|
||||
else {
|
||||
polygon_all_parts = []
|
||||
|
||||
// iterate through Polygon Parts
|
||||
for (let p = 0; p < coords.length; p++) {
|
||||
|
||||
// check orientation of each PolygonPart, if vertical - shift points slightly
|
||||
polygon_part = [];
|
||||
inner = false;
|
||||
|
||||
for (let c = 0; c < coords[p].length; c++) {
|
||||
polygon_part_loop = [];
|
||||
if (c>0){
|
||||
inner = true;
|
||||
}
|
||||
|
||||
sum_orientation = 0;
|
||||
polygon_pts = coords[p][c]; // usually 3 for Mesh faces
|
||||
for (let k = 0; k < polygon_pts.length; k++){
|
||||
index = k + 1
|
||||
if (k == polygon_pts.length - 1){index = 0}
|
||||
pt = polygon_pts[k]
|
||||
pt2 = polygon_pts[index]
|
||||
sum_orientation += (pt2[0] - pt[0]) * (pt2[1] + pt[1])
|
||||
};
|
||||
|
||||
createdPolygon = false;
|
||||
|
||||
if (-0.000000001 < sum_orientation && sum_orientation <0.000000001){
|
||||
|
||||
coords[p][c][0][0] += 0.0000001;
|
||||
coords[p][c][0][1] += 0.0000001;
|
||||
|
||||
coords[p][c][1][0] -= 0.0000001;
|
||||
coords[p][c][1][1] -= 0.0000001;
|
||||
|
||||
coords[p][c][2][0] += 0.0000001;
|
||||
coords[p][c][2][1] += 0.0000001;
|
||||
|
||||
if(polygon_pts.length==3) {
|
||||
createdPolygon = true;
|
||||
|
||||
multipolygon_coords = coords[p][c];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
|
||||
}
|
||||
else if (polygon_pts.length==4) {
|
||||
createdPolygon = true;
|
||||
|
||||
multipolygon_coords = coords[p][c].slice(0,3);
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
|
||||
/////////
|
||||
multipolygon_coords = [coords[p][c][2], coords[p][c][3], coords[p][c][0]];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
};
|
||||
|
||||
};
|
||||
if (createdPolygon == false){ // if non-vertical, or vertical with more than 4 vertices
|
||||
|
||||
multipolygon_coords = coords[p][c];
|
||||
polygon_part_loop = multipolygon_coords;
|
||||
|
||||
if (inner == false){
|
||||
polygon_part = [polygon_part_loop];
|
||||
polygon_all_parts.push(polygon_part);
|
||||
}
|
||||
else{
|
||||
polygon_all_parts[polygon_all_parts.length-1].push(polygon_part_loop);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
new_polygon = {"id": speckle_features.length, "type":"Feature",
|
||||
"geometry": {"type": "MultiPolygon", "coordinates":polygon_all_parts},
|
||||
"properties": speckle_data.features[i].properties,
|
||||
"displayProperties": speckle_data.features[i].displayProperties };
|
||||
|
||||
new_polygon.displayProperties.lineWidth = 0.03
|
||||
speckle_features.push(new_polygon);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
speckle_data.features = speckle_features
|
||||
}
|
||||
|
||||
function createSpeckleLayer(speckle_data) {
|
||||
|
||||
// Create layer
|
||||
speckle_layer = new deck.GeoJsonLayer({
|
||||
id: `speckle_data`,
|
||||
data: speckle_data,
|
||||
|
||||
// Styles
|
||||
filled: true,
|
||||
opacity: 0.8,
|
||||
getFillColor: f => rgbaToArgbList(f.displayProperties.color),
|
||||
getLineWidth: f => f.displayProperties.lineWidth,
|
||||
getPointRadius: f => f.displayProperties.radius / 2,
|
||||
})
|
||||
|
||||
return speckle_layer;
|
||||
}
|
||||
|
||||
|
||||
async function getFeaturesFromTraffic(traffic_url)
|
||||
{
|
||||
traffic_features = []
|
||||
try{
|
||||
const data = await fetch(traffic_url).then(response => response.json());
|
||||
results = data.results
|
||||
console.log(results);
|
||||
|
||||
var max_jam_factor = Math.max(...results.map(o => o.currentFlow.jamFactor))
|
||||
|
||||
for (segm in results){
|
||||
var jam_factor = results[segm].currentFlow.jamFactor / max_jam_factor;
|
||||
|
||||
links = results[segm].location.shape.links
|
||||
pts_coords = []
|
||||
for (link_ind in links){
|
||||
pts = links[link_ind].points
|
||||
for (pt_ind in pts){
|
||||
pt = pts[pt_ind]
|
||||
pts_coords.push([pt.lng, pt.lat, 0])
|
||||
}
|
||||
}
|
||||
lineColor = getTrafficColor(jam_factor);
|
||||
|
||||
new_line = {"type":"Feature",
|
||||
"geometry": {"type": "LineString", "coordinates": pts_coords},
|
||||
"displayProperties": {"color": lineColor, "width": results[segm].currentFlow.freeFlow} };
|
||||
traffic_features.push(new_line);
|
||||
}
|
||||
}
|
||||
catch {}
|
||||
return traffic_features
|
||||
}
|
||||
|
||||
function getTrafficColor(jam_factor){
|
||||
|
||||
// higher jam factor, higher red color
|
||||
color = 'rgba(100,100,100,255)'
|
||||
red = Math.pow( (jam_factor ),0.5) *240
|
||||
blue = 0
|
||||
green = Math.pow( ((1 - jam_factor) ),0.5) *240
|
||||
|
||||
color = `rgba(${Math.round(red)},${Math.round(green)},${Math.round(blue)},255)`
|
||||
return color
|
||||
}
|
||||
|
||||
async function addTrafficLayer(extent, all_layers){
|
||||
console.log("__update traffic")
|
||||
|
||||
const TRAFFIC_API_KEY = "41_ea6vX0sH8ec-wbRIEzUKt9tCUbW4rXDUeObNKoUc" // disposable key, replace with yours
|
||||
|
||||
// specify the traffic query extend a bit larger than the model extent
|
||||
buffer = 0.004 // degrees, approx 400 meters
|
||||
range_x = extent[2] - extent[0] + 2* buffer
|
||||
range_y = extent[3] - extent[1] + 2* buffer
|
||||
center_x = extent[0] + (extent[2] - extent[0])/2
|
||||
center_y = extent[1] + (extent[3] - extent[1])/2
|
||||
|
||||
var bbox = `${center_x-range_x/2},${center_y-range_y/2},${center_x+range_x/2},${center_y+range_y/2}`
|
||||
var traffic_url = `https://data.traffic.hereapi.com/v7/flow?locationReferencing=shape&in=bbox:${bbox}&apiKey=${TRAFFIC_API_KEY}`
|
||||
|
||||
var traffic_features = await getFeaturesFromTraffic(traffic_url);
|
||||
traffic_layer = new deck.GeoJsonLayer({
|
||||
id: 'traffic_layer',
|
||||
data: traffic_features,
|
||||
getLineWidth: f => Math.ceil(f.displayProperties.width),
|
||||
getLineColor: f => rgbaToArgbList(f.displayProperties.color),
|
||||
})
|
||||
all_layers.splice(2, 1); // remove old traffic layer
|
||||
all_layers.push(traffic_layer);
|
||||
}
|
||||
|
||||
|
||||
(async () => {
|
||||
// define Speckle Models' URLs
|
||||
var cad_data = 'https://app.speckle.systems/projects/0c353a2132/models/d9639e9a47';
|
||||
var speckle_url = `https://geo.speckle.systems/speckle/?speckleUrl=${cad_data}&limit=1000000&datatype=polygons&lon=26.7155164&lat=58.3739412&preserveattributes=true`;
|
||||
|
||||
// fetch Speckle data
|
||||
const speckle_data = await fetch(speckle_url, { headers: {'Accept': 'application/geo+json'}}).then(response => response.json());
|
||||
|
||||
// print received data to console
|
||||
console.log(speckle_data);
|
||||
|
||||
// fix rendering of vertical polygons
|
||||
fixVerticalPolygons(speckle_data)
|
||||
|
||||
// get spatial extent of Speckle data
|
||||
var extent = speckle_data["extent"]
|
||||
|
||||
// Create deck.gl map
|
||||
const deckgl = createDeckWithExtent(extent)
|
||||
var tile_layer = createTileLayer()
|
||||
var speckle_layer = createSpeckleLayer(speckle_data)
|
||||
|
||||
// add traffic layer
|
||||
all_layers = [tile_layer, speckle_layer]
|
||||
await addTrafficLayer(extent, all_layers);
|
||||
|
||||
deckgl.setProps({layers: all_layers})
|
||||
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"name": "square_json",
|
||||
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::27700" } },
|
||||
"features": [
|
||||
{ "type": "Feature", "properties": { "label": "EPSG:4326, [-10, -10, 10, 10]" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -10.0, -10.0 ], [ 10.0, -10.0 ], [ 10.0, 10.0 ], [ -10.0, 10.0 ], [ -10.0, -10.0 ] ] ] } }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"name": "square_json - Copy",
|
||||
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::27700" } },
|
||||
"features": [
|
||||
{ "type": "Feature", "properties": { "label": "EPSG:27700, [-1000000, -1000000, 1000000, 1000000]" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1000000.0, -1000000.0 ], [ 1000000.0, -1000000.0 ], [ 1000000.0, 1000000.0 ], [ -1000000.0, 1000000.0 ], [ -1000000.0, -1000000.0 ] ] ] } }
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user