function make_elevation_chart(trkpts) {
let w = window.innerWidth < 600 ? window.innerWidth : 600;
let h = w / 3;
let pad_left = 40;
let pad_bottom = 20;
let svg = d3
.create("svg")
.attr("class", "elevation_chart")
.attr("width", w)
.attr("height", h);
svg
.append('rect')
.attr('width', w)
.attr('height', h)
.attr('fill', 'white')
.attr('opacity', 0.5);
let lngLats = trkpts.map(pt => [pt[1], pt[0]]);
let elevation_path = [];
let cummulative_length = 0;
let R = 3;
let distance_to_point_map = new Map();
for (let i = R; i < trkpts.length - R; i++) {
cummulative_length =
cummulative_length + d3.geoDistance(lngLats[i - 1], lngLats[i]) * 3958.8;
let elevation = d3.mean(trkpts.slice(i - R, i + R).map(pt => pt.ele));
elevation_path.push([cummulative_length, elevation]);
distance_to_point_map.set(cummulative_length, lngLats[i]);
}
let path_length = elevation_path.slice(-1)[0][0];
let elevations = trkpts.map(o => o.ele);
let min_elevation = d3.min(elevations);
let max_elevation = d3.max(elevations);
let elevation_pad = 500;
let elevation_path2 = [[0, min_elevation - elevation_pad]]
.concat(elevation_path)
.concat([[path_length, min_elevation - elevation_pad]]);
let x_scale = d3
.scaleLinear()
.domain([0, path_length])
.range([pad_left, w]);
let y_scale = d3
.scaleLinear()
.domain([min_elevation - elevation_pad, max_elevation + elevation_pad])
.range([h - pad_bottom, 0]);
let pts_to_path = d3
.line()
.x(d => x_scale(d[0]))
.y(d => y_scale(d[1]));
svg
.append("path")
.attr("d", pts_to_path(elevation_path2))
.style("stroke", "black")
.style("stroke-width", "0px")
.style("stroke-linejoin", "round")
.style('opacity', 0.7)
.style("fill", "#eee");
svg
.append("path")
.attr("d", pts_to_path(elevation_path))
.style("stroke", "black")
.style("stroke-width", "3px")
.style("stroke-linejoin", "round")
.style("fill", "none");
svg
.append("g")
.attr("transform", `translate(0, ${h - pad_bottom})`)
.call(d3.axisBottom(x_scale));
svg
.append("g")
.attr("transform", `translate(${pad_left})`)
.call(d3.axisLeft(y_scale));
let position_marker = svg
.append('g')
.attr('class', 'position_marker')
.style('opacity', 0);
position_marker
.append('line')
.attr('stroke-width', '1px')
.attr('stroke', 'black')
.attr('y1', 0)
.attr('y2', h)
.attr('x1', w / 2)
.attr('x2', w / 2);
position_marker
.append('circle')
.attr('r', '5px')
.attr('cx', w / 2)
.attr('cy', h / 2)
.attr('fill', '#0ff')
.attr('stroke', 'black');
let lengths = elevation_path.map(pt => pt[0]);
svg
.on('mouseenter', function() {
position_marker.style('opacity', 1);
})
.on('mousemove', function(evt) {
evt.preventDefault();
let distance = x_scale.invert(d3.pointer(evt)[0]);
let i = binarySearch(lengths, distance);
let x = x_scale(distance);
let elevation = elevations[i];
let y = y_scale(elevation);
position_marker
.select('line')
.attr('x1', x)
.attr('x2', x);
position_marker
.select('circle')
.attr('cx', x)
.attr('cy', y);
global.gpx_path.getLayers()[1].setLatLng(trkpts[i]);
})
.on('mouseleave', function() {
position_marker.style('opacity', 0);
});
svg
.append('text')
.attr('x', 50)
.attr('y', 20)
.text(formatTime(trkpts[0].time));
svg
.append('text')
.attr('x', 50)
.attr('y', 40)
.text(`${d3.format('0.1f')(path_length)} miles`);
d3.select(elevation_chart_container)
.selectAll(".elevation_chart")
.remove();
d3.select(elevation_chart_container).append(() => svg.node());
}