Public
Edited
Apr 18, 2023
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof category = html`
<select name="" id="category-select">
<option value="CD">Census Demographics</option>
<option value="HCD">Housing and Community Development</option>
<option value="WED">Workforce and Economic Development</option>
<option value="CFH">Children and Family Health and Well-Being</option>
<option value="CS">Crime and Saftey</option>
<option value="AC">Arts and Culture</option>
<option value="EY">Education and Youth</option>
<option value="S">Sustainability</option>
</select>`
Insert cell
viewof indicator = html`<select name="" id="endpoint-select" placeholder="EsriURL Tpop" value="Tpop">
${
indicator_meta_table.map( record => {
return `<option value="${record.shortname}" style="display:${category == record.category ? 'block' : 'none'}">${record.description}</option>`
} )
}
</select>`
Insert cell
rawGeoData = d3.text(`https://services1.arcgis.com/mVFRs7NF4iFitgbY/ArcGIS/rest/services/${indicator}/FeatureServer/0/query?where=1%3D1&outFields=*&returnGeometry=true&f=pgeojson`)
Insert cell
Insert cell
Insert cell
mutable fileContents = JSON.parse(rawGeoData)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
initSVGObject = {
function initSVGObject(geojson, extrudeOn, groupOn, colorOn, colorScale, bll, extrudeMultiplier, addToExtrudeHeight, h, w){
var [minE, maxE] = minMax(geojson.features, extrudeOn)
var [minC, maxC] = minMax(geojson.features, colorOn)
var avgE = getAvg(geojson.features.map(a => a.properties[extrudeOn]))
var avgC = getAvg(geojson.features.map(a => a.properties[colorOn]))
console.log(geojson.features, colorOn, minC, maxC, avgC)
var getColor = (e) => {
var rsp = e.properties[groupOn] == bll ? `#000000` : `${ cchromahroma.scale(colorScale)( (e.properties[colorOn]) / (avgC) -.5 ).hex() } `
return rsp
}
// var getExtrude = (e) => { return ( ((e.properties[extrudeOn] / (avgE)) * extrudeMultiplier ) + addToExtrudeHeight) }
var getExtrude = (e) => { return e.properties[extrudeOn] }
//Define map and path projection then update the bounding box
var projection = d3.geoMercator().translate([0, 0]).scale(1);
const path = d3.geoPath().projection(projection);
var b = path.bounds( geojson );
var s = .95 / Math.max((b[1][0] - b[0][0]) / w, (b[1][1] - b[0][1]) / h);
var t = [(w - s * (b[1][0] + b[0][0])) / 2, (h - s * (b[1][1] + b[0][1])) / 2];
projection.scale(s).translate(t)
//Create SVG element
var svg = d3.select(DOM.svg()).attr("width", w).attr("height", h).attr("id", 'line')
.attr("minE", `${minE}`).attr("maxE", `${maxE}`).attr("avgE", `${avgE}`)
.attr("minC", `${minC}`).attr("maxC", `${maxC}`).attr("avgE", `${avgC}`)
.attr("fill", 'white').attr("stroke", 'gray')
//Bind data and create one path per GeoJSON feature
svg.selectAll("path").data(geojson.features).enter().append("path").attr("d", path)
.attr("fill", function(e,i){ return getColor(e) } )
.attr('extrudeOn', function(e){ return JSON.stringify(e.properties[extrudeOn]) } )
.attr('extrude', function(e){ return JSON.stringify( getExtrude(e) ) } )
.attr('groupOn', function(e){ return JSON.stringify(e.properties[groupOn]) } )
.attr('colorOn', function(e){ return JSON.stringify(e.properties[colorOn]) } )
.attr('color', function(e){ return JSON.stringify( getColor(e) ) } )
.attr('onmousemove', function(e){
return `tooltip.innerHTML = "${e.properties[groupOn]} : ${Number.parseFloat(e.properties[extrudeOn]).toFixed(2)}"`
} )
return svg.node()
}

// Start by adding a baselayer by using Turf.JS to create a convex hull of our geometry
var hull = turf.convex(fileContents);
hull.properties[extrudeOn] = minMax(fileContents.features, extrudeOn)[0]/1.25
hull.properties[colorOn] = minMax(fileContents.features, colorOn)[0];
hull.properties[groupOn] = "Baltimore City"
if(fileContents.features[0].properties[groupOn] != "Baltimore City" ){ fileContents.features.unshift(hull) }

return initSVGObject(fileContents, extrudeOn, groupOn, colorOn, ['darkblue', 'blue', 'lightblue','lavender', 'pink', 'red', 'darkred'], "Baltimore City", extrudeMultiplier, addToExtrudeHeight, height, width)
}
Insert cell
console.log(fileContents.features.map(a => a.properties[colorOn]))
Insert cell
minMax(fileContents.features, colorOn)
Insert cell
( ((0.005 - minMax(fileContents.features, colorOn)[0]) / (minMax(fileContents.features, colorOn)[1] - minMax(fileContents.features, colorOn)[0])) + .000001 ) * parseInt(extrudeMultiplier) + parseInt(addToExtrudeHeight)
Insert cell
parseInt(extrudeMultiplier)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
geoObj = {
function createGeoObject(svgObject){
var svgAttrs = Object.assign( {},
...Array.from(svgObject.attributes, ({name, value}) => ({[name]: value})) )
var svgPaths = svgObject.getElementsByTagName('path')
var svgPath = [], extrudeOn = [], extrude = [], groupOn = [], colorOn = [], color = [];
for (var i = 0 ; i < svgPaths.length ; i++) {
svgPath.push( svgPaths[i].getAttribute('d') )
extrudeOn.push( parseInt( JSON.parse(svgPaths[i].getAttribute('extrudeOn') ) ) )
extrude.push( parseInt( JSON.parse(svgPaths[i].getAttribute('extrude') ) ) )
groupOn.push( JSON.parse(svgPaths[i].getAttribute('groupOn') ) )
colorOn.push( parseInt( JSON.parse(svgPaths[i].getAttribute('colorOn') ) ) )
color.push( JSON.parse(svgPaths[i].getAttribute('color') ) )
}
return {svgPath, extrudeOn, extrude, groupOn, colorOn, color, svgAttrs}
}
return createGeoObject( initSVGObject )
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
/// Part from g0v/twgeojson
/// Graphic Engine and Geo Data Init Functions
addGeoObject = function ( group, geoObj, w, h ) {
var paths = geoObj.svgPath;
var depths = geoObj.extrude;
var colorOn = geoObj.colorOn;
var colors = geoObj.color;
var center = { x: w/2, y: h/2 };
var meshlist = []
// var [min, max] = [geoObj.svgAttrs.minC, geoObj.svgAttrs.maxC]
var [min, max] = [geoObj.svgAttrs.minE, geoObj.svgAttrs.maxE]
var scale = (min,max,val) => { return ((val - min) / (max - min)) }
// simple gradient function
function gradient(length, maxLength) {
var i = (length * 255 / maxLength);
var r = i; var g = 255-(i); var b = 0;
var rgb = b | (g << 8) | (r << 16);
return rgb;
}
// For every geometry
for ( var i = 0; i < paths.length; i ++ ) {
// Get Depth
// var depth = scale(min,max, Math.log( parseInt( depths[ i ] ) ) ) * extrudeMultiplier + addToExtrudeHeight
var depth = scale(min,max, parseInt( depths[ i ] ) ) * parseInt(extrudeMultiplier) + parseInt(addToExtrudeHeight)
//var depth = Math.log( parseInt( depths[ i ] ) )
/* console.log( {
'minMax': [min, max],
'given': parseInt( depths[ i ]),
'scaled': scale(min,max, parseInt( depths[ i ] ) ),
'extrude': scale(min,max, parseInt( depths[ i ] ) ) * parseInt(extrudeMultiplier)
} )
*/
// Get Color
// Method 1
var color = new THREE.Color( colors[i].trim() );
// Method 2
//var color = gradient(Math.round(scale),255)
// Set color onto material object
var material = new THREE.MeshLambertMaterial( { color: color, emissive: color } );
// Create a 3j 2d shape object from the svg path.
var $d3g = {}
d3threeD( $d3g )
var simpleShapes = $d3g.transformSVGPath(paths[ i ]).toShapes( true );
//
// datasets with disconnected multipolygon spatial assets yield a final meshlist > than its featurlist.length
//
for ( var j = 0; j < simpleShapes.length; j ++ ) {
var simpleShape = simpleShapes[ j ];
var shape3d = new THREE.ExtrudeBufferGeometry( simpleShape, {
depth: depth,
bevelEnabled: false
} );
var mesh = new THREE.Mesh( shape3d, material );
mesh.rotation.x = Math.PI;
mesh.translateZ( - depth - 1 );
mesh.translateX( - center.x );
mesh.translateY( - center.y );
meshlist.push(mesh)
}
}
return meshlist
};
Insert cell
mutable meshlist = addGeoObject( group, geoObj, width, height );
Insert cell
Insert cell
Insert cell
camera = {
const fov = 45;
const aspect = width/200;
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// camera.position.set( 0, 0, 700 );
camera.position.z = height+200;
return camera;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
dirLight = {
const dirLight = new THREE.DirectionalLight( 0xffffff, .1 );
const d = 50;
// dirLight.color.setHSL( .01, 1, .01 ); // hue, saturation, lightedness
dirLight.position.set( 0, 10, 500 ); // LR, UD, IO
dirLight.target.position.set(-5, 0, 0);
dirLight.castShadow = true;
return dirLight
}
Insert cell
hemLight = {
const skyColor = 0x000000;
const groundColor = 0xffffff
const intensity = .1;
const hemLight = new THREE.HemisphereLight(skyColor, groundColor, intensity);
return hemLight
}
Insert cell
scene = {
var scene = new THREE.Scene();
scene.add( group );
// Lighting
scene.add( dirLight );
const dirLightHelper = new THREE.DirectionalLightHelper( dirLight, 10 );
scene.add( dirLightHelper );
scene.add( hemLight );
// var ambientLight = new THREE.AmbientLight( 0x404040 , 0.2 );
const loader = new THREE.TextureLoader();
const bgTexture = loader.load('https://lh3.googleusercontent.com/p1MST_cB1OAL7qMwjQxaCIaVNFJEUn0sIlAFtOYVWkn0Y_uxDj9VKvyMydSq23hifkaMbTtA-95CZ1qzCbt67FTSemtSaytk1Su4roc=s0-k');
scene.background = bgTexture; // new THREE.Color('white'); null;
//scene.fog = new THREE.Fog( 0x000000, 250, 1400 )
var domEvents = new THREEx.DomEvents(camera, renderer.domElement )
// for (var i = 0; i < Object.keys(finalObj).length; i++) { scene.add( finalObj[i].mesh[0] )
for (let i = scene.children.length - 1; i >= 0; i--) {
if(scene.children[i].type === "Mesh")
scene.remove(scene.children[i]);
}
function clicked(e) { console.log("You clicked: ", {e}) }
for (var i=0; i < meshlist.length; i++){ var mesh = meshlist[i]
domEvents.addEventListener(mesh, 'click', function(event){ console.log("You clicked: ") }, true)
scene.add( mesh )
}
return scene;
}
Insert cell
scene.toJSON()
Insert cell
Insert cell
Insert cell
Insert cell
((2 - t[0]) / (t[1] - t[0])) * parseInt(extrudeMultiplier) + parseInt(addToExtrudeHeight)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
finalObj = {
var contentObj = {}
var count = 0
for( var feature of fileContents.features){
contentObj[count] = {}
contentObj[count][extrudeOn] = feature.properties[extrudeOn]
contentObj[count][colorOn] = feature.properties[colorOn]
contentObj[count][groupOn] = feature.properties[groupOn]
contentObj[count]['type'] = feature.geometry.type
contentObj[count]['polyCount'] = feature.geometry.coordinates.length
count++
}
var count = 0
for( var i in contentObj ){
contentObj[i]['mesh'] = []
for (var k = 0; k < contentObj[i].polyCount; k++) {
contentObj[i]['mesh'].push( meshlist[count] )
count++
}
}
return contentObj
}
Insert cell
finalObj[0].mesh[0].position
Insert cell
Insert cell
getCommunity = (column, community) =>{
mutable stlFilename = html`<input value=${community + '.stl'}>`
var newArray = []
for (var i = 0; i < Object.keys(finalObj).length; i++) {
if(finalObj[i][column]==community){
newArray.push(finalObj[i].mesh[0])
}
}
return newArray[0]
}
Insert cell
com = getCommunity(groupOn, "Brooklyn/Curtis Bay/Hawkins Point")
Insert cell
STLExporter = import("https://cdn.skypack.dev/three@0.136.0/examples/jsm/exporters/STLExporter.js")
Insert cell
mutable blobby = null
Insert cell
exporter = new STLExporter.STLExporter();
Insert cell
download = DOM.download(blobby, "scene.stl", "Download Blob")
Insert cell
exportSceneBtn = html`<button onclick=${exportScene(scene)}>exportScene</button>`
Insert cell
exportScene = (meshThis) => {
console.log('exportScene', meshThis)
let buffer = exporter.parse( meshThis )
mutable blobby = new Blob( [ buffer ], { type: "text/obj" } )
}
Insert cell
// exportBinaryBtn = html`<button onclick=${exportBinary(group)}>exportBinary</button>`
Insert cell
exportBinaryBtn = html`<button onclick=${exportBinary(group)}>exportBinary</button>`
Insert cell
exportBinary = (meshThis) => {
console.log('exportBinary', meshThis)
let buffer = exporter.parse( meshThis, { binary: true } )
mutable blobby = new Blob( [ buffer ], { type: 'application/octet-stream' } )
}
Insert cell
exportASCIIBtn = html`<button onclick=${exportASCII(group)}>exportASCII</button>`
Insert cell
exportASCII = (meshThis) => {
console.log('exportASCII', meshThis)
var text = exporter.parse( meshThis );
mutable blobby = new Blob( [ text ], { type: 'text/plain' } )
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more