UNPKG

@goshawk22/leaflet-elevation

Version:

A Leaflet plugin that allows to add elevation profiles using d3js

128 lines (108 loc) 4.17 kB
/** * @see https://github.com/Raruto/leaflet-elevation/issues/251 * * @example * ```js * L.control.Elevation({ * altitude: true, * distance: true, * handlers: [ 'Altitude', 'Distance', 'LinearGradient', ], * linearGradient: { * attr: 'z', * path: 'altitude', * range: { 0.0: '#008800', 0.5: '#ffff00', 1.0: '#ff0000' }, * min: 'elevation_min', * max: 'elevation_max', * }, * }) * ``` */ export function LinearGradient() { if (!this.options.linearGradient) { return {}; } const _ = L.Control.Elevation.Utils; /** * Initialize gradient color palette. */ const get_palette = function ({range, min, max, depth = 256}) { const canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'), gradient = ctx.createLinearGradient(0, 0, 0, depth); canvas.width = 1; canvas.height = depth; for (let i in range) { gradient.addColorStop(i, range[i]); } ctx.fillStyle = gradient; ctx.fillRect(0, 0, 1, depth); const { data } = ctx.getImageData(0, 0, 1, depth); return { /** * Gets the RGB values of a given z value of the current palette. * * @param {number} value - Value to get the color for, should be between min and max. * @returns {string} The RGB values as `rgb(r, g, b)` string */ getRGBColor(value) { const idx = Math.floor(Math.min(Math.max((value - min) / (max - min), 0), 0.999) * depth) * 4; return 'rgb(' + [data[idx], data[idx + 1], data[idx + 2]].join(',') + ')'; } }; }; // Use slope as the attribute for coloring const { preferCanvas } = this.options; const { attr, path: path_name, range, min, max } = L.extend({ attr: 'slope', // <-- use 'slope' instead of 'z' or 'elevation' path: 'altitude', range: { 0: '#008800', 0.5: '#008800', 0.6: '#ffff00', 0.8: '#ff0000', 1: '#ff0000' }, // example: green for negative/flat, yellow for uphill, red for steep min: -25, // <-- set min slope value to -25 max: 25, // <-- max slope property }, (true === this.options.linearGradient) ? {} : this.options.linearGradient); const gradient_id = path_name + '-gradient-' + _.randomId(); const legend_id = 'legend-' + gradient_id; // Charte profile gradient this.on('elechart_axis', () => { if (!this._data.length) return; const chart = this._chart; const path = chart._paths[path_name]; const { defs } = chart._chart.utils; const palette = get_palette({ min, max, range, }); let gradient; if (preferCanvas) { /** ref: `path.__fillStyle` within L.Control.Elevation.Utils::drawCanvas(ctx, path) */ path.__fillStyle = gradient = chart._context.createLinearGradient(0, 0, chart._width(), 0); } else { defs.select('#' + gradient_id).remove(); gradient = defs.append('svg:linearGradient').attr('id', gradient_id); gradient.addColorStop = function(offset, color) { gradient.append('svg:stop').attr('offset', offset).attr('stop-color', color) }; path.attr('fill', 'url(#' + gradient_id + ')').classed('area', false); } // Generate gradient for each segment picking colors from palette for (let i = 0, data = this._data; i < data.length; i++) { if (i < data.length - 1) { const dist = data[i + 1].dist - data[i].dist; const totalDist = data[data.length - 1].dist - data[0].dist; const offset = (data[i].dist - data[0].dist) / totalDist; gradient.addColorStop(offset, palette.getRGBColor(data[i][attr])); } } }); // Legend item gradient this.on('elechart_updated', () => { const chart = this._chart; const { defs } = chart._chart.utils; defs.select('#' + legend_id).remove(); const legendGradient = defs.append('svg:linearGradient').attr('id', legend_id); Object.keys(range).sort().forEach(i => legendGradient.append('svg:stop').attr('offset', i).attr('stop-color', range[i])); chart._container .select('.legend-' + path_name + ' > rect') .attr('fill', 'url(#' + legend_id + ')') .classed('area', false); }); return { }; }