I like what Mike did but I want to tweak the colours and make it feel more Martian.
original:
An example of d3-contour using R’s volcano. See also a heatmap of this dataset.
forked from mbostock's block: Contour Plot
| license: gpl-3.0 | |
| height: 673 | |
| border: no |
I like what Mike did but I want to tweak the colours and make it feel more Martian.
original:
An example of d3-contour using R’s volcano. See also a heatmap of this dataset.
forked from mbostock's block: Contour Plot
| <!DOCTYPE html> | |
| <div id="map" style='position:absolute'> | |
| </div> | |
| <script src="https://d3js.org/d3.v5.min.js"></script> | |
| <script src="https://unpkg.com/axios/dist/axios.min.js"></script> | |
| <script src="https://d3js.org/d3-hsv.v0.1.min.js"></script> | |
| <script src="https://d3js.org/d3-contour.v1.min.js"></script> | |
| <script src="https://unpkg.com/axios/dist/axios.min.js"></script> | |
| <script src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js'></script> | |
| <script src="https://unpkg.com/d3-summary-tiles/build/d3-summary-tiles.min.js"></script> | |
| <script src="https://d3js.org/d3-color.v1.min.js"></script> | |
| <script src="https://d3js.org/d3-dispatch.v1.min.js"></script> | |
| <script src="https://d3js.org/d3-ease.v1.min.js"></script> | |
| <script src="https://d3js.org/d3-interpolate.v1.min.js"></script> | |
| <script src="https://d3js.org/d3-selection.v1.min.js"></script> | |
| <script src="https://d3js.org/d3-timer.v1.min.js"></script> | |
| <script src="https://d3js.org/d3-transition.v1.min.js"></script> | |
| <script> | |
| var transition = d3.transition(); | |
| const volcanoPath = 'https://bl.ocks.org/madams1/raw/1613db1ba5d0c315b93e8543924c415c/05cbaf2c378751563964af6c6979ad76b2a6ac05/volcano.json' | |
| let svg, width, height; | |
| const getMars = () => { | |
| // Mars | |
| const i0 = d3.interpolateHsvLong(d3.hsv(21, 1, 0.65), d3.hsv(24, 1, 0.90)); | |
| const i1 = d3.interpolateHsvLong(d3.hsv(25, 1, 0.90), d3.hsv(40, 1, 0.95)); | |
| const interpolateTerrain = function(t) { return t < 0.5 ? i0(t * 2) : i1((t - 0.5) * 2); }; | |
| const color = d3.scaleSequential(interpolateTerrain).domain([90, 190]); | |
| return color; | |
| } | |
| // Hoth | |
| const getHoth = () => { | |
| // i0 set | |
| const lightIceH = 179; | |
| const lightIceS = 0.18 | |
| const lightIceL = 1; | |
| const darkIceH = 188; | |
| const darkIceS = 0.18 | |
| const darkIceL = 0.90; | |
| // il set | |
| const darkerIceH = 196; | |
| const darkerIceS = 0.05; | |
| const darkerIceL = 0.95; | |
| const darkestIceH = 165; | |
| const darkestIceS = 0.09 | |
| const darkestIceL = 0.78; | |
| const i0 = d3.interpolateHsvLong(d3.hsv(lightIceH, lightIceS, lightIceL), d3.hsv(darkIceH, darkIceS, darkIceL)); | |
| const i1 = d3.interpolateHsvLong(d3.hsv(darkestIceH, darkestIceS, darkestIceL), d3.hsv(darkerIceH, darkerIceS, darkerIceL)); | |
| const interpolateTerrain = function(t) { return t < 0.5 ? i0(t * 2) : i1((t - 0.5) * 2); }; | |
| const color = d3.scaleSequential(interpolateTerrain).domain([90, 190]); | |
| return color; | |
| } | |
| let lastPosition = {x: 300, y: 300} | |
| let roverModel = { | |
| id: 0, | |
| name: '01', | |
| cargo: 0, | |
| cargoMax: 500, | |
| fuel: 500, | |
| fuelMax: 500, | |
| } | |
| const drawGridLines = svg => { | |
| console.log('drawGridlines') | |
| const gridContainer = svg.append('g') | |
| .attr('id', 'grid'); | |
| const width = window.innerWidth; | |
| const height = window.innerHeight; | |
| const gap = 10; | |
| const totalHLines = Math.floor(height / gap); | |
| const totalVLines = Math.floor(width / gap); | |
| let hCount = 0; | |
| while (hCount < totalHLines) { | |
| const y = gap * hCount; | |
| gridContainer | |
| .append("line") | |
| .style("stroke", "#ccc") | |
| .style("stroke-width", "0.5px") | |
| .style("opacity", "0.25") | |
| .attr("x1",0) | |
| .attr("y1",y) | |
| .attr("x2",width) | |
| .attr("y2",y); | |
| hCount++ | |
| } | |
| let vCount = 0; | |
| while (vCount < totalVLines) { | |
| const x = gap * vCount; | |
| gridContainer | |
| .append("line") | |
| .style("stroke", "#ccc") | |
| .style("stroke-width", "0.5px") | |
| .style("opacity", "0.25") | |
| .attr("x1",x) | |
| .attr("y1",0) | |
| .attr("x2",x) | |
| .attr("y2",height); | |
| vCount++ | |
| } | |
| } | |
| const drawRover = svg => { | |
| const trans = `translate(${lastPosition.x},${lastPosition.y})` | |
| const gBody = svg.append('g') | |
| .attr('id','rover'); | |
| gBody.append('rect') | |
| .attr('width', 10) | |
| .attr('height',10) | |
| .style('fill','#666'); | |
| gBody.attr('transform', 'translate(300,300)'); | |
| const gWheelF1 = gBody.append('g') | |
| .append('rect') | |
| .attr('width', 3) | |
| .attr('height',2) | |
| .style('fill','#333'); | |
| gWheelF1.attr('transform', 'translate(1,10)'); | |
| const gWheelF2 = gBody.append('g') | |
| .append('rect') | |
| .attr('width', 3) | |
| .attr('height',2) | |
| .style('fill','#333'); | |
| gWheelF2.attr('transform', 'translate(1,-2)'); | |
| const gWheelF3 = gBody.append('g') | |
| .append('rect') | |
| .attr('width', 3) | |
| .attr('height',2) | |
| .style('fill','#333'); | |
| gWheelF3.attr('transform', 'translate(6,-2)'); | |
| const gWheelF4 = gBody.append('g') | |
| .append('rect') | |
| .attr('width', 3) | |
| .attr('height',2) | |
| .style('fill','#333'); | |
| gWheelF4.attr('transform', 'translate(6,10)'); | |
| const head = gBody.append('g') | |
| .append('rect') | |
| .attr('width', 4) | |
| .attr('height',6) | |
| .style('fill','#999'); | |
| head.attr('transform', 'translate(2,2)'); | |
| } | |
| const animateRover = position => { | |
| const rover = d3.select('#rover'); | |
| const trans = `translate(${position.x},${position.y})` | |
| let timer = 0; | |
| const handleStart = ()=>{ | |
| timer = setInterval( | |
| () => { | |
| const rover = d3.select('#rover'); | |
| const roverBBox = rover.node().getBBox(); | |
| const roverMatrix = rover.node().getScreenCTM(); | |
| //console.log(roverMatrix); | |
| const position = {x: Math.floor(roverMatrix.e), y: Math.floor(roverMatrix.f)} | |
| updateHud(position); | |
| }, 1000 | |
| ) | |
| } | |
| const handleComplete = ()=>{ | |
| clearInterval(timer) | |
| const coinFlip =() => { | |
| return (Math.floor(Math.random() * 2) == 0); | |
| } | |
| const flip = coinFlip(); | |
| let newPosition; | |
| const maximum = 40; | |
| const minimum = -40; | |
| const dist = Math.floor(Math.random() * (maximum - minimum + 1)) + minimum; | |
| if (flip) { | |
| const y = lastPosition.y - dist; | |
| newPosition = {...lastPosition, y}; | |
| } else { | |
| const x = lastPosition.x - dist; | |
| newPosition = {...lastPosition, x}; | |
| } | |
| if (roverModel.fuel >= 0) animateRover(newPosition); | |
| } | |
| //console.log('animate rover: ', rover) | |
| // by adding the d3-selection library, I lost the ability to transition! so be careful importing... | |
| rover.transition() | |
| .duration(5000) | |
| .attr('transform',trans) | |
| .on('start',handleStart) | |
| .on('end', handleComplete) | |
| lastPosition = position; | |
| } | |
| let lastEl = 100; | |
| const getElevation = (position) => { | |
| // https://bl.ocks.org/madams1/1613db1ba5d0c315b93e8543924c415c | |
| // detect collision between rover and path | |
| const rover = d3.select("#rover"); | |
| const paths = d3.selectAll('path'); | |
| const dif = 300; | |
| const elFilter = point => { | |
| //console.log('position: ', position) | |
| ///console.log('point: ', point) | |
| return ((position.x - dif - point.x) < 2 && (position.y - dif - point.y) < 2 ) | |
| } | |
| const foundPos = _.find(data, elFilter); | |
| if (foundPos) { | |
| lastEl = foundPos.value; | |
| return foundPos.value; | |
| } | |
| else return lastEl; | |
| } | |
| const drawHud = svg => { | |
| const hudG = svg.append('g') | |
| .attr('id','hud') | |
| .attr('transform', 'translate(10,60)'); | |
| hudG.append('rect') | |
| .attr('width','120px') | |
| .attr('height','70px') | |
| .style('fill', 'transparent') | |
| .style('stroke-width', '1px') | |
| .style('stroke','orange'); | |
| const elevation = hudG.append('g') | |
| .attr('transform', 'translate(10,20)'); | |
| elevation.append('text') | |
| .attr('id','elevation') | |
| .style('font-family','arial') | |
| .style('fill','orange') | |
| .text('Elevation 10'); | |
| const fuel = hudG.append('g') | |
| .attr('transform', 'translate(10,40)'); | |
| fuel.append('text') | |
| .attr('id','fuel') | |
| .style('font-family','arial') | |
| .style('fill','orange') | |
| .text(`Fuel ${roverModel.fuel}`); | |
| const cargo = hudG.append('g') | |
| .attr('id','elevation') | |
| .attr('transform', 'translate(10,60)'); | |
| cargo.append('text') | |
| .attr('id','cargo') | |
| .style('font-family','arial') | |
| .style('fill','orange') | |
| .text(`Cargo ${roverModel.cargo}%`); | |
| } | |
| const updateHud = (position) => { | |
| roverModel.fuel--; | |
| if (roverModel.cargo < roverModel.cargoMax) roverModel.cargo++; | |
| // rover / path collision to get elevation | |
| // or rover / grid square ?! | |
| //const elevation = Math.floor(Math.random() * 5).toFixed(2) * 100; | |
| const elevation = getElevation(position); | |
| const cargoPer = (roverModel.cargo / roverModel.cargoMax * 100).toFixed(1); | |
| //console.log("cargo", roverModel.cargo) | |
| ///console.log("cargoMax", roverModel.cargoMax) | |
| //console.log("cargoPer", cargoPer) | |
| d3.select("#elevation").text(`Elevation: ${elevation}`); | |
| d3.select("#fuel").text(`Fuel: ${roverModel.fuel}`); | |
| d3.select("#cargo").text(`Cargo: ${cargoPer}%`); | |
| } | |
| const drawTiles = response => { | |
| // https://bl.ocks.org/madams1/raw/1613db1ba5d0c315b93e8543924c415c/ | |
| console.log('drawTiles') | |
| let summaryTileChart = d3.summaryTiles(); | |
| // axios.get('volcano2.json').then(res => { | |
| console.log('drawTiles loaded') | |
| summaryTileChart | |
| .data(response) | |
| .x("x") | |
| .y("y") | |
| .fill("value") | |
| .tileWidth(9) | |
| .tileHeight(9) | |
| .reverseColorScale() | |
| //.noTooltip() | |
| //.onClick( e => {console.log("hey: ", e.target)} ) | |
| .flipYAxis() | |
| .noXTicks() | |
| .noYTicks() | |
| .horizontalPadding(0) | |
| .xLabel("") | |
| .yLabel(""); | |
| d3.select("#map") | |
| .call(summaryTileChart); | |
| // }); | |
| } | |
| const drawPlanet = (planet,terrain) => { | |
| const color = (planet === "mars") ? getMars() : getHoth(); | |
| const worldG = svg.append('g') | |
| .attr('id','world') | |
| .attr('transform', 'translate(0,50)'); | |
| worldG | |
| .selectAll("path") | |
| .data(d3.contours() | |
| .size([terrain.width, terrain.height]) | |
| .thresholds(d3.range(90, 195, 5)) | |
| (terrain.values)) | |
| .enter().append("path") | |
| .attr("d", d3.geoPath(d3.geoIdentity().scale(width / terrain.width))) | |
| .style('pointer-events','none') | |
| .attr("fill", d => { | |
| return color(d.value); | |
| }); | |
| } | |
| let data; | |
| d3.json("volcano2.json") | |
| .then( response => { | |
| data = response; | |
| console.log('data: ', data) | |
| const values = response.map( obj => (obj.value)); | |
| const terrain = {width: 87, height: 61, values} | |
| //console.log('terrain: ',terrain) | |
| drawTiles(response) | |
| svg = d3.select("svg"), | |
| width = +svg.attr("width"), | |
| height = +svg.attr("height"); | |
| drawPlanet('mars',terrain); | |
| drawGridLines(svg); | |
| drawHud(svg); | |
| drawRover(svg); | |
| const y = lastPosition.y + 20; | |
| const newPosition = {...lastPosition, y}; | |
| setTimeout( ()=>{ | |
| animateRover(newPosition); | |
| },1000); | |
| }, | |
| error => {if (error) throw error}); | |
| </script> |
| ���� JFIF 8 8 ��@ICC_PROFILE 0appl mntrRGB XYZ � | |